[单片机芯片] 【沁恒CH32V307开发板测评】CAN测试

[复制链接]
 楼主| 星享社 发表于 2025-8-2 23:14 | 显示全部楼层 |阅读模式
上一篇测评文章玩了一下SPI接口,还是很顺利的
一、CAN介绍
1、CAN是什么
CAN(Controller Area Network)是一种​​基于差分信号的异步串行通信协议​​,由博世公司于1983年开发,专为解决复杂系统中电子设备的实时数据交换问题。
其核心设计思想为:
​​        1)多主竞争,智能仲裁​​:任何节点均可主动发送数据,通过ID优先级(数值越小优先级越高)实现非破坏性仲裁,高优先级数据自动抢占总线
​        2)​差分抗扰,高可靠传输​​:通过CAN_H/CAN_L双绞线传输差分信号,电压差>0.9V为显性(逻辑0),<0.5V为隐性(逻辑1)。此设计天然抑制共模干扰,适合强电磁环境(如汽车引擎舱)
​​        3)极简布线,高扩展性​​:双线总线可连接多达110个节点,新增设备无需修改网络地址
通俗一点的理解,可以把CAN总线想象成个​​高效的会议讨论组​​:
​​        发言规则​​:谁有紧急消息(ID优先级高)谁先说,其他人自动静音等待(非破坏性仲裁)。
​​        抗干扰能力​​:成员戴降噪耳机(差分信号)确保在嘈杂环境中听清指令。
​​        容错机制​​:有人报错数据时,系统自动要求重说(错误重传)
2、CAN的应用场景
主要有三大应用场景
1)汽车电子​​(最核心场景)
连接发动机控制、ABS刹车、仪表盘等模块。例如:踩刹车时,刹车传感器通过CAN通知发动机降速
​​        2)工业自动化​​
工厂机器人、传感器群通过CAN协同工作,如机械臂收到指令后同步动作
​​        3)智能设备​​
医疗监护仪、电梯控制系统、智能家居设备(如安防传感器)的内部通信
3、优势和劣势
1)优势
高可靠性、实时性强、扩展灵活、成本低​​
2)劣势
数据量小、速率与距离矛盾、偶发冲突问题
二、CAN代码编程
查看哪些引脚支持CAN,如图,PB8;PB9;
引脚.png
开发板上没有CAN收发器,因此使用的CAN收发器型号是TCAN1044;绘制的CAN收发器原理图如下:
原理图.png
硬件准备好了,就需要进行代码编程了
1、初始化CAN GPIO和CAN接口
  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;         



2、配置过滤
  1. /*********************************************************************
  2.   * @fn      CAN_SoftFilterInit
  3.   *
  4.   * [url=home.php?mod=space&uid=247401]@brief[/url]   Initializes the CAN peripheral according to the specified
  5.   *        parameters in the CAN_FilterInitStruct.
  6.   *
  7.   * @param   CAN_FilterInitStruct - pointer to a CAN_FilterInitTypeDef
  8.   *        structure that contains the configuration information.
  9.   *
  10.   * [url=home.php?mod=space&uid=266161]@return[/url]  none
  11.   */
  12. void CAN_SoftFilterInit(CAN_FilterInitTypeDef* CAN_FilterInitStruct)
  13. {
  14.         if(CAN_FilterInitStruct->CAN_FilterNumber > CANSOFTFILTER_MAX_GROUP_NUM){
  15.                 return;
  16.         }
  17.         if(CAN_FilterInitStruct->CAN_FilterActivation)
  18.         {
  19.                 CANFilterStruct[CAN_FilterInitStruct->CAN_FilterNumber].en = 1;
  20.         }else
  21.         {
  22.                 CANFilterStruct[CAN_FilterInitStruct->CAN_FilterNumber].en = 0;
  23.         }
  24.         CANFilterStruct[CAN_FilterInitStruct->CAN_FilterNumber].FR[0].FR_16_H = CAN_FilterInitStruct->CAN_FilterIdHigh;
  25.         CANFilterStruct[CAN_FilterInitStruct->CAN_FilterNumber].FR[0].FR_16_L = CAN_FilterInitStruct->CAN_FilterIdLow;
  26.         CANFilterStruct[CAN_FilterInitStruct->CAN_FilterNumber].FR[1].FR_16_H = CAN_FilterInitStruct->CAN_FilterMaskIdHigh;
  27.         CANFilterStruct[CAN_FilterInitStruct->CAN_FilterNumber].FR[1].FR_16_L = CAN_FilterInitStruct->CAN_FilterMaskIdLow;
  28.         CANFilterStruct[CAN_FilterInitStruct->CAN_FilterNumber].mode = CAN_FilterInitStruct->CAN_FilterMode;
  29.         CANFilterStruct[CAN_FilterInitStruct->CAN_FilterNumber].scale = CAN_FilterInitStruct->CAN_FilterScale;
  30. }



