久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

專注電子技術(shù)學(xué)習(xí)與研究
當(dāng)前位置:單片機教程網(wǎng) >> MCU設(shè)計實例 >> 瀏覽文章

字符驅(qū)動編寫小結(jié)(基于mini2440,LED驅(qū)動)

作者:佚名   來源:本站原創(chuàng)   點擊數(shù):  更新時間:2014年08月18日   【字體:

    編程:需要什么功能(機制)、如何使用這些功能(策略)

    作為驅(qū)動程序編寫者,我們需要在所需的編程時間以及驅(qū)動程序的靈活性之間選擇一個可接受的折中。讀者可能奇怪于說驅(qū)動程序“靈活”,我們用這個詞實際上是強調(diào)設(shè)備驅(qū)動程序的作用在于提供機制,而不是提供策略。
    機制mechanism,策略policy。如果你看過《linux device drivers》,里面給出了大概的介紹。機制提供了干什么(do what),策略提供如何做(how to do)。驅(qū)動程序完成機制的功能,把策略的實現(xiàn)留給用戶的應(yīng)用程序。通常在機制中,驅(qū)動程序要完成打開,關(guān)閉,讀寫,控制等功能。這些都是設(shè)備使用時最基本的操作。而策略中就要實現(xiàn)一些高級的數(shù)據(jù)處理或界面功能。通過例子來說明會更好些。

    linux中設(shè)備通常分為三類:字符設(shè)備、塊設(shè)備、網(wǎng)絡(luò)接口;

    編寫訪問硬件的內(nèi)核代碼時,不要給用戶強加任何特定策略,驅(qū)動程序應(yīng)該處理如何使硬件可用的問題,而將怎樣使用硬件的問題留給上層應(yīng)用程序;
    不帶策略的驅(qū)動程序包括一些典型的特性:同時支持同步和異步操作、驅(qū)動程序能夠被多次打開、充分利用硬件特性,以及不具備用來“簡化任務(wù)”的或提供與策略相關(guān)的軟件層等。
    不帶策略的軟件設(shè)計是軟件設(shè)計者的一個共同目標(biāo)。
 
    在沒有操作系統(tǒng)的裸機系統(tǒng)中,我們編寫驅(qū)動程序,直接對物理地址、各種寄存器等進行操作并為應(yīng)用層直接提供接口;如果在有操作系統(tǒng)的平臺上,我們又應(yīng)該如何編寫驅(qū)動程序?以前在裸機上實現(xiàn)的驅(qū)動程序又如何能移植到帶操作系統(tǒng)的平臺上呢?
    我們學(xué)習(xí)linux的驅(qū)動編程,可以接觸linux這么優(yōu)秀的開源內(nèi)核并了解他的驅(qū)動架構(gòu)等各種機制。我們可以從中學(xué)習(xí)借鑒,來提高我們的編程能力以及編寫程序時需要考慮的各種方面,如設(shè)備、驅(qū)動等各種函數(shù)的注冊機制(各種回調(diào))、軟硬資源分離、資源保護等等。
    在編寫linux的字符驅(qū)動程序前,我們需要知道linux下驅(qū)動程序的架構(gòu),驅(qū)動程序的實現(xiàn)機制。
    linux驅(qū)動模塊,是一種可動態(tài)或靜態(tài)加入內(nèi)核的內(nèi)核模塊,內(nèi)核模塊的編程與編譯需要遵循一定的規(guī)則。假設(shè)我們有一定的基礎(chǔ),已經(jīng)清楚內(nèi)核模塊的架構(gòu),那么我們簡要分析字符驅(qū)動的程序架構(gòu)。
     1:模塊加載函數(shù):mini2440_led_init、module_init(mini2440_led_init);
     2:模塊卸載函數(shù):mini2440_led_exit、module_exit(mini2440_led_exit);
     3:實現(xiàn)相關(guān)的操作(file_operations):mini2440_led_open、mini2440_led_write mini2440_led_read
     4:相關(guān)數(shù)據(jù)結(jié)構(gòu)與在內(nèi)核中使用的相關(guān)函數(shù):cdev、dev_t、file_operations、cdev_add、device_create
 
    對字符設(shè)備的訪問是通過文件系統(tǒng)內(nèi)的設(shè)備名稱(open(xxx文件))進行的,即將字符設(shè)備已文件的形式訪問與管理,這些文件是特殊文件、設(shè)備文件或稱為文件系統(tǒng)樹的節(jié)點,他們通常位于/dev下。ls -l 可以查看設(shè)備文件相關(guān)信息。 
    設(shè)備文件創(chuàng)建:
    字符設(shè)備的設(shè)備文件可以通過mknod xxx c 250(主) 0,手動創(chuàng)建,也可以在模塊加載函數(shù)中通過device_create自動創(chuàng)建;
    inode結(jié)構(gòu):
    在linux 內(nèi)核中,使用inode結(jié)構(gòu)表示文件,即每個文件創(chuàng)建后內(nèi)核中都會有一個inode結(jié)構(gòu)與之關(guān)聯(lián),
    file結(jié)構(gòu):
    在linux中每個打開的文件在內(nèi)核中都有一個file結(jié)構(gòu)與之關(guān)聯(lián)(所有文件都這樣);文件與file結(jié)構(gòu)可以使一對一或一對多的關(guān)系,而文件與inode是一對一的關(guān)系。內(nèi)核在open文件時創(chuàng)建對應(yīng)的file結(jié)構(gòu),并傳遞給在該文件上進行操作的所有函數(shù),直到最后的close函數(shù)。
    字符設(shè)備的注冊
    字符設(shè)備需要注冊到內(nèi)核中,才能被使用。內(nèi)核中,使用struct cdev結(jié)構(gòu)體表示一個字符設(shè)備;cdev結(jié)構(gòu)包含設(shè)備的設(shè)備號以及該設(shè)備的操作方法file_operations等。而在設(shè)備文件的inode結(jié)構(gòu)體中又包含指向cdev的指針。
