深入探究STM32H743 FDCAN的Message RAM

先说为什么会有这篇文章。公司的一个产品进行了升级,MCU从STM32F429升级到了STM32H743。所有的功能都适配完了,产品交到客户手中,莫名的出现CAN能发不能收,不定时死机的问题。在只用CAN1的时候,不会出问题,在公司也模拟过此条件,2天没出问题。但是两个CAN一起用的时候,就出现了问题,CAN1消息乱了。按说CAN2和CAN1在物理上是独立的,CAN2的工作怎么会影响到CAN1呢。经过排查是由于CAN1和CAN2用的Message RAM有重叠,CAN2一但工作,收到的消息会写入与CAN1同样的Message RAM区,可能修改CAN1的Filter,也有可能将CAN1接受到的消息修改掉。某种情况下,CAN1消息的数据长度被修改,长度会大于8个字节。当读取消息时,由于给定的接收buffer只有8个字节,导致相邻指针变量的值被修改。如果这个指针变量的值被更改成了一个非法地址,当对这个指针变量进行操作时会导致死机。

下面来看下STM32H743的Message RAM。

The controller area network (CAN) subsystem consists of two CAN modules, a shared message RAM and a clock calibration unit.

A 10 Kbyte message RAM implements filters, receive FIFOs, receive buffers, transmit event FIFOs, transmit buffers (and triggers for TTCAN). This message RAM is shared between the FDCAN1 and FDCAN2 modules.

The message RAM has a width of 32 bits. The FDCAN module can be configured to allocate
up to 2560 words in the message RAM. It is not necessary to configure each of the sections
listed in Figure 734, nor is there any restriction with respect to the sequence of the sections

 Note:The FDCAN does not check for erroneous configuration of the message RAM. Especially the configuration of the start addresses of the different sections and the number of elements of each section has to be done carefully to avoid falsification or loss of data.

以上摘自参考手册,2个要点:

  • FDCAN1和FDCAN2是独立的,共享一块2560 words的Message RAM。
  • 这块RAM用户来配置,配错了FDCAN可不管。

Figure 734解析:

左边是能配置的寄存器,表里面是能配置的元素,右边是各元素能配置的大小。计算一下,如果右边都按最大值来配置,总共需要4480 words,如果两个FDCAN一起用,这个数据还要翻倍,Message RAM远不够用。所以实际中需要根据需求配置每种元素的数量及大小。

下面以代码注释掉方式描述各种元素

