天天看點

嵌入式linux led驅動有幾種寫法,嵌入式Linux字元驅動LED燈設計

一.任務要求

完成一個字元IO口驅動,在開發闆上該IO口對應LED燈。該驅動程式通過控制IO口的高低電平來控制亮滅。同時要寫一個應用層的測試程式,用來測試驅動程式。我的測試程式為myled_test.c,要求在shell下能夠通過該測試程式來控制LED燈的亮滅。如:

./myled_test

on 表示燈全亮;

./myled_test

off 表示燈全滅;

二.流程圖設計

圖1.應用層通路裝置的流程圖

三. 字元IO口驅動程式的設計流程

1)Linux核心的子產品機制

在Linux下,驅動程式都是以子產品存在的,子產品是向核心動态的增加功能,每個子產品都包括module_init和module_exit兩個函數,分别在向系統插入子產品和移除子產品時被調用。架構如下:

#include

#include

static int

hellomodule_init(void)

{

printk("hello word\n");

return

0;

}

static void

hellomodule_exit(void)

{

printk("goodbye word\n");

return;

}

module_init(hellomodule_init);

module_exit(hellomodule_exit);

MODULE_LICENSE("GPL");

2)Linux字元IO驅動設計

步驟如下:

1.定義描述字元IO裝置的結構體

在Linux中,每個裝置都有一個結構體來描述的,該結構體包含了該裝置的所有資訊。如下:

struct cdev

{

struct

kobject kobj;

struct module

*owner;

const struct

file_operations *ops;

struct

list_head list;

dev_t

dev;

unsigned int

count;

};

2.定義裝置結構體變量

用裝置結構體來定義一個變量,在核心中該變量就代表對應的裝置。如下:

struct cdev

myled_cdev;

3.定義裝置的操作接口函數

裝置都是有一些操作的,應用程式就通過這些接口操作函數來使用驅動程式對裝置的控制。如下:

static struct

file_operations led_ops={

.open =

myled_open,

.ioctl =

myled_ioctl,

.release =

myled_close,

};

4.裝置的操作函數

根據裝置的操作接口函數完成操作函數,如下:

int

myled_open(struct inode *inode,struct file *file)

{

int value =

0;

value =

(1<<10)|(1<<12)|(1<<16)|(1<<20);

writel(value,S3C2410_GPBCON);

value = 0x0;

writel(value,S3C2410_GPBUP);

value =

0xfffffffe;

writel(value,S3C2410_GPBDAT);

return 0;

}

int

myled_ioctl(struct inode *inode,struct file *file,unsigned int

cmd,unsigned long arg)

{

switch(cmd)

{

case

LED_ON:

writel(0x0,S3C2410_GPBDAT);

break;

case

LED_OFF:

writel(0xfffffffe,S3C2410_GPBDAT);

break;

default:

break;

}

return 0;

}

int

myled_close(struct inode *inode,struct file *file)

{

printk("bye");

return 0;

}

5.字元驅動子產品的初始化和退出函數

這兩個函數是子產品的架構,定義如下:

static int __init

LED_INIT(void)

{

int re = 0;

int ret = 0;

myled_no =

MKDEV(MAINLEDNO,MINLEDNO);

ret =

register_chrdev_region(myled_no,1,"myled");

if(ret<0)

{

printk("cant

regist");

return

ret;

}

printk("reg

ok");

cdev_init(&myled_cdev,&led_ops);

myled_cdev.owner =

THIS_MODULE;

re =

cdev_add(&myled_cdev,myled_no,1);

if(re<0)

{

printk("add

error");

return

re;

}

printk("add

ok");

return 0;

}

static void __exit

LED_EXIT(void)

{

cdev_del(&myled_cdev);

unregister_chrdev_region(myled_no,1);

printk("exit

ok");

}

和unregister_chrdev_region> 。

最後向核心申明myled_init和myled_exit函數以及LICENSE。如下:

module_init(LED_INIT);

module_exit(LED_EXIT);

MODULE_LICENSE("GPL");

6.裝置驅動的編譯

Linux驅動子產品的編譯是通過Linux頂層下的Makefile檔案來實作的,如下:

obj-m:=myled.o

KDIR=/home/neo/linux-2.6.30.9-EL-20101126

all:

make -C $(KDIR)

SUBDIRS=$(shell pwd) modules

arm-linux-gcc -o

myled_test myled_test.c

四. 字元IO驅動測試程式設計流程

為了測試IO驅動是否正常,在應用層編寫一個LED燈的程式,主要完成打開,關閉功能。如下:

int main(int

argc,char **argv)

{

int

fd;

fd =

open("/dev/ledS0",0);

if(!strcmp(argv[1],"on"))

{

ioctl(fd,LED_ON);

}

else

if(!strcmp(argv[1],"off"))

{

ioctl(fd,LED_OFF);

}

return 0;

}

五. 編譯及下載下傳流程

1.動态加載

a) 将myled.c和myled_test.c用make進行編譯,得到子產品myled.ko和執行檔案myled_test

b) 更新S3C2440的核心和檔案系統。啟動開發闆的LINUX系統。

c) 往開發闆核心中添加驅動子產品

在shell下執行insmod

myled.ko

d) 建立裝置檔案節點

在shell下執行mknod

/dev/ledS0 c 108 0

108:主裝置号

0:次裝置号>

e) 将執行檔案myled_test添加到開發闆上,并執行:

./myled_test

on

./myled_test

off

2.靜态加載

a) 打開核心源碼檔案包,将myled.c檔案拷到driver檔案夾下,打開Kconfig檔案,在其中添加如下代碼:

config

LINETECH_LED

bool "config

myled"

default y

help

myled driver

test

b) 同時打開Makefile檔案,添加如下代碼:

obj-$(CONFIG_LINETECH_LED) +=myled.o

c) 在終端輸入make

menuconfig進行核心的配置,如圖:

最後一個選項即為我要選得myled配置。

d)

将核心編譯得到zImage,下載下傳到開發闆中,建立裝置檔案節點

在shell下執行mknod

/dev/ledS0 c 108 0

108:主裝置号

0:次裝置号>\

e)

将執行檔案myled_test添加到開發闆上,并執行:

./myled_test

on

./myled_test

off

三. 總結

通過這次對LINUX字元驅動LED燈的設計讓我獲益匪淺,該實驗讓我對驅動程式的設計有了大概的了解。首先,要知道驅動程式必須要有架構,即初始化和退出。然後,每個裝置都有與之對應的結構體,而應用層要使用驅動程式,其中必須要有接口操作函數,特别注意下我在myled_open中将LED的寄存器進行配置,而不是在初始化函數中設定,是因為:雖然加載了子產品,但是這個子產品卻不一定會被用到,是以在使用時才去設定。最後在初始化函數中要對cdev結構體變量進行初始化,并把該結構體注冊到核心中。

概括為:應用程式在核心的字元裝置數組中能夠找到主裝置号,根據裝置号找到該裝置的結構體cdev,通路結構體中的變量*ops,而*ops指向file_operation結構體,該結構體中有被應用層調用的函數。

四. 代碼

//myled.c

#include

#include

#include

#include

#include

#include

#include

#define MAINLEDNO

108

#define MINLEDNO

#define LED_ON

0x01

#define LED_OFF

0x02

dev_t myled_no =

0;

struct cdev

myled_cdev;

````````int

myled_open(struct inode *inode,struct file *file)

{

int value =

0;

value =

(1<<10)|(1<<12)|(1<<16)|(1<<20);

writel(value,S3C2410_GPBCON);

value = 0x0;

writel(value,S3C2410_GPBUP);

value =

0xfffffffe;

writel(value,S3C2410_GPBDAT);

return 0;

}

int

myled_ioctl(struct inode *inode,struct file *file,unsigned int

cmd,unsigned long arg)

{

switch(cmd)

{

case LED_ON:

writel(0x0,S3C2410_GPBDAT);

break;

case LED_OFF:

writel(0xfffffffe,S3C2410_GPBDAT);

break;

default:

break;

}

return 0;

}

int

myled_close(struct inode *inode,struct file *file)

{

printk("bye");

return 0;

}

static struct

file_operations led_ops={

.open =

myled_open,

.ioctl =

myled_ioctl,

.release =

myled_close,

};

static int __init

LED_INIT(void)

{

int re = 0;

int ret = 0;

myled_no =

MKDEV(MAINLEDNO,MINLEDNO);

ret =

register_chrdev_region(myled_no,1,"myled");

if(ret<0)

{

printk("cant

regist");

return ret;

}

printk("reg

ok");

cdev_init(&myled_cdev,&led_ops);

myled_cdev.owner =

THIS_MODULE;

re =

cdev_add(&myled_cdev,myled_no,1);

if(re<0)

{

printk("add

error");

return re;

}

printk("add

ok");

return 0;

}

static void __exit

LED_EXIT(void)

{

cdev_del(&myled_cdev);

unregister_chrdev_region(myled_no,1);

printk("exit

ok");

}

module_init(LED_INIT);

module_exit(LED_EXIT);

MODULE_LICENSE("GPL");

//myled_test.c

#include

#include

#include

#include

#include

#define LED_ON

0x01

#define LED_OFF

0x02

int main(int

argc,char **argv)

{

int fd;

fd =

open("/dev/ledS0",0);

if(!strcmp(argv[1],"on"))

{

ioctl(fd,LED_ON);

}

else

if(!strcmp(argv[1],"off"))

{

ioctl(fd,LED_OFF);

}

return 0;

}

Makefile

obj-m:=myled.o

KDIR=/home/neo/linux-2.6.30.9-EL-20101126

all:

make -C $(KDIR)

SUBDIRS=$(shell pwd) modules

arm-linux-gcc -o

myled_test myled_test.c