MDC300F CAN应用笔记

型号

MDC300F, 刷的新的101版本, 操作系统是Ubuntu18.

有12路CAN可供使用, 本篇当标准CAN来用, 500Kbit/s, 用透传的方式(2ms上传一次)收数据, Event或者Method的方式下发数据.

CAN端口分配

参考 产品文档 -> 接口说明 -> MDC300F接口说明书 -> 数据透传 -> 通路配置说明 -> 通路与硬件线缆配套关系

12路CAN, 程序中的channelID范围[0,11], 对应的官方提供的线束的标签如下:

  • 0, CANAB1
  • 1, RADAR5
  • 2, ECUCAN2
  • 3, RADAR1
  • 4, RADAR2
  • 5, RADAR3
  • 6, GPS1
  • 7, ECUCAN0
  • 8, RADAR4
  • 9, RADAR6
  • 10, ECUCAN1
  • 11, CANAB2

除了反直觉的 CANAB1, CANAB2, GPS1 是绿H黄L, 其余都是正常的黄H绿L.

除了 ECUCAN0/1/2 没有接终端电阻, 其余均接有终端电阻.

SOME/IP定义

mdc_can_abstract_application.arxml 中定义的 udp 端口是 :

  • 上行 54870~54881 (12个端口对应12路CAN, 这是host的, mcu的为51301~51312).
  • 下行 54882~54893 (12个端口对应12路CAN, 这是host的, mcu的为51321~51332).

can_rx_service_interface.arxml 中定义了SOME/IP的Rx Event

  • EventID:0x8001 (32769)
  • ServiceID:0xd0 (208)
  • InstanceID:1~12, 对应12路CAN
  • EventGroupID:0x10

can_rx_service_interface.arxml 还定义了 Tx Method

  • MethodID:0x0001
  • ServiceID:0xd0
  • InstanceID:1~12

can_tx_service_interface.arxml 定义了 Tx Event

  • EventID:0x8001 (32769)
  • ServiceID:0xd1 (209)
  • InstanceID:1~12, 对应12路CAN
  • EventGroupID:0x10

发送参数配置

CAN配置文件的路径 /opt/platform/mdc_platform/runtime_service/devmc/conf/mcu_canbus_config.json

CAN通路配置参数定义 可以参考下CANFD的: 产品文档 -> 接口说明 -> 数据透传 -> 通路配置说明 -> CANFD通路配置参数定义, 或者参考之前版本的产品文档

由于CAN接收是透传(2ms一次), 不用配置接收, 只配置发送即可.

假设 我拿导远的INS570D(标签CAN1)挂到MDC的CAN01(标签RADAR5) :

  • 通信速率500Kbit/s
  • INS570D输出INS信息给MDC
  • MDC需要周期发送(这里用Event方式)四轮轮速和档位信息(可以从车辆CAN获得)给INS570D

由于MMC现在只能配CANFD, 不能配CAN, 所以还是拿模版 mcu_canbus_config_ars408.json 来改. 大概6500多行, 手撕json也不要怂, 有辅助工具. 下面这个就比较好用

https://jsoneditoronline.org/

先在右侧删除所有的 RxIdList, 这个不需要

在这里插入图片描述

12路 RxIdList 都删光以后, 发现只剩了3400多行, 少了将近一半, 接下来再移除除CAN01外所有的TxIdList(如果其它路又需要, 可以从这里拷贝)

在这里插入图片描述

发现只剩1100多行了, 修改CAN01的TxIdList(小于0x800就是标准帧, 否则为扩展帧), 删除多余的ID, 只留下3个坑位, 一共3个标准帧ID, 用来给INS570D发送四轮轮速和档位, 这里输入的CANID是十进制的:

在这里插入图片描述

再确认下通信速率, 确实为500Kbit/s:

在这里插入图片描述

至此, 1051行的 json文件, 就是我们用的, 保存下来上传为 /home/sftpuser/uploads/mcu_canbus_config.json

接下来下发配置

# 切换到root用户
$ su
# 切换到配置路径
$ cd /opt/platform/mdc_platform/runtime_service/devmc/conf
# 备份 mcu_canbus_config.json
$ cp mcu_canbus_config.json mcu_canbus_config.json.bak
# 拷贝配置好的json文件
$ cp /home/sftpuser/uploads/mcu_canbus_config.json mcu_canbus_config.json

# 删除canfd配置
$ mdc-tool mcu delete canfd
# 下发can配置
$ mdc-tool devm set-dev-cfg 1
# 复位mcu让配置生效, 或者断电重启
$ mdc-tool mcu reset mcu

