《電子技術(shù)應(yīng)用》
您所在的位置:首頁 > 通信與網(wǎng)絡(luò) > 設(shè)計應(yīng)用 > 嵌入式uClinux下的CAN總線設(shè)備驅(qū)動程序設(shè)計
嵌入式uClinux下的CAN總線設(shè)備驅(qū)動程序設(shè)計
EEworld
EEworld
摘要: 摘要:介紹了uClinux操作系統(tǒng)和嵌入式Linux系統(tǒng)的開發(fā)流程及其驅(qū)動程序的開發(fā),,通過對CAN總線設(shè)備SJA1000的驅(qū)動...
Abstract:
Key words :

關(guān)鍵字:uClinux,;嵌入式操作系統(tǒng),;CAN總線,;設(shè)備驅(qū)動程序

1、uClinux操作系統(tǒng)概述

uClinux是Linux2.0的一個分支,,它被設(shè)計用于沒有MMU的微控制器領(lǐng)域,,即被廣泛應(yīng)用于嵌入式Linux領(lǐng)域。uClinux的最大特征就是沒有MMU(內(nèi)存管理單元模塊),。它很適合那些沒有MMU的處理器,如ARM7TDMI,,m68ez328等,。

uClinux具有完全的TCP/IP協(xié)議棧,同時對其他許多的網(wǎng)絡(luò)協(xié)議都提供支持,。這些網(wǎng)絡(luò)協(xié)議都在uClinux上得到了很好的實現(xiàn),。uClinux可以稱作是一個針對嵌入式系統(tǒng)的優(yōu)秀網(wǎng)絡(luò)操作系統(tǒng),。

2、Linux驅(qū)動程序設(shè)計概述

     Linux系統(tǒng)內(nèi)核通過設(shè)備驅(qū)動程序與外圍設(shè)備交互,,設(shè)備驅(qū)動程序是Linux內(nèi)核的一部分,,它是一組數(shù)據(jù)結(jié)構(gòu)和函數(shù),這些數(shù)據(jù)結(jié)構(gòu)和函數(shù)通過定義的接口控制一個或多個設(shè)備,。

和UNIX一樣,,Linux中所有的設(shè)備均作為文件來對待,這些文件一般稱為特殊文件,,這樣做的一個好處是使用戶或應(yīng)用程序可按操縱普通文件的方式進行訪問控制硬件設(shè)備,。

Linux內(nèi)核有三種類型的設(shè)備驅(qū)動程序:字符設(shè)備驅(qū)動程序、塊設(shè)備驅(qū)動程序和網(wǎng)絡(luò)設(shè)備驅(qū)動程序,。Linux的設(shè)備由一個主設(shè)備號(major)和一個次設(shè)備號(minor)標識,。主設(shè)備號唯一標識了設(shè)備類型,它是塊設(shè)備表或字符設(shè)備表中設(shè)備表的索引,。次設(shè)備號僅由設(shè)備驅(qū)動程序解釋,,用于識別同類設(shè)備中,I/O請求所涉及到的那個設(shè)備,。設(shè)備驅(qū)動程序可以分為3個主要組成部分:

(l)自動配置和初始化子程序,,負責檢測所要驅(qū)動的硬件設(shè)備是否能正常工作。

(2)服務(wù)于I/O請求的子程序,,又稱為驅(qū)動程序的上半部分,。

(3)中斷服務(wù)子程序,又稱為驅(qū)動程序的下半部分,。

3,、uClinux下CAN設(shè)備的驅(qū)動程序編寫

根據(jù)上文對LINUX下設(shè)備驅(qū)動程序的描述,以及參考相關(guān)的實例分析,,下面對CAN總線設(shè)備SJA1000的驅(qū)動程序進行編寫,。

CAN設(shè)備驅(qū)動程序?qū)嶋H上是linux內(nèi)核直接對sja1000器件的初始化與讀寫操作。經(jīng)分析,,sja1000 CAN驅(qū)動程序構(gòu)成包括如下幾個部分:

1)定義sja1000芯片內(nèi)所有寄存器的訪問地址,,用于完成對其內(nèi)部寄存器以及緩沖區(qū)的讀寫訪問。例如:

#define IO_PMOD    (*(volatile unsigned *)0x3ff5000)

#define IO_PDATA   (*(volatile unsigned *)0x3ff5008)

#define IO_PCON    (*(volatile unsigned *)0x3ff5004)

#define SJA_MOD    (0x2700000) #define SJA_CMR     (0x2700004)

…………………

#define SJA_CANRXB7 (0x270006c) #define SJA_CANRXB8 (0x2700070)

因為在我們的系統(tǒng)中,,對sja1000的讀寫是采用的部分模擬時序的方式,,所以用到了S3C4510的IO端口。下面對sja1000地址的定義進行分析,。因為uClinux運行的時候,,采用的是32位方式,即兩個相鄰地址間相隔4個字節(jié),而在sja1000內(nèi)部的地址間的間隔只有1個字節(jié),。雖然可以對S3C4510的內(nèi)部寄存器定義為在訪問sja1000的時候,,將位寬度定義為8位,但這樣會與linux系統(tǒng)運行不匹配,,經(jīng)測試發(fā)現(xiàn)讀寫不正常,。所以將sja1000的地址定義為32位寬度。于是各個寄存器地址為(基址+sja1000內(nèi)部地址×4),。這里將sja1000的基址定義為0x2700000,。

2)編寫對SJA1000內(nèi)部寄存器訪問的讀寫函數(shù)

因為S3C4510B處理器的地址和數(shù)據(jù)總線是分開的,而SJA1000的地址與數(shù)據(jù)總線是8位分時復用的,。所以我們只有采用先向sja1000的8位地址數(shù)據(jù)總線上送出地址,,然后再送數(shù)據(jù)或者讀數(shù)據(jù)的方式。片選信號/CS,,讀信號/RD,,寫信號/WR仍由S3C4510B自己產(chǎn)生。需要模擬的是鎖存信號ALE,、地址數(shù)據(jù)總線AD0-AD7,。參照sja1000時序圖,具體的操作步驟見下面程序和注釋,。

寫子程序如下:

   void sja_write(unsigned int data, unsigned int addr)

{   unsigned char tmp;

    tmp=(addr)>>2;//將32位地址右移2位,,tmp的低8位即為sja1000實際地址。

        outl(tmp,addr);//將地址信息作為數(shù)據(jù)送往SJA1000數(shù)據(jù)總線

        IO_PDATA=0x32;//ALE=0,,讓SJA1000將該地址鎖存

        outl(data,addr);//將數(shù)據(jù)信息送往SJA1000數(shù)據(jù)總線

        O_PDATA=0x33; } //將ALE置高電平,,74HC245的/OE置高位

讀子程序如下:

unsigned char sja_read(unsigned int addr)

{ unsigned char data;

    volatile unsigned int data1;

    unsigned char tmp;

tmp=(addr)>>2; //將32位地址右移2位,tmp的低8位即為sja1000實際地址S3C2410

    outl(tmp,addr); //將地址信息作為數(shù)據(jù)送往SJA1000數(shù)據(jù)總線

    IO_PDATA=0x32; //p0-ALE=0,,鎖存地址信息

    IO_PDATA=0x12; //p5-245dir=0,,將74HC245的方向置為CPU輸入方向

    data1=inl(addr); //讀出所需的數(shù)據(jù)

    IO_PDATA=0x33; //ALE置高,74HC245置為不工作狀態(tài)

    data=data1;     return(data);   }//返回數(shù)據(jù)

    后面對sja1000的初始化,、CAN發(fā)送與CAN接收函數(shù)中需要對寄存器操作均調(diào)用sja_write()和sja_read()函數(shù)進行,。

3)定義驅(qū)動程序的文件結(jié)構(gòu)

    在LINUX系統(tǒng)中,對硬件設(shè)備的訪問也是被當作文件來操作的,。這里定義的文件接口將可以在外部的應(yīng)用程序中被調(diào)用,。在CAN驅(qū)動程序中,只定義了讀CAN信息(CAN接收),、向CAN節(jié)點寫信息(CAN發(fā)送),、打開CAN設(shè)備、關(guān)閉CAN設(shè)備等4個文件接口,。定義信息如下面的程序所示,。在sja1000_fops中所定義的函數(shù)都必須在驅(qū)動程序中編寫。

