LED驱动

自己对驱动的一些见解

---led驱动的代码,编译,加载,测试过程

yeh2008@126.com

(我是新手,写这些希望能够帮助新新手。同时也希望牛人们鄙视后给些意见和建议)

l 首先,介绍一下驱动的概念:

设备驱动是一种在应用程序和硬件设备之间的特殊程序,相当于硬件的接口。驱动对应用程序而言,透明化了硬件设备。

在没有操作系统的情况下,设备驱动的接口直接提交给应用软件工程师,应用软件没有跨越任何层次就可以直接访问设备驱动的接口。在存在操作系统时,需要将设备驱动融入到操作系统中,应用程序是通过调用操作系统的API来实现对硬件的操作,所以设备驱动必须设计面向操作系统的接口。 


 

l 接着,介绍一下驱动的流程

实验必备(以我的为例):linux系统(宿主机上的虚拟机),arm板(带有2.6.32.2内核的mini2440),2.6.32.2的内核源码。

首先需要配置2.6.32.2的内核源码,使其可以编译驱动文件(在虚拟机上的linux系统下操作的)。一般的随ARM板配套的软件包里都有已经裁剪过的内核,只是没有编译,这样就大大简化了我们的工作量,步骤如下:

1,解压2.6.32.2内核(已经裁剪过的),修改内核主目录下的Makefile文件,在下面两处做些修改:

ARCH ?= $(SUBARCH) 修改为:ARCH = arm

CROSS_COMPILE ?=    修改为:CROSS_COMPILE = arm-linux-

2,修改内核的时钟频率为12MHZ。

打开源码文件 arch/arm/mach-s3c2440/mach-smdk2440.c,将16.9344MHZ改为12MHZ。

3,在内核根目录下找到配置文件mini2440-defconfig(mini2440把几个常用配置文件放在根目录下,如果没有可以进入arch/arm/configs 下的s3c2410_defconfig找到), 将其改名为.config。

4,然后敲入命令:make menuconfig,进入图形化配置内核,作为新手,其他的不用改,直接退出保存。

5,敲入命令:make Image,开始编译内核生成镜像文件。

至此,可以编译驱动的内核生成了。

然后,就进入正题了---编写驱动程序。

编写驱动程序条例要清晰,驱动程序没有像应用程序main那样的入口,替代main的是module_init(function)中的function函数。我个人总结编写驱动的思路:

1,分配设备号

2,申请注册设备(主体部分,包括初始化设备,初始化端口,定义函数 的实现等)

3,删除设备

4,释放设备号。

写驱动是为了操作硬件,当然离不开原理图了,查看原理图,确定驱动需要控制的引脚。比如,我要控制ARM板子上四个LED灯,让她们依次不间断的闪烁。我就需要控制GPB5,GPB6,GPB7,GPB8四个引脚。我写的LED驱动代码如下:

#include<linux/kernel.h>

#include<linux/module.h>

#include<linux/device.h>

#include<linux/types.h>

#include<linux/ioctl.h>

#include<linux/errno.h>

#include<linux/init.h>

#include<linux/cdev.h>

#include<asm/uaccess.h>

#include<linux/gpio.h>

#include<linux/fs.h>

#include<asm/io.h>

#include<mach/regs-gpio.h>

#define DEVICE_NAME "myled"

#define DEVICE_MAJOR 251

static int led_major = DEVICE_MAJOR;

struct s3c2410_led_dev

{

struct cdev cdev;

int status;

};

static struct s3c2410_led_dev dev;

static int s3c2410_open(struct inode *inode, struct file *filp)

{

return 0;

}

static int s3c2410_release(struct inode *inode, struct file *filp)

{

return 0;

}

static int s3c2410_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)

{

switch(cmd)

{

case 0:

s3c2410_gpio_setpin(S3C2410_GPB(5), 0);

s3c2410_gpio_setpin(S3C2410_GPB(6), 1);

s3c2410_gpio_setpin(S3C2410_GPB(7), 1);

s3c2410_gpio_setpin(S3C2410_GPB(8), 1);

break;

case 1:

s3c2410_gpio_setpin(S3C2410_GPB(5), 1);

s3c2410_gpio_setpin(S3C2410_GPB(6), 0);

s3c2410_gpio_setpin(S3C2410_GPB(7), 1);

s3c2410_gpio_setpin(S3C2410_GPB(8), 1);

break;

case 2:

s3c2410_gpio_setpin(S3C2410_GPB(5), 1);

s3c2410_gpio_setpin(S3C2410_GPB(6), 1);

s3c2410_gpio_setpin(S3C2410_GPB(7), 0);

s3c2410_gpio_setpin(S3C2410_GPB(8), 1);

break;

case 3:

s3c2410_gpio_setpin(S3C2410_GPB(5), 1);

s3c2410_gpio_setpin(S3C2410_GPB(6), 1);

s3c2410_gpio_setpin(S3C2410_GPB(7), 1);

s3c2410_gpio_setpin(S3C2410_GPB(8), 0);

break;

default:

return -EINVAL;

}

return 0;

}

static struct file_operations s3c2410_led_fops =

