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!\n");

    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,这个是从高电平到低电平产生中断。下表是其他产生中断的方式:*/

3.按键驱动硬件操作_第1张图片

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

3.按键驱动硬件操作_第2张图片

从上面的代码看到,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\n", __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\n", __FUNCTION__, __LINE__);

        return -EINVAL;

    }

 

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

    {

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

    }

 

    //printk("%s %d\n", __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\n");

        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\n", i+1, buf[i] ? "released" : "pressed");

            }

        }

          

        memcpy(pre_buf, buf, 6);            

    }

 

    return 0;

}

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(3.按键驱动硬件操作)