/////////////////////////////////////////////////////////////////////////////////////////////////
    應(yīng)用程序需要使用某個字符設(shè)備,因為linux是通過文件的形式管理設(shè)備文件,所以在調(diào)用應(yīng)用程序前需要先創(chuàng)建設(shè)備文件,且應(yīng)用程序要使用驅(qū)動字符設(shè)備,就必須要用到驅(qū)動函數(shù),所以在應(yīng)用程序使用之前必須先注冊好該字符設(shè)備的驅(qū)動程序。
   字符設(shè)備的設(shè)備文件可以通過mknod手動創(chuàng)建,也可以在模塊加載函數(shù)使用device_create自動創(chuàng)建;自動創(chuàng)建利于模塊跨平臺的使用。設(shè)備文件被創(chuàng)建后,在內(nèi)核中會有一個inode結(jié)構(gòu)與之對應(yīng),inode結(jié)構(gòu)包含dev_t(設(shè)備號)以及file_operations結(jié)構(gòu)體等。字符驅(qū)動程序注冊時會將一個綁定了正確的設(shè)備號(該設(shè)備號要與被創(chuàng)建的設(shè)備文件對應(yīng))與各種操作的file_operations結(jié)構(gòu)體的cdev結(jié)構(gòu)(內(nèi)核中使用cdev表示設(shè)備)注冊到系統(tǒng)內(nèi)核中。
    當(dāng)應(yīng)用程序中通過open(“xxx”)系統(tǒng)調(diào)用打開設(shè)備文件(調(diào)用應(yīng)用程序之前)時,在內(nèi)核中同時會創(chuàng)建一個與之對應(yīng)的file結(jié)構(gòu)體,且通過設(shè)備號file結(jié)構(gòu)中的file_operations結(jié)構(gòu)體與內(nèi)核中cdev結(jié)構(gòu)中的file_operations結(jié)構(gòu)體對應(yīng)。這樣應(yīng)用層的各種系統(tǒng)調(diào)用就被映射到內(nèi)核中的cdev結(jié)構(gòu)中file_operations中的函數(shù)一一對應(yīng)起來,cdev結(jié)構(gòu)中file_operations各個函數(shù)在驅(qū)動編寫時實現(xiàn)。

    編寫思路: 字符驅(qū)動模塊安裝內(nèi)核模塊的程序架構(gòu)寫,即有加載卸載函數(shù)以及模塊聲明。加載函數(shù)中又有以下介個方面:
     1:向系統(tǒng)申請一個設(shè)備號(或自己通過全局變量指定一個可用的設(shè)備,但是仍然要通過向系統(tǒng)申請這個設(shè)備號)-----在向系統(tǒng)注冊cdev時需要使用設(shè)備號;
     2:定義一個file_operations實體----后面cdev需要將他綁定它
     3:申請cdev實例(可動態(tài)申請與靜態(tài)定義一塊),并初始化cdev(同時綁定file_operations)
     4:注冊cdev到內(nèi)核中去
     5:可以手動在/dev創(chuàng)建設(shè)備文件或者在模塊加載函數(shù)中自動生成
     6:加載模塊
     7:應(yīng)用程序通過系統(tǒng)調(diào)用打開設(shè)備文件,這時系統(tǒng)調(diào)用的打開讀寫等函數(shù) 將直接調(diào)用cdev中綁定的file_operation實例中的函數(shù)。
