zoukankan      html  css  js  c++  java
  • 3.按键驱动硬件操作

    按键驱动的操作

    通过上两节课的了解,接下来,我们通过例子来实现按键驱动的功能。

    首先是按键的初始化,按键的初始化可以选择在open函数,和模块的初始化函数当中完成硬件的初始化。下面我们是选择在模块的初始化函数进行按键的初始化。按键的初始化,主要涉及对GPIO的引脚的功能进行相应的设置。在OK6410中有6个按键,今天我们选择第一个进行操作。

    下面是buttons.c的代码:

    #include <linux/init.h>

    #include <linux/module.h>

    #include <linux/fs.h>

    #include <linux/io.h>

    #include <linux/miscdevice.h>

    #include <linux/interrupt.h>

     

    #define GPNCON 0x7f008830

    irqreturn_t key_int(int irq, void *dev_id)

    {

        //1.检测是否发生了按键中断

    /*在这里可以不做,因为中断只有一个,不是共享中断*/

        //2.清除已经发生的按键中断

    /*这里的清除是硬件内部,而我们的中断是处理器级别的,可以不清除。比如用DM9000的网卡芯片需要清除,因为里面本身就有状态的寄存器,需要清除。*/

        //3.打印按键值

        printk("<0>key down! ");

        return 0;

    }

     

    void key_hw_init()

    {

        unsigned int *gpio_config;

        unsigned short data;

        gpio_config = ioremap(GPNCON,4);//物理地址转为虚拟地址,//用gpio变量来接受。

        data = readw(gpio_config);//因为是16位的,所以用readw这个//函数来读出里面的值。

        data &= ~0b11;//首先把最低两位设置为00

        data |= 0b10;//最低的两位设置成10

        writew(data,gpio_config);//把data的值写到寄存器里。这样,硬件的初始化就完成了。接下来就是按键中断的处理。首先是实现中断处理函数:irqreturn_t.然后注册中断处理程序。

    }

    int key_open(struct inode *node, struct file *filp)

    {

        return 0;

    }

    struct file_operations key_fops =

    {

        .open = key_open,

    };

    struct miscdevice key_miscdevice =

    {

        .minor = 200,

        .name = "OK6410key",

        .fops = &key_fops,

    };

     

    static int key_init()

    {

        misc_register(&key_miscdevice);

        //硬件初始化

        key_hw_init();

        //注册中断处理程序

        request_irq(IRQ_EINT(0),key_int, IRQF_TRIGGER_FALLING,"OK6410key",0);

        

        return 0;

    }

    /*按键中断的处理,对于按键而言,可以在按下的时候产生中断,也可以在弹起的时候产生中断。需要通过一个标志来指定:IRQF_TRIGGER_FALLING,这个是从高电平到低电平产生中断。下表是其他产生中断的方式:*/

    接下来是中断号的确定,就是request_irq函数的第一个参数。我们在内核代码中搜索irqs.h,找对应的板子的。我的是6410的。

    从上面的代码看到,IRQ_EINT0_3的中断号是32.系统留出了S3C_IRQ_OFFSET=32个中断号,这是给软中断的。所以中断号就是等于硬件编号加上偏移量。可以查看内核代码的entry-macro.S

    static void key_exit()

    {

        misc_deregister(&key_miscdevice);

        

    }

     

    module_init(key_init);

    module_exit(key_exit);

    Makefile:

    obj-m := key.o

    KDIR := /home/samba/linux-ok6410

    all :

        make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-

    进行make编译完成,生成key.ko,拷贝到板子运行。在OK6410的终端看不到输出的效果。一个同伴说,这很可能是按键被屏幕占用的原因,所以,他把我的驱动程序拷贝到他的板子,装的是NFS的,屏幕是黑的,安装驱动,按下按键,真的在终端输出了key down。所以我就假定了真的是屏幕的原因。

    下面是他改进的,按键驱动控制灯的程序key.c:

    #include <linux/init.h>

    #include <linux/module.h>

    #include <linux/fs.h>

    #include <linux/io.h>

    #include <linux/miscdevice.h>

    #include <linux/interrupt.h>

     

     

    #define GPNCON 0x7F008830

    #define VIC0INTENABLE 0x71200010

    #define LEDCON 0x7F008820

    #define LEDDAT 0x7F008824

    unsigned int *ledcon;

    unsigned int *leddata;

    unsigned int *vic0intenable;

    unsigned int *gpncon;

    unsigned int data;

     

    int key_int(int irq, void *dev_id)

    {

        data = readl(leddata);

        data +=1;

        writel(data,leddata);

        

        return 0;

    }

     

    int key_open (struct inode *node, struct file *filp)

    {

        return 0;

    }

     

     

    struct file_operations key_ops =

    {

        .open = key_open,

        

    };

     

    struct miscdevice key =

    {

        .minor = 200,

        .name = "key",

        .fops = &key_ops

    };

     

    void key_hw_init()

    {

        gpncon = ioremap(GPNCON,4);

        vic0intenable = ioremap(VIC0INTENABLE,4);

        data = readl(gpncon);

        data &= ~0b11;

        data |= 0b10;

        writel(data,gpncon);

        

        data = readl(vic0intenable);

        data |= 0b1;

        writel(data,vic0intenable);

          

        

    }

    static int key_init()

    {

        misc_register(&key);

        

        request_irq(IRQ_EINT(0),key_int,IRQF_TRIGGER_FALLING,"key",0);

        

        key_hw_init();

        

        ledcon = ioremap(LEDCON,4);

        writel(0x1111,ledcon);

        leddata = ioremap(LEDDAT,4);

        writel(0,leddata);

        

        return 0;

    }

     

    static void key_exit()

    {

        misc_deregister(&key);

        free_irq(IRQ_EINT(0),0);

    }

    MODULE_LICENSE("GPL");

    module_init(key_init);

    module_exit(key_exit);

     

     

    下面的这个例子是通过应用程序来测试按键驱动程序的:

    Buttons.c:

    #include <linux/module.h>        /* For module specific items */

    #include <linux/moduleparam.h>        /* For new moduleparam's */

    #include <linux/types.h>        /* For standard types (like size_t) */

    #include <linux/errno.h>        /* For the -ENODEV/... values */

    #include <linux/kernel.h>        /* For printk/panic/... */

    #include <linux/fs.h>            /* For file operations */

    #include <linux/ioport.h>        /* For io-port access */

    #include<linux/platform_device.h>/*Forplatform_driverframework */

    #include <linux/init.h>            /* For __init/__exit/... */

    #include <linux/uaccess.h>        /* For copy_to_user/put_user/... */

    #include <linux/io.h>            /* For inb/outb/... */

     

    #define GPNCON_PA 0x7F008830

    static int major = 0;

    static struct class *buttons_class;

    static volatile unsigned long *gpncon;

    static volatile unsigned long *gpndat;

     

    int buttons_open(struct inode *inode, struct file *file)

    {

        /* configure gpio as input */

        printk("%s %d ", __FUNCTION__, __LINE__);

        *gpncon &= ~(0xfff);

        return 0;

    }

     

    ssize_t buttons_read(struct file *file, char __user *buf, size_t size, loff_t *offset)

    {

        char ker_buf[6];

        int i;

     

        if (size != 6)

        {

            printk("%s %d ", __FUNCTION__, __LINE__);

            return -EINVAL;

        }

     

        for (i = 0; i < 6; i++)

        {

            ker_buf[i] = ((*gpndat) >> i) & 1;

        }

     

        //printk("%s %d ", __FUNCTION__, __LINE__);

     

        copy_to_user(buf, ker_buf, 6);

        

        return 6;

    }

     

    static struct file_operations buttons_fops = {

        .owner = THIS_MODULE,

        .open = buttons_open,

        .read = buttons_read,

    };

     

    int buttons_init(void)

    {

          

        

        major = register_chrdev(0, "buttons", &buttons_fops);

     

        // mdev

        buttons_class = class_create(THIS_MODULE, "buttons"); /* sysfs */

        device_create(buttons_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */

        

        gpncon = ioremap(GPNCON_PA, 8);

        gpndat = gpncon + 1;

        

        return 0;

    }

     

    void buttons_exit(void)

    {

        unregister_chrdev(major, "buttons");

     

        device_destroy(buttons_class, MKDEV(major, 0));

        class_destroy(buttons_class);

    }

     

    module_init(buttons_init);

    module_exit(buttons_exit);

     

    MODULE_LICENSE("GPL");

     

    下面是测试应用程序:buttons_test.c:

     

    #include <stdio.h>

    #include <sys/types.h>

    #include <sys/stat.h>

    #include <fcntl.h>

    #include <string.h>

    int main(int argc, char **argv)

    {

        int fd;

        char buf[6];

        char pre_buf[6];

        int len;

        int i;

     

        fd = open("/dev/xyz", O_RDWR);

     

        if (fd < 0)

        {

            printf("can't open /dev/xyz ");

            return -1;

        }

     

        while (1)

        {

            len = read(fd, buf, 6);

            for (i = 0; i < 6; i++)        

            {

                if (pre_buf[i] != buf[i])

                {

                    printf("K%d %s ", i+1, buf[i] ? "released" : "pressed");

                }

            }

              

            memcpy(pre_buf, buf, 6);            

        }

     

        return 0;

    }

     

     

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    EditPlus 4.3.2502 中文版已经发布(12月5日更新)
    EditPlus 4.3.2499 中文版已经发布(11月21日更新)
    为什么有负频率,什么是相位谱 —— 关于傅立叶变换的随笔
    对“善于提问,主动解决问题”的程序员的吐槽
    .NET事件监听机制的局限与扩展
    .NET陷阱之六:从枚举值持久化带来大量空间消耗谈起
    深入挖掘.NET序列化机制——实现更易用的序列化方案
    在VMWare中建立Hadoop虚拟集群的详细步骤(使用CentOS)
    微信分享
    如何导入ShareSDK的sample
  • 原文地址:https://www.cnblogs.com/FORFISH/p/5188559.html
Copyright © 2011-2022 走看看