HC32 CAN通信

  • Post author:
  • Post category:其他




HC CAN简介和ST对比

  • HC CAN

    在这里插入图片描述

    手册里面明确写出有两种不同类型的缓冲器,PTB 优先于STB。这点和ST的不一样。

    HC的结构图

    在这里插入图片描述

  • ST

    ST CAN结构借用一下野火的图展示一下

    在这里插入图片描述

    STM32 的有两组 CAN 控制器,其中 CAN1 是主设备,框图中的“存储访问控制器”是由 CAN1 控制的, CAN2 无法直接访问存储区域,所以使用 CAN2 的时候必须使能CAN1 外设的时钟。



波特率设定

CAN 通信使用时钟 can_clk 的时钟源为外部高速振荡器, 使用 CAN 模块之前,需要设定 CAN 通信时钟。下图给出 CAN 位时间定义图,虚线上部分为 CAN 协议规定的位时间, 虚线下部分为本 CAN 控制器 CAN-CTRL 定义的位时间。 其中 segment1 和 segment2 可以通过寄存器 SBT 设定。 SBT 寄存器只能在 CAN_CFG_STAT.RESET=1 即 CAN 软件复位时设定。

在这里插入图片描述


注意:HC的CAN的时间片段并不是按照标准的CAN协议制定的。只有seg1和Seg2 两个参数,这个和ST的有不一样。在设置HCseg1 和seg2 的时候还需要满足手册上时间价格的要求,具体如下:


在这里插入图片描述



筛选器

CAN_CTRL 提供 16 组 32 位筛选器用于过滤接收到的数据从而降低 CPU 负荷, 筛选器可以支持标准格式 11 位 ID 或者扩展格式 29 位 ID。每组筛选器有一个 32 位 ID CODE 寄存器和一个 32 位 ID MASK 寄存器, ID CODE 寄存器用于比较接收到 CAN ID, 而 ID MASK 寄存器用于选择比较的 CAN ID 位。

对应的 ID MASK 位为 1 时,不比较该位的 ID CODE


注意: HC的 CODE模式和ST32的基本上一直表示 只有CAN ID和code寄存器里面的一样才会被接收。MASK模式也和ST32的掩码模式类似,进行过滤筛选,值得注意的是ST32的掩码模式相应的位置被设置为1后,进行掩码为的对比,只要掩码(关键字)相同,就符合要求,报文就会被保存到接收 FIFO。 但是HC的掩码模式是相应的掩码位设置为1后,是不对比CAN ID对应的改位的,反而设置为0后才需要和ID进行匹配。即 ST掩码模式设置1的是需要比对的ID位,而HC设置为1的是需要忽略的位。



需要注意的坑

  • CAN 的一些设置完全可以参照dll库中提供的配置方式进行为微调。值得注意的是CAN的中断模式是全部打开的,在设计的时候需要先将所有的全部失能,然后再根据自己的需求进行配置。
 CAN_IntCmd(TYPE_CANX, CAN_INT_ALL, Disable);
 CAN_IntCmd(TYPE_CANX, CAN_INT_RX | CAN_INT_ERR_INT 
                     |XXXXX| XXXXX | XXXXXX,
                    Enable);
  • stc_can_tx_t结构体
typedef struct
{
    uint32_t u32ID;                         /*!< 11 bits standard ID or 29 bits extended ID, depending on IDE. */
    union
    {
        uint32_t u32Ctrl;
        struct
        {
            uint32_t DLC: 4;                /*!< Data length code. Length of the data segment of data frame. \
                                                 It should be zero while the frame is remote frame. \
                                                 This parameter can be a value of @ref CAN_DLC */
            uint32_t BRS: 1;                /*!< Bit rate switch. */
            uint32_t FDF: 1;                /*!< CAN FD frame. */
            uint32_t RTR: 1;                /*!< Remote transmission request bit.
                                                 It is used to distinguish between data frames and remote frames. */
            uint32_t IDE: 1;                /*!< Identifier extension flag.
                                                 It is used to distinguish between standard format and extended format.
                                                 This parameter can be a 1 or 0. */
            uint32_t RSVD: 24;              /*!< Reserved bits. */
        };
    };
    uint8_t *pu8Data;                      /*!< Pointer to data filed of data frame. */
} stc_can_tx_t

需要注意这里面是一个联合体的形式,在设置DLC、BRS等后一定不要轻易的设置u32Ctrl,不然参数设置就会出现问题。

  • stc_can_rx_t 结构体
typedef struct
{
    uint32_t u32ID;                         /*!< 11 bits standard ID or 29 bits extended ID, depending on IDE. */
    union
    {
        uint32_t u32Ctrl;
        struct
        {
            uint32_t DLC: 4;                /*!< Data length code. Length of the data segment of data frame. \
                                                 It should be zero while the frame is remote frame. \
                                                 This parameter can be a value of @ref CAN_DLC */
            uint32_t BRS: 1;                /*!< Bit rate switch. */
            uint32_t FDF: 1;                /*!< CAN FD frame. */
            uint32_t RTR: 1;                /*!< Remote transmission request bit.
                                                 It is used to distinguish between data frames and remote frames. */
            uint32_t IDE: 1;                /*!< Identifier extension flag.
                                                 It is used to distinguish between standard format and extended format.
                                                 This parameter can be 1 or 0. */
            uint32_t RSVD: 4;               /*!< Reserved bits. */
            uint32_t TX: 1;                 /*!< This bit is set to 1 when receiving self-transmitted data in loopback mode. */
            uint32_t ERRT: 3;               /*!< Error type. */
            uint32_t CYCLE_TIME: 16;        /*!< Cycle time of time-triggered communication(TTC). */
        };
    };
    uint8_t *pu8Data;                       /*!< Pointer to data filed of data frame. */
} stc_can_rx_t;

