[单片机芯片] 体验CH32V307/317强大的CAN总线

[复制链接]
77|0
abner_ma 发表于 2025-10-30 21:15 | 显示全部楼层 |阅读模式

  Controller Area Network 控制器局域网络 是ISO国际标准化的串行通信协议。在汽车产业中,出于对安全性、舒适性、方便性、低公害、低成本的要求,各种各样的电子控制系统被开发了出来。由于这些系统之间通信所用的数据类型及对可靠性的要求不尽相同,由多条总线构成的情况很多,线束的数量也随之增加。为适应“减少线束的数量”、“通过多个LAN,进行大量数据的高速通信”的需要,1986 年德国电气商博世公司开发出面向汽车的CAN 通信协议。此后,CAN 通过ISO11898及ISO11519进行了标准化,在欧洲已是汽车网络的标准协议。CAN 的高性能和可靠性已被认同,并被广泛地应用于工业自动化、船舶、医疗设备、工业设备等方面。现场总线是当今自动化领域技术发展的热点之一,被誉为自动化领域的计算机局域网。它的出现为分布式控制系统实现各节点之间实时、可靠的数据通信提供了强有力的技术支持。近年来,它具有的高可靠性和良好的错误检测能力受到重视,被广泛应用于汽车计算机控制系统和环境温度恶劣、电磁辐射强及振动大的工业环境。


   CAN信号并不是低电平表示0高电平表示1,而是当两根数据线在同一时刻,如果有电压差表示0无电压差表示1。
Differential signal 差分信号:
3.png
5.png
相对于单信号线传输的方式,使用差分信号传输具有如下优点:
   1. 抗干扰能力强:当外界存在噪声干扰时,几乎会同时耦合到两条信号线上,而接收端只关心两个信号的差值,所以外界的共模噪声可以被完全抵消。
   2. 有效抑制电磁干扰:同样的道理,由于两根信号的极性相反,他们对外辐射的电磁场可以相互抵消,耦合的越紧密,泄放到外界的电磁能量越少。
   3. 时序定位精确:由于差分信号的开关变化是位于两个信号的交点,而不像普通单端信号依靠高低两个阈值电压判断,因而受工艺,温度的影响小,能降低时序上的误差,同时也更适合于低幅度信号的电路

   CH32V307 /317作为沁恒(WCH)高性能 RISCV 架构 MCU,其 CAN 外设具备 多通道、高兼容性、灵活滤波、低功耗 四大核心特点,同时支持 CAN 2.0A/B 协议. CAN控制器局域网是一种用于串行数据通信的高性能通信协议。CAN 控制器提供了一个完整的 CAN 协议实现方案,支持 CAN 协议 2.0A 和 2.0B。CAN 控制器可以用来构建强大的局域网来实现安全的分布式实时控制,以较小的 CPU 负荷来处理大量的数据报文。
1.png
2.png
   


一、核心硬件特性
1. 多通道与协议兼容性
双 CAN 控制器:内置 2 个独立 CAN 通道(CAN1、CAN2),支持同时与两条 CAN 总线通信,或实现主从备份、不同速率总线隔离(如一条 500Kbps 工业总线 + 一条 250Kbps 设备总线)。
协议支持:完全兼容 CAN 2.0A(标准帧,11 位 ID)和 CAN 2.0B(扩展帧,29 位 ID)协议,支持数据帧(最长 8 字节数据)和远程帧,满足绝大多数 CAN 通信场景。
2. 灵活的位时序与速率
宽速率范围:通信速率可配置为 10Kbps ~ 1Mbps,通过调整 同步段(SJW)、时间段 1(BS1)、时间段 2(BS2)、分频系数(BRP) 实现精准适配,支持工业常用的 125Kbps、250Kbps、500Kbps 速率。
时钟适配:CAN 外设时钟源自 APB1 总线(最高 108MHz),通过分频系数(BRP 范围 1~1024)灵活匹配不同总线时钟,无需额外外部时钟。

