ESP8266 SDK_RTOS UART操作

本文只是记录下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函数。将txdrxd 分别于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

展开阅读全文

没有更多推荐了,返回首页