一个温控器项目,可以本地控制空调,也可微信远程控制。其中,远程控制为:微信扫码进入控制页面,该页面实现对空调状态的实时显示和控制功能。页面也服务器之间采用websocket协议通信,空调与服务器通过ESP8266进行无线通信,采用MQTT协议,实现实时双向通信。因为ESP8266与空调控制器之间采用串口uart通信,所以需要给官方提供的mqtt demo里增加串口处理程序。
ESP8266有两个串口,UART0和UART1。他们各有一个128Bytes的硬件FIFO,读写FIFO都在同一个地址操作。UART0一般用于通信,UART1只有TX功能,一般用于打印日志。但是默认情况下,os_print()函数使用的是UART0,可在uart_init中进行修改
另外,模块刚上电时会打印一串乱码。这是因为这期间的ESP8266的串口波特率(115200)是跟外部晶振相关的,程序默认晶振为40M。所以如果你使用的外部晶振是26M,则应该调整串口工具波特率为26*(115200/40)=74880 。
下图是官方下载的demo结构,红框里的文件夹driver_lib是官方提供的各种驱动程序,我们使用的串口程序也在里面
打开driver_lib文件夹后,README.md就是它的使用方法
# driver_lib
## method 1
Generate libdriver.a in SDK/lib, in driver_lib folder, run:
./make_lib.sh driver
## method 2
* STEP 1:
Copy driver folder to your project sub-folder, such as app folder. Unused drivers can be removed in your project.
* STEP 2:
Modify Makefile in app folder.
1). Search SUBDIRS, add driver as subdir, such as:
SUBDIRS = user driver
2). Search COMPONENTS_eagle.app.v6, add libdriver.a, such as:
COMPONENTS_eagle.app.v6 = user/libuser.a driver/libdriver.a
我们采用方法2: 1)复制driver文件夹到自己的工程子文件夹里 2)修改app的makefile文件
上图红框内容为makefile增加内容
这样,就添加成功了。另外,uart.h和uart_register.h可能也需要从driver_lib文件夹里复制过来,不然有些宏可能会没有定义。
1. 接收数据:模块串口接收到数据时,会先填充到FIFO里。每当FIFO接收到的数据时,都会产生中断,中断处理程序为uart0_rx_intr_handler(uart_init -> uart_config->ETS_UART_INTR_ATTACH(uart0_rx_intr_handler, &(UartDev.rcv_buff))进行注册)。在中断处理程序中,判断接收的数据长度或者接收时间是否超过阈值。如果超过,则给接收任务(uart_init 里进行注册)发送信号量:system_os_post(uart_recvTaskPrio, 0, 0)。uart_recvTask接收到信号量后,就开始进行处理:
这里,宏UART_BUFF_EN非常重要,如果该宏值为0,则执行#else后面的程序,直接打印出来。如果我们需要处理,则需修改UART_BUFF_EN的值为1,进入Uart_rx_buff_enq()。在Uart_rx_buff_enq函数里主要的功能是把接收到的数据保存到pRxBuffer里。然后我们就可以对pRxBuffer进行解析来处理接收的内容了。(pRxBuffer是个环形缓存)
因为个人觉得Uart_rx_buff_enq这个函数写得非常优雅,所以特地放上来和学习一下,增加了注释:
//enqueue data from uart fifo to rx buffer
void Uart_rx_buff_enq(void)
{
uint8 fifo_len = 0, buf_idx = 0,loop;
ETSParam par = 0;
uint8 fifo_data;
uint8* tail = (pRxBuffer->pUartBuff + pRxBuffer->UartBuffSize);
fifo_len = (READ_PERI_REG(UART_STATUS(UART0))>>UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT;
//如果pRxBuffer的剩余空间不够,则只取pRxBuffer->Space
if(fifo_len > pRxBuffer->Space) {
buf_idx = pRxBuffer->Space;
}else{
buf_idx = fifo_len;
}
loop = buf_idx;
while (loop--) {
*(pRxBuffer->pInPos++) = READ_PERI_REG(UART_FIFO(UART0)) & 0xFF;
if (pRxBuffer->pInPos == tail) {
pRxBuffer->pInPos = pRxBuffer->pUartBuff;
}
}
fifo_len -= buf_idx;
//这里是当 fifo_len > pRxBuffer->Space, 释放fifo里没有被接收的数据
//如果fifo_len < pRxBuffer->Space,则fifo_len -= buf_idx == 0,则不进行下面的循环
while (fifo_len--) {
fifo_data = READ_PERI_REG(UART_FIFO(UART0)) & 0xFF; // discard data
}
pRxBuffer->Space -= buf_idx ;
par = buf_idx;
//这里给自己的任务发送信号量,进行串口接收后续处理
if(system_os_post(1, 1, 0) != TRUE) {
//os_printf("post fail!!!\n\r");
}
//中断信号清除和重新使能
WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_FULL_INT_CLR |
UART_RXFIFO_TOUT_INT_CLR);
uart_rx_intr_enable(UART0);
}
2. 发送数据:这里跟接收数据一样分析,省略。。。