这时应该能在设备上收到 周期性发送的3帧ID, 数据默认是0, 下面是用Xavier充当RADAR5的接收设备, 运行 python3 -m can.viewer -c can1 -i socketcan得到的结果

在这里插入图片描述

数据的值要在下面的程序用Event的方式修改

新建CAN工程

参考GPIO篇, 用MDS新建tk_can工程, 导入官方的 can_sample. 文件树为

$ tree
.
├── cmake
│   └── toolchain.cmake
├── CMakeLists.txt
├── manifest
│   ├── application_sample
│   │   └── can_sample
│   │       └── mdc_can_abstract_application.arxml
│   └── common
│       ├── data_type
│       │   ├── mdc_data_type.arxml
│       │   └── mdc_mode_declaration.arxml
│       ├── machine
│       │   ├── dmini0_machine.arxml
│       │   ├── dmini1_machine.arxml
│       │   ├── dmini2_machine.arxml
│       │   ├── dmini3_machine.arxml
│       │   └── host_machine.arxml
│       └── service_interface
│           └── canfd_can_uart_gpio_sample
│               ├── can_rx_service_interface.arxml
│               └── can_tx_service_interface.arxml
└── modules
    ├── can_sample
    │   ├── CMakeLists.txt
    │   └── src
    │       ├── can.cpp
    │       ├── can.h
    │       └── main.cpp
    └── CMakeLists.txt

12 directories, 17 files

依次点击 Generate, Reload Cmake, Build 按钮, 点齿轮图标配置如下

在这里插入图片描述

点击Launch按钮运行

cantools dbc 数据解析

先不接ins570d, 用xavier给mdc发送数据, 如 cansend can1 233#22.33

发现MDS收到了数据

在这里插入图片描述

接上ins570d, 发现车辆的指示灯已经亮了(表示已经收到轮速和档位的CAN消息), 但是MDS里面这个数据乱蹦, 没啥观赏性, 这里直接给解析出来.

使用cantools通过dbc文件生成c代码:

python3 -m cantools generate_c_source --database-name ins570d -e CP850 .\ins570d.dbc

会生成 ins570d.hins570d.c 这两个文件, 加入到工程中, 这个是可以c/c++混编的. 有了这个dbc生成的c文件, 信号解析起来就和抄作业一样简单, 举例如下:

在这里插入图片描述

回到工程中, 屏蔽掉can.cpp中的打印 std::cout<<"success send the value, reply value is: "<< static_cast<int>(value.result) <<std::endl; 改造下main.cpp的接收(请无视直接拿过来的printf):

#include "ins570d.h"

