【ESP8266之LUA开发】二、GPIO映射以及串口的空闲中断初探

1、关于GPIO

从实战中开始吧,就让我们利用GPIO2点亮8266的一个板载小灯吧。

gpio.mode(4, gpio.OUTPUT)
gpio.write(4, 0)

原理图上是GPIO2,为什么对应程序要写4呢?

LUA固件里面是这样封装的!

IO - IndexEPS8266 pin对应关系一览表
这里写图片描述

2、关于串口

ESP8266有两个串口,UART0UART1

由ESP8266 - 12E 规格书<这里, yc6y>可以知道,
这里写图片描述

UART0-TXDGPIO1
UART1-RXDGPIO3
UART1只有TXD引脚,在GPIO2

print输出默认用UART0
uart.write输出则可以使用串口0也可以串口1,输出前指明即可!

实例:当8266收到数据,就让数据回显!

tmr.alarm(1, 2000, 1, function()
            uart.on("data", 0, function(Rec)
                                uart.write(0, Rec)
                               end, 0)
end)

实例:

注意,发送命令的时候不要勾选发送新行

1、H控制继电器开,L控制继电器关

继电器在GPIO4,对应IO-Index2

gpio.mode(2, gpio.OUTPUT) --设置GPIO模式

tmr.alarm(1, 2000, 0, function() --2s后启动
            
            uart.on("data", 0, function(Rec)

                    if Rec == 'H' then 
                        gpio.write(2, 1)
                        print("High")
                    end

                    if Rec == 'L' then
                        gpio.write(2, 0)
                        print("LOW")
                    end

                    uart.write(0, Rec) --回显接收的数据

                    end, 0)
                                
end)

上面的作为一个实例来控制继电器吸合,当然是没有问题的,接下来我们稍微改动下,把命令从H改为++H

2、++H控制继电器

gpio.mode(2, gpio.OUTPUT)

tmr.alarm(1, 2000, 0, function()
            
            uart.on("data", 0, function(Rec)

                    if Rec == '++H' then 
                        gpio.write(2, 1)
                        print("High")
                    end

                    if Rec == '++L' then
                        gpio.write(2, 0)
                        print("LOW")
                    end

                    uart.write(0, Rec)

                    end, 0)
                                
end)

理论上可行,但是当我实际真的发送++H的时候却不行
你这个时候可以再试一下+++H的命令,却发现可行

问题分析

问题出在哪了呢?

