字符设备程序实列二-查询按键值,按键按下相应的LED灯亮,按键松开相应的LED灯灭

来源:互联网 发布:网络音视频许可证 编辑:程序博客网 时间:2024/05/20 22:29

按照实例一,实现了从应用程序空间向内核空间传递数据,这一例实现从内核读取按键值到应用空间,然后把刚刚保存在应用空间按键值写到内核空间,内核空间按键值来操作对应的LED

驱动源码:keys_leds.c

驱动源码Makefile

测试源码: keys_leds_test.c

keys_leds.c:

/***************************************************************

*filename:keys_leds.c*description:无按键按下,熄灭全部LED,按键按下,点亮相应LED,松开熄灭相应LED*author:xyc*create time:2014/6/2*version:1*modify info:******************************************************/#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/delay.h>#include <asm/irq.h>#include <asm/arch/regs-gpio.h>#include <asm/hardware.h>#include <asm-arm/io.h>#include <asm-arm/uaccess.h>static int KEYS_LEDS_MAJOR;static struct class *keys_leds;static struct class_device *keys_leds_dev;static volatile unsigned long *gpfcon=NULL;static volatile unsigned long *gpfdat=NULL;static int keys_leds_open(struct inode *inode, struct file *file){/*配置按键为输入,LED为输出*/*gpfcon &= ~(0x3<<(4*2)) & (~(0x3<<(0*2))) & (~(0x3<<(5*2)) ) & (~(0x3<<(2*2)));*gpfcon |= 0x1<<(4*2) |(0x0<<(0*2)) | (0x1<<(5*2) )|(0x0<<(2*2));*gpfdat |=1<<4|1<<5;return 0;}static ssize_t keys_leds_read(struct file *file, char __user *userbuf, size_t count, loff_t *off){char val =0;//初始化为0不要忘/*记录按下的键*/if( !(*gpfdat & (1<<0)) ){/*s2按下*/val =1;} if (!(*gpfdat & (1<<2))){/*s3按下*/val =2;}copy_to_user(userbuf, &val, 1);return 1;}static ssize_t keys_leds_write(struct file * file, const char __user * userbuf,     size_t count, loff_t * off){char val=0;  //初始化为0,不要忘/*   *按键没按下keys_leds_read传给应用read的按键值为0,   *应用write将按键值0传给 keys_leds_write的val,LED均熄灭   */      /*    *按键按下,keys_leds_read传给应用read的按键值为1或2,    *应用write将按键值1或2传给 keys_leds_write的val相应的LED亮    */copy_from_user(&val, userbuf, 1);switch(val){case 1:{/*点亮LED4*/*gpfdat &=~(1<<4);break;}case 2:{/*点亮LED5*/*gpfdat &=~(1<<5);break;}default:{/*俩个按键均没按下,此时read()读到的值为0,*再将0写入到驱动keys_leds_write()函数的自动变量val*/*gpfdat |= (1<<4)|(1<<5);  //不要忘记没按键或按键松开时灭灯break;}}}static struct file_operations keys_leds_op = {.owner = THIS_MODULE,.open= keys_leds_open,.read= keys_leds_read,.write= keys_leds_write,};static int __init keys_leds_init(){int minor;KEYS_LEDS_MAJOR = register_chrdev(0, "keys_leds", &keys_leds_op);if(KEYS_LEDS_MAJOR <0){printk("register char device failed\n");return KEYS_LEDS_MAJOR;}keys_leds = class_create(THIS_MODULE, "keys_leds");keys_leds_dev =class_device_create(keys_leds, NULL, MKDEV(KEYS_LEDS_MAJOR, 0), NULL, "leds");gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);gpfdat = gpfcon +1;return 0;}static void __exit keys_leds_exit(){unregister_chrdev(KEYS_LEDS_MAJOR, keys_leds);class_device_destroy(keys_leds, MKDEV(KEYS_LEDS_MAJOR, 0));class_destroy(keys_leds);iounmap(gpfcon);}module_init(keys_leds_init);module_exit(keys_leds_exit);MODULE_LICENSE("GPL");


程序编写过程中,编译出现的错误:

1.keys_leds_op后面忘掉等号,2.将按键驱动分为几个设备节点,没编译之前发现,但static struct class_device *keys_leds_dev[3]没有改过来3.头文件不记得写了