void ins570d_print(unsigned char channelId, const mdc::mdccan::CanBusDataParam &canData)
{
    for (unsigned int i = 0; i < canData.elementList.size(); i++) {
    	if(canData.elementList[i].canId == INS570D_INS_ACC_FRAME_ID) {	//0x500
    		printf("\n");
    		g_logger.LogInfo();
    	}
    	if(canData.elementList[i].canId < 0x800) {
    		printf("can%d  %03X  [%d] ", channelId, canData.elementList[i].canId, canData.elementList[i].validLen);
    	} else {
    		printf("can%d  %08X  [%d] ", channelId, canData.elementList[i].canId, canData.elementList[i].validLen);
    	}
    	for (unsigned int j = 0; j < canData.elementList[i].validLen; j++) {
    		printf(" %02X", canData.elementList[i].data[j]);
    	}

    	if(canData.elementList[i].canId == INS570D_INS_ACC_FRAME_ID) {	//0x500
    		struct ins570d_ins_acc_t msg;
    		ins570d_ins_acc_unpack(&msg, (uint8_t *)(&canData.elementList[i].data[0]), INS570D_INS_ACC_LENGTH);
    		double acc_x = ins570d_ins_acc_acc_x_decode(msg.acc_x);
    		double acc_y = ins570d_ins_acc_acc_y_decode(msg.acc_y);
    		double acc_z = ins570d_ins_acc_acc_z_decode(msg.acc_z);
    		printf(" :: acc_x: %.3f g, acc_y: %.3f g, acc_z: %.3f g", acc_x, acc_y, acc_z);
    	} else if(canData.elementList[i].canId == INS570D_INS_GYRO_FRAME_ID) {	//0x501
    		struct ins570d_ins_gyro_t msg;
    		ins570d_ins_gyro_unpack(&msg, (uint8_t *)(&canData.elementList[i].data[0]), INS570D_INS_GYRO_LENGTH);
    		double gyro_x = ins570d_ins_gyro_gyro_x_decode(msg.gyro_x);
    		double gyro_y = ins570d_ins_gyro_gyro_y_decode(msg.gyro_y);
    		double gyro_z = ins570d_ins_gyro_gyro_z_decode(msg.gyro_z);
    		printf(" :: gyro_x: %.3f deg/s, gyro_y: %.3f deg/s, gyro_z: %.3f deg/s", gyro_x, gyro_y, gyro_z);
    	} else if(canData.elementList[i].canId == INS570D_INS_HEADING_PITCH_ROLL_FRAME_ID) {	//0x502
    		struct ins570d_ins_heading_pitch_roll_t msg;
    		ins570d_ins_heading_pitch_roll_unpack(&msg, (uint8_t *)(&canData.elementList[i].data[0]), INS570D_INS_HEADING_PITCH_ROLL_LENGTH);
    		double ins_pitch_angle = ins570d_ins_heading_pitch_roll_ins_pitch_angle_decode(msg.ins_pitch_angle);
    		double ins_roll_angle = ins570d_ins_heading_pitch_roll_ins_roll_angle_decode(msg.ins_roll_angle);
    		double ins_heading_angle = ins570d_ins_heading_pitch_roll_ins_heading_angle_decode(msg.ins_heading_angle);
    		printf(" :: ins_pitch_angle: %.3f deg, ins_roll_angle: %.3f deg, ins_heading_angle: %.3f deg", ins_pitch_angle, ins_roll_angle, ins_heading_angle);
    	} else if(canData.elementList[i].canId == INS570D_INS_HEIGHT_AND_TIME_FRAME_ID) {	//0x503
    		struct ins570d_ins_height_and_time_t msg;
    		ins570d_ins_height_and_time_unpack(&msg, (uint8_t *)(&canData.elementList[i].data[0]), INS570D_INS_HEIGHT_AND_TIME_LENGTH);
    		double ins_locat_heigh = ins570d_ins_height_and_time_ins_locat_heigh_decode(msg.ins_locat_heigh);
    		double ins_time = ins570d_ins_height_and_time_ins_time_decode(msg.ins_time);
    		printf(" :: ins_locat_heigh: %.3f m, ins_time: %.3f ms", ins_locat_heigh, ins_time);
    	} else if(canData.elementList[i].canId == INS570D_INS_LATITUDE_LONGITUDE_FRAME_ID) {	//0x504
    		struct ins570d_ins_latitude_longitude_t msg;
    		ins570d_ins_latitude_longitude_unpack(&msg, (uint8_t *)(&canData.elementList[i].data[0]), INS570D_INS_LATITUDE_LONGITUDE_LENGTH);
    		double ins_latitude = ins570d_ins_latitude_longitude_ins_latitude_decode(msg.ins_latitude);
    		double ins_longitude = ins570d_ins_latitude_longitude_ins_longitude_decode(msg.ins_longitude);
    		printf(" :: ins_latitude: %.3f deg, ins_longitude: %.3f deg", ins_latitude, ins_longitude);
    	} else if(canData.elementList[i].canId == INS570D_INS_SPEED_FRAME_ID) {	//0x505
    		struct ins570d_ins_speed_t msg;
    		ins570d_ins_speed_unpack(&msg, (uint8_t *)(&canData.elementList[i].data[0]), INS570D_INS_SPEED_LENGTH);
    		double ins_north_spd = ins570d_ins_speed_ins_north_spd_decode(msg.ins_north_spd);
    		double ins_east_spd = ins570d_ins_speed_ins_east_spd_decode(msg.ins_east_spd);
    		double ins_to_ground_spd = ins570d_ins_speed_ins_to_ground_spd_decode(msg.ins_to_ground_spd);
    		printf(" :: ins_north_spd: %.3f m/s, ins_east_spd: %.3f m/s, ins_to_ground_spd: %.3f m/s", ins_north_spd, ins_east_spd, ins_to_ground_spd);
    	} else if(canData.elementList[i].canId == INS570D_INS_DATA_INFO_FRAME_ID) {	//0x506
    		struct ins570d_ins_data_info_t msg;
    		ins570d_ins_data_info_unpack(&msg, (uint8_t *)(&canData.elementList[i].data[0]), INS570D_INS_DATA_INFO_LENGTH);
    		double ins_gps_flag_pos = ins570d_ins_data_info_ins_gps_flag_pos_decode(msg.ins_gps_flag_pos);
    		double ins_num_sv = ins570d_ins_data_info_ins_num_sv_decode(msg.ins_num_sv);
    		double ins_gps_flag_heading = ins570d_ins_data_info_ins_gps_flag_heading_decode(msg.ins_gps_flag_heading);
    		double ins_gps_age = ins570d_ins_data_info_ins_gps_age_decode(msg.ins_gps_age);
    		double ins_car_status = ins570d_ins_data_info_ins_car_status_decode(msg.ins_car_status);
    		double ins_status = ins570d_ins_data_info_ins_status_decode(msg.ins_status);
    		printf(" :: ins_gps_flag_pos: %.3f, ins_num_sv: %.3f, ins_gps_flag_heading: %.3f, ins_gps_age: %.3f s, ins_car_status: %.3f, ins_status: %.3f",
    				ins_gps_flag_pos, ins_num_sv, ins_gps_flag_heading, ins_gps_age, ins_car_status, ins_status);
    	} else if(canData.elementList[i].canId == INS570D_INS_STD_FRAME_ID) {	//0x507
    		struct ins570d_ins_std_t msg;
    		ins570d_ins_std_unpack(&msg, (uint8_t *)(&canData.elementList[i].data[0]), INS570D_INS_STD_LENGTH);
    		double ins_std_lat = ins570d_ins_std_ins_std_lat_decode(msg.ins_std_lat);
    		double ins_std_lon = ins570d_ins_std_ins_std_lon_decode(msg.ins_std_lon);
    		double ins_std_locat_height = ins570d_ins_std_ins_std_locat_height_decode(msg.ins_std_locat_height);
    		double ins_std_heading = ins570d_ins_std_ins_std_heading_decode(msg.ins_std_heading);
    		printf(" :: ins_std_lat: %.3f, ins_std_lon: %.3f, ins_std_locat_height: %.3f, ins_std_heading: %.3f", ins_std_lat, ins_std_lon, ins_std_locat_height, ins_std_heading);
    	} else if(canData.elementList[i].canId == INS570D_INS_UTC_FRAME_ID) {	//0x508
    		struct ins570d_ins_utc_t msg;
    		ins570d_ins_utc_unpack(&msg, (uint8_t *)(&canData.elementList[i].data[0]), INS570D_INS_UTC_LENGTH);
    		double year = ins570d_ins_utc_utc_year_decode(msg.utc_year);
    		double month = ins570d_ins_utc_utc_month_decode(msg.utc_month);
    		double day = ins570d_ins_utc_utc_day_decode(msg.utc_day);
    		double hour = ins570d_ins_utc_utc_hour_decode(msg.utc_hour);
    		double min = ins570d_ins_utc_utc_min_decode(msg.utc_min);
    		double sec = ins570d_ins_utc_utc_sec_decode(msg.utc_sec);
    		double msec = ins570d_ins_utc_utc_msec_decode(msg.utc_msec);
    		printf("%d-%d-%d %d:%d:%d.%d", (int)year, (int)month, (int)day, (int)hour, (int)min, (int)sec, (int)msec);
    	}

    	printf("\n");
    }
}

