MM32F0160

1►FlexCAN-FD简介

MM32F0160 系列 MCU 具有一个 FlexCAN 模块,该模块遵循 ISO 11898-1 标准、 CAN FD 和 CAN 2.0B 协议规范,不仅兼容传统CAN,还支持CAN FD模式。在CAN-FD模式下,可实现最高8 Mbps的FD模式通信速率,支持标准帧(11位标识符)和扩展帧(29位标识符),支持最大64字节有效负载,并且具有非常灵活的用于传输和接收的邮箱系统。

使用CAN FD较传统CAN具有如下优点:

(1)增加了数据长度,在发送长数据时,软件更加简单高效,满足更高的数据吞吐量。

(2)提高了传输速率,使得延迟时间更短,具有更好的实时性,满足更高的带宽。

(3)扩展了CRC场,为数据内容提供了更好的保护,增加了系统安全性。

本章节初步学习使用MM32F0160 FlexCAN-FD接口实现CAN FD通信,相关例程参考灵动官网的LibSamples或在此基础上修改。关于CAN FD协议不再进行详细介绍,感兴趣的小伙伴可以查阅相关资料增进了解。

2►CAN FD帧

ISO 11898-1标准指定了符合ISO 11898-1(2003)标准的经典帧格式,并引入了CAN FD(Flexible Data Rate)帧格式。经典帧格式支持高达1Mbps的比特率,以及每帧8字节的有效负载。FD帧格式支持超过1Mbps的比特率,以及每帧超过8字节的有效负载。FlexCAN可以收发CAN FD和经典CAN格式交替的报文。

CAN FD 帧中有三个附加的控制位:

EDL:扩展数据长度位,支持更多的数据负载。

BRS比特率切换位:决定CAN FD帧是否切换比特率。

ESI错误状态指示:错误主动节点发送显性,错误被动节点发送隐性。

CAN FD格式不支持远程帧,远程帧总是以经典CAN格式传输。接收到FD帧并匹配邮箱时,接收报文缓冲区的RTR位将被无效化。

CAN FD报文数据字段可超过8字节,支持12至64字节。CAN FD报文可切换比特率,使CAN帧的控制字段、数据字段和CRC字段比特率高于帧的开始和结束。

CAN FD帧结构(部分)如下图所示:

1.png

2.png

CAN FD帧从SOF(帧起始)到BRS的仲裁段,以标称比特率传输;从BRS到CRC界定符的数据段,以数据比特率传输;从CRC界定符到Intermission位,传输恢复为标称比特率。如果CAN FD帧中BRS为隐性,则位时序在BRS的采样点发生变化。BRS位之前,CAN FD仲裁段的标称位时序由CAN_CBT或CAN_CTRL1寄存器定义;检测到隐性BRS时,数据位时序由CAN_FDCBT 寄存器定义。

3►协议时序

FlexCAN具有单独配置CAN FD协议时序的寄存器,CAN FD位时序寄存器(CAN_FDCBT)存储用于控制位时序参数的字段:FPRESDIV、FPROPSEG、FPSEG1、FPSEG2和FRJW。

CAN FD报文数据段的FDPRESDIV定义了串行时钟(Sclock)的预分频,见下列方程。串行时钟的周期定义了用于构成CAN FD波形的时间单位Tq,Tq为CAN引擎所能处理的最小时间单元。

3.jpg

比特率定义了接收或传输 CAN FD报文的速率,公式如下:

4.jpg

位时间可以细分为三个部分:

  • 同步段(SYNC_SEG):1Tq的固定长度;信号边沿出现在该段内。

  • 时间段1:包括传播段和相位段1。FlexCAN 使用 CAN_FDCBT寄存器的FDPROPSEG和FDPSEG1字段,其总和(+1)为2 ~ 39Tq。

  • 时间段 2:包括相位段2。其值(+1)为2 ~ 8Tq。时间段2不能小于信息处理时间2Tq。

当采用CAN FD位作为持续时间的衡量标准时,一个CAN FD位的外设时钟个数NumClkBit为:

5.jpg

fCANFDCLK为PE时钟,单位Hz。

fSYS为系统(CHI)时钟频率,单位Hz。

4►报文缓冲区

MM32F0160 FlexCAN遵循CAN FD协议规范,该模块已经设计了对应的CAN FD报文缓冲区结构。经典 CAN 帧使用传统型 Rx FIFO,CAN FD帧使用增强型Rx FIFO。下图为FlexCAN所使用的报文缓冲区结构,包括CAN 2.0B的两种帧格式:扩展帧(29位ID)和标准帧(11位ID)。每个报文缓冲区由16、24、40或72字节组成,其中包括8、16、32或64字节的数据。邮箱使用 0x80 ~ 0x27F 的内存区域。

6.png

EDL — 扩展数据长度,EDL位区分CAN帧和CAN FD 帧。

BRS — 比特率切换,定义是否在 CAN FD 帧内切换比特率。

ESI — 错误状态指示,表示发送节点是错误主动还是错误被动。

CODE — 报文缓冲区代码,CODE 字段可以被 CPU 和 FlexCAN 读写,用作报文缓冲区匹配和仲裁过程的一部分。编码详见用户手册。

SRR — 替代远程请求,

  • 1:扩展帧格式传输时,必须使用隐性位。

  • 0:扩展帧格式传输时,显性位无效。

只用于扩展帧格式。传输时(发送缓冲区)该位必须设置为 1,且将会和从 CAN 总线上接收到的值一起存储于接收缓冲区。该位可以被接收为隐性或显性,如果 FlexCAN 以显性位接收,则认为仲裁丢失。

IDE — ID扩展位,

  • 1:扩展帧;

  • 0:标准帧。

RTR — 远程传输请求

  • 1:如果是发送MB,则表示当前MB可能有一个远程帧待发送;如果是接收MB,则接收到的远程帧将会被存储;

  • 0:表示当前的M 中有一个数据帧待传输。在接收MB中,可能会被用于匹配过程。

