Dawno, dawno temu każdy szanujący się komputer PC był wyposażony z złącze RS-232C. Teraz to już historia, złącze szeregowe zostało zastąpione przez USB, ale w przypadku mikrokontrolerów starsze rozwiązanie nadal ma się całkiem dobrze. Powód jest oczywisty - prostota.
Można używać USB do komunikacji, ale nawet podstawowe przesłanie prostego komunikatu wymaga napisania znacznej ilości kodu. Ewentualnie użycia biblioteki, która na ogół jest droga, albo pełna błędów (czasem jedno i drugie).
Użycie portu szeregowego jest banalnie proste - chociaż komputery, czy laptopy go nie obsługują, ale od czego są konwertery UART-USB.
W przypadku płytki stm32f429-disc konieczny był właśnie taki konwerter, nowsza wersja stm32f429-disc1 ma już odpowiedni konwerter wbudowany.
Niezależnie od wersji trzeba odpowiednio skonfigurować piny przeznaczone do transmisji.
Jak widać chodzi o linie PA9 oraz PA10. Tym razem obie linie mają być połączone z modułem peryferyjnym. Oznacza to ustawienie trybu "10" w rejestrze MODER oraz wybór układu peryferyjnego za pomocą AFR.
W rejestrze MODER wystarczy zapalić wyższe bity dla odpowiednich linii:
GPIOA->MODER |= GPIO_MODER_MODE9_1|GPIO_MODER_MODE10_1;
Z rejestrzem AFR jest nieco trudniej. Piny mogą mieć przypisaną jedną z maksymalnie 16 funkcji - oznacza to 4 bity na każdy pin. Ponieważ pinów jest 16 w każdym porcie, do konfiguracji niezbędne są 64 bity. Oznacza to że jeden rejestr AFR nie wystarczy. Dlatego w dokumentacji znajdziemy rejestry AFRL oraz AFRH. Co ciekawe w pliku stm32f429xx.h te rejestry są zdefinowane jako tablica AFR[2].
Numery dostępnych modułów, które można wybierać za pomocą AFR znajdziemy w dokumentacji mikrokontrolera:
Jak widać chcemy użyć funkcji AF7 zarówno dla PA9 i PA10. Można więc napisać:
GPIOA->AFR[1] |= GPIO_AFRH_AFSEL9_0 | GPIO_AFRH_AFSEL9_1 | GPIO_AFRH_AFSEL9_2;
GPIOA->AFR[1] |= GPIO_AFRH_AFSEL10_0 | GPIO_AFRH_AFSEL10_1 | GPIO_AFRH_AFSEL10_2;
Dalej jest już tylko łatwiej. Jak zwykle musimy pamiętać o uruchomieniu taktowania modułu UART1:
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
Konfiguracja sprowadza się do ustawienia prędkości komunikacji:
USART1->BRR = 16000000uL / 115200;
A następnie włączenia nadajnika i odbiornika:
USART1->CR1 |= USART_CR1_TE | USART_CR1_RE;
Oraz samego modułu uart:
USART1->CR1 |= USART_CR1_UE;
Tutaj natrafiłem na pewną niespodziankę - jeśli zapalenie bitu UE następuje razem z konfiguracją pozostałych bitów, przed pierwszym wysłanym znakiem pojawiają się "śmieci". Konieczne okazało się dodanie pewnego opóźnienia - niestety nie wiem dlaczego.
Wysyłanie polega na wpisaniu wartości do rejestru UART1->DR, czyli:
USART1->DR = value;
Oczywiście przed zapisem należy upewnić się, że rejestr jest wolny. Rejestr statusu UART1->SR zawiera flagę TXE, która jest ustawiana gdy rejestr nadawania jest pusty. Należy więc przed wysłaniem poczekać na zapalenie tej flagi.
Cały program wysyłający komunikaty przez port szeregowy wygląda następująco:
Można używać USB do komunikacji, ale nawet podstawowe przesłanie prostego komunikatu wymaga napisania znacznej ilości kodu. Ewentualnie użycia biblioteki, która na ogół jest droga, albo pełna błędów (czasem jedno i drugie).
Użycie portu szeregowego jest banalnie proste - chociaż komputery, czy laptopy go nie obsługują, ale od czego są konwertery UART-USB.
W przypadku płytki stm32f429-disc konieczny był właśnie taki konwerter, nowsza wersja stm32f429-disc1 ma już odpowiedni konwerter wbudowany.
Niezależnie od wersji trzeba odpowiednio skonfigurować piny przeznaczone do transmisji.
Jak widać chodzi o linie PA9 oraz PA10. Tym razem obie linie mają być połączone z modułem peryferyjnym. Oznacza to ustawienie trybu "10" w rejestrze MODER oraz wybór układu peryferyjnego za pomocą AFR.
W rejestrze MODER wystarczy zapalić wyższe bity dla odpowiednich linii:
GPIOA->MODER |= GPIO_MODER_MODE9_1|GPIO_MODER_MODE10_1;
Z rejestrzem AFR jest nieco trudniej. Piny mogą mieć przypisaną jedną z maksymalnie 16 funkcji - oznacza to 4 bity na każdy pin. Ponieważ pinów jest 16 w każdym porcie, do konfiguracji niezbędne są 64 bity. Oznacza to że jeden rejestr AFR nie wystarczy. Dlatego w dokumentacji znajdziemy rejestry AFRL oraz AFRH. Co ciekawe w pliku stm32f429xx.h te rejestry są zdefinowane jako tablica AFR[2].
Numery dostępnych modułów, które można wybierać za pomocą AFR znajdziemy w dokumentacji mikrokontrolera:
Jak widać chcemy użyć funkcji AF7 zarówno dla PA9 i PA10. Można więc napisać:
GPIOA->AFR[1] |= GPIO_AFRH_AFSEL9_0 | GPIO_AFRH_AFSEL9_1 | GPIO_AFRH_AFSEL9_2;
GPIOA->AFR[1] |= GPIO_AFRH_AFSEL10_0 | GPIO_AFRH_AFSEL10_1 | GPIO_AFRH_AFSEL10_2;
Dalej jest już tylko łatwiej. Jak zwykle musimy pamiętać o uruchomieniu taktowania modułu UART1:
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
Konfiguracja sprowadza się do ustawienia prędkości komunikacji:
USART1->BRR = 16000000uL / 115200;
A następnie włączenia nadajnika i odbiornika:
USART1->CR1 |= USART_CR1_TE | USART_CR1_RE;
Oraz samego modułu uart:
USART1->CR1 |= USART_CR1_UE;
Tutaj natrafiłem na pewną niespodziankę - jeśli zapalenie bitu UE następuje razem z konfiguracją pozostałych bitów, przed pierwszym wysłanym znakiem pojawiają się "śmieci". Konieczne okazało się dodanie pewnego opóźnienia - niestety nie wiem dlaczego.
Wysyłanie polega na wpisaniu wartości do rejestru UART1->DR, czyli:
USART1->DR = value;
Oczywiście przed zapisem należy upewnić się, że rejestr jest wolny. Rejestr statusu UART1->SR zawiera flagę TXE, która jest ustawiana gdy rejestr nadawania jest pusty. Należy więc przed wysłaniem poczekać na zapalenie tej flagi.
Cały program wysyłający komunikaty przez port szeregowy wygląda następująco:
#include <stdint.h> #include <stdbool.h> #include "stm32f429xx.h" volatile uint32_t ms_counter = 0; void SysTick_Handler(void) { if (ms_counter) ms_counter--; } void delay_ms(uint32_t ms) { ms_counter = ms; while (ms_counter) ; } void usart_send(const char *s) { while (*s) { while ((USART1->SR & USART_SR_TXE) == 0); USART1->DR = *s++; } } int main(int argc, char *argv[]) { RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOGEN; RCC->APB2ENR |= RCC_APB2ENR_USART1EN; GPIOG->MODER |= GPIO_MODER_MODE13_0|GPIO_MODER_MODE14_0; GPIOA->MODER |= GPIO_MODER_MODE9_1|GPIO_MODER_MODE10_1; GPIOA->AFR[1] |= GPIO_AFRH_AFSEL9_0 | GPIO_AFRH_AFSEL9_1 | GPIO_AFRH_AFSEL9_2; GPIOA->AFR[1] |= GPIO_AFRH_AFSEL10_0 | GPIO_AFRH_AFSEL10_1 | GPIO_AFRH_AFSEL10_2; SysTick->LOAD = 16000-1; SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; USART1->BRR = 16000000uL / 115200; USART1->CR1 |= USART_CR1_TE | USART_CR1_RE; delay_ms(1); USART1->CR1 |= USART_CR1_UE; usart_send("Hello world!\r\n"); while (1) { GPIOG->BSRR = GPIO_BSRR_BS13; GPIOG->BSRR = GPIO_BSRR_BR14; delay_ms(500); GPIOG->BSRR = GPIO_BSRR_BS14; GPIOG->BSRR = GPIO_BSRR_BR13; delay_ms(500); usart_send("stm32f429\r\n"); } return 0; }
Brak komentarzy:
Prześlij komentarz