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

CAN数据格式:
标准帧

扩展帧

远程帧(省略)
二、原理图
2.1、MCP2515的原理图

2.2、SJA1000的原理图

三、调试思路
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置低。

注:/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通讯 (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

已知: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
具体详见下图:该数据位寻找原则:最小时间段的电平。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/225778.html原文链接:https://javaforall.net
