張永紅
(北京電子科技職業學院 自動化工程學院,北京 100176)
近年來,基于各種總線標準的網絡化控制系統已經在工業控制領域內得到廣泛應用。網絡化控制系統采用了完全分散化的控制節點結構,將控制的權力很大部分交給了處于控制現場的智能節點,系統內各種交互信息通過現場總線傳送。
當前已實用化的總線標準有許多種,如WorldFIP、Profibus、LONWORKS、CAN 等。 其中,CAN(Controller Area Network)是Bosch公司在現代汽車應用技術中領先推出的一種串行通信網絡。CAN主線采用多主站工作方式,根據優先權進行總線訪問仲裁,能夠檢測出通信過程產生的任何錯誤。CAN總線還具有卓越的信號傳輸性能,當信號傳輸距離達到10km時,它仍可提供高達50Kbit/s的數據傳輸速率。另外,CAN協議廢除了站地址編碼,而采用對通信數據塊編碼的方式,這樣使得網絡內的節點個數在理論上不受限制。目前,CAN總線已經在許多行業得到了廣泛的應用,尤其是工業控制領域,并常被認為是最有前途的現場總線之一。
常用的CAN總線節點一般采用的是“單片機+CAN控制器”的結構,這樣由于運算能力的限制,這類節點的智能化程度較低,常是作為工控機節點的從節點。而近年來,以ARM為代表的嵌入式32位微處理器技術得到了飛速發展,無論是在功耗、便攜性還是在硬件成本上,許多高性能的ARM芯片已經與單片機相差無幾,因此在CAN節點設計中,使用ARM芯片取代傳統的8/16位單片機已經是一個非常實用的選擇。這樣設計的CAN節點,不僅保留了低功耗、低成本和小體積的優點,而且性能得到了大幅提高,若輔以大容量的存儲器,同時運行功能強大的嵌入式操作系統,它幾乎已可以取代原先的工控機節點。本文則從軟硬件兩方面詳細介紹了上述設計方案的具體實現過程。
本文設計的CAN總線節點是某工業控制系統的一個子模塊,同時綜合考慮其它相關需求和功能擴展,因此選用了AT91RM9200處理器作為系統的核心處理單元。AT91RM9200[1]是ATMEL公司生產的一款高性能的ARM9處理器,它是一款通用工業級ARM芯片,主頻為180MHz/200MIPS,已經在工業控制、智能儀器儀表等領域內得到了大量的成功應用。
CAN控制器選用的是SJA1000芯片[2],它是Philips公司生產的一款獨立CAN總線控制器芯片,專用于移動目標和一般工業環境中控制器局域網絡(CAN)。SJA1000本質上是早期的PCA82C200的升級產品,與后者在管腳、電氣特性上完全兼容,而且除具有基本CAN工作模式(BasicCAN)外,還增加了一種新的增強工作模式(PeliCAN),這種新模式支持具有許多新特性的CAN2.0B協議。
SJA1000的總線接口采用的是地址總線和數據總線復用的方式,這種方式與51類似,也采用總線復用架構的處理器,接口很方便,并在讀寫時序上也很好配合,但當與數據總線和地址總線分離的微處理器接口時,則需要專門的讀寫邏輯與之配合,并且還相對比較復雜,基于這個原因,目前許多設計都采用諸如SPI等專用接口的CAN總線控制器,但這種方式使應用受到了諸多限制,如要求微處理器必須有SPI接口,同時當系統需要多路CAN總線接口時,會受到SPI端口數的限制等。
ARM架構的數據總線和地址總線是分離的,因此,必須引入專門的控制邏輯,才能實現對SJA1000的操作。
圖1和圖2分別為SJA1000讀/寫操作時序。分析其讀/寫時序,可以看出,無論是讀操作還是寫操作,首先必須送出操作寄存器的地址,然后讀/寫數據。在寫地址的過程中,片選信號(/CS)和讀(/RD)、寫(/WR)均無效(高電平),僅ALE信號有效(高電平),而在讀/寫數據的過程中,讀/寫信號有效(低電平),ALE信號無效(低電平),同時,在操作的過程中,還必須滿足信號電平的持續時間。


