一、用STM32F103做CAN的收发通信

 CAN 是Controller Area Network 的缩写(以下称为CAN),该通信使用的是ISO11898标准,该标准的物理层特征如下图所示。

51c嵌入式~CAN~合集1_嵌入式 物联网

 CAN协议是通过以下5种类型的帧进行的:

  • 数据帧
  • 摇控帧
  • 错误帧
  • 过载帧
  • 帧间隔

    另外,数据帧和遥控帧有标准格式和扩展格式两种格式。标准格式有11 个位的标识符(ID),扩展格式有29 个位的ID。

    大部分系统使用的都是数据帧 ,我这里使用的也是数据帧。
    数据帧一般由7个段构成,即:
(1) 帧起始。表示数据帧开始的段。
(2) 仲裁段。表示该帧优先级的段。
(3) 控制段。表示数据的字节数及保留位的段。
(4) 数据段。数据的内容,一帧可发送0~8个字节的数据。
(5) CRC段。检查帧的传输错误的段。
(6) ACK段。表示确认正常接收的段。
(7) 帧结束。表示数据帧结束的段。

    明确了数据帧概念,还需要理解一下过滤器的作用。

    STM32的标识符屏蔽滤波目的是减少了CPU处理CAN通信的开销。STM32的过滤器组最多有28个(互联型),但是STM32F103ZET6只有14个(增强型),每个滤波器组x由2个32为寄存器,CAN_FxR1和CAN_FxR2组成。
    STM32每个过滤器组的位宽都可以独立配置,以满足应用程序的不同需求。根据位宽的不同,每个过滤器组可提供:

  • 1个32位过滤器,包括:STDID[10:0]、EXTID[17:0]、IDE和RTR位
  • 2个16位过滤器,包括:STDID[10:0]、IDE、RTR和EXTID[17:15]位

    此外过滤器可配置为,屏蔽位模式和标识符列表模式。

    在屏蔽位模式下,标识符寄存器和屏蔽寄存器一起,指定报文标识符的任何一位,应该按照“必须匹配”或“不用关心”处理。
    而在标识符列表模式下,屏蔽寄存器也被当作标识符寄存器用。因此,不是采用一个标识符加一个屏蔽位的方式,而是使用2个标识符寄存器。接收报文标识符的每一位都必须跟过滤器标识符相同。相关文章:
 CAN总线详解

    一般也都是使用标识符列表模式,这里使用的也是标识符列表模式。滤波过程举例如下:

51c嵌入式~CAN~合集1_嵌入式 物联网_02

    在程序中就是:

