STM32的CAN通信的收发库函数解读

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;
  }
}

  • 4
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值