你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32H7的FDCAN总线应用之双FDCAN实现(支持经典CAN)

[复制链接]
STMCU小助手 发布时间:2021-11-6 23:52
92.1 初学者重要提示
1、  FDCAN相关知识点可以看第90和91章。

2、  CAN菊花链组网时,在两端接分别接120 Ω的终端电阻:链接地址。

3、  FDCAN控制器外接的PHY芯片输出的是差分信号,组网接线时,注意是CANL接CANL,CANH接CANH。

4、  经典CAN每帧最大8字节,FDCAN每帧最大64字节。

5、  CAN不接外置PHY芯片,通信测试方法:链接地址。

6、  关于CAN总线是否需要供地的问题:地址

7、  CAN组网只有一个节点的情况下,接示波器看不到FDCAN数据帧波形。

8、  特别推荐瑞萨的CAN入门中英文手册,做的非常好:链接地址

9、  带隔离功能的FDCAN芯片搜集:链接地址

10、  除了本章提供的基于ST HAL库实现的双FDCAN通信,再提供个基于CMSIS-Driver的:

基于STM32H7的CMSIS-Driver驱动实现双CAN FD和双经典CAN两种方式案例发布

92.2 FDCAN硬件接口设计
STM32H7带了两个FDCAN控制器,然后外接物理层PHY芯片就可以使用了。FDCAN1和FDCAN2外接芯片原理图如下:

c8e1fdb0d11f44ad8a5e73df6478daa3.png


f8ad9b93bf614fb5a9c3dca1300cd241.png




使用的PHY芯片SN65HVD230即支持经典CAN,也支持FDCAN。PHY芯片输出的是差分信号,逻辑0或者逻辑1的电平效果如下:链接地址


122127eeb7e147c08c738a8b723f72d7.png



92.3 FDCAN基础知识
FDCAN的基础知识在第90已经做了详细说,这里补充些本章要用到的知识点。

92.3.1 经典CAN和FDCAN的区别
CAN-FD的开发可以满足需要更高带宽的通信网络需求。每帧最多具有64个字节的CAN-FD以及将比特率提高到最大的可能性,使数据阶段要快8倍,在第二个仲裁阶段要恢复到正常的比特率。通过以下方式确保数据传输的完整性:

(1)17级多项式对最大16字节的有效载荷进行CRC。

(2)21级多项式对16到64字节之间的有效载荷进行校验。

标准帧和CAN FD的区别:


7509b460766846cc8e1b6a23f0814074.png



标识符后,CAN 2.0和CAN-FD具有不同的作用:

(1)CAN 2.0发送RTR位以精确确定帧类型:数据帧(RTR为主要)或远程帧(RTR)是隐性的)。

(2)由于CAN-FD仅支持数据帧,因此始终发送占优势的RRS(保留)。

IDE位保持在相同位置,并以相同的动作来区分基本格式(11位标识符)。请注意,在扩展格式的情况下,IDE位以显性或隐性方式传输(29位标识符)。

与CAN 2.0相比,在CAN-FD帧中,在控制字段中添加了三个新位:

(1)扩展数据长度(EDL)位:隐性表示帧为CAN-FD,否则该位为显性(称为R0)在CAN 2.0帧中。

(2)比特率切换(BRS):指示是否启用两个比特率(例如,当数据阶段位以不同的比特率传输到仲裁阶段)。

(3)错误状态指示器(ESI):指示节点处于错误活动模式还是错误被动模式。

控制字段的最后一部分是数据长度代码(DLC),它具有相同的位置和相同的长度(4位),用于CAN 2.0和CAN-FD。 DLC功能在CAN-FD和CAN 2.0中相同,但CAN-FD有很小变化(下表中的详细信息)。 CAN-FD扩展帧允许单个消息中发送64个数据字节,而CAN 2.0有效负载数据最多可以发送8个字节。


777694606c84458ba0e313d3bf13efb6.png



通过增加有效载荷数据的数据字段来改善网络带宽,因为需要更少的包处理。 同时,通过为CRC添加更多位来增强消息完整性:

(1)如果有效载荷数据最多为16个字节,则CRC以17位编码。

(2)如果有效载荷数据大于20(16)个字节,则CRC以21位编码。

另外,为了确保CAN-FD帧的鲁棒性,填充位机制支持CRC字段。下表总结了CAN-FD和CAN 2.0之间的主要区别。 提供的主要功能与CAN 2.0相比,CAN FD的改进之处在于数据有效负载的增加和速度的提高由CAN-FD中可用的BRS,EDL和ESI位来确保。

3bb1b6324791495684400b613b275f57.png



下表可帮助用户简化将STM32设备中的CAN 2.0协议升级到CAN-FD协议的过程。该表还指定了FDCAN的改进。与传统的BxCAN(基本扩展CAN)相比,FDCAN具有许多优势,包括更快的数据传输速度。速率和数据字节数的扩展,减少了帧开销。 总线负载也可以减少。 传输和接收中消息数量的增加要求RAM存储器的改进:


3288aaff95c4402780f756c76f2c5576.png



鉴于BxCAN的兼容性,BxCAN开发人员可以轻松地迁移到FDCAN,因为FDCAN可以无需对整个系统设计进行修订即可实施。 FDCAN包含所有BxCAN功能,并满足新应用程序的要求。

92.3.2 STM32H7的FDCAN1和FDCAN2的区别
仅FDCAN1支持TTCAN时间触发通信,而FDCAN2不支持。

92.3.3 FDCAN支持的最高速度
经典CAN是1Mbps,CAN FD最高2Mbps,CAN FD-SiC是5-8Mbps,CAN XL是10Mbps。


07af8d43554c422380843c1a6368d640.png



92.3.4 FDCAN的主时钟选择
FDCAN1和FDCAN2支持三种时钟源HSE,PLL1Q和PLL2Q,我们这里选择的PLL2Q输出20MHz。

bdbe36c6122e444a916af64f36152890.png



f5207eddb84f436fa75476af6d2b20ea.png



92.3.5 FDCAN仲裁阶段和通信阶段波特率配置(重要)
CAN FD中的FD含义就是flexible data,灵活数据通信,且波特率可以和仲裁阶段波特率不同。看下面的图,意思就是红色部分可以是一个波特率,蓝色部分也可以是一个波特率。