//要过滤的ID高位 
CAN_FilterInitStructure.CAN_FilterIdHigh=0X00;  
//要过滤的ID低位                 
CAN_FilterInitStructure.CAN_FilterIdLow= (((u32)0x1314<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF; 
//过滤器屏蔽标识符的高16位值
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0xFFFF;   
//过滤器屏蔽标识符的低16位值         
CAN_FilterInitStructure.CAN_FilterMaskIdLow=0xFFFF;
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
这里的CAN_FilterId和CAN_FilterMaskId是配合使用的,意思是CAN_FilterId指出需要屏蔽ID的什么内容,什么格式;CAN_FilterMaskId是指CAN_FilterId的每一位是否需要过滤,若CAN_FilterMaskId在某位上是1的话,ID对应位上的数值就必须和CAN_FilterId该位上的一样,保持一致,反之则是“不关心”。
  • 1.

上述程序的设置的含义就是:只接收来自0x1314的数据,屏蔽其他ID的数据。

程序思路

    这里准备做一个主机与从机的通信,主要用扩展标识符ExtId来区分,分配的标识符是:
主机:0x1314
从机:0x1311

    主机负责接收所有从机的数据,不需要过滤,用扩展标识符ExtId来区分不同从机的数据;主机还可以向不同从机发送信息。而从机则只接收来自主机的数据,同样用扩展标识符ExtId来区分是否是发向自己的数据;同时,也能够向主机发送信息。

相关代码

    代码也是非常简单的,这里贴出了主机和从机的can.c和can.h两个文件。

从机相关代码

can.c文件:

#include "can.h"




/* 在中断处理函数中返回 */
//__IO uint32_t ret = 0;




//接收数据缓冲器
u8 RxBuf[5];
u8 Rx_flag=0;




void CAN1_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure; 
    NVIC_InitTypeDef NVIC_InitStructure;
    CAN_InitTypeDef        CAN_InitStructure;
    CAN_FilterInitTypeDef  CAN_FilterInitStructure;




    /* 复用功能和GPIOB端口时钟使能*/    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB, ENABLE);                                                                      




    /* CAN1 模块时钟使能 */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE); 




    /* Configure CAN pin: RX */  // PB8
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;       //上拉输入
    GPIO_Init(GPIOB, &GPIO_InitStructure);




    /* Configure CAN pin: TX */   // PB9
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;     //复用推挽输出
    GPIO_Init(GPIOB, &GPIO_InitStructure);




    //#define GPIO_Remap_CAN    GPIO_Remap1_CAN1 本实验没有用到重映射I/O
    GPIO_PinRemapConfig(GPIO_Remap1_CAN1, ENABLE);




    //CAN_NVIC_Configuration(); //CAN中断初始化   
    /* Configure the NVIC Preemption Priority Bits */  
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);




    #ifdef  VECT_TAB_RAM  
      /* Set the Vector Table base location at 0x20000000 */ 
      NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); 
    #else  /* VECT_TAB_FLASH  */
      /* Set the Vector Table base location at 0x08000000 */ 
      NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);   
    #endif




    /* enabling interrupt */
    NVIC_InitStructure.NVIC_IRQChannel=USB_LP_CAN1_RX0_IRQn;;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);




    //CAN_INIT();//CA初始化N模块 
    /* CAN register init */
    CAN_DeInit(CAN1);                       //将外设CAN的全部寄存器重设为缺省值
    CAN_StructInit(&CAN_InitStructure);     //把CAN_InitStruct中的每一个参数按缺省值填入




    /* CAN cell init */
    CAN_InitStructure.CAN_TTCM=DISABLE;         //没有使能时间触发模式
    CAN_InitStructure.CAN_ABOM=DISABLE;         //没有使能自动离线管理
    CAN_InitStructure.CAN_AWUM=DISABLE;         //没有使能自动唤醒模式
    CAN_InitStructure.CAN_NART=DISABLE;         //没有使能非自动重传模式
    CAN_InitStructure.CAN_RFLM=DISABLE;         //没有使能接收FIFO锁定模式
    CAN_InitStructure.CAN_TXFP=DISABLE;         //没有使能发送FIFO优先级
    CAN_InitStructure.CAN_Mode=CAN_Mode_Normal; //CAN设置为正常模式
    CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;      //重新同步跳跃宽度1个时间单位
    CAN_InitStructure.CAN_BS1=CAN_BS1_3tq;      //时间段1为3个时间单位
    CAN_InitStructure.CAN_BS2=CAN_BS2_2tq;      //时间段2为2个时间单位
    CAN_InitStructure.CAN_Prescaler=60;         //时间单位长度为60 
    CAN_Init(CAN1,&CAN_InitStructure);          //波特率为:72M/2/60(1+3+2)=0.1 即波特率为100KBPs




    // CAN filter init 过滤器,注意,只接收主机发过来的数据,屏蔽其他数据
    CAN_FilterInitStructure.CAN_FilterNumber=1;                     //指定过滤器为1
    CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;   //指定过滤器为标识符屏蔽位模式
    CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;  //过滤器位宽为32位




    //CAN_FilterInitStructure.CAN_FilterIdHigh= (((u32)0x1314<<3)&0xFFFF0000)>>16;  
    CAN_FilterInitStructure.CAN_FilterIdHigh=0X00;                  //要过滤的ID高位 
    CAN_FilterInitStructure.CAN_FilterIdLow= (((u32)0x1314<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF; //要过滤的ID低位 




    CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0xFFFF;            //过滤器屏蔽标识符的高16位值
    CAN_FilterInitStructure.CAN_FilterMaskIdLow=0xFFFF;             //过滤器屏蔽标识符的低16位值
    CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_FIFO0;     //设定了指向过滤器的FIFO为0
    CAN_FilterInitStructure.CAN_FilterActivatinotallow=ENABLE;            //使能过滤器
    CAN_FilterInit(&CAN_FilterInitStructure);                       //按上面的参数初始化过滤器




    /* CAN FIFO0 message pending interrupt enable */ 
    CAN_ITConfig(CAN1,CAN_IT_FMP0, ENABLE);                         //使能FIFO0消息挂号中断
}




/* 发送两个字节的数据*/
u8 CAN_SetMsg(u8 Data1,u8 Data2)
{ 
    u8 mbox;
    u16 i=0; 
    CanTxMsg TxMessage;  




    TxMessage.StdId=0x0000;     //标准标识符为0x00
    TxMessage.ExtId=0x1311;     //扩展标识符0x1311,可以更改该标识符以示区分不同从机
    TxMessage.IDE=CAN_ID_EXT;   //使用扩展标识符
    TxMessage.RTR=CAN_RTR_DATA; //为数据帧
    TxMessage.DLC=2;            //消息的数据长度为2个字节
    TxMessage.Data[0]=Data1;    //第一个字节数据
    TxMessage.Data[1]=Data2;    //第二个字节数据 




    //发送数据
    mbox= CAN_Transmit(CAN1, &TxMessage);  
    while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))
        i++;    //等待发送结束
    if(i>=0XFFF)
        return 0;
    return 1;
}
u8 CAN_GetMsg(u8 *msg1,u8 *msg2)
{
    if(Rx_flag == 1)//发现数据
    {
        *msg1=RxBuf[0];
        *msg2=RxBuf[1];
        Rx_flag=0;//数据已经取走,可以更新数据
        return 1;
    }else
        return 0;
}
/* USB中断和CAN接收中断服务程序,USB跟CAN公用I/O,这里只用到CAN的中断。 */
void USB_LP_CAN1_RX0_IRQHandler(void)
{




  CanRxMsg RxMessage;




  RxMessage.StdId=0x00;
  RxMessage.ExtId=0x00;
  RxMessage.IDE=0;
  RxMessage.DLC=0;
  RxMessage.FMI=0;
  RxMessage.Data[0]=0x00;
  RxMessage.Data[1]=0x00;    




  CAN_Receive(CAN1,CAN_FIFO0, &RxMessage); //接收FIFO0中的数据  




  if(Rx_flag == 0)//数据已取走或者缓冲器为空
    {
        RxBuf[0]=RxMessage.Data[0];
        RxBuf[1]=RxMessage.Data[1];
        Rx_flag=1;//数据已经备好,等待取走
    }




}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.
  • 181.
  • 182.
  • 183.
  • 184.
  • 185.
  • 186.
  • 187.
  • 188.
  • 189.
  • 190.
  • 191.
  • 192.
  • 193.
  • 194.
  • 195.
  • 196.
  • 197.
  • 198.
  • 199.
  • 200.
  • 201.
  • 202.
  • 203.
  • 204.
  • 205.
  • 206.
  • 207.
  • 208.
  • 209.
  • 210.
  • 211.
  • 212.
  • 213.
  • 214.
  • 215.
  • 216.
  • 217.
  • 218.
  • 219.
  • 220.
  • 221.
  • 222.
  • 223.
  • 224.
  • 225.

