<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

EEPW首頁(yè) > 嵌入式系統 > 設計應用 > 如何一步一步建立CAN通訊

如何一步一步建立CAN通訊

作者: 時(shí)間:2016-12-14 來(lái)源:網(wǎng)絡(luò ) 收藏
CAN通訊的優(yōu)點(diǎn)在此就不多說(shuō)了,10公里,5Kb/s的速度是能保證的。
第一步:硬件環(huán)境的建立。
這里采用的是SJA1000作為總線(xiàn)控制器,CTM8251模塊作為總線(xiàn)驅動(dòng)器。MCU采用的是MEGA16:利用I/O口模擬數據總線(xiàn),當然也可以使用有總線(xiàn)的MCU:MCS-51,MEGA8515等。
原理圖如下:

第二步:SJA1000的控制
首先閱讀下SJA1000的手冊,基本了解下SJA1000的結構,主要是寄存器方面的。還要了解下CAN總線(xiàn)方面的東西:BasicCAN,Peli CAN,遠程幀,數據幀等等……
SJA1000工作之前需要配置一下,才能正常工作,沒(méi)有經(jīng)過(guò)配置的SJA1000回拉壞總線(xiàn)的:組成網(wǎng)絡(luò )的時(shí)候,如果其中有的SJA1000沒(méi)有正確配置,這個(gè)設備會(huì )干擾總線(xiàn),使其它設備的數據發(fā)送不出去。
怎么才能控制SJA1000呢,請看下面的SJA1000讀寫(xiě)的時(shí)序圖:

寫(xiě)的時(shí)序

根據時(shí)序要求,可以利用I/O口模擬總線(xiàn)了:
//**************************讀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;
}
//**************************寫(xiě)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();
}
現在可以讀寫(xiě)SJA1000了。
配置SJA1000需要使SJA1000進(jìn)入復位模式,然后對一些寄存器寫(xiě)入數據。在這里,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))//保證進(jìn)入復位模式,bit0.0不為1,再寫(xiě)CAN_MOD
{
Write_SJA1000(CAN_MOD,0x01);
dog();
}
Write_SJA1000(CAN_CDR,0xc8); //配置時(shí)鐘分頻寄存器-Pelican,CBP=1,
//關(guān)閉TX1中斷與時(shí)鐘輸出
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); //配置總線(xiàn)定時(shí)--5kbps
Write_SJA1000(CAN_BTR1,0xff);
Write_SJA1000(CAN_OCR,0x1a); //配置輸出控制
Write_SJA1000(CAN_EWLR,0xff); //配置錯誤報警限制為255
do
{
Write_SJA1000(CAN_MOD,0x00); //進(jìn)入工作模式雙濾波
dog();
}
while((Read_SJA1000(CAN_MOD))&0x01); // 確認復位標志是否被刪除
Write_SJA1000(CAN_TXB+4,ID3); //配置發(fā)送緩沖區的ID3-
Write_SJA1000(CAN_IER,0x07); //配置SJA10000中斷-錯誤報警/發(fā)送/接收中斷
SEI();
}
在這之前,需要獲取設備的地址,就是讀取撥碼開(kāi)關(guān)各個(gè)腳的電平。需要注意的是,SJA1000使用的是雙濾波模式,響應地址有:廣播的:0x00,還有自己的地址:0x**。為什么要這么做呢,一個(gè)系統中,主機的地址一般是0X00,從機地址從0X01開(kāi)始,這里面如果有兩個(gè)從機的地址一樣,就很可能產(chǎn)生一些混亂。從機一旦多了起來(lái),查找地址相同的設備就有些麻煩了。
在程序的初始化的時(shí)候,進(jìn)行SJA1000的配置。
第三部:工作程序
接下來(lái),做的工作就是CAN試發(fā)送,別小看這個(gè)試發(fā)送,這可是解決地址重復的問(wèn)題的哦,還能檢測CAN網(wǎng)絡(luò )是否正常。
//****************CAN第一次發(fā)送 通訊地址測試2e*****************//
void CAN_first_send(void)
{ //uchar add_temp=0;
uchar a_temp=0;
uchar SR_temp;
asm("nop"); //延時(shí)
NET_LED_on; //打開(kāi)網(wǎng)絡(luò )燈
do
{
a_temp=Read_SJA1000(CAN_SR);//讀CAN_SR,直到SR.2=1:CPU可以發(fā)送數據
dog();
}
while(!(a_temp&0x04))
CLI(); //關(guān)CAN中斷,即總中斷

Write_SJA1000(CAN_TXB+0,0xc0); //發(fā)送遠程幀0xc0
Write_SJA1000(CAN_TXB+1,0x00); //發(fā)送轉接器地址
Write_SJA1000(CAN_TXB+2,addr); //發(fā)送傳感器地址
Write_SJA1000(CAN_TXB+3,0x2e); //發(fā)送命令碼0x2e
Write_SJA1000(CAN_TXB+4,ID3); //發(fā)送ID3
Write_SJA1000(CAN_CMR,0x01); //啟動(dòng)發(fā)送,
//網(wǎng)絡(luò )故障錯誤在中斷中處理,短接H、L,按復位,先亮綠燈,后黃燈亮
asm("nop");
//SEI();
}
SJA1000的中斷引腳接到MEGA16的INT1上,需要在程序初始化的時(shí)候,配置一些INT1,使MCU能響應SJA1000的中斷。
數據發(fā)送前,點(diǎn)亮網(wǎng)絡(luò )指示燈,什么時(shí)候熄滅它呢,在發(fā)送中斷中熄滅它。
下面看看MCU對SJA1000中斷的一些處理:在這里只處理:接收中斷、發(fā)送中斷、總線(xiàn)關(guān)閉中斷。
#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;} //傳瞬時(shí)值數據
if (type==0x27)
{reload(); type=0;}//裝置復位
if(type==0x2e)
{active();type=0;} //通訊地址測試
}

Write_SJA1000(CAN_CMR,0x04); //釋放接收緩沖區

}

if(CAN_IR_temp&0x02) //發(fā)送中斷
{
NET_LED_off; //關(guān)閉網(wǎng)絡(luò )燈
ERR_LED_off; //關(guān)閉故障燈
CANBE_JSQ=0; //復位總線(xiàn)關(guān)閉計數器
asm("nop");
}
if(CAN_IR_temp&0x04) //錯誤報警中斷(僅有總線(xiàn)關(guān)閉處理)
{ //讀狀態(tài)寄存器,SR.7總線(xiàn)關(guān)閉:CAN控制器不參與總線(xiàn)活動(dòng)
CAN_SR_temp=Read_SJA1000(CAN_SR);

if(CAN_SR_temp&0x80)
{
CANBE_JSQ=CANBE_JSQ+1; //關(guān)閉次數加1
if(CANBE_JSQ{
do
{
Write_SJA1000(CAN_MOD,0x00); //重新進(jìn)入工作模式

}
while((Read_SJA1000(CAN_MOD))&0x01);//等待進(jìn)入工作模式
Write_SJA1000(CAN_CMR,0x01); //啟動(dòng)CAN重新發(fā)送
}
if(CANBE_JSQ>=CANBE_C) //總線(xiàn)關(guān)閉次數到達設定次數
{
NET_LED_off; //關(guān)閉網(wǎng)絡(luò )燈
ERR_LED_on; //打開(kāi)故障燈
CANBE_JSQ=0; //復位總線(xiàn)關(guān)閉計數器
do
{
Write_SJA1000(CAN_MOD,0x00); //重新進(jìn)入工作模式

}
while((Read_SJA1000(CAN_MOD))&0x01);//等待進(jìn)入工作模式
Write_SJA1000(CAN_CMR,0x01); //啟動(dòng)CAN重新發(fā)送
CANBE_JSQ=CANBE_C; //防止CANBE_JSQ溢出

}
}
asm("nop");
}
}

