Ten post chciałbym poświęcić na opisanie kilku funkcji pozwalających na dosyć efektywną pracę z USART'em na mikrokontrolerze STM32F7. Ja wykorzystywałem dwa z nich tzn. wbudowany do wejścia USB oraz ten podłączony pod linie PC6 i PC7.
Poniżej przedstawię tylko sposoby włączenia oraz opis potrzebnych i przydatnych funkcji:
Tak wiec włączenie poszczególnych zegarów dla wybranego USART'a:
Tak wiec włączenie poszczególnych zegarów dla wybranego USART'a:
- void INIT_UART_CLOCK(USART_TypeDef* USARTx)
- {
- if(USARTx == USART1){
- __HAL_RCC_USART1_CLK_ENABLE();
- __HAL_RCC_USART1_FORCE_RESET();
- __HAL_RCC_USART1_RELEASE_RESET();
- }
- else if(USARTx == USART2){
- __HAL_RCC_USART2_CLK_ENABLE();
- __HAL_RCC_USART2_FORCE_RESET();
- __HAL_RCC_USART2_RELEASE_RESET();
- }
- else if(USARTx == USART3){
- __HAL_RCC_USART3_CLK_ENABLE();
- __HAL_RCC_USART3_FORCE_RESET();
- __HAL_RCC_USART3_RELEASE_RESET();
- }
- else if(USARTx == UART4){
- __HAL_RCC_UART4_CLK_ENABLE();
- __HAL_RCC_UART4_FORCE_RESET();
- __HAL_RCC_UART4_RELEASE_RESET();
- }
- else if(USARTx == UART5){
- __HAL_RCC_UART5_CLK_ENABLE();
- __HAL_RCC_UART5_FORCE_RESET();
- __HAL_RCC_UART5_RELEASE_RESET();
- }
- else if(USARTx == USART6){
- __HAL_RCC_USART6_CLK_ENABLE();
- __HAL_RCC_USART6_FORCE_RESET();
- __HAL_RCC_USART6_RELEASE_RESET();
- }
- else if(USARTx == UART7){
- __HAL_RCC_UART7_CLK_ENABLE();
- __HAL_RCC_UART7_FORCE_RESET();
- __HAL_RCC_UART7_RELEASE_RESET();
- }
- else if(USARTx == UART8){
- __HAL_RCC_UART8_CLK_ENABLE();
- __HAL_RCC_UART8_FORCE_RESET();
- __HAL_RCC_UART8_RELEASE_RESET();
- }
- }
Teraz czas na inicjalizację, przedstawię dwie funkcje właściwie analogiczne:
- void INIT_USART1(void)
- {
- GPIO_InitTypeDef gpio_init_structure;
- PORT_CLOCK_ENABLE(GPIOA);
- PORT_CLOCK_ENABLE(GPIOB);
- // UART Clock Enable
- INIT_UART_CLOCK(USART1);
- // GPIO TX
- gpio_init_structure.Pin = GPIO_PIN_9;
- gpio_init_structure.Mode = GPIO_MODE_AF_PP;
- gpio_init_structure.Speed = GPIO_SPEED_FAST;
- gpio_init_structure.Pull = GPIO_PULLUP;
- gpio_init_structure.Alternate = GPIO_AF7_USART1;
- HAL_GPIO_Init(GPIOA, &gpio_init_structure);
- // GPIO RX
- gpio_init_structure.Pin = GPIO_PIN_7;
- gpio_init_structure.Mode = GPIO_MODE_AF_PP;
- gpio_init_structure.Alternate = GPIO_AF7_USART1;
- HAL_GPIO_Init(GPIOB, &gpio_init_structure);
- UART_Handle1.Instance = USART1;
- UART_Handle1.Init.BaudRate = 38200;
- UART_Handle1.Init.WordLength = UART_WORDLENGTH_8B;
- UART_Handle1.Init.StopBits = UART_STOPBITS_1;
- UART_Handle1.Init.Parity = UART_PARITY_NONE;
- UART_Handle1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
- UART_Handle1.Init.Mode = UART_MODE_TX_RX;
- UART_Handle1.Init.OverSampling = UART_OVERSAMPLING_8;
- UART_Handle1.Init.OneBitSampling = UART_ONEBIT_SAMPLING_DISABLED;
- UART_Handle1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
- HAL_UART_Init(&UART_Handle1);
- ISR_USART1_Init();
- }
- //=============================================================
- void INIT_USART6(void)
- {
- GPIO_InitTypeDef gpio_init_structure;
- PORT_CLOCK_ENABLE(GPIOC);
- INIT_UART_CLOCK(USART6);
- gpio_init_structure.Pin = USART6_TX1;
- gpio_init_structure.Mode = GPIO_MODE_AF_PP;
- gpio_init_structure.Speed = GPIO_SPEED_FAST;
- gpio_init_structure.Pull = GPIO_PULLUP;
- gpio_init_structure.Alternate = GPIO_AF8_USART6;
- HAL_GPIO_Init(USART6_GPIOC, &gpio_init_structure);
- // GPIO RX
- gpio_init_structure.Pin = USART6_RX1;
- gpio_init_structure.Mode = GPIO_MODE_AF_PP;
- gpio_init_structure.Alternate = GPIO_AF8_USART6;
- HAL_GPIO_Init(USART6_GPIOC, &gpio_init_structure);
- UART_Handle6.Instance = USART6;
- UART_Handle6.Init.BaudRate = 38200;
- UART_Handle6.Init.WordLength = UART_WORDLENGTH_8B;
- UART_Handle6.Init.StopBits = UART_STOPBITS_1;
- UART_Handle6.Init.Parity = UART_PARITY_NONE;
- UART_Handle6.Init.HwFlowCtl = UART_HWCONTROL_NONE;
- UART_Handle6.Init.Mode = UART_MODE_TX_RX;
- UART_Handle6.Init.OverSampling = UART_OVERSAMPLING_8;
- UART_Handle6.Init.OneBitSampling = UART_ONEBIT_SAMPLING_DISABLED;
- UART_Handle6.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
- HAL_UART_Init(&UART_Handle6);
- ISR_USART6_Init();
- }
- //Wlaczenie przerwan
- static void ISR_USART1_Init(void)
- {
- __HAL_UART_ENABLE_IT(&UART_Handle1, UART_IT_RXNE);
- HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
- HAL_NVIC_EnableIRQ(USART1_IRQn);
- }
- static void ISR_USART6_Init(void)
- {
- __HAL_UART_ENABLE_IT(&UART_Handle6, UART_IT_RXNE);
- HAL_NVIC_SetPriority(USART6_IRQn, 0, 0);
- HAL_NVIC_EnableIRQ(USART6_IRQn);
- }
Kolejnym elementem jest funkcja wyłączająca wybrany układ:
- void DEINIT_USART(USART_TypeDef* USARTx)
- {
- if(USARTx == USART1){
- __HAL_RCC_USART1_CLK_DISABLE();
- HAL_UART_DeInit(&UART_Handle1);
- __HAL_UART_DISABLE_IT(&UART_Handle1, UART_IT_RXNE);
- HAL_NVIC_DisableIRQ(USART1_IRQn);
- }
- else if(USARTx == USART2){
- __HAL_RCC_USART2_CLK_DISABLE();
- HAL_UART_DeInit(&UART_Handle2);
- __HAL_UART_DISABLE_IT(&UART_Handle2, UART_IT_RXNE);
- HAL_NVIC_DisableIRQ(USART2_IRQn);
- }
- else if(USARTx == USART3){
- __HAL_RCC_USART3_CLK_DISABLE();
- HAL_UART_DeInit(&UART_Handle3);
- __HAL_UART_DISABLE_IT(&UART_Handle3, UART_IT_RXNE);
- HAL_NVIC_DisableIRQ(USART3_IRQn);
- }
- else if(USARTx == UART4){
- __HAL_RCC_UART4_CLK_DISABLE();
- HAL_UART_DeInit(&UART_Handle4);
- __HAL_UART_DISABLE_IT(&UART_Handle4, UART_IT_RXNE);
- HAL_NVIC_DisableIRQ(UART4_IRQn);
- }
- else if(USARTx == UART5){
- __HAL_RCC_UART5_CLK_DISABLE();
- HAL_UART_DeInit(&UART_Handle5);
- __HAL_UART_DISABLE_IT(&UART_Handle5, UART_IT_RXNE);
- HAL_NVIC_DisableIRQ(UART5_IRQn);
- }
- else if(USARTx == USART6){
- __HAL_RCC_USART6_CLK_DISABLE();
- HAL_UART_DeInit(&UART_Handle6);
- __HAL_UART_DISABLE_IT(&UART_Handle6, UART_IT_RXNE);
- HAL_NVIC_DisableIRQ(USART6_IRQn);
- }
- else if(USARTx == UART7){
- __HAL_RCC_UART7_CLK_DISABLE();
- HAL_UART_DeInit(&UART_Handle7);
- __HAL_UART_DISABLE_IT(&UART_Handle7, UART_IT_RXNE);
- HAL_NVIC_DisableIRQ(UART7_IRQn);
- }
- else if(USARTx == UART8){
- __HAL_RCC_UART8_CLK_DISABLE();
- HAL_UART_DeInit(&UART_Handle8);
- __HAL_UART_DISABLE_IT(&UART_Handle8, UART_IT_RXNE);
- HAL_NVIC_DisableIRQ(UART8_IRQn);
- }
- }
Transmisja danych do komputera bądź innego urządzenia może być wykonana poprzez zdefiniowane funkcje w bibliotece:
- void UART_SENDBYTE(USART_TypeDef* USARTx, uint8_t data)
- {
- if(USARTx == USART1){ HAL_UART_Transmit(&UART_Handle1, &data, 1, UART_TX_TIMEOUT); }
- else if(USARTx == USART2){ HAL_UART_Transmit(&UART_Handle2, &data, 1, UART_TX_TIMEOUT); }
- else if(USARTx == USART3){ HAL_UART_Transmit(&UART_Handle3, &data, 1, UART_TX_TIMEOUT); }
- else if(USARTx == UART4){ HAL_UART_Transmit(&UART_Handle4, &data, 1, UART_TX_TIMEOUT); }
- else if(USARTx == UART5){ HAL_UART_Transmit(&UART_Handle5, &data, 1, UART_TX_TIMEOUT); }
- else if(USARTx == USART6){ HAL_UART_Transmit(&UART_Handle6, &data, 1, UART_TX_TIMEOUT); }
- else if(USARTx == UART7){ HAL_UART_Transmit(&UART_Handle7, &data, 1, UART_TX_TIMEOUT); }
- else if(USARTx == UART8){ HAL_UART_Transmit(&UART_Handle8, &data, 1, UART_TX_TIMEOUT); }
- }
Lub za pomocą oddzielnie przygotowanej funkcji obsługującej przesyłanie danych niskopoziomowo, którą można zdefiniować jako statyczną oraz inline. Dzięki temu uzyska się znaczne przyśpieszenie jej wykonywania w programie. Można by było zdefiniować ją jako define, natomiast takie rozwiązanie zapewnia większą kontrolę nad przesyłaniem tych danych:
- static inline void USART_PUT_CHAR(USART_TypeDef* USARTx, volatile char c)
- {
- //Sprawdz USART
- if ((USARTx->CR1 & USART_CR1_UE))
- {
- //Czekaj az bufor bedzie oprozniony
- while (!((USARTx)->ISR & USART_FLAG_TXE));
- //wyslij dane
- ((USARTx)->TDR = ((uint16_t)(c & 0x01FF)));
- //czekaj na oproznienie bufora
- while (!((USARTx)->ISR & USART_FLAG_TXE));
- }
- }
Przesłanie danych, które będą podane w postaci numerycznej. Przeprowadza ona konwersję na typ char dla liczb od 0 do 9:
- void USART_SEND_NUMBER(USART_TypeDef* USARTx, uint32_t x)
- {
- //Tymczasowa tablica do przechowywania danych z konwersji
- char conv_value[10];
- int i = 0;
- //Petla dokonujaca konwersji pomiedzy typem
- do
- {
- conv_value[i++] = (char)(x % 10) + '0';
- x /= 10;
- } while(x);
- //Przeslanie danych
- while(i) { UART_SENDBYTE(USARTx, (conv_value[--i])); }
- }
Poniższa funkcja pozwala na wysłanie danych w postaci ciągu znaków, na wybrany układ:
- void UART_SENDSTRING(USART_TypeDef* USARTx, char *ptr, UART_BYTEEND_t last_char)
- {
- uint16_t length = 0;
- //Sprawdzanie wielkosci danych
- while (ptr[length] != '\0') { length++; }
- //Wyslanie calego stringa
- if(USARTx == USART1) {
- HAL_UART_Transmit(&UART_Handle1, (uint8_t*)(ptr), length, UART_TX_TIMEOUT); }
- else if(USARTx == USART2) {
- HAL_UART_Transmit(&UART_Handle2, (uint8_t*)(ptr), length, UART_TX_TIMEOUT); }
- else if(USARTx == USART3) {
- HAL_UART_Transmit(&UART_Handle3, (uint8_t*)(ptr), length, UART_TX_TIMEOUT); }
- else if(USARTx == UART4) {
- HAL_UART_Transmit(&UART_Handle4, (uint8_t*)(ptr), length, UART_TX_TIMEOUT); }
- else if(USARTx == UART5) {
- HAL_UART_Transmit(&UART_Handle5, (uint8_t*)(ptr), length, UART_TX_TIMEOUT); }
- else if(USARTx == USART6) {
- HAL_UART_Transmit(&UART_Handle6, (uint8_t*)(ptr), length, UART_TX_TIMEOUT); }
- else if(USARTx == UART7) {
- HAL_UART_Transmit(&UART_Handle7, (uint8_t*)(ptr), length, UART_TX_TIMEOUT); }
- else if(USARTx == UART8) {
- HAL_UART_Transmit(&UART_Handle8, (uint8_t*)(ptr), length, UART_TX_TIMEOUT); }
- //Wyslanie ostatniej linii
- if(last_char==LF_CR)
- {
- UART_SENDBYTE(USARTx, d_LINEFEED);
- UART_SENDBYTE(USARTx, d_CARIAGERETURN);
- }
- else if(last_char==CR_LF)
- {
- UART_SENDBYTE(USARTx, d_CARIAGERETURN);
- UART_SENDBYTE(USARTx, d_LINEFEED);
- }
- else if(last_char==LF_)
- {
- UART_SENDBYTE(USARTx, d_LINEFEED);
- }
- else if(last_char==CR_)
- {
- UART_SENDBYTE(USARTx, d_CARIAGERETURN);
- }
- }
Znaki wprowadzone na końcu linii zostały zdefiniowane jako #define z odpowiednią wartością z kodu ASCII.
Kolejne funkcje będą już okrojone tylko do dwóch USARTów, natomiast przerobienie ich na pozostałe nie powinno być żadnym problemem:
- void UART_SEND_ARRAY(USART_TypeDef* USARTx, uint8_t *data, uint16_t cnt)
- {
- if(cnt==0) { return; }
- if(USARTx==USART1)
- { HAL_UART_Transmit(&UART_Handle1, data, cnt, UART_TX_TIMEOUT); }
- else if(USARTx==USART6)
- { HAL_UART_Transmit(&UART_Handle6, data, cnt, UART_TX_TIMEOUT); }
- }
Teraz czas na funkcje odbierające dane:
- uint8_t USART_RECEIVE_DATA(USART_TypeDef* USARTx, uint8_t char_number)
- {
- static uint8_t received;
- if(USARTx == USART1)
- { HAL_UART_Receive_IT(&UART_Handle1, &received, char_number); }
- else if(USARTx == USART6)
- { HAL_UART_Receive_IT(&UART_Handle6, &received, char_number); }
- return received;
- }
Co do obsługi przerwania to w sprawdzane jest w nim czy dane zostały odebrane, po czym są one wprowadzane do bufora danych. Funkcje dla bufora pobrałem ze strony stm32f4-discovery.net.
- void USART2_IRQHandler(void)
- {
- if (USART2->SR & USART_SR_RXNE)
- {
- USART_INT_InsertToBuffer(&COM_USART2, ((USART2)->DR));
- }
- /* Clear all USART flags */
- USART_INT_ClearAllFlags(USART2, USART2_IRQn);
- }