目录

  • 哪些STM32有CANFD外设
  • 参考文档
  • CAN-FD特性
  • CAN-FD格式
  • CAN 2.0 VS CAN-FD
  • STM32 FDCAN 外设特性
  • 位时间和采样点
  • Message RAM
  • 滤波器设置
  • Rx FIFO
  • Rx Buffer VS Rx FIFO
  • Tx Buffer, Tx FIFO, Tx Queue
  • 操作模式
  • 收发器延迟补偿TDC
  • 时钟校准
  • 低功耗模式
  • 现有BxCAN升级到CANFD
  • 下一篇用起来
  • 微信公众号

哪些STM32有CANFD外设

STM32G0, STM32G4, STM32H7, STM32L5,STM32MP1系列.

参考文档

  • AN5348_STM32器件上的FDCAN外设的应用手册: dm00625700-fdcan-peripheral-on-stm32-devices-stmicroelectronics.pdf
  • STM32G4-FD-CAN
  • AN5405_STM32H7和STM32L5系列微控制器加载程序时使用的FDCAN协议: dm00660346-fdcan-protocol-used-in-the-stm32-bootloader-stmicroelectronics.pdf

CAN-FD特性

CAN-FD协议的主要功能如下:

  • 与CAN协议的兼容, CAN-FD节点能够根据 ISO 11898-1 标准发送/接收CAN消息
  • 错误检查改进, 最高 21位 CRC 校验
  • 消息优先级
  • 延迟时间保证
  • 配置灵活性
  • 具有时间同步的组播接收
  • 系统范围内的数据一致性,每条消息最多64字节
  • 多主站
  • 错误检测和信令
  • 临时错误和永久错误之间的区别, 节点故障和缺陷节点的自动关闭

CAN-FD格式

CANFD发送的数据打包成一条消息, 分三个段:

  • 第一仲裁段 first arbitration phase, 下图左蓝色部分
  • 数据段 data phase, 下图中红色部分
  • 第二仲裁段 second arbitration phase, 下图右蓝色部分

上图只是标准帧的CAN-FD格式, 扩展帧是在IDE位后再加入18-bit identifier.

第一仲裁段:

  • 1位帧起始SOF
  • Arbitration field, 仲裁域, ID号和其他位,指示消息的目的(提供或请求数据)以及速度和格式配置(CAN或CAN-FD). 标准帧是12-bit = 11-bit ID + 1-bit r1, 扩展帧是 32-bit = 11-bit Base ID + 1-bit r1 + 1-bit IDE + 18-bit Extended-ID + 1-bit r0, 其中IDE应为identifier extension, 该位用于区分标准帧和扩展帧.

仲裁段和数据段的分割点在BRS位, BRS: Bit rate switching, 该位用于指示是否变换速率, 如从500K切换到2M, 或者保持500K不变.

数据段:

  • 数据长度代码(DLC, Data Length Code), 指示消息包含多少数据字节, 只有4bit共16种, 所以只能[0,8], 12, 16, 20, 24, 32, 48, 64这16种固定的字节数.
  • 用户希望发送的数据
  • 循环冗余序列校验(CRC), 通过以下方式确保数据传输的完整性:17级多项式用于对16字节内的有效载荷进行CRC校验, 21级多项式用于对16字节与64字节之间的有效载荷进行校验
  • 显性位

第二仲裁段:

  • 总线上其他节点发送的确认接收器(ACK)(如果至少一个已成功接收到消息)
  • 帧结束(EOF), 在IFS期间不发送消息, 目的是将当前帧与下一帧分开

搬来一些其它CAN-FD的图进一步了解:

下图中CAN中标识远程帧的RRS = remote request substitution, CAN-FD不用RRS, SRR = substitute remote request, 区分标准帧和扩展帧的IDE = identifier extension, FDF = flexible data rate format, d = dominant, r = recessive, r0 = reserved:

下图中 IDE (identifier extension), FDF (flexible data rate format), BRS (bit rate switch; recessive, if alternate bit-rate), ESI (error state indicator; recessive, if error passive):

FDCAN的两个变体:

  • Long Frame Mode (LFM) 长帧模式, 速率不变, 长度从最大8字节提到最大64字节
  • Fast Frame Mode (FFM) 快帧模式, 控制域, 数据域 和 CRC域 传输在更高的速率, STM32最高8Mbit/s, 设置BRS位.

一般将长帧模式和快帧模式联合使用.

CAN 2.0 VS CAN-FD

以标准帧为例:

RTR = Remote transmission request:

  • CAN 2.0 中RTR为显性(dominant, 逻辑0)表示数据帧, 为隐性(recessive, 逻辑1)表示远程帧
  • CANFD 仅支持数据帧, RTR始终为显性数据帧, 可以理解为保留不用

IDE 位保持不变, 用于区分标准帧还是扩展帧.

