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

專注電子技術學習與研究
當前位置:單片機教程網 >> MCU設計實例 >> 瀏覽文章

字符驅動編寫小結(基于mini2440,LED驅動)

作者:佚名   來源:本站原創   點擊數:  更新時間:2014年08月18日   【字體:

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

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

    linux中設備通常分為三類:字符設備、塊設備、網絡接口;

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

    編寫思路: 字符驅動模塊安裝內核模塊的程序架構寫,即有加載卸載函數以及模塊聲明。加載函數中又有以下介個方面:
     1:向系統申請一個設備號(或自己通過全局變量指定一個可用的設備,但是仍然要通過向系統申請這個設備號)-----在向系統注冊cdev時需要使用設備號;
     2:定義一個file_operations實體----后面cdev需要將他綁定它
     3:申請cdev實例(可動態申請與靜態定義一塊),并初始化cdev(同時綁定file_operations)
     4:注冊cdev到內核中去
     5:可以手動在/dev創建設備文件或者在模塊加載函數中自動生成
     6:加載模塊
     7:應用程序通過系統調用打開設備文件,這時系統調用的打開讀寫等函數 將直接調用cdev中綁定的file_operation實例中的函數。
////////////////////////////////////////////////////////////////////////////////////////////////
#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;主設備號
static struct cdev *led_cdev = NULL;                    指向cdev結構體
static struct class *led_class = NULL;                  為下面自動生成設備文件做準備
 
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合成設備號
if (global_led_major) {
result = register_chrdev_region(devno,1,"led_driver");注冊使用人為指定的設備號,可以從cat /proc/devices 看到"led_driver"
printk("sd!");
} else {
result = alloc_chrdev_region(&devno,0,1,"led_driver");由系統分配指定設備號 存放在devno參數中
global_led_major = MAJOR(devno);提取主設備號
printk("zd!");
}
if (result < 0){
        return result;
}
led_cdev = cdev_alloc();動態分配得到一個cdev
cdev_init(led_cdev,&led_fops);初始化cdev 將得到的cdev與具體操作綁定在一起
led_cdev->owner = THIS_MODULE;
err = cdev_add(led_cdev,devno,1);將使用devno設備號的綁定了led_fops的cdev注冊到內核中
led_class = class_create(THIS_MODULE,"led_class");先生存一個class類,再生成設備文件
device_create(led_class,NULL,MKDEV(global_led_major,0),NULL,"mini2440_led"); 在/dev下生存設備文件
物理地址空間映射到虛擬地址空間 這里將從物理地址0x56000010開始的12字節的物理空間映射到虛擬地址空間
        返回的是虛擬地址空間對應的起始地址,以后對該片虛擬地址空間的數據操作將映射到物理地址空間
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);從內核中注銷cdev結構體
iounmap(gpfcon);注銷物理地址空間與虛擬地址空間的映射
kfree(led_cdev);釋放動態分配到的led_cdev
unregister_chrdev_region(MKDEV(global_led_major,0),1);注銷使用過的設備號
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
////////////////////////////////////////////////
模塊加載函數的流程:(卸載函數與之相反)
1:向系統申請設備號(或向系統注冊自己設定的設備號)
2:向系統申請一塊cdev結構體
3:初始化cdev:cdev_init(led_cdev,&led_fops);綁定操作函數
關閉窗口

相關文章

主站蜘蛛池模板: 超碰在线免费公开 | 观看av| 欧美激情在线观看一区二区三区 | 成人h视频 | 欧美 日韩 国产 一区 | 亚洲一区在线日韩在线深爱 | 精品视频在线播放 | 亚洲一区电影 | 精品国产乱码久久久久久a丨 | 久草在线在线精品观看 | 久久亚洲一区二区 | 久草成人| 国产精品一区二区无线 | 精品国产一区二区三区在线观看 | 精品免费国产一区二区三区四区 | 一级毛片视频免费观看 | 成年人免费在线视频 | 日韩亚洲欧美一区 | av在线播放一区二区 | 欧美性吧 | 国产精品久久久久久婷婷天堂 | 久久麻豆精品 | 欧美精品久久久 | 在线成人 | 午夜免费影视 | 久久99久久久久 | 久久久精品视 | 久久久999精品 | 欧美 日韩精品 | 天天操夜夜艹 | 日韩a级片| 狠狠综合久久av一区二区小说 | 国产日韩欧美一区 | 久久久久亚洲 | 在线免费观看a级片 | 久久综合一区 | 九九热在线视频 | 日韩精品不卡 | 涩爱av一区二区三区 | 亚洲综合大片69999 | 亚洲一区二区三区免费视频 |