如果FlexCAN传输1(隐性),接收到0(显性),则认为仲裁丢失。如果 RTR传输0(显性),接收到1(隐性),则认为是位错误。如果接收到的值与发送值相同,则被认为是一次成功的位传输。

注:配置CAN FD帧时RTR位必须为0。

DLC — 数据字节长度

该4位字段为发送/接收数据的长度(以字节为单位),位于偏移地址为0x8到0xF的MB空间。

接收阶段,该字段由FlexCAN写入,从接收帧的DLC字段复制而得;

传输阶段,该字段由CPU写入,且与要传输的帧的DLC字段相对应。

当RTR = 1 时,被传输的帧为远程帧,不包含数据字段(DLC 字段的设置无效,参见表格“有效数据字节”)。

TIME STAMP — 自由运行计时器时间戳

该16位字段为自由运行计时器的复制,当标识符字段开头出现在CAN总线上时进行捕获。

PRIO — 本地优先级

该 3 位字段只有当MCR.LPRIO_EN被置位时才有效,且只针对传输邮箱。用于附加到 ID 来定义传输优先级,不会被传输。

ID — 帧标识符

标准帧格式,只有高 11 位(28 ~ 18)用于识别接收或发送帧,忽略低 18 位。扩展帧格式,所有位都用于识别传输或接收帧。

DATA BYTE0 ~ 63 — 数据字段

数据帧最多可以使用64个字节,取决于为MB选择的有效负载大小。从总线上接收到的帧以该帧被接收时的格式进行存放。只有n小于DLC时,DATA BYTE(n)才有效。

7.png

5► MB 内存映射

FlexCAN内存缓冲区的内存映射如下表所示:

8.png

6►FlexCAN-FD API

从灵动官网下载的FLEXCAN固件库中定义了FD相关的API函数如下:

9.jpg

7►FlexCAN-FD通信

配置FlexCAN为CAN FD模式,通过中断接收和发送报文。

7.1  FlexCAN配置

void FlexCAN_Configure(void)
{
    GPIO_InitTypeDef        GPIO_InitStruct;
    NVIC_InitTypeDef        NVIC_InitStruct;

    flexcan_config_t        FlexCAN_ConfigStruct;
    flexcan_rx_mb_config_t  FlexCAN_RxMB_ConfigStruct;

    RCC_ClocksTypeDef RCC_Clocks;

    RCC_GetClocksFreq(&RCC_Clocks);

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOB, ENABLE);

    RCC_APB1PeriphClockCmd(RCC_APB1ENR_FLEXCAN, ENABLE);

    GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_3);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_3);

    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_8;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_FLOATING;
    GPIO_Init(GPIOB, &GPIO_InitStruct);

    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_9;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOB, &GPIO_InitStruct);

    NVIC_InitStruct.NVIC_IRQChannel = FLEX_CAN_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);

    FLEXCAN_GetDefaultConfig(&FlexCAN_ConfigStruct);

    FlexCAN_ConfigStruct.baudRate             = 1000000U; /* 1Mbps */
    FlexCAN_ConfigStruct.baudRateFD           = 2000000U; /* 2Mbps */
    FlexCAN_ConfigStruct.clkSrc               = Enum_Flexcan_ClkSrc1;
    FlexCAN_ConfigStruct.enableLoopBack       = false;
    FlexCAN_ConfigStruct.disableSelfReception = true;
    FlexCAN_ConfigStruct.enableIndividMask    = true;
    FLEXCAN_Init(FLEX_CAN1, &FlexCAN_ConfigStruct);

    FLEXCAN_EnterFreezeMode(FLEX_CAN1);

    FLEX_CAN1->MCR   |= 1 << 13;
    FLEX_CAN1->CTRL1 &= (~CAN_CTRL1_SMP(1));

    FLEXCAN_ExitFreezeMode(FLEX_CAN1);

    /* Baudrate calculate by automatically */
    FLEXCAN_FDCalculateImprovedTimingValues(FlexCAN_ConfigStruct.baudRate, FlexCAN_ConfigStruct.baudRateFD,
                                            RCC_Clocks.PCLK1_Frequency, &FlexCAN_ConfigStruct.timingConfig);

    FLEXCAN_FDInit(FLEX_CAN1, &FlexCAN_ConfigStruct, FLEXCAN_64BperMB, true);

    FLEXCAN_SetFDTxMbConfig(FLEX_CAN1, 1, true);
    FLEXCAN_SetFDTxMbConfig(FLEX_CAN1, 3, true);

    FlexCAN_RxMB_ConfigStruct.id     = FLEXCAN_ID_STD(0x111);
    FlexCAN_RxMB_ConfigStruct.format = Enum_Flexcan_FrameFormatStandard;
    FlexCAN_RxMB_ConfigStruct.type   = Enum_Flexcan_FrameTypeData;
    FLEXCAN_SetFDRxMbConfig(FLEX_CAN1, 0, &FlexCAN_RxMB_ConfigStruct, true);

    FLEXCAN_SetRxIndividualMask(FLEX_CAN1, 0, FLEXCAN_RX_MB_STD_MASK(0xFFF, 0, 0));

    /* Enable MB0 Interrupt */
    FLEX_CAN1->IMASK1 |= (0x01U << 0);

    FlexCAN_RxMB_ConfigStruct.id     = FLEXCAN_ID_EXT(0x222);
    FlexCAN_RxMB_ConfigStruct.format = Enum_Flexcan_FrameFormatExtend;
    FlexCAN_RxMB_ConfigStruct.type   = Enum_Flexcan_FrameTypeData;
    FLEXCAN_SetFDRxMbConfig(FLEX_CAN1, 2, &FlexCAN_RxMB_ConfigStruct, true);

    FLEXCAN_SetRxIndividualMask(FLEX_CAN1, 2, FLEXCAN_RX_MB_EXT_MASK(0xFFF, 0, 1));

    /* Enable MB2 Interrupt */
    FLEX_CAN1->IMASK1 |= (0x01U << 2);
}

