zoukankan      html  css  js  c++  java
  • 驱动之字符设备按键中断

    写驱动流程:

    1.原理图-à输出高电平 led亮

    2.对应核心板的GPIO口

    3.查看寄存器地址

    4.了解管脚的功能

    5.开始写驱动

    ①许可证

    ②加载函数

    1. 申请设备号

     MKDEV

    Register_chrdev_region

    Alloc_chrdev_region

    1. 注册设备

    Cdev

    File_oparetions---àopen/release

    Cdev_init

    Cdev_add

    1. 寄存器映射

    Ioremap(PA,size)

    1. 配置寄存器(读改写的方式

    Readll()

    Write()

     ③卸载函数

    1. 取消映射
    2. 注销设备
    3. 释放

     

    传参时: 将字符串转换成整数;

      按键 驱动:

    1.原理图

    2.外部中断,下降沿触发

    Request_irq的第3个参数设置

    3.GPH0  

    开始写驱动:

    1. 许可证
    2. 加载模块

    ①     申请设备号

    ②     注册设备

    ③     IO的初始化为外部中断模式

    1. 卸载

    ①     注销设备

    ②     释放设备号

     

    open函数中:注册中断   request_irq(irq, )

    release 函数:释放中断  free_irq(unsigned  int  irq,  void  *dev_id);

    中断处理函数(中断号, , ,)。。。。。。(第一步,打印键值----à将键值保存在一个变量中,传递给驱动???如何通过缓存,将快速的按键都保存下来)

     

     

    按键驱动:(呵呵,虽然挺简单的,但是初学的我,还是搞了一天半,查了不少书。。。。。)

    ①     设备驱动模块的加载:(MKDEV生成主设备号、注册设备号、ioremap虚拟地址的映射、读改写、)

     1 /*设备驱动模块的加载*/
    2 static int __init my_key_init(void)
    3 {
    4 int error;
    5 dev = MKDEV(key_major,key_minor);
    6 #if 1
    7 if (key_major)
    8 {
    9 register_chrdev_region(dev,1,"mykey");
    10 }
    11 else
    12 {
    13 alloc_chrdev_region(dev,0,1,"my_key");
    14 }
    15 #endif
    16 error = register_chrdev_region(dev,1,"mykey");
    17 if (error < 0)
    18 {
    19 printk(KERN_WARNING "can't get major %d\n",key_major);
    20 return error;
    21 }
    22 char_reg_setup_cdev();
    23 gph0con_va = ioremap(GPH0CON,4);
    24 if (gph0con_va == NULL)
    25 {
    26 printk(KERN_WARNING "ioremap error......\n");
    27 return -ENOMEM;
    28 }
    29 writel((readl(gph0con_va)& ~0xff0ffff0) | 0x22022220, gph0con_va); //GPH0CON外部中断模式
    30 printk(KERN_INFO "mykey driver is ok.....\n");
    31 return 0;
    32
    33 }
    34
    35


    ②     设备驱动的卸载模块:(包括取消映射、注销设备、释放申请的设备号)
    View Code
    1 static void __exit my_key_exit(void)
    2 {
    3 iounmap(gph0con_va);
    4 cdev_del(&cdev);//注销设备
    5 unregister_chrdev_region(dev,1);//释放之前申请的设备号
    6 printk(KERN_INFO "myket clean up\n");

    7 return ;
    8 }

     

    ③     设备注册 (cdev_init建立cdev和file_operations之间的联系、cdev_add向系统添加一个设备)

     
     1 /*设备的注册*/
    2 static void char_reg_setup_cdev(void)
    3 {
    4 int error;
    5 cdev_init(&cdev,&key_fops);//设备初始化,建立cdev和file_operations之间的联系
    6      error = cdev_add(&cdev,dev,1);//向系统添加一个设备
    7 if (error)
    8 {
    9 printk(KERN_NOTICE "char_reg_setup_cdev errro.,....\n");
    10 }
    11 return ;
    12 }

    ④     file_operations结构体,成员函数实现字符设备和内核的接口

    1 struct file_operations key_fops = {
    2 .owner = THIS_MODULE,
    3 .open = my_key_open,
    4 .release = my_key_release,
    5 };
    6
    7


     ⑤     open函数中:注册中断   request_irq(irq,hander,irqflags,name, dev_id )其中添加了一种驱动程序的出错处理。。。goto 函数实现
     1 static int my_key_open(struct inode *inodep, struct file *filep)
    2 {
    3 if (request_irq(IRQ_EINT(1),hander,IRQF_DISABLED | IRQF_TRIGGER_FALLING,"key1",NULL)) //红色的flags为下降沿触发中断
    4 {
    5 printk("key 1 request_irq error...\n");
    6 goto key1;
    7 }
    8 if (request_irq(IRQ_EINT(2),hander,IRQF_DISABLED | IRQF_TRIGGER_FALLING,"key2",NULL))
    9 {
    10 printk("key 2 request_irq error...\n");
    11 goto key2;
    12 }
    13 printk(KERN_INFO "device opend success..\n");
    14 return 0;
    15
    16 key2: free_irq(IRQ_EINT(1),NULL);
    17 key1: return 0;
    18
    19 }

     

    ⑥     release函数:(释放中断)

    View Code
    1 static int my_key_release(struct inode *inodep,struct file *filep)
    2 {
    3 free_irq(IRQ_EINT(2),NULL);
    4 free_irq(IRQ_EINT(1),NULL);
    5 printk(KERN_INFO "devices closed.....\n");
    6 return 0;
    7 }


     

    ⑦     最后的中断处理函数(irqreturn_t 可能有两种返回值,IRQ_NONEIRQ_HANDLED;当中断处理函数程序检测到一个中断时,但该中断对应的设备并不是在注册处理函数期间指定的产生源时,返回IRQ_NONE;当中断处理程序被正确调用,且确实是他所对应设备产生了中断时,返回IRQ_HANDLED;)

     1 irqreturn_t hander(unsigned int irq, void *dev_id)
    2 {
    3 switch (irq)
    4 {
    5 case IRQ_EINT(1): printk(KERN_INFO "key 1 is pressed......\n");
    6 break;
    7 case IRQ_EINT(2): printk(KERN_INFO "key 2 is pressed......\n");
    8 break;
    9 default :
    10 break;
    11 }
    12 return IRQ_HANDLED;//当中断处理程序被正确调用,且确实是他所对应设备产生了中断时,返回IRQ_HANDLED
    13 }
    14
    15


     

    驱动程序的出错处理  goto

    if  (a)

             goto  a;

    if   (b)

             goto  b;

    if   (c)

             goto  c;

     

    c:   free_irq(b);

    b:   free_irq(a);

    a:   return 0;

     

    首先注册一个中断处理程序:request_irq(irq , hander, irqflags,devname, dev_id );

    有注册就有释放: free_irq(irqno, dev_id);

    中断处理程序:static  irqreturn_t   hander (int  irqno,void *  dev_id, struct  pt_regs *regs);

     注意:函数的返回值是一个特殊的类型,irqreturn_t 可能有两种返回值,IRQ_NONE和IRQ_HANDLED;当中断处理函数程序检测到一个中断时,但该中断对应的设备并不是在注册处理函数期间指定的产生源时,返回IRQ_NONE;当中断处理程序被正确调用,且确实是他所对应设备产生了中断时,返回IRQ_HANDLED;利用这两个返回值,内核可以知道设备发出的是否是一种虚假的(为请求)的中断;

     

     

    static :

              中断处理程序通常会标记位static,因为它从来都不会被别的文件中的代码直接调用。另外,中断处理程序是无需重入的,当一个给定的中断处理程序正在执行时,相应的中断线在所有的处理器上都会被屏蔽掉,以防止在同一个中断上接收另外一个新的中断处理。

     

     驱动程序:

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/fs.h>
    #include <linux/interrupt.h>
    #include <linux/uaccess.h>
    #include <linux/cdev.h>
    #include <asm/io.h>
    #include <mach/irqs.h>
    #include <linux/cdev.h>
    
    #define GPH0CON 0xE0300100
    
    MODULE_LICENSE ("GPL");
    
    int key_major = 250;
    int key_minor = 0;
    dev_t dev = 0;
    static struct cdev cdev;
    unsigned long *gph0con_va;
    
    irqreturn_t hander(unsigned int irq, void *dev_id)
    {
    	switch (irq)
    	{
    		case IRQ_EINT(1): printk(KERN_INFO "key 1 is pressed......\n");
    				break;
    		case IRQ_EINT(2): printk(KERN_INFO "key 2 is pressed......\n");
    				break;
    		default :
    				break;
    	}
    	return IRQ_HANDLED;
    }
    
    static int my_key_open(struct inode *inodep, struct file *filep)
    {
    	if (request_irq(IRQ_EINT(1),hander,IRQF_DISABLED | IRQF_TRIGGER_FALLING,"key1",NULL))
    	{
    		printk("key 1 request_irq error...\n");
    		goto key1;
    	}
    	if (request_irq(IRQ_EINT(2),hander,IRQF_DISABLED | IRQF_TRIGGER_FALLING,"key2",NULL))
    	{
    		printk("key 2 request_irq error...\n");
    		goto key2;
    	}
    	printk(KERN_INFO "device opend success..\n");
    	return 0;
    
    key2: free_irq(IRQ_EINT(1),NULL);
    key1: return 0;
    
    }
    
    static int my_key_release(struct inode *inodep,struct file *filep)
    {
    	free_irq(IRQ_EINT(2),NULL);
    	free_irq(IRQ_EINT(1),NULL);
    
    	printk(KERN_INFO "devices closed.....\n");
    	return 0;
    }
    
    struct file_operations key_fops = {
    	.owner = THIS_MODULE,
    	.open  = my_key_open,
    	.release = my_key_release,
    };
    static void char_reg_setup_cdev(void)
    {
    	int error;
    	cdev_init(&cdev,&key_fops);
    	error = cdev_add(&cdev,dev,1);
    	if (error)
    	{
    		printk(KERN_NOTICE "char_reg_setup_cdev errro.,....\n");
    	}
    	return ;
    }
    static int __init my_key_init(void)
    {
    	int error;
    	dev = MKDEV(key_major,key_minor);
    #if 1
    	if (key_major)
    	{
    		register_chrdev_region(dev,1,"mykey");
    	}
    	else
    	{
    		alloc_chrdev_region(dev,0,1,"my_key");
    	}
    #endif 
    	error = register_chrdev_region(dev,1,"mykey");
    	if (error < 0)
    	{
    		printk(KERN_WARNING "can't get major %d\n",key_major);
    		return error;
    	}
    	char_reg_setup_cdev();
    	gph0con_va = ioremap(GPH0CON,4);
    	if (gph0con_va == NULL)
    	{
    		printk(KERN_WARNING "ioremap error......\n");
    		return -ENOMEM;
    	}
    	writel((readl(gph0con_va)& ~0xff0ffff0) | 0x22022220, gph0con_va);
    	printk(KERN_INFO "mykey driver is ok.....\n");
    	return 0;
    	
    }
    static void __exit my_key_exit(void)
    {
    	iounmap(gph0con_va);
    	cdev_del(&cdev);
    	unregister_chrdev_region(dev,1);
    	printk(KERN_INFO "myket clean up\n");
    	return ;
    }
    module_init(my_key_init);
    module_exit(my_key_exit);
    

     测试程序:

     

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <fcntl.h>
    
    int main()
    {
    	int fd;
    	int ret;
    //	int key_num;
    
    	fd = open("/dev/my_key",O_RDWR);
    	if (fd < 0)
    	{
    		printf("open error\n");
    		return -1;
    	}
    /*
    	while (1)
    	{
    		ret = read(fd,&key_num,sizeof(int));
    		printf("you press the key of %d\n",key_num);
    	}
    	*/
    	while (1);
    	close(fd);
    	printf("devices close...\n");
    	return 0;
    }
    

     

     Makefile文件:

     

    $(warning KERNELRELEASE=$(KERNELRELEASE))
    
    ifeq ($(KERNELRELEASE),)
    
    #KERNELDIR ?= /lib/modules/$(shell uname -r)/build
    KERNELDIR ?= /home/linux/linux-2.6.35/  #该目录是你的内核的目录
    PWD := $(shell pwd)
    
    modules:
    	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
    
    modules_install:
    	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
    
    
    clean:
    	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* module
    
    .PHONY:modules modules_install clean
    
    else
    #	obj-m := hello.o
    	obj-m += my_key.o
    #	obj-m := char_reg.o
    endif
    

     

  • 相关阅读:
    第一次个人编程作业
    第一次软工作业
    [Manacher]最长回文子串
    面向对象程序设计 总结作业
    面向对象程序设计 小黄衫晒单
    面向对象程序设计 作业三
    面向对象程序设计 作业二
    面向对象程序设计 作业一
    SSD/Memory技术学习拼图
    第一次结对编程作业
  • 原文地址:https://www.cnblogs.com/zhou2011/p/2379073.html
Copyright © 2011-2022 走看看