如何一步一步建立CAN通讯
时间:12-14
来源:互联网
点击:
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,0x00); //发送转接器地址
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,0x80); //发送数据帧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,0x84); //发送数据帧0x84
Write_SJA1000(CAN_TXB+1,RxBuffer[1]); //发送转接器地址
Write_SJA1000(CAN_TXB+2,addr); //发送传感器地址
Write_SJA1000(CAN_TXB+3,0x34); //发送命令码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 //时钟
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,0x00); //发送转接器地址
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,0x80); //发送数据帧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,0x84); //发送数据帧0x84
Write_SJA1000(CAN_TXB+1,RxBuffer[1]); //发送转接器地址
Write_SJA1000(CAN_TXB+2,addr); //发送传感器地址
Write_SJA1000(CAN_TXB+3,0x34); //发送命令码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 //时钟
CAN通讯硬件环 相关文章:
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)
- Windows CE 进程、线程和内存管理(二)(11-09)