MCP2515+SJA1000通讯调试记录

一、CAN总线

  CAN是控制器局域网络(Controller Area Network, CAN)的简称,是由研发和生产汽车电子产品著称的德国BOSCH公司开发了的,并最终成为国际标准(ISO 11898)。SJA1000是飞利浦公司一款并行接口的CAN协议控制器,为了减少IO口资源占用,Microchip推出SPI接口CAN协议控制器,型号:MCP2515。这两款芯片都支持CAN V2.0B 技术规范,能发送和接收标准和扩展数据帧以及远程帧。

CAN电平:

                                MCP2515+SJA1000通讯调试记录_第1张图片

CAN数据格式:

标准帧

MCP2515+SJA1000通讯调试记录_第2张图片

扩展帧

MCP2515+SJA1000通讯调试记录_第3张图片

远程帧(省略)

二、原理图

 2.1、MCP2515的原理图

  MCP2515+SJA1000通讯调试记录_第4张图片

2.2、SJA1000的原理图

                                 MCP2515+SJA1000通讯调试记录_第5张图片

三、调试思路

3.1、SPI接口调试

 

 

3.2、CAN接口调试

第一步:配置CLKOUT输出(CANCTRL寄存器配置CLKEN和分频系数),通过示波器观察CLKOUT引脚输出。确认MCP2515受控。

第二步:环路测试(自发自收)
   首先MCP2515配置成环路模式,下面是验证环路测试

      1、配置RXB0禁止滤波屏蔽标识符功能
        1、收发功能是否正常。验证:RXB0接收数据(可以读取接收缓冲区获取)和发送数据 对比,是否一致。
        2、开启CANINTE.TX0IE、CANINTE.RX0IE中断,监测/INT和 /RX0B 引脚电平变化。
        理论上:

          1)成功发送数据,TXB0CTRL.TXREQ由1变成0和 /INT引脚产生中断,且CANINTF.TX0IF发送中断标志位置1。
          2)成功接收数据,/INT引脚产生中断,且CANINTF.RX0IF发送中断标志位置1。同时RX0BF引脚出现低电平(前提使能RX0BF 接收缓冲区满中断引脚,BFPCTRL.BxBFE和BFPCTRL.BxBFM 置1)
 
      2、配置RXB0使能滤波屏蔽标识符功能(11位标准标识符)
       1、发送两组数据帧: 先发送不符合滤波码的数据帧,然后发送符合滤波码的数据帧。
       验证方法: 1、读取RX接收缓冲区,打印出来
                  2、通过/INT 和 RX0BF引脚 时序判断。理论:当成功发送第一帧数据是,中断引脚/INT置低,但数据的标识符不符合滤波码;发送第二帧数据符合滤波码,所以RX0BF置低。

                                      MCP2515+SJA1000通讯调试记录_第6张图片

注:/INT引脚为什么一直置低?因为响应中断后,未清除中断标志位。当清除中断标志位后,/INT恢复高电平。


第三步:MCP2515同SJA1000通讯 (MCP2515发送)
         初步调试现象:SJA1000未将接收数据 发送。个人分析可能原因:
          1、硬件连接:通过示波器监测 TJA1040T的发送端TXD 和 PCA82C250的接收端RXD波形一致。
          2、波特率:通过一对SJA1000模块收发,测试CAN总线时序,可知波特率100K。而MCP2515 发送数据时,测试的波特率也是100K。
          3、电平兼容性:可以排除。
          4、有无验证滤波码: 查看外SJA1000程序,该程序中未设置屏蔽码(即接收标准标识符的所有数据),可以排除。
          5、CAN终端匹配电阻:TJA1040T的匹配电阻参考datasheet设计,而SJA1000未接匹配电阻,后来焊接60欧,可以排除。
          6、通过CAN错误标志寄存器EFLG状态判定问题
                      TXB0CTRL     [1B] ----- 报文发送期间发送总线错误 和报文发送请求位
                      CANINTF         [A0] ----- 报文错误中断标志位和 错误中断标志位置1
                      EFLG               [20] ----- 总线关闭错误标志位置1 (即发送错误计数器TEC > 255)
                      TEC                  [00]--- ---总线发送错误计数器
                      REC                 [5E]----- 总线接收错误计数器

             综上所述:CAN总线异常。

           7、查看CAN电平转换芯片TJ1040引脚信号,发现该芯片由3.3V,而datasheet表示需要5V。(该步骤应该放在第一步,自己过失一)。通过焊下芯片割断VCC与3.3V连接,并同5V连接。重新测试后,发现CAN总线上无数据,而TJ1040的TXD有数据。why??

           查看TJ1040的PIN8的引脚一直为高电平,由datasheet可知:芯片处于待机模式,无法实现CAN电平转换。通过确认:TJ1040的PIN8的引脚虚焊,导致的。 

