>一、原来用STM32做过一次CAN 通讯,按照洋桃电子的视频做的,通讯是成功了,但是因为理解不深,还是迷迷糊糊的,中间也看过一些书籍与资料。纸上得来终觉浅,还是要自己亲自试试才理解深刻。 1)CAN 总线通讯协议中,没有地址概念,但是发送方有ID,ID 分为标准ID(11位)与扩展ID(29位),ID数字越小,在同时发送时优先级越高。因为没有地址信息,所以发送方相当于广播发送,发送的信息可以达到任意节点,接受方根据需要自己决定接受那些ID 发送的信息。因为没有地址,原则上总线上可以挂接无限多设备。 标准报文,11位ID,四位是数据长度,最大发送8个字节,其它是辅助位。 扩展报文,29位ID,四位是数据长度,最大发送8个字节。 2)CAN总线是通过两条线来传输信号:CAN高线、CAN低线。信号由两条线的电位差将物理信号转换成数字信号,以高速CAN为例,CAN高为3.5V,CAN低为1.5V,电位差为2V,此时定义为显性,对应数字信号:0;CAN高与CAN低均为2.5V时,电位差为0V,此时定义为隐性,对应数字信号:1。 高速CAN协议规定,两根线都为2.5V时,为隐形,是数字1;两根线相差2V时,为显性,是数字0 3) 因为没有时钟信号,通讯双方靠相同的波特率来发送与解析数据。每一位又分为四个时间段 。 4)信息接受方,如何选择合适的信息呢? ①可以设置为接受所以信息,不加选择,比如中央控制器,就可以设置为接受所有信息,如何根据报文ID确定是谁发来的信息。 ② 根据滤波寄存器与屏蔽寄存器接受设备需要的信息。 这个理解稍微复杂一点。屏蔽寄存器相当于门卫,为1时在岗,来一个人都要查看身份信息,和滤波器的一致的才让通过。为0时意味着脱岗,不管是谁都可以进来。 比如屏蔽寄存器为111 1111 1111,滤波寄存器为000 0010 0011,那么只有ID和滤波寄存器完全一致的设备发送的信息才可以接受,因为滤波寄存器不是一个,这样就可以接受有限的几个ID 发送来的信息。(以标准ID为例) 如果屏蔽寄存器是111 1111 0000,滤波寄存器为000 0010 0011,那么所有000 0010 XXXX,最后四位无论是0或是1都可以接受,这样就可以接受一组信息 。 二、51单片机自身没有CAN 协议,也没有SPI协议,好在可以模拟SPI协议,CAN通讯需要一个CAN控制器与一个CAN收发器。stm32自带CAN控制器,加一个TJA1050CAN收发器就行了。在万能的淘宝买了四个MCP2515模块,模块上自带TJA1050收发器,每个就4元还包邮。 1)SPI接口,CS,MOSI,MISO,SCK,还有一个中断接到P3.2或P3.3,我没有用。商家给的参考程序是小泥人写的,有很多错误,并且啰里啰嗦。因为普中科技的单片机P2.2 2.3 2.4是数码管控制管脚,所以我稍微变了一下。
sbit MCP2515_SCK = P2^6; sbit MCP2515_MOSI = P2^5; sbit MCP2515_MISO = P2^1; sbit MCP2515_CS = P2^0;
2)51单片机没有SPI协议,只能模拟一个,SPI读写程序为 /******************************************************************************* * 函数名 : SPI_ReadByte * 描述 : 通过SPI读取一个字节数据 * 输入 : 无 * 输出 : 无 * 返回值 : rByte(读取到的一个字节数据) * 说明 : 无 *******************************************************************************/ unsigned char SPI_ReadByte(void) { unsigned char i,rByte=0; MCP2515_SCK=0; for(i=0;i<8;i++) { MCP2515_SCK=1; rByte<<=1; rByte|=MCP2515_MISO; MCP2515_SCK=0; } return rByte; } /******************************************************************************* * 函数名 : SPI_SendByte * 描述 : SPI发送一个字节数据 * 输入 : dt:待发送的数据 * 输出 : 无 * 返回值 : 无 * 说明 : 无 *******************************************************************************/ void SPI_SendByte(unsigned char dt) { unsigned char i; for(i=0;i<8;i++) { MCP2515_SCK=0; if((dt<<i)&0x80) MCP2515_MOSI=1; else MCP2515_MOSI=0; MCP2515_SCK=1; } MCP2515_SCK=0; }
3)设置通讯波特率,MCP2515主要有三个寄存器决定了波特率。CNF1,决定了同步跳转SJW与预分频系数,从而决定了一个TQ的时长。 CNF2决定了相位缓冲段1与传播段的长度。 CNF3在BTLMODE=1时,决定了相位缓冲段2的长度。 直接上代码吧,啰里啰嗦的,MCP2515.C文件里已经给做好了,只需要简单改一改就可以了。
MCP2515_WriteByte(CNF1,CAN_125Kbps); MCP2515_WriteByte(CNF2,0x80|PHSEG1_3TQ|PRSEG_1TQ); MCP2515_WriteByte(CNF3,PHSEG2_3TQ);
这个设置中,相位1缓冲段为3TQ,相位缓冲段2为3TQ,传播段为1TQ,再加上同步段的1TQ,总共为8TQ。只要改变CAN_125Kbps的值,就可以改变波特率了,这个值是一系列的宏定义。
#define CAN_10Kbps 0x31 #define CAN_25Kbps 0x13 #define CAN_50Kbps 0x09 #define CAN_100Kbps 0x04 #define CAN_125Kbps 0x03 #define CAN_250Kbps 0x01 #define CAN_500Kbps 0x00
4)设置ID值,TXB0SIDH里存储8位,TXB0SIDL存储3位。 MCP2515_WriteByte(TXB0SIDH,0x02); MCP2515_WriteByte(TXB0SIDL,0x00);
5)配置屏蔽寄存器与滤波寄存器的值 MCP2515_WriteByte(RXF0SIDH,0x02); MCP2515_WriteByte(RXF0SIDL,0x60);
MCP2515_WriteByte(RXM0SIDH,0xff); MCP2515_WriteByte(RXM0SIDL,0xe0);
6)工作模式
配置波特率与屏蔽寄存器与滤波寄存器的值需要在配置模式下进行,MCP2515共有5中模式,配置模式、正常模式、休眠模式、仅监听模式、回环模式。宏定义为:
#define REQOP_CONFIG 0x80 #define REQOP_LISTEN 0x60 #define REQOP_LOOPBACK 0x40 #define REQOP_SLEEP 0x20 #define REQOP_NORMAL 0x00
MCP2515_WriteByte(CANCTRL,REQOP_NORMAL|CLKOUT_ENABLED);
三、设置波特率为125K,发送的ID、屏蔽寄存器、滤波寄存器等值,清空中断标志,禁止中断,都在MCP2515初始化程序中。 1)初始化程序
void MCP2515_Init(void) { unsigned char temp=0;
MCP2515_Reset(); Delay_Nms(1);
MCP2515_WriteByte(CNF1,CAN_125Kbps); MCP2515_WriteByte(CNF2,0x80|PHSEG1_3TQ|PRSEG_1TQ); MCP2515_WriteByte(CNF3,PHSEG2_3TQ); MCP2515_WriteByte(TXB0SIDH,0x02); MCP2515_WriteByte(TXB0SIDL,0x00); MCP2515_WriteByte(RXB0SIDH,0x00); MCP2515_WriteByte(RXB0SIDL,0x00); MCP2515_WriteByte(RXB0CTRL,0x20); MCP2515_WriteByte(RXB0DLC,DLC_1); MCP2515_WriteByte(RXF0SIDH,0x02); MCP2515_WriteByte(RXF0SIDL,0x00);
MCP2515_WriteByte(RXM0SIDH,0xff); MCP2515_WriteByte(RXM0SIDL,0xe0); MCP2515_WriteByte(CANINTF,0x00); MCP2515_WriteByte(CANCTRL,REQOP_LOOPBACK|CLKOUT_ENABLED); temp=MCP2515_ReadByte(CANSTAT); if(OPMODE_LOOPBACK!=(temp&0xE0)) { MCP2515_WriteByte(CANCTRL,REQOP_LOOPBACK|CLKOUT_ENABLED); } }
2)发送程序 /******************************************************************************* * 函数名 : CAN_Send_Buffer * 描述 : CAN发送指定长度的数据 * 输入 : *CAN_TX_Buf(待发送数据缓冲区指针),len(待发送数据长度) * 输出 : 无 * 返回值 : 无 * 说明 : 无 *******************************************************************************/ void CAN_Send_Buffer(unsigned char *CAN_TX_Buf,unsigned char len) { unsigned char j,dly,count;
count=0; while(count<len) { dly=0; while((MCP2515_ReadByte(TXB0CTRL)&0x08) && (dly<50))//快速读某些状态指令,等待TXREQ标志清零 { Delay_Nms(1);//通过软件延时约nms(不准确) dly++; } for(j=0;j<8;) { MCP2515_WriteByte(TXB0D0+j,CAN_TX_Buf[count++]);//将待发送的数据写入发送缓冲寄存器 j++; if(count>=len) break; } MCP2515_WriteByte(TXB0DLC,j);//将本帧待发送的数据长度写入发送缓冲器0的发送长度寄存器 MCP2515_CS=0; MCP2515_WriteByte(TXB0CTRL,0x08);//请求发送报文 MCP2515_CS=1; } }
3)接受程序 /******************************************************************************* * 函数名 : CAN_Receive_Buffer * 描述 : CAN接收一帧数据 * 输入 : *CAN_TX_Buf(待接收数据缓冲区指针) * 输出 : 无 * 返回值 : len(接收到数据的长度,0~8字节) * 说明 : 无 *******************************************************************************/ unsigned char CAN_Receive_Buffer(unsigned char *CAN_RX_Buf) { unsigned char i=0,len=0,temp=0;
temp = MCP2515_ReadByte(CANINTF); if(temp & 0x01) { len=MCP2515_ReadByte(RXB0DLC);//读取接收缓冲器0接收到的数据长度(0~8个字节) while(i<len) { CAN_RX_Buf=MCP2515_ReadByte(RXB0D0+i);//把CAN接收到的数据放入指定缓冲区 i++; } } MCP2515_WriteByte(CANINTF,0);//清除中断标志位(中断标志寄存器必须由MCU清零) return len; }
四、51单片机程序,按下按键k3发送数据,并且每次第一个数据加1,第二个数据加2,接受标志位收到数据后会置1,回环模式下仍然要遵循滤波寄存器与屏蔽寄存器的设置要求,才能收到数据。收到的数据显示在数码管上。
#include <reg52.h> #include "MCP2515.H" #define uchar unsigned char #define uint unsigned int
uchar CAN_W_Buffer[8]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07}; unsigned char CAN_R_Buffer[8]={0}; uchar tab[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; sbit P3_2=P3^2; uchar len=0; void delay(uint ms) { uint j; for(;ms>0;ms--) for(j=124;j>0;j--); } void display(uchar dat,uchar pos) { P2=(P2&0xe3)+(pos<<2); P0=tab[dat]; delay(2); P0=0; }
void main(void) { MCP2515_Init(); while(1) { if(P3_2==0) { delay(20); if(!P3_2) { while(!P3_2); CAN_W_Buffer[0]++; CAN_W_Buffer[1]+=2; CAN_Send_Buffer(CAN_W_Buffer,2); } } if(MCP2515_ReadByte(CANINTF)&0X01) { len=CAN_Receive_Buffer(CAN_R_Buffer); } display(len,7); display(CAN_R_Buffer[0]/16,1); display(CAN_R_Buffer[0]%16,0); display(CAN_R_Buffer[1]/16,3); display(CAN_R_Buffer[1]%16,2); } }
完整程序下载: MCP2515模块-标准帧回环模式(STC89).rar
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |