参考:
https://blog.csdn.net/sunlight_vip/article/details/128639144
前言:
学习总结CAN的知识点:
1.can是什么,历史由来和背景
2.can的物理层,链路层
3.初始化的流程和关键点
4.波特率怎么设置
5.can id怎么过滤
6.can的发送和接收原理
7.can的发送和接收API
3. CAN Cubemx配置
我们通过问题来熟悉下cubemx配置,你熟悉了这些问题基本就知道怎么配置了!
问题1:Parameter Settings分别都是设置什么的?
答案:如图
问题2:怎么配置波特率呢?
答案:用我上面贴的工具(CAN波特率计算 f103AHP1_36M f407AHP1_42M 采样点软件有说明.rar)直接配置,举两个个例子
例子1:我们要配置成500KHz,那么我们这样配置
我们用采集点为80%,所以BS1为4tq, BS2为2tq, 分频系数为12,代进公式
Fpclk1 / ((CAN_BS1+CAN_BS2+1)*CAN_Prescaler) = 42M / (4+2+1) / 12=500kHz
例子2:我们要配置成1M Hz,那么我们这样配置
我们用采集点为75%,所以BS1为3tq, BS2为2tq, 分频系数为7,代进公式
Fpclk1/((CAN_BS1+CAN_BS2+1)*CAN_Prescaler)=42M/(3+2+1)/7=1MHz
问题3:Basic Parameter分别是啥意思呢?
Timer Triggered Communication Mode:是否使用时间触发功能 (ENABLE/DISABLE),时间触发功能在某些CAN 标准中会使用到。
Automatic Bus-Off Management:用于设置是否使用自动离线管理功能 (ENABLE/DISABLE),使用自动离线管理可以在出错时离线后适时自动恢复,不需要软件干预。
Automatic Wake-Up Mode:用于设置是否使用自动唤醒功能 (ENABLE/DISABLE),使能自动唤醒功能后它会在监测到总线活动后自动唤醒。
Automatic Retransmission:用于设置是否使用自动重传功能 (ENABLE/DISABLE),使用自动重传功能时,会一直发送报文直到成功为止。
Receive Fifo Locked Mode:用于设置是否使用锁定接收 FIFO(ENABLE/DISABLE),锁定接收 FIFO 后,若FIFO 溢出时会丢弃新数据,否则在 FIFO 溢出时以新数据覆盖旧数据。
Transmit Fifo Priority:用于设置发送报文的优先级判定方法 (ENABLE/DISABLE),使能时,以报文存入发送邮箱的先后顺序来发送,否则按照报文 ID 的优先级来发送。配置完这些结构体成员后,我们调用库函数 HAL_CAN_Init 即可把这些参数写入到 CAN 控制寄存器中,实现 CAN 的初始化
问题4:为啥CAN分为RX0,RX1中断呢?
答案:STM32有2个3级深度的接收缓冲区:FIFO0和FIFO1,每个FIFO都可以存放3个完整的报文,它们完全由硬件来管理。如果是来自FIFO0的接收中断,则用CAN1_RX0_IRQn中断来处理。如果是来自FIFO1的接收中断,则用CAN1_RX1_IRQn中断来处理,如图:
问题5:CAN SCE中断是什么?
答案:status chanege error,错误和状态变化中断!
4. 实验
4.1.Normal模式测试500K 波特率(定时发送,轮询接收)
4.1.1 CubeMx配置
4.1.2 设置Filter过滤,我们只使能FIFO0,并且不过滤任何消息
CAN_FilterTypeDef filter = {0};
filter.FilterActivation = ENABLE;
filter.FilterMode = CAN_FILTERMODE_IDMASK;
filter.FilterScale = CAN_FILTERSCALE_32BIT;
filter.FilterBank = 0;
filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;
filter.FilterIdLow = 0;
filter.FilterIdHigh = 0;
filter.FilterMaskIdLow = 0;
filter.FilterMaskIdHigh = 0;
HAL_CAN_ConfigFilter(&hcan1, &filter);
4.1.3 开启CAN(注意,默认Cubemx生成的代码并没有can start)
HAL_CAN_Start(&hcan1);
4.1.4 编写发送函数
uint8_t xl_can1_send_msg(uint32_t id, uint8_t *data, uint32_t data_len)
{uint8_t index = 0;uint32_t *msg_box;uint8_t send_buf[8] = {0};CAN_TxHeaderTypeDef send_msg_hdr;if (id < 0x7FF){send_msg_hdr.StdId = id;send_msg_hdr.ExtId = id;send_msg_hdr.IDE = CAN_ID_STD;}else{send_msg_hdr.StdId = id;send_msg_hdr.ExtId = id;send_msg_hdr.IDE = CAN_ID_EXT;}send_msg_hdr.RTR = CAN_RTR_DATA;send_msg_hdr.DLC = data_len;send_msg_hdr.TransmitGlobalTime = DISABLE;for(index = 0; index < data_len; index++)send_buf[index] = data[index];HAL_CAN_AddTxMessage(&hcan1, &send_msg_hdr, send_buf, msg_box);return 0;
}
4.1.5 编写轮询接收函数
uint8_t xl_can1_polling_recv_msg(uint32_t* id, uint8_t* data, uint32_t* data_len)
{uint8_t index = 0;uint8_t recv_data[8];CAN_RxHeaderTypeDef header;while (HAL_CAN_GetRxFifoFillLevel(&hcan1, CAN_RX_FIFO0) != 0){if (__HAL_CAN_GET_FLAG(&hcan1, CAN_FLAG_FOV0) != RESET)printf("[CAN] FIFO0 overrun!\n");HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &header, recv_data);if(header.IDE == CAN_ID_STD){printf("StdId ID:%d\r\n",header.StdId);}else{printf("ExtId ID:%d\r\n",header.ExtId);}printf("CAN IDE:0x%x\r\n",header.IDE);printf("CAN RTR:0x%x\r\n",header.RTR);printf("CAN DLC:0x%x\r\n",header.DLC);printf("RECV DATA:");for(index = 0; index < header.DLC; index++){printf("0x%x ",recv_data[index]);}printf("\r\n");}
}
备注:
1.STM32CubeMX默认生成的代码没有调用HAL_CAN_Start(&hcan1);,需要手动添加该函数使能CAN。
2…STM32CubeMX默认生成的代码没有编写关于Filter部分,需要手动添加过滤ID报文部分。 若是不添加过滤部分,硬件不知道过滤什么报文,过滤的报文放在哪个FIFO中。
4.1.6 现象:
通过串口输出命令“cmd_can”调用can发送函数,can设备上位机,收到can报文:
上位机发送can数据,开发板能接收解析:
4.2.Normal模式测试500K 波特率(定时发送,中断接收)
4.2.1 CubeMx配置
步骤2,3,4跟polling完全一致,我们来直接说下中断怎么用(主要是使能notifity就行了)
void MX_CAN1_Init(void)
{/* USER CODE BEGIN CAN1_Init 0 *//* USER CODE END CAN1_Init 0 *//* USER CODE BEGIN CAN1_Init 1 *//* USER CODE END CAN1_Init 1 */hcan1.Instance = CAN1;hcan1.Init.Prescaler = 12;hcan1.Init.Mode = CAN_MODE_NORMAL;hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;hcan1.Init.TimeSeg1 = CAN_BS1_4TQ;hcan1.Init.TimeSeg2 = CAN_BS2_2TQ;hcan1.Init.TimeTriggeredMode = DISABLE;hcan1.Init.AutoBusOff = ENABLE;hcan1.Init.AutoWakeUp = ENABLE;hcan1.Init.AutoRetransmission = DISABLE;hcan1.Init.ReceiveFifoLocked = DISABLE;hcan1.Init.TransmitFifoPriority = DISABLE;if (HAL_CAN_Init(&hcan1) != HAL_OK){Error_Handler();}/* USER CODE BEGIN CAN1_Init 2 */CAN_FilterTypeDef filter = {0};filter.FilterActivation = ENABLE;filter.FilterMode = CAN_FILTERMODE_IDMASK;filter.FilterScale = CAN_FILTERSCALE_32BIT;filter.FilterBank = 0;filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;filter.FilterIdLow = 0;filter.FilterIdHigh = 0;filter.FilterMaskIdLow = 0;filter.FilterMaskIdHigh = 0;HAL_CAN_ConfigFilter(&hcan1, &filter);HAL_CAN_Start(&hcan1);HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);/* USER CODE END CAN1_Init 2 */}
下面我们来编写下中断函数
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{uint8_t index = 0;uint8_t recv_data[8];CAN_RxHeaderTypeDef header;HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &header, recv_data);if(header.IDE == CAN_ID_STD){printf("StdId ID:%d\r\n", header.StdId);}else{printf("ExtId ID:%d\r\n", header.ExtId);}printf("CAN IDE:0x%x\n", header.IDE);printf("CAN RTR:0x%x\n", header.RTR);printf("CAN DLC:0x%x\n", header.DLC);printf("interrupt RECV DATA:");for(index = 0; index < header.DLC; index++){printf("0x%x ",recv_data[index]);}printf("\r\n");
}
4.2.2 现象
通过串口输出命令“cmd_can”调用can发送函数,can设备上位机,收到can报文:
上位机发送can数据,开发板能接收解析:
4.3 源码
git clone git@gitee.com:xiaoliangliangcong/stm32.git
STM32F407ZGT6/16.CAN