Skocz do zawartości
Komentator

Kurs STM32 - #4 - Sterowanie portami GPIO w praktyce

Pomocna odpowiedź

Elvis, Kod daje 200Hz na pinie15 czyli 5ms okres. Płytka Zl29ARM z STM32F107vct6 na zewnętrzny zegar podłączone jest 10MHz. Mi się właśnie skończył weekend, więc będę afk, ale dzięki za chęci 😉

#include "stm32f10x.h"

void SysTick_Handler(void)  {
GPIO_WriteBit(GPIOE, GPIO_Pin_15, (BitAction) ((1-GPIO_ReadOutputDataBit(GPIOE, GPIO_Pin_15))));
}

int main (void)  {
uint32_t returnCode;
returnCode = SysTick_Config(SystemCoreClock / 1000);    //200 tyknieć na sekundę
 if (returnCode != 0)  {                                   /* Check return code for errors */
   // Error Handling
 }
 //konfigurowanie portow GPIO
 GPIO_InitTypeDef  GPIO_InitStructure;

 /*Tu nalezy umiescic kod zwiazany z konfiguracja sygnalow zegarowych potrzebnych w programie peryferiow*/
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);//wlacz taktowanie portu GPIO E
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);//wlacz taktowanie portu GPIO C

 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14 | GPIO_Pin_15;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
 GPIO_Init(GPIOE, &GPIO_InitStructure);

 while(1);
}

Udostępnij ten post


Link to post
Share on other sites

Zacznijmy od programu z kursu, czyli przykładu opisanego w akapicie "Opóźnienia w STM32 – SysTick". Po uruchomieniu na płytce Nucleo, oscylogram wygłąda następująco:

Tak długie czasy znacznie lepiej wyglądają dla ludzkiego oka, niż oscyloskopu, więc lepiej będzie zmniejszyć opóźnienia. Zamiast 400ms i 100ms wstawiamy 4ms i 1ms. Teraz nawet nieco wiekowy oscyloskop może podpowiedzieć jakie czasu uzyskaliśmy:

Jak widać jest prawie-idealnie. Zamist 1ms mamy 1.12ms, czyli 12% dłyższy impuls niż oczekiwany. W kursie była uwaga o zmiennejSystemCoreClock - w wersji biblioteki ST, która była dostępna podczas pisania kursu jej wartość wynosiła 72000000 zamiast 64000000. Jak podzielimy 72 przez 64, to znajdziemy nasze brakujące 12%.

Mam nadzieję, że aktualna wersja biblioteki nie ma już tego błędu, ale zawsze możemy zmienić wartość zmiennej. Wpiszmy przed wywołaniem SysTick_Config:

SystemCoreClock = 64000000

Teraz oscylogram wygląda zgodnie z oczekiwaniami:

Na koniec można jeszcze zmienić program, zostawić pustą pętlę kodzie głównym, a przy każdym wywołaniu SysTick_Handler zmieniać stan wyjścia. Ponieważ timer jest konfigurowany instrukcjami:

	SystemCoreClock = 64000000;
SysTick_Config(SystemCoreClock / 1000);

Możemy oczekiwać przerwania co 1/1000s, co potwierdza oscylogram:

[ Dodano: 19-06-2016, 22:09 ]

To co przysłałeś to tylko fragment kodu programu. Mnie interesuje całość. Co wywołuje funkcję main?

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

Elvis, Jestem juz na 99% pewny, że kod jest ok tylko ustawienia standardowe u mnie są inne. Ja aby dostać takie same wykresy muszę wpisać:

SystemCoreClock=28800000;
returnCode = SysTick_Config(SystemCoreClock / 1000); 

Mam prośbę mógłbyś skompilować mi twój kod z twoimi ustawieniami pod STM32F107vct6 i przesłać mi elf, albo hex na maila makwiatkow@gmail.com? Bo może to wina tego mojego procka. Wtedy będę miał 100% pewności, albo nowe podejrzenia co do mojej IDE.

Udostępnij ten post


Link to post
Share on other sites