9b794eb29eef44b488e6dd3976fa9ecd.png




波特率计算公式,看如下的位时间特性图:

22317685503441f1985e42d006c9167a.png


1bit的CAN FD数据需要时间由Sync_Seg + Pro_Seg + Phase_Seg1 + Phase_Seg2组成。

  仲裁阶段波特率对应的HAL库配置如下:
  1. /* CAN时钟分配设置,一般设置为1即可,全部由PLL配置好,tq = NominalPrescaler x (1/ fdcan_ker_ck) */
  2. hfdcan2.Init.NominalPrescaler = 0x1;  

  3. /* 特别注意这里的Seg1,这里是两个参数之和,对应位时间特性图的 Pro_Seg + Phase_Seg1 */   
  4. hfdcan2.Init.NominalTimeSeg1 = 0x1F;

  5. /* 对应位时间特性图的 Phase_Seg2 */      
  6. hfdcan2.Init.NominalTimeSeg2 = 0x8;     

  7. /* 用于动态调节  Phase_Seg1和 Phase_Seg1,所以不可以比Phase_Seg1和 Phase_Seg1大 */
  8. hfdcan2.Init.NominalSyncJumpWidth = 0x8;  
复制代码

其中:

  1. Sync_Seg是固定值 = 1
  2. Pro_Seg + Phase_Seg1 = DataTimeSeg1
  3. hase_Seg2 = DataTimeSeg2
复制代码

FDCAN时钟是20MHz时,仲裁阶段的波特率就是

FDCAN Freq / (Sync_Seg + Pro_Seg + Phase_Seg1 + Phase_Seg2)

= 20MHz / (1+0x1F + 8)

= 0.5Mbps

  数据阶段波特率对应的HAL库配置如下:
  1. /* CAN时钟分配设置,一般设置为1即可,全部由PLL配置好,tq = NominalPrescaler x (1/ fdcan_ker_ck) */
  2. hfdcan2.Init.DataPrescaler = 0x1;               

  3. /* 特别注意这里的Seg1,这里是两个参数之和,对应位时间特性图的 Pro_Seg + Phase_Seg1 */
  4. hfdcan2.Init.DataTimeSeg1 = 0x5;

  5. /* 对应位时间特性图的 Phase_Seg2 */
  6. hfdcan2.Init.DataTimeSeg2 = 0x4;

  7. /* 用于动态调节  Phase_Seg1和 Phase_Seg1,所以不可以比Phase_Seg1和 Phase_Seg1大 */            
  8. hfdcan2.Init.DataSyncJumpWidth = 0x4;
复制代码

其中:

  1. Sync_Seg是固定值 = 1
  2. Pro_Seg + Phase_Seg1 = DataTimeSeg1
  3. hase_Seg2 = DataTimeSeg2
复制代码

FDCAN时钟是20MHz时,数据阶段的波特率就是

CAN FD Freq / (Sync_Seg + Pro_Seg + Phase_Seg1 + Phase_Seg2)

= 20MHz / (1+5+ 4)

= 2Mbps

STM32H7在400MHz情况下,PLL配置输出20MHz的CAN FD时钟(大家可以使用STM32CubeMX来配置的):

  1. /* 选择PLL2Q作为FDCANx时钟 */
  2. PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_FDCAN;
  3. PeriphClkInitStruct.PLL2.PLL2M = 5;
  4. PeriphClkInitStruct.PLL2.PLL2N = 80;
  5. PeriphClkInitStruct.PLL2.PLL2P = 2;
  6. PeriphClkInitStruct.PLL2.PLL2Q = 20;
  7. PeriphClkInitStruct.PLL2.PLL2R = 2;
  8. PeriphClkInitStruct.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_2;
  9. PeriphClkInitStruct.PLL2.PLL2VCOSEL = RCC_PLL2VCOWIDE;
  10. PeriphClkInitStruct.PLL2.PLL2FRACN = 0;
  11. PeriphClkInitStruct.FdcanClockSelection = RCC_FDCANCLKSOURCE_PLL2;
  12. if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
  13. {
  14.         Error_Handler(__FILE__, __LINE__);
  15. }
复制代码

92.3.6 FDCAN的采样点位置推荐设置为85% - 90%
FDCAN每个bit的时间组成如下:

362ffc5a51b5475eabc16e8e4e8f79a1.png




为了实现更好的稳定性,在满足指定波特率的情况下,让采样点的位置满足85%到90%是推荐的方式。主要用到HAL库的如下两个成员:

  1. hfdcan2.Init.NominalTimeSeg1         
  2. hfdcan2.Init.NominalTimeSeg2   
复制代码

SyncSeg是固定1,也就是:

(SyncSeg + NominalTimeSeg1)/ (SyncSeg + NominalTimeSeg1 + NominalTimeSeg2)满足这个条件。

92.3.7 FDCAN的2560字RAM空间分配问题
关于FDCAN的2560字RAM空间在本教程第90章的第5小节有详细说明。本章配套程序将前1280字分配给FDCAN1,后1280字分配给FDCAN2。

92.3.8 FDCAN过滤器常用的范围过滤器和经典的位屏蔽过滤器
关于FDCAN的过滤器类型在本教程第90章的第6小节有详细说明,我们这里重点了解范围过滤器和经典位屏蔽过滤器。

  范围过滤器
范围过滤器比较简单,也比较好理解,对应的HAL库配置代码如下:

  1. sFilterConfig2.IdType = FDCAN_STANDARD_ID;  
  2. sFilterConfig2.FilterIndex = 0;
  3. sFilterConfig2.FilterType = FDCAN_FILTER_RANGE;         
  4. sFilterConfig2.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
  5. sFilterConfig2.FilterID1 = 0x111;                     
  6. sFilterConfig2.FilterID2 = 0x555;                                                
  7. HAL_FDCAN_ConfigFilter(&hfdcan2, &sFilterConfig2);     
复制代码

FilterType类型配置为FDCAN_FILTER_RANGE表示范围过滤器。

FilterID1 = 0x111和FilterID2 = 0x555表示仅接收 ≥0x111且 ≤ 0x555的ID。

  经典的位屏蔽过滤
