W tym poście chciałbym opisać komunikację po RS485.
[Źródło: https://www.st.com/content/st_com/en/products/evaluation-tools/product-evaluation-tools/mcu-mpu-eval-tools/stm32-mcu-mpu-eval-tools/stm32-nucleo-boards/nucleo-h753zi.html#overview]
Program:
Na samym początku uruchomienie interfejsu USART oraz ustawienie pinu odpowiadającego za ustawienie kierunku transmisji. Ta część została opisana w innym poście dla tego układu pod tym linkiem.
- void RS485_InitProt(void) {
- RS485_InitRTSPin();
- RS485_SetControlPinToReceiveData();
- RS485_USART2_Init();
- RS485_SetControlPinToSendData();
- }
- static void RS485_SetControlPinToSendData(void) {
- RS485_RTS_PORT->BSRR = RS485_RTS_PIN;
- }
- static inline void RS485_SetControlPinToReceiveData(void) {
- RS485_RTS_PORT->BSRR = (uint32_t)RS485_RTS_PIN << 16U;
- }
- 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_VERY_HIGH;
- HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
- }
- 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;
- }
Standardowa obsługa przerwania będzie wyglądała następująco:
- 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 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);
- }
- static void RS485_SaveDataToBuffer(uint8_t recData) {
- RS485_Readers_Comm.recDataTimeout = 0;
- RS485_Readers_Comm.receiveData[RS485_Readers_Comm.recDataCounter] = recData;
- RS485_Readers_Comm.recDataCounter++;
- RS485_Readers_Comm.startRecData = 1;
- }
Obsługa odebranych danych odbywa się w zadaniu:
- void TaskRS485(void const *argument) {
- RS845_InitParameters(&RS485_Readers_Comm, &deviceSetting);
- RS485_SetStartData(&RS485_Readers_Comm, &deviceSetting);
- for(;;) {
- RS485_Send_Command(&RS485_Readers_Comm);
- RS485_CheckTimeoutAfterSendData(&RS485_Readers_Comm, &RS485_HeadData_t[0]);
- RS485_SendResetCommandToHead(&RS485_Readers_Comm, &RS485_HeadData_t[0]);
- RS485_ReaderCommunication(&RS485_Readers_Comm, &RS485_HeadData_t[0]);
- osDelay(200);
- }
- }
W celu synchronizacji przerwania z zadaniem można wykorzystać semafor lub task notification. Poniżej przedstawię przykład z drugim rozwiązaniem.
Na samym początku należy wywołać zadanie gdy pojawi się przerwanie od odebranych danych z UART.
- 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 */
- BaseType_t xHigherPriorityTaskWoken = pdFALSE;
- configASSERT( xTaskToNotifyatUARTRx != NULL );
- vTaskNotifyGiveFromISR( xTaskToNotifyatUARTRx, &xHigherPriorityTaskWoken );
- portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
- /* USER CODE END USART2_IRQn 1 */
- }
Poniżej obsługa odebranych danych.
- TaskHandle_t xTaskToNotifyatUARTRx;
- osThreadId taskUARTListeHandle;
- void TaskUsart(void const *argument) {
- configASSERT( xTaskToNotifyatUARTRx == NULL );
- xTaskToNotifyatUARTRx = xTaskGetCurrentTaskHandle();
- uint8_t receiveData = 0x00;
- for(;;) {
- HAL_UART_Receive_IT(&huart2, &receiveData, 1);
- uint32_t ulNotificationValue = ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
- if(ulNotificationValue == 1) {
- RS485_SaveDataInBuffer(receiveData);
- }
- else { }
- }
- }
- void RS485_SaveDataInBuffer(uint8_t data) {
- RS485_SaveDataToBuffer(data);
- }
- static void RS485_SaveDataToBuffer(uint8_t recData) {
- RS485_Readers_Comm.recDataTimeout = 0;
- RS485_Readers_Comm.receiveData[RS485_Readers_Comm.recDataCounter] = recData;
- RS485_Readers_Comm.recDataCounter++;
- RS485_Readers_Comm.startRecData = 1;
- }
Na samym początku następuje odbiór pojedynczego bajta danych. Dalej dane zostają dodane do bufora, ich odbiór jest obsłużony w osobnym zadaniu.