Ta strona używa ciasteczek (plików cookies), dzięki którym może działać lepiej. Dowiedz się więcejRozumiem i akceptuję

Kurs STM32 – #5 – Komunikacja z komputerem, UART

Programowanie 01.10.2015 Elvis

KursSTM32_5W poprzedniej części kursu STM32 nauczyliśmy się używać linii I/O do komunikacji z otoczeniem. Nadszedł czas na poznanie pierwszego interfejsu, który pozwoli na większą interakcję ze światem.

UART posłuży nam do przesyłania komunikatów między STM32, a komputerem PC.

Nawigacja serii artykułów:
« poprzednia częśćnastępna część »

Kup zestaw elementów i zacznij naukę w praktyce! Przejdź do strony dystrybutora »

RS-232, UART, USART – odrobina teorii

Kiedyś właściwie wszystkie komputery PC były wyposażone w interfejs RS-232. Obecnie USB prawie zupełnie wyparło starszy standard. Złącze szeregowe miało jedną ogromną zaletę – było proste w obsłudze (szczególnie porównując z USB).

USB vs. RS-232

Wtyczki USB vs. RS-232

Dlatego w przypadku mikrokontrolerów port szeregowy jest nadal popularny, chociaż coraz częściej w nieco innej postaci. RS-232 działał na dość nietypowych napięciach ±11V (właściwie tolerował od 3V do 15V), dlatego do jego obsługi potrzebny był dodatkowy układ konwertera napięcia np. MAX232.

W typowych mikrokontrolerach stosuje się uproszczoną wersję interfejsu, działającą na napięciach 0V i 3.3V (zamiast wspomnianych wcześniej). Moduł odpowiedzialny za obsługę takiej komunikacji nazywany jest UART (ang. Universal Asynchronous Receiver and Transmitter).

Przykładowy przebieg UART

Przykładowa ramka UART

Transmisja rozpoczyna się od bitu startu, zaznaczonego na rysunku jako BS. Zawsze jest to bit będący logicznym zerem. Następnie, zależnie od konfiguracji, następuje po sobie 7, 8 lub 9 bitów danych (tutaj zaznaczone jako B0-B7), które są wysyłaną informacją. Bit stopu (zaznaczony tutaj jako bit BK) to bit będący logiczną jedynką – mówi o końcu transmisji.

Format ramki oraz sposób transmisji jest właściwie niezmieniony względem RS-232.

Płytka Nucleo wyposażona jest w konwerter z UART na USB. Nie mamy więc po drodze RS-232, a po podłączeniu do PC, przejściówka będzie widziana jako port COM, czyli nasz port szeregowy.

Część płytki będąca programatorem i przejściówką UART.

Część płytki będąca programatorem i przejściówką UART.

Różnice między UART, a USART

UART – Universal Asynchronous Receiver and Transmitter
USART – Universal Synchronus and Asynchronous Receiver and Transmitter

W przypadku komputerów PC za obsługę portu szeregowego odpowiedzialny był układ UART, czyli: Universal Asynchronous Receiver and Transmitter.

Nasz STM32 posiada moduł, który może pracować zarówno synchronicznie, jak i asynchronicznie, więc nazwa stała się jeszcze bardziej skomplikowana – USART, Universal Synchronus and Asynchronous Receiver and Transmitter. Używamy jednak tylko komunikacji asynchronicznej, więc obie nazwy możemy traktować jako synonimy.

Jeśli chcesz dowiedzieć się więcej o UART oraz jego zastosowaniach przeczytaj również artykuł: port szeregowy i interfejs USART, czyli komunikacja z komputerem.

Możliwość przesyłania danych jest nieoceniona podczas uruchamiania i testowania układów. Można w tym celu wykorzystywać debugger, np. przez interfejs SWD, jednak komunikaty testowe często są niezastąpioną metodą wyszukiwania błędów.

W kolejnych częściach kursu jeszcze wielokrotnie wykorzystamy komunikację przez UART, np. do testowania przetwornika ADC. Dodatkowym plusem komunikacji szeregowej jest możliwość łatwego zastąpienia przejściówki UART – USB modułem radiowym i przesyłanie informacji np. za pomocą Bluetooth lub WiFi.

Zestaw elementów do przeprowadzenia ćwiczeń

Gwarancja pomocy na forum dla osób, które kupią poniższy zestaw!

Zestaw ponad 120 elementów do przeprowadzenia wszystkich ćwiczeń z kursu można nabyć u naszych dystrybutorów! Dostępne są wersje z płytką Nucleo lub bez niej!


Kup w Botlandzie »

Do obserwowania efektów pracy programów z tej części kursu konieczny jest dodatkowy program, terminal, przykładowo może to być darmowy: Tera Term lub Realterm.

Konfiguracja UART na STM32

Jak zwykle pierwszy krok to konfiguracja zegara. STM32 posiada kilka modułów USART, my wykorzystamy USART2, ponieważ jest on podłączony do naszej przejściówki, która znajduje się na płytce. Musimy również skonfigurować alternatywne funkcje pinów.

Pierwsza instrukcja uruchamia zegar funkcji alternatywnych linii I/O, druga uruchamia USART2.

Nie możemy w jednej instrukcji uruchomić obydwu,
ponieważ są to różne linie zegara (APB1 i APB2).

Zanim uruchomimy UART, zmienimy konfigurację linii RX oraz TX. Linia wyjściowa (TX) jest obecna na wyprowadzeniu PA2, a wejście (RX) na PA3. Wyjście skonfigurujemy jako funkcję alternatywną w trybie push-pull, natomiast wejście ustawimy jako pływające (ang. floating), czyli bez rezystora podciągającego. Kod będzie więc wyglądał wtedy następująco:

Gdy mamy już skonfigurowane linie, czas przygotować moduł USART2:

Prędkość transmisji 115200 jest jedną ze standardowych wartości. Jeśli pojawiałyby się błędy podczas komunikacji, można wykorzystać niższe, np.: 9600 lub 38400.

UART na STM32 – wysyłanie bajtów

Do wysyłania danych służy funkcja USART_SendData. Przyjmuje ona dwa parametry: wykorzystywany interfejs (USART2) oraz wartość (bajt) do wysłania. Jeśli chcemy wysłać np. napis musimy podzielić go na bajty i wysyłać kolejno.

Niestety prędkość wysyłania danych jest mała, więc jeśli po prostu wywołamy USART_SendData, szybko zapełnimy bufor nadawczy, a kolejne dane będą gubione. Przed wysłaniem powinniśmy więc upewnić się, że w buforze nadawczym jest miejsce. W tym celu wykorzystamy funkcję: USART_GetFlagStatus. Procedura wysyłania bajtu będzie miała więc postać:

W pętli while czekamy, aż bufor nadawczy będzie wolny, a następnie wysyłamy dane.

W tej części kursu nie użyjemy przerwań do obsługi komunikacji przez UART. Wrócimy do tego, jeśli będzie później taka potrzeba.

UART na STM32 – wysyłanie ciągu bajtów

Jak wysłać w takim razie cały napis? Możemy wykorzystać np. taką funkcję :

W uproszczeniu, jako argument, przyjmuje ona ciąg znaków (wskaźnik na jego początek, czyli adres pierwszego elementu tablicy), a dalej napis przesyłany jest znak, po znaku.

Teraz możemy napisać program, który będzie wysyłał przez UART cały napis:

Znak końca wiersza to nieustający problem w przypadku różnych systemów operacyjnych. System Windows używa dwóch znaków, CR LF (czyli \r\n) natomiast Linux tylko jednego LF (\n). Ponieważ przykłady uruchamiamy na komputerze z systemem Windows, wysyłamy dwa znaki.

Więcej o tym problemie można przeczytać na Wikipedii.

Cały kod realizujący wysyłanie napisu wygląda w sposób następujący:

Rezultat powinien wyglądać następująco:

UART_01

STM32 – wysyłanie danych przez UART.

UART na STM32 – odbieranie danych

Potrafimy już wysyłać dane. Teraz czas odebrać transmisję z PC – np.: instrukcje sterujące robotem lub innym urządzeniem. Do odbioru bajtu wykorzystamy funkcję USART_ReceiveData.

Tutaj pojawia się jednak analogiczny problem jak przy wysyłaniu danych. Nie możemy odczytać danych, jeśli w buforze odbiorczym ich nie ma. Zanim wywołamy tę funkcję powinniśmy upewnić się, że coś w buforze jest.

Wykorzystamy do tego tę samą funkcję co poprzednio, czyli: USART_GetFlagStatus. Fragment odbierający dane wygląda następująco:

Oczywiście w tym momencie powinniśmy do czegoś wykorzystać zawartość zmiennej c – np. zapisać ją w buforze. Jeśli wystarczy nam bardzo proste sterowanie, po odebraniu znaku możemy wykonać czynność, przykładowo skręcić robotem, zapalić diodę, albo wysłać komunikat.

Prosty przykład wyglądałby następująco:

Działanie programu w praktyce widoczne jest na poniższym zrzucie ekranu:

UART_02

STM32 – odbieranie danych przez UART.

Zadanie domowe 5.1

Napisz program do sterowania diodami led, np. ‚A’ zapala diodę 1, ‚a’ gasi, ‚B’ zapala diodę 2, itd. Diody podłącz do pinów, które wybierzesz samodzielnie.

Zadanie domowe 5.2

Napisz program, który odbiera kilka znaków, np. 3, zapisuje w buforze, a następnie wykonuje odebrane polecenie, np. „on1” włącza diodę 1, „of2” wyłącza diodę 2.

Zadanie domowe 5.3

Napisz program odbierający znaki, aż do znaku końca linii (\n), a następnie wykonaj polecenie zależne od odebranego ciągu, np. „on 1”, „off 2”.

Przekierowanie printf

Wykorzystując własną funkcję send_string możemy przygotować całkiem sprawną komunikację z PC lub innym mikrokontrolerem. Jednak o wiele wygodniej byłoby wykorzystać standardową instrukcję printf do wypisywania komunikatów na złącze szeregowe.

Funkcja send_string jest bardzo prosta i potrafi jedynie wysyłać ciągi znaków. Funkcja printf daje natomiast możliwość formatowania napisów oraz wyświetlania liczb zarówno całkowitych jak i zmiennopozycyjnych. Jej wykorzystanie jest o wiele łatwiejsze, o czym chyba przekonał się każdy kto programował PC.

Dzięki przekierowaniu wyjścia, będziemy mogli używać printf, a wynik działania pojawi się w oknie terminala portu szeregowego. Dokładnie jak w przypadku programu napisanego na komputerze stacjonarnym.

Dzięki temu będziemy mogli przetestować nieśmiertelny przykład:

UWAGA!
Wykorzystanie printf, chociaż wygodne, powoduje znaczne zwiększenie objętości programu. Jeśli nie jest to absolutnie niezbędne najlepiej unikać wykorzystywania tej funkcji w programach.

Okazuje się, że gdy wywołujemy printf, biblioteka wykonuje za nas mnóstwo pracy związanej z formatowaniem i przetwarzaniem parametrów, a na koniec wywołuje funkcję __io_putchar dla każdego wysyłanego bajtu. Wystarczy więc że napiszemy tę funkcję i printf będzie wysyłał dane na nasz port szeregowy:

Cały kod będzie więc wyglądał jak poniższy:

Teraz już możemy uruchomić nasz przykład.

W przypadku Windows możemy zaobserwować dziwne zachowanie na końcu wiersza. W zależności od wykorzystywanego programu do komunikacji, zamiast napisów w kolejnych wierszach możemy zobaczyć schodki. Wynika to z innego sposobu kodowania końców linii w systemach Windows oraz Unix, o którym pisałem wcześniej.

UART_03e

STM32 – błąd wyświetlania danych.

Możemy zmienić komunikat dodając \r\n na końcu (tak jak robiliśmy poprzednio), albo udoskonalić procedurę wysyłającą dane:

Teraz komunikaty powinny pojawić się prawidłowo.

UART_03

STM32 – poprawne wyświetlanie danych przez UART.

Poprawiony kod programu:

Formatowanie tekstu z printf

Osoby, które nie spotkały się wcześniej z printf mogą nie widzieć jej zalet. Możliwości tej pozornie prostej funkcji są całkiem spore. Po więcej informacji warto zajrzeć np.: do manuala. Na zachętę przeprowadźmy szybkie doświadczenie.

Załóżmy, że naszym celem będzie pomiar napięcia przez ADC (o czym w kolejnym odcinku), wynik zaokrąglamy do części całkowitych i wyświetlamy w formie napisu:

Odczytana wartosc to X V!

Oczywiście, za X podstawiamy otrzymaną liczbę. Normalnie musielibyśmy konwertować liczbę, łączyć ją z tekstem lub wysyłać całość w trzech osobnych krokach (napis, wartość i jednostka). Korzystając z właściwości printf możemy zrobić to w jednej linii:

Funkcja ta zamieni wystąpienie %d na cyfrę 2, która została podana jako argument funkcji. Printf pozwala na znacznie więcej operacji, zainteresowanych jeszcze raz odsyłam do manuala.

Wynik powyższego eksperymentu z printf.

Wynik powyższego eksperymentu z printf.

Zadanie domowe 5.4

Wykorzystaj właściwości funkcji printf do wyświetlania na ekranie komputera napisu, w którego treści podstawiane są dwie liczby całkowite:

To jest XX cz. kursu STM32, a to jest mój X dzien nauki!

Zadanie domowe 5.5

Wykorzystaj właściwości funkcji printf do wyświetlania na ekranie komputera napisu, w którego treści podstawiana jest liczba zmiennoprzecinkowa oraz całkowita:

To zadanie domowe nr XX, a ten napis wyswietlono juz X razy

Zadanie domowe 5.6

Napisz program działający jak w zadaniu 5.1, ale wykorzystując przerwanie do obsługi odbioru danych. Artykuł nie omawia przerwań od modułu UART, jednak wszystkie informacje są dostępne w dokumentacji mikrokontrolera. Obsługa przerwania jest podobna do omawianej w poprzedniej części kursu.

Podsumowanie

Poznaliśmy postawy komunikacji za pomocą portu szeregowego. Potrafimy wysyłać oraz odbierać komunikaty. W kolejnych częściach kursu wykorzystamy nabyte umiejętności do przesyłania wyników pomiarów, np. wartości odczytanych z przetwornika analogowo-cyfrowego.

Kup zestaw elementów i zacznij naukę w praktyce! Przejdź do strony dystrybutora »

Następna część naszego kursu STM32 będzie omawiała wykorzystanie przetwornika ADC. Jeśli nie chcesz przeoczyć kolejnego odcinka, to skorzystaj z poniższego formularza i zapisz się na powiadomienia o nowych publikacjach!

Autor kursu: Piotr (Elvis) Bugalski
Redakcja: Damian (Treker) Szymański

Powiadomienia o nowych, darmowych artykułach!

Komentarze

bambosze_babuni

20:50, 01.10.2015

#1

Boże, jak ja się cieszę, że robicie ten kurs! Wdzięczny jestem niesamowicie :)

pion3k

21:11, 01.10.2015

#2

bambosze_babuni napisał/a:

Boże, jak ja się cieszę, że robicie ten kurs! Wdzięczny jestem niesamowicie :)

Przyłączam się do podziękowań, zawsze znajdzie się coś ciekawego na co człowiek wcześniej nie zwrócił uwagi :)

Mam tylko jedno pytanie: czy można zrealizować w analogiczny sposób komunikację PC z makietką STM32F4 Discovery Disco? Próbowałem na kilka sposobów ale komputer wykrywa tylko programator STLink, który sam w sobie nie jest widoczny dla terminalu Tera Term jako port szeregowy. Czy konieczne jest zastosowanie w tym przypadku konwertera UART TTL z USB lub, w przypadku komputera z RS232, układu MAX232?

Pozdrawiam

Elvis
Autor wpisu

21:16, 01.10.2015

#3

Niestety programatory płytek Discovery nie mają wbudowanego konwertera USB-UART. Konieczne jest więc zastosowanie dodatkowej przejściówki.

Treker
Administrator

22:00, 01.10.2015