对应的HAL库配置代码如下:

  1. sFilterConfig2.IdType = FDCAN_STANDARD_ID;            
  2. sFilterConfig2.FilterIndex = 0;                                                   
  3. sFilterConfig2.FilterType = FDCAN_FILTER_MASK;         
  4. sFilterConfig2.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;  
  5. sFilterConfig2.FilterID1 = 0x222;                       
  6. sFilterConfig2.FilterID2 = 0x7FF;                 
  7. HAL_FDCAN_ConfigFilter(&hfdcan2, &sFilterConfig2);
复制代码

FilterType类型配置为FDCAN_FILTER_MASK表示经典的位屏蔽过滤。

FilterID1 = filter  表示ID。

FilterID2 = mask  表示ID屏蔽位,mask每个bit含义:

0: 表示FilterID1相应bit不关心,该位不用于比较。

1: 表示FilterID1相应bit必须匹配,即接收到的ID位必须与FilterID1的相应位一致。

我们这里FilterID1 = 0x222,FilterID2 = 0x7FF 表示仅接收ID为0x222的FDCAN帧。

92.4 FDCAN驱动代码实现
这里以FDCAN1为例进行说明,FDCAN2的驱动程序在本章配套例子里面也提供了。

92.4.1 FDCAN配置
代码里面对每个成员都进行了详细注释说明:

  1. /*
  2. *********************************************************************************************************
  3. *        函 数 名: bsp_InitCan1
  4. *        功能说明: 初始CAN1
  5. *        形    参: 无
  6. *        返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void bsp_InitCan1(void)
  10. {         
  11.         /*                    位时间特性配置
  12.                 Bit time parameter         | Nominal      |  Data
  13.                 ---------------------------|--------------|----------------
  14.                 fdcan_ker_ck               | 20 MHz       | 20 MHz
  15.                 Time_quantum (tq)          | 50 ns        | 50 ns
  16.                 Synchronization_segment    | 1 tq         | 1 tq
  17.                 Propagation_segment        | 23 tq        | 1 tq
  18.                 Phase_segment_1            | 8 tq         | 4 tq
  19.                 Phase_segment_2            | 8 tq         | 4 tq
  20.                 Synchronization_Jump_width | 8 tq         | 4 tq
  21.                 Bit_length                 | 40 tq = 2us  | 10 tq = 0.5us
  22.                 Bit_rate                   | 0.5 MBit/s   | 2 MBit/s
  23.         */
  24.         hfdcan1.Instance = FDCAN1;                     /* 配置FDCAN1 */            
  25.         hfdcan1.Init.FrameFormat = FDCAN_FRAME_FD_BRS; /* 配置使用FDCAN可变波特率 */  
  26.         hfdcan1.Init.Mode = FDCAN_MODE_NORMAL;         /* 配置使用正常模式 */
  27.         hfdcan1.Init.AutoRetransmission = ENABLE;      /*使能自动重发 */
  28.         hfdcan1.Init.TransmitPause = DISABLE;          /* 配置禁止传输暂停特性 */
  29.         hfdcan1.Init.ProtocolException = ENABLE;       /* 协议异常处理使能 */
  30.         
  31.         /*
  32.                 配置仲裁阶段波特率
  33.                 CAN时钟20MHz时,仲裁阶段的波特率就是
  34.                 CAN FD Freq / (Sync_Seg + Pro_Seg + Phase_Seg1 + Phase_Seg2) = 20MHz / (1+0x1F + 8) = 0.5Mbps        
  35.                
  36.                 其中Sync_Seg是固定值 = 1 , Pro_Seg + Phase_Seg1 = NominalTimeSeg1, Phase_Seg2 = NominalTimeSeg2
  37.         */
  38. /* CAN时钟分配设置,一般设置为1即可,全部由PLL配置好,tq = NominalPrescaler x (1/ fdcan_ker_ck) */
  39.         hfdcan1.Init.NominalPrescaler = 0x01;
  40.         /* 用于动态调节  Phase_Seg1和 Phase_Seg1,所以不可以比Phase_Seg1和 Phase_Seg1大 */
  41.         hfdcan1.Init.NominalSyncJumpWidth = 0x08;
  42. /* 特别注意这里的Seg1,这里是两个参数之和,对应位时间特性图的 Pro_Seg + Phase_Seg1 */
  43.         hfdcan1.Init.NominalTimeSeg1 = 0x1F;         
  44. /* 对应位时间特性图的 Phase_Seg2 */
  45.         hfdcan1.Init.NominalTimeSeg2 = 0x08;     
  46.         /*
  47.                 配置数据阶段波特率
  48.                 CAN时钟20MHz时,数据阶段的波特率就是
  49.                 CAN FD Freq / (Sync_Seg + Pro_Seg + Phase_Seg1 + Phase_Seg2) = 20MHz / (1+5+ 4) = 2Mbps
  50.                
  51.                 其中Sync_Seg是固定值 = 1 , Pro_Seg + Phase_Seg1 = DataTimeSeg1, Phase_Seg2 = DataTimeSeg2
  52.         */
  53. /* CAN时钟分配设置,一般设置为1即可,全部由PLL配置好,tq = NominalPrescaler x (1/ fdcan_ker_ck),
  54. 范围1-32 */
  55.         hfdcan1.Init.DataPrescaler = 0x01;
  56. /* 用于动态调节  Phase_Seg1和 Phase_Seg1,所以不可以比Phase_Seg1和 Phase_Seg1大,范围1-16 */
  57.         hfdcan1.Init.DataSyncJumpWidth = 0x04;  
  58. /* 特别注意这里的Seg1,这里是两个参数之和,对应位时间特性图的 Pro_Seg + Phase_Seg1,范围 */
  59.         hfdcan1.Init.DataTimeSeg1 = 0x05;
  60. /* 对应位时间特性图的 Phase_Seg2 */               
  61.         hfdcan1.Init.DataTimeSeg2 = 0x04;           
  62.         
  63.         
  64.         hfdcan1.Init.MessageRAMOffset = 0;      /* CAN1和CAN2共享2560个字, 这里CAN1分配前1280字 */
  65.         
  66.         
  67.         hfdcan1.Init.StdFiltersNbr = 1;                                 /* 设置标准ID过滤器个数,范围0-128 */      
  68.         hfdcan1.Init.ExtFiltersNbr = 0;                                 /* 设置扩展ID过滤器个数,范围0-64 */   
  69.         hfdcan1.Init.RxFifo0ElmtsNbr = 2;                   /* 设置Rx FIFO0的元素个数,范围0-64 */  
  70.         /* 设置Rx FIFO0中每个元素大小,支持8,12,16,20,24,32,48或者64字节 */   
  71. hfdcan1.Init.RxFifo0ElmtSize = FDCAN_DATA_BYTES_8;
  72.         hfdcan1.Init.RxFifo1ElmtsNbr = 0;                   /* 设置Rx FIFO1的元素个数,范围0-64 */
  73. /* 设置Rx FIFO1中每个元素大小,支持8,12,16,20,24,32,48或者64字节 */        
  74.         hfdcan1.Init.RxFifo1ElmtSize = FDCAN_DATA_BYTES_8;
  75.         hfdcan1.Init.RxBuffersNbr = 0;                      /* 设置Rx Buffer个数,范围0-64 */
  76.         
  77. /* 设置Rx Buffer中每个元素大小,支持8,12,16,20,24,32,48或者64字节 */
  78. hfdcan1.Init.RxBufferSize = 0;                             
  79.         hfdcan1.Init.TxEventsNbr = 0;              /* 设置Tx Event FIFO中元素个数,范围0-32 */        
  80.         hfdcan1.Init.TxBuffersNbr = 0;                 /* 设置Tx Buffer中元素个数,范围0-32 */
  81.         hfdcan1.Init.TxFifoQueueElmtsNbr = 2; /* 设置用于Tx FIFO/Queue的Tx Buffers个数。范围0到32 */
  82.         hfdcan1.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION; /* 设置FIFO模式或者QUEUE队列模式 */
  83. /* 设置Tx Element中的数据域大小,支持8,12,16,20,24,32,48或者64字节 */
  84.         hfdcan1.Init.TxElmtSize = FDCAN_DATA_BYTES_8;         
  85.         HAL_FDCAN_Init(&hfdcan1);
  86.         /*
  87.                 配置过滤器, 过滤器主要用于接收,这里采样屏蔽位模式。
  88.                 FilterID1 = filter
  89.                 FilterID2 = mask
  90.                
  91.                 FilterID2的mask每个bit含义
  92.                 0: 不关心,该位不用于比较;
  93.                 1: 必须匹配,接收到的ID必须与滤波器对应的ID位相一致。
  94.                
  95.                 举例说明:
  96.                 FilterID1 = 0x111
  97.                 FilterID2 = 0x7FF
  98.                 表示仅接收ID为0x111的FDCAN帧。
  99.                
  100.         */
  101.         sFilterConfig1.IdType = FDCAN_STANDARD_ID;              /* 设置标准ID或者扩展ID */
  102.         /* 用于过滤索引,如果是标准ID,范围0到127。如果是扩展ID,范围0到64 */
  103. sFilterConfig1.FilterIndex = 0;                                                   
  104.         sFilterConfig1.FilterType = FDCAN_FILTER_MASK;          /* 过滤器采样屏蔽位模式 */
  105.         sFilterConfig1.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;  /* 如果过滤匹配,将数据保存到Rx FIFO 0 */
  106.         sFilterConfig1.FilterID1 = 0x111;                       /* 屏蔽位模式下,FilterID1是消息ID */
  107.         sFilterConfig1.FilterID2 = 0x7FF;                                         /* 屏蔽位模式下,FilterID2是消息屏蔽位 */
  108.         HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig1);      /* 配置过滤器 */
  109.         
  110.         /* 设置Rx FIFO0的wartermark为1 */
  111.         HAL_FDCAN_ConfigFifoWatermark(&hfdcan1, FDCAN_CFG_RX_FIFO0, 1);
  112.         /* 激活RX FIFO0的watermark通知中断,位开启Tx Buffer中断*/
  113.         HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO0_WATERMARK, 0);
  114.         /* 启动FDCAN */
  115.         HAL_FDCAN_Start(&hfdcan1);        
  116. }
复制代码

92.4.2 FDCAN的GPIO,NVIC等配置
主要配置了FDCAN的GPIO,GPIO时钟,FDCAN时钟和NVIC:

  1. /*
  2. *********************************************************************************************************
  3. *        函 数 名: HAL_FDCAN_MspInit
  4. *        功能说明: 配置CAN gpio
  5. *        形    参: hfdcan
  6. *        返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void HAL_FDCAN_MspInit(FDCAN_HandleTypeDef* hfdcan)
  10. {
  11.         GPIO_InitTypeDef  GPIO_InitStruct;
  12.         RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};

  13.         if (hfdcan == &hfdcan1)
  14.         {
  15.                 /*##-1- 使能外设这个GPIO时钟 #################################*/
  16.                 FDCAN1_TX_GPIO_CLK_ENABLE();
  17.                 FDCAN1_RX_GPIO_CLK_ENABLE();

  18.                 /* 选择PLL2Q作为FDCANx时钟 */
  19.         PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_FDCAN;
  20.         PeriphClkInitStruct.PLL2.PLL2M = 5;
  21.         PeriphClkInitStruct.PLL2.PLL2N = 80;
  22.         PeriphClkInitStruct.PLL2.PLL2P = 2;
  23.         PeriphClkInitStruct.PLL2.PLL2Q = 20;
  24.         PeriphClkInitStruct.PLL2.PLL2R = 2;
  25.         PeriphClkInitStruct.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_2;
  26.         PeriphClkInitStruct.PLL2.PLL2VCOSEL = RCC_PLL2VCOWIDE;
  27.         PeriphClkInitStruct.PLL2.PLL2FRACN = 0;
  28.         PeriphClkInitStruct.FdcanClockSelection = RCC_FDCANCLKSOURCE_PLL2;
  29.         if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
  30.         {
  31.             Error_Handler(__FILE__, __LINE__);
  32.         }

  33.                 __HAL_RCC_FDCAN_CLK_ENABLE();

  34.                 GPIO_InitStruct.Pin       = FDCAN1_TX_PIN;
  35.                 GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
  36.                 GPIO_InitStruct.Pull      = GPIO_PULLUP;
  37.                 GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_HIGH;
  38.                 GPIO_InitStruct.Alternate = FDCAN1_TX_AF;
  39.                 HAL_GPIO_Init(FDCAN1_TX_GPIO_PORT, &GPIO_InitStruct);

  40.                 GPIO_InitStruct.Pin       = FDCAN1_RX_PIN;
  41.                 GPIO_InitStruct.Alternate = FDCAN1_RX_AF;
  42.                 HAL_GPIO_Init(FDCAN1_RX_GPIO_PORT, &GPIO_InitStruct);

  43.                 HAL_NVIC_SetPriority(FDCAN1_IT0_IRQn, 2, 0);
  44.                 HAL_NVIC_SetPriority(FDCAN1_IT1_IRQn, 2, 0);
  45.                 HAL_NVIC_SetPriority(FDCAN_CAL_IRQn, 2, 0);
  46.                 HAL_NVIC_EnableIRQ(FDCAN1_IT0_IRQn);
  47.                 HAL_NVIC_EnableIRQ(FDCAN1_IT1_IRQn);
  48.                 HAL_NVIC_EnableIRQ(FDCAN_CAL_IRQn);
  49.         }
  50. }
复制代码

92.4.3 FDCAN的数据发送
FDCAN每帧数据最大64字节:

  1. /*
  2. *********************************************************************************************************
  3. *        函 数 名: can1_SendPacket
  4. *        功能说明: 发送一包数据
  5. *        形    参:_DataBuf 数据缓冲区
  6. *                          _Len 数据长度, 支持8,12,16,20,24,32,48或者64字节
  7. *        返 回 值: 无
  8. *********************************************************************************************************
  9. */
  10. void can1_SendPacket(uint8_t *_DataBuf, uint8_t _Len)
  11. {               
  12.         FDCAN_TxHeaderTypeDef TxHeader = {0};
  13.         
  14.         /* 配置发送参数 */
  15.         TxHeader.Identifier = 0x222;                              /* 设置接收帧消息的ID */
  16.         TxHeader.IdType = FDCAN_STANDARD_ID;                      /* 标准ID */
  17.         TxHeader.TxFrameType = FDCAN_DATA_FRAME;                 /* 数据帧 */
  18.         TxHeader.DataLength = (uint32_t)_Len << 16;      /* 发送数据长度 */
  19.         TxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE; /* 设置错误状态指示 */
  20.         TxHeader.BitRateSwitch = FDCAN_BRS_ON;           /* 开启可变波特率 */
  21.         TxHeader.FDFormat = FDCAN_FD_CAN;                /* FDCAN格式 */
  22.         TxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;/* 用于发送事件FIFO控制, 不存储 */
  23.         TxHeader.MessageMarker = 0;     /* 用于复制到TX EVENT FIFO的消息Maker来识别消息状态,范围0到0xFF */
  24.         
  25.     /* 添加数据到TX FIFO */
  26.     HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader, _DataBuf);
  27. }
复制代码

这里特别注意两点:

  发送的数据幅值给DataLength,需要右移16bit。
  要发送的数据会被复制到TX FIFO硬件缓存中。
92.4.4 FDCAN的中断服务程序处理
这里FDCAN中断主要用于数据接收:

  1. /*
  2. *********************************************************************************************************
  3. *        函 数 名: FDCAN1_IT0_IRQHandler
  4. *        功能说明: CAN中断服务程序
  5. *        形    参: hfdcan
  6. *        返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void FDCAN1_IT0_IRQHandler(void)
  10. {
  11.         HAL_FDCAN_IRQHandler(&hfdcan1);
  12. }

  13. void FDCAN1_IT1_IRQHandler(void)
  14. {
  15.         HAL_FDCAN_IRQHandler(&hfdcan1);
  16. }

  17. void FDCAN_CAL_IRQHandler(void)
  18. {
  19.         HAL_FDCAN_IRQHandler(&hfdcan1);
  20. }
  21. /*
  22. *********************************************************************************************************
  23. *        函 数 名: HAL_FDCAN_RxFifo0Callback
  24. *        功能说明: CAN中断服务程序-回调函数
  25. *        形    参: hfdcan
  26. *        返 回 值: 无
  27. *********************************************************************************************************
  28. */
  29. void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs)
  30. {
  31.         if (hfdcan == &hfdcan1)
  32.         {
  33.                 if ((RxFifo0ITs & FDCAN_IT_RX_FIFO0_WATERMARK) != RESET)
  34.                 {
  35.                         /* 从RX FIFO0读取数据 */
  36.                         HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &g_Can1RxHeader, g_Can1RxData);

  37.                         /* 激活Rx FIFO0 watermark notification */
  38.                         HAL_FDCAN_ActivateNotification(hfdcan, FDCAN_IT_RX_FIFO0_WATERMARK, 0);
  39.                         
  40.                         if (g_Can1RxHeader.Identifier == 0x111 && g_Can1RxHeader.IdType == FDCAN_STANDARD_ID)
  41.                         {
  42.                                 bsp_PutMsg(MSG_CAN1_RX, 0);        /* 发消息收到数据包,结果在g_Can1RxHeader, g_Can1RxData */
  43.                         }
  44.                 }
  45.         }
  46. }
复制代码

92.5 双FDCAN测试的接线和跳线帽说明
大家可以直接使用板载的两个FDCAN进行通信测试。跳线帽设置如下:


9bcbfda8aef349d88bb0711c0c6ca60c.png



双CAN FD接线:


463a7f03e85a4a6296f35c1022dfebf8.png



92.6 开发板和H7-TOOL的FDCAN助手测试
H7-TOOL的CAN/CANFD助手支持以太网,USB和WiFi三种通信方式。大家可以用使用本章配套的双FDCAN例子与H7-TOOL的FDCAN助手进行通信。

92.6.1 接线说明
H7-TOOL和STM32H7的FDCAN2进行通信,注意是CANH接CANH,  CANL接CANL

a30fab073b3346c78a7e944aab3df4f3.png




92.6.2 启动H7-TOOL上位机
打开最新版的H7-TOOL上位机,使用USB,以太网或者WiFi方式均支持。选择左侧的CAN助手 -> 启动CAN助手:


08641fc534c14af3be6d5fce44a95f7e.png



