ARM9外接晶片读写的验证-以8255为范例.docx

上传人:牧羊曲112 文档编号:1662077 上传时间:2022-12-13 格式:DOCX 页数:16 大小:424.20KB
返回 下载 相关 举报
ARM9外接晶片读写的验证-以8255为范例.docx_第1页
第1页 / 共16页
ARM9外接晶片读写的验证-以8255为范例.docx_第2页
第2页 / 共16页
ARM9外接晶片读写的验证-以8255为范例.docx_第3页
第3页 / 共16页
ARM9外接晶片读写的验证-以8255为范例.docx_第4页
第4页 / 共16页
ARM9外接晶片读写的验证-以8255为范例.docx_第5页
第5页 / 共16页
点击查看更多>>
资源描述

《ARM9外接晶片读写的验证-以8255为范例.docx》由会员分享,可在线阅读,更多相关《ARM9外接晶片读写的验证-以8255为范例.docx(16页珍藏版)》请在三一办公上搜索。

1、ARM9外接晶片讀寫的驗証-以8255為範例外接8255主要目的是透過位址線及資料線做外接晶片讀寫的驗証。ARM9可透過如圖1所示的轉接訊號線擴充其他的晶片功能。圖1. ARM-9的轉接訊號線解碼位址圖2代表ARM-9的外接記憶體解碼位址分佈,共分8個區段(bank)。這些區段分別由nGCS0-nGCS7來解碼,每個區段(bank)共有128 MB的記憶體空間。 Bank0及Bank1分別由快閃記憶體(Flash memory)及隨機讀取記憶體(SDRAM)所佔用。Bank2的解碼致能線nCGS2位於透過ARM-9發展板轉接槽的第34個接腳,如圖1所示。nCGS2解碼位址分佈於0x100000

2、00-0x18000000,如圖2所示。圖2. nGCS2的解碼位址分佈圖3中ARM9的記憶體區段(Bank)的解碼線nGCS2接8255的/CS;(A1, A0)分別接8255的(A1, A0);DATA7-DATA0分別接8255的D7-D0,如圖3所示。所以8255的(PORTA, PORTB, PORTC, CTL)的寫入位址定義如下:PORTA=0x10000000;PORTB=0x10000001;PORTB=0x10000002;CTL=0x10000003;圖3. 8255與ARM-9的連接方法ARM-9的資料線共有32位元(DATA31-DATA0)。然而外接記憶體(或I/O

3、)可規劃為8位元、16位元及32位元共三種模式。究竟選擇那種模式,可透過規劃BWSCON暫存器的內容來決定。BWSCON暫存器的位址為0x48000000。規劃Bank2的外接記憶體(或I/O) 資料線讀寫位元數為8位元,可規劃BWSCON暫存器的(bit9, bit8)為00,如圖4所示。程式規劃的參考方法如下圖4. BWSCON暫存器的bit10-bit0所規劃的功能ARM-9外接記憶體(或I/O)的讀寫時序如圖6所示。圖6中讀寫的各項時間參數(Tacs, Tcos, Tacc, Tacp, Tcoh, Tcah)可由BANKCONx暫存器(x=0,1.5)來調整。BANKCON2暫存器的

4、位址為0x4800000c,如圖5所示。圖5. BANKCON2暫存器的位址圖6. ARM-9外接記憶體(或I/O)的讀寫時序8255驅動程式之建立在Linux核心中,驅動程式扮演特殊的黑盒子角色,它們能駕馭硬體裝置,並且隱藏硬體的實際作業細節,使硬體的回應方式符合一組嚴謹規定的程式介面,讓應用程式能透過一套標準化的系統呼叫來間接操作硬體。以實際的硬體動作來實現系統呼叫所定義的功能,正是驅動程式所扮演的角色。由於系統呼叫這層軟體介面的存在,使得驅動程式與核心的其餘部分可以分開製作,並且在需要時,才在執行期將驅動程式安插到核心裡。這種模組化的設計,不僅降低了Linux驅動程式的設計難度,也使得核