#4

pion3k napisał/a:

Mam tylko jedno pytanie: czy można zrealizować w analogiczny sposób komunikację PC z makietką STM32F4 Discovery Disco? Próbowałem na kilka sposobów ale komputer wykrywa tylko programator STLink, który sam w sobie nie jest widoczny dla terminalu Tera Term jako port szeregowy. Czy konieczne jest zastosowanie w tym przypadku konwertera UART TTL z USB lub, w przypadku komputera z RS232, układu MAX232?

Dodam jeszcze, że brak przejściówek UART<->USB był jednym z powodów, przez które zrezygnowaliśmy z zestawów Discovery i wybraliśmy Nucleo :)

oficerJK

21:15, 26.10.2015

#5

Jak dla mnie początkującego w 32 bitowcach ten cykl jest pierwszym , który zachęca do dalszego eksperymentowania z nimi.

Ośmielę się zapytać jak zrobić aby po podłączeniu się do STM32 za pomocą terminala z PC otrzymać na powitanie jakiś tekst tak automatycznie?

W arduino dało to się wykonać coś takiego : while(Serial1.available()>0) .

Elvis
Autor wpisu

21:34, 26.10.2015

#6

O ile rozumiem Serial.available() zwraca liczbę odebranych bajtów.

Odpowiednikiem byłoby więc:

if (USART_GetFlagStatus(USART2, USART_FLAG_RXNE))

Nie daje to co prawda automatycznego powitania - najpierw trzeba coś wysłać, dopiero wtedy dostaniemy powitanie. Ale działać powinno tak samo.

Żeby zrealizować faktycznie automatyczne powitanie, należałoby wykorzystać dodatkowe linie RS232, np RTS i CTS. Za ich pomocą można byłoby wykrywać samo podłączenie wtyczki. Niestety jest to nieco bardziej skomplikowane niż się wydaje - wiele przejściówek nie dostarcza tych sygnałów, programy emulujące terminal też nie zawsze prawidłowo nimi sterują. Więcej o pełnym RS232 można poczytać na wikipedii: https://pl.wikipedia.org/wiki/RS-232

oficerJK

21:42, 26.10.2015

#7

W przypadku arduino chodzi raczej o otwarcie portu

link do źródeł https://www.arduino.cc/en/Serial/IfSerial

Elvis
Autor wpisu

21:52, 26.10.2015

#8

Jak rozumiem dotyczy to Leonardo i konwertera USB. Ja znalazłem coś takiego: https://www.arduino.cc/en/Serial/Available

Niestety w przypadku stm32 i Nucleo nie mamy dodatkowych informacji jakie daje CDC, więc nie wykryjemy podłączenia tak łatwo. Jest to cecha samego interfejsu RS232, a nie STM32, czy Arduino.

W przypadku Leonardo, dostajemy informacje bezpośrednio z USB, dlatego mamy więcej możliwości.

simoon87

15:29, 31.10.2015

#9

Mam pytanie z innej beczki próbuje skonfigurować USART w STM32F103RTB6 z obsługą przerwań oraz buforem cyklicznym. USART podłączony jest do SN75176 ( transceiver RS485) schemat poniżej:

Mój problem jest taki że procesor w ogóle nie wchodzi do przerwania USART1_IRQHandler oraz wygląda jakby się zawiesił. Dodatkowo jeśli wysyłam w taki sposób USART_SendData(USART1, 'X'); zamiast USART_Puts( USART1, "485 test!\n" ); to transmisja działa. Wychodzi więc, że błąd jest gdzieś w funkcji SendChar lub w konfiguracji ale niestety nie potrafię go znaleźć. Chciałbym prosić bardziej doświadczonych forumowiczów o pomoc w rozwiązaniu problemu.

konfiguracja USART1 9600 8e1:

void RS485_Configuration(uint32_t baudrate, uint16_t parity)

{

GPIO_InitTypeDef GPIO_InitStructure;

USART_InitTypeDef USART_InitStructure;

USART_ClockInitTypeDef USART_ClockInitStructure;

NVIC_InitTypeDef NVIC_InitStructure;

USART_DeInit(USART1);

#ifdef VECT_TAB_RAM

/* Set the Vector Table base location at 0x20000000 */

NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);

