字符設備程序實列二-查詢按鍵值,按鍵按下相應的LED燈亮,按鍵鬆開相應的LED燈滅

按照實例一,實現了從應用程序空間向內核空間傳遞數據,這一例實現從內核讀取按鍵值到應用空間,然後把剛剛保存在應用空間按鍵值寫到內核空間,內核空間按鍵值來操作對應的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



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章