Kaynağa Gözat

update:更新Air8000 can demo

wjq 3 ay önce
ebeveyn
işleme
5b180d4dea

+ 154 - 0
module/Air8000/demo/can/can_normal.lua

@@ -0,0 +1,154 @@
+--[[
+@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 = 27 -- Air8000开发板上STB引脚为GPIO27
+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
+
+-- 数据插入发送队列
+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, id, id_type, rtr, data = can.rx(id)
+        while succ do
+            log.info(mcu.x32(id), #data, data:toHex())
+            succ, 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引脚为输出低电平
+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.filter(can_id, false, 0x123 << 21, 0x07ffffff) -- 接收消息id为0x12开头的标准帧数据,0x120~0x12f
+-- can.filter(can_id, false, 0x12345678 << 3, 0x07ffff) -- 接收消息id为0x1234开头的扩展帧数据,0x12340000~0x1234ffff
+-- can.filter(can_id, false, 0, 0xFFFFFFFF) -- 接收所有消息
+
+-- 模式
+can.mode(can_id, can.MODE_NORMAL) -- 一旦设置mode就开始正常工作了,此时不能再设置node,timing,filter等
+
+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)
+            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)
+                -- 循环发送直到发送成功
+            end
+        end
+    end
+end
+sys.taskInit(send_task)
+sys.taskInit(can_tx_test)

+ 100 - 0
module/Air8000/demo/can/can_self_test.lua

@@ -0,0 +1,100 @@
+--[[
+@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 = 27 -- Air8000开发板上STB引脚为GPIO27
+local tx_id = 0x12345677
+
+local test_cnt = 0
+local tx_buf = zbuff.create(8)  --创建zbuff
+local function can_cb(id, cb_type, param)
+    if cb_type == can.CB_MSG then
+        log.info("有新的消息")
+        local succ, id, id_type, rtr, data = can.rx(id)
+        while succ do
+            log.info(mcu.x32(id), #data, data:toHex())
+            succ, 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引脚为输出低电平
+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)

+ 111 - 0
module/Air8000/demo/can/can_sleep.lua

@@ -0,0 +1,111 @@
+--[[
+@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 = 27 -- Air8000开发板上STB引脚为GPIO27
+local rx_id = 0x12345678
+local tx_id = 0x12345677
+
+local test_cnt = 0
+local tx_buf = zbuff.create(8)  --创建zbuff
+local function can_cb(id, cb_type, param)
+    if cb_type == can.CB_MSG then
+        log.info("有新的消息")
+        local succ, id, id_type, rtr, data = can.rx(id)
+        while succ do
+            log.info(mcu.x32(id), #data, data:toHex())
+            succ, 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引脚为输出低电平
+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)

+ 71 - 71
module/Air8000/demo/can/main.lua

@@ -1,74 +1,74 @@
-PROJECT = "candemo"
-VERSION = "1.0.0"
-sys = require("sys")
-log.style(1)
-local SELF_TEST_FLAG = true --自测模式标识,写true就进行自收自发模式,写false就进行正常收发模式
-local node_a = true   -- A节点写true, B节点写false
-local can_id = 0
-local rx_id
-local tx_id
-local stb_pin = 28		-- stb引脚根据实际情况写,不用的话,也可以不写
-if node_a then          -- A/B节点区分,互相传输测试
-    rx_id = 0x12345678
-    tx_id = 0x12345677
-else
-    rx_id = 0x12345677
-    tx_id = 0x12345678
-end
-local test_cnt = 0
-local tx_buf = zbuff.create(8)
-local function can_cb(id, cb_type, param)
-    if cb_type == can.CB_MSG then
-        log.info("有新的消息")
-        local succ, id, id_type, rtr, data = can.rx(id)
-        while succ do
-            log.info(mcu.x32(id), #data, data:toHex())
-            succ, 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
-        log.info("CAN错误码", mcu.x32(param))
-    end
-    if cb_type == can.CB_STATE then
-        log.info("CAN新状态", param)
-    end
-end
+--[[
+@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这三位数字必须存在,但是没有任何用处,可以一直写为000
+        如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
+]]-- Luatools需要PROJECT和VERSION这两个信息
+PROJECT = "can"
+VERSION = "001.000.000"
 
-local function can_tx_test(data)
-    if node_a then
-        log.info("node a tx")
-    else
-        log.info("node b tx")
-    end
-	test_cnt = test_cnt + 1
-	if test_cnt > 8 then
-		test_cnt = 1
-	end
-	tx_buf:set(0,test_cnt)
-	tx_buf:seek(test_cnt)
-    can.tx(can_id, tx_id, can.EXT, false, true, tx_buf)
-end
--- can.debug(true)
-gpio.setup(stb_pin,0)
--- gpio.setup(stb_pin,1)	-- 如果开发板上STB信号有逻辑取反,则要配置成输出高电平
-can.init(can_id, 128)
-can.on(can_id, can_cb)
-can.timing(can_id, 1000000, 6, 6, 4, 2)
--- can.timing(can_id, 100000, 6, 6, 3, 2)
-if SELF_TEST_FLAG then
-	can.node(can_id, tx_id, can.EXT)	-- 测试模式下,允许接收的ID和发送ID一致才会有新数据提醒
-	can.mode(can_id, can.MODE_TEST)     -- 如果只是自身测试硬件好坏,可以用测试模式来验证,如果发送成功就OK
-else
-	can.node(can_id, rx_id, can.EXT)
-	can.mode(can_id, can.MODE_NORMAL)   -- 一旦设置mode就开始正常工作了,此时不能再设置node,timing,filter等
+
+-- 在日志中打印项目名和项目版本号
+log.info("main", PROJECT, VERSION)
+
+
+-- 如果内核固件支持wdt看门狗功能,此处对看门狗进行初始化和定时喂狗处理
+-- 如果脚本程序死循环卡死,就会无法及时喂狗,最终会自动重启
+if wdt then
+    --配置喂狗超时时间为9秒钟
+    wdt.init(9000)
+    --启动一个循环定时器,每隔3秒钟喂一次狗
+    sys.timerLoopStart(wdt.feed, 3000)
 end
 
-sys.timerLoopStart(can_tx_test, 1000)
-sys.run()
+
+-- 如果内核固件支持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)
+
+gpio.setup(23, 1) -- 要手动打开,否则无法使用CAN芯片不能正常工作
+
+require "can_normal"
+-- require "can_self_test"
+-- require "can_sleep"
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 68 - 0
module/Air8000/demo/can/readme.md

@@ -0,0 +1,68 @@
+## 演示模块概述
+
+1、main.lua:主程序入口;
+
+2、can_normal:CAN总线正常工作模式使用示例;
+
+3、can_self_test:CAN总线自测模式使用示例;
+
+4、can_sleep:CAN总线休眠模式使用示例;
+
+## 演示功能概述
+
+使用Air8000开发板测试can相关功能。
+
+## 演示硬件环境
+
+![](https://docs.openluat.com/air8000/luatos/app/driver/can/image/It2KbkiQMowvrJxPv7Sc1yQknRf.png)
+
+1、Air8000开发板一块:
+
+2、TYPE-C USB数据线一根 + UUSB_CAN调试工具,Air8000开发板和数据线的硬件接线方式为:
+
+- Air8000开发板通过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、接线说明:
+
+| Air8000开发板    | USB_CAN调试工具 |
+| ----------------- | -------------   |
+| H                 | H               |
+| L                 | L               |
+| GND               | GND             |
+
+![](https://docs.openluat.com/air8000/luatos/app/driver/can/image/Wueab47HaoIIP4xfrTjcp5TCnLe.png)
+
+## 演示软件环境
+
+1、Luatools下载调试工具
+
+2、[Air8000 V2018版本固件](https://docs.openluat.com/air8000/luatos/firmware/)(理论上,最新发布的固件都可以)
+
+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)