{

.owner = THIS_MODULE,

.open = s3c2410_open,

.ioctl = s3c2410_ioctl,

.release = s3c2410_release,

};

static int __init s3c2410_led_init(void)

{

int result;

/*******apply the device number*********/

dev_t devno = MKDEV(led_major, 0);

if(led_major)

{

result = register_chrdev_region(devno, 1, DEVICE_NAME);

}

else

{

result = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME);

led_major = MAJOR(devno);

}

if(result < 0)

{

return result;

}

/********register LED device**********/

devno = MKDEV(led_major, 0);

cdev_init(&dev.cdev, &s3c2410_led_fops);

dev.cdev.owner = THIS_MODULE;

dev.cdev.ops = &s3c2410_led_fops;

result = cdev_add(&dev.cdev, devno, 1);

/********init the IO_PORT***********/

unsigned int i;

for(i = 5; i < 9; i++)

{

s3c2410_gpio_cfgpin(S3C2410_GPB(i), S3C2410_GPIO_OUTPUT);

s3c2410_gpio_pullup(S3C2410_GPB(i), 1);

s3c2410_gpio_setpin(S3C2410_GPB(i), 1);

}

printk(DEVICE_NAME"Initialized---\n");

return 0;

}

static void __exit s3c2410_led_exit(void)

{

cdev_del(&dev.cdev);

unregister_chrdev_region(MKDEV(led_major, 0), 1);

}

module_init(s3c2410_led_init);

module_exit(s3c2410_led_exit);

MODULE_LICENSE("GPL");


测试程序如下:

#include"stdio.h"

#include"stdlib.h"

#include"unistd.h"

#include"sys/types.h"

#include"sys/ioctl.h"

#include"termios.h"

#include"sys/stat.h"

#include"fcntl.h"

#include"sys/time.h"

#define PATH "/dev/myled"

int main()

{

int fd = open(PATH, O_RDWR);

if(fd < 0)

{

printf("Open error!\n");

exit(1);

}

printf("Led test show. Press ctrl+c to exit\n");

while(1)

{

ioctl(fd, 0);

sleep(1);

ioctl(fd, 1);

sleep(1);

ioctl(fd, 2);

sleep(1);

ioctl(fd, 3);

sleep(1);

}

close(fd);

exit(0);
}


Makefile文件如下:

ifneq ($(KERNELRELEASE),)

obj-m:=myled.o

//myled-objs:=myled.o

else

KDIR :=/home/yeh2011/arm_kernel/linux-2.6.32.2

all:

make -C $(KDIR) M=$(PWD) modules

app:

arm-linux-gcc led_app.c -o led_app_more

clean:

rm *.ko *.mod.* *.markers *.o *.symvers *.order led_app -f

endif

敲入make命令,生成myled.ko 文件。我在执行这一步的时候出现了很多问题,比如:

1, h:18:26: error: linux/bounds.h: No such file or directory

 解决方法:在内核目录中执行make prepare命令。

2/lib/libc.so.6: version `GLIBC_2.8' not found (required by scripts/mod/modpost)

解决方法:好像是安装一个什么库吧,好像是叫ncurses吧,具体的给忘了。

3 总是提示S3C2410_GPB5未定义(正确代码之前的错误代码中用到的)。这个问题纠结我了好久啊,两周之后,突然不经意的看见一片文章就是讲述的这个问题,然后自己好好的理解了,没想到,还真的给解决了,原来是2.4和2.6的版本问题,旧版本直接通过S3C2410_GPmn来定义端口GPIOm的第n个引脚,比如GOIOB的第5个引脚引用为S3C2410_GPB5,在新版本中改为采用S3C2410_GPm(n)的方式,比如GPIOB的第5个引脚就改为S3C2410_GPB(5)不止是引脚做了变动,对引脚的配置也做了一定程度上的修改:比如将引脚GPB(5)设置为输出模式,则采用了下面的函数:S3C2410_gpio_cfgpin(S3C2410_GPB(5),S3C2410_GPIO_OUTPUT)

以上这些函数,宏定义都需要头文件:#include"linux/gpio.h"。

驱动的加载:

生成.ko 文件之后,接下来的问题就简单了。将.ko 文件和led_app文件烧到板子上,修改其权限为可执行。然后insmod myled.ko将其加载到操作系统上,此时会显示DEVICE_NAME"Initialized,表示加载成功。

然后需要在板子上新建设备节点。我根据自己的代码:

mknod /dev/myled c 251 0

其中,myled 是我在程序中定义的设备的名字,c表示是字符设备,251是我在程序中定义的设备的主设备号,0为次设备号。

当创建成功之后,直接执行led_app文件,这时,ARM 板上的四个led灯就会不停地依次闪烁。

至此,LED驱动程序开发算是完成了。在整个过程中,自己学到了不少东西,包括一些技术上的知识,我感觉更重要的是学会了遇到问题怎么去解决问题,怎么能让自己手头上的资源发挥到他们最大的价值。这些东西会让我终身受益。

当然要感谢我的师兄郝东东,还有许多热心的网友。

如果有什么问题,QQ:787689011,欢迎交流问题 。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值