CANFD在控制字段中新增了3个位:

  • EDL, Extend data length, 扩展数据长度位, 隐性(逻辑1)表示CANFD帧, 显性(逻辑0, R0)表示CAN2.0帧
  • BRS, Bit rate switching, 位速率切换, 指示是否切换高速率传输, 如从500K切换到2M
  • ESI, Error state indicator, 错误状态指示器, 指示节点处于 error-active模式 还是 error-passive模式

DLC, Data Length Code, 数据长度代码, CAN 2.0和CANFD处于相同位置和同样的4-bit长度, 但8以后的值表示意义不同:

CANFD有效载荷从CAN 2.0的最大8字节提升到最大64字节, 改善网络带宽, 对多包处理需求减少, 因此, 通过为CRC字段添加更多位来增强消息完整性:

  • 有效载荷在16字节及以内, CRC以17-bit编码
  • 有效载荷在20字节及以上, CRC以21-bit编码

另外, 为了确保CAN-FD帧的鲁棒性,填充位机制支持CRC字段.

可参考 CAN 总线 之六 BOSCH CAN 比特位填充(编码规则)、归零编码(RZ)和不归零编码(NRZ), 在相同极性的 5 个连续位之后使用位填充, 相同极性的六个连续比特被认为是错误, 位填充方案的一个副作用是,接收到的消息中的少量位错误可能会破坏解填充过程(接收器需要去除填充位),从而导致大量错误在解填充消息中传播。这降低了 CRC 针对原始错误提供的保护级别。该协议的不足之处已经在 CAN FD 帧中得到了解决,具体方法是 通过使用固定填充比特和记录插入的填充比特数的计数器的组合。

下图给出对比总结, 主要是 数据有效负载的增加以及CAN-FD中可用的BRS,EDL和ESI位确保的更高速度:

STM32 FDCAN 外设特性

特性:

  • 符合CAN 2.0的A,B部分和ISO 11898-1:2015,-4
  • 可访问的10 KB RAM内存,因为是32-bit, 1 word = 4 bytes, 最多可分配2560个字, 所有CAN外设共用
  • 改进的接收过滤
  • 两个可配置的接收FIFO
  • 多达64个专用接收缓冲区
  • 接收高优先级消息时的单独信令
  • 多达32个专用的发送缓冲区
  • 可配置的发送FIFO和发送队列
  • 可配置的发送事件FIFO
  • 时钟校准单元
  • 收发器延迟补偿

一个FDCAN外设框图:

注意Shared Memory, 所有的FDCAN外设共用.

STM32G4的3路FDCAN框图:

仲裁段位速率最高1Mbit/s, 数据段位速率最高8Mbit/s.

支持:

  • 2 maskable interrupts per controller
  • power-down
  • CAN error logging
  • AUTOSAR 和 J1939

位时间和采样点

同一网络中的所有节点采样点需一致, 一般在75%~80%, 不然发送时, 总线会进入 error passive state, 或者 bus-off.

一般主时钟设为 40MHz(可以分频后实现), 然后再设置其它参数, 如160MHz主时钟的STM32G4:

FDCAN在500K@2M时的配置:

计算公式参考:

    /** Bit timing & sampling* Tq = (BRP+1)/Fcan if DIV8 = 0* Tq = 8*(BRP+1)/Fcan if DIV8 = 1* TSync = 1.Tq* TSeg1 = (TSEG1+1)*Tq                >= 3Tq* TSeg2 = (TSEG2+1)*Tq                >= 2Tq* Bit Time = TSync + TSeg1 + TSeg2    >= 8Tq** Resynchronization:** Tsjw = (SJW + 1)*Tq* TSeg1 >= Tsjw + Tprop* TSeg2 >= Tsjw*/

仲裁段:

  • 时钟 = 160M / Nominal Prescaler = 160M / 4 = 40MHz
  • 1 CAN bit = (1 + Tseg1 + Tseg2) TQ = 80 TQ (time quanta)
  • 通信速率 = 40M / (1 + Tseg1 + Tseg2) = 40M / (63 + 16 + 1) = 500Kbit/s
  • 采样点 = (1 + Tseg1) / ((1 + Tseg1 + Tseg2)) = 64 / 80 = 0.8

数据段:

  • 时钟 = 160M / Data Prescaler = 160M / 4 = 40MHz
  • 1 CAN bit = (1 + DTseg1 + DTseg2) TQ = 20 TQ (time quanta)
  • 通信速率 = 40M / (1 + DTseg1 + DTseg2) = 40M / (14 + 5 + 1) = 2Mbit/s
  • 采样点 = (1 + DTseg1) / ((1 + DTseg1 + DTseg2)) = 15 / 20 = 0.75

可以通过 KVASER Bit Timing Calculator for CANFD 这个网站在线计算.

时钟源默认40M, 不改变:

位速率设置500K@2M:

仲裁段采样点改为80%:

数据段采样点改为75%:

最后算出Tseg1, Tseg2, SJW的值:

