uClinux下的DS1284設備驅動程序開發

admin @ 2014-03-25 , reply:0

引言
    ARM 器件是近年來興起的一種低功耗、高功效的嵌入式處理器。S3C4510B 是一款針對網路處理而推出的專用處理器。在利用S3C4510B 處理器構成的嵌入式系統中,為了保證系統的可靠穩定,多數都需要看門狗;同時,在某些應用領域還需要時鐘和日曆服務。Dallas公司的DS1284 集成了上面兩個功能。
    在嵌入式系統應用越來越廣泛的同時,嵌入式應用也變得越來越複雜。許多嵌入式系統都不得不藉助於專用的操作系統來支撐自己的應用。uClinux 作為類Unix 操作系統,繼承了Linux 的各種優秀品質,成為首選的嵌入式操作系統。本文以uClinux 為背景,以S3C4510B 為目標處理器,介紹uClinux 下DS1284 設備驅動的開發。

1 硬體設計
1.1 硬體描述
    DS1284 是美國Dallas 公司推出的一款集成日曆和看門狗功能的晶元。該晶元內部集成了實時時鐘、靜態RAM(內部寄存器)等, 支持電池供電可以保證主機掉電後繼續工作。DS1284 包含了64 個8 位寬的寄存器,其中50 個可供用戶存儲一些需要掉電保護的數據,所有這些寄存器都可以通過外部匯流排直接訪問。通過訪問寄存器可以得到時鐘、日曆等信息,還可以設置定時報警和看門狗定時功能。所有這些寄存器的數據都以BCD 碼方式保存和讀取。如果用戶需要,DS1284還可以輸出1024H z 的方波信號。在DS1284 中,前14 個寄存器為功能寄存器。各個寄存器保存內容的意義如表1 所列。
 
命令寄存器的內部定義如表2 所列。
 
1.2 硬體連接
    主機處理器採用的是三星公司出品的網路型ARM處理器S3C4510B。S3C4510B 處理器同DS1284 的連接電路如圖1 所示。
 
2 軟體設計
    操作系統的作用之一就是向用戶隱藏硬體設備的特殊性, 使應用程序的開發與低層物理設備無關。設備驅動程序就是連接應用程序和具體物理設備的橋樑。在uClinux 中設備可以分成三種:字元型設備、塊設備、網路設備。DS1284 可以認為是一種字元型設備。uClinux 中所有的設備都被看成是一個文件。因此了解uClinux 下的設備驅動程序, 首先要了解內核中與設備相關的一些數據結構。

2.1 內核中設備數據結構
    在應用程序里,訪問設備文件的介面是標準的和統一的。一般而言,open、release、read、write、ioctl等都是對設備文件常用的操作。不同的設備有不同的實現方式。uClinux 中,通過一個結構體記錄了每一種設備具體操作函數的函數指針, 以便在具體調用中轉入到實際操
作的函數中。File_operations 結構體負責記錄這些信息,其結構如下:
structfile_operations {
struct module *owner;
loff_t(*llseek)(structfile*, loff_t, int);
ssize_t(*read)(structfile*, char*, size_t, loff_t*);
ssize_t(*write)(structfile*, constchar*, size_t, loff_t*);
int(*readdir)(structfile*, void*, filldir_t);
unsignedint(*poll)(structfile*, structpoll_table_struct*);
int(*ioctl)(structinode*, structfile*, unsignedint, unsigned
long);
int(*mmap)(structfile*, structvm_area_struct*);
int(*open)(structinode*, structfile*);
int(*flush)(structfile*);
int(*release)(structinode*, structfile*);
int(*fsync)(structfile*, structdentry*, intdatasync);
int(*fasync)(int, structfile*, int);
int(*lock)(structfile*, int, structfile_lock*);
ssize_t(*readv)(structfile*, conststructiovec*, unsignedlong,
loff_t*);
ssize_t (*writev) (struct file *, const struct iovec *, unsigned
long, loff_t*);
ssize_t(*sendpage)(structfile*, structpage*, int, size_t, loff_t
*, int);
unsignedlong(*get_unmapped_area)(structfile*, unsignedlong,
unsignedlong, unsignedlong, unsignedlong);
};
在這個結構體中, 定義了各類操作函數的類型和入口參數, 常用的操作函數意義如下。
① llseek,移動文件指針的位置。顯然只能用於可以隨機存取的設備。
② read,進行讀操作。參數buf 為存放讀取結果的緩衝區,count 為所要讀取的數據長度。返回值為負表示讀取操作發生錯誤, 否則返回實際讀取的位元組數。
③ write,進行寫操作,與read 類似。
④ readdir,取得下一個目錄入口點,只有與文件系統相關的設備驅動程序才可使用。
⑤ select,進行選擇操作。如果驅動程序沒有提供select 入口,select 操作將會認為設備已經準備好進行任何的I/O 操作。
⑥ ioctl,進行讀、寫以外的其它操作,參數cmd 為自定義的命令。
⑦ mmap , 用於把設備的內容映射到地址空間,一般只有塊設備驅動程序使用。
⑧ open,打開設備準備進行I/O 操作。返回0 表示打開成功, 返回負數表示失敗。如果驅動程序沒有提供open 入口,則只要/dev/driver 文件存在就認為打開成功。
⑨ release,即close操作。
這裡要說明的是, 並不是所有的介面函數都需要在驅動程序中實現。對於設備不支持的操作,可以將對應的設備介面函數指針置為空。在編寫設備驅動程序中,只需要聲明一個結構體,然後用實際處理函數的指針初始化該結構體就可以了。對DS1284 的結構體聲明可聲明如下(未說明部分自動置空)。
structfile_operationsds1284_fops={
open: ds1284_open,
release: ds1284_release,
read: ds1284_read,
write: ds1284_write,
llseek: ds1284_llseek,
};