排除上述问题后,在CAN总线上有数据通讯。已知MCP2515发送数据格式:

11位标识符ID=0x110、数据长度DLC=8、data0-7=0x00 -0x07。通过示波器解码CAN_H的数据得出下图,对比后发现一致,且SJA1000接收到数据也一致。

MCP2515+SJA1000通讯调试记录_第7张图片

 

第三步:MCP2515同SJA1000通讯 (MCP2515接收)

      由第二步成功发送,表明CAN总线可以正常通讯。所以接收程序可以参考环路模式中接收程序。

 

通过上面调试感想:此次调试主要问题  硬件上大意:

1.、未发现TJ1040T芯片电源不合适(3.3V)

2、焊接TJ1040T时,虚焊。这两点都是低级错误。以后必须杜绝。

以后调试硬件,首先将该芯片相关信号(VCC、GND、RET、CS等)全部测试一下确认无误,在进行软件调试。

四、程序

 4.1、MCP2515程序

void MCP2515_init(void)
{
    MCP2515_Reset();
    /*  NO1 check if MODE_CONFIG  */
    while( (MCP2515_Read(MCP2515REG_CANSTAT) & MODE_CONFIG) != MODE_CONFIG )
	{
    	MCP2515_Write(MCP2515REG_CANCTRL , 0x86);//如果未进入MODE_CONFIG,重新配置
	}
    /*NO2   CNF1-3 ,Baud=100K对应20个*TQ*/
	MCP2515_Write(CNF1, SJW1|0x4);	                   //Synchronization Jump Width Length =1*TQ
	MCP2515_Write(CNF2, BTLMODE_CNF3|(SEG8<<3)|SEG3);  // Phase Seg 1 = 8*TQ, Prop Seg = 3*TQ
	MCP2515_Write(CNF3, SEG8);                         // Phase Seg 2 = 8*TQ

    /*NO3 设置MCP2515中断使能寄存器,使能接收缓冲器中断*/
	MCP2515_Write(CANINTE, 0xff);
	MCP2515_Write(CANINTF, 0x00);

    /*NO4 设置数据接收相关寄存器     */
	MCP2515_Write( RXB0CTRL, RXB_RXM0  );	// 设置RXM[1:0]=01,接收缓冲器0接收	标准标识符报文;禁止滚存功能
	MCP2515_Write( RXB1CTRL, RXB_RXM0|RXB_RXF2); // 设置RXM[1:0]=10,,接收缓冲器1 接收标准标识符报文;禁止滚存功能

    /*NO5 配置引脚    设置接收相关引脚控制寄存器,配置它们禁用第二功能 */
    MCP2515_Write( BFPCTRL, 0x05 ); //当由接收报文时,引脚/RX0BF 产生中断信号
	MCP2515_Write( RXB0DLC,  0x08); // 设置RX size
	MCP2515_Write( RXB1DLC,  0x08); // 设置RX size

	/*NO6 设置2个验收滤波寄存器   */
	MCP2515_Write(RXF0SIDH,0x22);//RXF0=0x22,即验收滤波器ID[10:3]=0x22
	MCP2515_Write(RXF0SIDL,0x00);//验收滤波器ID[2:0]=0x00
	
	MCP2515_Write(RXF2SIDH,0x22);//RXF0=0x000
	MCP2515_Write(RXF2SIDL,0x00);

	/*NO7 设置屏蔽滤波寄存器为   */

	MCP2515_Write(RXM0SIDH,0xFF);//RXM0=0x3FF,验收屏蔽使能[10:3]=0xFF
	MCP2515_Write(RXM0SIDL,0xE0);//验收屏蔽使能[2:0]=0x7 即是需要验证所有标准帧的11位ID。

	MCP2515_Write(RXM1SIDH,0xFF);//RXM0=0x3FF,验收屏蔽使能[10:3]=0xFF
	MCP2515_Write(RXM1SIDL,0xE0);//验收屏蔽使能[2:0]=0x7 即是需要验证所有标准帧的11位ID。

//	MCP2515_Write(RXM0SIDH,0x00);//RXM0=0x00,验收屏蔽使能[10:3]=0x00
//	MCP2515_Write(RXM0SIDL,0x00);//验收屏蔽使能[2:0]=0x0 即是不验证标准帧的11位ID,接收任何标准帧数据。

    /*N07   设置发送相关引脚控制寄存器,配置它们禁用第二功能   */
    	MCP2515_Write(TXRTSCTRL, 0x00);

    /*NO8 set  MODE_LOOPBACK  MCP2515进入环回模式,进行功能测试*/
#if DEBUG_loopback
    	MCP2515_Write(MCP2515REG_CANCTRL ,(MODE_LOOPBACK | CLKEN |CLK2));
#else
    	MCP2515_Write(MCP2515REG_CANCTRL ,(MODE_NORMAL | CLKEN |CLK2));
#endif
}

