基于Can總線的嵌入式網(wǎng)絡(luò)控制節(jié)點(diǎn)的設(shè)計(jì)與實(shí)現(xiàn)
近年來,基于各種總線標(biāo)準(zhǔn)的網(wǎng)絡(luò)化控制系統(tǒng)已經(jīng)在工業(yè)控制領(lǐng)域內(nèi)得到廣泛應(yīng)用。網(wǎng)絡(luò)化控制系統(tǒng)采用了完全分散化的控制節(jié)點(diǎn)結(jié)構(gòu),將控制的權(quán)力很大部分交給了處于控制現(xiàn)場的智能節(jié)點(diǎn),系統(tǒng)內(nèi)各種交互信息通過現(xiàn)場總線傳送。
本文引用地址:http://www.ex-cimer.com/article/201611/319045.htm當(dāng)前已實(shí)用化的總線標(biāo)準(zhǔn)有許多種,如WorldFIP, Profibus, LONWORKS, CAN等。其中,CAN(Controller Area Network)是Bosch公司在現(xiàn)代汽車應(yīng)用技術(shù)中領(lǐng)先推出的一種串行通信網(wǎng)絡(luò)。CAN主線采用多主站工作方式,根據(jù)優(yōu)先權(quán)進(jìn)行總線訪問仲裁,能夠檢測出通信過程產(chǎn)生的任何錯(cuò)誤。CAN總線還具有卓越的信號傳輸性能,當(dāng)信號傳輸距離達(dá)到l0km時(shí),它仍可提供高達(dá)SOKbit/s的數(shù)據(jù)傳輸速率。另外,CAN協(xié)議廢除了站地址編碼,而采用對通信數(shù)據(jù)塊編碼的方式,這樣使得網(wǎng)絡(luò)內(nèi)的節(jié)點(diǎn)個(gè)數(shù)在理論上不受限制。目前,CAN總線已經(jīng)在許多行業(yè)得到了廣泛的應(yīng)用,尤其是工業(yè)控制領(lǐng)域,并常被認(rèn)為是最有前途的現(xiàn)場總線之一。
常用的CAN總線節(jié)點(diǎn)一般采用的是“單片機(jī)+CAN控制器”的結(jié)構(gòu),這樣由于運(yùn)算能力的限制,這類節(jié)點(diǎn)的智能化程度較低,常是作為工控機(jī)節(jié)點(diǎn)的從節(jié)點(diǎn)。而近年來,以ARM為代表的嵌入式32位微處理器技術(shù)得到了飛速發(fā)展,無論是在功耗、便攜性還是在硬件成本上,許多高性能的ARM芯片已經(jīng)與單片機(jī)相差無幾,因此在CAN節(jié)點(diǎn)設(shè)計(jì)中,使用ARM芯片取代傳統(tǒng)的8/16位單片機(jī)已經(jīng)是一個(gè)非常實(shí)用的選擇。這樣設(shè)計(jì)的CAN節(jié)點(diǎn),不僅保留了低功耗、低成本和小體積的優(yōu)點(diǎn),而且性能得到了大幅提高,若輔以大容量的存儲(chǔ)器,同時(shí)運(yùn)行功能強(qiáng)大的嵌入式操作系統(tǒng),它幾乎已可以取代原先的工控機(jī)節(jié)點(diǎn)。本文則從軟硬件兩方面詳細(xì)介紹了上述設(shè)計(jì)方案的具體實(shí)現(xiàn)過程。
1 節(jié)點(diǎn)的接口電路設(shè)計(jì)
本文設(shè)計(jì)的CAN總線節(jié)點(diǎn)是某工業(yè)控制系統(tǒng)的一個(gè)子模塊,同時(shí)綜合考慮其它相關(guān)需求和功能擴(kuò)展,因此選用了AT91 RM9200處理器作為系統(tǒng)的核心處理單元。AT91 RM9200,是ATMEL公司生產(chǎn)的一款高性能的ARM9處理器,它是一款通用工業(yè)級ARM芯片,主頻為180MHz/200MIPS,已經(jīng)在工業(yè)控制、智能儀器儀表等領(lǐng)域內(nèi)得到了大量的成功應(yīng)用。
CAN控制器選用的是SJA 1000芯片,它是Philips公司生產(chǎn)的一款獨(dú)立CAN總線控制器芯片,專用于移動(dòng)目標(biāo)和一般工業(yè)環(huán)境中控制器局域網(wǎng)絡(luò)((CAN)。SJA 1000本質(zhì)上是早期的PCA82C200的升級產(chǎn)品,與后者在管腳、電氣特性上完全兼容,而且除具有基本CAN工作模式((BasicCAN)外,還增加了一種新的增強(qiáng)工作模式(PeIiCAN),這種新模式支持具有許多新特性的CAN2.OB協(xié)議。
SJAlooo的總線接口采用的是地址總線和數(shù)據(jù)總線復(fù)用的方式,這種方式與s1類似,也采用總線復(fù)用架構(gòu)的處理器,接口很方便,并在讀寫時(shí)序上也很好配合,但當(dāng)與數(shù)據(jù)總線和地址總線分離的微處理器接口時(shí),則需要專門的讀寫邏輯與之配合,并且還相對比較復(fù)雜,基于這個(gè)原因,目前許多設(shè)計(jì)都采用諸如SPI等專用接口的CAN總線控制器,但這種方式使應(yīng)用受到了諸多限制,如要求微處理器必須有SPI接口,同時(shí)當(dāng)系統(tǒng)需要多路CAN總線接口時(shí),會(huì)受到SPI端口數(shù)的限制等。
ARM架構(gòu)的數(shù)據(jù)總線和地址總線是分離的,因此,必須引入專門的控制邏輯,才能實(shí)現(xiàn)對SJA1000的操作。
圖1和圖2分別為SJA 1000讀/寫操作時(shí)序。分析其讀/寫時(shí)序,可以看出,無論是讀操作還是寫操作,首先必須送出操作寄存器的地址,然后讀/寫數(shù)據(jù)。在寫地址的過程中,片選信號(/CS )和讀(/RD )、寫(/WR)均無效(高電平),僅ALE信號有效(高電平),而在讀/寫數(shù)據(jù)的過程中,讀/寫信號有效(低電平),ALE信號無效(低電平),同時(shí),在操作的過程中,還必須滿足信號電平的持續(xù)時(shí)間。
因此,可以采用如圖3所示的控制邏輯實(shí)現(xiàn)。
圖1 SJA1000的讀操作時(shí)序(Intel模式)
圖2 SJA1000的寫操作時(shí)序(Intel模式)
圖3 SJA1000操作時(shí)序的實(shí)現(xiàn)
在圖3所示的SJA1000操作時(shí)序的實(shí)現(xiàn)中,左端的信號如/CS, /RD, /WR, A7分別為ARM微處理器的片選、讀、寫控制信號,A7為地址信號(也可以是其它的地址),右端產(chǎn)生的ALE CAN,/CS CAN, /WR_CAN, /RD_ CAN分別與SJA1000對應(yīng)的信號相連接,當(dāng)微處理器對SJA100()對應(yīng)的地址進(jìn)行讀寫操作時(shí),即可產(chǎn)生正確的控制邏輯。
2 節(jié)點(diǎn)的驅(qū)動(dòng)程序開發(fā)
在工業(yè)控制應(yīng)用中,使用嵌入式操作系統(tǒng)已逐漸成為一個(gè)流行的選擇。目前,市場上的嵌入式操作系統(tǒng)超過100種,其中嵌入式Linux是一種非常理想、經(jīng)濟(jì)的選擇,因?yàn)樗粌H具有功能強(qiáng)大、高性能、穩(wěn)定性好等優(yōu)點(diǎn),還是免費(fèi)并開放源代碼的。同時(shí)Linux內(nèi)核采用了模塊化設(shè)計(jì),具有非常良好的移植性和可定制性?,F(xiàn)在,許多ARM生產(chǎn)廠商都已經(jīng)將Linux系統(tǒng)移植到其生產(chǎn)的ARM芯片上,并發(fā)布了相關(guān)源代碼供用戶免費(fèi)使用。本系統(tǒng)中采用的操作系統(tǒng)就是ATMEL公司發(fā)布的支持其AT912RM9200處理器的ARM-Linux系統(tǒng)的版本,版本號為2.4.27。
CAN控制器SJA1000顯然屬于Linux系統(tǒng)中的字符設(shè)備類型,其驅(qū)動(dòng)程序的實(shí)現(xiàn)架構(gòu)類似于系統(tǒng)中字符設(shè)備的通用實(shí)現(xiàn)結(jié)構(gòu),關(guān)于Linux設(shè)備驅(qū)動(dòng)開發(fā)的詳細(xì)分析可參考文獻(xiàn)。本小節(jié)則以SJA1000的增強(qiáng)工作模式(PeIiCAN)為例,對Linux系統(tǒng)下CAN設(shè)備驅(qū)動(dòng)程序的主要實(shí)現(xiàn)部分進(jìn)行了詳細(xì)說明,包括主要數(shù)據(jù)結(jié)構(gòu)的定義、操作函數(shù)和中斷函數(shù)的實(shí)現(xiàn)三個(gè)部分。
2.1 CAN設(shè)備驅(qū)動(dòng)的主要數(shù)據(jù)結(jié)構(gòu)
為方便驅(qū)動(dòng)程序的設(shè)計(jì)和編寫,驅(qū)動(dòng)中定義了兩個(gè)數(shù)據(jù)結(jié)構(gòu)體,即協(xié)議幀數(shù)據(jù)結(jié)構(gòu)和緩沖區(qū)結(jié)構(gòu)體,下面給出每個(gè)結(jié)構(gòu)體的定義及成員變量的解釋。
其中,協(xié)議幀結(jié)構(gòu)體是用來對CAN網(wǎng)絡(luò)的報(bào)文數(shù)據(jù)幀進(jìn)行抽象,驅(qū)動(dòng)中使用該結(jié)構(gòu)體來進(jìn)行用戶與內(nèi)核空間的數(shù)據(jù)幀傳遞及發(fā)送/接收數(shù)據(jù)緩沖區(qū)的管理。
從緩沖區(qū)結(jié)構(gòu)體的定義可以看出,CAN設(shè)備的數(shù)據(jù)緩沖區(qū)由兩個(gè)獨(dú)立緩沖區(qū)構(gòu)成,一個(gè)用于設(shè)備讀操作,另一個(gè)用于寫操作。讀緩沖區(qū)和寫緩沖區(qū)都是一個(gè)先入先出的環(huán)形緩沖區(qū),緩沖區(qū)的大小設(shè)為協(xié)議幀結(jié)構(gòu)體的整數(shù)倍,如64倍。驅(qū)動(dòng)通過緩沖區(qū)的讀指針和寫指針來進(jìn)行緩沖區(qū)管理。如對于接收緩沖區(qū)(讀緩沖區(qū)),它的讀指針指向了當(dāng)驅(qū)動(dòng)程序從內(nèi)核空間向用戶空間拷貝報(bào)文數(shù)據(jù)幀時(shí),緩沖區(qū)中第一個(gè)有效數(shù)據(jù)幀的位置;而緩沖區(qū)的寫指針則代表了在控制器芯片執(zhí)行接收數(shù)據(jù)時(shí),即將數(shù)據(jù)幀從SJA1000的寄存器讀到緩沖區(qū),緩沖區(qū)的當(dāng)前可寫位置;這樣通過讀指針和寫指針的相對位置及緩沖區(qū)的整體長度就可以得到讀緩沖區(qū)中當(dāng)前的數(shù)據(jù)幀個(gè)數(shù)。寫緩沖區(qū)的管理與讀緩沖區(qū)基本相同,其詳細(xì)機(jī)制可參考后面給出的相關(guān)偽代碼。同時(shí)緩沖區(qū)中還定義了volatile int型變量tx_in_progress來表征當(dāng)前是否有實(shí)際的數(shù)據(jù)發(fā)送操作已被啟動(dòng),“1”標(biāo)識(shí)已啟動(dòng),否則為“0”。另外,結(jié)構(gòu)體定義中使用到了Linux系統(tǒng)的等待隊(duì)列結(jié)構(gòu),關(guān)于它的詳細(xì)機(jī)制可參考文獻(xiàn)。
2.2 CAN設(shè)備的操作函數(shù)
字符設(shè)備驅(qū)動(dòng)的核心就是實(shí)現(xiàn)設(shè)備的操作函數(shù)結(jié)構(gòu),所謂的操作函數(shù)結(jié)構(gòu),本質(zhì)上是定義了應(yīng)用程序在設(shè)備上的所有可能操作。但對于某一具體設(shè)備,驅(qū)動(dòng)中只需要實(shí)現(xiàn)設(shè)備工作所必須的操作。如對于CAN設(shè)備,本例中共實(shí)現(xiàn)了5種系統(tǒng)調(diào)用函數(shù),即open, close, read, write和ioctl函數(shù)。
其中,open函數(shù)是在應(yīng)用程序打開CAN設(shè)備時(shí)被調(diào)用,函數(shù)主要實(shí)現(xiàn)兩部分功能,首先對驅(qū)動(dòng)中的變量和數(shù)據(jù)結(jié)構(gòu)進(jìn)行初始化,并分配緩沖區(qū)空間。另一部分就是對CAN控制器SJA 1000初始化,即在復(fù)位時(shí)為芯片的各個(gè)寄存器設(shè)置正確的初始值。對于SJA1000芯片,需要設(shè)置的寄存器包括:1)模式和時(shí)鐘寄存器;2)輸出控制寄存器;3)驗(yàn)收代碼寄存器和驗(yàn)收屏蔽寄存器;4)總線定時(shí)寄存器;5)錯(cuò)誤計(jì)數(shù)寄存器;6)中斷使能寄存器。需要注意的是,SJA1000的配置寄存器只能在復(fù)位模式下可寫,所以在設(shè)置寄存器之前,必須先進(jìn)入復(fù)位模式,所有設(shè)置完成后必須返回正常工作模式,關(guān)于SJA 1000芯片寄存器設(shè)置的更多內(nèi)容可參見文獻(xiàn)。CAN設(shè)備的close調(diào)用的實(shí)現(xiàn)功能非常簡單,即等待緩沖區(qū)中已有的數(shù)據(jù)幀被處理完,然后釋放緩沖區(qū),最后關(guān)閉設(shè)備中斷。
驅(qū)動(dòng)程序的write就是對應(yīng)于用戶寫CAN設(shè)備時(shí)的系統(tǒng)調(diào)用。它的功能就是完成應(yīng)用程序在用戶空間中的報(bào)文發(fā)送,即報(bào)文數(shù)據(jù)從用戶空間向內(nèi)核空間的傳遞。下面給出了實(shí)現(xiàn)write函數(shù)的偽代碼:
ssize t can-write(......)
{
.....
判斷指定的數(shù)據(jù)長度是否滿足數(shù)據(jù)幀格式,若不滿足,則提示并返回;
通過寫指針和讀指針的相對位置及緩沖區(qū)的整體長度計(jì)算輸出緩沖區(qū)中空閑空間的大小,
while空閑空間長度<一個(gè)數(shù)據(jù)幀大小使用interruptible_sleep_on_timeout()函數(shù)將寫進(jìn)程放入寫操作等待隊(duì)列睡眠,該函數(shù)既說明了睡眠可被信號中斷,還可以指定進(jìn)程的最長睡眠時(shí)間;
睡眠結(jié)束后,再次計(jì)算空閑空間的大小。若空閑空間長度已大于或等于一個(gè)數(shù)據(jù)幀大小,則可跳出循環(huán),否則繼續(xù)while循環(huán);
從用戶空間拷貝N字節(jié)數(shù)據(jù)到輸出緩沖區(qū),其中:N = min(計(jì)算的空閑空間大小,數(shù)據(jù)長度參數(shù)),然后更新輸出緩沖區(qū)的寫指針位置;
if當(dāng)前無實(shí)際的發(fā)送操作已被啟動(dòng),即tx_in_progress等于0
置位tx in_progress為1,同時(shí)調(diào)用發(fā)送初始化函數(shù),即將輸出緩沖區(qū)中第一個(gè)有效數(shù)據(jù)幀寫入到控制器芯片相應(yīng)的發(fā)送寄存器中,同時(shí)使能發(fā)送操作,完成后,結(jié)束c} write()函數(shù)并返回?cái)?shù)值N;
else
函數(shù)直接結(jié)束,返回N
}
需要說明的是,上述偽碼中在對臨界區(qū)變量進(jìn)行操作和判斷時(shí),即計(jì)算空閑區(qū)長度和判斷飲_in_progress變量時(shí),必須要在關(guān)閉設(shè)備中斷條件下進(jìn)行,操作完成后立即重新打開中斷,下面描述的讀函數(shù)中與臨界區(qū)變量相關(guān)的操作也必須采用同樣的機(jī)制。
讀函數(shù)read的功能就是響應(yīng)用戶對CAN設(shè)備的讀操作,如果接收緩沖區(qū)中已有了數(shù)據(jù)幀,則直接從緩沖區(qū)拷貝M字節(jié)的數(shù)據(jù)返回給用戶,其中M為用戶要求字節(jié)數(shù)與緩沖區(qū)已有數(shù)據(jù)字節(jié)數(shù)之間的較小值,更新讀指針位置然后函數(shù)返回?cái)?shù)值M,而如果當(dāng)前緩沖區(qū)中無有效數(shù)據(jù),則需要判斷設(shè)備文件的讀取模式,若為非阻塞模式,則直接返回,否則將讀進(jìn)程放入讀操作等待隊(duì)列睡眠,指定進(jìn)程的最大睡眠時(shí)間并設(shè)置為睡眠可被信號中斷模式,當(dāng)任務(wù)被喚醒后,再對喚醒方式進(jìn)行判斷,若因睡眠時(shí)間結(jié)束而喚醒,則函數(shù)直接返回相應(yīng)標(biāo)識(shí),否則說明緩沖區(qū)中已有數(shù)據(jù),則執(zhí)行上述的拷貝動(dòng)作,并更新緩沖區(qū)的讀指針位置,函數(shù)結(jié)束并返回實(shí)際讀取字節(jié)數(shù),具體實(shí)現(xiàn)略。
設(shè)備的iOCtl函數(shù)是用于設(shè)備控制的公共接口,可以根據(jù)設(shè)備的具體需求來實(shí)現(xiàn)相應(yīng)的控制代碼,在本文中共實(shí)現(xiàn)了三種功能,即清除讀/寫緩沖區(qū)、設(shè)置總線波特率、設(shè)置驗(yàn)收代碼寄存器和驗(yàn)收屏蔽寄存器。
2.3 CAN設(shè)備的中斷函數(shù)
如上所述,設(shè)備的讀函數(shù)和寫函數(shù)只是完成用戶空間與設(shè)備的接收/發(fā)送緩沖區(qū)之間的數(shù)據(jù)幀傳遞,而真正的數(shù)據(jù)接收和發(fā)送工作,即芯片的寄存器與數(shù)據(jù)緩沖區(qū)之間的數(shù)據(jù)讀取和寫入,是由設(shè)備中斷函數(shù)來完成。因此,實(shí)現(xiàn)中斷函數(shù)既是驅(qū)動(dòng)程序開發(fā)的核心,也是難點(diǎn)。下面是本例中使用的中斷函數(shù)的偽代碼:
ssize_tcan_isr_handle(......)
{
......
讀Call芯片的中斷狀態(tài)寄存器,用來判斷中斷源類型;
if CAN接收中斷
讀芯片的RX幀信息寄存器,然后根據(jù)接收的數(shù)據(jù)幀類型,即標(biāo)準(zhǔn)幀或擴(kuò)展幀,讀取相應(yīng)的ID寄存器和RX數(shù)據(jù)寄存器,并將各數(shù)值寫入一個(gè)數(shù)據(jù)幀結(jié)構(gòu)體中,再把讀取的該數(shù)據(jù)幀拷貝到接收緩沖區(qū)中,緩沖區(qū)的寫指針就是緩沖區(qū)的當(dāng)前可寫位置,拷貝完成后,更新寫指針位置;
讀控制器的狀態(tài)寄存器,判斷RXFIFO是否還有可用信息,若還有信息,則重復(fù)執(zhí)行上述的數(shù)據(jù)讀取操作,否則就判斷讀操作等待隊(duì)列上是否有睡眠任務(wù),有則喚醒它,中斷函數(shù)結(jié)束;
else if CAN發(fā)送中斷
判斷設(shè)備的發(fā)送緩沖區(qū)是否已為空,若為空,則置tx_in_progress變量為0,并判斷寫操作等待隊(duì)列上是否有睡眠進(jìn)程,有則喚醒它,然后中斷函數(shù)結(jié)束,
否則繼續(xù)發(fā)送緩沖區(qū)中的數(shù)據(jù)幀,而緩沖區(qū)的讀指針代表了緩沖區(qū)中當(dāng)前需要發(fā)送的數(shù)據(jù)幀位置,發(fā)送完成后,更新讀指針位置,并判斷寫操作等待隊(duì)列上是否有睡眠進(jìn)程,有則喚醒它,中斷函數(shù)退出;
else即為其他中斷
判斷錯(cuò)誤類型,并置位相應(yīng)錯(cuò)誤標(biāo)識(shí),然后分別判斷寫操作等待隊(duì)列和讀操作等待隊(duì)列上是否有睡眠進(jìn)程,有則喚醒它,中斷函數(shù)退出;
}
當(dāng)CAN控制器發(fā)生除接收中斷和發(fā)送中斷外的其它類型中斷時(shí),一般是根據(jù)具體的應(yīng)用需求采取相應(yīng)的處理措施。本例中采用了置位相應(yīng)標(biāo)識(shí),由用戶程序進(jìn)行處理的方式。另外,驅(qū)動(dòng)程序除實(shí)現(xiàn)上述的操作函數(shù)和中斷函數(shù),還應(yīng)在模塊初始化函數(shù)中完成設(shè)備注冊、申請中斷并注冊中斷函數(shù)及其他初始化工作;在模塊清除函數(shù)中需要卸載設(shè)備并釋放中斷資源。
3 結(jié)束語
本文從軟硬件兩方面對基于ARM9芯片的CAN節(jié)點(diǎn)的具體設(shè)計(jì)過程進(jìn)行了介紹,對硬件設(shè)計(jì)中的關(guān)鍵性問題和驅(qū)動(dòng)模塊實(shí)現(xiàn)結(jié)構(gòu)都作了詳細(xì)分析。目前,該CAN總線節(jié)點(diǎn)作為一個(gè)子系統(tǒng)已在某高速的網(wǎng)絡(luò)化數(shù)據(jù)采集系統(tǒng)中得到應(yīng)用,運(yùn)行結(jié)果表明設(shè)備在CAN總線2.0B協(xié)議標(biāo)準(zhǔn)(兼容2.0A)下,數(shù)據(jù)發(fā)送、接收完全正常,滿足了系統(tǒng)工作要求。本文給出的示例具有一定的普適性,對基于其他嵌入式設(shè)備上的CAN模塊開發(fā)也有一定的參考價(jià)值。
評論