目录
流程
- 在cubeMX配置初始化CAN协议相关参数和硬件相关参数:
设置预分频、位段长度等实现需要的500Kbps波特率;
CAN时钟使能、GPIO端口时钟使能、引脚重映射、中断优先级等; - 生成代码
- 初始化筛选器
- 封装CAN发送函数和接收回调函数
CubeMX配置
常规配置
选择对应的芯片型号
配置debug方式
配置外部高速时钟源
配置时钟树
CAN相关配置
选择对应CAN,勾上Mode里的Activated,启动CAN
配置相关参数
设置如图即可
可以全勾上
相关参数介绍
1. Parameter Settings:
Bit Timing Parameters(位时间参数)
- Prescaler(for Time Quantum): 分频,可选范围为1-1024。
- Time Quantum:相当于上图的最小时间单位Tq,单位为ns
- Time Quanta in Bit Segment 1:PBS1段所占的时间,单位 (Tq)
- Time Quanta in Bit Segment 2:PBS2段所占的时间,单位(Tq)
- Time for one Bit:传输一位的时间
- Baud Rate:波特率
- ReSynchronization Jump Width:SS段所占的时间,单位(Tq)
Baud Rate(波特率) = APB1 peripheral clocks(时钟树所设置的APB1总线频率) /(Time Quanta in Bit Segment 1 + Time Quanta in Bit Segment 2 + ReSynchronization Jump Width)/ Prescaler(分频值)
Basic Parameters(基本参数,使能or不使能)
- Time Triggered Communication Mode(时间触发模式)
在此模式下,CAN使用它内部定时器产生时间戳。内部定时器在每个CAN位时间累加,在接收和改善的帧起始位被采样,并生成时间戳。利用它可以实现标准的分时同步通信功能。 - Automatic Bus-off Management(自动离线管理)
当节点检测到它发送错误或接收错误超过一定值时,会自动进入离线状态。在离线状态中,CAN不能接收或发送报文。这时可以软件控制恢复,或者使用自动离线管理功能。使能后,CAN会在适当的时候自动恢复。 - Automatic Wake-Up Mode(自动唤醒模式)
使能后,当CAN外设处于低功耗的睡眠模式且检测到总线活动时会自动唤醒。 - Automatic Retransmission(自动重传)
使能后,当报文发送失败时会自动重传至成功为止。不使能,则只发送一次。 - Receive Fifo Locked Mode(接收FIFO锁定模式)
使能后,当接收FIFO溢出时会丢弃下一个接收报文。不使能,则下一个接收报文会覆盖原报文。 - Transmit Fifo Priority(发送FIFO优先级)
使能后,根据报文ID优先级发送。不使能,则根据报文存进邮箱的顺序发送。
Advanced Parameters (高级参数)
- Operationg Mode
Normal(正常模式):可向总线发送或接收数据。
Loopback(回环模式):把数据发送给总线和自己,不能从总线接收数据。
Silent(静默模式):只可向总线发送数据1和接收数据,不能发送数据0。
Loopback combined with Silent(回环静默模式):把数据发送给自己,不能从总线接收也不能发送给总线数据。
2. NVIC Settings:
- CAN1 TX interrupts:发送中断(需使能)
- CAN1 RX0 interrupts:FIFO0接收中断
- CAN1 RX1 interrupt:FIFO1接收中断
- CAN1 SCE interrupt:故障监测中断
3. User Constants:设置常量
4. GPIO Settings:引脚配置(不可修改)
最后设置工程名生成即可
代码实现
定义变量接收与发送
CAN_TxHeaderTypeDef Tx1Message;/*can1发送帧相关信息*/
CAN_RxHeaderTypeDef Rx1Message;/*can1接收帧相关信息*/
uint8_t CAN1_Rx_data[8];/*can1发送帧数据*/
uint8_t CAN1_Tx_data[8];/*can1接收帧数据*/
初始化过滤器
void can_device_init(void)
{
CAN_FilterTypeDef can_filter;/*过滤器设置*/
can_filter.FilterBank = 0;
/*使用的过滤器编号。使用一个CAN,则可选0~13;使用两个CAN可选0~27*/
can_filter.FilterMode = CAN_FILTERMODE_IDMASK;
/*过滤器模式选择。掩码模式填写CAN_FILTERMODE_IDMASK,
列表模式填写CAN_FILTERMODE_IDLIST */
can_filter.FilterScale = CAN_FILTERSCALE_32BIT;
/*过滤器位宽,32位是CAN_FILTERSCALE_32BIT,
16位是CAN_FILTERSCALE_16BIT */
can_filter.FilterIdHigh = 0x0000;
/*过滤器验证码ID高16位,参数值:0~0xFFFF*/
can_filter.FilterIdLow = 0x0000;
/*过滤器验证码ID低16位,参数值:0~0xFFFF*/
can_filter.FilterMaskIdHigh = 0x0000;
/*过滤器掩码ID高16位,参数值:0~0xFFFF*/
can_filter.FilterMaskIdLow = 0x0000;
/*过滤器掩码ID低16位,参数值:0~0xFFFF*/
can_filter.FilterFIFOAssignment = CAN_FilterFIFO0;
/*将通过的报文放入哪个FIFOx中,填写FIFO(x)*/
can_filter.SlaveStartFilterBank = 0;
/*can2开始的过滤器*/
can_filter.FilterActivation = ENABLE;
/*是否使能过滤器,DISABLE 或 ENABLE */
HAL_CAN_ConfigFilter(&hcan1, &can_filter);
while (HAL_CAN_ConfigFilter(&hcan1, &can_filter) != HAL_OK);
/*配置can1过滤器并等待至配置完成*/
can_filter.FilterBank = 14;
HAL_CAN_ConfigFilter(&hcan2, &can_filter);
while (HAL_CAN_ConfigFilter(&hcan2, &can_filter) != HAL_OK);
/*配置can2过滤器并等待至配置完成*/
HAL_Delay(100);
HAL_CAN_Start(&hcan1);
HAL_CAN_ActivateNotification(&hcan1,CAN_IT_RX_FIFO0_MSG_PENDING);
/*开启can1并使能中断*/
HAL_CAN_Start(&hcan2);
HAL_CAN_ActivateNotification(&hcan2,CAN_IT_RX_FIFO0_MSG_PENDING);
/*开启can2并使能中断*/
}
CAN发送函数
void can1_send_message(int16_t TX_ID, int16_t iq1, int16_t iq2, int16_t iq3, int16_t iq4)
{
uint8_t FreeTxNum = 0;
Tx2Message.StdId = TX_ID;
/*标准ID。参数值:0到0x7FF*/
Tx2Message.ExtId = 0;
/*扩展ID。参数值:0到0x1FFFFFFF*/
Tx2Message.IDE = CAN_ID_STD;
/*标准帧填写CAN_ID_STD,扩展帧填写CAN_ID_EXT */
Tx2Message.RTR = CAN_RTR_DATA;
/*数据帧填写CAN_RTR_DATA,遥控帧填写CAN_RTR_REMOTE */
Tx2Message.DLC = 0x08;
/*数据长度,参数值:0到8*/
CAN1_Tx_data[0] = iq1 >> 8;
CAN1_Tx_data[1] = iq1;
CAN1_Tx_data[2] = iq2 >> 8 ;
CAN1_Tx_data[3] = iq2;
CAN1_Tx_data[4] = iq3 >> 8;
CAN1_Tx_data[5] = iq3;
CAN1_Tx_data[6] = iq4 >> 8;
CAN1_Tx_data[7] = iq4;
FreeTxNum = HAL_CAN_GetTxMailboxesFreeLevel(&hcan1);
while(FreeTxNum == 0)FreeTxNum = HAL_CAN_GetTxMailboxesFreeLevel(&hcan1);
/*等待发送邮箱有空*/
HAL_CAN_AddTxMessage(&hcan1, &Tx1Message,CAN1_Tx_data,(uint32_t*)CAN_TX_MAILBOX0);
/*将数据放入邮箱*/
}
接收回调函数
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
if(hcan == &hcan1)
{
//将fifo0接收到的id等信息赋给Rx1Message,数据赋给CAN1_Rx_data
HAL_CAN_GetRxMessage(hcan,CAN_RX_FIFO0,&Rx1Message,CAN1_Rx_data);
//根据ID进行不同的操作,也在中断外进行操作
switch (Rx1Message.StdId)
{
};
__HAL_CAN_ENABLE_IT(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);
}
else if(hcan == &hcan2 )
{
HAL_CAN_GetRxMessage(hcan,CAN_RX_FIFO0,&Rx2Message, CAN2_Rx_data);
switch (Rx2Message.StdId)
{
}
__HAL_CAN_ENABLE_IT(&hcan2, CAN_IT_RX_FIFO0_MSG_PENDING);
}
}
最终接收的数据将通过中断存入CAN1_Rx_data,发送数据则通过can1_send_message函数发送。