这个需要注意 uint8_t *pu8Data 这个只是一个指针,在使用之前必须赋值接收数据的缓存区,St32的在结构体里面是一个数组,HC这样设计可能是为了兼容CANFD协议。

  • CAN_TransData(M4_CAN_TypeDef *CANx, const stc_can_tx_t *pstcTx,

    uint8_t u8TxBufType, uint8_t u8STBTxCtrl, uint32_t u32Timeout)

    这个函数中有一个功能即 timeout,使用该功能需要将发送完成中断打开并在中断函数中及时清零,不然在发送的是时候回一直卡在其中,数据发送异常。

  • 发送函数

发送函数的时候自己遇到了一个奇怪的问题,不知道具体原因,但经过更改后基本上能用

使用PTB发送,使用这种发送方式 伪代码如下

//发送单包数据
bool CAN_Send_Data(eBsp_Can can,
                             uint32_t StdId,
                             uint32_t ExtId,
                             uint8_t Ide,
                             uint8_t Rtr,
                             uint8_t *Data,
                             int len
                             )
{
    uint16_t tranLen = 0;
    pCANx_TypeDef CANx = NULL;
    stc_can_tx_t stcTx;
    int countUs = 0;
    int i;
    int  errrr = 0;
    
    //获取CAN 实例
    CANx = CANInstance(can);
    if(NULL == CANx || NULL == Data)
    {
        return 0;
    }
        
    if(len >= 8)
    {
        len = 8;
    }
    
    stcTx.u32Ctrl = 0x0U;

    stcTx.DLC =GetDlc(can, len);
    stcTx.u32ID   = ExtId;
    stcTx.IDE     = Ide;                                              // 0- 标准ID 1 - 扩展ID
    stcTx.pu8Data = Data;
    ///
    ///while(((errrr =CAN_TransData(CANx, &stcTx, CAN_BUF_STB,CAN_STB_TRANS_ALL , 0U))) != Ok)
    while(((errrr =CAN_TransData(CANx, &stcTx, CAN_BUF_PTB,CAN_STB_TRANS_ALL , 0U))) != Ok)
    {
        delayus(10);   
        countUs += 10; 
        if(countUs >= 100000)
        {
            log("CAN_Send_Data Send Error[No Send][%d]\r\n", CAN_GetErrType(CANx)); 
        }
        log(" [%d]  %d \r\n", CAN_GetErrType(CANx),errrr); 
    }
    

    return FALSE;
}

//发送多包数据
void CAN_SendBuff(uint8_t *buf, int Len)
{
#define LEN  8
    int remains = 0;
    int pkt = 0;
    int i = 0;
    uint8_t dstAddr;
    uint8_t srcAddr;
    int index = 0;
    int j =0;
    //addr为id
    pkt = Len / 8;
    remains = Len % 8;

    for(i=0; i< pkt; i++)
    {
        CAN_Send_Data(CAN1,
                      0,
                     ID,  //CAN 地址
                      1,
                      0,
                      (buf + index), //数据地址
                      LEN);
        
        index += LEN;
    }
    if(remains > 0)
    {
        CAN_Send_Data(CAN1,
                      0,
                      ID,  //CAN 地址
                      1,
                      0,
                      (buf + index), //数据地址
                      remains);
    }
#undef LEN
}

问题是使用CAN_TransData(CANx, &stcTx, CAN_BUF_PTB,CAN_STB_TRANS_ALL , 0U) 发送的时候所有的数据都能发送完成,(while中出现重发,错误码中显示正在发送,即可以理解为上一包为发送完成)在CAN总线上使用设备抓包,数据完全能抓的到。

但是使用CAN_TransData(CANx, &stcTx, CAN_BUF_STB,CAN_STB_TRANS_ALL , 0U),压力测试过程中, 发送端通过测试时显示buff的数据发送完成,在CAN总线上使用设备抓包,数据不完整,在while中errrr 显示正在发送 CAN_GetErrType()显示没有错误。由此推测,can在将数据放到FIFO中的3个邮箱时,邮箱还是满的。并且数据放到邮箱的数据可能存在数据丢失的可能(在压力测试的情况下,1ms发送一包数据)。

猜想验证

bool CAN_Send_Data(eBsp_Can can,
                             uint32_t StdId,
                             uint32_t ExtId,
                             uint8_t Ide,
                             uint8_t Rtr,
                             uint8_t *Data,
                             int len
                             )
{
    
    //同上面函数
    ………………

    while(Set == CAN_GetStatus(CANx,CAN_FLAG_TB_FULL))
    {
         i++;
         delayus(CAN_LOOP_US);
         if( i >= 5000)
         {
            CAN_LOG("TB full %d\r\n",i); 
         }
    }

//    //猜测发完后可能还要进行一些动作要延时
//    if( i > 0)
//    {
//        delay_US(500);
//    }
    
    while(((errrr =CAN_TransData(CANx, &stcTx, CAN_BUF_STB,CAN_STB_TRANS_ALL , 0U))) != Ok)
    {
       delayus(10);   
        countUs += 10; 
        if(countUs >= 100000)
        {
            log("CAN_Send_Data Send Error[No Send][%d]\r\n", CAN_GetErrType(CANx)); 
        }
        log(" [%d]  %d \r\n", CAN_GetErrType(CANx),errrr); 
    }
}

更改为判断FIFO 不满的时候在填数据到fifo中,在压力测试的时候同样出现 发送端数据发送完成,总线上数据不完整,接收端 数据异常。

还需要加上一个延时才能正常发送。具体原因未知 试验的时候CAN的速率比较低,可能是改为题导致,后面验证一下高速率1M的时候看是什么情况。



版权声明:本文为zhaopenglihui原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。