因此,可以采用如圖3所示的控制邏輯實現。在圖3所示的SJA1000操作時序的實現中,左端的信號如/CS、/RD、/WR、A7分別為ARM微處理器的片選、讀、寫控制信號,A7為地址信號(也可以是其它的地址),右端產生的ALE_CAN、/CS_CAN、/WR_CAN、/RD_CAN 分別與SJA1000對應的信號相連接,當微處理器對SJA1000對應的地址進行讀寫操作時,即可產生正確的控制邏輯。
在工業控制應用中,使用嵌入式操作系統已逐漸成為一個流行的選擇。目前,市場上的嵌入式操作系統超過100種,其中嵌入式Linux是一種非常理想、經濟的選擇,因為它不僅具有功能強大、高性能、穩定性好等優點,還是免費并開放源代碼的。同時Linux內核采用了模塊化設計,具有非常良好的移植性和可定制性。現在,許多ARM生產廠商都已經將Linux系統移植到其生產的ARM芯片上,并發布了相關源代碼供用戶免費使用。本系統中采用的操作系統就是ATMEL公司發布的支持其AT912RM9200處理器的ARMLinux系統的版本,版本號為2.4.27。
CAN控制器SJA1000顯然屬于Linux系統中的字符設備類型,其驅動程序的實現架構類似于系統中字符設備的通用實現結構,關于Linux設備驅動開發的詳細分析可參考文獻[3]。本小節則以SJA1000的增強工作模式(PeliCAN)為例,對Linux系統下CAN設備驅動程序的主要實現部分進行了詳細說明,包括主要數據結構的定義、操作函數和中斷函數的實現三個部分。
為方便驅動程序的設計和編寫,驅動中定義了兩個數據結構體,即協議幀數據結構和緩沖區結構體,下面給出每個結構體的定義及成員變量的解釋。
1)協議幀數據結構


其中,協議幀結構體是用來對CAN網絡的報文數據幀進行抽象,驅動中使用該結構體來進行用戶與內核空間的數據幀傳遞及發送/接收數據緩沖區的管理。
從緩沖區結構體的定義可以看出,CAN設備的數據緩沖區由兩個獨立緩沖區構成,一個用于設備讀操作,另一個用于寫操作。讀緩沖區和寫緩沖區都是一個先入先出的環形緩沖區,緩沖區的大小設為協議幀結構體的整數倍,如64倍。驅動通過緩沖區的讀指針和寫指針來進行緩沖區管理。如對于接收緩沖區(讀緩沖區),它的讀指針指向了當驅動程序從內核空間向用戶空間拷貝報文數據幀時,緩沖區中第一個有效數據幀的位置;而緩沖區的寫指針則代表了在控制器芯片執行接收數據時,即將數據幀從SJA1000的寄存器讀到緩沖區,緩沖區的當前可寫位置;這樣通過讀指針和寫指針的相對位置及緩沖區的整體長度就可以得到讀緩沖區中當前的數據幀個數。寫緩沖區的管理與讀緩沖區基本相同,其詳細機制可參考后面給出的相關偽代碼。同時緩沖區中還定義了volatile int型變量tx_in_progress來表征當前是否有實際的數據發送操作已被啟動,“1”標識已啟動,否則為“0”。另外,結構體定義中使用到了Linux系統的等待隊列結構,關于它的詳細機制可參考文獻[3]。
字符設備驅動的核心就是實現設備的操作函數結構,所謂的操作函數結構,本質上是定義了應用程序在設備上的所有可能操作。但對于某一具體設備,驅動中只需要實現設備工作所必須的操作。如對于CAN設備,本例中共實現了5種系統調用函數,即open、close、read、write和ioctl函數。
其中,open函數是在應用程序打開CAN設備時被調用,函數主要實現兩部分功能,首先對驅動中的變量和數據結構進行初始化,并分配緩沖區空間;另一部分就是對CAN控制器SJA1000初始化,即在復位時為芯片的各個寄存器設置正確的初始值。對于SJA1000芯片,需要設置的寄存器包括:1)模式和時鐘寄存器;2)輸出控制寄存器;3)驗收代碼寄存器和驗收屏蔽寄存器;4)總線定時寄存器;5)錯誤計數寄存器;6)中斷使能寄存器。需要注意的是,SJA1000的配置寄存器只能在復位模式下可寫,所以在設置寄存器之前,必須先進入復位模式,所有設置完成后必須返回正常工作模式,關于SJA1000芯片寄存器設置的更多內容可參見文獻[2]。CAN設備的close調用的實現功能非常簡單,即等待緩沖區中已有的數據幀被處理完,然后釋放緩沖區,最后關閉設備中斷。
驅動程序的write就是對應于用戶寫CAN設備時的系統調用。它的功能就是完成應用程序在用戶空間中的報文發送,即報文數據從用戶空間向內核空間的傳遞。下面給出了實現write函數的偽代碼:

判斷指定的數據長度是否滿足數據幀格式,若不滿足,則提示并返回;
通過寫指針和讀指針的相對位置及緩沖區的整體長度計算輸出緩沖區中空閑空間的大小;
while 空閑空間長度 < 一個數據幀大小
使用interruptible_sleep_on_timeout()函數將寫進程放入寫操作等待隊列睡眠,該函數既說明了睡眠可被信號中斷,還可以指定進程的最長睡眠時間;
睡眠結束后,再次計算空閑空間的大小。若空閑空間長度已大于或等于一個數據幀大小,則可跳出循環,否則繼續while循環;
從用戶空間拷貝N字節數據到輸出緩沖區,其中:N = min(計算的空閑空間大小,數據長度參數),然后更新輸出緩沖區的寫指針位置;
if 當前無實際的發送操作已被啟動,即tx_in_progress 等于 0
置位tx_in_progress為1,同時調用發送初始化函數,即將輸出緩沖區中第一個有效數據幀寫入到控制器芯片相應的發送寄存器中,同時使能發送操作,完成后,結束can_write()函數并返回數值N;

