8、合宙Air模块Luat开发:基于官方库的二次封装,使串口更加易用

目录

点击这里查看所有博文

本系列博客,理论上适用于合宙的Air202、Air268、Air720x、Air720S以及最近发布的Air720U(我还没拿到样机,应该也能支持)。

先不管支不支持,如果你用的是合宙的模块,那都不妨一试,也许会有意外收获
我使用的是Air720SL模块,如果在其他模块上不能用,那就是底层core固件暂时还没有支持,这里的代码是没有问题的。例程仅供参考!

一、前言

每次写博客最痛苦的事情就是把要讲的东西用小白都能听懂的方式描述一遍,所以我决定换一种方式来写这篇博客,不扯这些乱七八糟的东西,默认大家都在其他地方了解过什么是串口,这里只教怎么用

  • 串口通讯协议简介

串口通讯(Serial Communication)是一种设备间非常常用的串行通讯方式,因为它简单便捷,大部分电子设备都支持该通讯方式,电子工程师在调试设备时也经常使用该通讯方式输出调试信息,因为Air720SL自有一个USB端口用于程序下载和log打印,所以这里主要是讲怎么和其他的单片机或者通讯,不建议用来打印调试信息。

  • 串口通讯的波特率

串口通讯是一种异步通信,异步通讯中由于没有时钟信号,所以两个通讯设备之间需要约定好波特率,即每个码元的长度,以便对信号进行解码。常见的波特率为9600、19200、115200等。

  • 通讯的起始和停止信号

串口通讯的一个数据包从起始信号开始,直到停止信号结束。数据包的起始信号由一个逻辑 0的数据位表示,而数据包的停止信号可由 0.5、1、1.5或 2 个逻辑 1 的数据位表示,只要双方约定一致即可。

  • 数据位长度

在数据包的起始位之后紧接着的就是要传输的主体数据内容,也称为有效数据,有效数据的长度常被约定为 5、6、7或 8位长。

  • 数据校验

在有效数据之后,有一个可选的数据校验位。由于数据通信相对更容易受到外部干扰导致传输数据出现偏差,可以在传输过程加上校验位来解决这个问题。校验方法有奇校验(odd)、偶校验(even)、0校验(space)、1校验(mark)以及无校验(noparity)。在无校验的情况下,数据包中不包含校验位。

以上内容引用开源一小步的ESP32的两个UART实验有兴趣的自行了解

二、Air720SL开发板硬件部分

  • 查看硬件设计手册能够看到Air720Sl模组一共拥有两个串口,串口2为主串口,串口1为辅助串口
    在这里插入图片描述在这里插入图片描述
  • 官方的开发板用了一个开关,使开发板能够很方便的在一个usb口上实现串口1和串口2的切换
    在这里插入图片描述

三、编写驱动库函数

  • 官方的串口驱动库可以通过订阅系统消息,产生中断来实现通知用户已经接收到数据,但是这个通知是硬件FIFO是从空到非空的时候产生中断,如果在中断到来的那一刻立即去读取可能会存在数据读取不完整。
  • 串口会存在分包发送的现象,那么就需要用户自己实现整合多个数据包的内容。
  • 官方的串口demo也给出了解决方案,就是利用数据包接收超时来判断系统有没有接受到完整的数据包,这样的话串口接收程序就显得比较复杂,用户需要考虑的东西比较多,用起来也不是很方便

我这里基于官方的demo进行了二次封装,采用回调通知的方式通知用户接收到了完整的数据包,提供的库函数包内部自整合的超时合包机制,以及延时读取机制,保证在通知用户的时候已经接收到完整的数据(如果出现通讯故障,是没法保证的)

