星享社 发表于 2025-8-2 23:14

【沁恒CH32V307开发板测评】CAN测试

上一篇测评文章玩了一下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;

开发板上没有CAN收发器,因此使用的CAN收发器型号是TCAN1044;绘制的CAN收发器原理图如下:

硬件准备好了,就需要进行代码编程了
1、初始化CAN GPIO和CAN接口
void CAN_Mode_Init( u8 tsjw, u8 tbs2, u8 tbs1, u16 brp, u8 mode )
{
       GPIO_InitTypeDef GPIO_InitSturcture={0};
       CAN_InitTypeDef CAN_InitSturcture={0};
       CAN_FilterInitTypeDef CAN_FilterInitSturcture={0};
       
       RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB, ENABLE );
       RCC_APB1PeriphClockCmd( RCC_APB1Periph_CAN1, ENABLE );       
       
       GPIO_PinRemapConfig( GPIO_Remap1_CAN1, ENABLE);       
       
       GPIO_InitSturcture.GPIO_Pin = GPIO_Pin_9;
       GPIO_InitSturcture.GPIO_Mode = GPIO_Mode_AF_PP;               
       GPIO_InitSturcture.GPIO_Speed = GPIO_Speed_50MHz;
       GPIO_Init( GPIOB, &GPIO_InitSturcture);
       
       GPIO_InitSturcture.GPIO_Pin = GPIO_Pin_8;
       GPIO_InitSturcture.GPIO_Mode = GPIO_Mode_IPU;       
       GPIO_Init( GPIOB, &GPIO_InitSturcture);
       
       CAN_InitSturcture.CAN_TTCM = DISABLE;               
       CAN_InitSturcture.CAN_ABOM = DISABLE;               
       CAN_InitSturcture.CAN_AWUM = DISABLE;               
       CAN_InitSturcture.CAN_NART = ENABLE;               
       CAN_InitSturcture.CAN_RFLM = DISABLE;               
       CAN_InitSturcture.CAN_TXFP = DISABLE;
       CAN_InitSturcture.CAN_Mode = mode;
       CAN_InitSturcture.CAN_SJW = tsjw;               
       CAN_InitSturcture.CAN_BS1 = tbs1;               
       CAN_InitSturcture.CAN_BS2 = tbs2;               
       CAN_InitSturcture.CAN_Prescaler = brp;               
       CAN_Init( CAN1, &CAN_InitSturcture );
       
       CAN_FilterInitSturcture.CAN_FilterNumber = 0;               

#if (Frame_Format == Standard_Frame)
/* identifier/mask mode, One 32-bit filter, StdId: 0x317 */
       CAN_FilterInitSturcture.CAN_FilterMode = CAN_FilterMode_IdMask;       
       CAN_FilterInitSturcture.CAN_FilterScale = CAN_FilterScale_32bit;
       CAN_FilterInitSturcture.CAN_FilterIdHigh = 0x62E0;       
       CAN_FilterInitSturcture.CAN_FilterIdLow = 0;
       CAN_FilterInitSturcture.CAN_FilterMaskIdHigh = 0xFFE0;       
       CAN_FilterInitSturcture.CAN_FilterMaskIdLow = 0x0006;       


2、配置过滤
/*********************************************************************
* @fn      CAN_SoftFilterInit
*
* @brief   Initializes the CAN peripheral according to the specified
*      parameters in the CAN_FilterInitStruct.
*
* @param   CAN_FilterInitStruct - pointer to a CAN_FilterInitTypeDef
*      structure that contains the configuration information.
*
* @returnnone
*/
void CAN_SoftFilterInit(CAN_FilterInitTypeDef* CAN_FilterInitStruct)
{
        if(CAN_FilterInitStruct->CAN_FilterNumber > CANSOFTFILTER_MAX_GROUP_NUM){
                return;
        }
        if(CAN_FilterInitStruct->CAN_FilterActivation)
        {
                CANFilterStruct.en = 1;
        }else
        {
                CANFilterStruct.en = 0;
        }
        CANFilterStruct.FR.FR_16_H = CAN_FilterInitStruct->CAN_FilterIdHigh;
        CANFilterStruct.FR.FR_16_L = CAN_FilterInitStruct->CAN_FilterIdLow;
        CANFilterStruct.FR.FR_16_H = CAN_FilterInitStruct->CAN_FilterMaskIdHigh;
        CANFilterStruct.FR.FR_16_L = CAN_FilterInitStruct->CAN_FilterMaskIdLow;
        CANFilterStruct.mode = CAN_FilterInitStruct->CAN_FilterMode;
        CANFilterStruct.scale = CAN_FilterInitStruct->CAN_FilterScale;
}


3、配置接收中断
void USB_LP_CAN1_RX0_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void USB_LP_CAN1_RX0_IRQHandler()
{
       int i;
       uint8_t px,pbuf;
       if (CAN_GetITStatus(CAN1,CAN_IT_FMP0))
       {
               px = CAN_Receive_Msg(pbuf);
               for ( i = 0; i < px; i++)
               {
                       canexbuf_interrupt = pbuf;
               }
               if(px)
               {
                       interrupt_rx_flag = 1;
               }
               CAN_ClearITPendingBit(CAN1, CAN_IT_FMP0);
       }
}

4、配置发送和接收函数
/*********************************************************************
* @fn      CAN_Send_Msg
*
* @brief   CAN Transmit function.
*
* @param   msg - Transmit data buffer.
*          len - Data length.
*
* @return0 - Send successful.
*          1 - Send failed.
*/
u8 CAN_Send_Msg( u8 *msg, u8 len )
{
       u8 mbox;
       u16 i = 0;
       
       CanTxMsg CanTxStructure;

#if (Frame_Format == Standard_Frame)
       CanTxStructure.StdId = 0x317;               
       CanTxStructure.IDE = CAN_Id_Standard;       
   
#elif (Frame_Format == Extended_Frame)
       CanTxStructure.ExtId = 0x12124567;               
       CanTxStructure.IDE = CAN_Id_Extended;       
       
#endif       
       
       CanTxStructure.RTR = CAN_RTR_Data;               
       CanTxStructure.DLC = len;
       
       for( i=0; i<len; i++ )
       {
               CanTxStructure.Data = msg;       
       }
       
       mbox = CAN_Transmit( CAN1, &CanTxStructure);
       i = 0;
       
       while( ( CAN_TransmitStatus( CAN1, mbox ) != CAN_TxStatus_Ok ) && (i < 0xFFF) )
       {
               i++;
       }
       
       if( i == 0xFFF )
       {
               return 1;
       }
       else
       {
               return 0;
       }       
}

/*********************************************************************
* @fn      CAN_Receive_Msg
*
* @brief   CAN Receive function.
*
* @param   buf - Receive data buffer.
*          len - Data length.
*
* @returnCanRxStructure.DLC - Receive data length.
*/
u8 CAN_Receive_Msg( u8 *buf )
{
       u8 i;
       
       CanRxMsg CanRxStructure;
       
       if( CAN_MessagePending( CAN1, CAN_FIFO0 ) == 0)       
       {
               return 0;
       }

#ifdef USE_SOFT_FILTER
       CAN_ReceiveViaSoftFilter( CAN1, CAN_FIFO0, &CanRxStructure );
#else
       CAN_Receive( CAN1, CAN_FIFO0, &CanRxStructure );       
#endif // USE_SOFT_FILTER
       
       for( i=0; i<8; i++ )
       {
               buf = CanRxStructure.Data;
       }
       
       return CanRxStructure.DLC;       
}


5、下载验证
1)发送数据
250K波特率

1M波特率接收

统计发送成功的次数,按1M的波特率去发送,没有出错;发送58055次数据。


2)接收数据
使用USB-CAN软件向开发板发送数据,如图,设备成功接收到

总结:根据测试和数据手册说明,CH32V307VCY6的can最大速率为1M,且稳定性是很不错的,具体还需要根据现场实际情况进行测试。


页: [1]
查看完整版本: 【沁恒CH32V307开发板测评】CAN测试