需要說明的是,上述偽碼中在對臨界區變量進行操作和判斷時,即計算空閑區長度和判斷tx_in_progress變量時,必須要在關閉設備中斷條件下進行,操作完成后立即重新打開中斷,下面描述的讀函數中與臨界區變量相關的操作也必須采用同樣的機制。
讀函數read的功能就是響應用戶對CAN設備的讀操作,如果接收緩沖區中已有了數據幀,則直接從緩沖區拷貝M字節的數據返回給用戶,其中M為用戶要求字節數與緩沖區已有數據字節數之間的較小值,更新讀指針位置然后函數返回數值M;而如果當前緩沖區中無有效數據,則需要判斷設備文件的讀取模式,若為非阻塞模式,則直接返回,否則將讀進程放入讀操作等待隊列睡眠,指定進程的最大睡眠時間并設置為睡眠可被信號中斷模式,當任務被喚醒后,再對喚醒方式進行判斷,若因睡眠時間結束而喚醒,則函數直接返回相應標識,否則說明緩沖區中已有數據,則執行上述的拷貝動作,并更新緩沖區的讀指針位置,函數結束并返回實際讀取字節數,具體實現略。
設備的ioctl函數是用于設備控制的公共接口,可以根據設備的具體需求來實現相應的控制代碼,在本文中共實現了三種功能,即清除讀/寫緩沖區、設置總線波特率、設置驗收代碼寄存器和驗收屏蔽寄存器。
如上所述,設備的讀函數和寫函數只是完成用戶空間與設備的接收/發送緩沖區之間的數據幀傳遞,而真正的數據接收和發送工作,即芯片的寄存器與數據緩沖區之間的數據讀取和寫入,是由設備中斷函數來完成。因此,實現中斷函數既是驅動程序開發的核心,也是難點。下面是本例中使用的中斷函數的偽代碼:

讀can芯片的中斷狀態寄存器,用來判斷中斷源類型;
if CAN接收中斷
讀芯片的RX幀信息寄存器,然后根據接收的數據幀類型,即標準幀或擴展幀,讀取相應的ID寄存器和RX數據寄存器,并將各數值寫入一個數據幀結構體中,再把讀取的該數據幀拷貝到接收緩沖區中,緩沖區的寫指針就是緩沖區的當前可寫位置,拷貝完成后,更新寫指針位置;
讀控制器的狀態寄存器,判斷RXFIFO是否還有可用信息,若還有信息,則重復執行上述的數據讀取操作,否則就判斷讀操作等待隊列上是否有睡眠任務,有則喚醒它,中斷函數結束;
else if CAN發送中斷
判斷設備的發送緩沖區是否已為空,若為空,則置tx_in_progress變量為0,并判斷寫操作等待隊列上是否有睡眠進程,有則喚醒它,然后中斷函數結束;
否則繼續發送緩沖區中的數據幀,而緩沖區的讀指針代表了緩沖區中當前需要發送的數據幀位置,發送完成后,更新讀指針位置,并判斷寫操作等待隊列上是否有睡眠進程,有則喚醒它,中斷函數退出;
else 即為其他中斷
判斷錯誤類型,并置位相應錯誤標識,然后分別判斷寫操作等待隊列和讀操作等待隊列上是否有睡眠進程,有則喚醒它,中斷函數退出;
}
當CAN控制器發生除接收中斷和發送中斷外的其它類型中斷時,一般是根據具體的應用需求采取相應的處理措施。本例中采用了置位相應標識,由用戶程序進行處理的方式。另外,驅動程序除實現上述的操作函數和中斷函數,還應在模塊初始化函數中完成設備注冊、申請中斷并注冊中斷函數及其他初始化工作;在模塊清除函數中需要卸載設備并釋放中斷資源。
本文從軟硬件兩方面對基于ARM9芯片的
CAN節點的具體設計過程進行了介紹,對硬件設計中的關鍵性問題和驅動模塊實現結構都作了詳細分析。目前,該CAN總線節點作為一個子系統已在某高速的網絡化數據采集系統中得到應用,
運行結果表明設備在CAN總線2.0B協議標準(兼容2.0A)下,數據發送、接收完全正常,滿足了系統工作要求。本文給出的示例具有一定的普適性,
對基于其他嵌入式設備上的CAN模塊開發也有一定的參考價值。
[1] Atmel Semiconductors. Data Sheet AT91RM9200 [EB/OL]. http://www.atmel.com, 2005.
[2] 周立功單片機發展有限公司. SJA1000獨立CAN控制器應用指南[EB/OL]. http://www.zlgmcu.com, 2004.
[3] 魏永明, 駱剛, 姜君, 譯. Linux設備驅動程序(第二版)[M]. 北京: 北京電力出版社,2002.
[4] 鄔寬明. CAN總線原理和應用系統設計[M]. 北京: 北京航空航天大學出版社, 1996.