#else /* VECT_TAB_FLASH */

/* Set the Vector Table base location at 0x08000000 */

NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);

#endif

/* Enable the USART1 Interrupt */

NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; // USART1_IRQn

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

/* Config DE/nRE */

/* Configure (PA.11) as Output push-pull */

GPIO_InitStructure.GPIO_Pin = DO_485;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

GPIO_Init(GPIOA, &GPIO_InitStructure);

/* Configure USART1 Tx (PA.9) as alternate function push-pull */

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOA, &GPIO_InitStructure);

/* Configure USART1 Rx (PA.10) as input floating */

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

GPIO_Init(GPIOA, &GPIO_InitStructure);

USART_InitStructure.USART_BaudRate = baudrate;

USART_InitStructure.USART_WordLength = USART_WordLength_9b;

USART_InitStructure.USART_StopBits = USART_StopBits_1;

if( (parity != USART_Parity_Even) && (parity != USART_Parity_Odd) )

{

parity = USART_Parity_No;

}

USART_InitStructure.USART_Parity = parity ;

USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

USART_ClockInitStructure.USART_Clock = USART_Clock_Disable;

USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low;

USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge;

USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable;

/* Configure the USART1 */

USART_Init(USART1, &USART_InitStructure);

USART_ClockInit(USART1,&USART_ClockInitStructure);

/* Enable the USART1 */

USART_Cmd(USART1, ENABLE);

/* Send RS485 in Rece Mode */

SendMode(FALSE);

}

Funkcja SendChar:

void USART_SendChar(USART_TypeDef* USARTx, uint8_t Char)

{

/* Check the parameters */

assert_param(IS_USART_ALL_PERIPH(USARTx));

assert_param(IS_USART_DATA(Char));

/* Fill Tx buffor */

uint8_t tmp_head;

tmp_head = (Buffer.TxBeg+1) & TX_BUF_MASK;

while(tmp_head == Buffer.TxEnd) {}

Buffer.TxBuf[tmp_head]=Char;

Buffer.TxBeg=tmp_head;

/* Turn On Tx Interrupt / Starting transmit */

USART_ITConfig(USARTx, USART_IT_TXE, ENABLE);

}

Funkcja Puts

void USART_Puts( USART_TypeDef* USARTx, char *s )

{

register uint8_t c;

while ((c = *s++)) USART_SendChar( USARTx, c );

}

Wojciech

16:41, 31.10.2015

#10

A zegary do GPIO i AFIO kolega podłącza?

simoon87

17:59, 31.10.2015

#11

Wojciech napisał/a:

A zegary do GPIO i AFIO kolega podłącza?

TAK

void RCC_Config(void)

{

ErrorStatus HSEStartUpStatus; //zmienna opisujaca rezultat uruchomienia HSE

RCC_DeInit(); //Reset ustawien RCC

RCC_HSEConfig(RCC_HSE_ON); //Wlaczenie HSE

HSEStartUpStatus = RCC_WaitForHSEStartUp(); //Odczekaj az HSE bedzie gotowy

if(HSEStartUpStatus == SUCCESS)

{

FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);//

FLASH_SetLatency(FLASH_Latency_2); //ustaw zwloke dla pamieci Flash; zaleznie od taktowania rdzenia

//0:<24MHz; 1:24~48MHz; 2:>48MHz

RCC_HCLKConfig(RCC_SYSCLK_Div1); //ustaw HCLK=SYSCLK

RCC_PCLK2Config(RCC_HCLK_Div1); //ustaw PCLK2=HCLK

RCC_PCLK1Config(RCC_HCLK_Div2); //ustaw PCLK1=HCLK/2

RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); //ustaw PLLCLK = HSE*9 czyli 8MHz * 9 = 72 MHz

RCC_PLLCmd(ENABLE); //wlacz PLL

while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); //odczekaj na poprawne uruchomienie PLL

RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //ustaw PLL jako zrodlo sygnalu zegarowego

while(RCC_GetSYSCLKSource() != 0x08); //odczekaj az PLL bedzie sygnalem zegarowym systemu