////////////////////////////////////////////////////////////////////////////////////////////////
#include<linux/init.h>
#include<linux/module.h>
#include<linux/fs.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/device.h>
#include <linux/cdev.h>
 
#define GLOBAL_LED_MAJOR  250
 
static unsigned int global_led_major = GLOBAL_LED_MAJOR;主設(shè)備號
static struct cdev *led_cdev = NULL;                    指向cdev結(jié)構(gòu)體
static struct class *led_class = NULL;                  為下面自動生成設(shè)備文件做準(zhǔn)備
 
static volatile unsigned long *gpfcon = NULL;
static volatile unsigned long *gpfdat = NULL; 
static volatile unsigned long *gpfup = NULL; 
 
 
static int mini2440_led_open(struct inode * inode,struct file * file)
{
printk("mini2440_open[kernel_space]\n");
*gpfcon &=~((0x3<<0) | (0x3<<8) |(0x3<<10) |(0x3<<12)|(0x3<<14));
*gpfcon |= (0x1<<0) | (0x1<<8) |(0x1<<10) |(0x1<<12)|(0x1<<14);
return 0;
}
 
static ssize_t mini2440_led_read(struct file * file,const char __user * in,size_t size,loff_t * off)
{
printk("mini2440_read[kernel_space]\n");
return 0;
}
 
static ssize_t mini2440_led_write(struct file * file,const char __user * in,size_t size,loff_t * off)
{
    int ret;
char ker_buf;
printk("mini2440_write[kernel_space]\n");
ret = copy_from_user(&ker_buf,in,size);
printk("ker_buf =%d\n",ker_buf);
if(ker_buf)
{
*gpfdat &=~((0x1<<4)|(0x1<<5)|(0x1<<6)|(0x1<<7));
       
*gpfdat |= (0x1<<0);
}
else
{
*gpfdat |=(0x1<<4)|(0x1<<5)|(0x1<<6)|(0x1<<7);
*gpfdat &= ~(0x1<<0);
}
return 0;
 
}
 
struct file_operations led_fops = {
.owner = THIS_MODULE,
.open  = mini2440_led_open,
.read  = mini2440_led_read,
.write = mini2440_led_write,
};
 
int mini2440_led_init(void)
{
int result;
int err;
    dev_t devno = MKDEV(global_led_major,0);通過全局變量global_led_major合成設(shè)備號
if (global_led_major) {
result = register_chrdev_region(devno,1,"led_driver");注冊使用人為指定的設(shè)備號,可以從cat /proc/devices 看到"led_driver"
printk("sd!");
} else {
result = alloc_chrdev_region(&devno,0,1,"led_driver");由系統(tǒng)分配指定設(shè)備號 存放在devno參數(shù)中
global_led_major = MAJOR(devno);提取主設(shè)備號
printk("zd!");
}
if (result < 0){
        return result;
}
led_cdev = cdev_alloc();動態(tài)分配得到一個cdev
cdev_init(led_cdev,&led_fops);初始化cdev 將得到的cdev與具體操作綁定在一起
led_cdev->owner = THIS_MODULE;
err = cdev_add(led_cdev,devno,1);將使用devno設(shè)備號的綁定了led_fops的cdev注冊到內(nèi)核中
led_class = class_create(THIS_MODULE,"led_class");先生存一個class類,再生成設(shè)備文件
device_create(led_class,NULL,MKDEV(global_led_major,0),NULL,"mini2440_led"); 在/dev下生存設(shè)備文件
物理地址空間映射到虛擬地址空間 這里將從物理地址0x56000010開始的12字節(jié)的物理空間映射到虛擬地址空間
        返回的是虛擬地址空間對應(yīng)的起始地址,以后對該片虛擬地址空間的數(shù)據(jù)操作將映射到物理地址空間
gpfcon = ioremap(0X56000010,12);
gpfdat = gpfcon + 1;
gpfup  = gpfcon + 2;
if (err) {
printk(KERN_NOTICE"Error %d adding led_cdev",err);
return -1;
} else {
printk("mini2440_led_init ok!\n");
return 0;
}
}
 