PEAK的Bit Rate Calculation Tool 用着也很不错

当然配置方式不止这几种, 周立功ZCANPRO软件安装目录下的baudcal.exe也能算:

stm32仲裁段的计算可以参考这个网站 CAN Bit Time Calaulation:

或者 参考 S32K系列学习笔记——FlexCAN 模块介绍与例程建立, 遵循一些算法自己计算:

CAN 波特率周期会被分为 12-20 个时间段
采样点通常选在波特率周期的 75%-80% 段
剩余的 20%-25% 会作为 Phase_Seg2 的值
Phase_Seg1 的值会与 Phase_Seg2 的值相同
Sync_Seg 是 1 个时间段
Resync Jumo Width(RJW+1)= Phase_Seg2(如果 Phase_Seg2<4,(RJW+1)=4)

Message RAM

所有发送和接收的消息都存储在CAN消息RAM中. 在CAN消息RAM初始化期间,用户必须定义​​11位过滤器,29位过滤器,接收到的消息以及要传输的消息的存储位置.

CAN消息RAM分为四个不同的部分:

  • 过滤(11位过滤器,29位过滤器)
  • 接收(Rx FIFO 0,Rx FIFO 1,Rx Buffer)
  • 发送(Tx event FIFO,Tx Buffer)
  • 触发存储器(Trigger Memory)

如下图所示:

乍一看不得了, 但这只是1路CANFD独享10KB RAM的最大分配量, 如果有多个CANFD外设, 比如STM32G4的3路CANFD一块用, 每路分到的资源就可怜了.

FDCAN外设的所有部分都可以由用户配置。所有部分的所有元素之和不得超过CAN消息RAM的总大小。该RAM通过消除多余部分并为其他部分扩展足够的内存,提供了更高的灵活性和性能。

根据上图所示的顺序,在CAN消息RAM中以动态且连续的方式分配每个部分的已组态元素;但是,为了避免超过RAM的风险以及出于可靠性的原因,没有为每个段分配特定的自己的开始和结束地址。

为了所谓的动态分配, 为了从10KB内存抠出来点给其它外设用, 挺煞费苦心的, 非得个人分配的话也很容易埋坑.

消息的接收和发送意味着在RAM级别存储“元素”(element)。该“元素”仅包含标识符(identifier),DLC,控制位(ESI,XTD,RTR,BRS,FDF),数据字段和用于控制的特定传输/接收位字段。 CAN消息的其余位由硬件自动处理,不会保存在RAM中。

用于控制接收的特定位字段是过滤索引(filter index),接受的不匹配帧和Rx时间戳。
用于传输的特定位字段是消息标记(message marker)和event FIFO控制位.

Tx buffer, Tx FIFO, Tx queue 或 Rx buffer 的 每个element分配word的数量通过以下方式计算:

  • Header信息(两个保留的32-bit-words, 相当于8字节)以分配identifier,DLC字段,控制位和特定的发送/接收位字段
  • 数据(足够的32-bit-words)包含每个数据字段的字节数

计算公式为: Element size (in words) = Header information (2 words) + Data (data field/4), data field在0~8时Data取2 words, 如下表:

常说的MTU应该是这个东西, 如 以太网mtu值设定为1500, CAN2.0的mtu为16, CANFD的mtu为72. 一帧CANFD 最大 72 bytes => 18 words => 1 T/R elements, 此时有效利用率 64 / 72 = 88.88%.

element 总结如下:

  • 11-bit filter, 标准帧滤波器, 因为滤波器ID设置0~0x7FF, 共两个, 通常如掩码模式下一个设置为CAN_ID, 一个设置为掩码, 占用2*2=4个字节, 1标准帧滤波器element = 4 bytes = 1 word
  • 29-bit filter, 扩展帧滤波器, 因为滤波器ID设置0~0x1FFFFFFF, 共两个, 通常如掩码模式下一个设置为CAN_ID, 一个设置为掩码, 占用2*4=8个字节, 1扩展帧滤波器element = 8 bytes = 2 words
  • 发送接收不仅要接收有效数据(最大64字节), 也要包括数据链路层的东西(BRS等), 有可能收标准CAN, 也有可能收CANFD, 有可能标准帧, 也有可以能扩展帧, 按最大算, CANFD MTU固定为72, 所以 1 Rx element = 72 bytes = 18 words, 64 * 18 = 1152

下图是10KB RAM分给2路FDCAN的示例:

可以看到分配还是很自由的.

如果实在讨厌这些东西, 不看也行, STM32CubeMX生成的代码初始化部分会自动调用分配RAM的函数, 截取STM32G474使用3路CANFD部分自动生成的相关代码如下:

