抑郁症健康,内容丰富有趣,生活中的好帮手!
抑郁症健康 > Linux 字符设备驱动开发基础(一)—— 编写简单 LED 设备驱动

Linux 字符设备驱动开发基础(一)—— 编写简单 LED 设备驱动

时间:2023-12-04 15:00:02

相关推荐

现在,我们来编写自己第一个字符设备驱动 —— 点亮LED。(不完善,后面再完善)

硬件平台:Exynos4412(FS4412)

编写驱动分下面几步:

a -- 查看原理图、数据手册,了解设备的操作方法;

b -- 在内核中找到相近的驱动程序,以它为模板进行开发,有时候需要从零开始;

c -- 实现驱动程序的初始化:比如向内核注册这个驱动程序,这样应用程序传入文件名,内核才能找到相应的驱动程序;

d -- 设计所要实现的操作,比如 open、close、read、write 等函数;

e -- 实现中断服务(中断不是每个设备驱动所必须的);

f -- 编译该驱动程序到内核中,或者用 insmod 命令加载;

g-- 测试驱动程序;

下面是一个点亮LED 的驱动:

第一步,当然是查看手册,查看原理图,找到相应寄存器;

查看手册,四个LED 所用寄存器为:

led2

GPX2CON 0x11000c40

GPX2DAT 0x11000c44

led3

GPX1CON 0x11000c20

GPX1DAT 0x11000c24

led4 3-4 3-5

GPF3CON 0x114001e0

GPF3DAT 0x114001e4

这里要注意:arm体系架构是io内存,必须要映射 ioremap( ); 其作用是物理内存向虚拟内存的映射。 用到writel readl这两个函数,详细解释会在后面不上,先看一下简单用法:

以LED2为例,下面是地址映射及读写:

[cpp]view plaincopyint*pgpx2con; int*pgpx2dat; pgpx2con=ioremap(GPX2CON,4); pgpx2dat=ioremap(GPX2DAT,4); readl(pgpx2con); writel(0x01,pgpx2dat);

下面是驱动程序,后面会更完善[cpp]view plaincopy#include<linux/module.h> #include<linux/fs.h> #include<linux/cdev.h> #include<linux/device.h> #include<asm/io.h> #include<asm/uaccess.h> staticintmajor=250; staticintminor=0; staticdev_tdevno; staticstructclass*cls; staticstructdevice*test_device; #defineGPX2CON0x11000c40 #defineGPX2DAT0x11000c44 #defineGPX1CON0x11000c20 #defineGPX1DAT0x11000c24 #defineGPF3CON0x114001e0 #defineGPF3DAT0x114001e4 staticint*pgpx2con; staticint*pgpx2dat; staticint*pgpx1con; staticint*pgpx1dat; staticint*pgpf3con; staticint*pgpf3dat; voidfs4412_led_off(intnum); voidfs4412_led_on(intnum) { switch(num) { case1: writel(readl(pgpx2dat)|(0x1<<7),pgpx2dat); break; case2: writel(readl(pgpx1dat)|(0x1<<0),pgpx1dat); break; case3: writel(readl(pgpf3dat)|(0x1<<4),pgpf3dat); break; case4: writel(readl(pgpf3dat)|(0x1<<5),pgpf3dat); break; default: fs4412_led_off(1); fs4412_led_off(2); fs4412_led_off(3); fs4412_led_off(4); break; } } voidfs4412_led_off(intnum) { switch(num) { case1: writel(readl(pgpx2dat)&(~(0x1<<7)),pgpx2dat); break; case2: writel(readl(pgpx1dat)&(~(0x1<<0)),pgpx1dat); break; case3: writel(readl(pgpf3dat)&(~(0x1<<4)),pgpf3dat); break; case4: writel(readl(pgpf3dat)&(~(0x1<<5)),pgpf3dat); break; } } staticintled_open(structinode*inode,structfile*filep) {//open fs4412_led_off(1); fs4412_led_off(2); fs4412_led_off(3); fs4412_led_off(4); return0; } staticintled_release(structinode*inode,structfile*filep) {//close fs4412_led_off(1); fs4412_led_off(2); fs4412_led_off(3); fs4412_led_off(4); return0; } staticssize_tled_read(structfile*filep,char__user*buf,size_tlen,loff_t*pos) { return0; } staticssize_tled_write(structfile*filep,constchar__user*buf,size_tlen,loff_t*pos) { intled_num; if(len!=4) { return-EINVAL; } if(copy_from_user(&led_num,buf,len)) { return-EFAULT; } fs4412_led_on(led_num); printk("led_num=%d\n",led_num); return0; } staticstructfile_operationshello_ops= { .open=led_open, .release=led_release, .read=led_read, .write=led_write, }; staticvoidfs4412_led_init(void) { pgpx2con=ioremap(GPX2CON,4); pgpx2dat=ioremap(GPX2DAT,4); pgpx1con=ioremap(GPX1CON,4); pgpx1dat=ioremap(GPX1DAT,4); pgpf3con=ioremap(GPF3CON,4); pgpf3dat=ioremap(GPF3DAT,4); writel((readl(pgpx2con)&~(0xf<<28))|(0x1<<28),pgpx2con); writel((readl(pgpx1con)&~(0xf<<0))|(0x1<<0),pgpx1con); writel((readl(pgpf3con)&~(0xff<<16))|(0x11<<16),pgpf3con); } staticintled_init(void) { intret; devno=MKDEV(major,minor); ret=register_chrdev(major,"led",&hello_ops); cls=class_create(THIS_MODULE,"myclass"); if(IS_ERR(cls)) { unregister_chrdev(major,"led"); return-EBUSY; } test_device=device_create(cls,NULL,devno,NULL,"led");//mknod/dev/hello if(IS_ERR(test_device)) { class_destroy(cls); unregister_chrdev(major,"led"); return-EBUSY; } fs4412_led_init(); return0; } voidfs4412_led_unmap(void) { iounmap(pgpx2con); iounmap(pgpx2dat); iounmap(pgpx1con); iounmap(pgpx1dat); iounmap(pgpf3con); iounmap(pgpf3dat); } staticvoidled_exit(void) { fs4412_led_unmap(); device_destroy(cls,devno); class_destroy(cls); unregister_chrdev(major,"led"); printk("led_exit\n"); } MODULE_LICENSE("GPL"); module_init(led_init); module_exit(led_exit);

测试程序:

[cpp]view plaincopy#include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<stdio.h> main() { intfd,i,lednum; fd=open("/dev/led",O_RDWR); if(fd<0) { perror("openfail\n"); return; } for(i=0;i<100;i++) { lednum=0; write(fd,&lednum,sizeof(int)); lednum=i%4+1; write(fd,&lednum,sizeof(int)); sleep(1); } close(fd); }

makefile:

[cpp]view plaincopyifneq($(KERNELRELEASE),) obj-m:=hello.o $(info"2nd") else #KDIR:=/lib/modules/$(shelluname-r)/build KDIR:=/home/xiaoming/linux-3.14-fs4412 PWD:=$(shellpwd) all: $(info"1st") make-C$(KDIR)M=$(PWD)modules arm-none-linux-gnueabi-gcctest.c sudocphello.koa.out/rootfs/test/ clean: rm-f*.ko*.o*.symvers*.mod.c*.mod.o*.order endif

编译结束后,将a.out 和 hello.ko 拷贝到开发板中:

# insmod hello.ko

#mknod /dev/hello c 250 0

#./a.out

会看到跑马灯效果。

后面会对该驱动完善。

如果觉得《Linux 字符设备驱动开发基础(一)—— 编写简单 LED 设备驱动》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。