中斷程序中,對命令碼等于0x2e的處理程序是:active();
active()程序如下:
//************************通訊地址測試2EH***********************//
void active(void)
{
uchar temp1,temp2;
asm("nop"); //延時(shí)
NET_LED_on; //打開(kāi)網(wǎng)絡(luò )燈
CLI(); //關(guān)CAN中斷,即總中斷
do
{
temp1=Read_SJA1000(CAN_SR);//讀CAN_SR,直到SR.2=1:CPU可以發(fā)送數據
dog();
}
while(!(temp1&0x04));

Write_SJA1000(CAN_TXB+0,0x80); //發(fā)送數據幀0x80
temp2=Read_SJA1000(CAN_RXB+1);
Write_SJA1000(CAN_TXB+1,temp2); //發(fā)送轉接器地址
Write_SJA1000(CAN_TXB+2,addr); //發(fā)送傳感器地址
Write_SJA1000(CAN_TXB+3,0x2e); //發(fā)送命令碼0x2e
Write_SJA1000(CAN_TXB+4,ID3); //發(fā)送ID3
Write_SJA1000(CAN_CMR,0x01); //啟動(dòng)發(fā)送
SEI(); //開(kāi)中斷
asm("nop");

}
大家仔細看看 active()程序的內容,發(fā)送了一個(gè)沒(méi)有數據的數據幀:0X80,再回過(guò)頭看看中斷處理函數,里面有這段程序, if(RxBuffer[0]==0x80) //地址測試數據幀
{
reload(); //數據幀中有和自己相同的地址
}
reload(); 程序很簡(jiǎn)單,就是停止喂狗,等待復位。復位之后呢,它會(huì )進(jìn)行試發(fā)送,哈哈,接下來(lái)的兩個(gè)地址相同的設備就“打架”起來(lái)了,現象就是一個(gè)設備不斷復位,一個(gè)設備通訊燈不斷閃爍。怎么樣,很容易就判斷出哪兩個(gè)地址重復了。
命令碼等于0x27時(shí),設備復位,一般是主機發(fā)送這個(gè)遠程幀。
0x34時(shí),發(fā)送數據:
//************************瞬時(shí)值發(fā)送 34H*********************//
void CAN_now_value_send(void)
{
//uchar a_temp=0;
uchar c_temp=0;
js_now_send_value(); //計算需要發(fā)送的瞬間數值
asm("nop"); //延時(shí)
NET_LED_on; //打開(kāi)網(wǎng)絡(luò )燈
do
{
b_temp=Read_SJA1000(CAN_SR); //讀CAN_SR,直到SR.2=1:CPU可以發(fā)送數據
dog();
}
while(!(b_temp&0x04))
CLI(); //關(guān)CAN中斷,即總中斷

Write_SJA1000(CAN_TXB+0,0x84); //發(fā)送數據幀0x84
Write_SJA1000(CAN_TXB+1,RxBuffer[1]); //發(fā)送轉接器地址
Write_SJA1000(CAN_TXB+2,addr); //發(fā)送傳感器地址
Write_SJA1000(CAN_TXB+3,0x34); //發(fā)送命令碼0x34
Write_SJA1000(CAN_TXB+4,ID3); //發(fā)送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); //啟動(dòng)發(fā)送
SEI(); //開(kāi)中斷
asm("nop");

}
發(fā)送了一個(gè)數據幀,這個(gè)數據幀有四字節的數據。
CAN的數據幀最多支持有8個(gè)字節的數據幀,如果數據較多,可以分為多個(gè)數據幀,在命令碼里面區分這些數據幀。

第四步:建立自己的CAN通訊網(wǎng)絡(luò )。
主機可以是一臺有CAN接口的計算機,一般在計算機上裝一個(gè)CAN接口卡,有ISA接口的,比如PCL-841;PCI接口的。CAN卡的銷(xiāo)售商都會(huì )提供驅動(dòng),依靠驅動(dòng)里面的函數,來(lái)控制CAN卡,此項不是專(zhuān)長(cháng),不好多說(shuō),反正就是這個(gè)思路。

好了,昨天從南京回來(lái)的路上,就考慮發(fā)個(gè)CAN的東西。咱們這個(gè)論壇,目前還沒(méi)有多少關(guān)于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 )) //網(wǎng)絡(luò )故障燈高電平,熄滅
#define NET_LED_on (PORTB&= ~(1<<0 )) //網(wǎng)絡(luò )故障燈低電平,點(diǎn)亮
#define ERR_LED_off (PORTB|= (1<<1 )) //裝置故障燈高電平,熄滅
#define ERR_LED_on (PORTB&= ~(1<<1 )) //裝置故障燈低電平,點(diǎn)亮
#define DOG_on (PORTB|= (1<<2 )) //看門(mén)狗高
#define DOG_off (PORTB&= ~(1<<2 )) //看門(mén)狗低
#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 //總線(xiàn)關(guān)閉次數設定值
//*******************CAN寄存器地址**************************//
#define CAN_MOD 0 //模式寄存器
#define CAN_CMR 1 //命令寄存器 只寫(xiě)
#define CAN_SR 2 //狀態(tài)寄存器 只讀
#define CAN_IR 3 //中斷寄存器 只讀
#define CAN_IER 4 //中斷使能寄存器
#define CAN_BTR0 6 //總線(xiàn)定時(shí)寄存器0
#define CAN_BTR1 7 //總線(xiàn)定時(shí)寄存器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 //發(fā)送緩沖區首地址(工作模式)
#define CAN_RXB 16 //接收緩沖區首地址(工作模式)
#define CAN_RMC 29 //RX信息計數器
#define CAN_RBSA 30 //RX緩沖區起始地址寄存器
#define CAN_CDR 31 //時(shí)鐘分頻器
#define ID3 00 //ID3
-----------------------------
初始化程序
uchar main_ch=0;
IO_Init(); //I/O口初始化
INT1_Init();
GET_add(); //獲取地址,地址為0,反復獲取地址,直到不為0。
NET_LED_on;
ERR_LED_on; //初始化中,點(diǎn)亮故障燈和通訊燈,
delay50ms(2);
dog();
delay50ms(2);
dog();
delay50ms(2);
dog();
CAN_Init(); //CAN初始化
NET_LED_off;
ERR_LED_off;
SEI();
CAN_first_send(); //CAN試發(fā)送
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);

}


關(guān)鍵詞: CAN通訊硬件環(huán)

評論


技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>