środa, 30 listopada 2016

[3] STM32F7 - Usart, zapis i odczyt danych

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:

  1. void INIT_UART_CLOCK(USART_TypeDef* USARTx)
  2. {
  3.     if(USARTx == USART1){
  4.         __HAL_RCC_USART1_CLK_ENABLE();
  5.         __HAL_RCC_USART1_FORCE_RESET();
  6.         __HAL_RCC_USART1_RELEASE_RESET();
  7.     }
  8.     else if(USARTx == USART2){
  9.         __HAL_RCC_USART2_CLK_ENABLE();
  10.         __HAL_RCC_USART2_FORCE_RESET();
  11.         __HAL_RCC_USART2_RELEASE_RESET();
  12.     }
  13.     else if(USARTx == USART3){
  14.         __HAL_RCC_USART3_CLK_ENABLE();
  15.         __HAL_RCC_USART3_FORCE_RESET();
  16.         __HAL_RCC_USART3_RELEASE_RESET();
  17.     }
  18.     else if(USARTx == UART4){
  19.         __HAL_RCC_UART4_CLK_ENABLE();
  20.         __HAL_RCC_UART4_FORCE_RESET();
  21.         __HAL_RCC_UART4_RELEASE_RESET();
  22.     }
  23.     else if(USARTx == UART5){
  24.         __HAL_RCC_UART5_CLK_ENABLE();
  25.         __HAL_RCC_UART5_FORCE_RESET();
  26.         __HAL_RCC_UART5_RELEASE_RESET();
  27.     }
  28.     else if(USARTx == USART6){
  29.         __HAL_RCC_USART6_CLK_ENABLE();
  30.         __HAL_RCC_USART6_FORCE_RESET();
  31.         __HAL_RCC_USART6_RELEASE_RESET();
  32.     }
  33.     else if(USARTx == UART7){
  34.         __HAL_RCC_UART7_CLK_ENABLE();
  35.         __HAL_RCC_UART7_FORCE_RESET();
  36.         __HAL_RCC_UART7_RELEASE_RESET();
  37.     }
  38.     else if(USARTx == UART8){
  39.         __HAL_RCC_UART8_CLK_ENABLE();
  40.         __HAL_RCC_UART8_FORCE_RESET();
  41.         __HAL_RCC_UART8_RELEASE_RESET();
  42.     }
  43. }

Teraz czas na inicjalizację, przedstawię dwie funkcje właściwie analogiczne:

  1. void INIT_USART1(void)
  2. {
  3.     GPIO_InitTypeDef gpio_init_structure;
  4.     PORT_CLOCK_ENABLE(GPIOA);
  5.     PORT_CLOCK_ENABLE(GPIOB);
  6.     // UART Clock Enable
  7.     INIT_UART_CLOCK(USART1);
  8.     // GPIO TX
  9.     gpio_init_structure.Pin = GPIO_PIN_9;
  10.     gpio_init_structure.Mode = GPIO_MODE_AF_PP;
  11.     gpio_init_structure.Speed = GPIO_SPEED_FAST;
  12.     gpio_init_structure.Pull = GPIO_PULLUP;
  13.     gpio_init_structure.Alternate = GPIO_AF7_USART1;
  14.     HAL_GPIO_Init(GPIOA, &gpio_init_structure);
  15.     // GPIO RX
  16.     gpio_init_structure.Pin = GPIO_PIN_7;
  17.     gpio_init_structure.Mode = GPIO_MODE_AF_PP;
  18.     gpio_init_structure.Alternate = GPIO_AF7_USART1;
  19.     HAL_GPIO_Init(GPIOB, &gpio_init_structure);
  20.     UART_Handle1.Instance = USART1;
  21.     UART_Handle1.Init.BaudRate     = 38200;
  22.     UART_Handle1.Init.WordLength   = UART_WORDLENGTH_8B;
  23.     UART_Handle1.Init.StopBits     = UART_STOPBITS_1;
  24.     UART_Handle1.Init.Parity       = UART_PARITY_NONE;
  25.     UART_Handle1.Init.HwFlowCtl    = UART_HWCONTROL_NONE;
  26.     UART_Handle1.Init.Mode         = UART_MODE_TX_RX;
  27.     UART_Handle1.Init.OverSampling = UART_OVERSAMPLING_8;
  28.     UART_Handle1.Init.OneBitSampling = UART_ONEBIT_SAMPLING_DISABLED;
  29.     UART_Handle1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  30.     HAL_UART_Init(&UART_Handle1);
  31.     ISR_USART1_Init();
  32. }
  33. //=============================================================
  34. void INIT_USART6(void)
  35. {
  36.     GPIO_InitTypeDef gpio_init_structure;
  37.     PORT_CLOCK_ENABLE(GPIOC);
  38.     INIT_UART_CLOCK(USART6);
  39.     gpio_init_structure.Pin = USART6_TX1;
  40.     gpio_init_structure.Mode = GPIO_MODE_AF_PP;
  41.     gpio_init_structure.Speed = GPIO_SPEED_FAST;
  42.     gpio_init_structure.Pull = GPIO_PULLUP;
  43.     gpio_init_structure.Alternate = GPIO_AF8_USART6;
  44.     HAL_GPIO_Init(USART6_GPIOC, &gpio_init_structure);
  45.     // GPIO RX
  46.     gpio_init_structure.Pin = USART6_RX1;
  47.     gpio_init_structure.Mode = GPIO_MODE_AF_PP;
  48.     gpio_init_structure.Alternate = GPIO_AF8_USART6;
  49.     HAL_GPIO_Init(USART6_GPIOC, &gpio_init_structure);
  50.     UART_Handle6.Instance = USART6;
  51.     UART_Handle6.Init.BaudRate     = 38200;
  52.     UART_Handle6.Init.WordLength   = UART_WORDLENGTH_8B;
  53.     UART_Handle6.Init.StopBits     = UART_STOPBITS_1;
  54.     UART_Handle6.Init.Parity       = UART_PARITY_NONE;
  55.     UART_Handle6.Init.HwFlowCtl    = UART_HWCONTROL_NONE;
  56.     UART_Handle6.Init.Mode         = UART_MODE_TX_RX;
  57.     UART_Handle6.Init.OverSampling = UART_OVERSAMPLING_8;
  58.     UART_Handle6.Init.OneBitSampling = UART_ONEBIT_SAMPLING_DISABLED;
  59.     UART_Handle6.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  60.     HAL_UART_Init(&UART_Handle6);
  61.     ISR_USART6_Init();
  62. }
  63. //Wlaczenie przerwan
  64. static void ISR_USART1_Init(void)
  65. {
  66.     __HAL_UART_ENABLE_IT(&UART_Handle1, UART_IT_RXNE);
  67.     HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
  68.     HAL_NVIC_EnableIRQ(USART1_IRQn);
  69. }
  70. static void ISR_USART6_Init(void)
  71. {
  72.     __HAL_UART_ENABLE_IT(&UART_Handle6, UART_IT_RXNE);
  73.     HAL_NVIC_SetPriority(USART6_IRQn, 0, 0);
  74.     HAL_NVIC_EnableIRQ(USART6_IRQn);
  75. }

