ESP32——串口通讯应用实例

一、简述

串口通讯为RS232通讯,接收采用中断方式(数据处理是消息队列+消息缓冲区),发送是查询方式(利用通知确定要发送的数据内容)。

本程序参考乐鑫官方示例,但做了如下修改:

1. 采用消息缓冲区进行接收数据处理,是因为当数据较长时接收中断并不能一次接收到一帧完整数据(取决于默认设置)。

2. 采用通知实现发送数据内容选择,是为了更高效的对发送进行控制。

二、代码

首先在一个公共头文件def.h声明一个通知变量,为的是在主函数中发送通知给串口处理源文件的发送任务函数。

extern TaskHandle_t UartTxTask_NotifyHandle;

串口处理头文件uhf.h中内容。

#define UART_UHF UART_NUM_2

esp_err_t uart2_init(int baud,int bufSize);

串口处理源文件uhf.c中变量定义部分

static const char *UART2_TAG = "uart2_events";

static QueueHandle_t uart2_rx_queue;
static uint8_t uart2_send_buf[512]={1,2,3,4,5,6,7,8};
static uint8_t uart2_recv_buf[512];
static int uart2_sendnum;
static int uart2_recvnum;

static MessageBufferHandle_t uart2_MsgBufHandle;

TaskHandle_t UartTxTask_NotifyHandle;

串口处理源文件uhf.c中实现串口初始化和发送接收任务创建部分