3. 强大的滤波机制
硬件滤波能力:每个 CAN 通道支持 14 个独立滤波组,每个滤波组可配置为 32 位或 16 位模式,支持两种滤波方式:
   掩码模式:通过“ID + 掩码”筛选符合范围的帧(如仅匹配 ID 高 11 位,忽略低 3 位)。
   列表模式:仅接收列表中明确指定的 ID 帧(如只接收 ID 为 0x317、0x318 的帧)。
软件滤波扩展:支持通过自定义逻辑扩展滤波功能(如你提供的代码中 `USE_SOFT_FILTER` 宏),可实现更复杂的滤波规则(如多 ID 组合、数据段筛选)。
二、可靠性与错误处理
1. 完善的错误检测与管理
错误检测:支持检测 CAN 总线的 位错误、填充错误、CRC 错误、形式错误、应答错误,并记录错误计数器(发送错误计数器 TEC、接收错误计数器 REC)。
错误处理:
   支持自动离线管理(ABOM):当错误计数器超限(TEC > 127)时,自动进入离线状态,避免影响总线。
   支持自动唤醒(AWUM):离线后可通过总线活动自动唤醒,恢复通信。
   支持禁止自动重发(NART):配置为禁止后,发送失败时不重发,适合对实时性要求高的场景。
2. 抗干扰设计
总线引脚配置:CAN_RX 引脚支持上拉输入模式(如代码中 PB8 配置为 GPIO_Mode_IPU),可减少外部噪声干扰。
错误限制:通过“总线关闭”“错误被动”等状态机,限制故障节点对总线的影响,保证总线整体稳定性。
三、低功耗与实时性优化
1. 低功耗模式支持
支持在 MCU 进入 睡眠模式、停机模式 时,CAN 外设可配置为唤醒源:当总线有数据时,触发中断唤醒 MCU,适合电池供电的低功耗设备(如远程传感器)。
2. 高效的中断与 FIFO
多中断源:支持 FIFO 消息 pending、发送完成、错误警告等多种中断,可精准触发 CPU 处理,减少轮询开销。
双接收 FIFO:每个 CAN 通道配备 2 个接收 FIFO(FIFO0、FIFO1),每个 FIFO 深度为 3 帧,可暂存接收数据,避免数据丢失(尤其在 CPU 处理其他任务时)。
发送邮箱:配备 3 个发送邮箱,支持连续发送多帧数据,无需等待前一帧发送完成,提升发送效率。