#define PERIPH_BASE           (0x40000000UL) /*!< Peripheral base address */
#define APB1PERIPH_BASE        PERIPH_BASE
#define SRAMCAN_BASE          (APB1PERIPH_BASE + 0xA400UL)#define SRAMCAN_FLS_NBR                  (28U)         /* Max. Filter List Standard Number      */
#define SRAMCAN_FLE_NBR                  ( 8U)         /* Max. Filter List Extended Number      */
#define SRAMCAN_RF0_NBR                  ( 3U)         /* RX FIFO 0 Elements Number             */
#define SRAMCAN_RF1_NBR                  ( 3U)         /* RX FIFO 1 Elements Number             */
#define SRAMCAN_TEF_NBR                  ( 3U)         /* TX Event FIFO Elements Number         */
#define SRAMCAN_TFQ_NBR                  ( 3U)         /* TX FIFO/Queue Elements Number         */#define SRAMCAN_FLS_SIZE            ( 1U * 4U)         /* Filter Standard Element Size in bytes */
#define SRAMCAN_FLE_SIZE            ( 2U * 4U)         /* Filter Extended Element Size in bytes */
#define SRAMCAN_RF0_SIZE            (18U * 4U)         /* RX FIFO 0 Elements Size in bytes      */
#define SRAMCAN_RF1_SIZE            (18U * 4U)         /* RX FIFO 1 Elements Size in bytes      */
#define SRAMCAN_TEF_SIZE            ( 2U * 4U)         /* TX Event FIFO Elements Size in bytes  */
#define SRAMCAN_TFQ_SIZE            (18U * 4U)         /* TX FIFO/Queue Elements Size in bytes  */#define SRAMCAN_FLSSA ((uint32_t)0)                                                      /* Filter List Standard StartAddress                  */
#define SRAMCAN_FLESA ((uint32_t)(SRAMCAN_FLSSA + (SRAMCAN_FLS_NBR * SRAMCAN_FLS_SIZE))) /* Filter List Extended StartAddress                  */
#define SRAMCAN_RF0SA ((uint32_t)(SRAMCAN_FLESA + (SRAMCAN_FLE_NBR * SRAMCAN_FLE_SIZE))) /* Rx FIFO 0 Start Address  */
#define SRAMCAN_RF1SA ((uint32_t)(SRAMCAN_RF0SA + (SRAMCAN_RF0_NBR * SRAMCAN_RF0_SIZE))) /* Rx FIFO 1 Start Address  */
#define SRAMCAN_TEFSA ((uint32_t)(SRAMCAN_RF1SA + (SRAMCAN_RF1_NBR * SRAMCAN_RF1_SIZE))) /* Tx Event FIFO StartAddress */
#define SRAMCAN_TFQSA ((uint32_t)(SRAMCAN_TEFSA + (SRAMCAN_TEF_NBR * SRAMCAN_TEF_SIZE))) /* Tx FIFO/Queue StartAddress                  */
#define SRAMCAN_SIZE  ((uint32_t)(SRAMCAN_TFQSA + (SRAMCAN_TFQ_NBR * SRAMCAN_TFQ_SIZE))) /* Message RAM size         *//*** @brief  Calculate each RAM block start address and size* @param  hfdcan pointer to an FDCAN_HandleTypeDef structure that contains*         the configuration information for the specified FDCAN.* @retval none*/
static void FDCAN_CalcultateRamBlockAddresses(FDCAN_HandleTypeDef *hfdcan)
{uint32_t RAMcounter;uint32_t SramCanInstanceBase = SRAMCAN_BASE;
#if defined(FDCAN2)if (hfdcan->Instance == FDCAN2){SramCanInstanceBase += SRAMCAN_SIZE;}
#endif /* FDCAN2 */
#if defined(FDCAN3)if (hfdcan->Instance == FDCAN3){SramCanInstanceBase += SRAMCAN_SIZE * 2U;}
#endif /* FDCAN3 *//* Standard filter list start address */hfdcan->msgRam.StandardFilterSA = SramCanInstanceBase + SRAMCAN_FLSSA;/* Standard filter elements number */MODIFY_REG(hfdcan->Instance->RXGFC, FDCAN_RXGFC_LSS, (hfdcan->Init.StdFiltersNbr << FDCAN_RXGFC_LSS_Pos));/* Extended filter list start address */hfdcan->msgRam.ExtendedFilterSA = SramCanInstanceBase + SRAMCAN_FLESA;/* Extended filter elements number */MODIFY_REG(hfdcan->Instance->RXGFC, FDCAN_RXGFC_LSE, (hfdcan->Init.ExtFiltersNbr << FDCAN_RXGFC_LSE_Pos));/* Rx FIFO 0 start address */hfdcan->msgRam.RxFIFO0SA = SramCanInstanceBase + SRAMCAN_RF0SA;/* Rx FIFO 1 start address */hfdcan->msgRam.RxFIFO1SA = SramCanInstanceBase + SRAMCAN_RF1SA;/* Tx event FIFO start address */hfdcan->msgRam.TxEventFIFOSA = SramCanInstanceBase + SRAMCAN_TEFSA;/* Tx FIFO/queue start address */hfdcan->msgRam.TxFIFOQSA = SramCanInstanceBase + SRAMCAN_TFQSA;/* Flush the allocated Message RAM area */for (RAMcounter = SramCanInstanceBase; RAMcounter < (SramCanInstanceBase + SRAMCAN_SIZE); RAMcounter += 4U){*(uint32_t *)(RAMcounter) = 0x00000000U;}
}HAL_StatusTypeDef HAL_FDCAN_Init(FDCAN_HandleTypeDef *hfdcan)...FDCAN_CalcultateRamBlockAddresses(hfdcan);...
}

