您的位置:首页 > 其它

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

2014-06-02 21:51 246 查看
按照实例一,实现了从应用程序空间向内核空间传递数据,这一例实现从内核读取按键值到应用空间,然后把刚刚保存在应用空间按键值写到内核空间,内核空间按键值来操作对应的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.6

all:
make -C $(KERN_DIR) M=`pwd` modules

clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order

obj-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 cached
CPU:   8% usr  91% sys   0% nice   0% idle   0% io   0% irq   0% softirq
Load 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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: