1、CAN基本驱动步骤
(1)CAN参数初始化
- 工作模式、波特率等
- 函数:HAL_CAN_Init
(2)使能CAN时钟和初始化相关引脚
- GPIO模式设为复用功能模式
- 函数:HAL_CAN_MspInit(CAN的初始化回调函数)
(3)设置过滤器
- 过滤器的配置
- 函数:HAL_CAN_ConfigFilter
(4)CAN数据的接收和发送
- HAL_CAN_AddTxMessage 发送消息
- HAL_CAN_GetRxMessage 接收数据
(5)使能CAN相关中断/设置NVIC/编写中断服务函数
- NVIC:中断控制器
- __HAL_CAN_ENABLE_IT (可选)
2、开发环境
(1)KeilMDK:V5.38.0.0
(2)STM32CubeMX:V6.8.1
(3)MCU:STM32F407ZGT6
3、实验目的
(1)使用回环模式实现自发自收。
(2)CAN发送数据,然后接收数据,将接收到的数据通过串口发送出去。
4、原理图
(1)CAN芯片选择TJA1040。
(2)CAN_TX接PA12,CAN_RX接PA11。
5、STM32CubeMX创建工程及配置
5.1、补充内容
(1)查阅数据手册,CAN外设接在总线APB1上,时钟频率此处配置为36MHz。
(2)CubeMX创建工程、配置时钟、串口不做详细介绍。
5.2、CubeMX中CAN的配置
(1)使能CAN外设。
(2)配置CAN的参数
- Bit Timings Parameters:位时序参数
- Prescaler:分频系数
- Time Quanta in Bit Segment 1:时间段1(配置为9标识9个时间单元)
- Time Quanta in Bit Segment 2:时间段2
- ReSynchronization Jump Width:重新同步跳跃宽度
- TS1=8、TS2=7、BRP=3,波特率 = 36000 / [( 9 + 8 + 1 ) * 4] = 500Kbps
- Basic Parameters:基本参数
- Time Triggered Communication Mode:时间触发通信方式
- Automatic Bus-Off Management:总线自动离线管理
- Automatic Wake-Up Mode:自动唤醒模式
- Automatic Retransmission:自动重发
- Receive Fifo Locked Mode:接收Fifo锁定模式
- Transmit Fifo Priority:发送Fifo优先级
- Advanced Parameters:先进的参数
- Operating Mode:操作模式
6、KeilMDK软件编写
6.1、CAN相关函数
CAN_TxHeaderTypeDef g_can1_txheader; /* CAN发送结构体 */
CAN_RxHeaderTypeDef g_can1_rxheader; /* CAN接收结构体 */
CAN_HandleTypeDef hcan1; // CAN控制句柄/* CAN1 init function */
void MX_CAN1_Init(void)
{hcan1.Instance = CAN1;hcan1.Init.Prescaler = 4; /* 分频系数 */ hcan1.Init.Mode = CAN_MODE_LOOPBACK; /* 工作模式设置 环回模式:自发自收 */hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ; /* 重新同步跳跃宽度 */hcan1.Init.TimeSeg1 = CAN_BS1_9TQ; /* 时间段1 */hcan1.Init.TimeSeg2 = CAN_BS2_8TQ; /* 时间段2 */hcan1.Init.TimeTriggeredMode = DISABLE; /* 禁止时间触发通信模式 */hcan1.Init.AutoBusOff = DISABLE; /* 禁止自动离线管理 */hcan1.Init.AutoWakeUp = DISABLE; /* 禁止自动唤醒 */hcan1.Init.AutoRetransmission = DISABLE; /* 禁止自动重发 */hcan1.Init.ReceiveFifoLocked = DISABLE; /* 禁止接收FIFO锁定 */hcan1.Init.TransmitFifoPriority = DISABLE; /* 禁止发送FIFO优先级 */if (HAL_CAN_Init(&hcan1) != HAL_OK){Error_Handler();}}void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle)
{GPIO_InitTypeDef GPIO_InitStruct = {0};if(canHandle->Instance==CAN1){/* USER CODE BEGIN CAN1_MspInit 0 *//* USER CODE END CAN1_MspInit 0 *//* CAN1 clock enable */__HAL_RCC_CAN1_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();/**CAN1 GPIO ConfigurationPA11 ------> CAN1_RXPA12 ------> CAN1_TX*/GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF9_CAN1;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);/* USER CODE BEGIN CAN1_MspInit 1 *//* USER CODE END CAN1_MspInit 1 */}
}/*
**功能:CAN过滤器配置
**参数:无
**返回值:无*/
void can_filter_config(void)
{CAN_FilterTypeDef can_filterconfig;/* 过滤器是接收所有报文,不筛选 */can_filterconfig.FilterMode = CAN_FILTERMODE_IDMASK; /* 过滤器模式:标识符掩码模式(屏蔽位模式)*/can_filterconfig.FilterScale = CAN_FILTERSCALE_32BIT; /* 过滤器位宽:32位位宽 *///STID[10:3] STID[2:0] EXID[17:13] EXID[12:5] EXID[4:0] IDE RTR 0can_filterconfig.FilterIdHigh = 0; /* ID高字节 */ can_filterconfig.FilterIdLow = 0; /* ID低字节 */can_filterconfig.FilterMaskIdHigh = 0; /* 掩码高字节 */can_filterconfig.FilterMaskIdLow = 0; /* 掩码低字节 */can_filterconfig.FilterBank = 0; /* 选择过滤器组 */can_filterconfig.FilterFIFOAssignment = CAN_FilterFIFO0; /* 过滤器关联FIFO */can_filterconfig.FilterActivation = CAN_FILTER_ENABLE; /* 过滤器使能 */can_filterconfig.SlaveStartFilterBank = 14;HAL_CAN_ConfigFilter(&hcan1, &can_filterconfig);}/* 发送消息数据函数 */
void can_send_message(uint32_t id, uint8_t *buf, uint8_t len)
{uint32_t tx_mail = CAN_TX_MAILBOX0; /* 发送邮箱 */g_can1_txheader.ExtId = id; /* 扩展标识符 */g_can1_txheader.DLC = len; /* 数据长度 */g_can1_txheader.IDE = CAN_ID_EXT; /* 帧格式(标准帧或扩展帧) */g_can1_txheader.RTR = CAN_RTR_DATA; /* 帧类型(数据帧或远程帧) */HAL_CAN_AddTxMessage(&hcan1, &g_can1_txheader, buf, &tx_mail);// 等待发送完成while(HAL_CAN_GetTxMailboxesFreeLevel(&hcan1) != 3);
}/* 接收数据函数 */
uint8_t can_receive_message(uint8_t *buf)
{if (HAL_CAN_GetRxFifoFillLevel(&hcan1, CAN_RX_FIFO0) == 0){return 0;}HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &g_can1_rxheader, buf);// 返回接收数据长度return g_can1_rxheader.DLC;
}
6.2、main.c部分代码
#include "can.h"int main(void)
{/* USER CODE BEGIN 1 */uint8_t can_sen_buf[8] = {0, 1, 2, 3, 4, 5, 6, 7}; // can发送数据uint8_t can_rec_len = 0; // can接收数据长度uint8_t can_rec_buf[8] = {0}; // can接收数据缓冲区MX_CAN1_Init();/* USER CODE BEGIN 2 */// 1、已经使能CAN时钟和初始化CAN// 2、配置CAN接收过滤器can_filter_config();// 3、启动CAN设备HAL_CAN_Start(&hcan1);while (1){printf("hello world\r\n");can_send_message(0xF0000000, can_sen_buf, 8);can_rec_len = can_receive_message(can_rec_buf);if (can_rec_len){for (uint8_t i = 0; i < can_rec_len; i++){printf("%x ",can_rec_buf[i]);}printf("\r\n");}}
}
6.3、完整工程下载地址
(1)完整工程存储再码云。
(2)STM32_CSDN: CSDN中STM32专栏的所有示例代码