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:
- static void RS485_InitRTSPin(void) {
- GPIO_InitTypeDef GPIO_InitStruct = {0};
- __HAL_RCC_GPIOB_CLK_ENABLE();
- GPIO_InitStruct.Pin = GPIO_PIN_5;
- GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
- HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
- }
Ustawienie pinu jako odpowiedni kierunek transmisji:
- static void RS485_SetControlPinToSendData(void) {
- RS485_RTS_PORT->BSRR = RS485_RTS_PIN;
- }
- static void RS485_SetControlPinToReceiveData(void) {
- RS485_RTS_PORT->BSRR = (uint32_t)RS485_RTS_PIN << 16U;
- }
RTS do wysłania danych musi być ustawiony wysoko. Aby odbierać dane linia musi ustawiona nisko.
Inicjalizacja interfejsu UART:
- static uint8_t RS485_USART2_Init(void) {
- huart2.Instance = USART2;
- huart2.Init.BaudRate = 9600;
- huart2.Init.WordLength = UART_WORDLENGTH_8B;
- huart2.Init.StopBits = UART_STOPBITS_1;
- huart2.Init.Parity = UART_PARITY_NONE;
- huart2.Init.Mode = UART_MODE_TX_RX;
- huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
- huart2.Init.OverSampling = UART_OVERSAMPLING_16;
- huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
- huart2.Init.ClockPrescaler = UART_PRESCALER_DIV1;
- huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
- if (HAL_UART_Init(&huart2) != HAL_OK) { return 0x01u; }
- if (HAL_UARTEx_SetTxFifoThreshold(&huart2, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK) { return 0x02u; }
- if (HAL_UARTEx_SetRxFifoThreshold(&huart2, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK) { return 0x03u; }
- if (HAL_UARTEx_DisableFifoMode(&huart2) != HAL_OK) { return 0x04u; }
- HAL_NVIC_DisableIRQ(USART2_IRQn);
- HAL_NVIC_SetPriority(USART2_IRQn, 0, 1);
- HAL_NVIC_EnableIRQ(USART2_IRQn);
- HAL_NVIC_ClearPendingIRQ(USART2_IRQn);
- __HAL_UART_ENABLE_IT(&huart2, UART_IT_RXNE);
- USART2->CR1 |= USART_CR1_RXNEIE;
- return 0x00u;
- }
Wysłanie danych:
- void RS485_SendData(uint8_t* buffer, uint8_t bufferLength) {
- RS485_SetControlPinToSendData();
- HAL_Delay(1);
- HAL_UART_Transmit(&huart2, (uint8_t*)buffer, bufferLength, 1000);
- HAL_Delay(1);
- RS485_SetControlPinToReceiveData();
- }
Odbieranie danych i zapisanie ich do buffora:
- void USART2_IRQHandler(void)
- {
- /* USER CODE BEGIN USART2_IRQn 0 */
- /* USER CODE END USART2_IRQn 0 */
- HAL_UART_IRQHandler(&huart2);
- /* USER CODE BEGIN USART2_IRQn 1 */
- RS485_GetDataToBuffer();
- /* USER CODE END USART2_IRQn 1 */
- }
- void RS485_GetDataToBuffer(void) {
- uint8_t dane = 0;
- dane = ((USART2)->RDR);
- RS485_SaveDataToBuffer(dane);
- RS485_ClearRXFlags();
- }
- static inline void RS485_SaveDataToBuffer(uint8_t recData) {
- RS485_Readers_Comm.startRecData = 1;
- RS485_Readers_Comm.recDataTimeout = 0;
- RS485_Readers_Comm.receiveData[RS485_Readers_Comm.recDataCounter] = recData;
- RS485_Readers_Comm.recDataCounter++;
- }
- static void RS485_ClearRXFlags(void) {
- #ifdef __HAL_UART_CLEAR_PEFLAG
- __HAL_UART_CLEAR_PEFLAG(&huart2);
- #endif
- #ifdef __HAL_UART_CLEAR_FEFLAG
- __HAL_UART_CLEAR_FEFLAG(&huart2);
- #endif
- #ifdef __HAL_UART_CLEAR_NEFLAG
- __HAL_UART_CLEAR_NEFLAG(&huart2);
- #endif
- #ifdef __HAL_UART_CLEAR_OREFLAG
- __HAL_UART_CLEAR_OREFLAG(&huart2);
- #endif
- #ifdef __HAL_UART_CLEAR_IDLEFLAG
- __HAL_UART_CLEAR_IDLEFLAG(&huart2);
- #endif
- HAL_NVIC_ClearPendingIRQ(USART2_IRQn);
- }
Odebrana ramka danych jest obrabiana dopiero po dopuszczalnym czasie oczekiwania na odpowiedź:
- void SysTick_Handler(void)
- {
- /* USER CODE BEGIN SysTick_IRQn 0 */
- /* USER CODE END SysTick_IRQn 0 */
- HAL_IncTick();
- /* USER CODE BEGIN SysTick_IRQn 1 */
- RS485_CountTimeout(&RS485_Readers_Comm);
- /* USER CODE END SysTick_IRQn 1 */
- }
- void RS485_CountTimeout(RS485_Data_t *RS485_Readers_CommPtr) {
- if(RS485_Readers_CommPtr->startRecData == 1) {
- RS485_Readers_CommPtr->recDataTimeout++;
- if(RS485_Readers_CommPtr->recDataTimeout == 100) {
- RS485_Readers_CommPtr->startRecData = 0;
- RS485_Readers_CommPtr->dataReceived = 1;
- }
- }
- }
Odliczanie czasu następuje poprzez obliczanie ilości wyzwolonych przerwań od SysTicka.