void OnCanRawDataRecieved(unsigned char channelId, const mdc::mdccan::CanBusDataParam &canData)
{
    // 入参判断
    if (channelId >= CAN_NUM) {
        return;
    }

    if(channelId == 1) {
    	ins570d_print(channelId, canData);
    }
}

重新编译运行, 发现原始数据和解析后的数据都出来了:

在这里插入图片描述

Event下发数据

接收基本没问题了, 开始改造Event发送.

//can.h
const int SLEEP_TIME = 10000;
const int CHANNEL_ID = 1;
//发送和组包函数增加 canid, data, len 参数
void CanEventSend(unsigned char channelId, uint32_t canid, uint8_t data[], uint8_t len);
void PackageEventCanData(mdc::mdccan::CanBusDataParam &canDataParm, uint32_t canid, uint8_t data[], uint8_t len);

//can.cpp
void McuApInterface::CanEventSend(unsigned char channelId, uint32_t canid, uint8_t data[], uint8_t len)
{
    ...
    // Event组包
    CanBusDataParam canDataParm;
    PackageEventCanData(canDataParm, canid, data, len);
    ...
}
void McuApInterface::PackageEventCanData(CanBusDataParam &canDataParm, uint32_t canid, uint8_t data[], uint8_t len)
{
    // vector中的其中一帧canRawdata
    struct Element canRawdata;
    canRawdata.timeStamp.second = 0xFFFFFFFF;
    canRawdata.timeStamp.nsecond = 0xFFFFFFFF;
    // 下发的CAN帧,需要在canbus_config.json中配置,比如CanId、DataLength
    canRawdata.canId = canid;
    canRawdata.validLen = len;
    // Event下发方式,每包(vector)包含1帧
    for (int i = 0; i < len; i++) {
        canRawdata.data.push_back(data[i]);
    }
    canDataParm.seq = 1;
    canDataParm.elementList.push_back(canRawdata);
}


