[MCU] 【国民技术低功耗系列N32L43x测评】07.通过CAN通讯波特率来自动计算配置参数

xld0932   2022-7-19 20:34 楼主

国民N32L43X系列MCU带有1路CAN接口,它支持CAN协议2.0A和2.0B。其在高效处理大量收到的报文的同时,极大地的降低了CPU资源的消耗。可以通过软件配置报文发送的优先级特性。对于一些安全要求较高的应用,bxCAN的硬件功能支持时间触发通信模式。其主要特点如下:

  • 支持CAN2.0A和2.0B主动模式
  • 波特率最高支持1Mbit/s
  • 支持时间触发通讯功能

发送:

  • 有3个发送邮箱
  • 软件可配置发送报文的优先级特性
  • 记录发送SOF时刻的时间戳功能

接收:

  • 有2个接收FIFO,每个FIFO都具有3级深度
  • 有14个过滤器组(每个CAN模块单独拥有)
  • 支持标识符列表模式
  • 可配置的FIFO溢出处理方式
  • 启示接收SOF时刻的时间戳功能
  • 时间触发通讯模式:
  • 支持禁止自动重传模式
  • 16位自由运行定时器
  • 可配置最后2个数据字节为发送时间戳

管理:

  • 中断可屏蔽
  • 邮箱单独分配了1块地址空间,用于提高软件执行的效率

单CAN:

  • CAN:管理在从bxCAN和512字节的SRAM存储器之间的通讯

 

 

CAN收发测试

参考例程的示例代码对CAN进行参数配置,使其通讯波特率为500kbps、使能所有的接收地址不进行滤波,使用MCU的PB8和PB9这两个端口引脚,配置CAN接收中断,在中断中打印出当前接收到的CAN消息内容,然后使用CAN任务每间隔1秒钟向外发送一个消息,使用PC端的CAN_Test软件进行监控或操作。硬件连接如下所示:

1.jpg

 

实现代码

void bxCAN_Init(void)
{
    CAN_InitType        CAN_InitStructure;
    CAN_FilterInitType  CAN_FilterInitStructure;
    GPIO_InitType       GPIO_InitStructure;
    NVIC_InitType       NVIC_InitStructure;

    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_AFIO,  ENABLE);
    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOB, ENABLE);

    GPIO_InitStruct(&GPIO_InitStructure);
    GPIO_InitStructure.Pin            = GPIO_PIN_8;
    GPIO_InitStructure.GPIO_Mode      = GPIO_Mode_Input;
    GPIO_InitStructure.GPIO_Pull      = GPIO_Pull_Up;
    GPIO_InitStructure.GPIO_Alternate = GPIO_AF5_CAN;
    GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);

    GPIO_InitStructure.Pin            = GPIO_PIN_9;
    GPIO_InitStructure.GPIO_Mode      = GPIO_Mode_AF_PP;
    GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);

    RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_CAN, ENABLE);

    CAN_DeInit(CAN);

    CAN_InitStruct(&CAN_InitStructure); /* 500kbps */
    CAN_InitStructure.TTCM              = DISABLE;
    CAN_InitStructure.ABOM              = DISABLE;
    CAN_InitStructure.AWKUM             = DISABLE;
    CAN_InitStructure.NART              = DISABLE;
    CAN_InitStructure.RFLM              = DISABLE;
    CAN_InitStructure.TXFP              = ENABLE;
    CAN_InitStructure.OperatingMode     = CAN_Normal_Mode;
    CAN_InitStructure.RSJW              = CAN_RSJW_1tq;
    CAN_InitStructure.TBS1              = CAN_TBS1_6tq;
    CAN_InitStructure.TBS2              = CAN_TBS2_2tq;
    CAN_InitStructure.BaudRatePrescaler = 6;
    CAN_Init(CAN, &CAN_InitStructure);

    CAN_FilterInitStructure.Filter_Num            = 0;
    CAN_FilterInitStructure.Filter_Mode           = CAN_Filter_IdMaskMode;
    CAN_FilterInitStructure.Filter_Scale          = CAN_Filter_32bitScale;
    CAN_FilterInitStructure.Filter_HighId         = 0x0000;
    CAN_FilterInitStructure.Filter_LowId          = 0x0000;
    CAN_FilterInitStructure.FilterMask_HighId     = 0x0000;
    CAN_FilterInitStructure.FilterMask_LowId      = 0x0000;
    CAN_FilterInitStructure.Filter_FIFOAssignment = CAN_FIFO0;
    CAN_FilterInitStructure.Filter_Act            = ENABLE;
    CAN_InitFilter(&CAN_FilterInitStructure);

    CAN_INTConfig(CAN, CAN_INT_FMP0, ENABLE);

    NVIC_InitStructure.NVIC_IRQChannel = CAN_RX0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    bxCAN_Baudrate(500000);

    TASK_Append(TASK_ID_CAN, bxCAN_Handler, 1000);
}

void CAN_RX0_IRQHandler(void)
{
    CanRxMessage CAN_RxMessage;

    CAN_ReceiveMessage(CAN, CAN_FIFO0, &CAN_RxMessage);

    if(CAN_RxMessage.IDE == CAN_Extended_Id)
    {
        printf("\r\nReceive ID[0x%02x], DLC[0x%02x]\r\n", CAN_RxMessage.ExtId, CAN_RxMessage.DLC);
    }
    else
    {
        printf("\r\nReceive ID[0x%02x], DLC[0x%02x]\r\n", CAN_RxMessage.StdId, CAN_RxMessage.DLC);
    }

    for(uint8_t i = 0; i < CAN_RxMessage.DLC; i++)
    {
        printf("0x%02x ", CAN_RxMessage.Data);
    }

    printf("\r\n");
}