配置PB8、PB9复用为FlexCAN的RX、TX引脚;

配置CAN 1Mbps和CAN FD 2Mbps、MB选择64字节负载;

配置NVIC中断;

配置MB1、MB3为发送邮箱;

配置MB0、MB2为接收邮箱;

MB0仅接收ID为0x111的标准帧;

MB2仅接收ID为0x222的扩展帧。

7.2  发送标准帧报文

void FlexCAN_FD_SendStandardFrameMessage(uint32_t ID, uint8_t *Buffer, uint8_t Length)
{
    flexcan_fd_frame_t FlexCAN_FD_FrameStruct;

    FlexCAN_FD_FrameStruct.length = Length;
    FlexCAN_FD_FrameStruct.type   = (uint8_t)Enum_Flexcan_FrameTypeData;
    FlexCAN_FD_FrameStruct.format = (uint8_t)Enum_Flexcan_FrameFormatStandard;
    FlexCAN_FD_FrameStruct.brs    = 1;
    FlexCAN_FD_FrameStruct.edl    = 1;
    FlexCAN_FD_FrameStruct.id     = ID;

    for (uint8_t i = 0; i < 16; i++)
    {
        FlexCAN_FD_FrameStruct.dataWord[i] = Buffer[i * 4] << 24 | Buffer[i * 4 + 1] << 16 | Buffer[i * 4 + 2] << 8 | Buffer[i * 4 + 3];
    }

    FLEXCAN_WriteFDTxMb(FLEX_CAN1, 1, &FlexCAN_FD_FrameStruct);
}

flexcan_fd_frame_t是按照FlexCAN MB结构定义的结构体,将要发送的标准帧按照帧结构依次设置结构体的各字段,接着写入MB1发送邮箱。

7.3  发送扩展帧报文

void FlexCAN_FD_SendExtendFrameMessage(uint32_t ID, uint8_t *Buffer, uint8_t Length)
{
    flexcan_fd_frame_t FlexCAN_FD_FrameStruct;

    FlexCAN_FD_FrameStruct.length = Length;
    FlexCAN_FD_FrameStruct.type   = (uint8_t)Enum_Flexcan_FrameTypeData;
    FlexCAN_FD_FrameStruct.format = (uint8_t)Enum_Flexcan_FrameFormatExtend;
    FlexCAN_FD_FrameStruct.brs    = 1;
    FlexCAN_FD_FrameStruct.edl    = 1;
    FlexCAN_FD_FrameStruct.id     = ID;

    for (uint8_t i = 0; i < 16; i++)
    {
        FlexCAN_FD_FrameStruct.dataWord[i] = Buffer[i * 4] << 24 | Buffer[i * 4 + 1] << 16 | Buffer[i * 4 + 2] << 8 | Buffer[i * 4 + 3];
    }

    FLEXCAN_WriteFDTxMb(FLEX_CAN1, 3, &FlexCAN_FD_FrameStruct);
}


同上,将要发送的扩展帧按照帧结构依次设置结构体的各字段,接着写入MB3发送邮箱。

7.4  获取报文并发送

void FlexCAN_FD_RxMB_Handler(uint8_t Index)
{
    uint8_t Buffer[64];
    flexcan_fd_frame_t FlexCAN_FD_FrameStruct;

    FLEXCAN_ReadFDRxMb(FLEX_CAN1, Index, &FlexCAN_FD_FrameStruct);

    for(uint8_t i = 0; i < 16; i++)
    {        
        Buffer[i*4+0] = (FlexCAN_FD_FrameStruct.dataWord[i] >> 0x18) & 0xFF;
        Buffer[i*4+1] = (FlexCAN_FD_FrameStruct.dataWord[i] >> 0x10) & 0xFF;
        Buffer[i*4+2] = (FlexCAN_FD_FrameStruct.dataWord[i] >> 0x08) & 0xFF;
        Buffer[i*4+3] = (FlexCAN_FD_FrameStruct.dataWord[i] >> 0x00) & 0xFF;
    }

    if (Index == 0)
    {
        FlexCAN_FD_SendStandardFrameMessage((FlexCAN_FD_FrameStruct.id >> CAN_ID_STD_SHIFT), Buffer, FlexCANFD_TX_64Bytes_DataLen);
    }
    else
    {
        FlexCAN_FD_SendExtendFrameMessage((FlexCAN_FD_FrameStruct.id >> CAN_ID_EXT_SHIFT), Buffer, FlexCANFD_TX_64Bytes_DataLen);
    }
}

读接收邮箱(Index),获取CAN FD报文中的数据,并发送该报文。

7.5  中断服务子程序

void FlexCAN_IRQHandler(void)
{
    uint32_t u32flag = 1;

    /* MB0 */
    if (FLEXCAN_GetMbStatusFlags(FLEX_CAN1, u32flag << 0) != 0)
    {
        FlexCAN_FD_RxMB_Handler(0);

        FLEXCAN_ClearMbStatusFlags(FLEX_CAN1, u32flag << 0);
    }

    /* MB1 */
    if (FLEXCAN_GetMbStatusFlags(FLEX_CAN1, u32flag << 1) != 0)
    {
        FLEXCAN_ClearMbStatusFlags(FLEX_CAN1, u32flag << 1);
    }

    /* MB2 */
    if (FLEXCAN_GetMbStatusFlags(FLEX_CAN1, u32flag << 2) != 0)
    {
        FlexCAN_FD_RxMB_Handler(2);

        FLEXCAN_ClearMbStatusFlags(FLEX_CAN1, u32flag << 2);
    }

    /* MB3 */
    if (FLEXCAN_GetMbStatusFlags(FLEX_CAN1, u32flag << 3) != 0)
    {
        FLEXCAN_ClearMbStatusFlags(FLEX_CAN1, u32flag << 3);
    }
}

MB0、MB2完成接收调用MB接收函数,获取报文并通过MB1、MB3发送。MB1、MB3完成传输,清除对应标志。

