新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > 一種CAN通信卡的Linux設備驅動程序設計

        一種CAN通信卡的Linux設備驅動程序設計

        作者: 時間:2014-06-13 來源:網絡 收藏

        摘要:介紹了下設備驅動程序的結構,描述了通信卡設備驅動程序的軟件框架以及如何將設備驅動程序加入到系統內核中。討論了具體實現中為了提高通信效率和通信能力,改進設備驅動程序的緩沖區管理以及利用的特點合理設計中斷處理程序。

        本文引用地址:http://www.104case.com/article/248286.htm

        關鍵詞:Linux操作系統 設備驅動程序 通信卡 中斷處理程序

        目前,許多工業現場如電力系統、化工系統等大量使用控制器局部網(CAN- Controller Area Network)現場總線網絡,CAN通信卡作為計算機的外設將計算機接入CAN網絡。市場上有不少CAN通信卡,但基本上都不帶Linux驅動程序,當需要在Linux下使用CAN通信卡設備時,需自己開發Linux的驅動程序。開發Linux驅動程序不但要求程序員要非常熟悉Linux系統,而且要熟悉Linux驅動程序開發的規范。本文將詳細介紹CAN通信卡的Linux驅動設備程序的設計和實現。

        1 CAN通信卡的Linux設備驅動程序結構

        Linux系統內核通過設備驅動程序與外圍設備進行交互,設備驅動程序是 Linux內核的一部分,它是一組數據結構和函數,這些數據結構和函數通過定義的接口控制一個或多個設備。對應用程序而言,設備驅動程序隱藏了設備的具體細節,對各種不同設備提供一致的接口,一般來說是把設備射為一個特殊的設備文件,用戶程序可以象對普通文件一樣對此設備文件進行操作。

        Linux將每個設備看作一個文件,即可以像對待文件那樣使用read、 write等系統調用進行讀寫。Linux的設備文件分為兩類:一是字符設備,只能對該類設備進行順序讀寫,對外提供字節流方式的操作;二是塊設備,可以對該類設備進行隨機訪問,一般是磁盤設備等大容量存儲設備。CAN通信卡設備屬于字符型設備。

        對設備的訪問是由設備驅動程序提供的。Linux的設備驅動程序可以用模塊的方式加載入內核,設備驅動程序與Linux系統的關系如圖1所示。

        1.1 CAN通信卡設備的特點

        控制器局部網(CAN)屬于現場總線的范疇,它是一種有效支持分布式控制或實時控制的串行通信網絡。由于其性能優異、價格低兼,很快被推廣到工業測控現場。

        CAN通信卡硬件實現CAN定義的物理層和數據鏈路層功能,收發報文中數據長度為0~8個字節,有2032個報文標識符。工作時通過報文標識符確定總線訪問優先權,高優先級報文具有低延遲時間,數據傳送速率可編程(最高為 1Mbps)。發送期間若丟二氧化碳仲裁或由于出錯而破壞的報文可自動重發。具有成組和廣播報文功能。

        當CAN通信卡接收到一個報文時,數據保存在CAN通信卡上的接收緩存器中,并產生一接收中斷。當一個報文被成功發關垢,發送緩沖器可再次被訪問,產生一個發送中斷信號。CAN通信卡發送報文,將數據送入CAN通信卡上的發送緩存器中,CAN通信卡將數據串行化發到CAN總線上。

        1.2 CAN通信卡設備驅動程序的任務

        由于CAN一幀的數據長度最大為8個字節,可以用多幀的Hilon A協議來使CAN傳輸數據任意長。CAN通信卡驅動程序主要完成按照Hilon A協議解包接收和打包發送任務,并要對接收的多幀進行管理。

        CAN通信卡驅動程序應該完成以下任務:

        (1)為應用程序提供通過CAN卡發送和接收任意長度數據的能力;

        (2)為應用程序提供設備CAN卡上CAN控制器運行參數的能力;

        (3)以阻塞或非阻塞方式讀寫CAN設備文件;

        (4)允許CAN卡同時收發多路數據。

        1.3 CAN通信卡驅動程序的處理流程

        用戶進程通過系統調用向驅動程序傳送一幀任意長度的數據,驅動程序中發送數據的程序按照Hilon A協議將該幀分段打包,放入發送隊列,并向CAN控制器請求發送,由中斷處理程序中發送部分負責發送完所有的數據包。

        當CAN通信卡接收到數據時,產生接收中斷,啟動接收中斷處理程序上半部將CAN控制器中接收緩沖器中的內容復制到接收隊列中,由中斷處理的下半部負責解包和組幀的任務,并將處理完的幀放入幀隊列中,最后用戶使用系統調用從接收幀隊列中讀取完整的一幀。

        CAN通信卡設備驅動程序處理框架如圖2所示。

        2 CAN通信卡設備驅動程序的模塊化程序設計

        根據Linux對設備驅動程序的要求,模塊化的CAN驅動程序軟件結構如圖3。

        2.1 初始化加載和卸載部分

        如果設備驅動程序以模塊方式加入內核,則一定會包括兩個模塊init_module和clear_module。init_module模塊用來加載設備,系統初始化時調用;clear_module模塊用來卸載設備,取消設備時調用。

        設備驅動程序是系統內核的一部分。在任何程序使用設備驅動程序之前,設備驅動程序應該向系統進行登記,以便系統在適當的時候調用。Linux系統里,通過調用register_chrdev函數向系統注冊字符型設備驅動程序。Register_chrdev定義為:

        int register_chrdev(unsigned int major,const char *name,struct file_operations *fops);

        其中,major是為設備驅動程序向系統申請的主設備號,如果為0則系統為此驅動程序動態分配一個主設備號;name是設備名;fopsa是對各個系統調用的入口點的說明 。

        CAN通信卡使用中斷與系統交換數據,CAN設備驅動程序需要使用內存來緩存接收到的數據和發送的數據,中斷和內存等資源是由Linux系統統一管理的,設備驅動程序在初始化時,需要申請資源。在資源不用的時候,應該釋放它們,以利于資源的共享。在Linux系統里,對中斷的處理是屬于系統核心的部分,設備驅動程序通過調用request_irq()函數來申請中斷,通過 free_irq()函數來釋放中斷。作為系統核心的一部分,設備驅動程序在申請和釋放內存時不是調用malloc()函數和free()函數,而代之以調用kmalloc函數和kfree()函數。

        在init_module模塊中,先檢查是否存在CAN通信卡,如果不存在則退出設備驅動程序的加載;如果存在,使用request_irq()函數為CAN通信卡申請系統中空閑的中斷,使用kmalloc()為設備驅動程序申請輸入輸出緩存隊列,如果這些資源申請不成功,則釋放已經申請到的系統資源,然后退出設備驅動程序的加載,如果申請成功,使用 register_chrdev()函數將CAN通信卡驅動程序注冊到Linux系統中,加載完成。

        在cleanup_module模塊中先使用free_irq()函數釋放 init_module模塊中申請到的中斷,然后使用kfree()函數釋放init_module模塊中申請到的內存空間,最后使用 unregister_chrdev()函數釋放init_module模塊中注冊的設備驅動程序,卸載完成。

        2.2 CAN通信卡設備驅動程序的中斷處理部分

        Linux中斷處理程序可以分為上半部和下半部。上半部即一般的中斷服務程序,由硬件中斷觸發,它一般運行在關中斷的方式下,應當盡可能短小,處理盡可能快;而下半部是單獨的一段處理程序,一般將其掛入立即隊列中以便快速執行。立即隊列中的任務在退出系統調用或調度器獲得運行時,將最優先地被執行。下半部運行在一個安全的環境,即開中斷和任務串行化,可以處理一些較花時間的任務。

        這樣,驅動程序上半部在處理完實時性很強的任務后,用queue_task()函數將下半部處理函數持入立即隊列,并用mark_bh()函數來激活立即隊列,則下半部可以最優先地被執行。

        當有接收中斷時,CAN通信卡設備驅動程序的中斷處理程序上半部首先獲取驅動程序接收緩存中的空閑塊,將CAN通信卡上的接收緩沖器中數據復制到驅動程序的接收緩存中,釋放CAN通信卡接收緩沖器,然后將下半部處理函數掛入立即隊列,最后激活立即隊列。

        當有發送中斷時,CAN通信卡設備驅動程序的中斷處理程序上半部首先獲取發送緩沖隊列中的數據,將需要發送的數據寫入CAN控制器的發送緩沖器,最后請求發送。

        CAN通信卡設備驅動程序的中斷處理程序下半部使用Hilon A協議對要傳送到CAN網絡上的數據打包,并對從CAN網絡上接收到的數據進行解包和組幀。這樣中斷處理程序的上半部只需要從CAN通信卡的緩沖器中將數據復制到驅動程序的緩沖區,系統開銷很小,但很費CPU時間;系統開銷較大的打包、解包和組幀處理則放在中斷處理程序的下半部,使用系統非中斷時間調度,可以使系統響應中斷更快,通信更穩定。

        2.3 緩沖區管理

        在CAN通信卡設備驅動程序中,為了增強CAN通信卡的通信能力、提高通信效率,根據CAN的特點,使用兩級緩沖區結構,即直接面向CAN通信卡的收發緩沖區和直接面向系統調用的接收幀緩沖區。

        通訊中的收發緩沖區一般采用環形隊列(或稱為FIFO隊列),使用環形的緩沖區可以使得讀寫并發執行,讀進程和寫進程可以采用“生產者和消費者”的模型來訪問緩沖區,從而方便了緩存的使用和管理。然而,環形緩沖區的執行效率并不高,每讀一個字節之前,需要判斷緩沖區是否為空,并且移動尾指針時需要進行“折行處理”(即當指針指到緩沖區內存的末尾時,需要新將其定向到緩沖區的首地址);每寫一個字節之前,需要判斷緩區是否為,并且移動尾指針時同樣需要進行“折行處理”。程序大部分的執行過程都是在處理個別極端的情況。只有小部分在進行實際有效的操作。這就是軟件工程中所謂的“8比2”關系。結合CAN通訊實際情況,在本設計中對環形隊列進行了改進,可以較大地提高數據的收發效率。

        由于CAN通信卡上接收和發送緩沖器每次只接收一幀CAN數據,而且根據CAN 的通訊協議,CAN控制器的發送數據由1個字節的標識符、一個字節的RTR和DLC位及8個字節的數據區組成,共10個字節;接收緩沖器與之類似,也有 10個字節的寄存器。所以CAN控制器收的數據是短小的定長幀(數據可以不滿8字節)。

        于是,采用度為10字節的數據塊業分配內存比較方便,即每次需要內存緩沖區時,直接分配10個字節,由于這10個字節的地址是線性的,故不需要進行“折行”處理。更重要的是,在向緩沖區中寫數據時,只需要判斷一次是否有空閑塊并獲取其塊首指針就可以了,從而減少了重復性的條件判斷,大大提高了程序的執行效率;同樣在從緩沖隊列中讀取數據時,也是一次讀取10字節的數據塊,同樣減少了重復性的條件判斷。

        在CAN卡驅動程序中采用如下所示的稱為“Block_Ring_t”的數據結構作為收發數據的緩沖區:

        typedef struct {

        log signature;

        unsigned char *head_p;

        unsigned char *tail_p;

        unsigned char *begin_p;

        unsigned char *end_p;

        unsigned char buffer [BLOCK_RING_BUFFER_SIZE];

        int usedbytes;

        }Block_Ring_t;

        該數據結構在通用的環形隊列上增加了一個數據成員usedbytes,它表示當前緩沖區中有多少字節的空間被占用了。使用usedbytes,可以比較方便地進行緩沖區滿或空的判斷。當usedbytes=0時,緩沖區空;當 usedbytes=BLOCK_RING_BUFFER_SIZE時,緩沖區滿。

        本驅動程序除了收發緩沖區外,還有一個接收幀緩沖區,接收幀隊列負責管理經 Hilon A協議解包后得到的數據幀。由于有可能要同接收多個數據幀,而根據CAN總線遙通信協議,高優先級的報文將搶占總線,則有可能在接收一個低優先級且被分為好幾段發送的數據幀時,被一個優先級高的數據幀打斷。這樣會出現同時接收到多個數據幀中的數據包,因而需要有個接收隊列對同時接收的數據幀進行管理。

        當有新的數據包到來時,應根據addr(通訊地址),mode(通訊方式),index(數據包的序號)來判斷是否是新的數據幀。如果是,則開辟新的frame_node;否則如果已有相應的幀節點存地,則將數據附加到該幀的末尾;在插入數據的同時,應該檢查接收包的序號是否正確,如不正確將丟棄這包數據。

        每次建立新的frame_node時,需要向frame_queue申請內存空間;當frame_queue已滿時,釋放掉隊首的節點(最早接收的但未完成的幀)并返回該節點的指針。

        當系統調用讀取了接收幀后,釋放該節點空間,使設備驅動程序可以重新使用該節點。

        2.4 服務于I/O請求的設備驅動程序部分

        這部分實際上是應用程序唯一可見的,應用程序通過系統來調用這部分程序,是設備驅動程序對應用程序的接口。本驅動程序提供文件操作接口。Linux系統中,字符型設備驅動程序提供的文件操作入口點由一個結構來向系統說明,此結構定義為:

        struct file_operations {

        int (*lseek)(strut inode *inode,struct file *file,off_toff,int pos);

        int (*read)(struct inode *inode,struct file *filp,char *buf,int count);

        int (*write)(struct inode *inode,struct file *file,char *buf,int count);

        int (*readdir)(struct inode *inode,struct file *filp,struct dirent *dirent,int count);

        int (*select)(struct inode *inode,struct file *filp,int sel_type,select_table *wait);

        int (*ioctl)(struct inode *inode,struct file *filp,unsigned int cmd,unsigned int arg);

        int (*mmap)(void);

        int (*open)(struct inode *inode,struct file *filp);

        void (*release)(struct inode *inode,struct file filp);int (fsync) (struct inode *inode,struct file *filp);

        };

        該結構定義為10個操作入口點,但是驅動程序沒有必要對每個入口點進行定義。根據需要,本驅動程序定義了如下的入口點。

        can_open(struct inode *inode,struct file *filp)入口點負責打開can設備,檢查can卡是否已被打開,完成can卡的初始化,設備設備的占用標志。can_release(struct inode *inode,struct file *filp)入口點負責關閉can設備。

        can_read(struct inode *,struct file , off_t,int)入口點負責檢查設備有沒有接收到完整的幀,can_read函數只是判斷是否有完整的數據幀可讀。要獲取數據幀,可以使用ioctl的CAN_READFRAME命令。can_write(struct inode,struct file *,const char *,int)入口點負責向CAN發送數據。如果發送隊列有足夠的空間,則向設備傳送數據,也可以使用ioctl的CAN_WRITEFRAME命令來實現can_write。

        Can_inoctl(struct inode *,struct file *,unsigned int cmd,unsigned long arg)入口點負責向CAN設備下發各種操作命令,命令代碼通過cmd參數傳送,命令參數通過arg參數傳送。本驅動程序提供了一些命令,配合 can_read()和can_write()可以實現對CAN通信卡的控制。CAN_IOCREADFFRAME命令可以從CAN通信卡上讀取數據幀;CAN_IOCWRITEFRAME命令可以向CAN通信卡發送數據;CAN_IOCSETCONF命令可以設備CAN通信卡的運行參數;CAN_IOCGETCONF命令可以獲取CAN控制器的運行參數;CAN_IOCQUERY-BUSSTATE命令可以查詢CAN總線狀態;CAN_INCCLEARBUF命令可以清除CAN通信卡的收發緩沖區。

        本設備驅動程序考慮到CAN通信卡的特點如CAN網絡傳輸數據的特點,設計了合理的數據結構和緩存管理方法,使得當有大量數據進出CAN通信卡時,既可以保證數據幀丟失和出錯幾率在允許范圍內,又可以保證數據幀能被快速下發和接收,實現應用中性能很好。Linux擅長通信,支持大多數以太網卡。如果將CAN通信卡的設備驅動程序加入到Linux系統,由于Linux的可裁減性和對硬件資源要求低的特點,可以用小硬盤、小內存和低檔CPU構成通信機連接高速以太網和低速現場總線CAN網絡,經濟實惠而且實用。

        linux操作系統文章專題:linux操作系統詳解(linux不再難懂)

        linux操作系統文章專題:linux操作系統詳解(linux不再難懂)

        linux相關文章:linux教程


        數字通信相關文章:數字通信原理


        通信相關文章:通信原理




        關鍵詞: CAN Linux

        評論


        相關推薦

        技術專區

        關閉
        主站蜘蛛池模板: 临高县| 宜都市| 涞源县| 陆河县| 沅陵县| 米林县| 屏边| 大悟县| 临邑县| 威海市| 垣曲县| 缙云县| 武威市| 翁牛特旗| 渝北区| 历史| 信丰县| 汤阴县| 巫山县| 枣强县| 宝清县| 敖汉旗| 大安市| 沁阳市| 铜鼓县| 连城县| 上栗县| 连云港市| 德清县| 涞水县| 紫云| 宁强县| 濮阳县| 南开区| 邻水| 安泽县| 余干县| 镇宁| 廊坊市| 商水县| 易门县|