先了解几个关键词简称:
最小时间单位(Tq,Time Quantum)
同步段(SS,Synchronization Segment)1tq
传播时间段(PTS,Propagation Time Segment)1~8tq
相位缓冲段1(PBS1,Phase Buffer Segment1)1~8tq
相位缓冲段2(PBS2,Phase Buffer Segment2)2~8tq
再同步补偿宽度(SJW,reSynchronization Jump Width)1~4tq
波特率分频器(BRP,Baud Rate Prescaler)
STM32把传播时间段(PTS)和相位缓冲段1(PBS1)合并了,形成了时间段1(TS1)。
CAN位时序寄存器(CAN_BTR)用于设置TS1、TS2、BRP、SJW等参数,这些参数直接决定CAN的波特率。
SJW[1:0]再同步补偿宽度
TS1[3:0]时间段1
TS2[2:0]时间段2
BRP[9:0]波特率分频器
可以看到没有同步段(SS段),这是因为STM32已经将SS段固化为1。
下面这张图是波特率计算公式:
整合一下波特率计算公式就是这样的:
波特率 = APB1 / [(1 + (TS1+1) + (TS2+1)) * (BRP+1)]
在简化就是:波特率 = 时钟主频 / 分频 / (tq1 + tq2 + ss)
其中SS就是同步段,已经恒为1,所以:波特率 = 时钟主频 / 分频 / (tq1 + tq2 + 1)
下面我们开始实际设置波特率,这里要注意,CAN的波特率最大为1Mbps。
另外还有一个参数是:再同步补偿宽度(reSynchronization Jump Width) 这个参数,其实就是一个由数个Tq组成的一个段,用来对同步误差进行补偿,可以简单理解为为了提高精准度的,例如两个CAN进行通讯时由于两个板子的晶振可能存在误差从而导致CAN的波特率没有那么精准,所以就需要设置一个补偿参数去修正,这个参数就需要根据你实际的板子情况去调整了。
时钟配置
CAN配置
CAN外设是挂载在APB1_PCLK1时钟上的,APB1_PCLK1是42M。CAN的最大速率是1MHZ,本实验配置为500KHZ。波特率配置计算方法:
CAN波特率 = APB1_PCLK1/分频/(tq1 + tq2 + ss),其中SS是SS段,在STM32中已经固定为1个tq
本实验波特率 = 42MHZ/4分频/(14 + 6 + 1) = 0.5MHZ = 500KHZ
使能CAN接收中断
配置CAN的IO,此步骤必须!!要不然HAL库函数MX_CAN_Init初始化会失败!!
CAN2和CAN1是同样的配置,这里不再贴CAN2的配置图了。
STM32外设CAN过滤器说明
STM32CUBEMX生成的代码默认是没有设置ID筛选器的,所以需要手动添加过滤器代码。下面一张图,STM32的过滤器组:
STM32F407ZG有28组筛选器,一组筛选器有两个32位的寄存器,筛选器组可配置为四种模式:
1个32位筛选器-标识符掩码模式,这时候筛选器组的两个32位寄存器一个用来存放ID,另一个用来存放ID的掩码
两个32位筛选器-标识符列表模式,这时候筛选器组的两个32位寄存器都用来存放ID
两个16位筛选器-标识符掩码模式,这时候筛选器组被分成了4个16位的寄存器,分别存放ID高16位+掩码高16位,ID低16位+掩码低16位
四个16位筛选-标识符列表模式,这时候筛选器组被分成了4个16位的寄存器,都存放ID的高16位和低16位
贴上我的配置代码:
- #define CAN1_FILTER_MODE_MASK_ENABLE 1 ///< CAN1过滤器模式选择:=1:屏蔽位模式 =0:屏蔽列表模式
- #define CAN2_FILTER_MODE_MASK_ENABLE 1 ///< CAN2过滤器模式选择:=1:屏蔽位模式 =0:屏蔽列表模式
- #define CAN1_BASE_ID 0x10F00266 ///< 主CAN过滤ID
- #define CAN2_BASE_ID 0x10F0F126 ///< 从CAN过滤ID
- #define CAN1_FILTER_BANK 0 ///< 主CAN过滤器组编号
- #define CAN2_FILTER_BANK 14 ///< 从CAN过滤器组编号
- /// CAN过滤器寄存器位宽类型定义
- typedef union
- {
- __IO uint32_t value;
- struct
- {
- uint8_t REV : 1; ///< [0] :未使用
- uint8_t RTR : 1; ///< [1] : RTR(数据帧或远程帧标志位)
- uint8_t IDE : 1; ///< [2] : IDE(标准帧或扩展帧标志位)
- uint32_t EXID : 18; ///< [21:3] : 存放扩展帧ID
- uint16_t STID : 11; ///< [31:22]: 存放标准帧ID
- } Sub;
- } CAN_FilterRegTypeDef;
- // CAN_FMR寄存器位宽类型定义
- typedef union
- {
- __IO uint32_t value;
- struct
- {
- uint8_t FINIT : 1;
- uint8_t RESERVER_0 : 7;
- uint8_t CAN2SB : 6;
- uint32_t RESERVER_1 : 18;
- }Sub;
- }FMR_TypeDef;
- /// 设置CAN1的过滤器(主CAN)
- static void CAN1_Filter_Config(void)
- {
- CAN_FilterTypeDef sFilterConfig;
- CAN_FilterRegTypeDef IDH = {0};
- CAN_FilterRegTypeDef IDL = {0};
- IDH.Sub.IDE = 0; // 标准帧
- IDH.Sub.STID = 0; // 标准帧ID值
- IDH.Sub.EXID = (CAN1_BASE_ID >> 16) & 0xFFFF; // 扩展帧高16位ID值
-
- IDL.Sub.IDE = 1; // 扩展帧
- IDL.Sub.STID = 0; // 标准帧ID值
- IDL.Sub.EXID = (CAN1_BASE_ID & 0xFFFF); // 扩展帧低16位ID值
- sFilterConfig.FilterBank = CAN1_FILTER_BANK; // 设置过滤器组编号
- #if CAN1_FILTER_MODE_MASK_ENABLE
- sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // 屏蔽位模式
- #else
- sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST; // 列表模式
- #endif
- sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 32位宽
- sFilterConfig.FilterIdHigh = IDH.value; // 标识符寄存器一ID高十六位,放入扩展帧位
- sFilterConfig.FilterIdLow = IDL.value; // 标识符寄存器一ID低十六位,放入扩展帧位
- sFilterConfig.FilterMaskIdHigh = IDH.value; // 标识符寄存器二ID高十六位,放入扩展帧位
- sFilterConfig.FilterMaskIdLow = IDL.value; // 标识符寄存器二ID低十六位,放入扩展帧位
- sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; // 过滤器组关联到FIFO0
- sFilterConfig.FilterActivation = ENABLE; // 激活过滤器
- sFilterConfig.SlaveStartFilterBank = CAN2_FILTER_BANK; // 设置CAN2的起始过滤器组(对于单CAN的CPU或从CAN此参数无效;对于双CAN的CPU此参数为从CAN的起始过滤器组编号)
- if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
- {
- Error_Handler();
- }
- {
- FMR_TypeDef regval = {0};
- regval.value = hcan1.Instance->FMR;
- printf("------ CAN1:> FMR:0x%0X CAN2SB:0x%X \r\n", regval.value, regval.Sub.CAN2SB);
- }
- }
- /// 设置CAN2的过滤器(从CAN)
- static void CAN2_Filter_Config(void)
- {
- CAN_FilterTypeDef sFilterConfig;
- CAN_FilterRegTypeDef IDH = {0};
- CAN_FilterRegTypeDef IDL = {0};
- IDH.Sub.IDE = 0;
- IDH.Sub.STID = 0;
- IDH.Sub.EXID = (CAN2_BASE_ID >> 16) & 0xFFFF;
-
- IDL.Sub.IDE = 1;
- IDL.Sub.STID = 0;
- IDL.Sub.EXID = (CAN2_BASE_ID & 0xFFFF);
- sFilterConfig.FilterBank = CAN2_FILTER_BANK; // 设置过滤器组编号
- #if CAN2_FILTER_MODE_MASK_ENABLE
- sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // 屏蔽位模式
- #else
- sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST; // 列表模式
- #endif
- sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 32位宽
- sFilterConfig.FilterIdHigh = IDH.value; // 标识符寄存器一ID高十六位,放入扩展帧位
- sFilterConfig.FilterIdLow = IDL.value; // 标识符寄存器一ID低十六位,放入扩展帧位
- sFilterConfig.FilterMaskIdHigh = IDH.value; // 标识符寄存器二ID高十六位,放入扩展帧位
- sFilterConfig.FilterMaskIdLow = IDL.value; // 标识符寄存器二ID低十六位,放入扩展帧位
- sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; // 过滤器组关联到FIFO0
- sFilterConfig.FilterActivation = ENABLE; // 激活过滤器
- sFilterConfig.SlaveStartFilterBank = 28; // 无效
- if (HAL_CAN_ConfigFilter(&hcan2, &sFilterConfig) != HAL_OK)
- {
- Error_Handler();
- }
- {
- FMR_TypeDef regval = {0};
- regval.value = hcan2.Instance->FMR;
- printf("------ CAN2:> FMR:0x%0X CAN2SB:0x%X \r\n", regval.value, regval.Sub.CAN2SB);
- }
- }
- /// CAN初始化
- void CAN_Init(void)
- {
- MX_CAN1_Init(); // 初始化CNA1
- CAN1_Filter_Config(); // 初始化CNA1过滤器
- HAL_CAN_Start(&hcan1); // 启动CAN1
- HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING); // 激活CAN1 FIFO0
- MX_CAN2_Init();
- CAN2_Filter_Config();
- HAL_CAN_Start(&hcan2);
- HAL_CAN_ActivateNotification(&hcan2, CAN_IT_RX_FIFO0_MSG_PENDING);
- }
- /**
- * CAN数据传输
- * @param buf 待发送的数据
- * @param len 数据长度
- * @param number CAN编号,=0:CAN1,=1:CAN2
- * @return 0:成功 other:失败
- */
- uint8_t CAN_Transmit(const void* buf, uint32_t len, uint8_t number)
- {
- uint32_t txmailbox = 0;
- uint32_t offset = 0;
- CAN_TxHeaderTypeDef hdr;
- hdr.IDE = CAN_ID_EXT; // ID类型:扩展帧
- hdr.RTR = CAN_RTR_DATA; // 帧类型:数据帧
- hdr.StdId = 0; // 标准帧ID,最大11位,也就是0x7FF
- hdr.ExtId = number == 0 ? CAN1_BASE_ID : CAN2_BASE_ID; // 扩展帧ID,最大29位,也就是0x1FFFFFFF
- hdr.TransmitGlobalTime = DISABLE;
- while (len != 0)
- {
- hdr.DLC = len > 8 ? 8 : len; // 数据长度
- if (HAL_CAN_AddTxMessage(number == 0 ? &hcan1 : &hcan2, &hdr, ((uint8_t *)buf) + offset, &txmailbox) != HAL_OK)
- return 1;
- offset += hdr.DLC;
- len -= hdr.DLC;
- }
- return 0;
- }
- uint8_t CAN1_RX_STA = 0; ///< CAN1数据接收标志:[7]:数据 [6:0]:未使用
- uint8_t CAN2_RX_STA = 0; ///< CAN2数据接收标志:[7]:数据 [6:0]:未使用
- uint8_t CAN1_RX_BUF[8]; ///< CAN1数据接收缓存
- uint8_t CAN2_RX_BUF[8]; ///< CAN2数据接收缓存
- uint8_t CAN1_TX_BUF[8]; ///< CAN1数据发送缓存
- uint8_t CAN2_TX_BUF[8]; ///< CAN2数据发送缓存
- /**
- * CAN FIFO0 数据接收中断回调函数
- * @param hcan CAN句柄
- */
- void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
- {
- static CAN_RxHeaderTypeDef CAN_RX_HDR;
- // CAN1数据接收
- if (hcan->Instance == hcan1.Instance)
- {
- // 数据已经处理
- if ((CAN1_RX_STA & 0x80) == 0)
- {
- // 清空缓存
- memset(CAN1_RX_BUF, 0, 8);
- // 接收数据
- if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &CAN_RX_HDR, CAN1_RX_BUF) == HAL_OK) // 获得接收到的数据头和数据
- {
- CAN1_RX_STA |= 0x80; // 标记接收到数据
- HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO0_MSG_PENDING); // 再次使能FIFO0接收中断
- }
- }
- }
- // CAN2数据接收
- else if (hcan->Instance == hcan2.Instance)
- {
- // 数据已经处理
- if ((CAN2_RX_STA & 0x80) == 0)
- {
- // 清空缓存
- memset(CAN2_RX_BUF, 0, 8);
- // 接收数据
- if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &CAN_RX_HDR, CAN2_RX_BUF) == HAL_OK) // 获得接收到的数据头和数据
- {
- CAN2_RX_STA |= 0x80; // 标记接收到数据
- HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO0_MSG_PENDING); // 再次使能FIFO0接收中断
- }
- }
- }
- }
- ///< CAN数据处理函数
- inline void CAN_RecvHandler(void)
- {
- // CAN1有数据收到
- if (CAN1_RX_STA & 0x80)
- {
- int i = 0;
- memcpy(CAN1_TX_BUF, CAN1_RX_BUF, sizeof(CAN1_RX_BUF)); // 拷贝出数据
- CAN1_RX_STA = 0; // 重置CAN1接收状态
- for(i = 0; i != 8; i++)
- {
- printf("CAN1_TX_BUF[%d]:0x%X\r\n", i, CAN1_TX_BUF<i>);
- }
- printf("\r\n\r\n");
- }
- // CAN2有数据收到
- if (CAN2_RX_STA & 0x80)
- {
- int i = 0;
- memcpy(CAN2_TX_BUF, CAN2_RX_BUF, sizeof(CAN2_RX_BUF)); // 拷贝出数据
- CAN2_RX_STA = 0; // 重置CAN1接收状态
- for(i = 0; i != 8; i++)
- {
- printf("CAN2_TX_BUF[%d]:0x%X\r\n", i, CAN2_TX_BUF<i>);
- }
- printf("\r\n\r\n");
- }
- }
- </i></i>
复制代码
|