参数介绍:

(1)帧类型 :0 - 经典CAN,最大收发8字节

              1 - CAN FD ,   仲裁段和数据点波特率相同,最大收发64字节

              2 - CAN FD 双波特率,仲裁段和数据点波特率不同,最大收发64字节

(2)仲裁段和数据段波特率 :除了提供常用的波特率,还提供了用户可自定义配置模式,需要用户选择如下选项,这样就可以配置右侧的“CAN波特率高级配置”


750b9edd448949f995d29ffe54e2da55.png



我们这里选择CAN FD双波特率,仲裁段设置为500Kbps,数据段设置为2Mbps,最大数据设置为8字节, CAN解码器设置为none_decoder.lua


3443de72595244f4a46ccabef5fbb2fa.png



92.6.3 H7-TOOL的FDCAN接收数据测试
第2步设置完毕参数后,按下几次STM32H7板子的 K1, 可以看到H7-TOOL上位机接收到了数据:


                            b003090ce5604f91ad2869ba4f1db3e4.png



92.6.4 H7-TOOL的FDCAN发送数据测试
如果有多种发送格式,用户可以在快捷面板里面发送,这个面板也支持用户加载专门的配置文件,不用每次都设置。


409f4cee477c479ab66d18e903eb1813.png



如下的配置,点击发送,可以控制STM32H7板子的LED1点亮:

                   6ed507e41f9c462eab8f5166309c44ee.png



如下的配置,点击发送,可以控制STM32H7板子的LED1熄灭:


cc499dff50f44b4db40c20782d8151f1.png



92.7 实验例程设计框架
通过程序设计框架,让大家先对配套例程有一个全面的认识,然后再理解细节,本次实验例程的设计框架如下:


f57cd06a08ab4b0196e4cd44db1812ed.png



  第1阶段,上电启动阶段:

这部分在第14章进行了详细说明。
  第2阶段,进入main函数:

第1步,硬件初始化,主要是MPU,Cache,HAL库,系统时钟,滴答定时器,LED和串口。
第2步,FDCAN应用程序设计部分。
92.8 实验例程说明(MDK)
配套例子:

V7-069_双CAN FD之间通信

实验目的:

学习双CAN FD的实现。
实验内容:

K1按键按下,CAN2发送消息给CAN1,蜂鸣器鸣响4次。
K2按键按下,CAN1发送消息给CAN2,点亮LED1。
K2按键按下,CAN1发送消息给CAN2,熄灭LED1。
注意事项:

接线方式是CAN1的CANL1和CAN2的CANL2连接,CAN1的CANH2和CAN2的CANH2连接,具体接线看本工程Doc文件中的截图。
启用CAN1,需要将V7主板上的J12跳线帽短接PA11,J13跳线帽短接PA12。
启用CNA2,硬件无需跳线,要禁止使用以太网功能(有引脚复用)。

18718e1e752a437db16e4e3b2a0ef70b.png



17067fb4c7d44039a80a95a981396c35.png


上电后串口打印的信息:

波特率 115200,数据位 8,奇偶校验位无,停止位 1


c635be44eb2f417d900def2f58fa1db8.png



程序设计:

  系统栈大小分配:


db5fd2b55b3b40dea6bc888327f1890e.png



  RAM空间用的AXI SRAM:


c7d78e321fb7444abb6012f2b4e93d7d.png



  硬件外设初始化

硬件外设的初始化是在 bsp.c 文件实现:

  1. /*
  2. *********************************************************************************************************
  3. *        函 数 名: bsp_Init
  4. *        功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
  5. *        形    参:无
  6. *        返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void bsp_Init(void)
  10. {
  11.     /* 配置MPU */
  12.         MPU_Config();
  13.         
  14.         /* 使能L1 Cache */
  15.         CPU_CACHE_Enable();

  16.         /*
  17.        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
  18.            - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
  19.            - 设置NVIV优先级分组为4。
  20.          */
  21.         HAL_Init();

  22.         /*
  23.        配置系统时钟到400MHz
  24.        - 切换使用HSE。
  25.        - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
  26.     */
  27.         SystemClock_Config();

  28.         /*
  29.            Event Recorder:
  30.            - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
  31.            - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第xx章
  32.         */        
  33. #if Enable_EventRecorder == 1  
  34.         /* 初始化EventRecorder并开启 */
  35.         EventRecorderInitialize(EventRecordAll, 1U);
  36.         EventRecorderStart();
  37. #endif
  38.         
  39.         bsp_InitKey();            /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
  40.         bsp_InitTimer();          /* 初始化滴答定时器 */
  41.         bsp_InitUart();        /* 初始化串口 */
  42.         bsp_InitExtIO();        /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */        
  43.         bsp_InitLed();            /* 初始化LED */        
  44. }
复制代码

  MPU配置和Cache配置:

数据Cache和指令Cache都开启。配置了AXI SRAM区和FMC的扩展IO区。

  1. /*
  2. *********************************************************************************************************
  3. *        函 数 名: MPU_Config
  4. *        功能说明: 配置MPU
  5. *        形    参: 无
  6. *        返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void MPU_Config( void )
  10. {
  11.         MPU_Region_InitTypeDef MPU_InitStruct;

  12.         /* 禁止 MPU */
  13.         HAL_MPU_Disable();

  14. #if 0
  15.            /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
  16.         MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
  17.         MPU_InitStruct.BaseAddress      = 0x24000000;
  18.         MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
  19.         MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
  20.         MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
  21.         MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
  22.         MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
  23.         MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
  24.         MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
  25.         MPU_InitStruct.SubRegionDisable = 0x00;
  26.         MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

  27.         HAL_MPU_ConfigRegion(&MPU_InitStruct);

  28. #else
  29.      /* 当前是采用下面的配置 */
  30.         /* 配置AXI SRAM的MPU属性为NORMAL, NO Read allocate,NO Write allocate */
  31.         MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
  32.         MPU_InitStruct.BaseAddress      = 0x24000000;
  33.         MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
  34.         MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
  35.         MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
  36.         MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
  37.         MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
  38.         MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
  39.         MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
  40.         MPU_InitStruct.SubRegionDisable = 0x00;
  41.         MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

  42.         HAL_MPU_ConfigRegion(&MPU_InitStruct);
  43. #endif
  44.         /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
  45.         MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
  46.         MPU_InitStruct.BaseAddress      = 0x60000000;
  47.         MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;        
  48.         MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
  49.         MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
  50.         MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
  51.         MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
  52.         MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
  53.         MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
  54.         MPU_InitStruct.SubRegionDisable = 0x00;
  55.         MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
  56.         
  57.         HAL_MPU_ConfigRegion(&MPU_InitStruct);

  58.         /*使能 MPU */
  59.         HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
  60. }

  61. /*
  62. *********************************************************************************************************
  63. *        函 数 名: CPU_CACHE_Enable
  64. *        功能说明: 使能L1 Cache
  65. *        形    参: 无
  66. *        返 回 值: 无
  67. *********************************************************************************************************
  68. */
  69. static void CPU_CACHE_Enable(void)
  70. {
  71.         /* 使能 I-Cache */
  72.         SCB_EnableICache();

  73.         /* 使能 D-Cache */
  74.         SCB_EnableDCache();
  75. }