5、心不必為了支援新硬體而頻頻改版。裝置驅動程式大致分為區塊驅動程式、字元驅動程式及網路介面驅動程式。區塊驅動程式是以固定大小長度來傳送轉移資料,且大致是可以隨機存取資料的設備,如硬碟機或光碟機;字元裝置是可被當成一連串位元組(串流)來存取的裝置,也就是說這類裝置的存取方式就像檔案一樣。字元裝置驅動程式的主要任務,正是實現這種行為。這類驅動程式至少會實作四項系統呼叫,分別是open()、close()、read()和write()。我們所熟悉的操控台與序列埠(/dev/console、/dev/ttyS0),就是典型的字元裝置,因為它們都可順利化身成串流。字元裝置必須透過檔案系統節點(filesy

6、stem node)來存取;檔案系統節點俗稱為裝置檔(device file),例如/dev/tttyS0、/dev/console。字元裝置與一般正常檔案之間的最大差異,是一般檔案容許你任意改變存取點(檔案指標),但字元裝置大部分都只是資料通道,只能被循序存取。1 模組程式的基本觀念一個驅動程式必須被撰寫成模組的型態,才能夠具備供使用者載入與移除的特性。因此一個驅動程式必然會具有載入模組函式與移除模組這兩個函式。故此兩個函式就分別載入驅動程式與移除驅動程式的角色。應用程式在啟動之後,從頭到尾都只執行同一件任務。另一方面,模組在被載入核心之後,必須先向核心註冊它自己,以便服務未來的reques

7、t,而其”main”函式這時候就立即結束了。換言之,init_module( ) 函式 (模組的入口) 的任務,是將模組的功能 (函式) 準備好,使這些功能可在事後被調用。模組的第二個入口點是 cleanup_module ( ),它在模組被卸載之前會被叫用一次,其作用就是告訴核心:我要離開了,別再叫我做任何事。圖7描繪模組如何利用函式呼叫與函式指標,將新功能加到運行中的核心。應用程式則是在使用者空間(user space) 內運作。每當應用程式發出系統呼叫,或是發生硬體中斷時,LINUX便會將執行模式從user-space切換到kernel-space。驅動程式碼必須在kernel-spac

8、e內運作。圖7 將模組連結至核心 2主標號(Major number)與次標號(Minor number) 當使用者要存取字元裝置時,必須透過檔案系統裡的代表名稱,這些特殊檔是集中在 /dev/目錄之下。使用指令ls-l /dev得到如圖8的結果。可看出在日期欄之前可以發現兩個以逗號隔開的數值,在逗號左方的數值是該裝置檔的主編號(major number),而逗號右方的數值是次編號(minor number)。主編號代表裝置所配合驅動程式。當核心收到open( )系統呼叫時,就是依據主編號來選擇驅動程式。另一方面,次編號的意義只有驅動程式自己才知道,核心會將使用者給定的次編號轉交給驅動程式,核

9、心本身用不到次編號。一般而言,驅動程式以次編號來辨識同類裝置的個體。圖8主編號與次標號要增加一個新驅動程式到系統,等於指派一個主編號給它。這項指派應該在驅動程式(模組)的初使期,藉由呼叫linux/fs.h 所定義的register_chrdev( )函式來完成:int register_chrdev(unsigned int major, const char *name, struct file_operation *fops);此函數的傳回值代表註冊動作的成功與否。負值表示失敗,0或正值表示成功。major引數是驅動程式想要註冊的主標號;name是裝置的名稱(如果註冊成功,這名稱將會出現

10、在 /proc/devices檔案的內容裡);fops是一個指標,指向一個函式指標陣列(arrary of function pointers),此陣列紀錄了驅動程式各個入口點(作業方法)的位置。在驅動程式向核心完成了註冊之後,驅動程式的各項作業方法(operation)就與給定的主編號產生了關連性。每當有人對字元裝置檔進行某項操作時,核心就能從該裝置檔的主編號找出對應的驅動程式,並透過其file_operations結構來呼叫對應的作業方法。下一個問題是如何給應用程式一個名稱,讓它們能利用此名稱來啟動驅動程式。這個名稱必須安插在 /dev/ 目錄下,而且必須關連到驅動程式的主編號與次編號。在