四、易用性与开发支持
1. 丰富的开发资源
标准库支持:提供完整的 CAN 外设驱动库(如代码中的 `CAN_Init`、`CAN_FilterInit` 等函数),无需直接操作寄存器,降低开发难度。
示例代码:官方提供多种 CAN 通信示例(如正常收发、回环测试、软件滤波),可直接基于示例修改(如你提供的代码即为官方典型示例)。
2. 调试便利性
支持通过 DBGMCU 模块读取芯片 ID、配置调试模式,方便定位通信故障(如代码中 `DBGMCU_GetCHIPID()` 打印芯片 ID)。
部分型号支持 CAN 总线调试工具(如 CANoe、USBCAN)直接连接,实时监控总线数据,快速排查问题。
    程序设计
  1. void CAN_Mode_Init( u8 tsjw, u8 tbs2, u8 tbs1, u16 brp, u8 mode )
  2. {
  3.          GPIO_InitTypeDef GPIO_InitSturcture={0};
  4.          CAN_InitTypeDef CAN_InitSturcture={0};
  5.          CAN_FilterInitTypeDef CAN_FilterInitSturcture={0};
  6.          
  7.          RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB, ENABLE );
  8.          RCC_APB1PeriphClockCmd( RCC_APB1Periph_CAN1, ENABLE );       
  9.          
  10.          GPIO_PinRemapConfig( GPIO_Remap1_CAN1, ENABLE);       
  11.          
  12.          GPIO_InitSturcture.GPIO_Pin = GPIO_Pin_9;
  13.          GPIO_InitSturcture.GPIO_Mode = GPIO_Mode_AF_PP;               
  14.          GPIO_InitSturcture.GPIO_Speed = GPIO_Speed_50MHz;
  15.          GPIO_Init( GPIOB, &GPIO_InitSturcture);
  16.          
  17.          GPIO_InitSturcture.GPIO_Pin = GPIO_Pin_8;
  18.          GPIO_InitSturcture.GPIO_Mode = GPIO_Mode_IPU;       
  19.          GPIO_Init( GPIOB, &GPIO_InitSturcture);
  20.          
  21.          CAN_InitSturcture.CAN_TTCM = DISABLE;               
  22.          CAN_InitSturcture.CAN_ABOM = DISABLE;               
  23.          CAN_InitSturcture.CAN_AWUM = DISABLE;               
  24.          CAN_InitSturcture.CAN_NART = ENABLE;               
  25.          CAN_InitSturcture.CAN_RFLM = DISABLE;               
  26.          CAN_InitSturcture.CAN_TXFP = DISABLE;
  27.          CAN_InitSturcture.CAN_Mode = mode;
  28.          CAN_InitSturcture.CAN_SJW = tsjw;               
  29.          CAN_InitSturcture.CAN_BS1 = tbs1;               
  30.          CAN_InitSturcture.CAN_BS2 = tbs2;               
  31.          CAN_InitSturcture.CAN_Prescaler = brp;               
  32.          CAN_Init( CAN1, &CAN_InitSturcture );
  33.          
  34.          CAN_FilterInitSturcture.CAN_FilterNumber = 0;               

  35. #if (Frame_Format == Standard_Frame)
  36. /* identifier/mask mode, One 32-bit filter, StdId: 0x317 */
  37.          CAN_FilterInitSturcture.CAN_FilterMode = CAN_FilterMode_IdMask;         
  38.          CAN_FilterInitSturcture.CAN_FilterScale = CAN_FilterScale_32bit;
  39.          CAN_FilterInitSturcture.CAN_FilterIdHigh = 0x62E0;         
  40.          CAN_FilterInitSturcture.CAN_FilterIdLow = 0;
  41.          CAN_FilterInitSturcture.CAN_FilterMaskIdHigh = 0xFFE0;         
  42.          CAN_FilterInitSturcture.CAN_FilterMaskIdLow = 0x0006;         
  43.          
  44. #ifndef USE_SOFT_FILTER
  45. /* identifier/mask mode, Two 16-bit filters, StdId: 0x317,0x316 */
  46. //        CAN_FilterInitSturcture.CAN_FilterMode = CAN_FilterMode_IdMask;       
  47. //        CAN_FilterInitSturcture.CAN_FilterScale = CAN_FilterScale_16bit;       
  48. //        CAN_FilterInitSturcture.CAN_FilterIdHigh = 0x62E0;       
  49. //        CAN_FilterInitSturcture.CAN_FilterIdLow = 0xFFF8;               
  50. //        CAN_FilterInitSturcture.CAN_FilterMaskIdHigh = 0x62C0;        
  51. //        CAN_FilterInitSturcture.CAN_FilterMaskIdLow = 0xFFF8;
  52. #endif               
  53.          
  54. /* identifier list mode, One 32-bit filter, StdId: 0x317,0x316 */
  55. //        CAN_FilterInitSturcture.CAN_FilterMode = CAN_FilterMode_IdList;       
  56. //        CAN_FilterInitSturcture.CAN_FilterScale = CAN_FilterScale_32bit;       
  57. //        CAN_FilterInitSturcture.CAN_FilterIdHigh = 0x62E0;       
  58. //        CAN_FilterInitSturcture.CAN_FilterIdLow = 0;
  59. //        CAN_FilterInitSturcture.CAN_FilterMaskIdHigh = 0x62C0;        
  60. //        CAN_FilterInitSturcture.CAN_FilterMaskIdLow = 0;       

  61. #ifndef USE_SOFT_FILTER
  62. /* identifier list mode, Two 16-bit filters, StdId: 0x317,0x316,0x315,0x314 */
  63. //        CAN_FilterInitSturcture.CAN_FilterMode = CAN_FilterMode_IdList;       
  64. //        CAN_FilterInitSturcture.CAN_FilterScale = CAN_FilterScale_16bit;       
  65. //        CAN_FilterInitSturcture.CAN_FilterIdHigh = 0x62E0;       
  66. //        CAN_FilterInitSturcture.CAN_FilterIdLow = 0x62C0;       
  67. //        CAN_FilterInitSturcture.CAN_FilterMaskIdHigh = 0x62A0;        
  68. //        CAN_FilterInitSturcture.CAN_FilterMaskIdLow = 0x6280;       
  69. #endif               

  70. #elif (Frame_Format == Extended_Frame)
  71. /* identifier/mask mode, One 32-bit filter, ExtId: 0x12124567 */
  72.          CAN_FilterInitSturcture.CAN_FilterMode = CAN_FilterMode_IdMask;               
  73.          CAN_FilterInitSturcture.CAN_FilterScale = CAN_FilterScale_32bit;       
  74.          CAN_FilterInitSturcture.CAN_FilterIdHigh = 0x9092;       
  75.          CAN_FilterInitSturcture.CAN_FilterIdLow = 0x2B3C;       
  76.          CAN_FilterInitSturcture.CAN_FilterMaskIdHigh = 0xFFFF;        
  77.          CAN_FilterInitSturcture.CAN_FilterMaskIdLow = 0xFFFE;       
  78.          
  79. #endif

  80.          CAN_FilterInitSturcture.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;
  81.          CAN_FilterInitSturcture.CAN_FilterActivation = ENABLE;

  82. #ifdef USE_INTERRUPT
  83.          CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE);
  84.          NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
  85. #endif

  86. #ifdef USE_SOFT_FILTER
  87.         (*(__IO uint32_t *)(0x40006600)) |= 0x1;        
  88.         (*(__IO uint32_t *)(0x4000660C)) |= 0x3;       
  89.         (*(__IO uint32_t *)(0x40006640)) = 0;       
  90.         (*(__IO uint32_t *)(0x40006644)) = 0;       
  91.         (*(__IO uint32_t *)(0x4000661C)) |= 0x3;       
  92.         (*(__IO uint32_t *)(0x40006600)) &= ~0x1;                
  93.          CAN_SoftFilterInit( &CAN_FilterInitSturcture );
  94. #else
  95.          CAN_FilterInit( &CAN_FilterInitSturcture );
  96. #endif // USE_SOFT_FILTER       
  97.          
  98. }
    该函数是 CAN 通信的基础,完成 GPIO、CAN 外设、滤波规则的初始化,核心步骤如下:
   1..时钟使能:开启 GPIOB(CAN 引脚)、AFIO(引脚重映射)、CAN1 的时钟。
   2. GPIO 配置:
    PB9(CAN_TX):推挽复用输出模式,速率 50MHz。
    PB8(CAN_RX):上拉输入模式,防止信号干扰。
   3.引脚重映射:通过GPIO_Remap1_CAN1将 CAN1 的 TX/RX 映射到 PB9/PB8(部分 CH32 型号默认引脚可能不同,需重映射)。
   4.  CAN 外设配置:设置 CAN 工作模式(正常 / 回环 / 静默等)、位时序(决定通信速率)、错误处理方式(如是否自动离线管理)。
     滤波初始化:根据USE_SOFT_FILTER宏,选择初始化硬件滤波(CAN_FilterInit)或软件滤波(CAN_SoftFilterInit)。

   滤波机制(硬件 / 软件滤波)
  CAN 滤波的作用是筛选需要接收的 CAN 帧,忽略无关帧,减少 CPU 负担,代码支持两种滤波模式:
  硬件滤波(USE_SOFT_FILTER关闭):直接使用 CH32 的 CAN 硬件滤波模块,支持 32 位 / 16 位的 “掩码模式” 和 “列表模式”:
  掩码模式:通过 “ID + 掩码” 筛选符合范围的帧(如掩码0xFFE0表示只匹配 ID 的高 11 位)。
    列表模式:仅接收列表中明确指定的 ID 的帧(如只接收 ID 为 0x317 和 0x316 的帧)。
   软件滤波(USE_SOFT_FILTER开启):通过自定义结构体CANFilterStruct存储滤波规则,在CAN_ReceiveViaSoftFilter函数中遍历滤波组,软件判断接收帧是否符合规则,灵活性更高(支持更多滤波组)。
   
USB逻辑分析仪测试:

4.png
   
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:项目经理
简介:资深嵌入式开发工程师

109

主题

197

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部