最近的幾個月裡,都在從事軟硬體間的程式設計,雖然我懂得不多,但是經過一段時間的摸索之後也找到了一些解決問題的方法。雖然從前的我,只會寫軟體程式,也從來沒有想到我會接觸到這個領域的東西,但是經過這幾個月的努力終於有了一些小小的收穫。
 
我所使用的實驗平台是S3C2440A開發板,外觀如下圖:
 

 
這篇文章主要針對如何在Linux環境下驅動LED燈號做一個簡單的說明,內容包含如何撰寫一個Linux的module、如何使用module、如何測試module。在嵌入式系統中最常碰到的事情就是要控制周邊的裝置,例如燈號、蜂鳴器等等,這些裝置可以透過GPIO來做控制,GPIO到底是什麼東西呢?你可以把GPIO當作是一堆可以控制周邊裝置的暫存器,每個暫存器都會有他對應的實體位置,對這些暫存器設定一些數值就可以控制周邊的裝置。我們先從最簡單的LED控制開始研究,其他的裝置都是大同小異,我也會在後面的文章中陸陸續續的說明這些裝置如何來實作。我們假設已經可以正常的從S3C2440實驗版上透過boot loader正常開機,並且載入Linux作業系統。如果我們能在Linux作業系統下控制LED燈號的亮或暗,就完成本文章的目的。
 
目前開發板上所使用的作業系統為Linux 2.4.18,所以該模組是針對2.4.18所撰寫的,所以必須在編輯環境(我們所使用的開發環境是Dev-C++,請參考之前的文章)中include到2.4.18的kernel,設定的方法:「專案(p)」->「專案選項」,會彈出如下圖的視窗,請設定「檔案/目錄」->「引入標頭檔目錄」,加入Linux 2.4.18中的include的目錄。
 

 
接下來我們必須先了解一下燈號的接線,了解燈號到底使用到哪些GPIO,你可以去翻閱你手邊所擁有的線路圖,應該能找到一份有關Led的訊號接線方式,如下圖所示:
 

 
從圖中你可以了解到控制第一個Led是由EINT4/GPF4來做控制的,有些線路圖可能不會直些寫出是哪個GPIO,你可能要去找找這個符號對應到2440A處理器的哪一個接腳以得知是由哪個GPIO控制。這張圖顯示第一個燈號是由GPF4所控制,當你知道是由GPF4所控制你可以從S3C2440A的規格書中得知對應的Address,如下圖所示:
 

 
當你已經得知這些基本的資料後可以開始撰寫Linux的驅動程式。
 
 
 
如何撰寫一個Linux的module
在這裡我們打算以module的方式將led driver放進Linux中,這種方式能讓你日後容易維護,有需要修改時只要將該module修改好之後就可以使用,不必重新編譯整個Linux Kernel。
一個module必須包含的三個東西:
 
1.  提供哪些控制的方法:
struct file_operations QuickMarkLed_ctl_fops= { 方法: 函數名稱, 方法: 函數名稱...};
方法:如ioctl
函數名稱:如QuickMarkLed_ctl_ioctl,該函數後面要自己實作,撰寫其動作方法
 
2.  初始化函數:
int init_module(void)
{
    //想要初始化的資料,我們會在這裡註冊一個裝置
    //register_chrdev(裝置編號, 裝置名稱, 提供哪些控制的方法);
    //裝置編號:0~255  隨便選一個號碼,建議選擇127以後的號碼,
    //          不要與系統其他裝置的號碼衝到
    //裝置名稱:一個字串,自己隨便取,讓使用者知道這是什麼裝置
    //提供哪些控制的方法:是一個結構指標,裡面紀錄可以對該裝置做哪些動作,如read, write, ioctl...
}
 
3.  模組關閉函數:
void cleanup_module(void)
{
    //反註冊該裝置
    //其它記憶體的使用歸還
}

所以一個模組是由這三部份組成,只要掌握好這三部份就快要成功了。再來我們要做的事就是存取裝置暫存器,我們存規格書中知道GPFCON的位置是0x56000050,GPFDATA的位置是0x56000054,GPFUP的位置是0x56000058,因此我們在程式碼定義了這些位置,我們以base=0x56000000加上0x50就是GPFCON,宣告如下:

#define GPIO_CTL_BASE  0x56000000
#define bGPIO(p)  __REG(GPIO_CTL_BASE + (p))
#define rGPFCON                  bGPIO(0x50)
#define rGPFDAT                  bGPIO(0x54)
#define rGPFUP                     bGPIO(0x58)
 
針對這些位置最控制,該bit是0就會亮燈,如果是1就會暗燈,如此控制燈號閃爍。void QuickMarkLed_ctl_ioctl(struct inode*inode,struct file *flip,U32 ledNumber, U32 ledStatus)函數提供外界控制燈號的方法,ledNumber表示要控制哪個燈號,目前開發板上有四個led燈號,ledStatus表示要使該燈號亮或不亮,可參考後面範例以了解其控制的方法。

程式碼下載:讓使用者能夠控制實驗版上的LED燈號,編譯完成之後會產生main.o的檔案
 
 
 
如何使用module
將main.o改名為QuickMarkLed.o download到實驗版上
在Linux下執行:
insmod QuickMarkLed.o
mknod /dev/QuickMarkLed c 221 0
 
 
 
如何測試module
測試程式如下:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <linux/kd.h>
#include <signal.h>
#include <errno.h>
 
int main(int argc, char *argv[])
{
    int testdev;
   
    //led test... 
    testdev = open("/dev/QuickMarkLed",O_RDWR);   
    ioctl(testdev, 2, 1); //ioctl(device, led number, open/close)  open=1
    ioctl(testdev, argv[1][0]-'0', argv[2][0]-'0'); //ioctl(device, led number, open/close)  open=1
    close(testdev);   
       
    return 0;
}
 
編譯完該程式,如果成功出始化會先將第二個燈號設成亮,之後透過使用者輸入了兩個參數設定Led的明暗,假設編譯完成產生執行檔的檔名叫leds
leds 1 1  ->表示第一個led亮
leds 1 0 -> 表示第一個led暗
leds 2 1 -> 表示第二個led亮.....以此類推
 
 

    全站熱搜

    CJY0503 發表在 痞客邦 留言(4) 人氣()