您的位置 首页 电源

怎么一步一步树立CAN通讯

CAN通讯的优点在此就不多说了,10公里,5Kbs的速度是能保证的。第一步:硬件环境的建立。这里采用的是SJA1000作为总线控制器,CTM8251模块

CAN通讯的长处在此就不多说了,10公里,5Kb/s的速度是能确保的。
第一步:硬件环境的树立。
这儿选用的是SJA1000作为总线操控器,CTM8251模块作为总线驱动器。MCU选用的是MEGA16:运用I/O口模仿数据总线,当然也能够运用有总线的MCU:MCS-51,MEGA8515等。
原理图如下:

第二步:SJA1000的操控
首要阅览下SJA1000的手册,根本了解下SJA1000的结构,主要是寄存器方面的。还要了解下CAN总线方面的东西:BasicCAN,Peli CAN,长途帧,数据帧等等……
SJA1000作业之前需求装备一下,才干正常作业,没有通过装备的SJA1000回拉坏总线的:组成网络的时分,假如其间有的SJA1000没有正确装备,这个设备会搅扰总线,使其它设备的数据发送不出去。
怎样才干操控SJA1000呢,请看下面的SJA1000读写的时序图:

写的时序

依据时序要求,能够运用I/O口模仿总线了:
//**************************读SJA1000*************************//
uint Read_SJA1000(uint address)
{
uchar data;
asm(“nop”);
ALE_off;
WR_on;
RD_on;
CAN_cs_on;
DDRA=0xff; //数据口为输出
PORTA=address; //输出数据的地址
asm(“nop”);//delay5us(1);
ALE_on;
asm(“nop”);//delay5us(1);
//DDRA=0xff; //数据口为输出
PORTA=address; //输出数据的地址 //再次输出地址,确保共同。
asm(“nop”);//delay5us(1);
ALE_off;
//delay5us(1);
CAN_cs_off;
RD_off;
asm(“nop”);//delay5us(2);
asm(“nop”);
DDRA=0x00; //数据口为输入
PORTA=0xff; //上拉
asm(“nop”);
data=PINA; //取得数据
asm(“nop”);//delay5us(1);
RD_on;
CAN_cs_on;
asm(“nop”);//delay5us(2);
//dog();
return data;
}
//**************************写SJA10000*************************//
void Write_SJA1000(uint address,uint data)
{ asm(“nop”);
//uint temp1,temp2;
DDRA=0xff; //数据口为输出
PORTA=address; //输出数据的地址
CAN_cs_on;
ALE_off;
WR_on;
RD_on;
asm(“nop”);//delay5us(1);
ALE_on;
asm(“nop”);//delay5us(1);
//DDRA=0xff; //数据口为输出
PORTA=address; //输出数据的地址 再次输出地址,确保数据精确
asm(“nop”);//delay5us(1);
ALE_off;
//delay5us(1);
CAN_cs_off;
WR_off;
asm(“nop”);//delay5us(1);
asm(“nop”);
//DDRA=0xff;
PORTA=data; //输出数据
asm(“nop”);//delay5us(2);
WR_on;
PORTA=data; //再次输出数据,取保共同
CAN_cs_on;
asm(“nop”);//delay5us(2);
asm(“nop”);
//dog();
}
现在能够读写SJA1000了。
装备SJA1000需求使SJA1000进入复位形式,然后对一些寄存器写入数据。在这儿,CAN运用Pelican形式,速率为5K,双滤波作业,
//*************************CAN复位初始化********************//
void CAN_Init(void)
{ uchar i_temp=0,j_temp=0;

CLI();
//Read_SJA1000(CAN_IR); //读中止寄存器,铲除中止位
Write_SJA1000(CAN_MOD,0x01);
while(!(Read_SJA1000(CAN_MOD)&0x01))//确保进入复位形式,bit0.0不为1,再写CAN_MOD
{
Write_SJA1000(CAN_MOD,0x01);
dog();
}
Write_SJA1000(CAN_CDR,0xc8); //装备时钟分频寄存器-Pelican,CBP=1,
//封闭TX1中止与时钟输出
Write_SJA1000(CAN_AMR0,0xff); //装备检验屏蔽AMR0=0FFH
Write_SJA1000(CAN_AMR1,0x00); //装备检验屏蔽AMR1=000H
Write_SJA1000(CAN_AMR2,0xff); //装备检验屏蔽AMR2=0FFH
Write_SJA1000(CAN_AMR3,0x00); //装备检验屏蔽AMR3=000H
Write_SJA1000(CAN_ACR1,0x00); //装备检验代码ACR1=0:播送
Write_SJA1000(CAN_ACR3,addr); //装备检验代码ACR3=地址
Write_SJA1000(CAN_BTR0,0x7f); //装备总线守时–5kbps
Write_SJA1000(CAN_BTR1,0xff);
Write_SJA1000(CAN_OCR,0x1a); //装备输出操控
Write_SJA1000(CAN_EWLR,0xff); //装备过错报警约束为255
do
{
Write_SJA1000(CAN_MOD,0x00); //进入作业形式双滤波
dog();
}
while((Read_SJA1000(CAN_MOD))&0x01); // 承认复位标志是否被删去
Write_SJA1000(CAN_TXB+4,ID3); //装备发送缓冲区的ID3-
Write_SJA1000(CAN_IER,0x07); //装备SJA10000中止-过错报警/发送/接纳中止
SEI();
}
在这之前,需求获取设备的地址,便是读取拨码开关各个脚的电平。需求留意的是,SJA1000运用的是双滤波形式,呼应地址有:播送的:0x00,还有自己的地址:0x**。为什么要这么做呢,一个体系中,主机的地址一般是0X00,从机地址从0X01开端,这儿面假如有两个从机的地址相同,就很或许发生一些紊乱。从机一旦多了起来,查找地址相同的设备就有些麻烦了。
在程序的初始化的时分,进行SJA1000的装备。
第三部:作业程序
接下来,做的作业便是CAN试发送,别小看这个试发送,这但是处理地址重复的问题的哦,还能检测CAN网络是否正常。
//****************CAN第一次发送 通讯地址测验2e*****************//
void CAN_first_send(void)
{ //uchar add_temp=0;
uchar a_temp=0;
uchar SR_temp;
asm(“nop”); //延时
NET_LED_on; //翻开网络灯
do
{
a_temp=Read_SJA1000(CAN_SR);//读CAN_SR,直到SR.2=1:CPU能够发送数据
dog();
}
while(!(a_temp&0x04))
CLI(); //关CAN中止,即总中止

Write_SJA1000(CAN_TXB+0,0xc0); //发送长途帧0xc0
Write_SJA1000(CAN_TXB+1,0×00); //发送转接器地址
Write_SJA1000(CAN_TXB+2,addr); //发送传感器地址
Write_SJA1000(CAN_TXB+3,0x2e); //发送指令码0x2e
Write_SJA1000(CAN_TXB+4,ID3); //发送ID3
Write_SJA1000(CAN_CMR,0x01); //发动发送,
//网络毛病过错在中止中处理,短接H、L,按复位,先亮绿灯,后黄灯亮
asm(“nop”);
//SEI();
}
SJA1000的中止引脚接到MEGA16的INT1上,需求在程序初始化的时分,装备一些INT1,使MCU能呼应SJA1000的中止。
数据发送前,点亮网络指示灯,什么时分平息它呢,在发送中止中平息它。
下面看看MCU对SJA1000中止的一些处理:在这儿只处理:接纳中止、发送中止、总线封闭中止。
#pragma interrupt_handler can_int:3
void can_int(void)
{
asm(“nop”);
CAN_IR_temp=Read_SJA1000(CAN_IR); //读取中止寄存器
if(CAN_IR_temp&0x01) //接纳中止
{
Get_RXB_temp();

if(RxBuffer[0]==0x80) //地址测验数据帧
{
reload(); //数据帧中有和自己相同的地址
}
if(RxBuffer[0]==0xc0) // 长途帧则开释接纳缓冲区
{
type=RxBuffer[3]; //读指令码

//处理指令码

if(type==0x30)
{ if(type==0x34)
{CAN_now_value_send();type=0;} //传瞬时值数据
if (type==0x27)
{reload(); type=0;}//设备复位
if(type==0x2e)
{active();type=0;} //通讯地址测验
}

Write_SJA1000(CAN_CMR,0x04); //开释接纳缓冲区

}

if(CAN_IR_temp&0x02) //发送中止
{
NET_LED_off; //封闭网络灯
ERR_LED_off; //封闭毛病灯
CANBE_JSQ=0; //复位总线封闭计数器
asm(“nop”);
}
if(CAN_IR_temp&0x04) //过错报警中止(仅有总线封闭处理)
{ //读状况寄存器,SR.7总线封闭:CAN操控器不参加总线活动
CAN_SR_temp=Read_SJA1000(CAN_SR);

if(CAN_SR_temp&0x80)
{
CANBE_JSQ=CANBE_JSQ+1; //封闭次数加1
if(CANBE_JSQ{
do
{
Write_SJA1000(CAN_MOD,0x00); //从头进入作业形式

}
while((Read_SJA1000(CAN_MOD))&0x01);//等候进入作业形式
Write_SJA1000(CAN_CMR,0x01); //发动CAN从头发送
}
if(CANBE_JSQ>=CANBE_C) //总线封闭次数抵达设定次数
{
NET_LED_off; //封闭网络灯
ERR_LED_on; //翻开毛病灯
CANBE_JSQ=0; //复位总线封闭计数器
do
{
Write_SJA1000(CAN_MOD,0x00); //从头进入作业形式

}
while((Read_SJA1000(CAN_MOD))&0x01);//等候进入作业形式
Write_SJA1000(CAN_CMR,0x01); //发动CAN从头发送
CANBE_JSQ=CANBE_C; //避免CANBE_JSQ溢出

}
}
asm(“nop”);
}
}

中止程序中,对指令码等于0x2e的处理程序是:active();
active()程序如下:
//************************通讯地址测验2EH***********************//
void active(void)
{
uchar temp1,temp2;
asm(“nop”); //延时
NET_LED_on; //翻开网络灯
CLI(); //关CAN中止,即总中止
do
{
temp1=Read_SJA1000(CAN_SR);//读CAN_SR,直到SR.2=1:CPU能够发送数据
dog();
}
while(!(temp1&0x04));

Write_SJA1000(CAN_TXB+0,0×80); //发送数据帧0x80
temp2=Read_SJA1000(CAN_RXB+1);
Write_SJA1000(CAN_TXB+1,temp2); //发送转接器地址
Write_SJA1000(CAN_TXB+2,addr); //发送传感器地址
Write_SJA1000(CAN_TXB+3,0x2e); //发送指令码0x2e
Write_SJA1000(CAN_TXB+4,ID3); //发送ID3
Write_SJA1000(CAN_CMR,0x01); //发动发送
SEI(); //开中止
asm(“nop”);

}
我们细心看看 active()程序的内容,发送了一个没有数据的数据帧:0X80,再回过头看看中止处理函数,里边有这段程序, if(RxBuffer[0]==0x80) //地址测验数据帧
{
reload(); //数据帧中有和自己相同的地址
}
reload(); 程序很简单,便是中止喂狗,等候复位。复位之后呢,它会进行试发送,哈哈,接下来的两个地址相同的设备就“打架”起来了,现象便是一个设备不断复位,一个设备通讯灯不断闪耀。怎样样,很简单就判别出哪两个地址重复了。
指令码等于0x27时,设备复位,一般是主机发送这个长途帧。
0x34时,发送数据:
//************************瞬时值发送 34H*********************//
void CAN_now_value_send(void)
{
//uchar a_temp=0;
uchar c_temp=0;
js_now_send_value(); //核算需求发送的瞬间数值
asm(“nop”); //延时
NET_LED_on; //翻开网络灯
do
{
b_temp=Read_SJA1000(CAN_SR); //读CAN_SR,直到SR.2=1:CPU能够发送数据
dog();
}
while(!(b_temp&0x04))
CLI(); //关CAN中止,即总中止

Write_SJA1000(CAN_TXB+0,0×84); //发送数据帧0x84
Write_SJA1000(CAN_TXB+1,RxBuffer[1]); //发送转接器地址
Write_SJA1000(CAN_TXB+2,addr); //发送传感器地址
Write_SJA1000(CAN_TXB+3,0×34); //发送指令码0x34
Write_SJA1000(CAN_TXB+4,ID3); //发送ID3
Write_SJA1000(CAN_TXB+5,CBDJ_Send_L); //
Write_SJA1000(CAN_TXB+6,CBDJ_Send_H); //
Write_SJA1000(CAN_TXB+7,GD_Send_L); //
Write_SJA1000(CAN_TXB+8,GD_Send_H); //
Write_SJA1000(CAN_CMR,0x01); //发动发送
SEI(); //开中止
asm(“nop”);

}
发送了一个数据帧,这个数据帧有四字节的数据。
CAN的数据帧最多支撑有8个字节的数据帧,假如数据较多,能够分为多个数据帧,在指令码里边区别这些数据帧。

第四步:树立自己的CAN通讯网络。
主机能够是一台有CAN接口的核算机,一般在核算机上装一个CAN接口卡,有ISA接口的,比方PCL-841;PCI接口的。CAN卡的销售商都会供给驱动,依托驱动里边的函数,来操控CAN卡,此项不是特长,欠好多说,横竖便是这个思路。

好了,昨日从南京回来的路上,就考虑发个CAN的东西。我们这个论坛,现在还没有多少关于CAN的帖子,意在抛砖引玉…………本坛高手许多,尤其是有许多潜水的高高手~~~~
——————–
程序中的一些DEFINE
//******************引脚信号界说***************************//
#define CS_1 (PORTB|= (1<<4 )) //AD7705片选
#define CS_0 (PORTB&= ~(1<<4 ))
#define DRDY (PINB&0x08) //AD转化DRDY信号输入
#define NET_LED_off (PORTB|= (1<<0 )) //网络毛病灯高电平,平息
#define NET_LED_on (PORTB&= ~(1<<0 )) //网络毛病灯低电平,点亮
#define ERR_LED_off (PORTB|= (1<<1 )) //设备毛病灯高电平,平息
#define ERR_LED_on (PORTB&= ~(1<<1 )) //设备毛病灯低电平,点亮
#define DOG_on (PORTB|= (1<<2 )) //看门狗高
#define DOG_off (PORTB&= ~(1<<2 )) //看门狗低
#define WR_on (PORTD|= (1<<0 )) //WR高
#define WR_off (PORTD&= ~(1<<0)) //WR低
#define RD_on (PORTD|= (1<<1 )) //RD高
#define RD_off (PORTD&= ~(1<<1)) //RD低
#define CAN_cs_on (PORTD|= (1<<4 )) //CAN高
#define CAN_cs_off (PORTD&= ~(1<<4)) //CAN低
#define ALE_on (PORTD|= (1<<2 )) //ALE高
#define ALE_off (PORTD&= ~(1<<2)) //ALE低
#define FALSE 0
#define TRUE 1
#define CANBE_C 6 //总线封闭次数设定值
//*******************CAN寄存器地址**************************//
#define CAN_MOD 0 //形式寄存器
#define CAN_CMR 1 //指令寄存器 只写
#define CAN_SR 2 //状况寄存器 只读
#define CAN_IR 3 //中止寄存器 只读
#define CAN_IER 4 //中止使能寄存器
#define CAN_BTR0 6 //总线守时寄存器0
#define CAN_BTR1 7 //总线守时寄存器1
#define CAN_OCR 8 //输出操控寄存器
#define CAN_TEST 9 //测验寄存器
#define CAN_ALC 11 //裁定丢掉寄存器
#define CAN_ECC 12 //过错代码捕捉寄存器
#define CAN_EWLR 13 //过错报警约束寄存器
#define CAN_EXERR 14 //RX过错计数寄存器
#define CAN_TXERR 15 //TX过错计数寄存器
#define CAN_ACR0 16 //检验码寄存器0
#define CAN_ACR1 17 //检验码寄存器1
#define CAN_ACR2 18 //检验码寄存器2
#define CAN_ACR3 19 //检验码寄存器3
#define CAN_AMR0 20 //检验屏蔽寄存器0
#define CAN_AMR1 21 //检验屏蔽寄存器1
#define CAN_AMR2 22 //检验屏蔽寄存器2
#define CAN_AMR3 23 //检验屏蔽寄存器3
#define CAN_TXB 16 //发送缓冲区首地址(作业形式)
#define CAN_RXB 16 //接纳缓冲区首地址(作业形式)
#define CAN_RMC 29 //RX信息计数器
#define CAN_RBSA 30 //RX缓冲区开始地址寄存器
#define CAN_CDR 31 //时钟分频器
#define ID3 00 //ID3
—————————–
初始化程序
uchar main_ch=0;
IO_Init(); //I/O口初始化
INT1_Init();
GET_add(); //获取地址,地址为0,重复获取地址,直到不为0。
NET_LED_on;
ERR_LED_on; //初始化中,点亮毛病灯和通讯灯,
delay50ms(2);
dog();
delay50ms(2);
dog();
delay50ms(2);
dog();
CAN_Init(); //CAN初始化
NET_LED_off;
ERR_LED_off;
SEI();
CAN_first_send(); //CAN试发送
delay50ms(1);
dog();
void GET_add(void) //地址获取程序
{
uchar add_temp=0,add_temp1=0,add_temp2=0,add_temp3=0,addr_temp=0;
do
{
dog();
NET_LED_on;
ERR_LED_on;
add_temp1=PINC&0xc3;
add_temp2=add_temp1>>4;
add_temp1=add_temp1&0x03;
add_temp3=(PIND&0xe0)>>1;
add_temp=add_temp1+add_temp2+add_temp3;
add_temp=(~add_temp)&0x7f;
addr=add_temp;
delay50ms(2);
}
while(addr==0);

}

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/dianyuan/234225.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部