środa, 12 lipca 2017

[16] STM32F7 - RS485

Ten bardzo krótki post chciałbym poświęcić na opisanie sposobu komunikacji za pomocą RS485 przygotowanego w oparciu o UART7 na płytce STM32F7 - Discovery. Komunikacja będzie wykonana za pomocą układu ST3485EB. 

Wykorzystywane będą trzy linie, jedna nadająca, druga odbierająca, trzecia będzie służyła do wyboru trybu pracy, tzn czy będzie występowało nadanie czy odbieranie.

Wysyłanie i odbieranie danych będzie przebiegać w standardowy sposób. Jedyna różnica jaka dochodzi to sterowanie pinem określającym kierunek przepływu danych. Domyślnie aby układ mógł odebrać dane to ten pin musi być utrzymywany w stanie niskim. Aby wysłać dane do układu to należy ustawić go w stan wysoki, a potem zaraz po ich wysłaniu zmienić stan na niski.

CubeMx:


Tutaj wprowadzane będą podstawowe ustawienia, jeden pin będzie wykorzystywany jako wyjściowy, na którym będzie wykonywane sterowanie (użyłem dowolnego pinu do sterowania). Do tego celu posłużyłem się pinem GPIOF 10.

Drugą rzeczą do włączenia jest UART (ja wykorzystałem UART7, można użyć dowolnego). Wprowadzam go w standardowe ustawienia(Baud rate: 115200, Data bits: 8, Parity: none, Stop Bits 1. Handshaking: none).

Program:


Program jest dosyć prosty. Jeśli nie wykonywało się inicjalizacji za pomocą CubeMx to procedura ustawienia UART-a wygląda następująco:

  1. void Initialization_Uart7(uint32_t baudRate)
  2. {
  3.     GPIO_InitTypeDef gpio_init_structure;
  4.     PORT_CLOCK_ENABLE(USART7_GPIOF);
  5.     Uart_Usart_Initialize_Clock(UART7);
  6.     __HAL_RCC_UART7_CLK_ENABLE();
  7.     __HAL_RCC_UART7_FORCE_RESET();
  8.     __HAL_RCC_UART7_RELEASE_RESET();
  9.     gpio_init_structure.Pin         = USART7_TX2;
  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_AF8_UART7;
  14.     HAL_GPIO_Init(USART7_GPIOF, &gpio_init_structure);
  15.     // GPIO RX
  16.     gpio_init_structure.Pin         = USART7_RX2;
  17.     gpio_init_structure.Mode        = GPIO_MODE_AF_PP;
  18.     gpio_init_structure.Pull        = GPIO_PULLUP;
  19.     gpio_init_structure.Alternate   = GPIO_AF8_UART7;
  20.     HAL_GPIO_Init(USART7_GPIOF, &gpio_init_structure);
  21.     #if UART_7_RS485==1
  22.     /* Define initialize communication pin for RS485 */
  23.     PORT_CLOCK_ENABLE(GPIOF);
  24.     gpio_init_structure.Pin = GPIO_PIN_10;
  25.     gpio_init_structure.Mode = GPIO_MODE_OUTPUT_PP;
  26.     gpio_init_structure.Pull = GPIO_NOPULL;
  27.     gpio_init_structure.Speed = GPIO_SPEED_HIGH;
  28.     HAL_GPIO_Init(GPIOF, &gpio_init_structure);
  29.     RS485_LOW_REC_DATA();
  30.     #endif
  31.     UART_Handle7.Instance                       = UART7;
  32.     UART_Handle7.Init.BaudRate                  = USART7_BAUD;
  33.     UART_Handle7.Init.WordLength                = UART_WORDLENGTH_8B;
  34.     UART_Handle7.Init.StopBits                  = UART_STOPBITS_1;
  35.     UART_Handle7.Init.Parity                    = UART_PARITY_NONE;
  36.     UART_Handle7.Init.HwFlowCtl                 = UART_HWCONTROL_NONE;
  37.     UART_Handle7.Init.Mode                      = UART_MODE_TX_RX;
  38.     UART_Handle7.Init.OverSampling              = UART_OVERSAMPLING_8;
  39.     UART_Handle7.Init.OneBitSampling            = UART_ONEBIT_SAMPLING_DISABLED;
  40.     UART_Handle7.AdvancedInit.AdvFeatureInit    = UART_ADVFEATURE_NO_INIT;
  41.     HAL_UART_Init(&UART_Handle7);
  42.     HAL_NVIC_DisableIRQ(UART7_IRQn);
  43.     HAL_NVIC_SetPriority(UART7_IRQn, 0, 1);
  44.     HAL_NVIC_EnableIRQ(UART7_IRQn);
  45.     HAL_NVIC_ClearPendingIRQ(UART7_IRQn);
  46.     __HAL_UART_ENABLE_IT(&UART_Handle7, UART_IT_RXNE);
  47.     UART7->CR1 |= USART_CR1_RXNEIE;
  48. }

