硬汉嵌入式论坛

 找回密码
 立即注册
搜索
查看: 259|回复: 1
收起左侧

[CAN助手] H7-TOOL的CAN/CANFD助手增加DBC文件解析功能,同时增加ID过滤UI配置(2026-10-07)

[复制链接]

1万

主题

7万

回帖

12万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
124403
QQ
发表于 2026-10-7 00:00:57 | 显示全部楼层 |阅读模式

DBC档也称为CAN database file,是控制局域网络(CAN)设备的设备描述档案,其中会包括各物理量的名称、转换比例、定义等资料。

这几天学习了下CAN DBC文件,并为TOOL添加了支持,上位机这里加载DBC文件,直接就可以生成解码器LUA文件,然后勾选上就可以测试了,非常方便

234.png

动态图:

余晖效果29.gif

同时增加ID过滤的UI配置,之前是LUA脚本设置的,大家使用不够便捷,直接将其放在上位机上,方便大家配置。
129.png








回复

使用道具 举报

1万

主题

7万

回帖

12万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
124403
QQ
 楼主| 发表于 2026-10-7 00:02:15 | 显示全部楼层
备份另外一个TOOL发送的测试脚本


[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

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2026-10-22 23:40 , Processed in 0.127057 second(s), 27 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表