复制代码

  主功能:

主程序实现如下操作:

  上电启动了一个软件定时器,每100ms翻转一次LED2。
  K1按键按下,CAN2发送消息给CAN1,蜂鸣器鸣响4次。
  K2按键按下,CAN1发送消息给CAN2,点亮LED1。
  K2按键按下,CAN1发送消息给CAN2,熄灭LED1。
  1. /*
  2. *********************************************************************************************************
  3. *        函 数 名: DemoCANFD
  4. *        功能说明: CAN测试
  5. *        形    参: 无
  6. *        返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void DemoCANFD(void)
  10. {
  11.         uint8_t ucKeyCode;                /* 按键代码 */

  12.         can_Init();         /* 初始化CAN */
  13.         
  14.         bsp_StartAutoTimer(0, 500);        /* 启动1个500ms的自动重装的定时器 */
  15.         
  16.         while (1)
  17.         {
  18.         {
  19.                         MSG_T msg;

  20.                         if (bsp_GetMsg(&msg))
  21.                         {
  22.                                 switch (msg.MsgCode)
  23.                                 {
  24.                                         case MSG_CAN1_RX:                /* 接收到CAN设备的应答 */
  25.                         printf("CAN1接收到消息\r\n");
  26.                                         can1_Analyze();
  27.                                                 break;        
  28.                                        
  29.                                         case MSG_CAN2_RX:           /* 接收到CAN设备的应答 */
  30.                          printf("CAN2接收到消息\r\n");
  31.                                                 can2_Analyze();
  32.                                                 break;                                       
  33.                                 }
  34.                         }
  35.                 }
  36.                
  37.                 /* 判断定时器超时时间 */
  38.                 if (bsp_CheckTimer(0))        
  39.                 {            
  40.                         /* 每隔500ms 进来一次 */  
  41.                         bsp_LedToggle(2);
  42.                 }

  43.         /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */
  44.                 ucKeyCode = bsp_GetKey();        /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
  45.                 if (ucKeyCode != KEY_NONE)
  46.                 {
  47.                         switch (ucKeyCode)
  48.                         {
  49.                                 case KEY_DOWN_K1:                        /* K1键按下 */
  50.                                         printf("CAN2发送消息给CAN1,蜂鸣器鸣响4次\r\n");
  51.                     can_BeepCtrl(1, 4);
  52.                                         break;

  53.                                 case KEY_DOWN_K2:                        /* K2键按下 */
  54.                                         printf("CAN1发送消息给CAN2,点亮LED1\r\n");
  55.                     can_LedOn(1, 1);
  56.                                         break;


  57.                                 case KEY_DOWN_K3:                /* K3键按下 */
  58.                                         printf("CAN1发送消息给CAN2,熄灭LED1\r\n");
  59.                     can_LedOff(1, 1);
  60.                                         break;

  61.                                 default:
  62.                                         /* 其它的键值不处理 */
  63.                                         break;
  64.                         }
  65.                
  66.                 }
  67.         }
  68. }
复制代码

92.9 实验例程说明(IAR)
配套例子:

V7-069_双CAN FD之间通信

实验目的:

学习双CAN FD的实现。
实验内容:

K1按键按下,CAN2发送消息给CAN1,蜂鸣器鸣响4次。
K2按键按下,CAN1发送消息给CAN2,点亮LED1。
K2按键按下,CAN1发送消息给CAN2,熄灭LED1。
注意事项:

接线方式是CAN1的CANL1和CAN2的CANL2连接,CAN1的CANH2和CAN2的CANH2连接,具体接线看本工程Doc文件中的截图。
启用CAN1,需要将V7主板上的J12跳线帽短接PA11,J13跳线帽短接PA12。
启用CNA2,硬件无需跳线,要禁止使用以太网功能(有引脚复用)。



9316089d4b744e4e945eb5e9a502e38e.png



上电后串口打印的信息:

波特率 115200,数据位 8,奇偶校验位无,停止位 1

4204b6053a2048529babc9aa6831ea0b.png




程序设计:

  系统栈大小分配:


c3c33295de6247208e94e06ce759bad6.png



  RAM空间用的AXI SRAM:


bb9f74b277df4fdaa0d797f1fe3dc453.png



  硬件外设初始化

硬件外设的初始化是在 bsp.c 文件实现:

  1. /*
  2. *********************************************************************************************************
  3. *        函 数 名: bsp_Init
  4. *        功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
  5. *        形    参:无
  6. *        返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void bsp_Init(void)
  10. {
  11.     /* 配置MPU */
  12.         MPU_Config();
  13.         
  14.         /* 使能L1 Cache */
  15.         CPU_CACHE_Enable();

  16.         /*
  17.        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
  18.            - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
  19.            - 设置NVIV优先级分组为4。
  20.          */
  21.         HAL_Init();

  22.         /*
  23.        配置系统时钟到400MHz
  24.        - 切换使用HSE。
  25.        - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
  26.     */
  27.         SystemClock_Config();

  28.         /*
  29.            Event Recorder:
  30.            - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
  31.            - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第xx章
  32.         */        
  33. #if Enable_EventRecorder == 1  
  34.         /* 初始化EventRecorder并开启 */
  35.         EventRecorderInitialize(EventRecordAll, 1U);
  36.         EventRecorderStart();
  37. #endif
  38.         
  39.         bsp_InitKey();            /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
  40.         bsp_InitTimer();          /* 初始化滴答定时器 */
  41.         bsp_InitUart();        /* 初始化串口 */
  42.         bsp_InitExtIO();        /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */        
  43.         bsp_InitLed();            /* 初始化LED */        
  44. }