/* FDCAN1 init function */
void MX_FDCAN1_Init(void)
{

  /* USER CODE BEGIN FDCAN1_Init 0 */

  /* USER CODE END FDCAN1_Init 0 */

  /* USER CODE BEGIN FDCAN1_Init 1 */

  /* USER CODE END FDCAN1_Init 1 */
  hfdcan1.Instance = FDCAN1;
  hfdcan1.Init.FrameFormat = FDCAN_FRAME_CLASSIC;
  hfdcan1.Init.Mode = FDCAN_MODE_NORMAL;
  hfdcan1.Init.AutoRetransmission = ENABLE;
  hfdcan1.Init.TransmitPause = DISABLE;
  hfdcan1.Init.ProtocolException = ENABLE;
  hfdcan1.Init.NominalPrescaler = 1;
  hfdcan1.Init.NominalSyncJumpWidth = 1;
  hfdcan1.Init.NominalTimeSeg1 = 2;
  hfdcan1.Init.NominalTimeSeg2 = 2;
  hfdcan1.Init.DataPrescaler = 1;
  hfdcan1.Init.DataSyncJumpWidth = 1;
  hfdcan1.Init.DataTimeSeg1 = 1;
  hfdcan1.Init.DataTimeSeg2 = 1;
  hfdcan1.Init.MessageRAMOffset = 0;							//Message RAM的偏移地址,如果用CAN2,此处可设置为2560/2,各占一半。
  hfdcan1.Init.StdFiltersNbr = 8;								//StdFilter元素数量,元素大小固定为1 word
  hfdcan1.Init.ExtFiltersNbr = 4;								//ExtFilter元素数量,元素大小固定为2 words
  hfdcan1.Init.RxFifo0ElmtsNbr = 64;							//RxFifo0元素数量,元素大小与RxFifo0ElmtSize有关,从0x4-0x12 words
  hfdcan1.Init.RxFifo0ElmtSize = FDCAN_DATA_BYTES_8;			//RxFifo0元素大小
  hfdcan1.Init.RxFifo1ElmtsNbr = 0;								//RxFifo1元素数量,元素大小与RxFifo1ElmtSize有关,从0x4-0x12 words
  hfdcan1.Init.RxFifo1ElmtSize = FDCAN_DATA_BYTES_8;			//RxFifo1元素大小
  hfdcan1.Init.RxBuffersNbr = 0;								//RxBuffers元素数量,元素大小与RxBufferSize有关,从0x4-0x12 words
  hfdcan1.Init.RxBufferSize = FDCAN_DATA_BYTES_8;				//RxBuffers元素大小
  hfdcan1.Init.TxEventsNbr = 32;								//TxEvents元素数量,元素大小固定为2 words
  hfdcan1.Init.TxBuffersNbr = 32;								//TxBuffers元素数量,元素大小与TxElmtSize有关,从0x4-0x12 words
  hfdcan1.Init.TxFifoQueueElmtsNbr = 0;							//TxFifoQueue元素数量,元素大小与TxElmtSize有关,从0x4-0x12 words
  hfdcan1.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION;
  hfdcan1.Init.TxElmtSize = FDCAN_DATA_BYTES_8;					//Tx元素大小
  if (HAL_FDCAN_Init(&hfdcan1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN FDCAN1_Init 2 */

  /* USER CODE END FDCAN1_Init 2 */

}

接收和发送的消息大小不固定,他们的大小(words)由以下选择:

#define FDCAN_DATA_BYTES_8  ((uint32_t)0x00000004U) /*!< 8 bytes data field  */
#define FDCAN_DATA_BYTES_12 ((uint32_t)0x00000005U) /*!< 12 bytes data field */
#define FDCAN_DATA_BYTES_16 ((uint32_t)0x00000006U) /*!< 16 bytes data field */
#define FDCAN_DATA_BYTES_20 ((uint32_t)0x00000007U) /*!< 20 bytes data field */
#define FDCAN_DATA_BYTES_24 ((uint32_t)0x00000008U) /*!< 24 bytes data field */
#define FDCAN_DATA_BYTES_32 ((uint32_t)0x0000000AU) /*!< 32 bytes data field */
#define FDCAN_DATA_BYTES_48 ((uint32_t)0x0000000EU) /*!< 48 bytes data field */
#define FDCAN_DATA_BYTES_64 ((uint32_t)0x00000012U) /*!< 64 bytes data field */

设定了Message RAM的偏移地址,在函数HAL_FDCAN_Init中就会计算各种元素的起始地址和总大小,并依次从Message RAM中分配地址。此时filter的地址已经得到了分配,后续在HAL_FDCAN_ConfigFilter函数中会根据filter类型和FilterIndex将过滤器的配置写入对应的地址。注意FilterIndex的大小不能超过对应类型的元素数量-1。Message RAM可以让CAN1和CAN2对半分,要自己计算CAN1元素的截止地址不能达到CAN2的开始地址。如果想CAN2的紧挨着CAN1存放,可以按以下方式设置:

hfdcan2.Init.MessageRAMOffset = (hfdcan1.msgRam.EndAddress-SRAMCAN_BASE)/4;

此方式能正确工作的前提是CAN1在CAN2之前初始化,否则会出不负责任的问题。

关于Message RAM的问题差不多就这么多。H7的CAN比之前的产品强大了很多,但是也复杂了很多。比如支持Transmit pause,就是不让单一节点长时间占用总线,给其他低优先级节点一些机会。比如支持TTCAN等,留待以后慢慢研究。

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值