can.h文件

51c嵌入式~CAN~合集1_嵌入式 物联网_03

主机相关代码

    这里主机代码大部分是和从机类似的,就只贴出不同的地方了。
can.c文件:

#include "can.h"




/* 在中断处理函数中返回 */
//__IO uint32_t ret = 0;




void CAN1_Init(void)
{
    ......//以上与从机部分相同




    //CAN filter init 过滤器,已经设置为任意,可以通过ExtId标识符区分从机代号
    CAN_FilterInitStructure.CAN_FilterNumber=1;                     //指定过滤器为1
    CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;   //指定过滤器为标识符屏蔽位模式
    CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;  //过滤器位宽为32位
    CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000;                //过滤器标识符的高16位值
    CAN_FilterInitStructure.CAN_FilterIdLow=CAN_ID_EXT|CAN_RTR_DATA;//过滤器标识符的低16位值
    CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;            //过滤器屏蔽标识符的高16位值
    CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;             //过滤器屏蔽标识符的低16位值
    CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_FIFO0;     //设定了指向过滤器的FIFO为0
    CAN_FilterInitStructure.CAN_FilterActivatinotallow=ENABLE;            //使能过滤器
    CAN_FilterInit(&CAN_FilterInitStructure);                       //按上面的参数初始化过滤器




    /* CAN FIFO0 message pending interrupt enable */ 
    CAN_ITConfig(CAN1,CAN_IT_FMP0, ENABLE);                         //使能FIFO0消息挂号中断
}