2.2 設備的註冊
在uClinux 系統里,通過調用下面這個函數向系統註冊字元型設備。
int register_chrdev(unsigned int major, const char *name,struct
file_operations*fops)
其中,major 是為設備驅動程序向系統申請的主設備號。如果為0 , 則系統為此驅動程序動態地分配一個主設備號,name 是設備名,fops 就是前面所說的對各個調用的入口點的說明。此函數返回0 , 表示成功;返回-EINVAL,表示申請的主設備號非法。主設備號大於系統所允許的最大設備號或所申請的主設備號正在被其它設備驅動程序使用時, 將返回- EBUSY 。如果是動態分配主設備號成功, 此函數將返回所分配的主設備號。如果register_chrdev 操作成功,設備名就會出現在/proc/devices 文件里。初始化部分一般還負責給設備驅動程序申請系統資源,包括內存、中斷、時鐘、I/O 埠等,這些資源也可以在open 子程序或別的地方申請。在這些資源不用的時候, 應該釋放它們,以利於資源的共享。
DS1284 的設備註冊函數設計如下。
intds1284_init(void){
intresult;
result=register_chrdev(WDT_MAJOR,"ds1284",&ds1284_fops);
returnresult;
}
2.3 I/O 埠的申請
    任何進程都可以訪問任何一個I / O 埠。此時系統無法保證對I / O 埠的操作不會發生衝突, 甚至會因此而使系統崩潰。因此,在使用I / O 埠前, 也應該檢查此I / O 埠是否已有別的程序在使用, 若沒有, 再把此埠標記為正在使用, 在使用完以後釋放它。編寫驅動程序時需要用到如下幾個函數:
int check_region(unsignedintfrom, unsignedintextent);
void request_region(unsignedintfrom, unsignedintextent,constchar *name);
void release_region(unsignedintfrom, unsignedintextent);
調用這些函數時的參數為:from 表示所申請I/O 埠的起始地址;extent 為所要申請的從from 開始的埠數;name 為設備名,將會出現在/proc/ioports 文件里。
check_region 返回0 表示I/O 埠空閑,否則為正在被使用。在申請了I/O 埠之後, 就可以用如下幾個函數來訪問I/O 埠:
inline unsigned char inb(unsignedshortport);
inline unsigned char inb_p(unsignedshortport);
inline void outb(charvalue, unsignedshortport);
inline void outb_p(charvalue, unsignedshortport);
其中inb_p 和outb_p 插入了一定的延時,以適應某些慢的I/O 埠。
在用戶程序調用read 、write 時,因為進程的運行狀態由用戶態變為核心態, 地址空間也變為核心地址空間。而read、write 中的參數buf 是指向用戶程序的私有地址空間, 所以不能直接訪問, 必須通過以下兩個系統函數來訪問用戶程序的私有地址空間。
void copy_from_user(void * to,const void * from,unsigned long n);
void copy_to_user(void * to,const void * from,unsigned long n);
copy_from_user 由用戶程序地址空間往核心地址空間複製,copy_to_user 則反之。參數to 為複製的目的指針,from 為源指針,n 為要複製的位元組數。

2.4 定時機制的實現
    為防止看門狗電路意外複位, 系統需要在固定的時間間隔內重置看門狗計數器的值,因此在DS1284 系統中就需要用到定時器。在uClinux 系統中,時鐘是由系統接管的。如果設備驅動程序中需要使用時鐘的話,就需要向系統申請定時器資源。定時器部分的系統調用主要由以下三個函數完成:
void add_timer(structtimer_list* timer);
int del_timer(structtimer_list* timer);
inline void init_timer(structtimer_list* timer);
其中timer_list 結構體的內容如下所示:
structtimer_list {
structtimer_list*next;
structtimer_list*prev;
unsignedlongexpires;
unsignedlongdata;
void(*function)(unsignedlongd);
};
    其中expires 是要執行function 的時間。系統核心有一個全局變數JIFFIES,表示當前時間。一般在調用add_timer 時,jiffies=JIFFIES+num,表示在num個系統最小時間間隔后執行function 。系統最小時間間隔與所用的硬體平台有關,在核心裡定義了常數HZ ,表示1s 內最小時間間隔的數目,則num * HZ 表示num 秒。系統計時到預定時間就調用function ,並把此子程序從定時隊列里刪除,因此如果想要每隔一定時間間隔執行一次的話,就必須在function 里再一次調用add_timer。function的參數d 即為timer 裡面的data 項。DS1284 的定時函數實現如下:
static struct timer_listds1284_timer;
/*在ds1284_open函數中申請定時器資源*/
init_timer(&ds1284_timer);
ds1284_timer.expires=jiffies+WDT_NUM*HZ/2;
ds1284_timer.function=ds1284_timeout;
add_timer(&ds1284_timer);
void ds1284_timeout(unsigned long d){
unsignedcharvalue1,value2;
del_timer(&ds1284_timer);
ds1284_timer.expires=jiffies+WDT_NUM*HZ/2;
value1=inb(WDT_ALARM_L); //讀取看門狗計時器的值,
//則自動更新計時器
value2=inb(WDT_ALARM_H);
add_timer(&ds1284_timer);
}

結語
    嵌入式系統的應用以穩定為首要目標,uClinux 在擴展系統性能的同時保持了系統的穩定性。DS1284 的加入不但從硬體上保證了系統的可自恢復性, 而且擴展了系統的使用背景,可以作為一些無人看管設備且有時間記錄要求的嵌入式系統平台。




[admin via 研發互助社區 ] uClinux下的DS1284設備驅動程序開發已經有1249次圍觀

http://cocdig.com/docs/show-post-42331.html