//-------------------------串口2初始化---------------------------
esp_err_t uart2_init(int baud,int bufSize)
{
    esp_log_level_set(UART2_TAG, ESP_LOG_INFO);

    /* Configure parameters of an UART driver,
     * communication pins and install the driver */
    uart_config_t uart_config = {
        .baud_rate = baud,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_APB,
    };
    //Install UART driver, and get the queue.
    uart_driver_install(UART_UHF, bufSize * 2, bufSize * 2, 20, &uart2_rx_queue, 0);
    uart_param_config(UART_UHF, &uart_config);

    //Set UART log level
    esp_log_level_set(UART2_TAG, ESP_LOG_INFO);
    //Set UART pins 
    uart_set_pin(UART_UHF, 21, 19, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
    //Reset the pattern queue length to record at most 20 pattern positions.
    uart_pattern_queue_reset(UART_UHF, 20);

    uart_enable_rx_intr(UART_UHF);
    //创建发送和接收任务
    uart2_MsgBufHandle = xMessageBufferCreate(1500);
    if(uart2_MsgBufHandle != NULL){   
        xTaskCreate(uart2_event_task, "uart2_event_task", bufSize*2, NULL, configMAX_PRIORITIES-1, NULL);
        xTaskCreate(uart2_rx_task, "uart2_rx_task", bufSize*2, NULL, configMAX_PRIORITIES-3, NULL);
        xTaskCreate(uart2_tx_task, "uart2_tx_task", bufSize*2, NULL, configMAX_PRIORITIES-5, &UartTxTask_NotifyHandle); 
    }  
    return ESP_OK; 
}

串口处理源文件uhf.c中接收中断处理和数据缓存和解析,根据通讯协议确定接收数据是否完成和正确(协议不同处理方式需做相应调整)。

//-------------------------串口2接收任务---------------------------
static void uart2_rx_task(void *arg)
{
    static const char *TX_TASK_TAG = "TX_TASK";
    int len = 0;
    uint16_t crc;
    int num;
    static uint8_t frame_buf[512];
    static int frame_header = 0;
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;

    esp_log_level_set(TX_TASK_TAG, ESP_LOG_INFO);
    while (1) {
        vTaskDelay(10 / portTICK_PERIOD_MS);
        num = xMessageBufferReceiveFromISR(uart2_MsgBufHandle,frame_buf,sizeof(frame_buf),&xHigherPriorityTaskWoken);       
        datacopy(&uart2_recv_buf[uart2_recvnum],frame_buf,num); 
        uart2_recvnum += num; 
        //接收数据长度需满足最小要求    
        if((uart2_recvnum-frame_header) < 9) continue;
        //定位帧头位置
        if(uart2_recv_buf[frame_header] != 0x5A){
            frame_header++;
            continue;
        }
        //找到了正取的帧头,然后需要判断是否是一组完整的数据帧,CRC校验是否正确
        len = uart2_recv_buf[frame_header+5]*256 + uart2_recv_buf[frame_header+6];
        if((uart2_recvnum-frame_header) >= (len + 9))
        {
            // if(DEBUG_ON) printf("uart2 recv end,data len = %4d \r\n", uart2_recvnum);
            // for(int i=0;i<uart2_recvnum;i++) if(DEBUG_ON) printf("%02x  ", uart2_recv_buf[i]);
            crc = crc16_CCITT(&uart2_recv_buf[frame_header+1],len + 9-3);
            if((((crc & 0xff00)>>8) != uart2_recv_buf[len + 9-2]) || ((crc & 0x00ff) != uart2_recv_buf[len + 9-1]))
            {
                frame_header++;
                if(DEBUG_ON) printf("uart2 crc error!\r\n");
            }
            else//找到正取的数据帧
            {
                datacopy(frame_buf,&uart2_recv_buf[frame_header],len + 9);
                uart2_recvnum = uart2_recvnum - frame_header - len - 9;
                if(uart2_recvnum > 0) datacopy(uart2_recv_buf,&uart2_recv_buf[frame_header+len + 9],uart2_recvnum);
                //处理接收到的数据
                frame_handing(frame_buf);
                                                            
            }
        }      
    }
}
//-------------------------串口2接收事件---------------------------
static void uart2_event_task(void *pvParameters)
{
    uart_event_t event;
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;

    uint8_t* dtmp = (uint8_t*) malloc(256);
    for(;;) {
        //Waiting for UART event.
        if(xQueueReceive(uart2_rx_queue, (void * )&event, (portTickType)portMAX_DELAY)) {
            bzero(dtmp, 256);
            // if(DEBUG_ON) ESP_LOGI(UART2_TAG, "uart[%d] event:", UART_UHF);
            switch(event.type) {
                //Event of UART receving data
                /*We'd better handler data event fast, there would be much more data events than
                other types of events. If we take too much time on data event, the queue might
                be full.*/
                case UART_DATA:
                    // if(DEBUG_ON) ESP_LOGI(UART2_TAG, "[UART event DATA]: %d", event.size);
                    uart_read_bytes(UART_UHF, dtmp, event.size, portMAX_DELAY);
                    //xMessageBufferSend(MsgBufHandle,dtmp,event.size, portMAX_DELAY);
                    xMessageBufferSendFromISR(uart2_MsgBufHandle,dtmp,event.size, &xHigherPriorityTaskWoken);
                    break;
                //Event of HW FIFO overflow detected
                case UART_FIFO_OVF:
                    if(DEBUG_ON) ESP_LOGI(UART2_TAG, "hw fifo overflow");
                    // If fifo overflow happened, you should consider adding flow control for your application.
                    // The ISR has already reset the rx FIFO,
                    // As an example, we directly flush the rx buffer here in order to read more data.
                    uart_flush_input(UART_UHF);
                    xQueueReset(uart2_rx_queue);
                    break;
                //Event of UART ring buffer full
                case UART_BUFFER_FULL:
                    if(DEBUG_ON) ESP_LOGI(UART2_TAG, "ring buffer full");
                    // If buffer full happened, you should consider encreasing your buffer size
                    // As an example, we directly flush the rx buffer here in order to read more data.
                    uart_flush_input(UART_UHF);
                    xQueueReset(uart2_rx_queue);
                    break;
                //Event of UART RX break detected
                case UART_BREAK:
                    if(DEBUG_ON) ESP_LOGI(UART2_TAG, "uart rx break");
                    break;
                //Event of UART parity check error
                case UART_PARITY_ERR:
                    if(DEBUG_ON) ESP_LOGI(UART2_TAG, "uart parity error");
                    break;
                //Event of UART frame error
                case UART_FRAME_ERR:
                    if(DEBUG_ON) ESP_LOGI(UART2_TAG, "uart frame error");
                    break;
                default:
                    if(DEBUG_ON) ESP_LOGI(UART2_TAG, "uart event type: %d", event.type);
                    break;
            }
        }
    }
    free(dtmp);
    dtmp = NULL;
    vTaskDelete(NULL);
}

串口处理源文件uhf.c中发送处理

//-------------------------串口2发送任务---------------------------
static void uart2_tx_task(void *arg)
{
    static const char *TX_TASK_TAG = "TX_TASK";
    uint32_t ulNotifiedValue = 0;
    esp_log_level_set(TX_TASK_TAG, ESP_LOG_INFO);
    while (1) {              
        vTaskDelay(10 / portTICK_PERIOD_MS); 
        xTaskNotifyWait(0, ULONG_MAX,&ulNotifiedValue, pdMS_TO_TICKS(0));         
        if((ulNotifiedValue & 0x0100) == 0x0100)//停止
        {            
            stop();
            uart_write_bytes(UART_UHF, uart2_send_buf, uart2_sendnum);
            if(DEBUG_ON) printf("Send stop to UHF!\n");
        }
        else if((ulNotifiedValue & 0x0200) == 0x0200)//读TID
        {            
            stop();
            uart_write_bytes(UART_UHF, uart2_send_buf, uart2_sendnum);
            vTaskDelay(50 / portTICK_PERIOD_MS);//发送间隔
            read_tid();
            uart_write_bytes(UART_UHF, uart2_send_buf, uart2_sendnum);
            if(DEBUG_ON) printf("Send read TID to UHF!\n");          
        }  
                                    
        ulNotifiedValue = 0;                        
    }
}

main.c文件中主函数

扫描二维码关注公众号,回复: 15582848 查看本文章
void app_main(void)
{
    uart2_init(115200,2048);
    read = 1;
    while (1)
    {
        vTaskDelay(5000 / portTICK_PERIOD_MS);
        if(read){
            read = 0;
            xTaskNotify(UartTxTask_NotifyHandle,0x0200,eSetBits);//读TID
        }
        else{
            read = 1;
            xTaskNotify(UartTxTask_NotifyHandle,0x0100,eSetBits);//停止
        }
    }
}

猜你喜欢

转载自blog.csdn.net/tsliuch/article/details/125887650