void bxCAN_Handler(void)
{
    static uint8_t StdId = 0;
    static uint8_t Count = 0;

    uint8_t TransmitMailbox = 0;
    CanTxMessage  CAN_TxMessage;

    CAN_TxMessage.StdId = StdId++;
    CAN_TxMessage.IDE   = CAN_ID_STD;
    CAN_TxMessage.RTR   = CAN_RTRQ_DATA;
    CAN_TxMessage.DLC   = 0x08;

    for(uint8_t i = 0; i < CAN_TxMessage.DLC; i++)
    {
        CAN_TxMessage.Data = Count++;
    }

    TransmitMailbox = CAN_TransmitMessage(CAN, &CAN_TxMessage);
    while(CAN_TransmitSTS(CAN, TransmitMailbox) != CANTXSTSOK);
}

 

 

运行测试

2.jpg 3.png

 

我们在配置CAN参数时,官方的示例程序给出几个常用的通讯波特率和对应的配置参数值,如下图所示:

4.jpg

 

那我们需要使用其它通讯波特率时,该如何设置呢?是不是可以通过波特自动计算出这些配置参数呢?我们需要先了解一下这些配置参数和通讯波特率之间的关系,这样就可以通过编程的方式来自动获取配置参数了。

 

参考MCU用户手册中CAN位时间特性和CAN位时序寄存器的章节内容,如下所示:

5.png 6.png 7.png

 

我们可以根据最终需要的通讯波特率计算出正常的位时间,通过PCLK1时钟和波特率分频器可以计算出时间单元tq的时间长度,然后再通过TBS1、TBS2结合tq计算得到位时间,如下程序中通过穷举的方式列出了所有满足条件的配置值,同时根据不同的配置计算出了对应的采样点,具体程序如下所示:


void bxCAN_Baudrate(uint32_t Baudrate)
{
    printf("\r\n%s : %d", __FUNCTION__, Baudrate);

    const char *str_TBS1[16] =
    {
        "CAN_TBS1_1tq ", "CAN_TBS1_2tq ", "CAN_TBS1_3tq ", "CAN_TBS1_4tq ",
        "CAN_TBS1_5tq ", "CAN_TBS1_6tq ", "CAN_TBS1_7tq ", "CAN_TBS1_8tq ",
        "CAN_TBS1_9tq ", "CAN_TBS1_10tq", "CAN_TBS1_11tq", "CAN_TBS1_12tq",
        "CAN_TBS1_13tq", "CAN_TBS1_14tq", "CAN_TBS1_15tq", "CAN_TBS1_16tq",
    };

    const char *str_TBS2[8] =
    {
        "CAN_TBS2_1tq", "CAN_TBS2_2tq", "CAN_TBS2_3tq", "CAN_TBS2_4tq",
        "CAN_TBS2_5tq", "CAN_TBS2_6tq", "CAN_TBS2_7tq", "CAN_TBS2_8tq",
    };

    uint16_t BRTP = 0, TBS1 = 0, TBS2 = 0;

    RCC_ClocksType RCC_Clocks;
    RCC_GetClocksFreqValue(&RCC_Clocks);

    for(BRTP = 0; BRTP < 1024; BRTP++)
    {
        for(TBS1 = 0; TBS1 < 16; TBS1++)
        {
            for(TBS2 = 0; TBS2 < 8; TBS2++)
            {
                if(Baudrate == (RCC_Clocks.Pclk1Freq / (BRTP + 1) / (TBS1 + TBS2 + 3)))
                {
                    float SamplePoint = (float)(TBS1 + 2) / (float)(TBS1 + TBS2 + 3) * 100;

                    printf("\r\n-----------------------------------------------------------------------------------");
                    printf("\r\nBRTP : %d\tTBS1 : %s\tTBS2 : %s\tSamplePoint : %0.1f%%", BRTP + 1, str_TBS1[TBS1], str_TBS2[TBS2], SamplePoint);
                }
            }
        }
    }

    printf("\r\n-----------------------------------------------------------------------------------");
}

 

 

运行结果

8.png

 

 

附件

软件工程源代码:

Template.zip (44.58 MB)
(下载次数: 67, 2022-7-19 20:34 上传)

本帖最后由 xld0932 于 2022-7-19 20:35 编辑
We are a team and we work as a team !

回复评论 (5)

以为是芯片自适应的,结果后面看见了三个for

默认摸鱼,再摸鱼。2022、9、28
点赞  2022-7-20 19:11
引用: freebsder 发表于 2022-7-20 19:11 以为是芯片自适应的,结果后面看见了三个for

我还没遇到哪个芯片的CAN通讯波特率说是能够自适应的

We are a team and we work as a team !
点赞  2022-7-20 21:58
引用: xld0932 发表于 2022-7-20 21:58 我还没遇到哪个芯片的CAN通讯波特率说是能够自适应的

半自适应,哈哈哈。

默认摸鱼,再摸鱼。2022、9、28
点赞  2022-7-21 18:55
引用: freebsder 发表于 2022-7-21 18:55 半自适应,哈哈哈。

We are a team and we work as a team !
点赞  2022-7-21 20:56

6666


点赞  2024-5-12 09:31
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复