5.0 KiB
can_comm
TODO:
- 对
FDCANCommGet()
进行修改,使得其可以返回数据是否更新的相关信息。
重要提醒
如果传输过程中出现多次丢包或长度校验不通过,尤其是传输长度较大的时候,请开启CAN的Auto Retransmission,并尝试修改CANComm实例的发送和接受ID(以提高在总线仲裁中的优先级)。
总览和封装说明
CAN comm是用于CAN多机通信的模块。你不需要关心实现的协议,只需要传入你想发送/接收的结构体的长度(通过sizeof()
获取)即可。单帧发送长度目前限制为60字节,如果需要加长可以在can_comm.h
中修改CAN_COMM_MAX_BUFFSIZE
的定义值。注意CAN的负载以及使用此模块的发送频率,否则可能造成堵塞。
代码结构
.h中放置的是数据定义和外部接口,以及协议的定义和宏,.c中包含一些私有函数。
外部接口
FDCANCommInstance *FDCANCommInit(CANComm_Init_Config_s* comm_config);
void CANCommSend(FDCANCommInstance *instance, uint8_t *data);
void *FDCANCommGet(FDCANCommInstance *instance);
uint8_t FDCANCommIsOnline(FDCANCommInstance *instance);
第一个函数将会初始化一个CANComm实例,返回其指针。使用CANComm进行通信的应用应该保存返回的指针。初始化需要传入一个初始化结构体。请在应用初始化的时候调用该函数。推荐的结构体配置方式如下:
/* 初始化结构体的定义 */
typedef struct
{
can_instance_config_s fdcan_config;
uint8_t send_data_len; //发送数据长度
uint8_t recv_data_len; //接收数据长度,两者请用sizeof获取
} CANComm_Init_Config_s;
CANComm_Init_Config_s cconfig = {
.fdcan_config = {
.can_handle=&hcan1,
.tx_id=0x02,
.rx_id=0x03},
.send_data_len = sizeof(your_data_structure),
.recv_data_len = sizeof(recv_data_structure)
};
第二个函数将会通过CAN发送一帧数据。这里的“帧”不是CAN的一帧报文,而是你使用的数据。在CANComm初始化的时候,使用sizeof获得你要发送的数据的长度。调用此函数时,将你的数据结构或数据cast成u8
类型的指针传入即可。==特别注意,你的结构体需要使用预编译指令#pragma pack(1)
以及#pragma pack()
包裹==,从而进行字节压缩而不进行字节对齐,如:
#pragma pack(1)
typedef struct
{
uint8_t aa;
float bb;
double cc;
uint16_t dd;
} some_struct;
#pragma pack()
只有这样,这个结构体才不会进行padding扩充字节实现字节对齐。你可以尝试一下,如果不使用pack处理,上面的结构体将会占据18个字节以上的空间;开启pack后,会降低至15。更多关于字节对齐的内容,自行查询。
后期可能更新字节对齐和内存访问相关的教程。
FDCANCommGet()
是获取来自CANComm接收到的数据的接口,返回值为一个void类型指针,你需要通过强制类型转换将其变为你设定的接收类型指针,然后就可以访问其数据了。
私有函数和变量
static FDCANCommInstance *can_comm_instance[MX_CAN_COMM_COUNT] = {NULL};
static uint8_t idx;
static void FDCANCommResetRx(FDCANCommInstance *ins);
static void FDCANCommRxCallback(can_instance *_instance);
第一个指针数组保存所有的can comm实例,从而在callback中区分不同实例。idx是初始化使用的索引,用于确定每个can comm实例指针存放的位置。
FDCANCommResetRx()
在FDCANCommRxCallback()
中多次被调用,用于清空接收缓冲区。
FDCANCommRxCallback()
是CAN comm初始化can实例时的回调函数,用于can接收中断,进行协议解析。
使用范例
例如,这里要发送的数据是一个float,接收的数据是如下的struct
,==注意要使用pack==:
#pragma pack(1)
struct test
{
float aa;
float bb;
float cc;
uint16_t dd;
};
#pragma pack()
初始化时设置如下:
CANComm_Init_Config_s cconfig = {
.fdcan_config = {
.can_handle=&hcan1,
.tx_id=0x02,
.rx_id=0x03},
.send_data_len = sizeof(float),
.recv_data_len = sizeof(struct test)
};
FDCANCommInstance* ins = FDCANCommInit(&cconfig);
通过FDCANCommGet()
并使用强制类型转换获得接收到的数据指针:
struct test* data_ptr=(struct test*)FDCANCommGet(ins)
发送通过CANCommSend()
,建议使用强制类型转换:
float tx=114.514;
CANCommSend(ins, (uint8_t*)&tx);
接收解析流程
CAN comm的通信协议如下:
帧头 | 数据长度 | 数据 | crc8校验 | 帧尾 |
---|---|---|---|---|
's'(0x73) | 0~255 | 最大60(可修改) | 校验码 | 'e'(0x65) |
1-byte | 1-byte | n-byte | 1-byte | 1-byte |
接收的流程见代码注释。