谷动谷力

 找回密码
 立即注册
查看: 1356|回复: 0
打印 上一主题 下一主题
收起左侧

灵动微课堂 |MM32F0160 FlexCAN-FD 通信

[复制链接]
跳转到指定楼层
楼主
发表于 2023-12-14 18:58:14 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
灵动微课堂 |MM32F0160 FlexCAN-FD 通信


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帧结构(部分)如下图所示:
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引擎所能处理的最小时间单元。
比特率定义了接收或传输 CAN FD报文的速率,公式如下:
位时间可以细分为三个部分:
同步段(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为:
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 的内存区域。
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)才有效。

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

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

7►
FlexCAN-FD通信
配置FlexCAN为CAN FD模式,通过中断接收和发送报文。
7.1  FlexCAN配置
  1. void FlexCAN_Configure(void)
  2. {

  3.     GPIO_InitTypeDef        GPIO_InitStruct;
  4.     NVIC_InitTypeDef        NVIC_InitStruct;

  5.     flexcan_config_t        FlexCAN_ConfigStruct;
  6.     flexcan_rx_mb_config_t  FlexCAN_RxMB_ConfigStruct;

  7.     RCC_ClocksTypeDef RCC_Clocks;

  8.     RCC_GetClocksFreq(&RCC_Clocks);

  9.     RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOB, ENABLE);

  10.     RCC_APB1PeriphClockCmd(RCC_APB1ENR_FLEXCAN, ENABLE);

  11.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_3);
  12.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_3);

  13.     GPIO_StructInit(&GPIO_InitStruct);
  14.     GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_8;
  15.     GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  16.     GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_FLOATING;
  17.     GPIO_Init(GPIOB, &GPIO_InitStruct);

  18.     GPIO_StructInit(&GPIO_InitStruct);
  19.     GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_9;
  20.     GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  21.     GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AF_PP;
  22.     GPIO_Init(GPIOB, &GPIO_InitStruct);

  23.     NVIC_InitStruct.NVIC_IRQChannel = FLEX_CAN_IRQn;
  24.     NVIC_InitStruct.NVIC_IRQChannelPriority = 0;
  25.     NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
  26.     NVIC_Init(&NVIC_InitStruct);

  27.     FLEXCAN_GetDefaultConfig(&FlexCAN_ConfigStruct);

  28.     FlexCAN_ConfigStruct.baudRate             = 1000000U; /* 1Mbps */
  29.     FlexCAN_ConfigStruct.baudRateFD           = 2000000U; /* 2Mbps */
  30.     FlexCAN_ConfigStruct.clkSrc               = Enum_Flexcan_ClkSrc1;
  31.     FlexCAN_ConfigStruct.enableLoopBack       = false;
  32.     FlexCAN_ConfigStruct.disableSelfReception = true;
  33.     FlexCAN_ConfigStruct.enableIndividMask    = true;
  34.     FLEXCAN_Init(FLEX_CAN1, &FlexCAN_ConfigStruct);

  35.     FLEXCAN_EnterFreezeMode(FLEX_CAN1);

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

  38.     FLEXCAN_ExitFreezeMode(FLEX_CAN1);

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

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

  43.     FLEXCAN_SetFDTxMbConfig(FLEX_CAN1, 1, true);
  44.     FLEXCAN_SetFDTxMbConfig(FLEX_CAN1, 3, true);

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

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

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

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

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

  57.     /* Enable MB2 Interrupt */
  58.     FLEX_CAN1->IMASK1 |= (0x01U << 2);
  59. }
复制代码

配置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  发送标准帧报文
  1. void FlexCAN_FD_SendStandardFrameMessage(uint32_t ID, uint8_t *Buffer, uint8_t Length)
  2. {

  3.     flexcan_fd_frame_t FlexCAN_FD_FrameStruct;

  4.     FlexCAN_FD_FrameStruct.length = Length;
  5.     FlexCAN_FD_FrameStruct.type   = (uint8_t)Enum_Flexcan_FrameTypeData;
  6.     FlexCAN_FD_FrameStruct.format = (uint8_t)Enum_Flexcan_FrameFormatStandard;
  7.     FlexCAN_FD_FrameStruct.brs    = 1;
  8.     FlexCAN_FD_FrameStruct.edl    = 1;
  9.     FlexCAN_FD_FrameStruct.id     = ID;

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

  14.     FLEXCAN_WriteFDTxMb(FLEX_CAN1, 1, &FlexCAN_FD_FrameStruct);
  15. }
复制代码