4.keys_leds_read/keys_leds_write中val忘记初始化为0,keys_leds_write对val = 0即没有按键按下或按键松开时进行灭灯处理

Makefile:

跟实例一差不多

KERN_DIR = /work/system/linux-2.6.22.6all:make -C $(KERN_DIR) M=`pwd` modules clean:make -C $(KERN_DIR) M=`pwd` modules cleanrm -rf modules.orderobj-m+= keys_leds.o

这里obj-m表示以模块动态加载的方式启用,并没有编入内核,所以开发板断电重启后,断电前的模块已经卸载掉了,同理对应的在模块中创建的设备文件也不存在了

测试模块keys_leds_test.c:

/****************************************************************filename:keys_leds_test.c*description:测试keys_leds.c驱动*author:xyc*create time:2014/6/2*version:1*modify info:****************************************************************/#include <stdio.h>#include <fcntl.h>#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>int main(int argc, char *argv[]){int fd1;char key_value=0;if(argc !=2 ){    printf("Usage:\n");    printf("%s <dev> <on|off>\n",argv[0]);    printf("eg. \n");    printf("%s /dev/leds \n", argv[0]);    return -1;}fd1 = open(argv[1], O_RDWR);if( fd1<0){printf("%s open failed", argv[1]);return -1;}while(1){read(fd1, &key_value, 1);if( (key_value==1) |(key_value==2) ) write(fd1, &key_value, 1);else write(fd1, &key_value, 1);}close(fd1);return 0;}


测试模块容易忘记的是头文件忘了写,一般复制以前模块的,如果再出现相应编译错误,然后man比如我只写了
#include <stdio.h>#include <fcntl.h>#include <unistd.h>
这3个头文件,导致O_RDWR未定义,此时因为O_RDWR是open使用的,这时在ubuntu下man 2 open看其需要包含的头文件为

#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>

所以加上
#include <sys/types.h>#include <sys/stat.h>
这2头文件O_RDWR的编译错误就消失了

还有可能忘了的对key_value==1或2调用的write将按键值传给内核空间,但没按下时的按键值key_value==0没调用write即没把无按键按下的值传给内核空间,导致松开按键后,LED灯不熄灭,因为程序不断read  ->第一个write写1或2->read->第一个write写1或2...   在第一个write写1或2都是点亮LED4 或LED5,这样按下S2 LED4,LED4亮,松开S2 LED4不灭,所以需要对驱动没有读到按键值时在测试程序中做判断,然后将无按键的值0传给驱动keys_leds_write中的临时变量val使得在switch(0)中关掉LED4 LED5


测试看按键按下,相应的LED灯是否亮,松开,相应的LED灯黑,同时按下的反应


本驱动代码因为是在测试代码中轮询按键,所以CPU占用率几乎达到了100%,所以实用中均是用的中断方式

Mem: 6696K used, 54488K free, 0K shrd, 0K buff, 2068K cachedCPU:   8% usr  91% sys   0% nice   0% idle   0% io   0% irq   0% softirqLoad average: 0.99 0.96 0.84  PID  PPID USER     STAT   VSZ %MEM %CPU COMMAND  793   770 0        R     1308   2%  99% ./keys_leds_test /dev/leds   806   770 0        R     3096   5%   0% top   770     1 0        S     3096   5%   0% -sh     1     0 0        S     3092   5%   0% init       762     2 0        SW<      0   0%   0% [rpciod/0]    6     2 0        SW<      0   0%   0% [khelper]  745     2 0        SW<      0   0%   0% [kmmcd]    2     0 0        SW<      0   0%   0% [kthreadd]    3     2 0        SWN      0   0%   0% [ksoftirqd/0]    4     2 0        SW<      0   0%   0% [watchdog/0]    5     2 0        SW<      0   0%   0% [events/0]   55     2 0        SW<      0   0%   0% [kblockd/0]   56     2 0        SW<      0   0%   0% [ksuspend_usbd]   59     2 0        SW<      0   0%   0% [khubd]   61     2 0        SW<      0   0%   0% [kseriod]   73     2 0        SW       0   0%   0% [pdflush]   74     2 0        SW       0   0%   0% [pdflush]   75     2 0        SW<      0   0%   0% [kswapd0]   76     2 0        SW<      0   0%   0% [aio/0]nfs: server 192.168.1.19 not responding, still trying



0 0
原创粉丝点击