void MCP2515_loopback (void)
{
	Uint8 data[8]={1,2,3,4,5,6,7,8},temp;
	printf("\n This MCP2515_loopback  test\n");

	MCP2515_Write(TXB0CTRL,((MCP2515_Read(TXB0CTRL)) & (~TXB_TXREQ_M) ));//清除请求发送位TXB_TXREQ_M
/*发送第一帧不可识别标识符数据帧*/
   MCP2515_Write(TXB0CTRL,0x03);//设置为发送最高优先级
   MCP2515_Write(TXB0SIDH,0x88);// SID10--SID3
   MCP2515_Write(TXB0SIDL,0x00);//SID2--SID0

   MCP2515_Write(TXB0DLC,0x08);// 发送数据长度为8 字节
   MCP2515_Write(TXB0D0,0x11);// 发送数据长度为8 字节
   MCP2515_Write(TXB0D1,0x22);// 发送数据长度为8 字节
   MCP2515_Write(TXB0D2,0x33);// 发送数据长度为8 字节
   MCP2515_Write(TXB0D3,0x44);// 发送数据长度为8 字节
   MCP2515_Write(TXB0D4,0x55);// 发送数据长度为8 字节
   MCP2515_Write(TXB0D5,0x66);// 发送数据长度为8 字节
   MCP2515_Write(TXB0D6,0x77);// 发送数据长度为8 字节
   MCP2515_Write(TXB0D7,0x88);// 发送数据长度为8 字节
   
   temp =	MCP2515_Read(TXB0CTRL);
   MCP2515_Write(TXB0CTRL,(temp | TXB_TXREQ_M ) );//请求发送

   while((MCP2515_Read(TXB0CTRL) & TXB_TXREQ_M) == TXB_TXREQ_M )//等待发送完毕
   {
//    	printf("TXB0CTRL 				[%02X]\n",  MCP2515_Read(TXB0CTRL));
//    	printf("CANINTF 				[%02X]\n",  MCP2515_Read(CANINTF));
   }
   printf("CANINTF 				[%02X]\n",  MCP2515_Read(CANINTF));

	if ( MCP2515_Read(CANINTF) & 0x01 == 01)
	{
		MCP2515_Write(CANINTF,0);//清除中断标志位

	printf("RXB0SIDH 			[%02X]\n",  MCP2515_Read(RXB0SIDH ));
	printf("RXB0SIDL  			[%02X]\n",  MCP2515_Read(RXB0SIDL ));
//		printf("TXB0CTRL 			[%02X]\n",  MCP2515_Read(TXB0CTRL));
//		printf("CANINTF 			[%02X]\n",  MCP2515_Read(CANINTF));
   	printf("RXB0D0 				[%02X]\n",  MCP2515_Read(RXB0D0));
   	printf("RXB0D1 				[%02X]\n",  MCP2515_Read(RXB0D1));
   	printf("RXB0D2 				[%02X]\n",  MCP2515_Read(RXB0D2));
   	printf("RXB0D3 				[%02X]\n",  MCP2515_Read(RXB0D3));
   	printf("RXB0D4 				[%02X]\n",  MCP2515_Read(RXB0D4));
   	printf("RXB0D5 				[%02X]\n",  MCP2515_Read(RXB0D5));
   	printf("RXB0D6 				[%02X]\n",  MCP2515_Read(RXB0D6));
   	printf("RXB0D7 				[%02X]\n",  MCP2515_Read(RXB0D7));

	}
/*发送第二帧可识别标识符数据帧*/
		MCP2515_Write(CANINTF,0);//清除中断标志位
	    MCP2515_Write(TXB0CTRL,0x03);//设置为发送最高优先级
	    MCP2515_Write(TXB0SIDH,0x22);// SID[10:3]=0x22 
	    MCP2515_Write(TXB0SIDL,0x00);//SID2--SID0
		
	    MCP2515_Write(TXB0DLC,0x08);// 发送数据长度为8 字节
        MCP2515_Write(TXB0D0,data[0]);// 发送数据长度为8 字节
        MCP2515_Write(TXB0D1,data[1]);// 发送数据长度为8 字节
        MCP2515_Write(TXB0D2,data[2]);// 发送数据长度为8 字节
        MCP2515_Write(TXB0D3,data[3]);// 发送数据长度为8 字节
        MCP2515_Write(TXB0D4,data[4]);// 发送数据长度为8 字节
        MCP2515_Write(TXB0D5,data[5]);// 发送数据长度为8 字节
        MCP2515_Write(TXB0D6,data[6]);// 发送数据长度为8 字节
        MCP2515_Write(TXB0D7,data[7]);// 发送数据长度为8 字节


	    temp =	MCP2515_Read(TXB0CTRL);
	    MCP2515_Write(TXB0CTRL,(temp | TXB_TXREQ_M ) );//请求发送

	    while((MCP2515_Read(TXB0CTRL) & TXB_TXREQ_M) == TXB_TXREQ_M )//等待发送完毕
	    {
	    	printf("TXB0CTRL 				[%02X]\n",  MCP2515_Read(TXB0CTRL));
	    	printf("CANINTF 				[%02X]\n",  MCP2515_Read(CANINTF));
	    }

		if ( MCP2515_Read(CANINTF) & 0x01 == 01) //判断是否接收到符合规则数据帧
		{
			MCP2515_Write(CANINTF,0);//清除中断标志位
	    	printf("RXB0D0 				[%02X]\n",  MCP2515_Read(RXB0D0));
	    	printf("RXB0D1 				[%02X]\n",  MCP2515_Read(RXB0D1));
	    	printf("RXB0D2 				[%02X]\n",  MCP2515_Read(RXB0D2));
	    	printf("RXB0D3 				[%02X]\n",  MCP2515_Read(RXB0D3));
	    	printf("RXB0D4 				[%02X]\n",  MCP2515_Read(RXB0D4));
	    	printf("RXB0D5 				[%02X]\n",  MCP2515_Read(RXB0D5));
	    	printf("RXB0D6 				[%02X]\n",  MCP2515_Read(RXB0D6));
	    	printf("RXB0D7 				[%02X]\n",  MCP2515_Read(RXB0D7));
		}
 }

