<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          如何一步一步建立CAN通訊

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

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

          寫的時(shí)序

          根據(jù)時(shí)序要求,可以利用I/O口模擬總線了:
          //**************************讀SJA1000*************************//
          uint Read_SJA1000(uint address)
          {
          uchar data;
          asm("nop");
          ALE_off;
          WR_on;
          RD_on;
          CAN_cs_on;
          DDRA=0xff; //數(shù)據(jù)口為輸出
          PORTA=address; //輸出數(shù)據(jù)的地址
          asm("nop");//delay5us(1);
          ALE_on;
          asm("nop");//delay5us(1);
          //DDRA=0xff; //數(shù)據(jù)口為輸出
          PORTA=address; //輸出數(shù)據(jù)的地址 //再次輸出地址,確保一致。
          asm("nop");//delay5us(1);
          ALE_off;
          //delay5us(1);
          CAN_cs_off;
          RD_off;
          asm("nop");//delay5us(2);
          asm("nop");
          DDRA=0x00; //數(shù)據(jù)口為輸入
          PORTA=0xff; //上拉
          asm("nop");
          data=PINA; //獲得數(shù)據(jù)
          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; //數(shù)據(jù)口為輸出
          PORTA=address; //輸出數(shù)據(jù)的地址
          CAN_cs_on;
          ALE_off;
          WR_on;
          RD_on;
          asm("nop");//delay5us(1);
          ALE_on;
          asm("nop");//delay5us(1);
          //DDRA=0xff; //數(shù)據(jù)口為輸出
          PORTA=address; //輸出數(shù)據(jù)的地址 再次輸出地址,確保數(shù)據(jù)準(zhǔn)確
          asm("nop");//delay5us(1);
          ALE_off;
          //delay5us(1);
          CAN_cs_off;
          WR_off;
          asm("nop");//delay5us(1);
          asm("nop");
          //DDRA=0xff;
          PORTA=data; //輸出數(shù)據(jù)
          asm("nop");//delay5us(2);
          WR_on;
          PORTA=data; //再次輸出數(shù)據(jù),取保一致
          CAN_cs_on;
          asm("nop");//delay5us(2);
          asm("nop");
          //dog();
          }
          現(xiàn)在可以讀寫SJA1000了。
          配置SJA1000需要使SJA1000進(jìn)入復(fù)位模式,然后對(duì)一些寄存器寫入數(shù)據(jù)。在這里,CAN使用Pelican模式,速率為5K,雙濾波工作,
          //*************************CAN復(fù)位初始化********************//
          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)入復(fù)位模式,bit0.0不為1,再寫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); //配置驗(yàn)收屏蔽AMR0=0FFH
          Write_SJA1000(CAN_AMR1,0x00); //配置驗(yàn)收屏蔽AMR1=000H
          Write_SJA1000(CAN_AMR2,0xff); //配置驗(yàn)收屏蔽AMR2=0FFH
          Write_SJA1000(CAN_AMR3,0x00); //配置驗(yàn)收屏蔽AMR3=000H
          Write_SJA1000(CAN_ACR1,0x00); //配置驗(yàn)收代碼ACR1=0:廣播
          Write_SJA1000(CAN_ACR3,addr); //配置驗(yàn)收代碼ACR3=地址
          Write_SJA1000(CAN_BTR0,0x7f); //配置總線定時(shí)--5kbps
          Write_SJA1000(CAN_BTR1,0xff);
          Write_SJA1000(CAN_OCR,0x1a); //配置輸出控制
          Write_SJA1000(CAN_EWLR,0xff); //配置錯(cuò)誤報(bào)警限制為255
          do
          {
          Write_SJA1000(CAN_MOD,0x00); //進(jìn)入工作模式雙濾波
          dog();
          }
          while((Read_SJA1000(CAN_MOD))&0x01); // 確認(rèn)復(fù)位標(biāo)志是否被刪除
          Write_SJA1000(CAN_TXB+4,ID3); //配置發(fā)送緩沖區(qū)的ID3-
          Write_SJA1000(CAN_IER,0x07); //配置SJA10000中斷-錯(cuò)誤報(bào)警/發(fā)送/接收中斷
          SEI();
          }
          在這之前,需要獲取設(shè)備的地址,就是讀取撥碼開(kāi)關(guān)各個(gè)腳的電平。需要注意的是,SJA1000使用的是雙濾波模式,響應(yīng)地址有:廣播的:0x00,還有自己的地址:0x**。為什么要這么做呢,一個(gè)系統(tǒng)中,主機(jī)的地址一般是0X00,從機(jī)地址從0X01開(kāi)始,這里面如果有兩個(gè)從機(jī)的地址一樣,就很可能產(chǎn)生一些混亂。從機(jī)一旦多了起來(lái),查找地址相同的設(shè)備就有些麻煩了。
          在程序的初始化的時(shí)候,進(jìn)行SJA1000的配置。
          第三部:工作程序
          接下來(lái),做的工作就是CAN試發(fā)送,別小看這個(gè)試發(fā)送,這可是解決地址重復(fù)的問(wèn)題的哦,還能檢測(cè)CAN網(wǎng)絡(luò)是否正常。
          //****************CAN第一次發(fā)送 通訊地址測(cè)試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ā)送數(shù)據(jù)
          dog();
          }
          while(!(a_temp&0x04))
          CLI(); //關(guān)CAN中斷,即總中斷

          Write_SJA1000(CAN_TXB+0,0xc0); //發(fā)送遠(yuǎn)程幀0xc0
          Write_SJA1000(CAN_TXB+1,0x00); //發(fā)送轉(zhuǎn)接器地址
          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ò)故障錯(cuò)誤在中斷中處理,短接H、L,按復(fù)位,先亮綠燈,后黃燈亮
          asm("nop");
          //SEI();
          }
          SJA1000的中斷引腳接到MEGA16的INT1上,需要在程序初始化的時(shí)候,配置一些INT1,使MCU能響應(yīng)SJA1000的中斷。
          數(shù)據(jù)發(fā)送前,點(diǎn)亮網(wǎng)絡(luò)指示燈,什么時(shí)候熄滅它呢,在發(fā)送中斷中熄滅它。
          下面看看MCU對(duì)SJA1000中斷的一些處理:在這里只處理:接收中斷、發(fā)送中斷、總線關(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) //地址測(cè)試數(shù)據(jù)幀
          {
          reload(); //數(shù)據(jù)幀中有和自己相同的地址
          }
          if(RxBuffer[0]==0xc0) // 遠(yuǎn)程幀則釋放接收緩沖區(qū)
          {
          type=RxBuffer[3]; //讀命令碼

          //處理命令碼

          if(type==0x30)
          { if(type==0x34)
          {CAN_now_value_send();type=0;} //傳瞬時(shí)值數(shù)據(jù)
          if (type==0x27)
          {reload(); type=0;}//裝置復(fù)位
          if(type==0x2e)
          {active();type=0;} //通訊地址測(cè)試
          }

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

          }

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

          if(CAN_SR_temp&0x80)
          {
          CANBE_JSQ=CANBE_JSQ+1; //關(guān)閉次數(shù)加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) //總線關(guān)閉次數(shù)到達(dá)設(shè)定次數(shù)
          {
          NET_LED_off; //關(guān)閉網(wǎng)絡(luò)燈
          ERR_LED_on; //打開(kāi)故障燈
          CANBE_JSQ=0; //復(fù)位總線關(guān)閉計(jì)數(shù)器
          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");
          }
          }

          中斷程序中,對(duì)命令碼等于0x2e的處理程序是:active();
          active()程序如下:
          //************************通訊地址測(cè)試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ā)送數(shù)據(jù)
          dog();
          }
          while(!(temp1&0x04));

          Write_SJA1000(CAN_TXB+0,0x80); //發(fā)送數(shù)據(jù)幀0x80
          temp2=Read_SJA1000(CAN_RXB+1);
          Write_SJA1000(CAN_TXB+1,temp2); //發(fā)送轉(zhuǎn)接器地址
          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");

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

          Write_SJA1000(CAN_TXB+0,0x84); //發(fā)送數(shù)據(jù)幀0x84
          Write_SJA1000(CAN_TXB+1,RxBuffer[1]); //發(fā)送轉(zhuǎn)接器地址
          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è)數(shù)據(jù)幀,這個(gè)數(shù)據(jù)幀有四字節(jié)的數(shù)據(jù)。
          CAN的數(shù)據(jù)幀最多支持有8個(gè)字節(jié)的數(shù)據(jù)幀,如果數(shù)據(jù)較多,可以分為多個(gè)數(shù)據(jù)幀,在命令碼里面區(qū)分這些數(shù)據(jù)幀。

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

          好了,昨天從南京回來(lái)的路上,就考慮發(fā)個(gè)CAN的東西。咱們這個(gè)論壇,目前還沒(méi)有多少關(guān)于CAN的帖子,意在拋磚引玉…………本壇高手很多,尤其是有很多潛水的高高手~~~~
          --------------------
          程序中的一些DEFINE
          //******************引腳信號(hào)定義***************************//
          #define CS_1 (PORTB|= (1<<4 )) //AD7705片選
          #define CS_0 (PORTB&= ~(1<<4 ))
          #define DRDY (PINB&0x08) //AD轉(zhuǎn)換DRDY信號(hào)輸入
          #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 )) //看門狗高
          #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 //總線關(guān)閉次數(shù)設(shè)定值
          //*******************CAN寄存器地址**************************//
          #define CAN_MOD 0 //模式寄存器
          #define CAN_CMR 1 //命令寄存器 只寫
          #define CAN_SR 2 //狀態(tài)寄存器 只讀
          #define CAN_IR 3 //中斷寄存器 只讀
          #define CAN_IER 4 //中斷使能寄存器
          #define CAN_BTR0 6 //總線定時(shí)寄存器0
          #define CAN_BTR1 7 //總線定時(shí)寄存器1
          #define CAN_OCR 8 //輸出控制寄存器
          #define CAN_TEST 9 //測(cè)試寄存器
          #define CAN_ALC 11 //仲裁丟失寄存器
          #define CAN_ECC 12 //錯(cuò)誤代碼捕捉寄存器
          #define CAN_EWLR 13 //錯(cuò)誤報(bào)警限制寄存器
          #define CAN_EXERR 14 //RX錯(cuò)誤計(jì)數(shù)寄存器
          #define CAN_TXERR 15 //TX錯(cuò)誤計(jì)數(shù)寄存器
          #define CAN_ACR0 16 //驗(yàn)收碼寄存器0
          #define CAN_ACR1 17 //驗(yàn)收碼寄存器1
          #define CAN_ACR2 18 //驗(yàn)收碼寄存器2
          #define CAN_ACR3 19 //驗(yàn)收碼寄存器3
          #define CAN_AMR0 20 //驗(yàn)收屏蔽寄存器0
          #define CAN_AMR1 21 //驗(yàn)收屏蔽寄存器1
          #define CAN_AMR2 22 //驗(yàn)收屏蔽寄存器2
          #define CAN_AMR3 23 //驗(yàn)收屏蔽寄存器3
          #define CAN_TXB 16 //發(fā)送緩沖區(qū)首地址(工作模式)
          #define CAN_RXB 16 //接收緩沖區(qū)首地址(工作模式)
          #define CAN_RMC 29 //RX信息計(jì)數(shù)器
          #define CAN_RBSA 30 //RX緩沖區(qū)起始地址寄存器
          #define CAN_CDR 31 //時(shí)鐘分頻器
          #define ID3 00 //ID3
          -----------------------------
          初始化程序
          uchar main_ch=0;
          IO_Init(); //I/O口初始化
          INT1_Init();
          GET_add(); //獲取地址,地址為0,反復(fù)獲取地址,直到不為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)

          評(píng)論


          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();