这一句uart.on("data", 0, ...will receive every char in buffer当接收到一接收到字符不管多少个,就直接触发回调函数,造成的结果呢,发送一个完整的++H指令,却被拆分为++H两个。
这个结论从串口助手打印的回显数据也不能看出。

看串口数据之前,先来了解下printuart.write打印的区别!

  • print默认UART0,不能更换!而uart.write可以使用UART0或者UART1
  • print打印数据后会自动加回车换行!而uart.write不会!

OK,带着上面这两条(主要是第二条)来分析下串口助手打印的信息!

发送++H
串口助手显示

++H

很容易分析出,就是uart.write把我们输入的命令原封不动的回显过来的!

大概过程+触发uart.on回调函数,都不满足然后用uart.write(不会自己加回车换行)回显+,然后接着的+H也都不满足,就直接回显了+H
都是用uart.write打印的,它们之间自然没有换行了!

也许你有一个问题,为什么第一次只有+一个字符触发,而第二次可以+H两个字符。你可以这样想,uart.on 是一接收到字符就触发,但是第一次触发肯定是一个字符就可以,然后触发要执行回调函数,这段时间完全够剩余数据写入缓冲区,然后再读缓冲区的时候,肯定是截断后的全部数据!

发送+++H
串口助手显示

+High
++H

注意,别自动忽略两条消息不在一行的信息!显然有print的功劳!

大概过程:+触发uart.on回调函数,都不满足然后用uart.write(不会自己加回车换行)回显出来+。随后截断剩余数据++H符合条件,然后执行条件判断print打印High(High后加了回车换行!),然后原封不动的数据回显,显然此时的数据是++H

问题解决

分析好了问题,自然解决问题相对有点思路了,比如我们这个++H的指令问题。

No.1

既然uart.on("data", 0, ... 0不行,那换成3不就OK了嘛!接收到三个数据再触发回调!

确实可以,但是程序每次都得事先指定好指令长度,局限性很大!那么有没有一种办法,还是uart.on("data", 0, ... ,然后随意规定指令都可以呢?
of Course

当当当,空闲中断隆重登场!

No.2

ReadCnt = 0
ReadCntCopy = 0
ReadData = nil
ReadDataCopy = nil

gpio.mode(2, gpio.OUTPUT)

tmr.alarm(1, 2000, 0, function() -- 初始化后2s串口进行监听数据
            uart.on("data", 0, function(Rec)
                ReadData = ReadData..Rec --把接收到的数据连接在一起
                ReadCnt = ReadCnt + 1 --记录进入回调函数的次数
            end, 0)                              
end)   

tmr.alarm(2, 5, 1, function()
        if ReadCnt ~= 0 then
            if ReadCnt == ReadCntCopy then
                ReadCnt = 0
                ReadCntCopy = 0
                ReadDataCopy = ReadData
                ReadData = ""

                if ReadDataCopy == "++H" then
                    gpio.write(2, 1)
                end

                if ReadDataCopy == "+L" then
                    gpio.write(2, 0)
                end
                
            else
                ReadCntCopy = ReadCnt//只要一赋值就变成了全局变量
            end
        end
        
end)
             

全局变量并未声明,而是直接使用,这个算是LUA的一个特点吧,但是为了程序的可读性,我们还是进行声明的好!

定时器1还是用来接收数据,但是并不直接处理数据,而是把接收到的数据连接在一起,并记录进入回调函数的次数。

定时器2每隔2S,判断ReadCntReadCntCopy是否相等,一旦相等,就可说明此时一帧数据已经接收完毕,可以进行处理!

配送一个自己写的串口驱动程序 用DMA接收数据 接收完会产生一个空闲中断 由此可判断接收完一个包的数据 再配送一个我自己写的动态内存管理 跟ESP8266的驱动 在项目中测试460800的波特率 30kb一秒的数据接收 一包1024个字节 每包需要应答15字节的情况下 AT指令处理是使用多个缓冲级来处理模块发送过来的数据 分别有模块应答缓冲级 跟等待应答的缓冲级、被忽略的AT指令集的缓冲级(例如注册一个SEND OK\r\n则模块应答此条指令立刻清除缓冲级释放内存无需等待超时直接忽略)还有需要回调的缓冲级(则出现此指令调用回调函数)都是通过注册的方式来实现 如果出现一包跟指令被分到一个包内 AT处理函数一样可以搜索到AT指令 使用strstr函数来实现 函数的缓冲级都是指针不占用内存 使用动态内存管理的方式 有数据则创建内存放入数据作为一个缓冲级 如果模块应答的数据在规定的时间内没有响应则删除此缓冲级 函数前都有注释介绍 下面介绍一些常用的函数: at_init初始化一些变量已经串口 at_time_task使用定时器回调 1毫秒回调一次 用来计数超时的指令缓冲&等待超时的计数 at_clear_all 在模块开机的时候可能会有很多乱数据 可以在初始化完毕后使用此函数清除所有缓冲级 释放所有内存 at_processing处理AT的应答超时的指令(做删除释放内存的动作),还有处理等待的AT指令 此函数一定要不断循环处理 可以加入到定时器 目前我实验是在UCOS上的 所以直接创建个任务来执行此函数 当程序在等待某个AT指令的时候此函数会寻找接收的缓冲级是否有等待的AT指令 at_cmd_cb_hand回调处理函数 如果接收缓冲级出现某个已经注册的指令则回调注册时所填写的函数地址 at_send_cmd 发送一个AT指令 可以用跟printf一样使用 %d等等 at_send_data 发送数据的时候所使用 需要填写长度 at_cmd_ignore_register 注册一个被忽略的AT指令 带入参数 *s (例如填写一个"SEND OK" 则模块应答的此条指令直接被忽略 释放内存 被忽略之前会检查此缓冲级会不会带有别的AT指令需要回调的) at_cmd_ignore_cancel 取消被忽略的指令 则取消已经注册的被忽略的AT指令 at_cmd_cb_register AT指令的回调注册 例如参数填写"+IPD",函数名a 则出现+IPD的时候回调a函数 a函数有类型 在at.h文件里面有 at_cmd_cb_cancel注销回调你懂得 at_wait_cmd 等待一个AT指令集或者超时则立刻返回 等待途中会不断调用OS的延迟程序 让系统能有时间去执行其他任务 使用方法例如{ at_send_cmd("AT+UART=%u,%u,%u,%u,%u\r\n",baudrate,databit,stopbit,parity,flow_control); return (esp_error)at_wait_cmd("\r\nOK\r\n",2000,NULL); } at_error at_wait2_cmd(char *s,char *s2,u16 timeout,u8 *index) 此函数是等待两个AT指令集 如果出现一个则立刻返回 返回值h文件有介绍 AT_DONE则出现此条指令 index参数则提取应答的缓冲首地址 使用at_buf_get函数获取首地址 使用完后要调用at_free_buf来清除并释放这个缓冲级 at_buf_len_get查询此应答的缓冲级长度 如果在index填写NULL则不需要缓冲级首地址 直接清除释放缓冲级
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ReCclay

如果觉得不错,不妨请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值