/*Tu nalezy umiescic kod zwiazny z konfiguracja sygnalow zegarowych potrzebnych w programie peryferiow*/

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//wlacz taktowanie portu GPIO A enkoder

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//wlacz taktowanie portu GPIO B led

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);//wlacz taktowanie USART1 uart

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

} else {}

}

simoon87

18:59, 01.11.2015

#12

Źródło problemu częściowo zostało znalezione. Jest to wina IDE używałem System Workbench for STM32 polecanego w kursie i ku mojemu zaskoczeniu kod po przekopiowaniu do Keil uVision5 działa bez zarzutu. W System Workbench for STM32 USART działał w moim kodzie tylko gdy używam go bez obsługi przerwań. Czy ktoś Was spotkał się z takim problemem? Nie ukrywam, że bardziej przyzwyczajony jestem do Eclipse i brakuje mi kilku udogodnień w Keilu.

Treker
Administrator

19:17, 01.11.2015

#13

simoon87, czy oba środowiska korzystają z tej samej biblioteki standardowej? Może masz gdzieś dwie różne wersje i stąd problemy?

simoon87

19:40, 01.11.2015

#14

Treker napisał/a:

simoon87, czy oba środowiska korzystają z tej samej biblioteki standardowej? Może masz gdzieś dwie różne wersje i stąd problemy?

Nie, korzystają z tej samej wersji (StdPeriph_Lib_V3.5.0). Sprawdzałem ;-) Poza tym System Workbench for STM32 samo pobiera sobie biblioteki StdPeriph w trakcie tworzenia projektu wiec nie ma mowy o pomyłce prędzej mógłbym się pomylić dodając ręcznie biblioteki do uVision.

Treker, używałeś w tym IDE Usartu z obsługą przerwań? Chciałbym ustalić czy jest to problem u mnie czy coś innego?

Elvis
Autor wpisu

19:49, 01.11.2015

#15

simoon87, OpenSTM32 to nic innego jak gcc - cała reszta to otoczka. Więc źródło problemów to raczej nie kompilator, a IDE już na pewno.

Natomiast co do biblioteki standardowej, to gcc oraz Keil używają oczywiście innych - nie chodzi o StdPeriph, ale o bibliotekę standardową języka C oraz kod uruchamiany przed main().

Na pewno niepotrzebnie wykonujesz w kodzie inicjalizację PLL - w przypadku kodu, który jest dostarczany z OpenSTM32, SystemInit jest wywoływane przed main(), więc nie ma potrzeby ponownej inicjalizacji PLL. To może być przyczyna błędu, ale nie sądzę.

Musiałbyś dokładniej poszukać kiedy ten błąd występuje. O ile rozumiem wywołanie USART_SendChar działa poprawnie, a USART_Puts nie? Dziwna sprawa. Próbowałeś wysłać kilka znaków za pomocą USART_SendChar, np. wywołać funkcję kolejno dla literek z "Hello World!\n"?

Nie wkleiłeś kodu funkcji obsługi przerwania - może tam coś się psuje.

