STM32F4xx CAN HAL模块驱动
stm32f4xx_hal_can.c、stm32f4xx_hal_can.h是CAN HAL模块驱动的代码实现文件,本文从结构体、接口函数、中断回调函数这三个方面介绍CAN HAL模块驱动。
1. 结构体
1.1 CAN_TypeDef
结构体CAN_TypeDef定义了bxCAN所有相关寄存器,结构体CAN_HandleTypeDef的成员Instance是CAN_TypeDef类型的指针。接口函数是通过Instance访问bxCAN寄存器,因此我们需要把bxCAN寄存器的基地址赋值给Instance。
typedef struct
{
__IO uint32_t MCR; /*!< CAN master control register, Address offset: 0x00 */
__IO uint32_t MSR; /*!< CAN master status register, Address offset: 0x04 */
__IO uint32_t TSR; /*!< CAN transmit status register, Address offset: 0x08 */
__IO uint32_t RF0R; /*!< CAN receive FIFO 0 register, Address offset: 0x0C */
__IO uint32_t RF1R; /*!< CAN receive FIFO 1 register, Address offset: 0x10 */
__IO uint32_t IER; /*!< CAN interrupt enable register, Address offset: 0x14 */
__IO uint32_t ESR; /*!< CAN error status register, Address offset: 0x18 */
__IO uint32_t BTR; /*!< CAN bit timing register, Address offset: 0x1C */
uint32_t RESERVED0[88]; /*!< Reserved, 0x020 - 0x17F */
CAN_TxMailBox_TypeDef sTxMailBox[3]; /*!< CAN Tx MailBox, Address offset: 0x180 - 0x1AC */
CAN_FIFOMailBox_TypeDef sFIFOMailBox[2]; /*!< CAN FIFO MailBox, Address offset: 0x1B0 - 0x1CC */
uint32_t RESERVED1[12]; /*!< Reserved, 0x1D0 - 0x1FF */
__IO uint32_t FMR; /*!< CAN filter master register, Address offset: 0x200 */
__IO uint32_t FM1R; /*!< CAN filter mode register, Address offset: 0x204 */
uint32_t RESERVED2; /*!< Reserved, 0x208 */
__IO uint32_t FS1R; /*!< CAN filter scale register, Address offset: 0x20C */
uint32_t RESERVED3; /*!< Reserved, 0x210 */
__IO uint32_t FFA1R; /*!< CAN filter FIFO assignment register, Address offset: 0x214 */
uint32_t RESERVED4; /*!< Reserved, 0x218 */
__IO uint32_t FA1R; /*!< CAN filter activation register, Address offset: 0x21C */
uint32_t RESERVED5[8]; /*!< Reserved, 0x220-0x23F */
CAN_FilterRegister_TypeDef sFilterRegister[28]; /*!< CAN Filter Register, Address offset: 0x240-0x31C */
} CAN_TypeDef;
1.2 CAN_InitTypeDef
结构体CAN_InitTypeDef定义了初始化bxCAN时所需的参数,结构体CAN_HandleTypeDef的成员Init是CAN_InitTypeDef类型的变量。
我们需要给Init的各成员赋值,为bxCAN初始化提供所需的参数。成员Prescaler、SyncJumpWidth、TimeSeg1、TimeSeg2用于配置CAN通信的位时序和位速率(波特率),关于位时序和位速率可参考《CAN总线协议》第8小节以及《STM32F4xx bxCAN》第5小节。tq = Prescaler x tPCLK,TimeSeg1 、TimeSeg2 、SyncJumpWidth分别表示BS1段、BS2段以及SJW各占多少个tq 。
FunctionalState是枚举类型,枚举值为Enable和Disable。TimeTriggeredMode为Enable时使能“时间触发通信模式”,关于“时间触发通信模式”可参考《STM32F4xx bxCAN》第2.1小节。
bxCAN处于总线关闭状态时,有两种方式退出总线关闭状态:(1)在CAN_RX上检测到128次连续11个位的1时,自动退出总线关闭状态;(2)需先将bxCAN切换至初始化模式,然后再退出初始化模式,之后在CAN_RX上检测到128次连续11个位的1时,退出总线关闭状态。AutoBusOff值为Enable时使用方式(1),值为Disable时使用方式(2)。
bxCAN处于睡眠模式时,有两种方式恢复到正常模式:(1)在CAN_RX引脚上检测到帧起始(SOF)时,由硬件将CAN_MCR寄存器SLEEP位清零,自动恢复至正常模式(2)由软件将CAN_MCR寄存器的SLEEP位清零,退出睡眠模式。AutoWakeUp值为Enable时使用方式(1),值为Disable时使用方式(2)。
AutoRetransmission值为Enable时,如果消息发送不成功,会被重新发送,直到发送成功,值为Disalble时,无论发送结果如何,消息均只发送一次。
TransmitFifoPriority值为Enable时,当多个发送邮箱同时处于“挂起”状态,发送顺序按先进先出(FIFO)规则,即先进入“挂起”状态的邮箱会被优先安排发送;值为Disable时,邮箱中消息的标识符ID越小,该邮箱的优先级越高,具有最高优先级的邮箱会被优先安排发送。
typedef struct
{
uint32_t Prescaler;
uint32_t Mode;
uint32_t SyncJumpWidth;
uint32_t TimeSeg1;
uint32_t TimeSeg2;
FunctionalState TimeTriggeredMode;
FunctionalState AutoBusOff;
FunctionalState AutoWakeUp;
FunctionalState AutoRetransmission;
FunctionalState ReceiveFifoLocked;
FunctionalState TransmitFifoPriority;
} CAN_InitTypeDef;
1.3 CAN_HandleTypeDef
我们在编写CAN通信程序时,需要定义一个结构体CAN_HandleTypeDef类型的变量,并对其成员Instance、Init进行赋值。调用相关接口函数时,将该变量的地址作为第一个参数传入。
typedef struct __CAN_HandleTypeDef
{
CAN_TypeDef *Instance;
CAN_InitTypeDef Init;
__IO HAL_CAN_StateTypeDef State;
__IO uint32_t ErrorCode;
} CAN_HandleTypeDef;
1.4 CAN_RxHeaderTypeDef
结构体CAN_RxHeaderTypeDef定义了消息(接收)的头部信息。StdID、ExtID、IDE、RTR、DLC是CAN数据报文中的字段,StdID、ExtID分别是标准标识符和扩展标识符,IDE指示报文的标识符是标准标识符还是扩展标识符,RTR指示报文属于数据帧还是遥控帧,DLC指示报文的数据段的长度,以字节为单位。Timestamp、FilterMatchIndex分别是消息的接收时间戳以及筛选器匹配索引,关于消息的接收时间戳以及筛选器匹配索引请参考《STM32F4xx bxCAN》第3.2小节。
typedef struct
{
uint32_t StdId;
uint32_t ExtId;
uint32_t IDE;
uint32_t RTR;
uint32_t DLC;
uint32_t Timestamp;
uint32_t FilterMatchIndex;
} CAN_RxHeaderTypeDef;
1.5 CAN_TxHeaderTypeDef
结构体CAN_TxHeaderTypeDef定义了消息(发送)的头部信息。StdID、ExtID、IDE、RTR、DLC是CAN报文中的字段。
如果TransmitGlobalTime值为Enable,并且使能了时间触发通信模式,则发送时间戳会作为数据报文的一部分一起被发送,关于时间触发通信模式以及发送时间戳请参考《stm32F4xx bxCAN》2.1小节。
typedef struct
{
uint32_t StdId;
uint32_t ExtId;
uint32_t IDE;
uint32_t RTR;
uint32_t DLC;
FunctionalState TransmitGlobalTime;
} CAN_TxHeaderTypeDef;
1.6 CAN_FilterTypeDef
结构体CAN_FilterTypeDef定义了配置标识符筛选器所需的参数,关于标识符筛选器请参考《STM32F4xx bxCAN》第4小节。
FilterBank是筛选器组的编号,取值0 ~ 27,每个筛选器组有CAN_FxR1、CAN_FxR2两个寄存器(x = 0…27,对应28个筛选器组)。FilterIdHigh、FilterIdLow分别对应寄存器CAN_FxR1的高16位和低16位,FilterMaskIdHigh、FilterMaskIdHigh分别对应寄存器CAN_FxR2的高16位和低16位。
FilterMode值为CAN_FILTERMODE_IDMASK时,将该筛选器组配置为标识符掩码模式,值为CAN_FILTERMODE_IDLIST时,将该筛选器组配置为标识符列表模式。
FilterScale值为CAN_FILTERSCALE_16BIT时,将该筛选器组配置为16位尺度,值为CAN_FILTERSCALE_32BIT时,配置为32位尺度。
FilterFIFOAssignment值为CAN_FILTER_FIFO0时,将该筛选器组分配给FIFO0,值为CAN_FILTER_FIFO1时,分配给FIFO1。
FilterActivation值为CAN_FILTER_ENABLE时,激活该筛选器组,值为CAN_FILTER_DISABLE时,不激活该筛选器组。
typedef struct
{
uint32_t FilterIdHigh;
uint32_t FilterIdLow;
uint32_t FilterMaskIdHigh;
uint32_t FilterMaskIdLow;
uint32_t FilterFIFOAssignment;
uint32_t FilterBank;
uint32_t FilterMode;
uint32_t FilterScale;
uint32_t FilterActivation;
uint32_t SlaveStartFilterBank;
} CAN_FilterTypeDef;
2. 接口函数
(1)HAL_CAN_Init(CAN_HandleTypeDef *hcan)
接口函数HAL_CAN_Init用于初始化bxCAN,它将bxCAN切换至初始化模式后,使用初始化参数hcan->Init对bxCAN进行初始化。
(2)HAL_CAN_DeInit(CAN_HandleTypeDef *hcan)
接口函数HAL_CAN_DeInit用于初始化bxCAN为缺省值,它首先调用函数HAL_CAN_Stop,然后软件复位bxCAN。
(3)HAL_CAN_ConfigFilter(CAN_HandleTypeDef *hcan, CAN_FilterTypeDef *sFilterConfig)
接口函数HAL_CAN_ConfigFilter用于配置标识符筛选器,配置参数通过指针sFilterConfig传入。
(4)HAL_CAN_Start(CAN_HandleTypeDef *hcan)
初始化bxCAN后,调用接口函数HAL_CAN_Start将bxCAN切换至正常模式,开始CAN报文的收发。
(5)HAL_CAN_Stop(CAN_HandleTypeDef *hcan)
可以调用接口函数HAL_CAN_Stop将bxCAN退出正常模式,切换回初始化模式,停止CAN报文的收发。
(6)HAL_CAN_RequestSleep(CAN_HandleTypeDef *hcan)
可以调用接口函数HAL_CAN_RequestSleep将bxCAN退出初始化模式或正常模式,切换至睡眠模式。
(7)HAL_CAN_WakeUp(CAN_HandleTypeDef *hcan)
可以调用接口函数HAL_CAN_RequestSleep将bxCAN退出睡眠模式,切换至进入睡眠模式之前所处的模式。
(8)HAL_CAN_AddTxMessage(CAN_HandleTypeDef *hcan, CAN_TxHeaderTypeDef *pHeader, uint8_t aData[], uint32_t *pTxMailbox)
接口函数HAL_CAN_AddTxMessage用于请求发送一条消息,它首先选择一个空的发送邮箱,并将消息写入邮箱对应的寄存器,然后将该邮箱置为“挂起”状态。处于“挂起”状态的发送邮箱会排队等待硬件安排发送。pHeader是消息的头部信息,包括CAN报文的IDE、RTR、DLC以及标识符ID,数组aData[]是CAN报文的数据段。接口函数通过指针pTxMailbox返回发送该消息所使用的发送邮箱,值可以是CAN_TX_MAILBOX0或CAN_TX_MAILBOX1或CAN_TX_MAILBOX2(bxCAN有三个发送邮箱)。
(9)HAL_CAN_AbortTxRequest(CAN_HandleTypeDef *hcan, uint32_t TxMailboxes)
接口函数HAL_CAN_AbortTxRequest用于中止邮箱消息的发送,TxMailboxes的值可以是CAN_TX_MAILBOX0或CAN_TX_MAILBOX1或CAN_TX_MAILBOX2。
(10)uint32_tHAL_CAN_IsTxMessagePending(CAN_HandleTypeDef*hcan,
uint32_t TxMailboxes)
接口函数HAL_CAN_IsTxMessagePending用于检查某个发送邮箱是否处于“挂起”状态,参数TxMailboxes的值可以是CAN_TX_MAILBOX0或CAN_TX_MAILBOX1或CAN_TX_MAILBOX2。
(11)uint32_t HAL_CAN_GetTxMailboxesFreeLevel(CAN_HandleTypeDef *hcan)
接口函数 HAL_CAN_GetTxMailboxesFreeLevel用于获取当前处于“空”状态的发送邮箱的个数。
(12)HAL_CAN_GetRxMessage(CAN_HandleTypeDef*hcan,uint32_t RxFifo, CAN_RxHeaderTypeDef *pHeader,uint8_t aData[])
bxCAN会将接收的有效消息放入FIFO中,接口函数HAL_CAN_GetRxMessage用于从FIFO中取出一条消息,参数RxFifo的值可以是CAN_RX_FIFO0或CAN_RX_FIFO1(bxCAN有两个接收FIFO)。pHeader是消息的头部信息,包括CAN报文的IDE、RTR、DLC以及标识符ID,数组aData[]是CAN报文的数据段。
(13)uint32_t HAL_CAN_GetRxFifoFillLevel(CAN_HandleTypeDef *hcan, uint32_t RxFifo)
接口函数HAL_CAN_GetRxFifoFillLevel用于获取FIFO中的消息数,参数RxFifo的值可以是CAN_RX_FIFO0或CAN_RX_FIFO1。
(14)HAL_CAN_ActivateNotification(CAN_HandleTypeDef *hcan, uint32_t ActiveITs)
接口函数HAL_CAN_ActivateNotification用于使能bxCAN的相关中断。
(15)HAL_CAN_DeactivateNotification(CAN_HandleTypeDef *hcan, uint32_t InactiveITs)
接口函数HAL_CAN_DeactivateNotification用于禁止bxCAN的相关中断。
3. 中断回调函数
中断触发后,中断向量函数都会统一调用函数HAL_CAN_IRQHandlerCAN_Handle(TypeDef *hcan),然后由函数HAL_CAN_IRQHandler根据中断事件的类型调用相应的回调函数。回调函数为弱函数(__weak),我们可以重写回调函数来实现处理逻辑。
(1)HAL_CAN_TxMailbox0CompleteCallback(CAN_HandleTypeDef *hcan)
当邮箱 0中的消息被成功发送后,函数HAL_CAN_TxMailbox0CompleteCallback会被调用。同理,邮箱1、邮箱2中的消息被成功发送后,函数HAL_CAN_TxMailbox1CompleteCallback、HAL_CAN_TxMailbox2CompleteCallback会被调用。
(2)HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
硬件每向FIFO0存储一条消息,函数HAL_CAN_RxFifo0MsgPendingCallback会被调用。同理,硬件每向FIFO1存储一条消息,函数HAL_CAN_RxFifo1MsgPendingCallback会被调用。
(3)HAL_CAN_RxFifo0FullCallback(CAN_HandleTypeDef *hcan)
FIFO0中存储三条消息后,函数HAL_CAN_RxFifo0FullCallback会被调用。同理,FIFO1中存储三条消息后,函数HAL_CAN_RxFifo1FullCallback会被调用。
(4)HAL_CAN_SleepCallback(CAN_HandleTypeDef *hcan)
bxCAN进入睡眠模式后,函数 HAL_CAN_SleepCallback会被调用。
(5)HAL_CAN_WakeUpFromRxMsgCallback(CAN_HandleTypeDef *hcan)
BxCAN退出睡眠模式后,函数HAL_CAN_WakeUpFromRxMsgCallback会被调用。
(6)HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan)
当bxCAN检查到总线错误时,函数HAL_CAN_WakeUpFromRxMsgCallback会被调用。