7.6  FlexCAN_FD中断示例

void FlexCAN_FD_Interrupt_Sample(void)
{
    uint8_t Buffer[64] =
    {
        0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0xAA,
        0xAA,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x55,
        0x55,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0xAA,
        0xAA,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x88,
    };

    printf("\r\nTest %s", __FUNCTION__);

    FlexCAN_Configure();

    while (1)
    {
        FlexCAN_FD_SendStandardFrameMessage(0x214, Buffer, FlexCANFD_TX_64Bytes_DataLen);

        PLATFORM_LED_Toggle(LED1);
        PLATFORM_DelayMS(1000);
    }
}

调用FlexCAN_Configure(),在while中间隔1s中发送标准帧报文,帧ID为0x214,数据为定义好的Buffer[64]。

在主函数中调用FlexCAN_FD_Interrupt_Sample()。

8►验证

连接CAN调试工具,配置波特率CAN 1Mbps、CAN FD 2Mbps,观测上位机软件:

9.png

接收区间隔1s接收到一次FD报文,ID为0x214。

在发送区发送标准帧FD报文,ID为0x111,发送扩展帧FD报文,ID为0x222,各发送5次:

11.png

每发送1次报文,接收区接收到1次该ID的报文,和程序预期一致。

来源:灵动MM32MCU

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 24

1►FlexCAN简介

FlexCAN模块是一个通信控制器,扩展了CAN FD功能,遵循ISO 11898-1标准、CAN FD和CAN 2.0B协议规范。 CAN协议主要被设计用作车载串行总线,满足实时处理,带宽要求,车辆在电磁干扰环境下的可靠操作,该模块支持标准和扩展帧,支持最大64字节有效负载,传输速率高达 8Mbps,并且具有非常灵活的用于传输和接收的邮箱系统和RxFIFO接收机制。邮箱系统由 32 个报文缓冲区(MB)组成。

2►FlexCAN的功能框图

FlexCAN 的功能框图如下图1所示,包括用于存储报文缓冲区(MB)、接收全局掩码寄存器、接收私有掩码寄存器、接收(Rx) FIFO 过滤器以及接收 FIFO 标识符过滤器的内存。

1.png

图1 FlexCAN功能框图

如上图1所示为FlexCAN的功能框图,协议引擎(PE)子模块管理 CAN 总线上的串行通信:

  • 请求存取 RAM 接收和传输帧

  • 验收接收到的报文

  • 进行错误处理

  • 检测 CAN FD 报文

控制器主机接口(CHI)子模块负责选择接收和传输的报文缓冲区,以及对报文的仲裁和 ID 匹配算法。

总线接口单元(BIU)子模块控制内部接口总线的访问,建立与 CPU 和其他模块的连接。时钟、地址和数据总线、中断输出、 DMA 都通过 BIU 进行访问。

3►FlexCAN的时钟

如下图2所示为产生 PE 时钟的电路结构。时钟源选择位(CAN_CTRL1.CLKSRC)定义了内部时钟为异步时钟或同步时钟。其中,同步时钟为外设时钟(由APB1总线时钟提供);异步时钟的时钟源可选(细节请参考 RCC 章节 RCC_CFGR2寄存器)。为保证可靠运行,应在模块禁止模式时(CAN_MCR.MDIS 置位)选择时钟源。

2.png

图2 FlexCAN的PE时钟结构

4►FlexCAN的协议时序

FlexCAN支持多种方式来设置 CAN 协议所要求的位时序参数。控制寄存器 1(CAN_CTRL1)有各种用于控制位时序参数的字段: PRESDIV、 PROPSEG、 PSEG1、 PSEG2 和 RJW。 CAN 位时序寄存器(CAN_CBT)扩展了 CAN_CTRL1 中 CAN 位时序变量的范围。 CAN_FDCBT 提供了用于 BRS 置位的 CAN FD 帧数据段的位时序变量。

CAN FD 使能时,应始终置位 CAN_CBT.BTF 或 CAN_CTRL2.BTE,并在 CAN_CBT 中配置CAN 位时序变量。

PRESDIV字段(及其扩展范围 EPRESDIV 和用于 CAN FD 报文数据段的 FDPRESDIV)定义了

串行时钟(Sclock)的预分频(见下列方程)。串行时钟的周期定义了用于构成 CAN 波形的时间单位 Tq(Time Quantum)。 Tq 为 CAN 引擎所能处理的最小时间单元。

3.png

比特率定义了接收或传输 CAN 报文的速率,公式如下:

4.png

FlexCAN的位时间可细分为三个部分:

  • 同步段(SYNC_SEG)

    1Tq 的固定长度;信号边沿出现在该段内。

  • 时间段 1

    包括 CAN 标准的传播段和相位段1。该段可通过设置 CAN_CTRL1 寄存器的PROPSEG和PSEG1 字段来编程,其总和(+2)为 2 ~ 16Tq。当 CAN_CBT.BTF 被置位时,FlexCAN 使用来自 CAN_CBT 寄存器的EPROPSEG和EPSEG1 字段,其总和(+2)为 2~ 96Tq 。对于BRS置位的CAN FD报文,FlexCAN使用CAN_FDCBT寄存器的FDPROPSEG 和 FDPSEG1 字段,其总和为 2 ~ 39Tq。

  • 时间段 2

    CAN 标准的相位段2。该段可通过设置CAN_CTRL1寄存器的PSEG2字段来编程,其值(+1)为 2 ~ 8Tq。当CAN_CBT.BTF被置位时, FlexCAN使用来自CAN_CBT寄存器的EPSEG2字段,其值(+1)为 2 ~ 32Tq。对于BRS置位的 CAN FD 报文, FlexCAN使用CAN_FDCBT寄存器的FDPSEG2字段,其值(+1)为 2 ~ 8Tq。时间段2不能小于信息处理时间(IPT),IPT 在 FlexCAN 中为 2Tq。

  • 注意事项:

    FPRESDIV 定义了 BRS 置位的 CAN FD 帧数据比特率部分的PE时钟频率和串行时钟(Sclock)频率之间的比率。 Sclock 周期定义了CAN FD协议数据比特率的 Tq。Sclock 频率 = PE 时钟频率 /(FPRESDIV + 1)注:为避免处理FD帧时出错, FPRESDIV 和PRESDIV(CAN_CBT 或 CAN_CTRL1)请使用相同的值。FPRESDIV 只能在冻结模式下写入,其他模式下被硬件锁定。