折算一下(如有错误, 请指正):

//11-bit filter 基地址相对偏移, 上面定义1路CANFD最多28个标准帧滤波器
SRAMCAN_FLSSA = 0;
//29-bit fliter 基地址相对偏移, 上面定义1路CANFD最多8个扩展帧滤波器
SRAMCAN_FLESA = SRAMCAN_FLSSA + (SRAMCAN_FLS_NBR * SRAMCAN_FLS_SIZE) = 0 + 28 * 4 = 112;
//Rx FIFO 0 基地址相对偏移, Rx FIFO 0 的深度也就3, 最多可扔进去3个CANFD帧, 可怜弱小无助...
SRAMCAN_RF0SA = SRAMCAN_FLESA + (SRAMCAN_FLE_NBR * SRAMCAN_FLE_SIZE) = 112 + 8 * 2 * 4 = 176;
//Rx FIFO 1 基地址相对偏移, Rx FIFO 1 的深度也是3
SRAMCAN_RF1SA = SRAMCAN_RF0SA + (SRAMCAN_RF0_NBR * SRAMCAN_RF0_SIZE) = 176 + 3 * 18 * 4 = 392;
//Tx event FIFO 基地址相对偏移, 可以存3个Tx event
SRAMCAN_TEFSA = SRAMCAN_RF1SA + (SRAMCAN_RF1_NBR * SRAMCAN_RF1_SIZE) = 392 + 3 * 18 * 4 = 608;
//Tx buffers 基地址相对偏移, Tx buffers 也就 3帧 CANFD...
SRAMCAN_TFQSA = SRAMCAN_TEFSA + (SRAMCAN_TEF_NBR * SRAMCAN_TEF_SIZE) = 608 + 3 * 2 * 4  = 632;
// Message RAM Size
SRAMCAN_SIZE  = SRAMCAN_TFQSA + (SRAMCAN_TFQ_NBR * SRAMCAN_TFQ_SIZE) = 632 + 3 * 18 * 4 = 848 = 0x350;FDCAN1 基地址 = SRAMCAN_BASE = 0x40000000 + 0xA400 = 0x4000 A400;
FDCAN2 基地址 = SRAMCAN_BASE +  SRAMCAN_SIZE = 0x4000 A400 + 0x350 = 0x4000 A750;
FDCAN3 基地址 = SRAMCAN_BASE +  SRAMCAN_SIZE * 2 = 0x4000 A400 + 0x350 * 2 = 0x4000 AAA0;

上面的代码中总结一下, STM32G474一共3个CANFD外设, 其中每个CANFD外设:

  • 最多28个标准帧滤波器(Cube软件中能设置的最大值)
  • 最多8个扩展帧滤波器(Cube软件中设置的最大值)
  • Rx FIFO 0 能缓存 3 帧 CANFD
  • Rx FIFO 1 能缓存 3 帧 CANFD
  • Tx buffer 能缓存 3 帧 CANFD
  • 3个CANFD外设共用 848 * 3 = 2544 bytes的空间, 没有超过 10 Kbytes = 10240 / 4 =2560words 空间的限制

滤波器设置

STM32所有的CANFD外设合计最多可以同时设置 128x 11-bit filter + 64x 29-bit filter, 但具体到各个型号又有不同, 如STM32G4号称每路 Each set has 28 entries : 28x 11-bit filter entries+ 28x 29-bit filter entries, 但Cube里面配置每路最多 28x 11-bit filter entries + 8x 29-bit filter, 可以试试手动更改生成代码中宏定义SRAMCAN_FLE_NBR的值.

可以将这些过滤器分配给Rx FIFO 0/1或专用的Rx缓冲区。当FDCAN执行验收过滤时,它总是从过滤器元素#0开始,并遍历过滤器列表以查找匹配元素。接受过滤在该消息的第一个匹配元素处停止,随后的过滤元素被注释。因此,配置的过滤器元素的顺序对过滤过程的性能有重大影响。用户选择启用或禁用每个过滤器元素,并可以将每个元素配置为接受或拒绝过滤。每个过滤器元素可以配置为:

  • Range filter, 范围过滤器:该过滤器与标识符在两个ID定义的范围内的所有邮件匹配。
  • Filter for one or two dedicated IDs, 专用ID过滤器:可以配置为与一个或两个特定标识符匹配。
  • Classic bit mask filter, 经典位掩码过滤器:通过对接收到的标识符的位进行掩码来匹配标识符组。配置的第一个ID用作消息ID过滤器,第二个ID用作过滤器掩码。过滤器上的每个零位都会掩盖已配置的ID过滤器的相应位位置。注意:如果所有位均等于1,则仅当接收到的消息ID和消息ID过滤器相同时才会匹配。如果所有掩码位都等于0,则所有消息ID都匹配。