static struct file_operations sja1000_fops = {

    read:       sja1000_read,   //CAN接收數(shù)據(jù)

    write:      sja1000_write,//CAN發(fā)送

   open:       sja1000_open,   //打開設(shè)備S3C2410 開發(fā)板II(B)+3.5寸帶觸摸TFT液晶屏

   release:  sja1000_release, };//關(guān)閉設(shè)備

4)定義sja1000_write :CAN發(fā)送函數(shù)(寫函數(shù))

static int sja1000_write(struct file *filp, const char *buf, size_t size,

loff_t *offp){  }

    在CAN總線控制器Sja1000初始化完成后,即可設(shè)置CAN發(fā)送,,具體對sja1000寄存器的相關(guān)操作的相關(guān)程序可參閱SJA1000器件的數(shù)據(jù)手冊。

5)定義sja1000_read :CAN接收函數(shù)(讀函數(shù))

static int sja1000_read(struct file *filp, char *buf, size_t size,

loff_t *offp) {  }

   該函數(shù)完成對CAN總線網(wǎng)絡(luò)上相應(yīng)信息的接收,。在本系統(tǒng)中CAN接收采用的是查詢方式,。

6)編寫sja1000_open:文件打開函數(shù)

static int sja1000_open(struct inode *inode,struct file *file) { }S3C2410 開發(fā)板II(B)+3.5寸帶觸摸TFT液晶屏

進程調(diào)用該函數(shù)表示對設(shè)備的占用。如果返回為-1,,表示設(shè)備已被其他進程占用,,打開非法。如果采用中斷方式,,對中斷的注冊也可放在本函數(shù)中,。

7)編寫sja1000_release:文件關(guān)閉函數(shù)

static int sja1000_release(struct inode *inode, struct file *file) { }

該函數(shù)進程完成對設(shè)備占有權(quán)的釋放,釋放后,,其他的進程就可以訪問這個設(shè)備了,。

8)編寫sja1000_init(void):void sja1000_init(void) { } CAN設(shè)備初始化函數(shù)

 該函數(shù)完成設(shè)備在LINUX內(nèi)核中的登記。并完成對sja1000初始化,。

Sja1000寄存器配置通過調(diào)用上面已寫好的sja_write()函數(shù)完成,。驅(qū)動函數(shù)登記我們采用的是靜態(tài)加載的方式,通過調(diào)用register_chrdev()完成,,程序如下:

if(result = register_chrdev(254,"sja1000",&sja1000_fops))  S3C2410 開發(fā)板II(B)+3.5寸帶觸摸TFT液晶屏

   printk("S3C4510-sja1000: Error %d registering device sja1000\n", result);

其中,,254是為sja1000設(shè)備分配的主設(shè)備號,“sja1000”是顯示在/dev中的設(shè)備名,,sja1000_fops為對應(yīng)的文件系統(tǒng)指針,。返回值小于0表示失敗,大于或等于0表示成功,。

9)將驅(qū)動程序加到uClinux內(nèi)核中

    當驅(qū)動程序sja1000.c編寫完成后,,下面的工作就是將它加到uClinux內(nèi)核中了。這需要修改uClinux的源代碼,,然后重新編譯uClinux內(nèi)核,。

    ①將設(shè)備驅(qū)動程序文件sja1000.c復制到/uClinux-dist/linux/drivers/char目錄下。該目錄保存了uClinux字符設(shè)備的設(shè)備驅(qū)動程序,。修改該目錄下mem.c文件,,在Init chrdev_init()函數(shù)中增加如下代碼:

#ifdef CONFIG_SJA1000_DRIVER device_init() #endifS3C2410 開發(fā)板II(B)+3.5寸帶觸摸TFT液晶屏

其中CONFIG_SJA1000_DRIVER是在配置uClinux內(nèi)核時賦值的。

②在uClinux/linux/drivers/char目錄下 Makefile中增加如下代碼:

