UART更好的封装 添加容错代码
modbus_new_st_rtu函数里有pdev = GetUARTDevice((char*)device);
两个串口结构体使用了相似的函数,为避免代码冗余,特进行改进
uart_device.htypedef struct UART_Device{char* name;int (*Init)(struct UART_Device* pDev, int baud, char parity, int data_bit, int stop_bit);int (*Send)(struct UART_Device* pDev, uint8_t* datas, uint32_t len, int timeout);int (*RecvByte)(struct UART_Device* pDev, uint8_t* data, int timeout);int (*Flush)(struct UART_Device* pDev);void *priv_data; }UART_Device, *PUART_Device;UART_Device* GetUARTDevice(char* name);
uart.c#define UART_RX_BUF_LEN 100//定义私有数据用于区分usart2和usart4 typedef struct UART_Data{UART_HandleTypedef* huart;//有了以下就可以删去以前定义的全局变量QueueHandle_t rxQueue;SemaphoreHandle_t txSemaphore;uint8_t rx_buf[UART_RX_BUF_LEN]; }UART_Data, *PUART_Data;static int UART2_Rx_Start(PUART_Device pDev, int baud, char parity, int data_bit int stop_bit){PUART_Data pdata = pDev->priv_data; if(!pdata->rxQueue){pdata->rxQueue = xQueueCreate(200,1);pdata->txSemaphore = xSemaphoreCreateBinary();HAL_UARTEx_ReceiveToIdle_DMA(pdata->huart, pdata->rx_buf, UART_RX_BUF_LEN);}return 0; }static int UART_Send(PUART_Device pDev, uint8_t* datas, uint32_t len, int timeout){PUART_Data pdata = pDev->priv_data;HAL_UART_Transmit_DMA(pdata->huart, datas, len);if(pdTRUE == xSemaphoreTake(pdata->txSemaphore, timeout))return 0;elsereturn -1; }int UART_GetData(PUART_Device pdev, uint8_t* pData, int timeout){PUART_Data pdata = pDev->priv_data;if(pdPASS == xQueueReceive(pdata->rxQueue, pData, timeout))return 0;elsereturn -1; }int UART_Flush(PUART_Device pdev){PUART_Data pdata = pDev->priv_data;int cnt = 0;uint8_t data;while(1){if(pdPASS != xQueueReceive(pdata->rxQueue, &data, 0))break;cnt++;}return cnt; }static UART_Data g_uart2_data = {&huart2, };static UART_Data g_uart4_data = {&huart4, };UART_Device g_uart2_dev = {"uart2", UART_Rx_Start, UART_Send, UART_GetData, UART_Flush, &g_uart2_data}; UART_Device g_uart4_dev = {"uart4", UART_Rx_Start, UART_Send, UART_GetData, UART_Flush, &g_uart4_data};
uart.cvoid HAL_UART_TxCpltCallback(UART_HandleTypeDef* huart){PUART_Data pdata;if(huart == &huart2){pdata = &g_uart2_data;}if(huart == &huart4){pdata = &g_huart4_data;}xSemaphoreGiveFromISR(pdata->txSemaphore, NULL); }void HAL_UART_RxCpltCallback(UART_HandleTypeDef* huart){PUART_Data pdata;if(huart == &huart2){pdata = &g_uart2_data;}if(huart == &huart4){pdata = &g_huart4_data;}for(int i=0; i<UART_RX_BUF_LEN; ++i){xQueueSendFromISR(pdata->rxQueue, (const void*)&pdata->rx_buf[i], NULL);}HAL_UARTEx_ReceiveToIdle_DMA(pdata->huart, pdata->rx_buf, UART_RX_BUF_LEN); }void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef* huart, uint16_t Size){PUART_Data pdata;if(huart == &huart2){pdata = &g_uart2_data;}if(huart == &huart4){pdata = &g_huart4_data;}for(int i=0; i<Size; ++i){xQueueSendFromISR(pdata->rxQueue, (const void*)&pdata->rx_buf[i], NULL);}HAL_UARTEx_ReceiveToIdle_DMA(pdata->huart, pdata->rx_buf, UART_RX_BUF_LEN); }void HAL_UART_ErrorCallback(UART_HandleTypeDef* huart){PUART_Data pdata;if(huart == &huart2){pdata = &g_uart2_data;}if(huart == &huart4){pdata = &g_huart4_data;}HAL_UARTEx_ReceiveToIdle_DMA(pdata->huart, pdata->rx_buf, UART_RX_BUF_LEN);}
为了提高产品的稳定性,添加容错代码,增强健壮性。
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) {PUART_Data data = NULL;if (huart == &huart2){data = &g_uart2_data;}if (huart == &huart4){data = &g_uart4_data;}if (data){HAL_UART_DeInit(data->huart);HAL_UART_Init(data->huart);/* re-start DMA+IDLE rx */HAL_UARTEx_ReceiveToIdle_DMA(data->huart, data->rx_buf, RX_BUF_LEN);//HAL_UARTEx_ReceiveToIdle_IT(data->huart, data->rx_buf, 1);} }在f030中添加如下容错代码: void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) {if (huart == &huart1){struct UART_Data* uart_data = g_uart1_dev.priv_data;}HAL_UART_DeInit(uart_data->huart);HAL_UART_Init(uart_data->huart);HAL_UART_Receive_IT(data->huart, &uart_data->rx_buf, 1);}
粘包现象举例:传感器接收数据包超时时间太长,而H5传来的两包数据之间时间间隔太短,就会使得两包数据粘在一起,导致传输来的数据无法解析,传感器程序一直卡在接收那里:
for(;;){do{rc = modbus_receive(ctx, query); //内部会校验接收的数据是否正确,不正确继续循环下去}while(rc == 0);......}
modbus规范中,由于没有起始/结束符,因此在两个数据包之间间隔3.5个字符时间,一个完整的字符时间(包括起始位、数据位、停止位)通常是10-11个比特时间。
对于115200的波特率来说,两个数据包的时间间隔是10*3.5/115200 = 0.3ms.
在modbus_new_st_rtu函数中的_modbus_init_common函数内部ctx->byte_timeout.tv_usec, ctx->response_timeout.tv_usec(即接收一个字节的超时时间,回应的超时时间)都可以改小点,比如
表示Master程序发出请求后,等待回复时,最多等待多久,这个时间可以设置的大一点 #define _RESPONSE_TIMEOUT 10000表示的是,开始得到数据后,如果后续没有数据了,等待多久退出。这个时间可以设置的小一点。 #define _BYTE_TIMEOUT 10000在h5和f030中都做以上更改
以上是通过1)减小超时时间来减少粘包现象的,在此基础上可以2)增加发送数据包的间隔减小该现象。如以下在获得锁之后延时20ms使得发送和接收不再那么频繁。
以上会让传感器超时退出,进入新一波的等待流程。