STM32的CAN通信的收发函数:
// 发送函数
uint8_t CAN_Transmit(CAN_TypeDef* CANx, CanTxMsg* TxMessage);
// 接收函数
void CAN_Receive(CAN_TypeDef* CANx, uint8_t FIFONumber, CanRxMsg* RxMessage);
CAN发送消息结构体定义:
typedef struct {
uint32_t StdId; /* 存储报文的标准标识符11位,0-0x7FF. */
uint32_t ExtId; /* 存储报文的扩展标识符29位,0-0x1FFFFFFF. */
/* ExtId与StdId这两个成员根据IDE位配置,只有一个是有效的。*/
uint8_t IDE; /* 存储IDE扩展标志 */
/* 当它的值为宏CAN_ID_STD时表示本报文是标准帧,使用StdId成员存储报文ID;
当它的值为宏CAN_ID_EXT时表示本报文是扩展帧,使用ExtId成员存储报文ID。*/
uint8_t RTR; /* 存储RTR远程帧标志*/
/* 当它的值为宏CAN_RTR_Data时表示本报文是数据帧;
当它的值为宏CAN_RTR_Remote时表示本报文是遥控帧,
由于遥控帧没有数据段,所以当报文是遥控帧时,下面的Data[8]成员的内容是无效的。*/
uint8_t DLC; /* 存储报文数据段的长度,0-8, 当报文是遥控帧时DLC值为0。 */
uint8_t Data[8]; /* 存储报文数据段的内容 */
} CanTxMsg;
当需要使用CAN发送报文时,先定义一个上面发送类型的结构体,然后把报文的内容按成员赋值到该结构体中,最后调用库函数CAN_Transmit把这些内容写入到发送邮箱即可把报文发送出去。
CAN接收消息结构体定义:
typedef struct {
uint32_t StdId; /* 存储了报文的标准标识符11位,0-0x7FF. */
uint32_t ExtId; /* 存储了报文的扩展标识符29位,0-0x1FFFFFFF. */
/* ExtId与StdId这两个成员根据IDE位配置,只有一个是有效的。*/
uint8_t IDE; /* 存储了IDE扩展标志 */
/* 当它的值为宏CAN_ID_STD时表示本报文是标准帧,使用StdId成员存储报文ID;
当它的值为宏CAN_ID_EXT时表示本报文是扩展帧,使用ExtId成员存储报文ID。*/
uint8_t RTR; /* 存储了RTR远程帧标志*/
/* 当它的值为宏CAN_RTR_Data时表示本报文是数据帧;
当它的值为宏CAN_RTR_Remote时表示本报文是遥控帧,
由于遥控帧没有数据段,所以当报文是遥控帧时,下面的Data[8]成员的内容是无效的。*/
uint8_t DLC; /* 存储报文数据段的长度,0-8, 当报文是遥控帧时DLC值为0。 */
uint8_t Data[8]; /* 存储了报文数据段的内容 */
uint8_t FMI; /* 存储了筛选器的编号,表示本报文是经过哪个筛选器存储进接收FIFO的,可以用它简化软件处理,0-0xFF */
} CanRxMsg;
CAN发送消息函数:
/**
* @brief Initiates the transmission of a message.
* @param CANx: where x can be 1 or 2 to to select the CAN peripheral.
* @param TxMessage: pointer to a structure which contains CAN Id, CAN
* DLC and CAN data.
* @retval The number of the mailbox that is used for transmission
* or CAN_TxStatus_NoMailBox if there is no empty mailbox.
*/
uint8_t CAN_Transmit(CAN_TypeDef *CANx, CanTxMsg *TxMessage)
{
uint8_t transmit_mailbox = 0;
/* 参数检查 */
assert_param(IS_CAN_ALL_PERIPH(CANx)); // 检查CANx是否是有效的CAN接口
assert_param(IS_CAN_IDTYPE(TxMessage->IDE)); // 检查TxMessage中的IDE是否是有效的标识符类型
assert_param(IS_CAN_RTR(TxMessage->RTR)); // 检查TxMessage中的RTR是否是有效的标识符类型
assert_param(IS_CAN_DLC(TxMessage->DLC)); // 检查TxMessage中的DLC是否是有效的标识符类型
/* 选择一个空的发送邮箱 */
/*逐个检查发送邮箱是否为空,为空则记录邮箱号,否则检查下一个;
如果所有邮箱都非空,则给邮箱号赋值CAN_TxStatus_NoMailBox,表示没有非空邮箱*/
if ((CANx->TSR & CAN_TSR_TME0) == CAN_TSR_TME0)
{
transmit_mailbox = 0;
}
else if ((CANx->TSR & CAN_TSR_TME1) == CAN_TSR_TME1)
{
transmit_mailbox = 1;
}
else if ((CANx->TSR & CAN_TSR_TME2) == CAN_TSR_TME2)
{
transmit_mailbox = 2;
}
else
{
transmit_mailbox = CAN_TxStatus_NoMailBox;
}
/*如果存在空邮箱,开始设置发送消息结构体*/
if (transmit_mailbox != CAN_TxStatus_NoMailBox)
{
/* 设置消息的标识符 */
CANx->sTxMailBox[transmit_mailbox].TIR &= TMIDxR_TXRQ; // 清除发送邮箱的发送标志位,为了确保在消息设置完毕之前不会意外地发送出去
if (TxMessage->IDE == CAN_Id_Standard) // 检查IDE,分辨要发送的消息的类型(标准帧和扩展帧)
{
/*要发送的消息是标准帧*/
assert_param(IS_CAN_STDID(TxMessage->StdId)); // 检查标准帧的有效性
// 将标准标识符的值左移21位,并与RTR进行或操作,然后设置到对应的传输邮箱的TIR寄存器中
CANx->sTxMailBox[transmit_mailbox].TIR |= ((TxMessage->StdId << 21) |
TxMessage->RTR);
}
else
{
/*要发送的消息是扩展帧*/
assert_param(IS_CAN_EXTID(TxMessage->ExtId)); // 检查扩展帧的有效性
// 将扩展标识符的值左移3位,并与IDE、RTR进行或操作,然后设置到对应的传输邮箱的TIR寄存器中
CANx->sTxMailBox[transmit_mailbox].TIR |= ((TxMessage->ExtId << 3) |
TxMessage->IDE |
TxMessage->RTR);
}
/* 设置消息的数据长度 */
/*对数据长度进行处理和设置,确保传输邮箱的 TDTR 寄存器的数据长度字段与消息的数据长度一致。*/
/* 通过与操作,将 TxMessage->DLC 的高 4 位全部清零,只保留低 4 位的值。
确保 DLC 的值不超过 4 位的掩码。*/
TxMessage->DLC &= (uint8_t)0x0000000F;
/* 保留 TDTR 寄存器的高 28 位,将低 4 位设置为 0,
清除传输邮箱中 TDTR 寄存器(数据长度寄存器)的低 4 位。*/
CANx->sTxMailBox[transmit_mailbox].TDTR &= (uint32_t)0xFFFFFFF0;
/* 将 DLC 的值设置给传输邮箱的 TDTR 寄存器。
通过或操作,将上述清除低 4 位值的 TDTR 寄存器与消息的数据长度(经过掩码操作后的 TxMessage->DLC)进行或操作,
将 DLC 的值写入到 TDTR 寄存器的低 4 位中,完成数据长度的设置。*/
CANx->sTxMailBox[transmit_mailbox].TDTR |= TxMessage->DLC;
/* 设置消息的数据内容 */
/*将数据的高4位和低4位分别设置到对应的传输邮箱的TDLR和TDHR寄存器中。
函数通过位操作将数据字节按照大端字节序进行转换,使得数据在传输时按照正确的顺序排列*/
CANx->sTxMailBox[transmit_mailbox].TDLR = (((uint32_t)TxMessage->Data[3] << 24) |
((uint32_t)TxMessage->Data[2] << 16) |
((uint32_t)TxMessage->Data[1] << 8) |
((uint32_t)TxMessage->Data[0]));
CANx->sTxMailBox[transmit_mailbox].TDHR = (((uint32_t)TxMessage->Data[7] << 24) |
((uint32_t)TxMessage->Data[6] << 16) |
((uint32_t)TxMessage->Data[5] << 8) |
((uint32_t)TxMessage->Data[4]));
/* 置位发送邮箱的发送标志位,请求发送 */
CANx->sTxMailBox[transmit_mailbox].TIR |= TMIDxR_TXRQ;
}
/*函数返回所使用的传输邮箱的编号,如果没有空闲的传输邮箱可用,则返回CAN_TxStatus_NoMailBox*/
return transmit_mailbox;
}
CAN接收消息函数:
/**
* @brief Receives a message.
* @param CANx: where x can be 1 or 2 to to select the CAN peripheral.
* @param FIFONumber: Receive FIFO number, CAN_FIFO0 or CAN_FIFO1.
* @param RxMessage: pointer to a structure receive message which contains
* CAN Id, CAN DLC, CAN datas and FMI number.
* @retval None.
*/
void CAN_Receive(CAN_TypeDef *CANx, uint8_t FIFONumber, CanRxMsg *RxMessage)
{
/* 参数检查 */
assert_param(IS_CAN_ALL_PERIPH(CANx)); // 检查CANx是否为有效的CAN接口
assert_param(IS_CAN_FIFO(FIFONumber)); // 检查FIFONumber是否为有效的FIFO编号
/* 标识符检测 */
// 获取接收到报文的IDE
RxMessage->IDE = (uint8_t)0x04 & CANx->sFIFOMailBox[FIFONumber].RIR;
if (RxMessage->IDE == CAN_Id_Standard) // 判断是否是标准帧
{
// 获取标准帧的标准ID
RxMessage->StdId = (uint32_t)0x000007FF & (CANx->sFIFOMailBox[FIFONumber].RIR >> 21);
}
else // 接收到的是扩展帧
{
// 获取扩展帧的扩展ID
RxMessage->ExtId = (uint32_t)0x1FFFFFFF & (CANx->sFIFOMailBox[FIFONumber].RIR >> 3);
}
// 获取远程帧标志位
RxMessage->RTR = (uint8_t)0x02 & CANx->sFIFOMailBox[FIFONumber].RIR;
// 获取数据位长度
RxMessage->DLC = (uint8_t)0x0F & CANx->sFIFOMailBox[FIFONumber].RDTR;
// 获取筛选器编号
RxMessage->FMI = (uint8_t)0xFF & (CANx->sFIFOMailBox[FIFONumber].RDTR >> 8);
// 获取数据内容
RxMessage->Data[0] = (uint8_t)0xFF & CANx->sFIFOMailBox[FIFONumber].RDLR;
RxMessage->Data[1] = (uint8_t)0xFF & (CANx->sFIFOMailBox[FIFONumber].RDLR >> 8);
RxMessage->Data[2] = (uint8_t)0xFF & (CANx->sFIFOMailBox[FIFONumber].RDLR >> 16);
RxMessage->Data[3] = (uint8_t)0xFF & (CANx->sFIFOMailBox[FIFONumber].RDLR >> 24);
RxMessage->Data[4] = (uint8_t)0xFF & CANx->sFIFOMailBox[FIFONumber].RDHR;
RxMessage->Data[5] = (uint8_t)0xFF & (CANx->sFIFOMailBox[FIFONumber].RDHR >> 8);
RxMessage->Data[6] = (uint8_t)0xFF & (CANx->sFIFOMailBox[FIFONumber].RDHR >> 16);
RxMessage->Data[7] = (uint8_t)0xFF & (CANx->sFIFOMailBox[FIFONumber].RDHR >> 24);
/* 释放接收FIFO */
/* 释放FIFO0 */
if (FIFONumber == CAN_FIFO0)
{
CANx->RF0R |= CAN_RF0R_RFOM0;
}
/* 释放FIFO1 */
else /* FIFONumber == CAN_FIFO1 */
{
CANx->RF1R |= CAN_RF1R_RFOM1;
}
}