4.2、SJA10000程序

/**********************************************
工程:芯片SJA1000从CAN总线接收任何标识符的数据
功能:从CAN总线接收一帧数据,并通过RS232发送出去。
      数据格式= 标识符(任意)+长度(8)+数据
参数:CAN总线波特率100K,RS232通讯波特率57600
时间:2011年4月6日
************************************************/
// 根据时序要求,可以利用I/O口模拟总线了: 
//**************************读SJA1000*************************// 
uint Read_SJA1000(uint address) 
{ 
  uchar data;  
  asm("nop");    
  ALE_0; 
  WR_1; 
  RD_1; 
//DDRB=0xff;          //数据口为输出 
//PORTB=address;     //输出数据的地址 
  DATA_D1;//数据口为输出 
  DATA=address;//输出数据的地址 
  asm("nop");//delay5us(1);     
  ALE_1; 
  asm("nop");//delay5us(1); 
  asm("nop");//delay5us(1);  
  ALE_0; 
  RD_0; 
  asm("nop");//delay5us(2); 
  asm("nop"); 
//DDRB=0x00;       //数据口为输入 
//PORTB=0xff;      //上拉   
  DATA_D0;//数据口为输入 
  DATA=0xff;//上拉 
  asm("nop"); 
//data=PINB;       //获得数据
  data=DATA_IN;//获得数据 
  asm("nop");//delay5us(1); 
  RD_1; 
  asm("nop");//delay5us(2); 
  return data; 
} 
//**************************写SJA10000*************************// 
void Write_SJA1000(uint address,uint data) 
{ asm("nop"); 
//DDRB=0xff;          //数据口为输出 
//PORTB=address;     //输出数据的地址
  DATA_D1;          //数据口为输出 
  DATA=address;     //输出数据的地址      
  ALE_0; 
  WR_1; 
  RD_1; 
  asm("nop");//delay5us(1);     
  ALE_1;   
  asm("nop");//delay5us(1); 
  asm("nop");//delay5us(1); 
  ALE_0; 
  WR_0; 
  asm("nop");//delay5us(1); 
  asm("nop"); 
//PORTB=data;     //输出数据
  DATA=data; //输出数据
  asm("nop");//delay5us(2); 
  WR_1; 
  asm("nop");//delay5us(2); 
  asm("nop"); 
  //dog(); 
} 

