工业互联项目总结:UART
3.3 UART编程
UART有三种编程方式:
(1)查询方式:也是最基础的一种方式,通过TDR和RDR寄存器来发送和接收数据;
static void CH1_UART2_TxTaskFunction( void *pvParameters ) {uint8_t c = 0;//每隔500ms发送while (1){HAL_UART_Transmit(&huart2, &c, 1, 100);vTaskDelay(500);c++;} }
static void CH2_UART4_RxTaskFunction( void *pvParameters ) {uint8_t c = 0;int cnt = 0;char buf[100];HAL_StatusTypeDef err;while (1){//每次接收一个字节并打印(此函数不常用)err = HAL_UART_Receive(&huart4, &c, 1, 100);if (!err){sprintf(buf, "Recv Data : 0x%02x, Cnt : %d", c, cnt++);Draw_String(0, 0, buf, 0x0000ff00, 0);}} }
(2)中断方式:
uart.cpp文件//发送和接收完成标志 static volatile int g_uart2_tx_complete = 0; static volatile int g_uart4_rx_complete = 0;//发送完成回调函数 void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {if (huart == &huart2){g_uart2_tx_complete = 1;} }//等待发送完成 int Wait_UART2_Tx_Complete(int timeout) {while (g_uart2_tx_complete == 0 && timeout){vTaskDelay(1);timeout--;}if (timeout == 0)return -1;else{//清空发送完成标志,用于下次发送g_uart2_tx_complete = 0;return 0;} }//接收完成回调函数 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {if (huart == &huart4){g_uart4_rx_complete = 1;} }//等待接收完成 int Wait_UART4_Rx_Complete(int timeout) {while (g_uart4_rx_complete == 0 && timeout){vTaskDelay(1);timeout--;}if (timeout == 0)return -1;else{//清空接收完成标志,用于下次接收g_uart4_rx_complete = 0;return 0;} }
//app_freertos.c 文件static void CH1_UART2_TxTaskFunction( void *pvParameters ) {uint8_t c = 0;while (1){//启动发送中断HAL_UART_Transmit_IT(&huart2, &c, 1);Wait_UART2_Tx_Complete(100);vTaskDelay(500);c++;} }static void CH2_UART4_RxTaskFunction( void *pvParameters ) {uint8_t c = 0;int cnt = 0;char buf[100];HAL_StatusTypeDef err;while (1){//启动接收中断err = HAL_UART_Receive_IT(&huart4, &c, 1);if (Wait_UART4_Rx_Complete(10) == 0) {sprintf(buf, "Recv Data : 0x%02x, Cnt : %d", c, cnt++);Draw_String(0, 0, buf, 0x0000ff00, 0);}else{HAL_UART_AbortReceive_IT(&huart4);}} }
(3)DMA方式(和中断方式的回调函数相同)
static void CH1_UART2_TxTaskFunction( void *pvParameters ) {uint8_t c = 0;while (1){//改为DMA后缀HAL_UART_Transmit_DMA(&huart2, &c, 1);Wait_UART2_Tx_Complete(100);vTaskDelay(500);c++;} }static void CH2_UART4_RxTaskFunction( void *pvParameters ) {uint8_t c = 0;int cnt = 0;char buf[100];HAL_StatusTypeDef err;while (1){//改为DMA后缀err = HAL_UART_Receive_DMA(&huart4, &c, 1);if (Wait_UART4_Rx_Complete(10) == 0) {sprintf(buf, "Recv Data : 0x%02x, Cnt : %d", c, cnt++);Draw_String(0, 0, buf, 0x0000ff00, 0);}else{//改动HAL_UART_DMAStop(&huart4);}} }
3.8 在RTOS中使用DMA
//在接收指定字节数完成时触发 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {if (huart == &huart4){g_uart4_rx_complete = 1;/* write queue : g_uart4_rx_buf 100 bytes ==> queue */for (int i = 0; i < 100; i++){xQueueSendFromISR(g_xUART4_RX_Queue, (const void *)&g_uart4_rx_buf[i], NULL);}/* re-start DMA+IDLE rx */HAL_UARTEx_ReceiveToIdle_DMA(&huart4, g_uart4_rx_buf, 100);} }//在接收事件发生时触发(如 DMA 完成或空闲状态),接收不固定长度的数据 void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) {if (huart == &huart4){g_uart4_rx_complete = 1;/* write queue : g_uart4_rx_buf Size bytes ==> queue */for (int i = 0; i < Size; i++){xQueueSendFromISR(g_xUART4_RX_Queue, (const void *)&g_uart4_rx_buf[i], NULL);}/* re-start DMA+IDLE rx */HAL_UARTEx_ReceiveToIdle_DMA(&huart4, g_uart4_rx_buf, 100);} }void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) {if (huart == &huart4){/* re-start DMA+IDLE rx */HAL_UARTEx_ReceiveToIdle_DMA(&huart4, g_uart4_rx_buf, 100);} }int UART4_GetData(uint8_t *pData) {xQueueReceive(g_xUART4_RX_Queue, pData, portMAX_DELAY);return 0; }void UART4_Rx_Start(void) {g_xUART4_RX_Queue = xQueueCreate(200, 1);HAL_UARTEx_ReceiveToIdle_DMA(&huart4, g_uart4_rx_buf, 100); }
static void CH1_UART2_TxTaskFunction( void *pvParameters ) {uint8_t c = 0;while (1){/* send data */HAL_UART_Transmit_DMA(&huart2, &c, 1);Wait_UART2_Tx_Complete(100);vTaskDelay(500);c++;} }static void CH2_UART4_RxTaskFunction( void *pvParameters ) {uint8_t c = 0;int cnt = 0;char buf[100];HAL_StatusTypeDef err;UART4_Rx_Start();while (1){err = UART4_GetData(&c);if (err == 0) {sprintf(buf, "Recv Data : 0x%02x, Cnt : %d", c, cnt++);Draw_String(0, 0, buf, 0x0000ff00, 0);}else{HAL_UART_DMAStop(&huart4);}} }
3.9 面向对象封装UART
static SemaphoreHandle_t g_UART2_TX_Semaphore; static uint8_t g_uart2_rx_buf[100]; static QueueHandle_t g_xUART2_RX_Queue;static SemaphoreHandle_t g_UART4_TX_Semaphore; static uint8_t g_uart4_rx_buf[100]; static QueueHandle_t g_xUART4_RX_Queue;void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {if (huart == &huart2){//使用信号量替换发送完成标志xSemaphoreGiveFromISR(g_UART2_TX_Semaphore, NULL);}if (huart == &huart4){xSemaphoreGiveFromISR(g_UART4_TX_Semaphore, NULL);} }void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {if (huart == &huart2){/* write queue : g_uart4_rx_buf 100 bytes ==> queue */for (int i = 0; i < 100; i++){xQueueSendFromISR(g_xUART2_RX_Queue, (const void *)&g_uart2_rx_buf[i], NULL);}/* re-start DMA+IDLE rx */HAL_UARTEx_ReceiveToIdle_DMA(&huart2, g_uart2_rx_buf, 100);} if (huart == &huart4){/* write queue : g_uart4_rx_buf 100 bytes ==> queue */for (int i = 0; i < 100; i++){xQueueSendFromISR(g_xUART4_RX_Queue, (const void *)&g_uart4_rx_buf[i], NULL);}/* re-start DMA+IDLE rx */HAL_UARTEx_ReceiveToIdle_DMA(&huart4, g_uart4_rx_buf, 100);} }void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) {if (huart == &huart2){/* write queue : g_uart4_rx_buf Size bytes ==> queue *///将实际接收到的字节数据放入队列,而不是100字节for (int i = 0; i < Size; i++){xQueueSendFromISR(g_xUART2_RX_Queue, (const void *)&g_uart2_rx_buf[i], NULL);}/* re-start DMA+IDLE rx */HAL_UARTEx_ReceiveToIdle_DMA(&huart2, g_uart2_rx_buf, 100);}if (huart == &huart4){/* write queue : g_uart4_rx_buf Size bytes ==> queue */for (int i = 0; i < Size; i++){xQueueSendFromISR(g_xUART4_RX_Queue, (const void *)&g_uart4_rx_buf[i], NULL);}/* re-start DMA+IDLE rx */HAL_UARTEx_ReceiveToIdle_DMA(&huart4, g_uart4_rx_buf, 100);} }void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) {if (huart == &huart2){/* re-start DMA+IDLE rx */HAL_UARTEx_ReceiveToIdle_DMA(&huart2, g_uart2_rx_buf, 100);} if (huart == &huart4){/* re-start DMA+IDLE rx */HAL_UARTEx_ReceiveToIdle_DMA(&huart4, g_uart4_rx_buf, 100);} }/* uart2 */ //从队列取数据 int UART2_GetData(struct UART_Device *pdev, uint8_t *pData, int timeout) {//从队列获取一个字节的数据if (pdPASS == xQueueReceive(g_xUART2_RX_Queue, pData, timeout))return 0;elsereturn -1; }int UART2_Rx_Start(struct UART_Device *pDev, int baud, char parity, int data_bit, int stop_bit) {if (!g_xUART2_RX_Queue){g_xUART2_RX_Queue = xQueueCreate(200, 1); // 200个元素,每个元素为一个字节大小g_UART2_TX_Semaphore = xSemaphoreCreateBinary( ); //创建二值信号量/*deepseek:在使用HAL_UARTEx_ReceiveToIdle_DMA时,当发生空闲中断时,会调用HAL_UARTEx_RxEventCallback,而如果接收满100字节,会先调用HAL_UART_RxCpltCallback,然后调用HAL_UARTEx_RxEventCallback(Size=100)。因此,这里存在重复处理的风险:当收到100字节时,两个回调都会被调用,导致数据被重复放入队列两次(一次100字节,一次又100字节)。这显然是不正确的。*/HAL_UARTEx_ReceiveToIdle_DMA(&huart2, g_uart2_rx_buf, 100); //启动DMA+空闲中断接收,每次最多接收100个字节}return 0; }int UART2_Send(struct UART_Device *pDev, uint8_t *datas, uint32_t len, int timeout) {HAL_UART_Transmit_DMA(&huart2, datas, len); /* 等待发生完成信号量(为何不用mutex? 因为在中断里Give mutex会出锿) */if (pdTRUE == xSemaphoreTake(g_UART2_TX_Semaphore, timeout))return 0;elsereturn -1; }/**/int UART4_GetData(struct UART_Device *pDev, uint8_t *pData, int timeout) {if (pdPASS == xQueueReceive(g_xUART4_RX_Queue, pData, timeout))return 0;elsereturn -1; }int UART4_Rx_Start(struct UART_Device *pDev, int baud, char parity, int data_bit, int stop_bit) {if (!g_xUART4_RX_Queue){g_xUART4_RX_Queue = xQueueCreate(200, 1);g_UART4_TX_Semaphore = xSemaphoreCreateBinary();HAL_UARTEx_ReceiveToIdle_DMA(&huart4, g_uart4_rx_buf, 100);}return 0; }int UART4_Send(struct UART_Device *pDev, uint8_t *datas, uint32_t len, int timeout) {HAL_UART_Transmit_DMA(&huart4, datas, len);/* 等待丿个信号量(为何不用mutex? 因为在中断里Give mutex会出锿) */if (pdTRUE == xSemaphoreTake(g_UART4_TX_Semaphore, timeout))return 0;elsereturn -1; }struct UART_Device g_uart2_dev = {"uart2", UART2_Rx_Start, UART2_Send, UART2_GetData}; struct UART_Device g_uart4_dev = {"uart4", UART4_Rx_Start, UART4_Send, UART4_GetData};
//uart_device.h文件#include <stdio.h> #include <string.h> #include "uart_device.h"extern struct UART_Device g_uart2_dev; extern struct UART_Device g_uart4_dev;static struct UART_Device *g_uart_devices[] = {&g_uart2_dev, &g_uart4_dev};struct UART_Device *GetUARTDevice(char *name) {int i = 0;for (i = 0; i < sizeof(g_uart_devices)/sizeof(g_uart_devices[0]); i++){if (!strcmp(name, g_uart_devices[i]->name))return g_uart_devices[i];}return NULL; }
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); };struct UART_Device *GetUARTDevice(char *name);
static void CH1_UART2_TxTaskFunction( void *pvParameters ) {uint8_t c = 0;struct UART_Device *pdev = GetUARTDevice("uart2");pdev->Init(pdev, 115200, 'N', 8, 1);while (1){/* send data */pdev->Send(pdev, &c, 1, 100);vTaskDelay(500);c++;} }static void CH2_UART4_RxTaskFunction( void *pvParameters ) {uint8_t c = 0;int cnt = 0;char buf[100];int err;struct UART_Device *pdev = GetUARTDevice("uart4");pdev->Init(pdev, 115200, 'N', 8, 1);while (1){/* 接收数据 */err = pdev->RecvByte(pdev, &c, 200);if (err == 0) {sprintf(buf, "Recv Data : 0x%02x, Cnt : %d", c, cnt++);Draw_String(0, 0, buf, 0x0000ff00, 0);}else{//HAL_UART_DMAStop(&huart4);}} }