3、配置接收中断
  1. void USB_LP_CAN1_RX0_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
  2. void USB_LP_CAN1_RX0_IRQHandler()
  3. {
  4.          int i;
  5.          uint8_t px,pbuf[8];
  6.          if (CAN_GetITStatus(CAN1,CAN_IT_FMP0))
  7.          {
  8.                  px = CAN_Receive_Msg(pbuf);
  9.                  for ( i = 0; i < px; i++)
  10.                  {
  11.                          canexbuf_interrupt[i] = pbuf[i];
  12.                  }
  13.                  if(px)
  14.                  {
  15.                          interrupt_rx_flag = 1;
  16.                  }
  17.                  CAN_ClearITPendingBit(CAN1, CAN_IT_FMP0);
  18.          }
  19. }


4、配置发送和接收函数
  1. /*********************************************************************
  2.   * @fn      CAN_Send_Msg
  3.   *
  4.   * @brief   CAN Transmit function.
  5.   *
  6.   * @param   msg - Transmit data buffer.
  7.   *          len - Data length.
  8.   *
  9.   * @return  0 - Send successful.
  10.   *          1 - Send failed.
  11.   */
  12. u8 CAN_Send_Msg( u8 *msg, u8 len )
  13. {
  14.          u8 mbox;
  15.          u16 i = 0;
  16.          
  17.          CanTxMsg CanTxStructure;

  18. #if (Frame_Format == Standard_Frame)
  19.          CanTxStructure.StdId = 0x317;               
  20.          CanTxStructure.IDE = CAN_Id_Standard;       
  21.    
  22. #elif (Frame_Format == Extended_Frame)
  23.          CanTxStructure.ExtId = 0x12124567;               
  24.          CanTxStructure.IDE = CAN_Id_Extended;       
  25.          
  26. #endif       
  27.          
  28.          CanTxStructure.RTR = CAN_RTR_Data;               
  29.          CanTxStructure.DLC = len;
  30.          
  31.          for( i=0; i<len; i++ )
  32.          {
  33.                  CanTxStructure.Data[i] = msg[i];       
  34.          }
  35.          
  36.          mbox = CAN_Transmit( CAN1, &CanTxStructure);
  37.          i = 0;
  38.          
  39.          while( ( CAN_TransmitStatus( CAN1, mbox ) != CAN_TxStatus_Ok ) && (i < 0xFFF) )
  40.          {
  41.                  i++;
  42.          }
  43.          
  44.          if( i == 0xFFF )
  45.          {
  46.                  return 1;
  47.          }
  48.          else
  49.          {
  50.                  return 0;
  51.          }       
  52. }

  53. /*********************************************************************
  54.   * @fn      CAN_Receive_Msg
  55.   *
  56.   * @brief   CAN Receive function.
  57.   *
  58.   * @param   buf - Receive data buffer.
  59.   *          len - Data length.
  60.   *
  61.   * @return  CanRxStructure.DLC - Receive data length.
  62.   */
  63. u8 CAN_Receive_Msg( u8 *buf )
  64. {
  65.          u8 i;
  66.          
  67.          CanRxMsg CanRxStructure;
  68.          
  69.          if( CAN_MessagePending( CAN1, CAN_FIFO0 ) == 0)         
  70.          {
  71.                  return 0;
  72.          }

  73. #ifdef USE_SOFT_FILTER
  74.          CAN_ReceiveViaSoftFilter( CAN1, CAN_FIFO0, &CanRxStructure );
  75. #else
  76.          CAN_Receive( CAN1, CAN_FIFO0, &CanRxStructure );       
  77. #endif // USE_SOFT_FILTER
  78.          
  79.          for( i=0; i<8; i++ )
  80.          {
  81.                  buf[i] = CanRxStructure.Data[i];
  82.          }
  83.          
  84.          return CanRxStructure.DLC;       
  85. }



5、下载验证
1)发送数据
250K波特率
CAN接收.png
1M波特率接收
CAN1M接收.png
统计发送成功的次数,按1M的波特率去发送,没有出错;发送58055次数据。
发送成功.png 发送数据58500.png

2)接收数据
使用USB-CAN软件向开发板发送数据,如图,设备成功接收到
CAN接收8个字节接收.png
总结:根据测试和数据手册说明,CH32V307VCY6的can最大速率为1M,且稳定性是很不错的,具体还需要根据现场实际情况进行测试。


寂静之回响 发表于 2025-8-4 12:49 | 显示全部楼层
楼主用的哪家的CAN收发器??

评论

用的是TI的  发表于 2025-8-15 22:11
Labyrinth 发表于 2025-8-9 21:15 | 显示全部楼层
最后测试下来丢包率如何??多少速率测试的?
 楼主| 星享社 发表于 2025-8-15 22:12 | 显示全部楼层
Labyrinth 发表于 2025-8-9 21:15
最后测试下来丢包率如何??多少速率测试的?

1M的速率,测试发送接收基本没有丢包
您需要登录后才可以回帖 登录 | 注册

本版积分规则

9

主题

26

帖子

0

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