如下图3所示为FlexCAN位时间内的段使用 CAN_CTRL1位时序变量的经典 CAN 格式。

5.png

图3

如下图4所示为FlexCAN FD位时间内的段,使用CAN FD格式的CAN_CBT和CAN_FDCBT位时序变量,其中FlexCAN FD的仲裁段使用经典CAN格式配置位时间,即使用CAN_CBT位时序变量用于配置仲裁段的位时间,FlexCAN FD的可变速率的位时间使用CAN_FDCBT位时序变量配置。

6.png

图4

FlexCAN的语法说明如下表1所示:

7.png

表1

当采用CAN位作为持续时间的衡量标准时(例如,评估报文中的CAN位事件),一个 CAN 位的外设时钟个数(NumClkBit)为:

8.png

其中:

  • NumClkBit 为一个 CAN 位的外设时钟个数

  • fSYS 为系统(CHI)时钟频率,单位 Hz

  • PSEG1 为 CAN_CTRL1.PSEG1 的值

  • PSEG2 为 CAN_CTRL1.PSEG2 的值

  • PROPSEG 为 CAN_CTRL1.PROPSEG 的值

  • PRESDIV 为 CAN_CTRL1.PRESDIV 的值

上述公式也适用于 CAN 位时序寄存器(CAN_CBT)所述的 CAN 位时间变量。

因此,FlexCAN FD仲裁段使用经典CAN位的速率计算公式为:

9.png

FlexCAN FD可变速率数据段的计算公式为:

10.png

  • fCANCLK为PE时钟,单位Hz

  • BITRATEN是由CAN 标称位时间变量计算出的 CAN 位速率,单位bps

  • BITRATEF是由CAN 数据位时间变量计算出的CAN位速率,单位bps

  • EPSEG1为CAN_CBT.EPSEG1的值(也可使用 CAN_CTRL1.PSEG1)

  • EPSEG2 为 CAN_CBT.EPSEG2 的值(也可使用 CAN_CTRL1.PSEG2)

  • EPROPSEG 为CAN_CBT.EPROPSEG 的值(也可使用 CAN_CTRL1.PROPSEG)

  • EPRESDIV 为CAN_CBT.EPRESDIV 的值(也可使用 CAN_CTRL1.PRESDIV)

那么CAN FD帧标称比特率相应的每个CAN位的外设时钟数量CPCBN为:

11.png

CAN FD 帧数据比特率相应的每个CAN位的外设时钟数量CPCBF为:

12.png

因此在已知FlexCAN FD时钟和预分频系数以及波特率的情况下可以计算出FlexCAN FD的标称位时间TqN总数和可变速率数据位时间TqF总数,计算公式分别如下所示:

fcanclk / BITRATEN x (EPRESDIV+1) = [1+(EPSEG1+1)+(EPSEG2+1)+(EPROPSEG+1)]

fcanclk / BITRATEF x (FPRESDIV+1) = [1+(FPSEG1+1)+(FPSEG2+1)+FPROPSEG]

根据以上FlexCAN FD的位时序可知可以分别根据标称位时间TqN总数和可变速率数据位时间TqF总数分别计算出其标称位时间的采样点和可变速率数据位时间的采样点,即FlexCAN FD标称位时间的采样点的计算公式为:

FlexCAN_samplepoint = (SYNC_SEG+(EPROPSEG+1) +(EPSEG1+1) )/ (SYNC_SEG+(EPROPSEG+1) +(EPSEG1+1)+ (EPSEG2+1))

FlexCAN FD可变速率位时间的采样点的计算公式为:

FlexCAN_FDsamplepoint = (SYNC_SEG+FPROPSEG+(FPSEG1+1) )/ (SYNC_SEG+FPROPSEG+(FPSEG1+1)+ (FPSEG2+1))

5►FlexCAN FD位时间采样点的计算

根据以上4章节FlexCAN的位时间描述以及FlexCAN FD位时间和采样点的计算公式推导,在给定FlexCAN FD时钟和波特率以及分频系数的情况下,可以使用FlexCAN的库函数通过循环遍历的方式实现自动计算FlexCAN FD的位时间的采样点。

FlexCAN FD库函数定义的协议时序代码如下所示:

typedef struct _flexcan_timing_config {
    u16 preDivider;                  /*!< Clock Pre-scaler Division Factor. */
    u8 rJumpwidth;                   /*!< Re-sync Jump Width. */
    u8 phaseSeg1;                    /*!< Phase Segment 1. */
    u8 phaseSeg2;                    /*!< Phase Segment 2. */
    u8 propSeg;                      /*!< Propagation Segment. */
    /*!< FlexCAN has flexible data rate. */
    uint16_t fpreDivider;          /*!< Fast Clock Pre-scaler Division Factor. */
    uint8_t frJumpwidth;            /*!< Fast Re-sync Jump Width. */
    uint8_t fphaseSeg1;             /*!< Fast Phase Segment 1. */
    uint8_t fphaseSeg2;             /*!< Fast Phase Segment 2. */
    uint8_t fpropSeg;               /*!< Fast Propagation Segment. */
} flexcan_timing_config_t;*/


FlexCAN FD标称位时间采样点和可变速率采样点的位时间的计算,可以通过循环遍历方式实现自动计算,代码如下所示:

bool FLEXCAN_FDCalculateImprovedTimingValues(uint32_t baudRate, uint32_t baudRateFD, uint32_t sourceClock_Hz, flexcan_timing_config_t *pTimingConfig)
{
    bool fgRet = false;
    pTimingConfig->preDivider = 0U;
    pTimingConfig->fpreDivider = 0U;
    if(FLEXCAN_CalculateImprovedTimingValuesByFDCBT(baudRateFD, sourceClock_Hz, pTimingConfig))
    {                                                                                          
        if(FLEXCAN_CalculateImprovedTimingValuesByCBT(baudRate, sourceClock_Hz, pTimingConfig))
        {
            fgRet = true;
        }
    }

    return (fgRet);
}        

static bool FLEXCAN_CalculateImprovedTimingValuesByFDCBT(uint32_t baudRate, uint32_t sourceClock_Hz, flexcan_timing_config_t *pTimingConfig)
{
    uint32_t clk;                      /* the clock is tqNumb x baudRateFD. */
    uint32_t tqNum;                    /* Numbers of TQ. */
    bool fgRet = false;
    tqNum = FDCBT_MAX_TIME_QUANTA;
     /*  Auto Improved Protocal timing. */
    do
    {
        clk = baudRate * tqNum;

        if (clk > sourceClock_Hz)
        {
            continue;                  /* tqNum too large, clk has been exceed sourceClock_Hz. */
        }
        if ((sourceClock_Hz / clk * clk) != sourceClock_Hz)
        {
            continue;                  /* Non-supporting: the frequency of clock source is not divisible by target baud rate, the user
                                          should change a divisible baud rate. */
        }
         /* Make sure the new calculated divider value is greater than the previous one. */
        if (pTimingConfig->fpreDivider > ((uint16_t)(sourceClock_Hz / clk) - 1U))
        {
            continue;
        }
        else
        {
            pTimingConfig->fpreDivider = (uint16_t)(sourceClock_Hz / clk) - 1U;
        }
        if (pTimingConfig->fpreDivider > MAX_FPRESDIV)
        {
            break;                     /* The frequency of source clock is too large or the baud rate is too small, the pre-divider could
                                          not handle it. */
        }
         /* Try to get the best timing configuration. */
        if (FLEXCAN_FDGetSegmentswithBRS(baudRate, tqNum, pTimingConfig))
        {
            fgRet = true;
            break;
        }
    }
    while(--tqNum >= FDCBT_MIN_TIME_QUANTA);

    return (fgRet);
}

static bool FLEXCAN_FDGetSegmentswithBRS(uint32_t baudRatebrs, uint32_t tqNum, flexcan_timing_config_t *pTimingConfig)
{
    uint32_t ideal_sp;
    uint32_t p1;
    bool fgRet = false;

     /* get ideal sample point. */
    if (baudRatebrs >= 1000000U)  
    {
        ideal_sp = IDEAL_SP_LOW;
    }
    else if (baudRatebrs >= 800000U)
    {
        ideal_sp = IDEAL_SP_MID;
    }
    else
    {
        ideal_sp = IDEAL_SP_HIGH;
    }

     /* distribute time quanta. */
    p1 = tqNum * (uint32_t)ideal_sp;
    pTimingConfig->fpropSeg = (uint8_t)(p1 / (uint32_t)IDEAL_SP_FACTOR - 2U);

    if (pTimingConfig->fpropSeg <= (MAX_FPSEG1 + MAX_FPROPSEG))
    {
        if (pTimingConfig->fpropSeg > MAX_FPROPSEG)
        {
            pTimingConfig->fphaseSeg1 = pTimingConfig->fpropSeg - MAX_FPROPSEG;
            pTimingConfig->fpropSeg   = MAX_FPROPSEG;
        }
        else
        {
            pTimingConfig->fphaseSeg1 = 0;
        }

        if(pTimingConfig->fphaseSeg1 <= MAX_PSEG1)
        {
            if ((pTimingConfig->fpropSeg + pTimingConfig->fphaseSeg1) < ((uint8_t)tqNum - 3U))
            {
                pTimingConfig->fphaseSeg2 = (uint8_t)tqNum - (pTimingConfig->fphaseSeg1 + pTimingConfig->fpropSeg + 3U);

                if (pTimingConfig->fphaseSeg2 <= MAX_PSEG2)
                {
                    if ((pTimingConfig->fphaseSeg1 < pTimingConfig->fphaseSeg2) && (pTimingConfig->fpropSeg > (pTimingConfig->fphaseSeg2 - pTimingConfig->fphaseSeg1)))
                    {
                        pTimingConfig->fpropSeg  -= (pTimingConfig->fphaseSeg2 - pTimingConfig->fphaseSeg1);
                        pTimingConfig->fphaseSeg1 = pTimingConfig->fphaseSeg2;
                    }

                     /* subtract one TQ for sync seg. */
                     /* sjw is 20% of total TQ, rounded to nearest int. */
                    pTimingConfig->frJumpwidth = ((uint8_t)tqNum + 4U) / 5U - 1U;

                    if (pTimingConfig->frJumpwidth > MAX_FRJW)
                    {
                        pTimingConfig->frJumpwidth = MAX_FRJW;
                    }

                    fgRet = true;
                }          
            }
        }       
    }

    return (fgRet);
}