void mini2440_led_exit(void)
{
cdev_del(led_cdev);從內(nèi)核中注銷cdev結(jié)構(gòu)體
iounmap(gpfcon);注銷物理地址空間與虛擬地址空間的映射
kfree(led_cdev);釋放動態(tài)分配到的led_cdev
unregister_chrdev_region(MKDEV(global_led_major,0),1);注銷使用過的設(shè)備號
device_destroy(led_class, MKDEV(global_led_major,0));
class_destroy(led_class);
        printk("mini2440_led_exit!\n");
}
 
MODULE_AUTHOR("aaaa");
MODULE_LICENSE("GPL");
module_param(global_led_major,int,S_IRUGO);
module_init(mini2440_led_init);
module_exit(mini2440_led_exit);
 
//////////////////////////////////////////////
ifneq ($(KERNELRELEASE), )
obj-m  := mini2440_led.o
else 
KDIR := /home/tools/linux-2.6.32.2 
all:
make -C $(KDIR)  M=/linux_prg  modules
clean:
rm -f *.ko  *.o  *.mod.o  *.mod.c  *.symvers 
endif
////////////////////////////////////////////////
清除:make clean
編譯【自動尋找Makefile文件】:make
////////////////////////////////////////////////
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
 
int main(int argc,char **argv)
{
int fd;
char val;
 
fd =open("/dev/mini2440_led",O_RDWR);
if(fd<0)
{
printf("cannot open /dev/led!\n");
return 0;
}
printf("open /dev/mini2440_led[usr_space]!\n");
if(strcmp(argv[1],"on")==0)
{
val =1;
}
else
{
val =0;
}
write(fd,&val,1);
printf("finish!\n");
return 0;
}
////////////////////////////////////////////////
arm-linux-gcc mini2440_app.c -o mini2440_led_app
////////////////////////////////////////////////
點亮以及蜂鳴器響  ./mini2440_led_app on
不亮以及蜂鳴器不響 ./mini2440_led_app off
////////////////////////////////////////////////
模塊加載函數(shù)的流程:(卸載函數(shù)與之相反)
1:向系統(tǒng)申請設(shè)備號(或向系統(tǒng)注冊自己設(shè)定的設(shè)備號)
2:向系統(tǒng)申請一塊cdev結(jié)構(gòu)體
3:初始化cdev:cdev_init(led_cdev,&led_fops);綁定操作函數(shù)
關(guān)閉窗口

相關(guān)文章

主站蜘蛛池模板: 日韩不卡视频在线 | 亚洲欧美在线视频 | 天天躁日日躁aaaa视频 | 成人精品一区二区三区中文字幕 | 亚洲一av| 97色综合| 91精品国产综合久久久动漫日韩 | 日韩超碰 | 黑人久久久| 天天天天操 | 亚洲二区在线 | 国产高清免费视频 | 一级毛片免费完整视频 | 午夜免费网站 | 天天草天天操 | 精品欧美一区二区三区免费观看 | 亚洲一区二区三区视频免费观看 | 国产电影一区二区在线观看 | 久久久久久国产精品 | 国产成人一区二区三区 | 观看av | 久久久久久女 | 日韩欧美在线播放 | 夜夜摸天天操 | 蜜桃视频在线观看免费视频网站www | 91av视频在线播放 | 久久成人午夜 | 日韩中文字幕在线播放 | 免费看黄视频网站 | 日韩中文字幕一区二区 | 欧美色成人 | 欧美福利在线 | 欧美另类视频在线 | 日韩视频在线观看一区二区 | 99精品在线免费观看 | 欧美影院| 日韩av在线不卡 | 久久在线看 | 欧美一级在线 | 亚洲精品一区中文字幕乱码 | 性精品 |