Nie do końca rozumiem co to są ustawienia standardowe. W każdym razie proste mikrokontrolery jak np. stm32 nie posiadają rozbudowanych systemów operacyjnych, nie mają też wiedzy o tym co jest podłączone do nich. Efekt jest taki, że programista musi wiedzieć i rozumieć na jakim sprzęcie pracuje. W przypadku kursu, programy były pisane na Nucleo z prockiem stm32f103rb i tylko w takiej konfiguracji działają bez zmian. Jeśli chcesz żeby program działał w innym srodowisku musisz go zmodyfikować. Nie znaczy to że program jest zły, nie znaczy również że IDE źle działa. Po prostu program musi być dopasowany do sprzętu, jeśli nie jest to nie będzie działał.

Doszliśmy do pierwszych wniosków, teraz możemy spróbować ustalić jaką konfigurację posiadasz i dlaczego uzyskujesz takie a nie inne rezultaty. Od razu podpowiem, że metodą na napisanie poprawnego programu jest zrozumienie jak działa sprzęt. Wtedy można policzyć odpowiednie parametry, a na koniec sprawdzić uzyskane wyniki np. oscyloskopem. Metoda prób i błędów, dobieranie parametrów na chybił-trafił to fajna zabawa, ale na pewno nie metoda której powinno się uczyć i polecać.

Na początek musimy ustalić jaki zegar wykorzystuje procesor. Piszesz że HSI, jednak coś tutaj się nie zgadza. Jak słusznie zauważyłeś wbudowany generator RC pracuje z częstotliwością 8MHz, więc gdyby to on był źródłem sygnału, cały system pracowałby 9 razy wolniej, a stała SystemCoreClock powinna mieć wartość 8000000.

Przy okazji - SystemCoreClock musi mieć wartość odpowiadającą częstotliowści pracy mikrokontrolera. Niestety kod od ST przyjmuje wartość 72000000, co jest typowe dla ich płytek ewaluacyjnych (Discovery). Jednak inne płytki jak np. Nucleo, czy Kamami mogą pracować z innymi częstotliwościami, co niestety nie jest automatycznie rozpoznawane.

Skoro Twoja płytka pracuje poprawnie z SystemCoreClock ustawionym na 28800000, zegar systemowy ma prawdopodobnie taką częstotliwość.

Ponieważ generator wewnętrzny ma 8MHz, zewnętrzny 10MHz, a my szukamy 28.8MHz, prawdopodobnie uruchomiona jest pętla PLL. Teraz wypadałoby sprawdzić, gdzie znajduje się kod, który ją uruchamia.

Używasz innego środowiska niż opisywane w kursie, więc prosiłem Cię o pełny kod źródłowy - pownieważ nie mam tego IDE, nie wiem gdzie zaczyna się program.

Przy okazji ważne pytanie testowe - jak w szkole: gdzie zaczyna się program?

Udostępnij ten post


Link to post
Share on other sites

Elvis, Na wstępie chcę podziękować Tobie za te oscylogramy. Były bardzo pomocne już wiem gdzie tkwił mój problem i problem z tymi bibliotekami. Już wyjaśniam.

1) Zakładałem, że jak w kodzie nie skonfiguruje żadnego zegara, to on automatycznie przydzieli mi HSI, czyli 8Mhz. Co okazało się błędnym założeniem. Za każdym razem trzeba konfigurować zegar, bo inaczej liczymy na szczęście.

2) Skonfigurowałem zegar w taki sposób:

Przypisałem HSE zewnętrzny 10Mhz do mnożnika 7, czyli uzyskałem 70Mhz:

// PLLCLK = 10MHz * 7 = 70MHz
RCC_PLLConfig(RCC_PLLSource_PREDIV1, RCC_PLLMul_7);

Następnie przypisałem PLL do SysClk:

RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

W tym momencie jak wywołam:

SysTick_Config(70000000/1000);

Dostałem idealnie 1ms.

Powtórzyłem to dla innych ustawień, czyli ustawiłem 40Mhz i przypisałem:

SysTick_Config(40000000/1000);

Dalej dostałem 1ms, wszystko grało jak należy. Problem był tylko wtedy, gdy chciałem użyć:

SysTick_Config(SystemCoreClock / 1000);  

Wtedy pod zmienną SystemCoreClock nie ma aktualnego zegara, tylko coś innego i tu tkwi problem.

Dziś jestem już nieźle wydymany tym problemem, bo nie daje mi to spać i muszę odpocząć. Nie wiem, czy to jest wina CooCox, ale jeszcze to będę testował.

W sumie to można zamknąć temat 😉, bo mi się wyjaśniło, a nad poprawą tych bibliotek i tak popracuję.

Dzięki za wszystkie posty, które były pomocne.

Udostępnij ten post


Link to post
Share on other sites

Wydaje mi się, że jesteś już blisko rozwiązania zagadki, ale jeszcze trochę brakuje

Za każdym razem trzeba konfigurować zegar, bo inaczej liczymy na szczęście

Nie udostępniłeś całego programu, więc wyjaśnię "w ciemno".

W przypadku mikrokontrolerów program wcale nie zaczyna się wykonywać od funkcji main(), przed nią całkiem sporo się dzieje. Gdy tworzymy projekt za pomocą odpowiedniego kreatora, dodaje on kilka plików. Jeden ma najczęściej "startup" w nazwie i jest napisany w assemblerze, chociaż zdarza się i kod w C. W każdym razie zawartość tego pliku jest uruchamiana przed funkcją main. Kod jest odpowiedzialny za ustalenie adresu stosu, wyzerowanie zmiennych (inaczej zmienne globalne wcale nie miałyby początkowej wartości zero), wywołanie niskopoziomowych funkcji inicjalizujących, a na koniec funkcji main.

Okazuje się, że większość projektów dla STM32 przed funkcją main wywołuje inicjalizację zegara. Zawartość tej funkcji ustala więc początkową konfigurację. Gdyby usunąć jej treść, mielibyśmy to co podaje dokumentacja procesora - czyli HSI i 8MHz jako źródło zegara. Jednak jeśli zostawimy domyślny kod, najczęściej dostaniemy 72MHz z pętli PLL zasilanej z zewnętrznego generatora 8MHz. W przypadku Nucleo, środowisko OpenSTM32 potrafiło samo przygotować odpowiednie pliki dla wbudowanego generatora RC 8MHz i pętli PLL 64Mhz - ponieważ na płytce nie zamontowano kwarca niestety.

Nie jest to więc kwestia szczęścia, ale raczej sprawdzenia, co wyprawia kod uruchamiany przed funkcją main.

[ Dodano: 20-06-2016, 22:52 ]

Jako ciekawostkę poniżej możemy zobaczyć fragment pliku startup_stm32f10x_ms.S, który jest dodawany przez OpenSTM32 podczas tworzenia projektu (zgodnie z instrukcją z kursu):

/* Call the clock system intitialization function.*/
   bl  SystemInit
/* Call static constructors */
   bl __libc_init_array
/* Call the application's entry point.*/
bl	main
bx	lr

Jak widać najpierw wywoływana jest funkcja SystemInit. To w niej konfigurowana jest pętla PLL i procesor zaczyna pracować na 64MHz. Dalej inicjalizowana jest biblioteka standardowa C. Instrukcja "bl main" powoduje skok do funkcji main, czyli miejsca gdzie większość programistów C zaczyna programowanie 🙂

Widzimy też co stanie się jeśli funkcja main zostanie zakończona - bx lr oznacza skok do funkcji, której adres jest w rejestrze lr. Jest to niestety błąd, który spowoduje dość dziwne zachowanie programu. Wniosek jest taki, że nie należy wychodzić z funkcji main, ani ufać dostawcom bibliotek.

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

Elvis, Dzięki za ten ostatni post, to wiele zmienia. Ja zawsze deklarowałem zegary i nawet tego nie zauważałem. Tu chciałem, aby kod był jak najkrótszy, żeby się zmieścił na 1 slajdzie prezentacji i to mnie zgubiło. Złe założenia, dobre chęci 😋.

