środa, 9 czerwca 2021

STM32 - H723ZG - RS485

W tym poście opiszę sposób komunikacji przez interfejs RS485.

[Źródło: https://www.st.com/en/evaluation-tools/nucleo-h723zg.html]

Konwerter UART->RS485:


W projekcie wykorzystałem układ od ST348. Poniżej schemat podłączenia układu:


Program:


Inicjalizacja pinu sterującego kierunkiem transmisji:

  1. static void RS485_InitRTSPin(void) {
  2.     GPIO_InitTypeDef GPIO_InitStruct = {0};
  3.  
  4.     __HAL_RCC_GPIOB_CLK_ENABLE();
  5.  
  6.     GPIO_InitStruct.Pin = GPIO_PIN_5;
  7.     GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  8.     GPIO_InitStruct.Pull = GPIO_NOPULL;
  9.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  10.     HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  11. }

Ustawienie pinu jako odpowiedni kierunek transmisji:

  1. static void RS485_SetControlPinToSendData(void) {
  2.     RS485_RTS_PORT->BSRR = RS485_RTS_PIN;
  3. }
  4.  
  5. static void RS485_SetControlPinToReceiveData(void) {
  6.     RS485_RTS_PORT->BSRR = (uint32_t)RS485_RTS_PIN << 16U;
  7. }

RTS do wysłania danych musi być ustawiony wysoko. Aby odbierać dane linia musi ustawiona nisko. 

Inicjalizacja interfejsu UART:

  1. static uint8_t RS485_USART2_Init(void) {
  2.     huart2.Instance = USART2;
  3.     huart2.Init.BaudRate = 9600;
  4.     huart2.Init.WordLength = UART_WORDLENGTH_8B;
  5.     huart2.Init.StopBits = UART_STOPBITS_1;
  6.     huart2.Init.Parity = UART_PARITY_NONE;
  7.     huart2.Init.Mode = UART_MODE_TX_RX;
  8.     huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  9.     huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  10.     huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  11.     huart2.Init.ClockPrescaler = UART_PRESCALER_DIV1;
  12.     huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  13.  
  14.     if (HAL_UART_Init(&huart2) != HAL_OK) { return 0x01u;  }
  15.     if (HAL_UARTEx_SetTxFifoThreshold(&huart2, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK) { return 0x02u; }
  16.     if (HAL_UARTEx_SetRxFifoThreshold(&huart2, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK) { return 0x03u; }
  17.     if (HAL_UARTEx_DisableFifoMode(&huart2) != HAL_OK) { return 0x04u; }
  18.  
  19.     HAL_NVIC_DisableIRQ(USART2_IRQn);
  20.     HAL_NVIC_SetPriority(USART2_IRQn, 0, 1);
  21.     HAL_NVIC_EnableIRQ(USART2_IRQn);
  22.     HAL_NVIC_ClearPendingIRQ(USART2_IRQn);
  23.     __HAL_UART_ENABLE_IT(&huart2, UART_IT_RXNE);
  24.     USART2->CR1 |= USART_CR1_RXNEIE;
  25.  
  26.     return 0x00u;
  27. }

Wysłanie danych:

  1. void RS485_SendData(uint8_t* buffer, uint8_t bufferLength) {
  2.     RS485_SetControlPinToSendData();
  3.     HAL_Delay(1);
  4.     HAL_UART_Transmit(&huart2, (uint8_t*)buffer, bufferLength, 1000);
  5.     HAL_Delay(1);
  6.     RS485_SetControlPinToReceiveData();
  7. }

Odbieranie danych i zapisanie ich do buffora:

  1. void USART2_IRQHandler(void)
  2. {
  3.   /* USER CODE BEGIN USART2_IRQn 0 */
  4.  
  5.   /* USER CODE END USART2_IRQn 0 */
  6.   HAL_UART_IRQHandler(&huart2);
  7.   /* USER CODE BEGIN USART2_IRQn 1 */
  8.   RS485_GetDataToBuffer();
  9.   /* USER CODE END USART2_IRQn 1 */
  10. }
  11.  
  12. void RS485_GetDataToBuffer(void) {
  13.     uint8_t dane = 0;
  14.     dane = ((USART2)->RDR);
  15.     RS485_SaveDataToBuffer(dane);
  16.     RS485_ClearRXFlags();
  17. }
  18.  
  19. static inline void RS485_SaveDataToBuffer(uint8_t recData) {
  20.     RS485_Readers_Comm.startRecData = 1;
  21.     RS485_Readers_Comm.recDataTimeout = 0;
  22.     RS485_Readers_Comm.receiveData[RS485_Readers_Comm.recDataCounter] = recData;
  23.     RS485_Readers_Comm.recDataCounter++;
  24. }
  25.  
  26. static void RS485_ClearRXFlags(void) {
  27.     #ifdef __HAL_UART_CLEAR_PEFLAG
  28.         __HAL_UART_CLEAR_PEFLAG(&huart2);
  29.     #endif
  30.     #ifdef __HAL_UART_CLEAR_FEFLAG
  31.         __HAL_UART_CLEAR_FEFLAG(&huart2);
  32.     #endif
  33.     #ifdef __HAL_UART_CLEAR_NEFLAG
  34.         __HAL_UART_CLEAR_NEFLAG(&huart2);
  35.     #endif
  36.     #ifdef __HAL_UART_CLEAR_OREFLAG
  37.         __HAL_UART_CLEAR_OREFLAG(&huart2);
  38.     #endif
  39.     #ifdef __HAL_UART_CLEAR_IDLEFLAG
  40.         __HAL_UART_CLEAR_IDLEFLAG(&huart2);
  41.     #endif
  42.  
  43.     HAL_NVIC_ClearPendingIRQ(USART2_IRQn);
  44. }

Odebrana ramka danych jest obrabiana dopiero po dopuszczalnym czasie oczekiwania na odpowiedź:

  1. void SysTick_Handler(void)
  2. {
  3.   /* USER CODE BEGIN SysTick_IRQn 0 */
  4.  
  5.   /* USER CODE END SysTick_IRQn 0 */
  6.   HAL_IncTick();
  7.   /* USER CODE BEGIN SysTick_IRQn 1 */
  8.   RS485_CountTimeout(&RS485_Readers_Comm);
  9.   /* USER CODE END SysTick_IRQn 1 */
  10. }
  11.  
  12. void RS485_CountTimeout(RS485_Data_t *RS485_Readers_CommPtr) {
  13.     if(RS485_Readers_CommPtr->startRecData == 1) {
  14.         RS485_Readers_CommPtr->recDataTimeout++;
  15.  
  16.         if(RS485_Readers_CommPtr->recDataTimeout == 100) {
  17.             RS485_Readers_CommPtr->startRecData = 0;
  18.             RS485_Readers_CommPtr->dataReceived = 1;
  19.         }
  20.     }
  21. }

Odliczanie czasu następuje poprzez obliczanie ilości wyzwolonych przerwań od SysTicka.