DBC_2_C上位机前言位域的局限cantools简介DBC_2_C简介DBC_2_C生成文件使用DBC_2_C的局限性尾声链接前言时隔半年我又回来了今天带来的是一款相对实用一点的工具——DBC_2_C。在汽车电子圈里混的各位佬都知道主机厂定义信号矩阵像lin啊can啊这些8个字节分成了十几个或者几十个信号每个信号的意义也不同如何从一帧数据截取对应的信号如何将信号打包到一帧数据里边再发送到总线。本人之前使用到的是位域相比移位我更喜欢位域清晰简单。第一次接触到位域也是刚进入行业的时候。当时很感叹位域设计的精妙这不就是为解析lin报文而生的嘛直到那次我意识到位域的局限性这是后话了下面会介绍到。然后我又去咨询了两位同事他们的之前做法是移位。“手写” 移位函数。虽然已经猜到他们是这么做的但是还是对他们的回答很失望。没有让我眼前一亮的方案一个个信号去移位要累死个人啊要是真得舍弃掉位域移位运算我也是真不想要。有没有一个好的折中的简单的方案。有工具生成。python提供了一个可以解析can报文的库cantools搭建好环境选择dbc直接就给你生成两个全是移位函数的文件。是不是这个cantools的存在感比较低好多人都没怎么听说我也是deekseek推荐才知道的。好了。如果只是为了找到一个可以生成移位函数的工具文章到这里就可以结束了。哈哈哈。但是我就不死犟的我打算打造出属于我的工具位域的局限好了上面废话有点多了进入正题。#includestdio.htypedefunion{struct{unsignedcharAS_NtcOpen_Fault:3;unsignedcharDR_NtcOpen_Fault:3;unsignedcharAS_DOOR_STA:2;unsignedcharDR_DOOR_STA:3;unsignedcharreserved1:5;unsignedcharreserved2:5;unsignedcharreserved3:5;unsignedcharreserved4:5;unsignedcharreserved5:5;unsignedcharreserved6:5;unsignedcharreserved7:5;}Bit;unsignedcharByte[8];}BCM_LIN_DRVR_UNION;intmain(){BCM_LIN_DRVR_UNION look;look.Bit.AS_NtcOpen_Fault7;look.Bit.DR_NtcOpen_Fault2;printf(0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n,look.Byte[0],look.Byte[1],look.Byte[2],look.Byte[3],look.Byte[4],look.Byte[5],look.Byte[6],look.Byte[7]);return0;}拿这段代码来说之前在芯旺微的芯片上跑输出的是0xE8 0x00…同样的代码在nxp的s32k144上跑却是0x17 0x00…看下边这个图片就可以一目了然是因为什么deepseek给出的解释就是不同的编译器位域在内存中的布局是不一样的像gcciar都会有差别。在来看看下边两张图片这个是c语言在线编译器采用gcc的两个不同的版本编译相同的代码输出也存在差别。不同的编译器对位域的实现是会有影响的。一方面不太方便移植到不同的平台上边去。另一方面把信号映射到位域上边去还要根据存储规则去做运算就太多此一举了直接采用移位运算。可能有些芯片可以更改lsb还是msb但是s32ds我没找到哪里可以设置我太菜了。cantools简介python环境搭建根据上边的链接自己搭建一下python环境还有安装下cantools这个库。接下来让我来介绍一下生成了些什么东西。cantools生成.c文件DBC_2_C简介这个上位机界面很简单几个按钮几个文本框就完了主要功能也仅是生成文件所以就单线程了一般不会导致界面卡死的。主要生成的就是pack、unpack、encode、decode、is_in_range函数以及不同id的结构体包含的哪些信号。在cantools的基础上对代码排布代码功能做了点优化。dbc_2_c生成.c文件新建一个Generate_File_DBC2C_wby_dbc_mess_0x501_t结构体然后将这个结构体调用pack函数给到目标数组底层根据这个目标数组发送到总线底层接收到一帧数据然后将缓存unpack到Generate_File_DBC2C_wby_dbc_mess_0x501_t就完成了信号的截取。到这里其实以及够了但是怎么感觉有点膈应这一块老感觉这一块会经常出问题。所以我又做了优化新增了.c和.h中/start/ 到 /end/中间的那一块代码新增代码部分DBC_2_C生成文件使用生成文件的使用voidDBC2C_Update_device(void){uint8_t*data_bufferNULL;if(dbc_2_c_find_array(Generate_File_DBC2C_wby_dbc_mess_0x503_FRAME_ID,data_buffer)!-1){//wby_dbc_mess_0x503_pack(data_buffer,wby_dbc_mess_0x503_w);}if(dbc_2_c_find_array(Generate_File_DBC2C_CANV_CCM_0x18000001_CAN14_FRAME_ID,data_buffer)!-1){CANV_CCM_0x18000001_CAN14_pack(data_buffer,CANV_CCM_0x18000001_CAN14_w);}}应应层把所有id的pack函数都统一写到这里会查找dbc有无对应的id把信号映射到对应的缓冲区。这个缓冲区驱动层同样要使用到驱动层利用dbc_2_c_find_array找到对应的缓冲区。/*main函数里边调用*/native_CAN0_writge_tmp_extension(1,0x18000001);native_CAN0_writge_tmp_standard(3,0x501,0x506,0x502);/*写函数实现*/voidnative_CAN0_writge_tmp_extension(intcount,...){can_message_tTx_msg_1;Tx_msg_1.cs0U;int8_tarray_length0;uint32_tcanid;uint8_t*data_bufferNULL;va_list args;va_start(args,count);for(inti0;icount;i){canidva_arg(args,int);array_lengthdbc_2_c_find_array(canid,data_buffer);if(array_length-1){//警告}else{Tx_msg_1.idcanid;memcpy(Tx_msg_1.data,data_buffer,array_length);Tx_msg_1.lengtharray_length;while(CAN_Send(can_pal0_instance,TX_MAILBOX_CAN0,Tx_msg_1)!STATUS_SUCCESS);}}va_end(args);}/*读函数实现*/voidDBC2C_Update_standard_driver(uint32_tcanid,uint8_t*data_buffer){int8_tdata_length;data_lengthdbc_2_c_find_array(recvMsg_CAN1.id,data_buffer);if(data_length!-1){memcpy(data_buffer,recvMsg_CAN1.data,data_length);if(recvMsg_CAN1.idGenerate_File_DBC2C_wby_dbc_mess_0x503_FRAME_ID){wby_dbc_mess_0x503_unpack(wby_dbc_mess_0x503_r,data_buffer);}}}这里使用到可变参数第一个形参是参数的个数后边跟随的是要发送的id如果id在dbc中没有找到就不会去发送。另外驱动层相对麻烦一点哪个id放在哪个can口通过哪个邮箱发送出去从哪个邮箱接收接收的can还是canfd。所以很难做到在Generate_File_DBC2C.c生成一个函数供底层调用。如果大佬们有好的想法也可以一起沟通改进。DBC_2_C的局限性视频中也讲到了关于这个上位机的一些不足1、虽然qt的代码可以识别哪个些d是can哪些id是canfd但是生成代码是没有开放给底层去调用的。如果有需要后边会做一个接口。2、上位机要求导入的dbc必须全是大端或者全是小端如果一个id中大小端相间解析会出错一方面这种大端插小端的场景很少见另一方面大端插小端实现的难度有点大特别的pack和unpack函数先这样子吧后边心血来潮再来解决这个问题。3、不对dbc文件进行检查。生成之前一定要确保dbc是正确的DBC_2_C只会将错就错不提供纠错功能。尾声视频录制时间有点晚我都有点不知道在说什么了大家凑合着看虽然 “DBC_2_C” 看起来还是个毛坯房但是精装修一下应该还是有点价值的。就这样子吧下课链接上位机和源码