//main.cpp
int main(int argc, char *argv[])
{
    ...
    int32_t i = -1;
    //这个数据从车辆CAN上更新, 这里造假数据
    //导远给每个厂家的手册可能不一样, 这里用111,222,333代替
    uint8_t data_0x111[8] = {0};
    uint8_t data_0x222[8] = {0};
    uint8_t data_0x333[8] = {0};
    while (1) {
    	i = (i + 1) % 100;
    	if(i%2==0) {	//20ms
    		data_0x111[7]++;	//最后一个字节累加
    		data_0x222[7]++;
    		mcuApInterfaceNode.CanEventSend(CHANNEL_ID, 0x111, data_0x111, 8);
    		mcuApInterfaceNode.CanEventSend(CHANNEL_ID, 0x222, data_0x222, 8);
    	}
    	if(i%20==0) {	//200ms
    		data_0x333[7]++;
    		mcuApInterfaceNode.CanEventSend(CHANNEL_ID, 0x333, data_0x333, 8);
    	}
        usleep(SLEEP_TIME);	//10ms
    }
    return 0
}

运行, 就可以看到不断变化的can值
当然, 也可以仿照接收, 对信号值进行encode, 对消息pack, 再发送, 参考ins570d.c

Method方式下发

例子中用 Method 下发了0x410 的帧, 但是没有收到, 因为mcu_canbus_config.json中没有配, 在原来3个ID的基础上加上这第4个ID(网页上直接复制).

在这里插入图片描述

sftp上传为2.json, 重新配置

cd /opt/platform/mdc_platform/runtime_service/devmc/conf
cp /home/sftpuser/uploads/2.json mcu_canbus_config.json   
mdc-tool devm set-dev-cfg 1
mdc-tool mcu reset mcu

在Xavier上就收到了MDC通过Method方式发出的周期为400ms的CAN数据

$ candump -td -x can1,410:7FF
 (000.000000)  can1  RX - -  410   [8]  01 01 01 01 01 01 01 01
 (000.402229)  can1  RX - -  410   [8]  02 02 02 02 02 02 02 02
 (000.401927)  can1  RX - -  410   [8]  03 03 03 03 03 03 03 03
 (000.402004)  can1  RX - -  410   [8]  04 04 04 04 04 04 04 04
 (000.401919)  can1  RX - -  410   [8]  05 05 05 05 05 05 05 05

多路CAN

如CAN00-底盘, CAN01-惯导, CAN02-EyeQ3, CAN03-毫米波ARS430… 想揉到一个工程里面(不太清楚这样合不合适), 这样来回转发车速轮速之类的信息也方便, 那就使劲造node, 举个栗子

int main(int argc, char *argv[])

{
	ara::log::InitLogging("CAN", "can abstract example", LogLevel::kVerbose, (LogMode::kConsole | LogMode::kRemote));

	McuApInterface can0_node;
	can0_node.Init(0); //物理连接为CANAB1
	g_logger.LogInfo() << "can0_node init success";
	can0_node.RegisterRxEventCallBackFunc(std::bind(&can0_rx_callback, std::placeholders::_1, std::placeholders::_2)); // 注册CAN帧处理回调函数

	McuApInterface can1_node;
	can1_node.Init(1); //物理连接为RADAR5
	g_logger.LogInfo() << "can1_node init success";
	can1_node.RegisterRxEventCallBackFunc(std::bind(&can1_rx_callback, std::placeholders::_1, std::placeholders::_2)); // 注册CAN帧处理回调函数
	...
}

一般工作流程大致为:

  • 确定传感器和MDC连接关系
  • 改 mcu_canbus_config.json 文件, 周期性下发报文
  • 向供应商索取或者自己编写DBC文件, 使用 python cantools 生成c文件, 加到工程中
  • 工程中 解析 / 转发 数据
  • AP to ROS

结语: 由于导远给各个厂家的协议有差异, DBC文件请自己获取, 这里不提供

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值