本文只是记录下ESP8266 的串口操作,方便以后查阅。
文字和图片内容转载半颗心脏的博客。原文地址:https://blog.csdn.net/xh870189248/article/details/81146532
(esp8266的串口 Uart 通讯中断编程)
esp8266
的串口分布情况
①:
esp8266
有几个串口?
- 答:我们常见的
ESP8266-12f
有两个 UART,其中 UARTO 有 TX、RX,可做数据传输;UART1 由于 RX 脚被 SPI-Flash 占用,只能使用 TX,可以做串口调试信息打印。见下图:串口一是在GPIO2
,只可以查看信息。 -
②:
esp8266
如何屏蔽上电打印?? - 答:不管什么情况,U0TXD默认上电有系统打印,对此敏感应用可通过UART的内部引脚交换功能,在初始化的时候,调用system_uart_swap函数。将
txd
和rxd
分别于U0RTS(MTDO/GPIO15)、U0CTS (MTCK/GPIO13)交换来屏蔽该上电的系统打印。 交换后,硬件上的下载管脚还是使用U0TXD + U0RXD,通信时需要将MTDO对应接到MCU的RXD,MTCK对应加到MCU的TXD。
esp8266
的串口通讯时候,应该怎么接线;
串口实现的功能
由于串口1只能发送,可以用来做调试信息的打印。UART0用来做数据的收发。
UART0和单片机进行通信,简单的通信协议为:数据头 长度 数据 数据尾 总共4部分。
编程能力不高,方法只能做为参考使用。
数据接收的方式采用中断的方式,也就是参考SDK里面的代码来进行编写。
1. 接收缓冲配置为1,收到一个数据时就产生中断。
2.创建队列,对接收到的数据进行打印。
3. 在中断中解析通信协议。先判断接收的字符0XFE,设置标记为head,然后下一个字节为长度,然后再接收指定长度的数据,并判断最后一个字节是否为0XFF。是的话就认为数据包接收完成,传给消息队列,否者的话就重新接收,帮判断数据头。
具体代码:
#define RX_HEAD 0xFE
#define RX_OK_OR_NULL 0
#define RX_HEAD_ACT 1
#define RX_LENGTH_DATA 2
#define RX_MAX_LENGTH 64
typedef struct _uartRx_event_
{
unsigned char length;
unsigned char event;
char rxBuf[RX_MAX_LENGTH];
}uartRx_event_t;
uart_init_new(void)
{
UART_WaitTxFifoEmpty(UART0);
UART_WaitTxFifoEmpty(UART1);
UART_ConfigTypeDef uart_config;
uart_config.baud_rate = BIT_RATE_38400;//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_config.baud_rate = BIT_RATE_74880;//波特率为74880
UART_ParamConfig(UART1, &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; //FIFO 深度
// uart_intr.UART_RX_TimeOutIntrThresh = 2;
// uart_intr.UART_TX_FifoEmptyIntrThresh = 20;
// UART_IntrConfig(UART0, &uart_intr);
UART_IntrConfTypeDef uart_intr;
uart_intr.UART_IntrEnMask = UART_FRM_ERR_INT_ENA | UART_RXFIFO_FULL_INT_ENA;
uart_intr.UART_RX_FifoFullIntrThresh = 1; //FIFO 深度
//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, NULL);
ETS_UART_INTR_ENABLE();
xQueueUart = xQueueCreate(4, sizeof(uartRx_event_t));
xTaskCreate(uart_task, (uint8 const *)"uTask", 512, NULL, tskIDLE_PRIORITY + 2, &xUartTaskHandle);
}
中断处理函数部分代码:
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)
{
RcvChar = READ_PERI_REG(UART_FIFO(UART0)) & 0xFF;
switch(gRxHeadFlag)
{
case RX_OK_OR_NULL:
{
if(RcvChar==RX_HEAD)
{
gRxHeadFlag = RX_HEAD_ACT;
}
}
break;
case RX_HEAD_ACT:
{
gRxHeadFlag = RX_LENGTH_DATA;
gRxLength = RcvChar;
if(gRxLength>RX_MAX_LENGTH) //错误的数据长度
{
gRxHeadFlag = RX_OK_OR_NULL;
}
gUartEvent.length = 0;
}
break;
case RX_LENGTH_DATA:
{
gUartEvent.rxBuf[gUartEvent.length++] = RcvChar;
if(gUartEvent.length>=gRxLength)
{
if(gUartEvent.rxBuf[gUartEvent.length-1] == 0xFF)
{
xQueueSendFromISR(xQueueMqtt, (void *)&gUartEvent, &xHigherPriorityTaskWoken);
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
gRxHeadFlag = RX_OK_OR_NULL;
}
else
{
gRxHeadFlag = RX_OK_OR_NULL;
}
}
else if(gUartEvent.length>RX_MAX_LENGTH)//如果数据溢出了
{
gRxHeadFlag = RX_OK_OR_NULL;
}
}
break;
}
buf_idx++;
}
WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_FULL_INT_CLR);
}
消息队列的处理:
LOCAL void
uart_task(void *pvParameters)
{
uartRx_event_t e;
printf("uart_TASK Runing..\r\n");
int i=0;
for (;;)
{
if (xQueueReceive(xQueueUart, (void *)&e, (portTickType)portMAX_DELAY))
{
for(i=0; i<e.length-1; i++)
{
printf("0x%x ",e.rxBuf[i]);
}
printf("lg=%d\r\n",e.length-1);
}
}
vTaskDelete(NULL);
}
测试发送数据: FE030102FF
测试了几次还能用。。增加数据尾的目的是。如果偶尔发生单个数据丢失的情况,代码还能自己修复回来。
如果没有数据尾的话,如果发送0XFE030102 这次少发了一个数据,那么后面所有的数据帧都会解析错误。
加上数据尾再加上消息回传确认,这个通信协议勉强能用,不过传送的数据信息最好是字符串格式的。
如果是TCP透传的话,可以把消息队列放到TCP的任务中。再任务中接收消息,然后再用TCP转发出去。
没用过freertos的消息队列,是看着例程直接代码拷贝过来的。。以前用的队列中的消息都是动态内存的。这个貌似是静态的。
本来想把这两句代码调整下:
uart_intr.UART_RX_FifoFullIntrThresh = 1; //FIFO 深度
uart_intr.UART_RX_TimeOutIntrThresh = 2;
把FIFO利用起来,尽量降低中断的频繁程序,尝试了几次没有实现。如果数据包是固定长度的话,应该能利用起来。
其他记录
串口中断函数分析:
原文地址:https://blog.csdn.net/qq_28877125/article/details/65448598
首先UART的中断寄存器有:
- UART_INT_RAW 中断原始状态寄存器
- UART_INT_ENA 中断使能寄存器:表⽰示当前使能的uart中断。
- UART_INT_ST 中断状态寄存器:表⽰示当前有效的中断状态
- UART_INT_CLR 清除中断寄存器:置对应位来清除中断状态寄存器
然后UART的一些特殊的位:
- UART_RXFIFO_FULL_INT_ST :接收full中断位
- UART_RXFIFO_OVF_INT_ST:接收溢出中断位
- UART_RXFIFO_TOUT_INT_ST :接收超时中断位
- UART_TXFIFO_EMPTY_INT_ST:发送空中断位
然后UART的寄存器操作函数:
- READ_PERI_REG(addr) 读寄存器值的函数
- WRITE_PERI_REG(addr, val) 写寄存器函数
FreeRTOS 学习四:队列
原文地址:https://blog.csdn.net/qqliyunpeng/article/details/53618675