static bool FLEXCAN_CalculateImprovedTimingValuesByCBT(uint32_t baudRate, uint32_t sourceClock_Hz, flexcan_timing_config_t *pTimingConfig)
{
    uint32_t clk;                      /* the clock is tqNumb x baudRateFD. */
    uint32_t tqNum;                    /* Numbers of TQ. */
    bool fgRet = false;

    tqNum = CBT_MAX_TIME_QUANTA;

     /*  Auto Improved Protocal timing. */
    do
    {
        clk = baudRate * tqNum;

        if (clk > sourceClock_Hz)
        {
            continue;                  /* tqNum too large, clk has been exceed sourceClock_Hz. */
        }

        if ((sourceClock_Hz / clk * clk) != sourceClock_Hz)
        {
            continue;                  /* Non-supporting: the frequency of clock source is not divisible by target baud rate, the user
                                          should change a divisible baud rate. */
        }

         /* Make sure the new calculated divider value is greater than the previous one. */
        if (pTimingConfig->preDivider > ((uint16_t)(sourceClock_Hz / clk) - 1U))
        {
            continue;
        }
        else
        {
            pTimingConfig->preDivider = (uint16_t)(sourceClock_Hz / clk) - 1U;
        }

        /* To minimize errors when processing FD frames, try to calculate the same value for FPRESDIV and PRESDIV (in CBT). */
        if (pTimingConfig->preDivider != pTimingConfig->fpreDivider)
        {
            continue;
        }

        if (pTimingConfig->preDivider > MAX_EPRESDIV)
        {
            break;                     /* The frequency of source clock is too large or the baud rate is too small, the pre-divider could
                                          not handle it. */
        }

         /* Try to get the best timing configuration. */
        if (FLEXCAN_FDGetSegments(baudRate, tqNum, pTimingConfig))
        {
            fgRet = true;
            break;
        }
    }
    while(--tqNum >= CBT_MIN_TIME_QUANTA);

    return (fgRet);
}

FlexCAN FD位时间采样点应用举例,本文示例以MM32F0160的FlexCAN FD外设为例,其中FlexCAN FD的标称波特率为500K,可变速率波特率为2MHz。fCAN的时钟为72MHz,把FlexCAN FD的fCAN时钟,标称波特率500K,可变速率波特率2MHz代入库函数FLEXCAN_FDCalculateImprovedTimingValues分别计算得出FlexCAN FD标称波特率和可变速率波特率的位时间参数如下:

标称波特率的位时间参数:

preDivider = 0x01;

propSeg = 0x34;

phaseSeg1 = 0x08;

phaseSeg2 = 0x08;

rJumpwidth = 0x0E

可变速率波特率位时间参数:

fpreDivider = 0x01;

fpropSeg = 0x07;

fphaseSeg1 = 0x04;

fphaseSeg2 = 0x04;

frJumpwidth = 0x03

注:再同步参数rJumpwidth 和frJumpwidth不参与FlexCANFD采样点的计算

又因为在已知FlexCAN FD时钟和预分频系数,以及波特率的情况下可以计算出FlexCAN FD的标称位时间TqN总数和可变速率数据位时间TqF总数,计算公式分别如下所示:

标称位时间TqN总数:

fcanclk / BITRATEN x (EPRESDIV+1) = [1+(EPSEG1+1)+(EPSEG2+1)+(EPROPSEG+1)]

代入自动计算得出的标称波特率500K的位时间参数,计算出TqN:

TqN = 72000000/500000x(1+1) = 72

可变速率数据位时间TqF总数:

fcanclk / BITRATEF x (FPRESDIV+1) = [1+(FPSEG1+1)+(FPSEG2+1)+FPROPSEG]

代入自动计算得出的可变波特率2MHz的位时间参数,计算出TqF:

TqF = 72000000/2000000x(1+1) = 18

根据以上FlexCAN FD使用库自动计算出的位时间参数,以及标称位时间TqN总数和可变速率位时间TqF总数,可以分别计算出其标称位时间的采样点和可变速率数据位时间的采样点,即FlexCAN FD标称位时间的采样点的计算公式为:

FlexCAN_samplepoint = (SYNC_SEG+(EPROPSEG+1) +(EPSEG1+1) )/ (SYNC_SEG+(EPROPSEG+1) +(EPSEG1+1)+ (EPSEG2+1))

代入标称波特率500K的位时间参数计算标称波特率的位时间的采样点如下:

FlexCAN_samplepoint =(1+(52+1)+(8+1))/(1+(52+1)+(8+1)+(8+1))= 87.5%

FlexCAN FD可变速率位时间的采样点的计算公式为:

FlexCAN_FDsamplepoint = (SYNC_SEG+FPROPSEG+(FPSEG1+1) )/ (SYNC_SEG+FPROPSEG+(FPSEG1+1)+ (FPSEG2+1))

代入可变速率2MHz波特率的位时间参数计算可变速率的位时间的采样点如下:

FlexCAN_FDsamplepoint = (1+7+(4+1))/(1+7+(4+1)+(4+1))= 72%

CAN采样点一般设置在75%—80%之间,具体要根据CAN通信波特率大小配置。当波特率不超过500K时,建议采样点设置在87.5%;当波特率大小在500K—800K之间的时候,建议采样点设置在80%;当波特率大于800K的时候,建议采样点设置在75%。以上建议并不是绝对的,需根据应用环境复杂程度通过调整FlexCAN FD的再同步参数rJumpwidth和frJumpwidth使得phaseSeg1和phaseSeg2以及fphaseSeg1和fphaseSeg2在标称波特率和可变波特率的位时间上前移或后移N(0-15)个Tq值来微调采样点(虽然rJumpwidth和frJumpwidth不参与采样点计算)。

注意事项:

关于采样点的配置的一些额外建议:当用户在实际车用环境中出现FlexCAN无正常收发数据时除了根据错误标志查找问题定位问题外,还可使用专业的CAN诊断仪测试出多节点总线上的rJumpwidth和frJumpwidth,phaseSeg1和phaseSeg2以及fphaseSeg1和fphaseSeg2参数,参考这些位时间参数值来调整适配rJumpwidth和frJumpwidth,phaseSeg1和phaseSeg2以及fphaseSeg1和fphaseSeg2参数到CAN通信的合适的采样点

来源:灵动MM32MCU

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 10

引言

最近,为了配合新品推广,灵动的软件与系统工程SE团队,基于带有 USB 外设模块的 MM32F0160 微控制器,实现了一套低成本带RGB灯效的机械键盘。这里将设计机械键盘的全过程开源出来,感兴趣的开发者,可以向灵动申请 MM32F0160 微控制器的样片,复刻一把自己专属的机械键盘,也可以继续进行二次开发,实现更加有趣的作品。