Kolejnym elementem jest funkcja wyłączająca wybrany układ:

  1. void DEINIT_USART(USART_TypeDef* USARTx)
  2. {
  3.     if(USARTx == USART1){
  4.         __HAL_RCC_USART1_CLK_DISABLE();
  5.         HAL_UART_DeInit(&UART_Handle1);
  6.         __HAL_UART_DISABLE_IT(&UART_Handle1, UART_IT_RXNE);
  7.         HAL_NVIC_DisableIRQ(USART1_IRQn);
  8.     }
  9.     else if(USARTx == USART2){
  10.         __HAL_RCC_USART2_CLK_DISABLE();
  11.         HAL_UART_DeInit(&UART_Handle2);
  12.         __HAL_UART_DISABLE_IT(&UART_Handle2, UART_IT_RXNE);
  13.         HAL_NVIC_DisableIRQ(USART2_IRQn);
  14.     }
  15.     else if(USARTx == USART3){
  16.         __HAL_RCC_USART3_CLK_DISABLE();
  17.         HAL_UART_DeInit(&UART_Handle3);
  18.         __HAL_UART_DISABLE_IT(&UART_Handle3, UART_IT_RXNE);
  19.         HAL_NVIC_DisableIRQ(USART3_IRQn);
  20.     }
  21.     else if(USARTx == UART4){
  22.         __HAL_RCC_UART4_CLK_DISABLE();
  23.         HAL_UART_DeInit(&UART_Handle4);
  24.         __HAL_UART_DISABLE_IT(&UART_Handle4, UART_IT_RXNE);
  25.         HAL_NVIC_DisableIRQ(UART4_IRQn);
  26.     }
  27.     else if(USARTx == UART5){
  28.         __HAL_RCC_UART5_CLK_DISABLE();
  29.         HAL_UART_DeInit(&UART_Handle5);
  30.         __HAL_UART_DISABLE_IT(&UART_Handle5, UART_IT_RXNE);
  31.         HAL_NVIC_DisableIRQ(UART5_IRQn);
  32.     }
  33.     else if(USARTx == USART6){
  34.         __HAL_RCC_USART6_CLK_DISABLE();
  35.         HAL_UART_DeInit(&UART_Handle6);
  36.         __HAL_UART_DISABLE_IT(&UART_Handle6, UART_IT_RXNE);
  37.         HAL_NVIC_DisableIRQ(USART6_IRQn);
  38.     }
  39.     else if(USARTx == UART7){
  40.         __HAL_RCC_UART7_CLK_DISABLE();
  41.         HAL_UART_DeInit(&UART_Handle7);
  42.         __HAL_UART_DISABLE_IT(&UART_Handle7, UART_IT_RXNE);
  43.         HAL_NVIC_DisableIRQ(UART7_IRQn);
  44.     }
  45.     else if(USARTx == UART8){
  46.         __HAL_RCC_UART8_CLK_DISABLE();
  47.         HAL_UART_DeInit(&UART_Handle8);
  48.         __HAL_UART_DISABLE_IT(&UART_Handle8, UART_IT_RXNE);
  49.         HAL_NVIC_DisableIRQ(UART8_IRQn);
  50.     }
  51. }

Transmisja danych do komputera bądź innego urządzenia może być wykonana poprzez zdefiniowane funkcje w bibliotece:

  1. void UART_SENDBYTE(USART_TypeDef* USARTx, uint8_t data)
  2. {
  3.    if(USARTx == USART1){ HAL_UART_Transmit(&UART_Handle1, &data, 1, UART_TX_TIMEOUT); }
  4.    else if(USARTx == USART2){ HAL_UART_Transmit(&UART_Handle2, &data, 1, UART_TX_TIMEOUT); }
  5.    else if(USARTx == USART3){ HAL_UART_Transmit(&UART_Handle3, &data, 1, UART_TX_TIMEOUT); }
  6.    else if(USARTx == UART4){ HAL_UART_Transmit(&UART_Handle4, &data, 1, UART_TX_TIMEOUT); }
  7.    else if(USARTx == UART5){ HAL_UART_Transmit(&UART_Handle5, &data, 1, UART_TX_TIMEOUT); }
  8.    else if(USARTx == USART6){ HAL_UART_Transmit(&UART_Handle6, &data, 1, UART_TX_TIMEOUT); }
  9.    else if(USARTx == UART7){ HAL_UART_Transmit(&UART_Handle7, &data, 1, UART_TX_TIMEOUT); }
  10.    else if(USARTx == UART8){ HAL_UART_Transmit(&UART_Handle8, &data, 1, UART_TX_TIMEOUT); }
  11. }

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:

  1. static inline void USART_PUT_CHAR(USART_TypeDef* USARTx, volatile char c)
  2. {
  3.     //Sprawdz USART
  4.     if ((USARTx->CR1 & USART_CR1_UE))
  5.     {
  6.         //Czekaj az bufor bedzie oprozniony
  7.         while (!((USARTx)->ISR & USART_FLAG_TXE));
  8.         //wyslij dane
  9.         ((USARTx)->TDR = ((uint16_t)(& 0x01FF)));
  10.         //czekaj na oproznienie bufora
  11.         while (!((USARTx)->ISR & USART_FLAG_TXE));
  12.     }
  13. }

Przesłanie danych, które będą podane w postaci numerycznej. Przeprowadza ona konwersję na typ char dla liczb od 0 do 9:

  1. void USART_SEND_NUMBER(USART_TypeDef* USARTx, uint32_t x)
  2. {
  3.   //Tymczasowa tablica do przechowywania danych z konwersji
  4.   char conv_value[10];
  5.   int i = 0;
  6.   //Petla dokonujaca konwersji pomiedzy typem
  7.   do
  8.   {
  9.     conv_value[i++] = (char)(% 10) + '0';
  10.     x /= 10;
  11.   } while(x);
  12.   //Przeslanie danych
  13.   while(i) { UART_SENDBYTE(USARTx, (conv_value[--i])); }
  14. }

