一、CAN总线波特率计算
CAN总线通信的各节点通信时会产生相位差,所以要进行位同步,两个节点保持步调一致。
CAN_SJW:重新同步跳跃宽度(SJW) 。定义了在每位中可以延长或缩短多少个时间单元的上限。其值可以编程为1到4个时间单元。
CAN_BS1:时间段1(BS1):定义采样点的位置。其值可以编程为1到16个时间单元,但也可以被自动延长,以补偿因为网络中不同节点的频率差异所造成的相位的正向漂移。
CAN_BS2:时间段2(BS2):定义发送点的位置。其值可以编程为1到8个时间单元,但也可以被自动缩短以补偿相位的负向漂移。
CAN_Prescaler:直观理解就是分频率。
CAN总线的波特率是取自于总线APB1(PCLK1),通过函数RCC_PCLK1Config给PCLK1配置频率。设置了以上的四个值之后,
CAN总线的波特率=PCLK1/((CAN_SJW +CAN_BS1 + CAN_BS2)*CAN_Prescaler)
假设PCLK1=36MHz、CAN_SJW=1、CAN_BS1=8、CAN_BS2=7、CAN_Prescaler=9
则CAN总线的波特率=PCLK1/((1 + 8 + 7) * 9) = 36MHz / 16 / 9 = 250Kbits
二、查看对应数据手册(stm32F407ZG),看CAN挂载在哪条总线上面,以及对应的引脚
三、CAN过滤器介绍
typedef struct
{__IO uint32_t FR1; //标识符屏蔽位模式中标识符寄存器,配置ID__IO uint32_t FR2; //标识符屏蔽位模式中屏蔽寄存器
}CAN_FilterRegister_TypeDef; //标识符列表模式中为2个标识符寄存器,配置ID
例如:屏蔽位寄存器的bit15=1,标识符寄存器的bit15=0,那么接受的Message里面的标识符的bit15必须为0才可能被硬件接受。如果屏蔽位寄存器的bit15=0,Message里面的标识符的bit15无论为什么值,bit15都能匹配通过。
32位屏蔽位模式下:1个过滤器。FR2指定需要关心哪些位,FR1指定这些位的标准值
32位列表模式下:2个过滤器,FR1指定过滤器0的标准值,FR2指定过滤器1的标准值
16位屏蔽位模式下:2个过滤器。FR1高位配置过滤器0标准值,低位配置给过滤器1的标准值。FR2高位配置过滤器0关心的位,低位配置给过滤器1的关心的位。
16位列表模式下:4个过滤器。FR1低位配置过滤器0,高位配置过滤器1。FR2配置过滤器2和过滤器3
IDE=0为标准ID,IDE=1为扩展ID。
RTR=0代表数据帧,RTR=1代表远程帧。
MASKID设置屏蔽码,ID设置标准值。
四、CAN过滤器配置
只有配置了CAN过滤器才能接收到数据,下面配置过滤器是所有ID都可以接收。加入到CAN初始化的函数中
CAN_FilterTypeDef sFilterConfig;sFilterConfig.FilterBank = 0; //过滤器0sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; //标识符屏蔽位模式sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; //过滤器位宽为单个32位sFilterConfig.FilterIdHigh = 0x0000; //标识符寄存器 sFilterConfig.FilterIdLow = 0x0000; //标识符寄存器 sFilterConfig.FilterMaskIdHigh = 0x0000; //屏蔽寄存器,只存在于标识符屏蔽位模式中,在标识符列表模式中为标识符寄存器 sFilterConfig.FilterMaskIdLow = 0x0000; //屏蔽寄存器 sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0; //FIFO0的中断和FIFO1的中断是不一样的,这里是把接收到的报文放入到FIFO0中sFilterConfig.FilterActivation = CAN_FILTER_ENABLE; //enable filtersFilterConfig.SlaveStartFilterBank = 0; /* 过滤器配置 */if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK){Error_Handler();}/* 启动CAN外围设备 */if (HAL_CAN_Start(&hcan1) != HAL_OK){Error_Handler();}
五、HAL库初始化
发送函数代码具体如下:
CAN_TxHeaderTypeDef g_canx_txheader; /* 发送参数句柄 */
CAN_RxHeaderTypeDef g_canx_rxheader; /* 接收参数句柄 *//**
* @brief CAN 发送一组数据
* @note 发送格式固定为: 标准 ID, 数据帧
* @param id : 标准 ID(11 位)
* @retval 发送状态 0, 成功; 1, 失败;
*/
uint8_t can_send_msg(uint32_t id, uint8_t *msg, uint8_t len)
{uint32_t TxMailbox = CAN_TX_MAILBOX0;g_canx_txheader.StdId = id; /* 标准标识符 */g_canx_txheader.ExtId = id; /* 扩展标识符(29 位) */g_canx_txheader.IDE = CAN_ID_STD; /* 使用标准帧 */g_canx_txheader.RTR = CAN_RTR_DATA; /* 数据帧 */g_canx_txheader.DLC = len;if (HAL_CAN_AddTxMessage(&g_canx_handler, &g_canx_txheader,msg, &TxMailbox) != HAL_OK) {return 1;}/* 等待发送完成,所有邮箱为空(3 个邮箱) */while (HAL_CAN_GetTxMailboxesFreeLevel(&g_canx_handler) != 3);return 0;
}
在 CAN 初始化时,我们对于过滤器的配置是不过滤任何报文 ID,也就是说可以接收全部
报文。但是我们可以编写接收函数时,使用软件的方式过滤报文 ID,通过形参来跟接收到的报
文 ID 进行匹配。接收函数代码具体如下:
/**
* @brief CAN 接收数据查询
* @note 接收数据格式固定为: 标准 ID, 数据帧
* @param id : 要查询的 标准 ID(11 位)
* @param buf : 数据缓存区
* @retval 接收结果
* @arg 0 , 无数据被接收到;
* @arg 其他, 接收的数据长度
*/
uint8_t can_receive_msg(uint32_t id, uint8_t *buf)
{if (HAL_CAN_GetRxFifoFillLevel(&g_canx_handler, CAN_RX_FIFO0) != 1){return 0;}if (HAL_CAN_GetRxMessage(&g_canx_handler, CAN_RX_FIFO0, &g_canx_rxheader,buf) !=HAL_OK){return 0;}/* 接收到的 ID 不对 / 不是标准帧 / 不是数据帧 */if (g_canx_rxheader.StdId!= id || g_canx_rxheader.IDE != CAN_ID_STD ||g_canx_rxheader.RTR != CAN_RTR_DATA){return 0;}return g_canx_rxheader.DLC;
}
六、验证代码
(1)将CAN配置为静默回环模式,配置过滤器不过滤
/* CAN1 init function */
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 = 16;hcan1.Init.Mode = CAN_MODE_SILENT_LOOPBACK;//静默回环模式hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;hcan1.Init.TimeSeg1 = CAN_BS1_4TQ;hcan1.Init.TimeSeg2 = CAN_BS2_3TQ;hcan1.Init.TimeTriggeredMode = DISABLE;hcan1.Init.AutoBusOff = DISABLE;hcan1.Init.AutoWakeUp = DISABLE;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 sFilterConfig;sFilterConfig.FilterBank = 0; //过滤器0sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; //标识符屏蔽位模式sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; //过滤器位宽为单个32位sFilterConfig.FilterIdHigh = 0x0000; //标识符寄存器 sFilterConfig.FilterIdLow = 0x0000; //标识符寄存器 sFilterConfig.FilterMaskIdHigh = 0x0000; //屏蔽寄存器,只存在于标识符屏蔽位模式中,在标识符列表模式中为标识符寄存器 sFilterConfig.FilterMaskIdLow = 0x0000; //屏蔽寄存器 sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0; //FIFO0的中断和FIFO1的中断是不一样的,这里是把接收到的报文放入到FIFO0中sFilterConfig.FilterActivation = CAN_FILTER_ENABLE; //enable filtersFilterConfig.SlaveStartFilterBank = 0; /* 过滤器配置 */if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK){Error_Handler();}/* 启动CAN外围设备 */if (HAL_CAN_Start(&hcan1) != HAL_OK){Error_Handler();}/* USER CODE END CAN1_Init 2 */}
(2)加入CAN数据收发代码
(3)仿真验证数据是否接收成功
uint8_t buf[10]="aaa";uint8_t buf1[10]={0};can_send_msg(0x12,buf,3);can_receive_msg(0x12,buf1);