11、檔案系統製作節點的命令是mknod,你必須有特權身分(root)才能使用此工具。mknod至少需要四個參數,例如:mknod /dev/c8255_driver c 42 0第一個引數是節點名稱,其後的三個引數分別是裝置類型(c代表char device)、主編號、以及次編號。就像任何儲存在磁碟上的普通檔案一樣,mknod所產生的裝置節點會被保存下來,除非刻意用rm命令刪除它們。我們在驅動程式主程式中做以下定義及宣告:#defineCHDRV_MAJOR42char CHDRV_NAME = c8255_driver;而我們系統實作時的驅動程式註冊的動作是在一個init_module程式中實現

12、:int init_module(void)if(register_chrdev(CHDRV_MAJOR, CHDRV_NAME, &c8255_chdrv_fops) printk(kernel: %s: Unable to register character devicen, CHDRV_NAME);return -EIO;.在模組被卸載之前,它必須先釋放主標號,而這個動作可由unregister_chrdev( ) 完成,我們在模組的清理函式裡呼叫它:void cleanup_module(void)unregister_chrdev(CHDRV_MAJOR, CHDRV_NAME);

13、printk(unloaded.n);return;CHDRV_MAJOR引數是要被釋放的主編號,CHDRV_NAME是當初註冊的裝置名稱。這兩個引數必須與當初呼叫register_chrdev( ) 所用的引數一致,否則 unregister_chrdev( ) 將傳回EINVAL。2 檔案作業與file 結構驅動程式內部以一個file結構來代表一個已開啟的裝置,核心透過一個file_operations 結構來存取驅動程式內部的作業函式(method)。這結構定義在linux/fs.h。file_operations 結構包括指向驅動程式的各種系統呼叫,例如read, write, ope

14、n, release.。操作檔案結構的各種系統呼叫,其實際動作就是靠驅動程式內部的作業系統來達成的。file_operations 結構宣告範例如下:static struct file_operations c8255_chdrv_fops = NULL, /*lseek*/read: c8255_chdev_read, /* read */write: c8255_chdev_write, /* write */ NULL, /* readdir */ NULL, /* POLL */ NULL, /* ioctl */ NULL, /* mmap */ open: c8255_chdev

15、_open, /* open */ NULL, /* FLUSH */release: c8255_chdev_release, /* release */ NULL, /* fsync */ NULL, /* fasync */ NULL, /* lock */ NULL, /* readv */ NULL, /* writev */ NULL, /* send page */ NULL, /* get unmapped area */ ;習慣上,我們以變數名稱(例如c8255_chdrv_fops)來表示file_operations結構。在file_operations結構中每一個欄位,

16、都必須指向驅動程式中負責特定作業方法,對於驅動程式不需要的作業項目,其對應欄位需指向NULL。我們在此文中所使用到的作業方法包括有read( )、write( )、open( )及release( ),在此做一個簡單的介紹。1. open( ) 範例如下:int c8255_chdev_open( struct inode *inode, struct file *file )printk(c8255_openn);MOD_INC_USE_COUNT;return 0; 這是裝置操作的第一步,一般會在此函數中進行必要的初始化準備工作及遞增目標裝置的用量計次,以避免模組在檔案關閉之前被卸載。2.

17、 release( ) 範例如下:int c8255_chdev_release( struct inode *inode, struct file *file )MOD_DEC_USE_COUNT;printk(releasen); return 0;release 作業方法的用途與open相反。主要包括遞減用量次數、再最後一次關閉時,將目標裝置關機、釋放open配置給filp-private_data的任何東西。3. read( ) 範例如下:static ssize_t c8255_chdev_read(struct file *filp, char *buf, size_t count

18、,loff_t *offset)unsigned int ix;buffer2 = kmalloc(1,GFP_KERNEL);if(verify_area(VERIFY_WRITE,buf,count)=-EFAULT)return -EFAULT; printk(c8255_readn);/* -read data from 8255 PORT A- */io_base=ioremap_nocache(PORTA, LENGTH); *(buffer2)=readb(io_base); copy_to_user(buf,buffer,ix);kfree(buffer2);return ix

19、; 驅動程式用此指令擷取出裝置上的資料,並將擷取到的資料透過copy_to_user指令傳遞給應用程式。如果copy_to_user執行成功則傳回一個非負數值,代表成功讀取的位元組個數。各個引數的意義如下: filp 是檔案指標;count 是要被傳輸的資料量;buf引數指向user-space 的緩衝區對於read,它是一塊空白記憶區,用於存放裝置讀出的資料。4. write( ) 範例如下:static ssize_t c8255_chdev_write(struct file *filp,const char *buf,size_t count, loff_t *offset) unsi

20、gned int iy=0; unsigned char datax; buffer1 = kmalloc(1,GFP_KERNEL); printk(c8255_write.n); io_base=ioremap_nocache(PORTA,LENGTH); copy_from_user(buffer1,buf,count); datax=*(buffer1);printk(c8255_write.%dn,iy); writeb(datax, io_base+1); kfree(buffer1);return iy;將資料寫入裝置若發生錯誤,則觸發 write( ) 系統呼叫的行程會收到-E

21、INVAL。如果成功,write將傳回一個非負值,代表成功寫出的位元數。我們在實作write函式裡利用copy_from_user函式把使用者空間把之前所錄的資料傳送至核心空間。3 在應用程式開啟驅動程式的功能完成了驅動程式的撰寫之後,接下來要寫一個應用程式來作測試。程式裡首先要使用系統呼叫對裝置做開啟的動作,藉由 open 系統呼叫來呼叫驅動程式裡的 open 函式,便會執行驅動程式裡相對應的動作:int fd1,i;unsigned char databuf10;fd1 = open(C8255, O_RDWR);if (fd1=-1) printf(c8255 driver open e

22、rrorn);exit(0);使用 read 系統呼叫將10字元從核心空間傳送至使用者空間: read(fd1,databuf,10); for (i=0;i10;i+) printf(8255 port A data=%xn,databufi); 資料傳送到使用者空間後,接下來我們可以做需要的應用,例如傳送至網路上或做聲音的處理.等。使用 write 系統呼叫來將將1字元傳送至核心空間: write(fd1,databuf,1);執行應用程式之後便可驗證驅動程式的功能。完整應用程式範例如下:/*/* 使用者程式 */*/#include #include #include #include

23、#include #include #include #define C8255 /dev/c8255_driverint main()int fd1,i;unsigned char databuf;fd1 = open(C8255, O_RDWR);if (fd1=-1) printf(c8255 driver open errorn);exit(0);read(fd1,&databuf,10); for (i=0;i10;i+) printf(8255 port A data=%xn,databufi); write(fd1,&databuf,1);close(fd1);return 0;

24、4 實作步驟一、建立應用程式在PC的環境下開啟一個終端機視窗,設定好交叉編譯器的路徑。在應用程式usr8255.c所在的資料夾執行#arm-linux-gcc -o usr8255 usr8255.c產生應用程式執行檔usr8255。在PC的環境下,在驅動程式c8255_driver所在的資料夾執行make指令,欲重新產生新的應用程式執行檔以前,必須先做make clean動作,以便清除舊有的應用程式功能。如果一切順利,將產生可執行的驅動程式。執行minicom,將應用程式usr8255及驅動程式c8255_driver.o下載至ARM的tmp資料夾。接著掛載模組程式:#insmod c8255_driver.o再來產生字元裝置節點:#mknod /dev/c8255_driver c 42 0改變使用者程式權限,並執行:#chmod a+x usr8255#./usr8255用lsmod指令查看驅動程式是否掛載成功。若掛載成功,可在tmp資料夾下執行應用程式usr8255以便驗證驅動程式c8255_driver.o的功能。用rmmod指令卸載驅動程式。16

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号