init: Air780EPM 官方 LuatOS 项目代码基线

- 来源: 合宙 LuatOS 官方仓库 air780epm 模块完整代码
- 路径: luatos/air780epm/module/Air780EPM/demo 含官方 demo(含 mqtt/mqtts/socket/uart 等)
- 后续: 基于 demo 开发 UART<->MQTT 数据上报功能
This commit is contained in:
wangfq
2026-08-31 08:53:49 +08:00
commit 173dd6874f
813 changed files with 125484 additions and 0 deletions
@@ -0,0 +1,168 @@
--[[
@module can_normal
@summary CAN总线正常工作模式使用示例
@version 1.0
@date 2025.11.25
@author 魏健强
@usage
本文件为CAN总线正常工作模式使用示例,核心业务逻辑为:
1. 初始化CAN总线
2. 发送和接收CAN总线数据
本文件没有对外接口,直接在main.lua模块中require "can_normal"就可以加载运行;
]]
local can_id = 0
local stb_pin = 28 -- 780EPM V1.3开发板上STB引脚为GPIO28
local rx_id = 0x12345677
local tx_id = 0x12345678
local test_cnt = 0
local tx_buf = zbuff.create(8) -- 创建zbuff
local send_queue = {} -- 发送队列
local MAX_SEND_QUEUE_LEN = 50 -- 发送队列最大长度
local send_res = false
gpio.setup(23, 1) -- 要手动打开,开发板can电路需要使用gpio23引脚作为参考电平。
-- 数据插入发送队列
local function can_send_data(id, msg_id, id_type, RTR, need_ack, data)
if #send_queue >= MAX_SEND_QUEUE_LEN then
log.error("can_send_data", "send queue full")
end
table.insert(send_queue, {
id = id,
msg_id = msg_id,
id_type = id_type,
RTR = RTR,
need_ack = need_ack,
data = data
})
sys.publish("CAN_SEND_DATA_EVENT")
end
local function can_cb(id, cb_type, param)
if cb_type == can.CB_MSG then
log.info("有新的消息")
local succ, msg_id, id_type, rtr, data = can.rx(id)
while succ do
log.info(mcu.x32(msg_id), #data, data:toHex())
succ, msg_id, id_type, rtr, data = can.rx(id)
end
end
if cb_type == can.CB_TX then
if param then
log.info("发送成功")
send_res = true
else
log.info("发送失败")
send_res = false
end
sys.publish("CAN_SEND_DATA_RES")
end
if cb_type == can.CB_ERR then
-- param参数就是4字节错误码
log.error("CAN错误", "错误码:", string.format("0x%08X", param))
-- 解析错误码
local direction = (param >> 16) & 0xFF -- byte2: 方向
local error_type = (param >> 8) & 0xFF -- byte1: 错误类型
local position = param & 0xFF -- byte0: 错误位置
-- 判断错误方向
if direction == 0 then
log.info("错误方向", "发送错误")
else
log.info("错误方向", "接收错误")
end
-- 判断错误类型
if error_type == 0 then
log.info("错误类型", "位错误")
elseif error_type == 1 then
log.info("错误类型", "格式错误")
elseif error_type == 2 then
log.info("错误类型", "填充错误")
end
-- 输出错误位置
log.info("错误位置", string.format("0x%02X", position))
end
if cb_type == can.CB_STATE then
-- 获取总线状态
local state = can.state(can_id)
log.info("can.state", "当前状态", state)
-- 根据状态处理
if state == can.STATE_ACTIVE then
log.info("can.state", "总线正常")
elseif state == can.STATE_PASSIVE then
log.warn("can.state", "被动错误状态")
elseif state == can.STATE_BUSOFF then
log.error("can.state", "总线离线")
-- 需要手动恢复
can.reset(can_id)
end
end
end
local function can_tx_test(data)
while true do
sys.wait(10000)
test_cnt = test_cnt + 1
if test_cnt > 8 then
test_cnt = 1
end
tx_buf:set(0, test_cnt) -- zbuff的类似于memset操作,类似于memset(&buff[start], num, len)
tx_buf:seek(test_cnt) -- zbuff设置光标位置(可能与当前指针位置有关;执行后指针会被设置到指定位置)
can_send_data(can_id, 0x123, can.STD, false, true, "Hello") -- 发送标准帧数据
can_send_data(can_id, 0x123, can.STD, true, true, "") -- 发送遥控帧数据
can_send_data(can_id, tx_id, can.EXT, false, true, tx_buf)--发送扩展帧数据
end
end
-- can.debug(true)
-- gpio.setup(stb_pin,0) -- 配置STB引脚为输出低电平
gpio.setup(stb_pin, 1) -- 780EPM V1.3开发板STB信号有逻辑取反,要配置成输出高电
can.init(can_id, 128) -- 初始化CAN,参数为CAN ID,接收缓存消息数的最大值
can.on(can_id, can_cb) -- 注册CAN的回调函数
can.timing(can_id, 1000000, 6, 6, 4, 2) -- CAN总线配置时序
local num = 0x00f -- 接收屏蔽寄存器值,对应bit写0表示需要检测,写1表示不检测,默认是0xff ff ff ff,不过滤全接收
-- 接收消息过滤(以下四行代码四选一使用)
can.node(can_id, rx_id, can.EXT) -- 只接收消息id为rx_id的扩展帧数据
-- can.filter(can_id, false, 0x123 << 21, (num << 21) | 0x1FFFFF) -- 接收消息id为0x12开头的标准帧数据,0x120~0x12f,左移后接收屏蔽寄存器的值需要用1填充,所以 | 0x1FFFFF
-- can.filter(can_id, false, 0x12345678 << 3, (num << 3) | 0x7) -- 接收消息id为0x1234开头的扩展帧数据,0x12340000~0x1234ffff,左移后接收屏蔽寄存器的值需要用1填充,所以 | 0x7
-- can.filter(can_id, false, 0, 0xFFFFFFFF) -- 接收所有消息
-- 模式
can.mode(can_id, can.MODE_NORMAL) -- 一旦设置mode就开始正常工作了,此时不能再设置node,timing,filter等
local resend_num = 5 -- 发送失败重发次数
local function send_task()
local send_item
local result, buff_full
-- 遍历数据发送队列send_queue
while true do
sys.waitUntil("CAN_SEND_DATA_EVENT",1000)
while #send_queue > 0 do
send_res = false
-- 取数据发送
send_item = table.remove(send_queue, 1)
local resend_cnt = 0
while not send_res do
can.tx(send_item.id, send_item.msg_id, send_item.id_type, send_item.RTR, send_item.need_ack,
send_item.data)
sys.waitUntil("CAN_SEND_DATA_RES",500)
sys.wait(10)
resend_cnt = resend_cnt + 1
if resend_cnt >= resend_num then
log.warn("can send", "重发次数到达上限,停止重发")
-- 默认直接丢弃数据
-- table.insert(send_queue, send_item) -- 重新插入发送队列
break
end
end
end
end
end
sys.taskInit(send_task)
sys.taskInit(can_tx_test)
@@ -0,0 +1,104 @@
--[[
@module can_self_test
@summary CAN总线自测模式使用示例
@version 1.0
@date 2025.11.25
@author 魏健强
@usage
本文件为CAN总线自测模式使用示例,核心业务逻辑为:
1. 初始化CAN总线
2. 启用测试模式测试数据自发自收
本文件没有对外接口,直接在main.lua模块中require "can_self_test"就可以加载运行;
]]
local can_id = 0
local stb_pin = 28 -- 780EPM V1.3开发板上STB引脚为GPIO28
local tx_id = 0x12345677
local test_cnt = 0
local tx_buf = zbuff.create(8) --创建zbuff
gpio.setup(23, 1) -- 要手动打开,开发板can电路需要使用gpio23引脚作为参考电平。
local function can_cb(id, cb_type, param)
if cb_type == can.CB_MSG then
log.info("有新的消息")
local succ, msg_id, id_type, rtr, data = can.rx(id)
while succ do
log.info(mcu.x32(msg_id), #data, data:toHex())
succ, msg_id, id_type, rtr, data = can.rx(id)
end
end
if cb_type == can.CB_TX then
if param then
log.info("发送成功")
else
log.info("发送失败")
end
end
if cb_type == can.CB_ERR then
-- param参数就是4字节错误码
log.error("CAN错误", "错误码:", string.format("0x%08X", param))
-- 解析错误码
local direction = (param >> 16) & 0xFF -- byte2: 方向
local error_type = (param >> 8) & 0xFF -- byte1: 错误类型
local position = param & 0xFF -- byte0: 错误位置
-- 判断错误方向
if direction == 0 then
log.info("错误方向", "发送错误")
else
log.info("错误方向", "接收错误")
end
-- 判断错误类型
if error_type == 0 then
log.info("错误类型", "位错误")
elseif error_type == 1 then
log.info("错误类型", "格式错误")
elseif error_type == 2 then
log.info("错误类型", "填充错误")
end
-- 输出错误位置
log.info("错误位置", string.format("0x%02X", position))
end
if cb_type == can.CB_STATE then
-- 获取总线状态
local state = can.state(can_id)
log.info("can.state", "当前状态", state)
-- 根据状态处理
if state == can.STATE_ACTIVE then
log.info("can.state", "总线正常")
elseif state == can.STATE_PASSIVE then
log.warn("can.state", "被动错误状态")
elseif state == can.STATE_BUSOFF then
log.error("can.state", "总线离线")
-- 需要手动恢复
can.reset(can_id)
end
end
end
local function can_tx_test()
log.info("can tx")
test_cnt = test_cnt + 1
if test_cnt > 8 then
test_cnt = 1
end
tx_buf:set(0,test_cnt) --zbuff的类似于memset操作,类似于memset(&buff[start], num, len)
tx_buf:seek(test_cnt) --zbuff设置光标位置(可能与当前指针位置有关;执行后指针会被设置到指定位置)
can.tx(can_id, tx_id, can.EXT, false, true, tx_buf)
end
-- can.debug(true)
-- gpio.setup(stb_pin,0) -- 配置STB引脚为输出低电平
gpio.setup(stb_pin, 1) -- 780EPM V1.3开发板STB信号有逻辑取反,要配置成输出高电
can.init(can_id, 128) -- 初始化CAN,参数为CAN ID,接收缓存消息数的最大值
can.on(can_id, can_cb) -- 注册CAN的回调函数
can.timing(can_id, 1000000, 6, 6, 4, 2) --CAN总线配置时序
can.node(can_id, tx_id, can.EXT) -- 测试模式下,允许接收的ID和发送ID一致才会有新数据提醒
can.mode(can_id, can.MODE_TEST) -- 如果只是自身测试硬件好坏,可以用测试模式来验证,如果发送成功就OK
sys.timerLoopStart(can_tx_test, 2000)
@@ -0,0 +1,115 @@
--[[
@module can_sleep
@summary CAN总线休眠模式使用示例
@version 1.0
@date 2025.11.25
@author 魏健强
@usage
本文件为CAN总线休眠模式使用示例,核心业务逻辑为:
1. 初始化CAN总线
2. 空闲时间进入休眠模式,定时唤醒发送数据
本文件没有对外接口,直接在main.lua模块中require "can_self_test"就可以加载运行;
]]
local can_id = 0
local stb_pin = 28 -- 780EPM V1.3开发板上STB引脚为GPIO28
local rx_id = 0x12345678
local tx_id = 0x12345677
local test_cnt = 0
local tx_buf = zbuff.create(8) --创建zbuff
gpio.setup(23, 1) -- 要手动打开,开发板can电路需要使用gpio23引脚作为参考电平。
local function can_cb(id, cb_type, param)
if cb_type == can.CB_MSG then
log.info("有新的消息")
local succ, msg_id, id_type, rtr, data = can.rx(id)
while succ do
log.info(mcu.x32(msg_id), #data, data:toHex())
succ, msg_id, id_type, rtr, data = can.rx(id)
end
end
if cb_type == can.CB_TX then
if param then
log.info("发送成功")
else
log.info("发送失败")
end
end
if cb_type == can.CB_ERR then
-- param参数就是4字节错误码
log.error("CAN错误", "错误码:", string.format("0x%08X", param))
-- 解析错误码
local direction = (param >> 16) & 0xFF -- byte2: 方向
local error_type = (param >> 8) & 0xFF -- byte1: 错误类型
local position = param & 0xFF -- byte0: 错误位置
-- 判断错误方向
if direction == 0 then
log.info("错误方向", "发送错误")
else
log.info("错误方向", "接收错误")
end
-- 判断错误类型
if error_type == 0 then
log.info("错误类型", "位错误")
elseif error_type == 1 then
log.info("错误类型", "格式错误")
elseif error_type == 2 then
log.info("错误类型", "填充错误")
end
-- 输出错误位置
log.info("错误位置", string.format("0x%02X", position))
end
if cb_type == can.CB_STATE then
-- 获取总线状态
local state = can.state(can_id)
log.info("can.state", "当前状态", state)
-- 根据状态处理
if state == can.STATE_ACTIVE then
log.info("can.state", "总线正常")
elseif state == can.STATE_PASSIVE then
log.warn("can.state", "被动错误状态")
elseif state == can.STATE_BUSOFF then
log.error("can.state", "总线离线")
-- 需要手动恢复
can.reset(can_id)
end
end
end
local function can_tx_test()
log.info("can tx")
test_cnt = test_cnt + 1
if test_cnt > 8 then
test_cnt = 1
end
tx_buf:set(0,test_cnt) --zbuff的类似于memset操作,类似于memset(&buff[start], num, len)
tx_buf:seek(test_cnt) --zbuff设置光标位置(可能与当前指针位置有关;执行后指针会被设置到指定位置)
can.tx(can_id, tx_id, can.EXT, false, true, tx_buf)
end
-- can.debug(true)
-- gpio.setup(stb_pin,0) -- 配置STB引脚为输出低电平
gpio.setup(stb_pin, 1) -- 780EPM V1.3开发板STB信号有逻辑取反,要配置成输出高电
can.init(can_id, 128) -- 初始化CAN,参数为CAN ID,接收缓存消息数的最大值
can.on(can_id, can_cb) -- 注册CAN的回调函数
can.timing(can_id, 1000000, 6, 6, 4, 2) --CAN总线配置时序
can.node(can_id, rx_id, can.EXT) -- 设置过滤,只接收消息id为rx_id的扩展帧数据
can.mode(can_id, can.MODE_SLEEP) -- 设置sleep模式
local function CAN_MODE_SLEEP()
while true do
sys.wait(1000)
log.info("can_state", can.state(can_id))
if can.state(can_id) == can.STATE_ACTIVE then
can.mode(can_id, can.MODE_SLEEP)
end
end
end
sys.taskInit(CAN_MODE_SLEEP)
sys.timerLoopStart(can_tx_test, 10000)
@@ -0,0 +1,65 @@
--[[
@module main
@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
@version 1.0
@date 2025.11.25
@author 魏健强
@usage
本demo演示的核心功能为:
演示can功能的使用:
1. can正常工作模式
2. can自测模式,自发自收
3. can休眠模式
]]
--[[
必须定义PROJECT和VERSION变量,Luatools工具会用到这两个变量,远程升级功能也会用到这两个变量
PROJECT:项目名,ascii string类型
可以随便定义,只要不使用,就行
VERSION:项目版本号,ascii string类型
如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为999
如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
]]-- Luatools需要PROJECT和VERSION这两个信息
PROJECT = "can"
VERSION = "001.999.000"
-- 在日志中打印项目名和项目版本号
log.info("main", PROJECT, VERSION)
-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
-- 启动errDump日志存储并且上传功能,600秒上传一次
-- if errDump then
-- errDump.config(true, 600)
-- end
-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
-- 可以使用合宙的iot.openluat.com平台进行远程升级
-- 也可以使用客户自己搭建的平台进行远程升级
-- 远程升级的详细用法,可以参考fota的demo进行使用
-- 启动一个循环定时器
-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
-- 方便分析内存使用是否有异常
-- sys.timerLoopStart(function()
-- log.info("mem.lua", rtos.meminfo())
-- log.info("mem.sys", rtos.meminfo("sys"))
-- end, 3000)
require "can_normal"
-- require "can_self_test"
-- require "can_sleep"
-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!
@@ -0,0 +1,68 @@
## 演示模块概述
1、main.lua:主程序入口;
2、can_normal:CAN总线正常工作模式使用示例;
3、can_self_testCAN总线自测模式使用示例;
4、can_sleepCAN总线休眠模式使用示例;
## 演示功能概述
使用Air780EPM开发板测试can相关功能。
## 演示硬件环境
![](https://docs.openluat.com/air780epm/luatos/app/driver/eth/image/RFSvb75NRoEWqYxfCRVcVrOKnsf.jpg)
1、Air780EPM V1.3版本开发板一块:
2、TYPE-C USB数据线一根 + UUSB_CAN调试工具,Air780EPM V1.3版本开发板和数据线的硬件接线方式为:
- Air780EPM V1.3版本开发板通过TYPE-C USB口供电;(外部供电/USB供电 拨动开关 拨到 USB供电一端)
- TYPE-C USB数据线直接插到核心板的TYPE-C USB座子,另外一端连接电脑USB口;
- USB_CAN调试工具;
[购买链接](https://item.taobao.com/item.htm?ali_refid=a3_420434_1006%3A1330120002%3AH%3A9SFQIGQ4mY23bDITa5hTWDVt9Q0NFo%2BR%3Af5b96aea99b81f4ec91d26df711cca50&ali_trackid=282_f5b96aea99b81f4ec91d26df711cca50&id=812265392082&mi_id=0000vjVYSYIby0jbxBAjaGtBcBvrJQC3ZiM5PLnmCR72-A4&mm_sceneid=1_0_1265540007_0&priceTId=213e087917624285597364626e0f67&spm=a21n57.1.hoverItem.7&utparam=%7B%22aplus_abtest%22%3A%2287f8626860a1f088a37e15c1554afbf4%22%7D&xxc=ad_ztc&skuId=5507953049692)
3、接线说明:
| Air780EPM开发板 | USB_CAN调试工具 |
| ----------------- | ------------- |
| H | H |
| L | L |
| GND | GND |
![](https://docs.openluat.com/air780epm/luatos/app/driver/can/image/VGHJbqeJVobFHYxx11Dcy2a8ngf.png)
## 演示软件环境
1、Luatools下载调试工具
2、[Air780EPM V2016版本固件](https://docs.openluat.com/air780epm/luatos/firmware/version/)(理论上,最新发布的固件都可以)
3、PC端的USB_CAN调试工具上位机软件;ZCANPRO和UCANFDtoCANFDNETTool
![](https://docs.openluat.com/air780epm/luatos/app/driver/can/image/usb-can-1.png)
![](https://docs.openluat.com/air780epm/luatos/app/driver/can/image/usb-can-2.png)
![](https://docs.openluat.com/air780epm/luatos/app/driver/can/image/usb-can-3.png)
## 演示核心步骤
1、搭建好硬件环境
2、main.lua 中加载需要用的功能模块,三个功能模块同时只能选择一个使用,其他的注释。
3、Luatools 烧录内核固件和修改后的 demo 脚本代码
4、烧录成功后,代码会自动运行,查看打印日志,如果正常运行,会打印can初始化和can收发数据等相关信息。
5、can_normal
![](https://docs.openluat.com/air780epm/luatos/app/driver/can/image/780EPM-can1.png)
6、can_self_test
![](https://docs.openluat.com/air780epm/luatos/app/driver/can/image/780EPM-can2.png)
7、can_sleep
![](https://docs.openluat.com/air780epm/luatos/app/driver/can/image/780EPM-can3.png)