首先在乐鑫官方的参考文档里面只有简单的几句话:
官方参考文档里有关串口中断的描述
ESP8266_RTOS_SDK 默认使⽤ UART0 打印调试信息,默认波特率为 74880。用户可以在 user_init 中⾃定义初始化 UART,参考 uart_init_new 实现。UART 驱动示例: \ESP8266_RTOS_SDK\driver_lib\driver\uart.c 以初始化 UART0 为例。定义 UART 参数:
UART_ConfigTypeDef uart_config; uart_config.baud_rate = BIT_RATE_74880; uart_config.data_bits = UART_WordLength_8b; uart_config.parity = USART_Parity_None; uart_config.stop_bits = USART_StopBits_1; uart_config.flow_ctrl = USART_HardwareFlowControl_None; uart_config.UART_RxFlowThresh = 120; uart_config.UART_InverseMask = UART_None_Inverse; UART_ParamConfig(UART0, &uart_config);注册 UART 中断处理函数,使能 UART 中断:
UART_IntrConfTypeDef uart_intr;编程示例
uart_intr.UART_IntrEnMask = UART_RXFIFO_TOUT_INT_ENA | UART_FRM_ERR_INT_ENA | UART_RXFIFO_FULL_INT_ENA | UART_TXFIFO_EMPTY_INT_ENA; uart_intr.UART_RX_FifoFullIntrThresh = 10; uart_intr.UART_RX_TimeOutIntrThresh = 2; uart_intr.UART_TX_FifoEmptyIntrThresh = 20; UART_IntrConfig(UART0, &uart_intr); UART_SetPrintPort(UART0); UART_intr_handler_register(uart0_rx_intr_handler); ETS_UART_INTR_ENABLE();然后就没有更多的解释了!
从这篇博客里看到:http://blog.sina.com.cn/s/blog_ed2c83c30102xc0o.html
串口经常会随机的发来数据,而且数据大小未知;对收到的数据如何存储;如何确定是将一条完整的数据发送出去了。要达到上述三点要求,其实现思路大致如下:
对于随机数据的接收,要依靠中断来实现,在中断的结构体中设置UART_RXFIFO_FULL_INT_ENA 和UART_RXFIFO_TOUT_INT_ENA,然后据此设定相应门限值。 UART_IntrConfTypeDef uart_intr; uart_intr.UART_IntrEnMask = UART_FRM_ERR_INT_ENA | UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_TOUT_INT_ENA; uart_intr.UART_RX_FifoFullIntrThresh = 63; uart_intr.UART_RX_TimeOutIntrThresh = 20; uart_intr.UART_TX_FifoEmptyIntrThresh = 20; UART_IntrConfig(UART0, &uart_intr); 一次性接收到63字节(实际测试似乎是64字节)会产生UART_RXFIFO_FULL_INT_ST 中断。如果接收到数据小于63字节,并且在规定时间没有收到数据,就会产生UART_RXFIFO_TOUT_INT_ST中断。(esp8266并不需要自己来实现中断函数,而是通过注册一个中断回调函数来实现)由于接收数据比较快,所以要先缓存下来再处理,我在这里采用一个循环队列来实现缓存,最初,我是逐个字符往队列里存储,但是终端回调函数发送数据快,逐个字符存储处理太慢,于是就将其改成了用memcpy()函数来往队列里存储。(注意这里要合理估算,确保下一个数据发过来之前前一数据已被任务处理完成)对于存储下来的数据,设计了一个定时任务,并在队列里设计了一个查找函数,每隔一段时间对队列里面的数据进行查询,查找到最后一个’\n’字符串,返回这段数据长度,然后封装成json数据发送出去。这里用循环队列实现要避免重复发送数据,而且不用擦除数据,可以直接覆盖原有数据。 队列结构体如下: typedef struct{ u8* p_o; u8* volatile p_r; u8* volatile p_w; volatile u32 fill_cnt; u32 size; }RINGBUF;它的思想就是: - 首先注册中断服务函数,并注册完成之后马上初始化一个队列(便于将数据在任务之间传递)和开始一个串口任务 - 编写中断服务函数,在中断服务函数中判断条件,可以设置为串口数据溢出时就把数据发送到队列 - 在串口任务中只要一检测到队列中有数据发送就马上用串口打印出来。
user_main.c(删除了一些不必要的代码)
#include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/queue.h" #include "esp_common.h" #include "esp_wifi.h" #include "uart.h" #include "apps/sntp.h" #include "iot_export.h" #include "aliyun_port.h" #include "aliyun_config.h" #include "ota.h" #include "mqtt.h" int got_ip_flag = 0; ota_info_t *p_ota_info = NULL; enum { UART_EVENT_RX_CHAR, UART_EVENT_MAX }; typedef struct _os_event_ { uint32 event; uint32 param; } os_event_t; xTaskHandle xUartTaskHandle; xQueueHandle xQueueUart; LOCAL STATUS uart_tx_one_char(uint8 uart, uint8 TxChar) { while (true) { uint32 fifo_cnt = READ_PERI_REG(UART_STATUS(uart)) & (UART_TXFIFO_CNT << UART_TXFIFO_CNT_S); if ((fifo_cnt >> UART_TXFIFO_CNT_S & UART_TXFIFO_CNT) < 126) { break; } } WRITE_PERI_REG(UART_FIFO(uart) , TxChar); return OK; } // 串口处理任务 步骤:3 LOCAL void user_uart_task(void *pvParameters) { os_event_t e; for (;;) { if (xQueueReceive(xQueueUart, (void *)&e, (portTickType)portMAX_DELAY)) { switch (e.event) { case UART_EVENT_RX_CHAR: printf("%c", e.param); // 打印一个字符 break; default: break; } } } vTaskDelete(NULL); } // 串口中断服务函数 步骤:2 LOCAL void user_uart0_rx_intr_handler(void *para) { os_event_t e; portBASE_TYPE xHigherPriorityTaskWoken; uint8 RcvChar; uint8 uart_no = UART0;//UartDev.buff_uart_no; uint8 fifo_len = 0; uint8 buf_idx = 0; uint8 fifo_tmp[128] = {0}; uint32 uart_intr_status = READ_PERI_REG(UART_INT_ST(uart_no)) ; while (uart_intr_status != 0x0) { if (UART_FRM_ERR_INT_ST == (uart_intr_status & UART_FRM_ERR_INT_ST)) { //printf("FRM_ERR\r\n"); WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_FRM_ERR_INT_CLR); } else if (UART_RXFIFO_FULL_INT_ST == (uart_intr_status & UART_RXFIFO_FULL_INT_ST)) { // 串口数据溢出 //printf("*************full\r\n"); fifo_len = (READ_PERI_REG(UART_STATUS(UART0)) >> UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT; buf_idx = 0; /* while (buf_idx < fifo_len) { uart_tx_one_char(UART0, READ_PERI_REG(UART_FIFO(UART0)) & 0xFF); buf_idx++; } */ RcvChar = READ_PERI_REG(UART_FIFO(uart_no)) & 0xFF; // 将一个字符拷贝过去 WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_FULL_INT_CLR); // 设置为串口接收溢出时就将数据传到队列 e.event = UART_EVENT_RX_CHAR; e.param = RcvChar; xQueueSendFromISR(xQueueUart, (void *)&e, &xHigherPriorityTaskWoken); // 将数据发送到队列一次只有一次字符 portEND_SWITCHING_ISR(xHigherPriorityTaskWoken); } uart_intr_status = READ_PERI_REG(UART_INT_ST(uart_no)) ; } } // 串口中断初始化 步骤:1 void user_uart_init(void) { UART_WaitTxFifoEmpty(UART0); UART_WaitTxFifoEmpty(UART1); UART_ConfigTypeDef uart_config; uart_config.baud_rate = BIT_RATE_115200; uart_config.data_bits = UART_WordLength_8b; uart_config.parity = USART_Parity_None; uart_config.stop_bits = USART_StopBits_1; uart_config.flow_ctrl = USART_HardwareFlowControl_None; uart_config.UART_RxFlowThresh = 120; uart_config.UART_InverseMask = UART_None_Inverse; UART_ParamConfig(UART0, &uart_config); UART_IntrConfTypeDef uart_intr; uart_intr.UART_IntrEnMask = UART_RXFIFO_TOUT_INT_ENA | UART_FRM_ERR_INT_ENA | UART_RXFIFO_FULL_INT_ENA | UART_TXFIFO_EMPTY_INT_ENA; uart_intr.UART_RX_FifoFullIntrThresh = 10; // 设置10个字符就溢出 uart_intr.UART_RX_TimeOutIntrThresh = 10; uart_intr.UART_TX_FifoEmptyIntrThresh = 20; UART_IntrConfig(UART0, &uart_intr); UART_SetPrintPort(UART0); UART_intr_handler_register(user_uart0_rx_intr_handler, NULL); ETS_UART_INTR_ENABLE(); // 中断设置完成以后就马上初始化队列和创建串口处理任务 xQueueUart = xQueueCreate(32, sizeof(os_event_t)); xTaskCreate(user_uart_task, (uint8 const *)"uTask", 512, NULL, 5, &xUartTaskHandle); } void user_init(void) { user_uart_init(); }