复制代码

  MPU配置和Cache配置:

数据Cache和指令Cache都开启。配置了AXI SRAM区和FMC的扩展IO区。

  1. /*
  2. *********************************************************************************************************
  3. *        函 数 名: MPU_Config
  4. *        功能说明: 配置MPU
  5. *        形    参: 无
  6. *        返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void MPU_Config( void )
  10. {
  11.         MPU_Region_InitTypeDef MPU_InitStruct;

  12.         /* 禁止 MPU */
  13.         HAL_MPU_Disable();

  14. #if 0
  15.            /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
  16.         MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
  17.         MPU_InitStruct.BaseAddress      = 0x24000000;
  18.         MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
  19.         MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
  20.         MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
  21.         MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
  22.         MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
  23.         MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
  24.         MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
  25.         MPU_InitStruct.SubRegionDisable = 0x00;
  26.         MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

  27.         HAL_MPU_ConfigRegion(&MPU_InitStruct);

  28. #else
  29.      /* 当前是采用下面的配置 */
  30.         /* 配置AXI SRAM的MPU属性为NORMAL, NO Read allocate,NO Write allocate */
  31.         MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
  32.         MPU_InitStruct.BaseAddress      = 0x24000000;
  33.         MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
  34.         MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
  35.         MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
  36.         MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
  37.         MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
  38.         MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
  39.         MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
  40.         MPU_InitStruct.SubRegionDisable = 0x00;
  41.         MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

  42.         HAL_MPU_ConfigRegion(&MPU_InitStruct);
  43. #endif
  44.         /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
  45.         MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
  46.         MPU_InitStruct.BaseAddress      = 0x60000000;
  47.         MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;        
  48.         MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
  49.         MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
  50.         MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
  51.         MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
  52.         MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
  53.         MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
  54.         MPU_InitStruct.SubRegionDisable = 0x00;
  55.         MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
  56.         
  57.         HAL_MPU_ConfigRegion(&MPU_InitStruct);

  58.         /*使能 MPU */
  59.         HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
  60. }

  61. /*
  62. *********************************************************************************************************
  63. *        函 数 名: CPU_CACHE_Enable
  64. *        功能说明: 使能L1 Cache
  65. *        形    参: 无
  66. *        返 回 值: 无
  67. *********************************************************************************************************
  68. */
  69. static void CPU_CACHE_Enable(void)
  70. {
  71.         /* 使能 I-Cache */
  72.         SCB_EnableICache();

  73.         /* 使能 D-Cache */
  74.         SCB_EnableDCache();
  75. }
复制代码

主功能:

主程序实现如下操作:

  上电启动了一个软件定时器,每100ms翻转一次LED2。
  K1按键按下,CAN2发送消息给CAN1,蜂鸣器鸣响4次。
  K2按键按下,CAN1发送消息给CAN2,点亮LED1。
  K2按键按下,CAN1发送消息给CAN2,熄灭LED1。
  1. /*
  2. *********************************************************************************************************
  3. *        函 数 名: DemoCANFD
  4. *        功能说明: CAN测试
  5. *        形    参: 无
  6. *        返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void DemoCANFD(void)
  10. {
  11.         uint8_t ucKeyCode;                /* 按键代码 */

  12.         can_Init();         /* 初始化CAN */
  13.         
  14.         bsp_StartAutoTimer(0, 500);        /* 启动1个500ms的自动重装的定时器 */
  15.         
  16.         while (1)
  17.         {
  18.         {
  19.                         MSG_T msg;

  20.                         if (bsp_GetMsg(&msg))
  21.                         {
  22.                                 switch (msg.MsgCode)
  23.                                 {
  24.                                         case MSG_CAN1_RX:                /* 接收到CAN设备的应答 */
  25.                         printf("CAN1接收到消息\r\n");
  26.                                         can1_Analyze();
  27.                                                 break;        
  28.                                        
  29.                                         case MSG_CAN2_RX:           /* 接收到CAN设备的应答 */
  30.                          printf("CAN2接收到消息\r\n");
  31.                                                 can2_Analyze();
  32.                                                 break;                                       
  33.                                 }
  34.                         }
  35.                 }
  36.                
  37.                 /* 判断定时器超时时间 */
  38.                 if (bsp_CheckTimer(0))        
  39.                 {            
  40.                         /* 每隔500ms 进来一次 */  
  41.                         bsp_LedToggle(2);
  42.                 }

  43.         /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */
  44.                 ucKeyCode = bsp_GetKey();        /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
  45.                 if (ucKeyCode != KEY_NONE)
  46.                 {
  47.                         switch (ucKeyCode)
  48.                         {
  49.                                 case KEY_DOWN_K1:                        /* K1键按下 */
  50.                                         printf("CAN2发送消息给CAN1,蜂鸣器鸣响4次\r\n");
  51.                     can_BeepCtrl(1, 4);
  52.                                         break;

  53.                                 case KEY_DOWN_K2:                        /* K2键按下 */
  54.                                         printf("CAN1发送消息给CAN2,点亮LED1\r\n");
  55.                     can_LedOn(1, 1);
  56.                                         break;


  57.                                 case KEY_DOWN_K3:                /* K3键按下 */
  58.                                         printf("CAN1发送消息给CAN2,熄灭LED1\r\n");
  59.                     can_LedOff(1, 1);
  60.                                         break;

  61.                                 default:
  62.                                         /* 其它的键值不处理 */
  63.                                         break;
  64.                         }
  65.                
  66.                 }
  67.         }
  68. }
复制代码

92.10   总结
本章节就为大家讲解这么多,FDCAN涉及到的知识点非常多,建议先将例子熟练运用,然后再深入了解。


43dbb3cf8b9c445d8f8a5694f8615987.png
收藏 评论0 发布时间:2021-11-6 23:52

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版