Poniższa funkcja pozwala na wysłanie danych w postaci ciągu znaków, na wybrany układ:

  1. void UART_SENDSTRING(USART_TypeDef* USARTx, char *ptr, UART_BYTEEND_t last_char)
  2. {
  3.   uint16_t length = 0;
  4.   //Sprawdzanie wielkosci danych
  5.   while (ptr[length] != '\0') { length++; }
  6.   //Wyslanie calego stringa
  7.   if(USARTx == USART1) {
  8.       HAL_UART_Transmit(&UART_Handle1, (uint8_t*)(ptr), length, UART_TX_TIMEOUT); }
  9.   else if(USARTx == USART2) {
  10.       HAL_UART_Transmit(&UART_Handle2, (uint8_t*)(ptr), length, UART_TX_TIMEOUT); }
  11.   else if(USARTx == USART3) {
  12.       HAL_UART_Transmit(&UART_Handle3, (uint8_t*)(ptr), length, UART_TX_TIMEOUT); }
  13.   else if(USARTx == UART4) {
  14.       HAL_UART_Transmit(&UART_Handle4, (uint8_t*)(ptr), length, UART_TX_TIMEOUT); }
  15.   else if(USARTx == UART5) {
  16.       HAL_UART_Transmit(&UART_Handle5, (uint8_t*)(ptr), length, UART_TX_TIMEOUT); }
  17.   else if(USARTx == USART6) {
  18.       HAL_UART_Transmit(&UART_Handle6, (uint8_t*)(ptr), length, UART_TX_TIMEOUT); }
  19.   else if(USARTx == UART7) {
  20.       HAL_UART_Transmit(&UART_Handle7, (uint8_t*)(ptr), length, UART_TX_TIMEOUT); }
  21.   else if(USARTx == UART8) {
  22.       HAL_UART_Transmit(&UART_Handle8, (uint8_t*)(ptr), length, UART_TX_TIMEOUT); }
  23.   //Wyslanie ostatniej linii
  24.   if(last_char==LF_CR)
  25.   {
  26.       UART_SENDBYTE(USARTx, d_LINEFEED);
  27.       UART_SENDBYTE(USARTx, d_CARIAGERETURN);
  28.   }
  29.   else if(last_char==CR_LF)
  30.   {
  31.       UART_SENDBYTE(USARTx, d_CARIAGERETURN);
  32.       UART_SENDBYTE(USARTx, d_LINEFEED);
  33.   }
  34.   else if(last_char==LF_)
  35.   {
  36.       UART_SENDBYTE(USARTx, d_LINEFEED);
  37.   }
  38.   else if(last_char==CR_)
  39.   {
  40.       UART_SENDBYTE(USARTx, d_CARIAGERETURN);
  41.   }
  42. }

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:

  1. void UART_SEND_ARRAY(USART_TypeDef* USARTx, uint8_t *data, uint16_t cnt)
  2. {
  3.   if(cnt==0) { return; }
  4.   if(USARTx==USART1)
  5.   { HAL_UART_Transmit(&UART_Handle1, data, cnt, UART_TX_TIMEOUT); }
  6.   else if(USARTx==USART6)
  7.   { HAL_UART_Transmit(&UART_Handle6, data, cnt, UART_TX_TIMEOUT); }
  8. }

Teraz czas na funkcje odbierające dane:

  1. uint8_t USART_RECEIVE_DATA(USART_TypeDef* USARTx, uint8_t char_number)
  2. {
  3.     static uint8_t received;
  4.     if(USARTx == USART1)
  5.     { HAL_UART_Receive_IT(&UART_Handle1, &received, char_number); }
  6.     else if(USARTx == USART6)
  7.     { HAL_UART_Receive_IT(&UART_Handle6, &received, char_number); }
  8.     return received;
  9. }

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.

  1. void USART2_IRQHandler(void)
  2. {
  3.     if (USART2->SR & USART_SR_RXNE)
  4.     {
  5.         USART_INT_InsertToBuffer(&COM_USART2, ((USART2)->DR));
  6.     }
  7.     /* Clear all USART flags */
  8.     USART_INT_ClearAllFlags(USART2, USART2_IRQn);
  9. }