[Lua] 纯文本查看 复制代码
--==============================================================================
-- H7-TOOL CAN 发送脚本 : 模拟一个"设备", 每 100ms 发一轮
-- 用于验证上位机 CAN 助手的 DBC 解码效果
--
-- 配套文件 (相对 exe 目录)
-- DBC : ini\can\edge_cases.dbc
-- 解码器 : ini\can\Decoder\edge_cases_DBC.lua <-- 上位机解码器下拉框选它
--
-- 用法
-- 1) 上位机 -> CAN 助手 -> 波特率 1M (经典CAN) -> 解码器选 edge_cases_DBC
-- 2) 把本脚本下载到 H7-TOOL 运行 (每 100ms 发送一轮)
-- 3) 对照本脚本打印的 [send] 物理值 与 CAN 助手里解析出的信号值
--
-- API 依据 (固件 User/lua/if/lua_if_can.c)
-- can_bus("open", 帧格式, 最大字节数, 仲裁波特率, 数据波特率)
-- 帧格式: 0=经典CAN 1=CANFD(无BRS) 2=CANFD(BRS)
-- can_bus("send", ID类型, 帧类型, ID, 数据串) ID类型: 0=标准ID 1=扩展ID
-- can_bus("close")
-- delayms(ms)
--
-- 覆盖的 DBC 边界: 小端 / 大端(含跨字节) / 有符号 / 多路复用 /
-- 32位float / 64位double / 扩展帧 / 64字节CANFD
--==============================================================================
--================================ 配置 ========================================
local CFG_DO_OPEN = false -- false = 沿用 CAN 助手已配好的 CAN (推荐, 脚本只发不初始化)
-- true = 脚本自己 can_bus("open") 初始化 (不依赖 CAN 助手)
local CFG_FD = false -- true=CANFD(可多发 0x107/0x108), false=经典CAN
local CFG_BAUD = 1000000 -- 仲裁域波特率 (CFG_DO_OPEN=true 时生效)
local CFG_DBAUD = 2000000 -- 数据域波特率 (经典CAN忽略; FD 建议 2M)
local CFG_PERIOD = 100 -- 发送周期 ms
local CFG_PRINT_N = 10 -- 每 N 轮打印一次摘要 (0=不打印)
local CFG_DUMP = false -- true=每轮打印 0x100 原始字节(排查用)
local CFG_BEEP = true -- 启动时蜂鸣一声
--============================== 通用位打包 ====================================
-- 用 byte 数组构造, 最后 string.char 成 Lua 字符串
local function new_buf(n)
local t = {}
for i = 1, n do t[i] = 0 end
return t
end
local function mask_of(len)
if len >= 64 then return -1 end
return (1 << len) - 1
end
-- 小端(Intel): 起始位放 LSB, 位序向高位增长
local function set_le(t, sb, len, raw)
raw = raw & mask_of(len)
for i = 0, len - 1 do
local p = sb + i
local idx = (p // 8) + 1
local bit = 1 << (p % 8)
if (raw >> i) & 1 == 1 then
t[idx] = t[idx] | bit
else
t[idx] = t[idx] & (0xFF ~ bit)
end
end
end
-- 大端(Motorola): 起始位放 MSB, 其后按 DBC 锯齿编号推进
local function set_be(t, sb, len, raw)
raw = raw & mask_of(len)
local pos = sb
for i = len - 1, 0, -1 do
local row = pos // 8
local col = pos % 8
local bit = 1 << col
if (raw >> i) & 1 == 1 then
t[row + 1] = t[row + 1] | bit
else
t[row + 1] = t[row + 1] & (0xFF ~ bit)
end
pos = (col == 0) and ((row + 1) * 8 + 7) or (pos - 1)
end
end
-- 小端逐字节写入(供 float/double 使用)
local function set_bytes_le(t, sb, s)
for i = 1, #s do
set_le(t, sb + (i - 1) * 8, 8, string.byte(s, i))
end
end
local function put_f32(t, sb, v) set_bytes_le(t, sb, string.pack("<f", v)) end
local function put_f64(t, sb, v) set_bytes_le(t, sb, string.pack("<d", v)) end
-- 物理值 -> 原始值 -> 落位
local function enc(t, sb, len, be, signed, factor, offset, phys)
if not factor or factor == 0 then factor = 1 end
local raw = math.floor((phys - (offset or 0)) / factor + 0.5)
if signed and raw < 0 then raw = raw + (1 << len) end
if be == 1 then set_be(t, sb, len, raw) else set_le(t, sb, len, raw) end
end
local function to_str(t, n)
return string.char(table.unpack(t, 1, n))
end
--==============================================================================
-- 按轮次 n 构造整轮报文
-- 返回 { {id=, ext=, s=数据串, e={信号名=物理值}}, ... }
-- 纯函数, 不依赖 can_bus, 便于离线对拍
--==============================================================================
function SimBuild(n, fd)
local out = {}
local function push(id, ext, buf, len, e)
out[#out + 1] = { id = id, ext = ext, s = to_str(buf, len), e = e }
end
--==================== 0x100 IntelMsg : 小端/有符号/带枚举 ====================
local vI1 = n % 2
local vI8 = n % 3
local vI16 = (n % 200) * 10 -- rpm
local vI32s = -100 + (n % 201) -- V
local b = new_buf(8)
enc(b, 0, 1, 0, false, 1, 0, vI1)
enc(b, 8, 8, 0, false, 1, 0, vI8)
enc(b, 16, 16, 0, false, 0.1, 0, vI16)
enc(b, 32, 32, 0, true, 0.01, -5, vI32s)
push(0x100, 0, b, 8, { I1 = vI1, I8 = vI8, I16 = vI16, I32s = vI32s })
--==================== 0x101 MotorolaMsg : 大端 ===============================
local vM8 = n % 256
local vM16 = (n % 1001) * 0.25 -- deg
b = new_buf(8)
enc(b, 7, 8, 1, false, 1, 0, vM8)
enc(b, 23, 16, 1, false, 0.25, 0, vM16)
push(0x101, 0, b, 8, { M8 = vM8, M16 = vM16 })
--==================== 0x10A Motorola32Msg : 大端 32 位有符号 ==================
local vM32s = (n * 1000) % 4000001 - 2000000
b = new_buf(8)
enc(b, 31, 32, 1, true, 1, 0, vM32s)
push(0x10A, 0, b, 8, { M32s = vM32s })
--==================== 0x109 CrossByteMsg : 大端跨字节 12 位 ===================
local vMCross = n % 4096
b = new_buf(8)
enc(b, 15, 12, 1, false, 1, 0, vMCross)
push(0x109, 0, b, 8, { MCross = vMCross })
--==================== 0x103 SignedIntel : 小端有符号 =========================
local vSI12 = (n % 4096) - 2048
local vSI48 = (n * 100000) % 2000000000 - 1000000000
b = new_buf(8)
enc(b, 4, 12, 0, true, 1, 0, vSI12)
enc(b, 16, 48, 0, true, 1, 0, vSI48)
push(0x103, 0, b, 8, { SI12 = vSI12, SI48 = vSI48 })
--==================== 0x104 MuxMsg : 多路复用 =================================
local vSel = n % 2
local vMuxA = n % 60000
local vMuxB = (n % 30000) * 2
b = new_buf(8)
enc(b, 0, 8, 0, false, 1, 0, vSel)
if vSel == 0 then
enc(b, 8, 16, 0, false, 1, 0, vMuxA)
push(0x104, 0, b, 8, { MuxSel = vSel, MuxA = vMuxA })
else
enc(b, 8, 16, 0, false, 2, 0, vMuxB)
push(0x104, 0, b, 8, { MuxSel = vSel, MuxB = vMuxB })
end
--==================== 0x105 FloatMsg : 32 位 IEEE float =======================
local fv = (n % 100) / 2 -- float32 原始值
local vF32 = fv * 2 + 1 -- 物理值 (factor 2, offset 1)
b = new_buf(8)
put_f32(b, 0, fv)
push(0x105, 0, b, 8, { F32 = vF32 })
--==================== 0x106 BigMotorola : 大端 48 位 ==========================
local vBM48 = (n * 123456789) % 281474976710656
b = new_buf(8)
enc(b, 7, 48, 1, false, 1, 0, vBM48)
push(0x106, 0, b, 8, { BM48 = vBM48 })
--==================== 0x18FF50E5 ExtMsg : 扩展帧 ==============================
local vE16 = n % 65536
b = new_buf(8)
enc(b, 0, 16, 0, false, 1, 0, vE16)
push(0x18FF50E5, 1, b, 8, { E16 = vE16 })
--==================== CANFD 专属报文 =========================================
if fd then
-- 0x107 FdIntel (64 字节)
local vFdA = n % 4096
local vFdB = n % 65536
b = new_buf(64)
enc(b, 100, 12, 0, false, 1, 0, vFdA)
enc(b, 496, 16, 0, false, 1, 0, vFdB)
push(0x107, 0, b, 64, { FdA = vFdA, FdB = vFdB })
-- 0x108 FdDouble (16 字节, 64 位 double)
local vD64 = 1000 + (n % 1000) * 0.5
b = new_buf(16)
put_f64(b, 0, vD64)
push(0x108, 0, b, 16, { D64 = vD64 })
end
return out
end
--============================== 主程序 =======================================
-- 只在真机环境运行 (上位机里 can_bus 是已注册的 API);
-- 离线对拍时只调用上面的 SimBuild, 不定义 can_bus, 因此不会进主循环
if type(can_bus) == "function" then
if CFG_DO_OPEN then
local frame = CFG_FD and 2 or 0
local bytes = CFG_FD and 64 or 8
print(string.format("can_sim: open frame=%d bytes=%d baud=%d/%d",
frame, bytes, CFG_BAUD, CFG_DBAUD))
print("can_sim: open re =", can_bus("open", frame, bytes, CFG_BAUD, CFG_DBAUD))
else
print("can_sim: 复用 CAN 助手已配置的 CAN (CFG_DO_OPEN=false)")
end
if CFG_BEEP then beep() end
print("can_sim: start, period =", CFG_PERIOD, "ms")
local n = 0
while true do
local list = SimBuild(n, CFG_FD)
-- 发送整轮
for i = 1, #list do
local f = list[i]
can_bus("send", f.ext, 0, f.id, f.s)
end
-- 打印
if CFG_PRINT_N > 0 and (n % CFG_PRINT_N == 0) then
print(string.format("round=%d I1=%d I8=%d I16=%drpm I32s=%dV M8=%d M16=%.2fdeg SI12=%d Mux=%d F32=%.2fV E16=%d",
n, n % 2, n % 3, (n % 200) * 10, -100 + (n % 201),
n % 256, (n % 1001) * 0.25, (n % 4096) - 2048, n % 2,
(((n % 100) / 2) * 2 + 1), n % 65536))
if CFG_BEEP then beep() end
end
if CFG_DUMP then
-- 打印 0x100 的原始字节, 便于与 CAN 助手抓包对照
local s = list[1].s
local hex = {}
for i = 1, #s do hex[i] = string.format("%02X", string.byte(s, i)) end
print("0x100 : " .. table.concat(hex, " "))
end
n = n + 1
delayms(CFG_PERIOD)
end
end