//接收数据缓冲器
u8 CAN_RX_BUF[CAN_RX_LEN]={0};     //接收缓冲,最大USART_REC_LEN个字节.
//接收标志位
u8 Rx_flag=0;
/* USB中断和CAN接收中断服务程序,USB跟CAN公用I/O,这里只用到CAN的中断。 */
void USB_LP_CAN1_RX0_IRQHandler(void)
{
    u8 i=0;
    CanRxMsg RxMessage;




    RxMessage.StdId=0x00;
    RxMessage.ExtId=0x00;
    RxMessage.IDE=0;
    RxMessage.DLC=0;
    RxMessage.FMI=0;




    CAN_Receive(CAN1,CAN_FIFO0, &RxMessage); //接收FIFO0中的数据  




    if(Rx_flag == 0)//数据已取走或者缓冲器为空
    {
        if((RxMessage.DLC) == 2)//是否收到2位字节数据
        {
             CAN_RX_BUF[0]=RxMessage.Data[0];
             CAN_RX_BUF[1]=RxMessage.Data[1];     
        }
    }




} 




/* 发送两个字节的数据*/
u8 CAN_SendMsg(u8* data1, u8* data2)
{ 
    u8 mbox;
    u16 i=0; 
    CanTxMsg TxMessage;  




    TxMessage.StdId=0x0000;     //标准标识符为0x00
    TxMessage.ExtId=0x1314;     //扩展标识符0x0000
    TxMessage.IDE=CAN_ID_EXT;   //使用扩展标识符
    TxMessage.RTR=CAN_RTR_DATA; //为数据帧
    TxMessage.DLC=2;            //消息的数据长度为2个字节
    TxMessage.Data[0]=Data1;    //第一个字节数据
    TxMessage.Data[1]=Data2;    //第二个字节数据 




    //发送数据
    mbox= CAN_Transmit(CAN1, &TxMessage);  
    while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))
        i++;    //等待发送结束
    if(i>=0XFFF)
        return 0;//发送失败
    return 1;//发送成功 
}
u8 CAN_GetMsg(u8 *msg1,u8 *msg2)
{
    if(Rx_flag == 1)//发现数据
    {
        *msg1=CAN_RX_BUF[0];
        *msg2=CAN_RX_BUF[1];
        Rx_flag=0;//数据已经取走,可以更新数据
        return 1;
    }else
        return 0;
}
void Clear_canBuffer(void)
{
    Rx_flag=0;//清楚接收标志位
    memset(CAN_RX_BUF, 0, sizeof(u8)*CAN_RX_LEN);//清空缓冲区
}
u8 Check_canRX(void)
{
    return (Rx_flag == 6);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.

can.h文件:

#ifndef __CAN_H
#define __CAN_H




#include "sys.h"
#include "string.h"




#define CAN_RX_LEN          30          //定义最大接收字节数 




extern u8  CAN_RX_BUF[CAN_RX_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 




void CAN1_Init(void);
u8 CAN_SendMsg(u8* data1, u8* data2);
u8 CAN_GetMsg(u8 *msg1,u8 *msg2);




#endif /* __CAN_H */
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.

开发板商城 天皓智联 CAN总线设备~~



二、CAN总线终端电阻为什么是120Ω?为什么是0.25W?

51c嵌入式~CAN~合集1_嵌入式 物联网_04

    但是作为学渣的我,知道这个是在各种标准以及各种数据手册和应用笔记里面常用的电阻值,但是这两个终端电阻的具体作用是什么呢?之前就知道阻抗匹配,但是究竟匹配的是什么呢?

    然后我就上知乎遨游了一下,半抄半写的总结了下面的这些知识点。知道终端电阻的作用,对于日常工作中波形不稳定等问题,也能更快的找到问题的原因。

终端电阻的作用

    CAN总线终端电阻的作用有3个:

    1、提高抗干扰能力,让高频低能量的信号迅速走掉

    2、确保总线快速进入隐性状态,让寄生电容的能量更快走掉;

    3、提高信号质量,放置在总线的两端,让反射能量降低。

1、提高抗干扰能力

    CAN总线有“显性”和“隐性”两种状态,“显性”代表“0”,“隐性”代表“1”,由CAN收发器决定。下图是一个CAN收发器的典型内部结构图,CANH、CANL连接总线。

51c嵌入式~CAN~合集1_嵌入式 物联网_05

    总线显性时,收发器内部Q1、Q2导通,CANH、CANL之间产生压差;隐性时,Q1、Q2截止,CANH、CANL处于无源状态,压差为0。

    总线若无负载,隐性时差分电阻阻值很大,内部的MOS管属于高阻态,外部的干扰只需要极小的能量即可令总线进入显性(一般的收发器显性门限最小电压仅500mV)。这个时候如果有差模干扰过来,总线上就会有明显的波动,而这些波动没有地方能够吸收掉他们,就会在总线上创造一个显性位出来。所以为提升总线隐性时的抗干扰能力,可以增加一个差分负载电阻,且阻值尽可能小,以杜绝大部分噪声能量的影响。然而,为了避免需要过大的电流总线才能进入显性,阻值也不能过小。

2、确保快速进入隐性状态

    在显性状态期间,总线的寄生电容会被充电,而在恢复到隐性状态时,这些电容需要放电。如果CANH、CANL之间没有放置任何阻性负载,电容只能通过收发器内部的差分电阻放电,这个阻抗是比较大的,按照RC滤波电路的特性,放电时间就会明显比较长。我们在收发器的CANH、CANL之间加入一个220PF的电容进行模拟试验,位速率为500kbit/s,波形如图,这个波形的下降沿就是比较长的状态。

51c嵌入式~CAN~合集1_嵌入式 物联网_06

    为了让总线寄生电容快速放电,确保总线快速进入隐性状态,需要在CANH、CANL之间放置一个负载电阻。增加一个60Ω的电阻后,波形如图,从图中看出,显性恢复到隐性的时间缩减到128nS,与显性建立时间相当。

51c嵌入式~CAN~合集1_嵌入式 物联网_07

3、提高信号质量

    信号在较高的转换速率情况下,信号边沿能量遇到阻抗不匹配时,会产生信号反射;传输线缆横截面的几何结构发生变化,线缆的特征阻抗会随之变化,也会造成反射。

    能量发生反射时,导致反射的波形与原来的波形进行叠加,就会产生振铃。

    在总线线缆的末端,阻抗急剧变化导致信号边沿能量反射,总线信号上会产生振铃,若振铃幅度过大,就会影响通信质量。在线缆末端增加一个与线缆特征阻抗一致的终端电阻,可以将这部分能量吸收,避免振铃的产生。

    别人进行了一个模拟试验(图片都是我抄过来的),位速率为1Mbit/s,收发器CANH、CANL接一根10m左右的双绞线,收发器端接120Ω电阻保证隐性转换时间,末端不加负载。末端信号波形如图所示,信号上升沿出现了振铃。

51c嵌入式~CAN~合集1_嵌入式 物联网_08

    若双绞线末端增加一个120Ω的电阻,末端信号波形明显改善,振铃消失。

51c嵌入式~CAN~合集1_嵌入式 物联网_09

    一般在直线型拓扑中,线缆两端即是发送端,也是接收端,故线缆两端需各加一个终端电阻。

    而在实际应用过程中,CAN总线一般都不是完美的总线式的设计,很多时候是总线型和星型的混合结构,这个时候一般都将CAN终端电阻布置在线束最远的两端,来尽量的模拟CAN总线的标准结构。

为什么选120Ω?

    什么是阻抗?在电学中,常把对电路中电流所起的阻碍作用叫做阻抗。阻抗单位为欧姆,常用Z表示,是一个复数Z= R+i( ωL–1/(ωC))。具体说来阻抗可分为两个部分,电阻(实部)和电抗(虚部)。其中电抗又包括容抗和感抗,由电容引起的电流阻碍称为容抗,由电感引起的电流阻碍称为感抗。这里的阻抗是指Z的模。

    任何一根线缆的特征阻抗都可以通过实验的方式得出。线缆的一端接方波发生器,另一端接一个可调电阻,并通过示波器观察电阻上的波形。调整电阻阻值的大小,直到电阻上的信号是一个良好的无振铃的方波: 阻抗匹配与信号完整性,此时的电阻值可以认为与线缆的特征阻抗一致。

    采用两根汽车使用的典型线缆,将它们扭制成双绞线,就可根据上述方法得到特征阻抗大约为120Ω,这也是CAN标准推荐的终端电阻阻值,所以这个120Ω是测出来的,不是算出来的,都是根据实际的线束特性进行计算得到的。当然在ISO 11898-2这个标准里面也是有定义的。

51c嵌入式~CAN~合集1_嵌入式 物联网_10

为什么功率还要选0.25W?

    这个就要结合一些故障状态也计算,汽车ECU的所有接口都需要考虑短路到电源和短路到地的情况,所以我们也需要考虑CAN总线的节点短路到电源的情况,根据标准需要考虑短路到18V的情况,假设CANH短路到18V,电流会通过终端电阻流到CANL上,而CANL内部由于限流的原因,最大注入电流为50mA(TJA1145的规格书上标注),这时候120Ω电阻的功率就是50mA*50mA*120Ω=0.3W。考虑到高温情况下的降额,终端电阻的功率就是0.5W。