当收到高优先级消息时,FDCAN可以通知用户。此通知可用于监视传入的高优先级消息的状态并启用对这些元素的快速访问。FDCAN在消息过滤器的帮助下检测到高优先级消息。过滤器元素提供与高优先级消息相关的以下设置:

  • 设置优先级并在过滤器匹配的情况下存储在FIFO 0/1中:如果此消息过滤器匹配,则FDCAN通知高优先级消息到达并将其存储在Rx FIFO 0/1中.
  • 如果过滤器匹配,则设置优先级:如果此消息过滤器匹配,则FDCAN通知高优先级消息到达,但不存储该元素。

如设置全接收的一段代码:

void fdcan2_filter_config(void)
{sFilterConfig2.IdType = FDCAN_STANDARD_ID;sFilterConfig2.FilterIndex = 0;sFilterConfig2.FilterType = FDCAN_FILTER_MASK;sFilterConfig2.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;sFilterConfig2.FilterID1 = 0;sFilterConfig2.FilterID2 = 0;if (HAL_FDCAN_ConfigFilter(&hfdcan2, &sFilterConfig2) != HAL_OK){Error_Handler();}sFilterConfig2.IdType = FDCAN_EXTENDED_ID;sFilterConfig2.FilterIndex = 0;sFilterConfig2.FilterType = FDCAN_FILTER_MASK;sFilterConfig2.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;sFilterConfig2.FilterID1 = 0;sFilterConfig2.FilterID2 = 0;   if (HAL_FDCAN_ConfigFilter(&hfdcan2, &sFilterConfig2) != HAL_OK){Error_Handler();}/* Configure global filter on both FDCAN instances:Filter all remote frames with STD and EXT IDReject non matching frames with STD ID and EXT ID */if (HAL_FDCAN_ConfigGlobalFilter(&hfdcan2, FDCAN_REJECT, FDCAN_REJECT, FDCAN_FILTER_REMOTE, FDCAN_FILTER_REMOTE) != HAL_OK){Error_Handler();}/* Activate Rx FIFO 0 new message notification on both FDCAN instances */if (HAL_FDCAN_ActivateNotification(&hfdcan2, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0) != HAL_OK){Error_Handler();}if (HAL_FDCAN_ActivateNotification(&hfdcan2, FDCAN_IT_BUS_OFF, 0) != HAL_OK){Error_Handler();}HAL_FDCAN_Start(&hfdcan2);
}

Rx FIFO

Rx FIFO的起始地址是第一个Rx FIFO元素的第一个字的地址。通过匹配过滤的接收到的元素将根据匹配的过滤器元素存储在适当的Rx FIFO中。如果Rx FIFO已满,则可以根据两种不同模式处理新到达的元素:

  • 阻止模式:这是默认操作模式, 新元素抛弃并提示
  • 覆盖模式:Rx FIFO中接受的新元素将覆盖Rx FIFO中最旧的元素, 同时FIFO中元素的put和get索引加1

相关的代码如下:

#define FDCAN_RX_FIFO_BLOCKING  ((uint32_t)0x00000000U) /*!< Rx FIFO blocking mode  */
#define FDCAN_RX_FIFO_OVERWRITE ((uint32_t)0x00000001U) /*!< Rx FIFO overwrite mode */#define IS_FDCAN_RX_FIFO_MODE(MODE) (((MODE) == FDCAN_RX_FIFO_BLOCKING ) || \((MODE) == FDCAN_RX_FIFO_OVERWRITE))HAL_StatusTypeDef HAL_FDCAN_ConfigRxFifoOverwrite(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo, uint32_t OperationMode)
{/* Check function parameters */assert_param(IS_FDCAN_RX_FIFO(RxFifo));assert_param(IS_FDCAN_RX_FIFO_MODE(OperationMode));...
}

要从Rx FIFO读取元素,CPU必须执行以下步骤:

  • 读取寄存器FDCAN_RXF1S以了解Rx FIFO的状态.
  • 按照以下公式计算RAM中最旧元素的地址:最旧元素地址= CAN_message_RAM_base_address + FDCAN_RXF1C.F1SA(起始地址)+ FDCAN_RXF1S.F1GI(获取索引)x Rx FIFO_element_size.
  • 从计算出的地址中读取元素。CPU从Rx FIFO中读取一个元素或一系列元素后,必须确认读取。确认后,FDCAN可以将相应的Rx FIFO缓冲区重新用于新元素。为了确认一个或多个元素,CPU必须将从Rx FIFO读取的最后一个元素的缓冲区索引写入FDCAN_RXF1A寄存器。因此,FDCAN会更新FIFO填充级别和获取索引.