flexcan_fd_frame_t是按照FlexCAN MB结构定义的结构体,将要发送的标准帧按照帧结构依次设置结构体的各字段,接着写入MB1发送邮箱。
7.3  发送扩展帧报文
  1. void FlexCAN_FD_SendExtendFrameMessage(uint32_t ID, uint8_t *Buffer, uint8_t Length)
  2. {

  3.     flexcan_fd_frame_t FlexCAN_FD_FrameStruct;

  4.     FlexCAN_FD_FrameStruct.length = Length;
  5.     FlexCAN_FD_FrameStruct.type   = (uint8_t)Enum_Flexcan_FrameTypeData;
  6.     FlexCAN_FD_FrameStruct.format = (uint8_t)Enum_Flexcan_FrameFormatExtend;
  7.     FlexCAN_FD_FrameStruct.brs    = 1;
  8.     FlexCAN_FD_FrameStruct.edl    = 1;
  9.     FlexCAN_FD_FrameStruct.id     = ID;

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

  14.     FLEXCAN_WriteFDTxMb(FLEX_CAN1, 3, &FlexCAN_FD_FrameStruct);
  15. }
复制代码


同上,将要发送的扩展帧按照帧结构依次设置结构体的各字段,接着写入MB3发送邮箱。
7.4  获取报文并发送
  1. void FlexCAN_FD_RxMB_Handler(uint8_t Index)
  2. {

  3.     uint8_t Buffer[64];
  4.     flexcan_fd_frame_t FlexCAN_FD_FrameStruct;

  5.     FLEXCAN_ReadFDRxMb(FLEX_CAN1, Index, &FlexCAN_FD_FrameStruct);

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

  13.     if (Index == 0)
  14.     {
  15.         FlexCAN_FD_SendStandardFrameMessage((FlexCAN_FD_FrameStruct.id >> CAN_ID_STD_SHIFT), Buffer, FlexCANFD_TX_64Bytes_DataLen);
  16.     }
  17.     else
  18.     {
  19.         FlexCAN_FD_SendExtendFrameMessage((FlexCAN_FD_FrameStruct.id >> CAN_ID_EXT_SHIFT), Buffer, FlexCANFD_TX_64Bytes_DataLen);
  20.     }
  21. }
复制代码

读接收邮箱(Index),获取CAN FD报文中的数据,并发送该报文。
7.5  中断服务子程序
  1. void FlexCAN_IRQHandler(void)
  2. {

  3.     uint32_t u32flag = 1;

  4.     /* MB0 */
  5.     if (FLEXCAN_GetMbStatusFlags(FLEX_CAN1, u32flag << 0) != 0)
  6.     {
  7.         FlexCAN_FD_RxMB_Handler(0);

  8.         FLEXCAN_ClearMbStatusFlags(FLEX_CAN1, u32flag << 0);
  9.     }

  10.     /* MB1 */
  11.     if (FLEXCAN_GetMbStatusFlags(FLEX_CAN1, u32flag << 1) != 0)
  12.     {
  13.         FLEXCAN_ClearMbStatusFlags(FLEX_CAN1, u32flag << 1);
  14.     }

  15.     /* MB2 */
  16.     if (FLEXCAN_GetMbStatusFlags(FLEX_CAN1, u32flag << 2) != 0)
  17.     {
  18.         FlexCAN_FD_RxMB_Handler(2);

  19.         FLEXCAN_ClearMbStatusFlags(FLEX_CAN1, u32flag << 2);
  20.     }

  21.     /* MB3 */
  22.     if (FLEXCAN_GetMbStatusFlags(FLEX_CAN1, u32flag << 3) != 0)
  23.     {
  24.         FLEXCAN_ClearMbStatusFlags(FLEX_CAN1, u32flag << 3);
  25.     }
  26. }
复制代码

MB0、MB2完成接收调用MB接收函数,获取报文并通过MB1、MB3发送。MB1、MB3完成传输,清除对应标志。
7.6  FlexCAN_FD中断示例
  1. void FlexCAN_FD_Interrupt_Sample(void)
  2. {

  3.     uint8_t Buffer[64] =
  4.     {
  5.         0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0xAA,
  6.         0xAA,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x55,
  7.         0x55,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0xAA,
  8.         0xAA,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x88,
  9.     };

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

  11.     FlexCAN_Configure();

  12.     while (1)
  13.     {
  14.         FlexCAN_FD_SendStandardFrameMessage(0x214, Buffer, FlexCANFD_TX_64Bytes_DataLen);

  15.         PLATFORM_LED_Toggle(LED1);
  16.         PLATFORM_DelayMS(1000);
  17.     }
  18. }
复制代码


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

8►
验证
连接CAN调试工具,配置波特率CAN 1Mbps、CAN FD 2Mbps,观测上位机软件:
接收区间隔1s接收到一次FD报文,ID为0x214。
在发送区发送标准帧FD报文,ID为0x111,发送扩展帧FD报文,ID为0x222,各发送5次:
每发送1次报文,接收区接收到1次该ID的报文,和程序预期一致。


+10
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|深圳市光明谷科技有限公司|光明谷商城|Sunshine Silicon Corpporation ( 粤ICP备14060730号|Sitemap

GMT+8, 2025-1-29 03:08 , Processed in 0.107871 second(s), 44 queries .

Powered by Discuz! X3.2 Licensed

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表