Przesyłanie danych wygląda następująco:

  1. void Usart_Uart_SendString(USART_TypeDef* USARTx, char *ptr, UART_BYTEEND_t last_char)
  2. {
  3.   uint16_t length = 0;
  4.   /* check data length */
  5.   while (ptr[length] != '\0')   {   length++;   }
  6.   if(USARTx == UART7) {
  7.       #if UART_7_RS485==1
  8.       RS485_HIGH_SEND_DATA();
  9.       HAL_Delay(2);
  10.       #endif
  11.       for(uint8_t i = 0; i<length; i++) {
  12.           Usart_Uart_SendByte(USARTx, ptr[i]);
  13.       }
  14.   }
  15.   /* Send last line */
  16.   if(last_char==LF_CR){
  17.       Usart_Uart_SendByte(USARTx, d_LINEFEED);
  18.       Usart_Uart_SendByte(USARTx, d_CARIAGERETURN);
  19.   }
  20.   else if(last_char==CR_LF){
  21.       Usart_Uart_SendByte(USARTx, d_CARIAGERETURN);
  22.       Usart_Uart_SendByte(USARTx, d_LINEFEED);
  23.   }
  24.   else if(last_char==LF_){
  25.       Usart_Uart_SendByte(USARTx, d_LINEFEED);
  26.   }
  27.   else if(last_char==CR_){
  28.       Usart_Uart_SendByte(USARTx, d_CARIAGERETURN);
  29.   }
  30.   #if UART_7_RS485==1
  31.   HAL_Delay(5);
  32.   RS485_LOW_REC_DATA();
  33.   #endif
  34. }

Funkcja wysyłająca dane do układu po jednym bajcie:

  1. static inline void Usart_Uart_SendByte(USART_TypeDef* USARTx, volatile char c)
  2. {
  3.     if ((USARTx->CR1 & USART_CR1_UE))
  4.     {
  5.         while (!((USARTx)->ISR & USART_FLAG_TXE));
  6.         /* Send data */
  7.         (USARTx)->TDR = (uint16_t)(& 0x01FF);
  8.         while (!((USARTx)->ISR & USART_FLAG_TXE));
  9.     }
  10. }

Obsługa przerwania wygląda następująco:

  1. void UART7_IRQHandler(void)
  2. {
  3.     static uint8_t dane;
  4.     //Sprawdzenie czy przerwanie wywolane przez odebranie danych
  5.     if (UART7->ISR & USART_ISR_RXNE){
  6.         dane = ((UART7)->RDR);
  7.         /* All data go to USART1*/
  8.         Usart_Uart_SendByte(USART1, dane);
  9.     }
  10.     // Clear all USART flags
  11.     #ifdef __HAL_UART_CLEAR_PEFLAG
  12.         __HAL_UART_CLEAR_PEFLAG(&UART_Handle7);
  13.     #endif
  14.     #ifdef __HAL_UART_CLEAR_FEFLAG
  15.         __HAL_UART_CLEAR_FEFLAG(&UART_Handle7);
  16.     #endif
  17.     #ifdef __HAL_UART_CLEAR_NEFLAG
  18.         __HAL_UART_CLEAR_NEFLAG(&UART_Handle7);
  19.     #endif
  20.     #ifdef __HAL_UART_CLEAR_OREFLAG
  21.         __HAL_UART_CLEAR_OREFLAG(&UART_Handle7);
  22.     #endif
  23.     #ifdef __HAL_UART_CLEAR_IDLEFLAG
  24.         __HAL_UART_CLEAR_IDLEFLAG(&UART_Handle7);
  25.     #endif
  26.         HAL_NVIC_ClearPendingIRQ(UART7_IRQn);
  27. }

Dane odebrane wypisywane są na USART1.