[电工&电子] 51单片机CAN 通讯,MCP2515芯片,普中科技单片机--回环模式(含源程序下载

[复制链接]
查看346 | 回复0 | 2024-7-20 16:17:27 | 显示全部楼层 |阅读模式
>

一、原来用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是数码管控制管脚,所以我稍微变了一下。

//MCP2515引脚定义sbit MCP2515_SCK  = P2^6;//SPI时钟引脚 sbit MCP2515_MOSI = P2^5;//SPI主机输出从机输入引脚 sbit MCP2515_MISO = P2^1;//SPI主机输入从机输出引脚 sbit MCP2515_CS   = P2^0;//SPI片选引脚 

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文件里已经给做好了,只需要简单改一改就可以了。

//设置波特率为125Kbps  //set CNF1,SJW=00,长度为1TQ,BRP=49,TQ=[2*(BRP+1)]/Fsoc=2*50/8M=12.5us  MCP2515_WriteByte(CNF1,CAN_125Kbps);  //set CNF2,SAM=0,在采样点对总线进行一次采样,PHSEG1=(2+1)TQ=3TQ,PRSEG=(0+1)TQ=1TQ  MCP2515_WriteByte(CNF2,0x80|PHSEG1_3TQ|PRSEG_1TQ);  //set CNF3,PHSEG2=(2+1)TQ=3TQ,同时当CANCTRL.CLKEN=1时设定CLKOUT引脚为时间输出使能位  MCP2515_WriteByte(CNF3,PHSEG2_3TQ);

  这个设置中,相位1缓冲段为3TQ,相位缓冲段2为3TQ,传播段为1TQ,再加上同步段的1TQ,总共为8TQ。只要改变CAN_125Kbps的值,就可以改变波特率了,这个值是一系列的宏定义。

//MCP2515波特率预分频#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);//发送缓冲器0标准标识符高位MCP2515_WriteByte(TXB0SIDL,0x00);//发送缓冲器0标准标识符低位

5)配置屏蔽寄存器与滤波寄存器的值

MCP2515_WriteByte(RXF0SIDH,0x02);//配置验收滤波寄存器n标准标识符高位  MCP2515_WriteByte(RXF0SIDL,0x60);//配置验收滤波寄存器n标准标识符低位//  MCP2515_WriteByte(RXF1SIDH,0x02);//配置验收滤波寄存器n标准标识符高位//  MCP2515_WriteByte(RXF1SIDL,0x40);//配置验收滤波寄存器n标准标识符低位  MCP2515_WriteByte(RXM0SIDH,0xff);//配置验收屏蔽寄存器n标准标识符高位  MCP2515_WriteByte(RXM0SIDL,0xe0);//配置验收屏蔽寄存器n标准标识符低位

6)工作模式

配置波特率与屏蔽寄存器与滤波寄存器的值需要在配置模式下进行,MCP2515共有5中模式,配置模式、正常模式、休眠模式、仅监听模式、回环模式。宏定义为:

/* CANCTRL */ #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);//将MCP2515设置为正常模式,退出配置模式

三、设置波特率为125K,发送的ID、屏蔽寄存器、滤波寄存器等值,清空中断标志,禁止中断,都在MCP2515初始化程序中。

1)初始化程序

/******************************************************************************** 函数名  : MCP2515_Init* 描述    : MCP2515初始化配置* 输入    : 无* 输出    : 无* 返回值  : 无* 说明    : 初始化包括:软件复位、工作波特率设置、标识符相关配置等。*******************************************************************************/void MCP2515_Init(void){  unsigned char temp=0;
MCP2515_Reset(); //发送复位指令软件复位MCP2515 Delay_Nms(1); //通过软件延时约nms(不准确)
//设置波特率为125Kbps //set CNF1,SJW=00,长度为1TQ,BRP=49,TQ=[2*(BRP+1)]/Fsoc=2*50/8M=12.5us MCP2515_WriteByte(CNF1,CAN_125Kbps); //set CNF2,SAM=0,在采样点对总线进行一次采样,PHSEG1=(2+1)TQ=3TQ,PRSEG=(0+1)TQ=1TQ MCP2515_WriteByte(CNF2,0x80|PHSEG1_3TQ|PRSEG_1TQ); //set CNF3,PHSEG2=(2+1)TQ=3TQ,同时当CANCTRL.CLKEN=1时设定CLKOUT引脚为时间输出使能位 MCP2515_WriteByte(CNF3,PHSEG2_3TQ); MCP2515_WriteByte(TXB0SIDH,0x02);//发送缓冲器0标准标识符高位 MCP2515_WriteByte(TXB0SIDL,0x00);//发送缓冲器0标准标识符低位 MCP2515_WriteByte(RXB0SIDH,0x00);//清空接收缓冲器0的标准标识符高位 MCP2515_WriteByte(RXB0SIDL,0x00);//清空接收缓冲器0的标准标识符低位 MCP2515_WriteByte(RXB0CTRL,0x20);//仅仅接收标准标识符的有效信息 MCP2515_WriteByte(RXB0DLC,DLC_1);//设置接收数据的长度为8个字节 MCP2515_WriteByte(RXF0SIDH,0x02);//配置验收滤波寄存器n标准标识符高位 MCP2515_WriteByte(RXF0SIDL,0x00);//配置验收滤波寄存器n标准标识符低位// MCP2515_WriteByte(RXF1SIDH,0x02);//配置验收滤波寄存器n标准标识符高位// MCP2515_WriteByte(RXF1SIDL,0x40);//配置验收滤波寄存器n标准标识符低位 MCP2515_WriteByte(RXM0SIDH,0xff);//配置验收屏蔽寄存器n标准标识符高位 MCP2515_WriteByte(RXM0SIDL,0xe0);//配置验收屏蔽寄存器n标准标识符低位 MCP2515_WriteByte(CANINTF,0x00);//清空CAN中断标志寄存器的所有位(必须由MCU清空) //MCP2515_WriteByte(CANINTE,0x01);//配置CAN中断使能寄存器的接收缓冲器0满中断使能,其它位禁止中断 MCP2515_WriteByte(CANCTRL,REQOP_LOOPBACK|CLKOUT_ENABLED);//将MCP2515设置为正常模式,退出配置模式 temp=MCP2515_ReadByte(CANSTAT);//读取CAN状态寄存器的值 if(OPMODE_LOOPBACK!=(temp&0xE0))//判断MCP2515是否已经进入回环 { MCP2515_WriteByte(CANCTRL,REQOP_LOOPBACK|CLKOUT_ENABLED);//再次将MCP2515设置为回环模式,退出配置模式 }}

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,回环模式下仍然要遵循滤波寄存器与屏蔽寄存器的设置要求,才能收到数据。收到的数据显示在数码管上。

/********************************************************************************** * 工程名  :MCP2515模块-标准帧例程 * 普中科技15单片机开发板+MCP2515 CAN模块 * 硬件连接:            P2^6 -> MCP2515_SCK     SPI时钟引脚  *         P2^5 -> MCP2515_MOSI    SPI主机输出从机输入引脚  *         P2^1 -> MCP2515_MISO    SPI主机输入从机输出引脚  *         P2^0 -> MCP2515_CS    SPI片选引脚 
**********************************************************************************/#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};//显示0~F的值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;}/******************************************************************************** 函数名 : main* 描述 : 主函数,用户程序从main函数开始运行* 输入 : 无* 输出 : 无* 返回值 : 无* 说明 : 无*******************************************************************************/void main(void){ MCP2515_Init(); //MCP2515初始化配置 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);//CAN发送指定长度的数据 } } 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




免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册哦

x
您需要登录后才可以回帖 登录 | 注册哦

本版积分规则