Ale dobrze posłuchać kogoś kto wie więcej na ten tema.

Co do bibliotek standardowych, to ja nigdy im nie ufałem i radziłem sobie w inny sposób widoczny na filmie (czyli doświadczalnie). Niestety nie jest to dobry sposób jak się chce, to wytłumaczyć w technikum ^^, bo za każdą zmianą zegara trzeba było podłączać oscyloskop i konfigurować go na nowo, albo liczyć z proporcji do nowych ustawień.

Dobrze, że dziś będę mógł już spać 😃

Udostępnij ten post


Link to post
Share on other sites

Witam. Mam problem ze zdebugowaniem pierwszego programu. Projekt założyłem dokładnie według poradnika ale wyskakuje mi błąd jak na obrazku:

1881959056_bd.thumb.png.2491a9e9a6f59dc3c23999ce4c03f648.png

Udostępnij ten post


Link to post
Share on other sites

patsos95, czy kompilacja i samo wgrywanie przebiega bez problemu? Wgrałeś wymagane sterowniki? Sprawdź też, czy nic innego nie korzysta z tego portu COM (np. terminal).

Udostępnij ten post


Link to post
Share on other sites

Moze mi ktos powiedziec co jest zle w tym kodzie ze dioda nie chce migac? Stm32 L152re

#include "stm32l1xx.h"
#include "stm32l1xx_nucleo.h"

void delay(int time)
{
   int i;
   for (i = 0; i < time * 4000; i++) {}
}

int main(void)
{
 GPIO_InitTypeDef gpio;

 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

 GPIO_StructInit(&gpio);
     gpio.GPIO_Pin = GPIO_Pin_5;
     gpio.GPIO_Mode =  GPIO_Mode_OUT;
     gpio.GPIO_OType =  GPIO_OType_PP;
     GPIO_Init(GPIOA, &gpio);

     while (1) {
                 GPIO_SetBits(GPIOA, GPIO_Pin_5);
                 delay(50);
                 GPIO_ResetBits(GPIOA, GPIO_Pin_5);
                 delay(100);
             }
     }

Udostępnij ten post


Link to post
Share on other sites

Qwerty123, _kursSprzet

Udostępnij ten post


Link to post
Share on other sites

Na jakiej podstawie wiadomo do jakiego pinu jest podłączony przycisk bądź dioda?

Bo z tego co wyczytałem z dokumentacji tej płytki to Przyciski B1 jest podłączony do pinu2 a nie 13. datasheet

I nie bardzo rozumiem to, że mikrokontroler jest wyposażony w porty od A do E a przykład włączania wszystkich portów zawiera od A do D.

Udostępnij ten post


Link to post
Share on other sites

fizzysky, wszystko jest opisane właśnie w tym dokumencie, który podajesz:

"B1 USER: the user button is connected to the I/O PC13 (pin 2) of the STM32 microcontroller"

Jeśli popatrzysz na dokumentację mikrokontrolera STM32F103RB, zobaczysz że pin 2 to wyprowadzenie PC13. Jest to ten sam pin, 2 to jego fizyczny numer, czyli druga "nóżka" jeśli popatrzysz na scalak. PC13 to moduł do którego jest podłączony, program będzie go widział jako pin 13 na porcie C.

To trochę jak z adresem zamieszkania - można np. podać adres pocztowy, współrzędne GPS albo opisać jak trafić do celu. Za każdym razem będziemy opisywali to samo miejsce, ale na różne sposoby.

Pin 2 jest ważny gdy projektujemy płytkę PCB, do niego muszą dochodzić ścieżki łączące z przełącznikiem. PC13 jest informacją dla programisty - niezależnie od typu obudowy i fizycznego wyprowadzenia.

Udostępnij ten post


Link to post
Share on other sites

Witam serdecznie

Prośba do autora o wyjaśnienie:

 GPIO_ResetBits(GPIOC, 1 << led)

Pozdrawiam

Udostępnij ten post


Link to post
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Gość
Dołącz do dyskusji! Kliknij, aby zacząć pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.


×
×
  • Utwórz nowe...