RxFIFO中断函数中使用的HAL_FDCAN_GetRxMessage函数帮我们做了这些工作.

Rx Buffer VS Rx FIFO

Tx Buffer, Tx FIFO, Tx Queue

Cube中 Tx Fifo Queue Mode 选项用来配置是 FIFO Mode 还是 Queue Mode.

发送函数是 HAL_FDCAN_AddMessageToTxFifoQ, Add a message to the Tx FIFO/Queue and activate the corresponding transmission request.

FDCAN支持混合配置: dedicated Tx buffer + Tx FIFO 或者 dedicated Tx buffer + Tx queue.

操作模式

如下操作模式, 可在Cube中Mode选项直接配置:

  • Normal mode, 正常模式
  • Test mode, 测试模式仅用于生产测试, 自测和校准单元
  • Restricted-operation mode, 受限操作模式, 可以接收数据帧/远程帧, 对有效帧确认, 不支持发送(数据/远程帧, 活动错误帧, 过载帧)
  • Bus-monitoring mode, 总线监视模式, 不影响总线传输, 流量分析, 可接收有效数据帧/远程帧, 不支持传输开始/有效帧确认
  • External loop-back mode, 外回环模式, 用于硬件自检, FDCAN将自己发送的消息视为已接收的消息,如果通过接收过滤将它们存储在Rx FIFO中,则为了独立于外部刺激,FDCAN会忽略确认错误(在确认插槽中进行隐性位采样)。 FDCAN从其“发送”输出到其“接收”输入执行内部反馈。
  • Internal loop-back mode, 内回环模式, 用于硬件自检, FDCAN可以在不影响连接到FDCAN_TX和FDCAN_RXpins的运行CAN系统的情况下进行测试。 FDCAN_RX引脚与FDCAN断开连接,并且FDCAN_TX引脚处于隐性状态.

收发器延迟补偿TDC

CAN发送数据前有这样的代码:

     /* Configure and enable Tx Delay Compensation, required for BRS mode.TdcOffset default recommended value: DataTimeSeg1 * DataPrescalerTdcFilter default recommended value: 0 */HAL_FDCAN_ConfigTxDelayCompensation(&hfdcan1, 80, 0);HAL_FDCAN_EnableTxDelayCompensation(&hfdcan1);

TDC, Transceiver delay compensation, 收发器延迟补偿

在采样点,所有发送器检查先前发送的位是否被正确采样。需要这种机制来检查问题并检测其他节点错误帧。由于发送器看到自己的发送位由于收发器环路延迟而延迟,因此该延迟为TSEG1设置了下限,如下图所示(采样点之前的时间段),这也是数据比特率的上限。这就是为什么引入收发器延迟补偿机制(TDC)的原因.

为了在检查位错误时补偿此环路延迟,定义了一个辅助采样点(SSP),而不是在采样点进行操作,而是在SSP处检查传输的位。该检查的结果将存储到到达下一个采样点为止。

在数据阶段,将为每个发送的位生成一个SSP。对于SSP位置,要考虑收发器的不对称性和振铃,但是由于收发器监视自己的比特流,因此没有时钟容限。

通过将1写入FDCAN_DBTP中的TDC位,可以启用收发器延迟补偿。在数据阶段开始之前(在FDF位到res的下降沿),在每个发送的FDCAN帧内开始测量。当在发送器的“接收”输入引脚FDCAN_RX上看到该边沿时,测量将停止。该测量的分辨率为1 mtq(minimum time quantum, 最小时间量子).

在仲裁阶段,始终禁用延迟补偿。SSP位置定义为从FDCAN_TX引脚到FDCAN_RX引脚的测量延迟之和,加上通过TDCO [6:0]字段配置的发送器延迟补偿偏移.

发送器延迟补偿偏移量用于调整SSP在接收位内部的位置。存储发送位的值,直到达到其SSP,然后将其与实际接收的位值进行比较.

时钟校准

FDCAN支持时钟校准单元(CCU, clock calibration unit)功能。该功能允许用户通过FDCAN发送器(主机)校准FDCAN接收器(设备)。例如,当FDCAN设备与主机的最新比特率通信时,此功能允许用户在总线中添加新实例,并且比特率的存在是未知的。当FDCAN接收器没有精确的石英时(可能导致准时错误),这也很有用。

CCU仅在FDCAN比特率在125 Kbit/s和1 Mbit/s之间时运行.

低功耗模式

现有BxCAN升级到CANFD

下表帮助用户简化了将STM32设备中的CAN 2.0协议升级到CAN-FD协议的过程:

下一篇用起来

微信公众号