--- 模块功能:更加易用的串口
-- @author CX
-- @module
-- @license MIT
-- @copyright CX
-- @release 2020.02.13
module(..., package.seeall)require "clib"------------------------------------------------------------------------------------
-------------------------------函数说明---------------------------------------------
------------------------------------------------------------------------------------
--[[
* @description: Uart初始化
* @param
----------uart_id            {numble} 串口号,可选0,1,2
----------baud_rate          {numble} 波特率,可选1200240048009600104001440019200288003840057600115200230400460800921600
----------data_bits          {numble} 数据位,78
----------parity             {numble} 校验位,可选uart.PAR_EVEN, uart.PAR_ODD或uart.PAR_NONE
----------stop_bits          {numble} 停止位,可选uart.STOP_1,uart.STOP_2
----------receive_cb         {function} 接收到数据回调函数,回调函数参数uid:接收到数据的串口id
---------------------------------------------------------------------str:接收到的完整数据
---------------------------------------------------------------------len:接收到的数据长度
* @return: 无function Uart_Init(uart_id, baud_rate, data_bits, parity, stop_bits, receive_cb)
]]--[[
* @description: 串口发送
* @param
----------uart_id            {numble} 串口号,可选0,1,2
----------String             {String} 要发送的数据
----------sent_cb            {function} 可选,数据发送成功回调函数,回调函数参数uid:发送成功的串口id,
* @return: 无
function Uart_Sent(uart_id, String, sent_cb)
]]--[[
* @description: 串口关闭,释放占用的资源
* @param
----------uart_id   {numble} 串口号,可选0,1,2
* @return: 无
function Uart_Close(uart_id)
]]------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
-------------------------------下面都不要管了----------------------------------------
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
local Uart_Receive_Data = {}
local Uart_Receive_cb = {}
local Uart_Sent_cb = {}
local Uart_Sent_Busy = {}
local function Uart_Receive_timeout(uid)local str = table.concat(Uart_Receive_Data[uid])if Uart_Receive_cb[uid] thenUart_Receive_cb[uid](uid, str, #str)end
end
local function Uart_Re_Notice(uid, ulength)table.insert(Uart_Receive_Data[uid], uart.read(uid, 8192))sys.timerStart(Uart_Receive_timeout, 100, uid)
end
local function Uart_Se_Notice(uid, ulength)Uart_Sent_Busy[uid] = falseif Uart_Sent_cb[uid] thenUart_Sent_cb[uid](uid)endlog.info("uart" .. uid .. "send done")
end
--[[
* @description: Uart初始化
* @param
----------uart_id            {numble} 串口号,可选0,1,2
----------baud_rate          {numble} 波特率,可选1200240048009600104001440019200288003840057600115200230400460800921600
----------data_bits          {numble} 数据位,78
----------parity             {numble} 校验位,可选uart.PAR_EVEN, uart.PAR_ODD或uart.PAR_NONE
----------stop_bits          {numble} 停止位,可选uart.STOP_1,uart.STOP_2
----------receive_cb         {function} 接收到数据回调函数,回调函数参数uid:接收到数据的串口id
---------------------------------------------------------------------str:接收到的完整数据
---------------------------------------------------------------------len:接收到的数据长度
* @return:]]
function Uart_Init(uart_id, baud_rate, data_bits, parity, stop_bits, receive_cb)if receive_cb == nil thenlog.info("CXUART", "Please fill in the receive data callback function!")return falseendUart_Receive_cb[uart_id] = receive_cbUart_Sent_Busy[uart_id] = falsepm.wake("mcuart")uart.setup(uart_id, baud_rate, data_bits, parity, stop_bits, nil, 1)uart.on(uart_id, "receive", Uart_Re_Notice)uart.on(uart_id, "sent", Uart_Se_Notice)
end
--[[
* @description: 串口发送
* @param
----------uart_id            {numble} 串口号,可选0,1,2
----------String             {String} 要发送的数据
----------sent_cb            {function} 数据发送成功回调函数,可选
* @return:]]
function Uart_Sent(uart_id, String, sent_cb)if Uart_Sent_Busy[uart_id] == false thenUart_Sent_Busy[uart_id] = trueUart_Sent_cb[uart_id] = sent_cbuart.write(uart_id, String)elselog.info("CXUART", "Device is busy, please try again later")end
end
--[[
* @description: 串口关闭,释放占用的资源
* @param
----------uart_id   {numble} 串口号,可选0,1,2
* @return:]]
function Uart_Close(uart_id)uart.close(uart_id)Uart_Receive_Data[uart_id] = nilUart_Receive_cb[uart_id] = nilUart_Sent_Busy[uart_id] = nilUart_Sent_cb[uart_id] = nil
end

上面直接给出了二次封装的库函数代码,使用者只需要新建一个文件,我这里是新建了一个CXUART.lua,具体新建什么文件,看心情自己定,然后将上面的代码全部复制放到文件中保存

四、编写测试程序并且下载测试程序到开发板

这里我们直接贴出全部代码

--必须在这个位置定义PROJECT和VERSION变量
--PROJECT:ascii string类型,可以随便定义,只要不使用,就行
--VERSION:ascii string类型,如果使用Luat物联云平台固件升级的功能,必须按照"X.X.X"定义,X表示1位数字;否则可随便定义
PROJECT = "UART"
VERSION = "0.0.1"
require "sys"
--加载日志功能模块,并且设置日志输出等级
--如果关闭调用log模块接口输出的日志,等级设置为log.LOG_SILENT即可
require "log"
LOG_LEVEL = log.LOGLEVEL_TRACErequire "CXUART"local function Test_Task()while true dolog.info("Test_Task_run")sys.wait(5000)end
endlocal function MyUart_receive_cb(uid, str, len)--串口接收回调函数log.info("uart" .. uid .. ":Len:" .. len)log.info("uart" .. uid .. ":str:" .. str)CXUART.Uart_Sent(uid, str)--原封不动返回
endlocal function user_main()sys.taskInit(Test_Task)sys.wait(5000)CXUART.Uart_Init(1, 9600, 8, uart.PAR_NONE, uart.STOP_1, MyUart_receive_cb)  --串口1初始化CXUART.Uart_Init(2, 115200, 8, uart.PAR_NONE, uart.STOP_1, MyUart_receive_cb)--串口2初始化
end--启动系统框架
sys.taskInit(user_main)
sys.init(0, 0)
sys.run()

下载程序需要连同库函数文件一并下载
在这里插入图片描述

五、总结

1、我提供的库函数

  • Uart_Init(uart_id, baud_rate, data_bits, parity, stop_bits, receive_cb)–串口初始化
  • Uart_Sent(uart_id, String, sent_cb)–串口发送数据
  • Uart_Close(uart_id)–关闭串口,释放资源

不会下载的点击这里,进去查看我的第二篇博文2、Air720SL模块Luat开发:第一个Luat的Hello World里面讲了怎么下载
这里只是我的学习笔记,拿出来给大家分享,欢迎大家批评指正,本篇教程到此结束

Published by

风君子

独自遨游何稽首 揭天掀地慰生平