一些机械设计工作

首先需要选择将要设计机械键盘的配列,也就是按键的布局。这里选用了对程序员最友好的87键配列。使用“键盘布局编辑器”(http://www.keyboard-layout-editor.com/)可以导入标准的配列布局。如图1所示。

1.png

图1 keyboard-layer-editor在线编辑器

这里可以选择标准104键配列,再删除右侧小键盘的区域,得到标准87键配列的方案,如图2所示。

2.png

图2 使用keyboard-layer-editor设计标准87键配列

然后,复制 Raw data 中的代码,到“定位板在线编辑器”(http://builder.swillkb.com/)中,如图3所示。

3.png

图3 定位板在线编辑器

此时,点击页面最下方的 Draw My CAD!!! 按钮,生成定位板的图纸。如图4所示。

4.png

图4 生成87键配列的定位板

此时,在页面的右上角选择下载 DXF 格式的文件。这个文件可以发送给工厂,用于生产机械键盘的定位板。板子的厚度为1.5mm,在本案中使用了PC材料。

设计PCB

机械键盘的按键部分电路,就是普通的键盘矩阵,但为了避免出现“鬼影按键”(Ghost Key)和实现“全键无冲”,每个按键开关上都需要串联二极管。为了在每个按键下放置一颗RGB彩灯实现灯效,本案使用了 WS2812 级联的电路。

本案使用KiCAD 7.0设计了整个机械键盘电路系统的原理图。

按键矩阵部分的电路如图5所示。

5.png

图5 按键矩阵原理图

RGB彩灯矩阵部分的电路,如图6所示。

6.png

图6 RGB彩灯矩阵原理图

使用MM32F0160微控制器作为核心的控制电路,如图7所示。

7.png

图7 基于MM32F0160的控制电路原理图

这里还引出了扩展插座,包含了 MM32F0160 微控制器的调试信号、按键矩阵行列信号、RGB灯控制信号,可用于扩展更大的键盘配列(例如增加数字小键盘)。

然后将原理图配合合适的封装(可以在KiCAD的元件库中找到Cherry轴的封装),导出生成PCB文件。在PCB文件中,可以导入之前用于生产定位板的 DXF 文件,参照着定位板的位置放置按键和RGB灯。最终完成走线的PCB板效果图,如图8所示。

8.png9.png

图8 走线完成的机械键盘电路板

设计软件

本案使用的软件来自于灵动的软件与系统工程SE团队开发和维护的MindSDK软件开发平台,可以选择 Mini-F0160 开发板的软件包作为模板,下载已经适配TinyUSB协议栈的 tud_hid_keyboard 样例工程。如图9所示。

10.png

图9 MindSDK中的tud_hid_keyboard样例工程

之后再进行改装,适配使用 GPIO 扫描按键矩阵,并增加 DMA+SPI 的方式控制 WS2812 灯带的功能(可参考MindSDK中的 ws2812_spi_dma 样例工程)。

特别注意,本案使用Keil MDK编译生成可执行文件,除了需要安装Keil MDK集成开发环境外,还需要开发者从灵动官网下载 MM32F0160 的设备支持包(https://www.mindmotion.com.cn/support/software/keil_pack/),以及需要准备一个适配Arm核微控制器的调试器,例如JLink或者DAPLink等。

技术资料

这里开源基于MM32F0160微控制器的机械键盘项目的所有资料,托管在Gitee的平台上(https://gitee.com/suyong_yq/mechanical-keyboard-diy),如图10所示。

11.png

图10 托管在Gitee上的机械键盘开源项目

最后,放一张第一个成品样品的照片,如图11所示。

12.gif

图11 基于MM32F0160微控制器的机械键盘成品

来源:灵动MM32MCU

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 133

1.jpg

灵动股份发布全新MM32F0160 系列 MCU,其搭载 72MHz Arm® Cortex-M0 处理器,内置 128KB Flash,16KB RAM,配备高速 ADC、USB、CAN-FD、I3C 等丰富的模拟和通信资源,适用于工业物联网设备、PC外设、电子门锁、医疗和保健设备、个人手持设备、游戏娱乐等多种应用场景。目前MM32F0160系列已开放样片和开发板申请。

2.jpg

MM32F0160 系列主要特点

  • 72MHz Arm® Cortex-M0

  • 128KB Flash,16KB SRAM

  • 7 通道 DMA

  • 32 位除法加速单元

  • 1 个 12位 SAR ADC,14 个外部通道,高达 1MSPS 采样率

  • 2 个比较器

  • 1 个 16 位高级定时器、5 个 16 位或 32 位普通定时器、1 个低功耗定时器

  • 1 个 RTC 模块

  • 多达 5 个 UART (包括 1 路 LPUART)接口,2 个 SPI 或 I2S 接口,1 个 I2C 接口

  • 1 个 I3C 从机接口,高达 12.5 Mbps

  • 1 个 USB 设备接口,配备独立时钟,适用于小至 5x5 mm2 的封装

  • 1 个 FlexCAN-FD 接口,配备独立时钟,适用于小至 4x4 mm2 的封装

  • 工作电压为2.0V ∼ 5.5V

  • 支持环境温度 -40℃ ∼ +85℃的工业型和 -40℃ ∼ +105℃ 的扩展工业型(尾缀 V)

  • 提供 LQFP64(10x10 mm2)、LQFP48(7x7 mm2)、QFN32(5x5 mm2) 和 QFN28(4x4 mm2)封装

  • 引脚和软件兼容已量产的 MM32F0140 系列产品

MM32F0160系列选型信息以及样片状态表

3.png

4.png

同步发布的 Mini-F0160 开发板,板载 F0160系列Superset 芯片(MM32F0163D7P),现 MM32F0160 系列产品信息已上线灵动官网,更多详细信息,请访问:www.mm32mcu.com

来源:灵动MM32MCU

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 45
订阅 RSS - MM32F0160