嵌入式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

0

#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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值