我在學(xué)習(xí)CAN應(yīng)用設(shè)計(jì)中的一點(diǎn)心得體會,也算是走了一些彎路,現(xiàn)在把它寫出來和大家共同交流。 不知道各位注意到?jīng)]有,大多數(shù)情況下,SJA1000是與8250+51系列單片機(jī)的方式來做CAN的控制應(yīng)用。如果是1M/s的通訊速度的話,實(shí)際上的流量只有大概500多K的樣子,此點(diǎn)希望大家在以后的應(yīng)用設(shè)計(jì)中需要考慮周到。 上訴問題產(chǎn)生的原因是SJA1000的發(fā)送緩沖區(qū)只有1個(gè),應(yīng)用程序在判斷發(fā)送完一幀數(shù)據(jù)后,需要計(jì)算下一幀需要裝載的數(shù)據(jù)長度,然后根據(jù)長度再裝載相應(yīng)的數(shù)據(jù)到緩沖區(qū),這個(gè)過程要消耗掉了一定的時(shí)間(特別是標(biāo)準(zhǔn)51內(nèi)核的單片機(jī)),因此CAN總線上的數(shù)據(jù)流實(shí)際上是發(fā)送一幀就有一個(gè)比較長的空閑區(qū)(單片機(jī)裝載數(shù)據(jù)的時(shí)間),這樣一來就浪費(fèi)了總線資源。微芯的MCP2510有3個(gè)發(fā)送緩沖區(qū),在5V的電源下以5Mb/s的SPI端口讀寫數(shù)據(jù),可以較好的解決這個(gè)問題,但是多數(shù)的51單片機(jī)都無SPI,這樣也給單片機(jī)的選擇上帶來了一定的麻煩。具體選擇怎樣的方案,只能看各自的應(yīng)用情況來定了! sja1000調(diào)試經(jīng)驗(yàn) 去年年底的時(shí)候,一個(gè)公司給我打電話,問我最近有沒有空,說要請我?guī)兔ψ鲆粋(gè)基于CAN總線通訊的東西,我去看了看,是一個(gè)數(shù)據(jù)采集系統(tǒng),下面是一系列數(shù)據(jù)采集的智能板卡,上位機(jī)是基于 WINBOND的一塊486的工業(yè)嵌入式控制板,操作系統(tǒng)使用的是WINCE。智能板卡通過工業(yè)底板和數(shù)據(jù)線兩種方式和上位機(jī)通訊,通信協(xié)議選擇的是 CAN,其中底板上的通信選用高速波特率(1Mbps),數(shù)據(jù)線選用低速(100kbps)。 去公司的時(shí)候,公司給了我一個(gè)參考的東西,采用SST單片機(jī)+SJA1000的方案構(gòu)成的智能板卡,同時(shí)告訴我可以自己設(shè)計(jì)方案?紤]到SST的東西沒有用過,P8X591是PLCC封裝的,燒寫起來不方便,于是我設(shè)計(jì)了如下的方案: 1、智能板卡上的通訊采用AT89S51+兩塊SJA1000的方式進(jìn)行; 2、上位機(jī)通過PC104總線和一塊CAN控制板卡連接,CAN控制板卡上同樣采用AT89S51+兩塊SJA1000的方案。AT89S51和上位機(jī)通過PC104總線共享內(nèi)存(使用IDT的雙口RAM); 3、采用西門子的組態(tài)軟件進(jìn)行WINCE下的板卡驅(qū)動開發(fā); 由于以前沒有做過CAN的東西,于是決定了先調(diào)試CAN通信,然后設(shè)計(jì)板卡的方案。方案確定之后,首先是上www.zlgmcu.com上下載了全部的SJA1000和PCA82C250的資料。然后開始設(shè)計(jì)電路板。采用了SJA1000應(yīng)用指南中推薦的方案,采用SJA1000的時(shí)鐘輸出為AT89S51的時(shí)鐘,沒有采用光電隔離芯片,把TX1接地,TX0和RX0分別連接到PCA82C250的TXD和RXD引腳上,RX1連接到 PCA82C250的VR上;加上了5歐姆的限流電阻和120歐姆的匹配電阻(用110歐姆替代),另外加上了一個(gè)調(diào)試用的串口。沒有注意而且要命的是把 SJA1000的復(fù)位引腳和單片機(jī)的復(fù)位引腳連接到了一起。 第一次的板子用的加急,用了三天,結(jié)果那次的板子做的極差——連銅皮都翻起來了;我馬上讓那個(gè)電路板廠重新做了三塊。在做板的過程中我發(fā)現(xiàn)了復(fù)位引腳的錯(cuò)誤,SJA1000的文檔上提供的是一個(gè)復(fù)位電路,但是沒有給出電路的詳細(xì)組成,于是我就誤以為和單片機(jī)的復(fù)位電路是一樣的了。在設(shè)計(jì)這塊電路板的時(shí)候,最擔(dān)心的事情就是SJA1000的輸出時(shí)鐘能不能夠驅(qū)動AT89S51,如果不能夠驅(qū)動,那么一切就OVER了,可惜的是我的擔(dān)心成為了現(xiàn)實(shí),板子焊好之后系統(tǒng)不工作,在SJA1000的時(shí)鐘輸入引腳上有信號輸入,而且輸出時(shí)鐘也正常,但是單片機(jī)就是不工作。于是我先把SJA1000 的復(fù)位引腳連線割斷,連接到了AT89S51的IO引腳上,再把S51的XTAL的兩個(gè)引腳連接到SJA晶體的上,可惜系統(tǒng)還是不工作,這次電路板設(shè)計(jì)失敗了。 在總結(jié)了第一次失敗的經(jīng)驗(yàn)后,參看了21IC上的一個(gè)設(shè)計(jì),決定把AT89S51和SJA的晶體分開。并且用單片機(jī)的一個(gè)IO引腳來控制對SJA的復(fù)位。 第二次的電路板比較成功,焊接好了之后首先測試單片機(jī)的串口和LED指示燈,一切OK。然后就開始測試SJA。ZLG提供了一個(gè) BASIC模式下的參考例程,我看了一下,然后又找了本《現(xiàn)場總線CAN的原理和測試》把SJA的寄存器詳細(xì)看了看(由于開始的時(shí)候比較忙,所以直到這個(gè)時(shí)候才算是仔細(xì)看了看SJA的內(nèi)部,至于CAN的基礎(chǔ)協(xié)議我是根本沒有看,這給我后面帶來了極大的麻煩)。然后就參考ZLG的程序開始寫SJA的測試程序,那個(gè)程序?qū)懙暮艽,也很全,因(yàn)槲蚁肟禳c(diǎn)把東西給做出來,于是弄了一個(gè)1000多行的程序,以前我的調(diào)試程序一般都很小的。寫好程序之后就開始測試,首先測試的是測試寄存器,然后一步步測試下去,在BASIC模式下所有的寄存器都正常,但是在發(fā)送的時(shí)候是總是不正常,啟動發(fā)送之后就一直在發(fā)送,狀態(tài)寄存器的標(biāo)志位一直處在發(fā)送的狀態(tài)下,然后就是報(bào)總線錯(cuò)誤,不知道是怎么會事情,很郁悶,上bbs看了一下。bullfrog告訴我單個(gè)CAN節(jié)點(diǎn)發(fā)送是成功不了的,如果沒有收到接受CAN節(jié)點(diǎn)的應(yīng)答,發(fā)送節(jié)點(diǎn)就會一直發(fā)送,直到超出錯(cuò)誤計(jì)數(shù)器的允許值使得總線關(guān)閉。同時(shí)在精華區(qū)發(fā)現(xiàn)在peli模式下有 ECC(錯(cuò)誤寄存器),可以跟蹤錯(cuò)誤,于是開始看peli模式操作過程。這個(gè)東西比較麻煩,zlg沒有提供公開的c代碼,我找了一個(gè)匯編的作為參考。 我第一步的目標(biāo)是自發(fā)送,在peli模式下有自發(fā)送這種模式,在有匹配電阻的情況下可以進(jìn)行單個(gè)節(jié)點(diǎn)的接收和發(fā)送。第一次調(diào)試的時(shí)候沒有成功,給北京zlg打電話,北京分公司說讓我給廣州打電話,給廣州打電話,幾個(gè)問題都得到了很好的解答(在此謝謝zlg的工程師了): 1、自發(fā)送的時(shí)候必須加上匹配電阻; 2、5歐的限流電阻可以不需要; 3、每次發(fā)送完成之后 4、建議使用中止發(fā)送來進(jìn)行單步發(fā)送; 另外他告訴我可以在zlg的論壇上找到很多很有用的東西。 聽了他的建議,我第一件事情就是檢查我的電路板,檢查的結(jié)果讓我大吃一驚——我的ch和cl竟然是短路的,萬用表的狂叫不止。一步步檢查,發(fā)現(xiàn)那個(gè)110歐的匹配電阻有問題,萬用表碰上去就叫,于是把那個(gè)電阻剪下來,量量還是短路。于是我懷疑把5歐的限流電阻當(dāng)成了110歐的電阻,于是把匹配電阻都去掉了。沒有想到的是當(dāng)我把新的110歐電阻拿來的時(shí)候,萬用表還是叫,這時(shí)候才發(fā)現(xiàn)這塊萬用表在300歐姆以下都要叫,可憐我又打理了n長時(shí)間的電路板......再仔細(xì)閱讀了一次peli模式下的操作指南,又仔細(xì)閱讀了 zlg提供的初始化規(guī)范,發(fā)現(xiàn)在子發(fā)送的時(shí)候發(fā)送的命令應(yīng)該是0x10或者是0x12(即CMR寄存器里面有一個(gè)專門的控制位是用來控制自發(fā)送的,和普通的發(fā)送命令位是不同的)。在發(fā)現(xiàn)了這個(gè)問題之后,自發(fā)送一切順利的通過了。 接下來就是兩個(gè)節(jié)點(diǎn)的互調(diào)了,我首先用自發(fā)送程序把兩個(gè)節(jié)點(diǎn)都調(diào)試了一下,保證單個(gè)節(jié)點(diǎn)發(fā)送硬件沒有任何問題。然后就用雙絞線通過接線端子把兩個(gè)系統(tǒng)連接到了一起。第一次調(diào)試采用的是1M的波特率(由于ZLG只給出了16M晶體下的BTR0和BTR1的初始值,我在ZLG的論壇上找到了一個(gè)網(wǎng)友自己計(jì)算的數(shù)值,后來證明這個(gè)東西有些問題),沒有成功。發(fā)送節(jié)點(diǎn)通過串口利用串口調(diào)試助手來控制發(fā)送,接收節(jié)點(diǎn)通過仿真器觀察數(shù)據(jù)。雖然沒有發(fā)送成功,但是通過串口的反饋數(shù)據(jù)和仿真器的觀察窗,可以看到ECC寄存器都發(fā)生了變化,證明數(shù)據(jù)線上有數(shù)據(jù)過去(由于我沒有示波器,只有采用這種辦法)。于是我改變了兩次波特率,最低到了5k,都沒有成功,最后我從21IC上的一篇應(yīng)用文章上找到了兩個(gè)參數(shù),這次就成功了,通訊速率20k,F(xiàn)在一切穩(wěn)定,在寫這篇文章的時(shí)候哪幾個(gè)LED正歡快的閃爍著。 最后,總結(jié)幾個(gè)經(jīng)驗(yàn): 1、一定要詳細(xì)的閱讀sja的手冊和CAN的相關(guān)知識; 2、 SJA的復(fù)位是低電平,而且不是用一個(gè)非們把單片機(jī)的RST反相就可以的,有兩種解決方式:第一種是使用單片機(jī)的IO引腳來控制SJA的復(fù)位引腳,好處是單片機(jī)完全控制SJA的復(fù)位過程;第二種是采用適當(dāng)?shù)膹?fù)位芯片,ZLG給我推薦的是CAT1161,我沒有用過,其好處是同步復(fù)位。 3、在自發(fā)送的模式下,需要匹配電阻,而且自發(fā)送的啟動命令和普通發(fā)送的啟動命令不相同; 4、BRT0和BRT1的選擇,和串口通信中只要兩個(gè)的誤差一樣就可以了不同,一定要精心選擇,建議SJA的外部晶體選擇16M的,這樣有利于參考ZLG的標(biāo)準(zhǔn)數(shù)值 5、SJA和其他外部器件連接的時(shí)候,數(shù)據(jù)線在373前后都可以; 6、最好有一個(gè)示波器; 7、不要太大意的相信萬用表的蜂鳴器; 8、這是從ZLG網(wǎng)站上轉(zhuǎn)載過來的peli模式下的初始化流程 a)檢測硬件連接是否正確 b)進(jìn)入復(fù)位狀態(tài) c)設(shè)置時(shí)鐘分頻寄存器 d)設(shè)置輸出控制寄存器 e)設(shè)置通訊波特率 f)設(shè)置代碼驗(yàn)收寄存器 g)設(shè)置代碼屏蔽寄存器 h)退出復(fù)位狀態(tài) i)設(shè)置工作模式 j)設(shè)置中斷使能寄存器 這是一個(gè)自發(fā)收程序,采用at89s51+sja1000,分離晶體,at89s51晶體11.0592MHz。 sja1000外部晶體為12M,通過串口進(jìn)行監(jiān)控 ****************************************************** 以下為頭文件定義 copyright by alloy ****************************************************** #define SJA_REG_BaseADD 0x7800 #define REG_MODE XBYTE[SJA_REG_BaseADD + 0x00] #define REG_CMD XBYTE[SJA_REG_BaseADD + 0x01] #define REG_SR XBYTE[SJA_REG_BaseADD + 0x02] #define REG_IR XBYTE[SJA_REG_BaseADD + 0x03] #define REG_IR_ABLE XBYTE[SJA_REG_BaseADD + 0x04] #define REG_BTR0 XBYTE[SJA_REG_BaseADD + 0x06] //05保留 #define REG_BTR1 XBYTE[SJA_REG_BaseADD + 0x07] #define REG_OCR XBYTE[SJA_REG_BaseADD + 0x08] #define REG_TEST XBYTE[SJA_REG_BaseADD + 0x09] #define REG_ALC XBYTE[SJA_REG_BaseADD + 0x0b] //0a保留 #define REG_ECC XBYTE[SJA_REG_BaseADD + 0x0c] #define REG_EMLR XBYTE[SJA_REG_BaseADD + 0x0d] #define REG_RXERR XBYTE[SJA_REG_BaseADD + 0x0e] #define REG_TXERR XBYTE[SJA_REG_BaseADD + 0x0f] #define REG_ACR0 XBYTE[SJA_REG_BaseADD + 0x10] #define REG_ACR1 XBYTE[SJA_REG_BaseADD + 0x11] #define REG_ACR2 XBYTE[SJA_REG_BaseADD + 0x12] #define REG_ACR3 XBYTE[SJA_REG_BaseADD + 0x13] #define REG_AMR0 XBYTE[SJA_REG_BaseADD + 0x14] #define REG_AMR1 XBYTE[SJA_REG_BaseADD + 0x15] #define REG_AMR2 XBYTE[SJA_REG_BaseADD + 0x16] #define REG_AMR3 XBYTE[SJA_REG_BaseADD + 0x17] #define REG_RxBuffer0 XBYTE[SJA_REG_BaseADD + 0x10] #define REG_RxBuffer1 XBYTE[SJA_REG_BaseADD + 0x11] #define REG_RxBuffer2 XBYTE[SJA_REG_BaseADD + 0x12] #define REG_RxBuffer3 XBYTE[SJA_REG_BaseADD + 0x13] #define REG_RxBuffer4 XBYTE[SJA_REG_BaseADD + 0x14] #define REG_TxBuffer0 XBYTE[SJA_REG_BaseADD + 0x10] #define REG_TxBuffer1 XBYTE[SJA_REG_BaseADD + 0x11] #define REG_TxBuffer2 XBYTE[SJA_REG_BaseADD + 0x12] #define REG_TxBuffer3 XBYTE[SJA_REG_BaseADD + 0x13] #define REG_TxBuffer4 XBYTE[SJA_REG_BaseADD + 0x14] #define REG_DataBuffer1 XBYTE[SJA_REG_BaseADD + 0x15] #define REG_DataBuffer2 XBYTE[SJA_REG_BaseADD + 0x16] #define REG_DataBuffer3 XBYTE[SJA_REG_BaseADD + 0x17] #define REG_DataBuffer4 XBYTE[SJA_REG_BaseADD + 0x18] #define REG_DataBuffer5 XBYTE[SJA_REG_BaseADD + 0x19] #define REG_DataBuffer6 XBYTE[SJA_REG_BaseADD + 0x1a] #define REG_DataBuffer7 XBYTE[SJA_REG_BaseADD + 0x1b] #define REG_DataBuffer8 XBYTE[SJA_REG_BaseADD + 0x1c] #define REG_RBSA XBYTE[SJA_REG_BaseADD + 0x1e] #define REG_CDR XBYTE[SJA_REG_BaseADD + 0x1f] #define REG_Receive_Counter XBYTE[SJA_REG_BaseADD + 0x1d] #define OK 1 #define Fail 0 #define ON 1 #define OFF 0 #define True 1 #define False 0 sbit SJARst = P2 ^ 6; //復(fù)位控制 sbit LED0 = P1 ^ 0; sbit LED1 = P1 ^ 1; sbit Key0 = P1 ^ 2; sbit Key1 = P1 ^ 3; sbit Key2 = P1 ^ 4; sbit Key3 = P1 ^ 5; bit step_flg; bit Tx_flg; bit Rx_flg; unsigned char step_counter; unsigned char Tx_counter; unsigned char PC_RX_Buffer; unsigned char temp_data1; unsigned char Rx_Buffer[6]; void MCU_Init(void); void SJA_Init(void); void send(unsigned char S_Data); void Serial(void); void Delay(unsigned char Delay_time); void step(void); ******************************************************* 以下為c的主程序 copyright by alloy ******************************************************* 。 nclude 。 nclude 。 nclude 。 nclude 。 nclude main() { unsigned char i; MCU_Init(); SJA_Init(); REG_MODE = 0x01; //進(jìn)入復(fù)位模式 temp_data1 = REG_MODE; temp_data1 = temp_data1 & 0x01; if(temp_data1 == 0x01) //在復(fù)位模式中 { REG_BTR0 = 0x85; REG_BTR1 = 0xb4; //100k REG_OCR = 0x1a; REG_CDR = 0xc0; REG_RBSA = 0x00; REG_ACR0 = 0xff; REG_ACR1 = 0xff; REG_ACR2 = 0xff; REG_ACR3 = 0xff; REG_AMR0 = 0xff; REG_AMR1 = 0xff; REG_AMR2 = 0xff; REG_AMR3 = 0xff; REG_IR_ABLE = 0xff; } REG_MODE = 0x0c; //進(jìn)入自接收模式 REG_MODE = 0x0c; for(i = 0;i<100;i++); temp_data1 = REG_Receive_Counter; send(temp_data1); for(;;) { while(Tx_flg == False); Tx_flg = False; Tx_counter++; send(Tx_counter); temp_data1 = REG_SR; while((temp_data1 & 0x10) == 0x10); temp_data1 = REG_SR; if((temp_data1 & 0x04) == 0x04) { REG_RxBuffer0 = 0x08; //標(biāo)準(zhǔn)幀,長度為8 REG_RxBuffer1 = 0xff; REG_RxBuffer2 = 0xff; REG_RxBuffer3 = 0x01; REG_RxBuffer4 = 0x02; REG_DataBuffer1 = 0x03; REG_DataBuffer2 = 0x04; REG_DataBuffer3 = 0x05; REG_DataBuffer4 = 0x06; REG_DataBuffer5 = 0x07; REG_DataBuffer6 = 0x08; REG_DataBuffer7 = 0x09; REG_DataBuffer8 = 0x0a; } REG_CMD = 0x10; temp_data1 = REG_SR; temp_data1 = temp_data1 & 0x20; while(temp_data1 == 0x20) //檢查是否發(fā)送完成 { //send(0xaa); temp_data1 = REG_ECC; send(temp_data1); temp_data1 = REG_SR; temp_data1 = temp_data1 & 0x20; //send(temp_data1); } send(0x66); temp_data1 = REG_ALC; send(temp_data1); temp_data1 = REG_ECC; send(temp_data1); temp_data1 = REG_SR; send(temp_data1); temp_data1 = REG_Receive_Counter; send(temp_data1); PC_RX_Buffer = 0x77; LED0 = ~LED0; // Tx_counter = 0x00; } } void MCU_Init(void) { SJARst = 1; LED0 = OFF; LED1 = OFF; PC_RX_Buffer = 0x77; step_counter = 0x00; step_flg = False; Tx_flg = False; temp_data1 = 0x00; TMOD = 0x20; TH1 = 0xff; TL1 = 0xff; TR1 = 1; SCON = 0x50; PCON = 0x80; EA = 1; ES = 1; Tx_counter = 0x00; } void SJA_Init(void) { unsigned char i; for(i = 0;i < 125;i++); SJARst = 0; for(i = 0;i < 125;i++); SJARst = 1; for(i = 0;i < 125;i++); } void send(unsigned char S_Data) { SBUF = S_Data; while(TI == 0); TI =0; } void Serial() interrupt 4 using 2 { if(RI == 1) { PC_RX_Buffer = SBUF; RI = 0; if(PC_RX_Buffer == 0xaa) { send(0x13); Tx_flg = True; PC_RX_Buffer = 0x77; } else if(PC_RX_Buffer == 0x55) { send(0x14); Rx_flg = True; PC_RX_Buffer = 0x77; } else { send(0x15); PC_RX_Buffer = 0x77; } } } 另外,我給大家推薦一本書,還不錯(cuò)的 北航出版的《現(xiàn)場總線CAN原理與應(yīng)用技術(shù)》(饒運(yùn)濤等) |