void CAN_init(void)//CAN初始化
{
  uchar a;
  do
   { Write_SJA1000(CAN_MOD,0x01);//设定复位模式的请求位
	 a=Read_SJA1000(CAN_MOD);}  //读取模式寄存器中状态
  while( (a&0x01)==0x00);      //保证进入复位模式,bit0.0不为1,再写CAN_MOD  
  
  Write_SJA1000(CAN_CDR,0x40);//写时钟分频寄存器,Basic模式
  Write_SJA1000(CAN_BTR0,0x43);//写总线定时器0寄存器 晶振 16M,通讯波特率100K
  Write_SJA1000(CAN_BTR1,0x2f);//写总线定时器1寄存器
  Write_SJA1000(CAN_OCR,0x1a);//写输出控制寄存器
  Write_SJA1000(CAN_ACR,0x01);//验收码寄存器,表示需要验证的接收标识符
  Write_SJA1000(CAN_AMR,0xff);//写验收屏蔽寄存器,
  							 //当某一位为1时,表示对应验收码寄存器的位无屏蔽作用
  							 //报文标识符高8位(ID.10-ID03)对应着验收代码AC.7-AC.0
  							 // 而AM.7-AM.0对应AC.7-AC.0									
  do
   { Write_SJA1000(CAN_MOD,0x00);//退出复位模式
	 a=Read_SJA1000(CAN_MOD);}//读取模式寄存器中状态
  while( (a&0x01)==0x01);//保证进入复位模式,bit0.0不为0,再写CAN_MO
 }
 
/*-----------------------------------------------------------------
函数名称: void Usart_Receive(void)
函数功能: 查询方式,接收数据
参    数: 
返 回 值: 无
-----------------------------------------------------------------*/
unsigned char Usart_Receive(void)    //定义返回值类型,否则出错
{
  while(!(UCSRA&(1<


 

五、遇到问题

1、MCP2515和SJA1000波特率配置如何配置?配置成功,如何验证?

波特率概念:串行通讯中,每秒内传送bit数。例如波特率9600,即每秒传送9600bit。
MCP2515波特率配置100K

同波特率配置相关寄存器CNF1-3,波特率NBR =  fbit  =1/Tbit,而

               Tbit =同步段 + 传播段 +相位缓冲段PS1 +相位缓冲段PS2,其中这些段都是时间份额TQ为单位。TQ=2*BRP*Tosc

MCP2515+SJA1000通讯调试记录_第8张图片

 

已知:Fosc=20 MHz,Tosc=50ns, TQ==2*BRP*Tosc=( CFG1[5:0] = 4)=2*5*50=500ns   则波特率=100K占用TQ数计算如下:

波特率baud =  1/Tbit =  1/N*TQ (此处用N表示同步段、传播段、相位缓冲段PS1、相位缓冲段PS2总共对应TQ数),则

            N= 1/baud*TQ=1/100 000*500us  =20 个TQ

同步段:                            1个TQ(固定值)

传播段:                            3个TQ(配置CNF2[2:0]  = 0x02)

相位缓冲段PS1:               8个TQ(配置CNF2[5:3]  = 0x07)

相位缓冲段PS2:               8个TQ(配置CNF3[2:0]  = 0x07)

同步跳转宽度位SJW:      1个TQ( 该位用于为补偿总线上各节点振荡器频率之间的相移,详细参见MCP2515手册)

 

SJA1000波特率配置

 

验证波特率方法:

通过示波器监测CAN总线或转换芯片的RXD、TXD端,一位数据占用时间长度。例如波特率100K,每位占用的时间是 1/100K =10us

具体详见下图:该数据位寻找原则:最小时间段的电平。

             MCP2515+SJA1000通讯调试记录_第9张图片

你可能感兴趣的:(通信协议,DM816X学习)