新聞中心

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

        如何一步一步建立CAN通訊

        作者: 時間:2016-12-14 來源:網絡 收藏
        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,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 //時鐘分頻器
        #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);

        }


        關鍵詞: CAN通訊硬件環

        評論


        技術專區

        關閉
        主站蜘蛛池模板: 营口市| 玛多县| 庄浪县| 武冈市| 县级市| 来安县| 合作市| 盐山县| 宿迁市| 天峻县| 通江县| 民丰县| 托克逊县| 勐海县| 沐川县| 内黄县| 化德县| 台北市| 英德市| 宁陵县| 汪清县| 土默特左旗| 迭部县| 微山县| 南开区| 揭阳市| 鄂托克旗| 武冈市| 阳曲县| 松溪县| 道孚县| 肃宁县| 永修县| 普格县| 临夏市| 大石桥市| 天门市| 若尔盖县| 阿克| 二手房| 太仆寺旗|