ifeq($(CONFIG_SJA1000_DRIVER,y)  L_OBJS+=sja1000.c  endif

如果在配置uClinux內(nèi)核的時候選擇了支持我們定義的設(shè)備,,則在編譯內(nèi)核的時候會編譯sja1000.c,,生成 sja1000.o文件。

③修改 /uClinux-dist/linux/arch/m68knonunu目錄下 config.in文件,,在 comment' Character devices’語句下面加上

bool 'support for sja1000 driver'CONFIG_SJA1000_DRIVERS3C2410 開發(fā)板II(B)+3.5寸帶觸摸TFT液晶屏

這樣,,在編譯內(nèi)核,運行make menuconfig的時候,且在配置字符設(shè)備時就會有選項:

    support for sja1000 driver 當選中這個選項的時候,,設(shè)備驅(qū)動就加到內(nèi)核中了,。

    ④在romfs中加上設(shè)備驅(qū)動程序?qū)?yīng)的設(shè)備文件。設(shè)備文件都被包含在/dev目錄下,。uClinux中使用的根文件系統(tǒng)是romfs文件系統(tǒng),。這個文件系統(tǒng)是一個只讀文件系統(tǒng),所以設(shè)備文件必須在編譯內(nèi)核的時候加到romfs文件系統(tǒng)的image中,。

    不同的硬件系統(tǒng)對應(yīng)不同的設(shè)備文件,,在/uClinux-dist/vendors目錄下,分別定義了它們的Makefile文件,。在uClinux-dist/Vendors里S3C4510對應(yīng)的目錄下找到它的Makefile文件,,并找到區(qū)域DEVICES=\ tty,c,5,0 console,c,5,1 cua0,c,5,64 cual,c,5,65\,在后面再加上設(shè)備項 sja1000,c,254,1\就行了。

    ③重新編譯內(nèi)核,;在shell中將當前目錄cd到uClinux-dist目錄下,,然后:S3C2410 開發(fā)板II(B)+3.5寸帶觸摸TFT液晶屏

      #make menuconfig      #make dep      #make

    當驅(qū)動程序和uClinux內(nèi)核一起編譯鏈接并生成映像下載到目標板運行以后,可以通過查看/proc/devices,,如果已經(jīng)顯示有sja1000,,則表明設(shè)備加載成功。如果使用了中斷,,也可以查看/proc/interrupts,,該文件記錄了當時已經(jīng)完成的所有系統(tǒng)中斷情況。

4,、結(jié)束語

實時性方面,可以使用進程間通信如管道,、消息隊列、共享內(nèi)存等方法將CAN總線的接收中斷與應(yīng)用程序直接關(guān)聯(lián),,加快系統(tǒng)對CAN總線事件的響應(yīng)速度,。進一步可以通過RTLinux和RTAI(Real Time Application Interface)這兩種方案增強uClinux的實時性。

本文作者創(chuàng)新點:有效地解決了在沒有MMU的CPU之上開發(fā)一些簡單任務(wù)操作系統(tǒng)或控制程序效率低,、程序簡單的缺點,。通過使用嵌入式uClinux,它既保存了原先Linux操作系統(tǒng)穩(wěn)定性、功能強大等優(yōu)點,,又對內(nèi)核的代碼重新編寫,,減少了內(nèi)核容量,提高了效率,。同時也提出了在CAN總線設(shè)備下設(shè)計驅(qū)動程序的方法,。

5、參考文獻

[1] 白小明,,邱桃榮.基于Linux的嵌入式實時操作系統(tǒng)的研究 [J].微計算機信息,,2006,,5-22:78-79。http://www.51kaifa.com/shop/read.php?ID=4812

[2] 魏長江,,張凌云,,李國財.基于uClinux的設(shè)備驅(qū)動程序設(shè)計方法及應(yīng)用實例[J].煤礦機械,2005,,8

[3] 蔡莉,盧珞先.RS-485通信與CAN總線的接口設(shè)計[J].武漢理工大學學報(信息與管理工程版),, 2002,1

[4] 胡晨峰.JFFS2文件系統(tǒng)在uClinux中的應(yīng)用[J].電子產(chǎn)品世界,,2003,7  http://www.51kaifa.com/shop/read.php?ID=4812

此內(nèi)容為AET網(wǎng)站原創(chuàng),,未經(jīng)授權(quán)禁止轉(zhuǎn)載,。