Jest jeszcze jedna możliwość, która tłumaczyłaby dziwne zachowanie - przepełnienie stosu. Domyślnie stos jest bardzo mały (256B o ile dobrze pamiętam, więc można go łatwo przepełnić. Efektem są właśnie programy, które działają, ale po drobnej zmianie przestają.

simoon87

20:13, 01.11.2015

#16

Elvis napisał/a:

Musiałbyś dokładniej poszukać kiedy ten błąd występuje. O ile rozumiem wywołanie USART_SendChar działa poprawnie, a USART_Puts nie? Dziwna sprawa. Próbowałeś wysłać kilka znaków za pomocą USART_SendChar, np. wywołać funkcję kolejno dla literek z "Hello World!\n"?

Nie, to jest tak, że działa tylko funkcja USART_SendData() która jest standardowo w pliku stm32f10x_usart.h ale ona wysyła bajt bez obsługi przerwania.

Nie działa ani USART_SendChar ani USART_Puts które ładują odpowiednio znak lub łańcuch do bufora cyklicznego a następnie wywołują przerwanie. To, że program w ogóle nie wchodzi do przerwania zdiagnozowałem w banalny sposób, na sam początku funkcji obsługi przerwania dodałem zasiedzenie diody. Niestety dioda się nie świeci. Dodatkowo w Debugu widać ze program wykonuje się OK do momentu w którym powinien wejść do przerwania i nagle umiera. Kod przerwanie poniżej:

void USART1_IRQHandler(void)

{

GPIO_WriteBit( LED_GPIO, ERR_LED, 0); //DEBUG

if( USART_GetITStatus(USART1, USART_IT_TXE) == SET )

{

// sprawdzamy czy indeksy są różne

if( Buffer.TxBeg != Buffer.TxEnd )

{

Buffer.TxEnd = (Buffer.TxEnd + 1) & TX_BUF_MASK; // obliczamy i zapamiętujemy nowy indeks konca (może się zrównać z poczatkiem)

USART1->DR = Buffer.TxBuf[Buffer.TxEnd]; // zwracamy bajt pobrany z bufora jako rezultat funkcji

}

else

{

USART_ITConfig(USART1, USART_IT_TXE, DISABLE); // wylacz przerwanie = koniec transmisji

}

}

if( USART_GetITStatus(USART1, USART_IT_TC) == SET ) //gdy bufor nadawczy pusty

{

SendMode(FALSE); // odbiornik załaczony && nadajnik wyłaczony

}

if( USART_GetITStatus(USART1, USART_IT_RXNE) == SET )

{

register uint8_t tmp_head;

register uint8_t data;

tmp_head = (Buffer.RxBeg + 1) & RX_BUF_MASK; // obliczamy nowy indeks poczatku

data = (uint8_t)(USART1->DR);

if( tmp_head == Buffer.RxEnd ) Buffer.RxBeg = Buffer.RxEnd; // sprawdzamy nadmisanie

else

{

switch( data )

{

case 0: break; // ignorujemy bajt = 0

case 10: break; // ignorujemy znak LF

case 13: Buffer.ascii_line++; // sygnalizujemy obecność kolejnej linii w buforze

default : Buffer.RxBeg = tmp_head; Buffer.RxBuf[tmp_head] = data;

}

}

}

}

Elvis napisał/a:

Jest jeszcze jedna możliwość, która tłumaczyłaby dziwne zachowanie - przepełnienie stosu.

Co do stosu to jestem sceptycznie nastawiony gdyż ten sam kod działa w Keil oraz ten program robi tylko 2 rzeczy miga diodą oraz wysyła jeden znak. Co miało by przepełniać stos?

plik main:

#include "SystemInit.h" //Konfiguracja systemu

int main(void)

{

RCC_Config();

GPIO_Config();

RS485_Configuration(9600, USART_Parity_Even);

if( SysTick_Config_Mod(SysTick_CLKSource_HCLK_Div8, 90000ul) ) while(1);

GPIO_WriteBit( LED_GPIO, ERR_LED, 1); //Wygaszenie diody ERROR

for(;;)

{

if(!SysTimer.SoftTimer[0]) //100ms

{

SysTimer.SoftTimer[0]=10;

GPIO_WriteBit( LED_GPIO, LIVE_LED, (BitAction)(1-GPIO_ReadOutputDataBit(LED_GPIO, LIVE_LED)) ); // XOR LIVE LED

}

if(!SysTimer.SoftTimer[1]) //500ms

{

SysTimer.SoftTimer[1]=50;

SendMode(TRUE);

// USART_SendData(USART1, 'X'); //Działa ale to jest wyslanie bez obsługi przerwania

// USART_SendChar(USART1, 'X'); //nie działa

USART_Puts( USART1, "X" );

}

}

return 0;

}

Edit: Bez inicjalizacji PLL wszystko bez zmian

Zobacz wszystkie komentarze (56) na forum

FORBOT Damian Szymański © 2006 - 2017 Zakaz kopiowania treści oraz grafik bez zgody autora. vPRsLH.

Radar z metamateriału to przyszłość dronów

Dokładne radary były dotychczas niedostępne dla małych dronów. Główną przeszkodą były...

Zamknij