欢迎扫描二维码关注本人微信公众号, 及时获取或者发送给我最新消息:

STM32 CANFD 基础知识相关推荐

  1. 【STM32】基础知识 第十课 CubeMx

    [STM32]基础知识 第十课 CubeMx STM32 CubeMX 简介 安装 JAVA CubeMX 安装 新建 STM32 CubeMX 工程步骤 新建工程 时钟模块配置 GPIO 配置 生成 ...

  2. 【STM32】基础知识 第一课 单片机简介

    [STM32]基础知识 第一课 单片机简介 单片机是什么 单片机和电脑区别 单片机的特点 单片机有什么用 单片机发展历程 单片机发展趋势 CISC vs RISC CISC 和 RISC 举例 冯诺依 ...

  3. STM32学习——基础知识

    STM32学习--基础知识 1. 芯片运作原理 ICode用于读取Flash中的指令(就是编写的代码)ICode一条条读取代码然后执行 DCode:读取数据数据分为常量与变量,常量存在Flash中,变 ...

  4. STM32 USB基础知识

    文章目录 拓展阅读 前言 一.USB基础知识 二.USB2.0 1.usb2.0模式说明 2.USB2.0全速USB和高速USB的识别过程分析 3.usb协议关系 4.USB2.0与USB1.x 硬件 ...

  5. STM32开发基础知识——OLED开发基础

    基于stm32芯片的OLED应用,需要做的是: ①移植OLED的底层驱动函数库 ②调用OLED驱动库中的底层函数进行应用开发 OLED驱动库中常用函数 OLED初识化函数 void OLED_Iint ...

  6. STM32开发基础知识——定时器

    目录 常见的定时器资源 系统嘀嗒定时器 Sys Tick 看门狗定时器watchDog 基本定时器 TIM6.TIM7 通用定时器 TIM2.TIM3.TIM4.TIM5 通用定时器的基本工作原理 定 ...

  7. STM32开发基础知识入门

    C语言基础 位操作 对基本类型变量可以在位级别进行操作. 1) 不改变其他位的值的状况下,对某几个位进行设值. 先对需要设置的位用&操作符进行清零操作,然后用|操作符设值. 2) 移位操作提高 ...

  8. stm32单片机基础知识总结(三)

    今天天津下了好大的雪,我想这应该是2022年的第一场雪.虽然已经进入了春天,但是能下这么大的雪,对于大多数北方人来说,并不会感到意外.大家都知道有个词叫做"倒春寒",就是说春天来临 ...

  9. STM32基础知识(一)

    文章目录 前言 一.什么是STM32 二.STM32 的分类 三.STM32的命名规则 前言 在正式学习 stm32 之前,先简单的介绍一下stm32的基础知识: 包括一些 STM32的基本概念.ST ...

  10. STM32 CubeMx教程 -- 基础知识及配置使用教程

    文章目录 前言 一.STM32CubeMx 界面介绍 File 界面 Windows 界面 Help 界面 Updater Settings 界面 二.STM32CubeMx 使用教程 新建工程 配置 ...

最新文章

  1. 单行子查询 多行子查询 关联子查询 笔记
  2. EOS 共识机制 (3)DPOS+BFT
  3. struts-config.xml 简述
  4. 涨姿势,图文带你了解 8 大排序算法
  5. 关于gedit的编码问题
  6. 数学建模学习笔记(七)——图论最短路问题
  7. cocos creator 打包apk_cocos creator android 编译踩坑
  8. 第四届CocoaChina开发者大会官网上线
  9. python十进制转换_Python进制转换
  10. C结构和C++结构之间的区别是什么?
  11. 太极图正确画法_什么是太极?其意义是什么?太极图是谁画的?
  12. selinium如何多线程_求教个selenium+grid+testng多线程运行的问题
  13. Sum nyoj 欧拉定理简单运用(数论入门)
  14. 数据结构与算法 计算表达式(一)
  15. 高级java技术栈图
  16. 网银汇款提示服务器无响应,为什么我的网上银行支付页面总是未响应
  17. 2021年中国货车行业发展现状分析,新能源货车将迎来快速发展「图」
  18. google chrome的图标成一页纸了_10 款 Chrome 扩展,让你的浏览器好用到飞起
  19. 解析迅捷产品的八大特点
  20. dom4j解析XML实例

热门文章

  1. 用这个玩吃鸡:宏按键加一键恢复加自由移动视角,你想要的功能它都有
  2. 黑客帝国产业链调查:熊猫烧香作者一年赚千万
  3. 通过网店管家抓取奇门淘系订单数据
  4. h5 7个移动端框架
  5. Python3爬虫系列:理论+实验+爬取妹子图实战
  6. Java单例模式简单代码
  7. 层次分析法(AHP)及源代码实现
  8. Matlab遗传算法大纲
  9. pg数据库客户端linux,PostgresSQL客户端pgAdmin4使用
  10. 微信开发api文档地址