• 嵌入式开发之hi3519---GPIO 按键驱动


    摸索了一个星期,终于把海思HI3515开发板的按键中断程序搞出来了,hi3515的核心芯片与网上例子较多的s3c之类的有一些区别,以至于浪费了好些时间去琢磨。管脚配置方式不一样,中断的使用情况也不一样。而比较麻烦的是网上关于海思的资料太少了。对于水平不太高的人,老自己摸索还是会走不少弯路。现在就把本人写的能在开饭板测试运行通过的程序贴出来,但愿这个不会违反到保密协议的内容,代码可都是我自己写的哈。希望能给后来者带来些帮助,也希望大家多提意见,一起进步·,^_^

     第一步,编写按键驱动程序,button.c代码如下:

    /*所有模块都需要的头文件*/

    #include<linux/module.h>

    /*声明printk()这个内核态的函数*/

    #include<linux/kernel.h>

    /*文件系统有关的,结构体file_operations也在fs头文件定义*/

    #include<linux/fs.h>

    /*init和exit相关宏*/

    #include<linux/init.h>

    #include<linux/delay.h>

    #include<linux/poll.h>

    /*linux中断定义*/

    #include<linux/irq.h>

    /**/

    #include<asm/irq.h>

    /*包含与中断相关的大部分宏及结构体的定义,request_irq()等*/

    #include<linux/interrupt.h>

    /*linux中的用户态内存交互函数,copy_from_user(),copy_to_user()等*/

    #include<asm/uaccess.h>

    //#include<mach/regs-gpio.h>

    //#include<mach/hardware.h>

    #include<linux/platform_device.h>

    #include<linux/cdev.h>

    /*misc混合设备注册与注销*/

    #include<linux/miscdevice.h>

    #include <asm/io.h>

    #include <asm/system.h>

    #define BUTTON_READ   0x01

    #define DEVICE_NAME        "BUTTON_irq"

    #define REG_WRITE(addr,value)  ((*(volatile unsigned int *)(addr)) = (value))

    #define REG_READ(Addr)         (*(volatile unsigned int *)(Addr))

    static unsigned int gpio3_virtual_addr = 0;

    static unsigned int reg_virtual_addr = 0;

    /*数组中是否有数据标志,0表示无数据可读,1表示有数字可读*/

    static volatile char key;

    /*定义和初始化一个等待队列头*/

    static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

    /*定义一个整形变量,判断按键是否按下*/

    static volatile int ev_press = 0;

    /*

    *定义结构体类型,由它把按钮中断的信息综合起来

    */

    struct button_irq_desc {

        int irq;/*中断号*/

        int pin;/*中断标志寄存器,有中断产生时为1,无中断时为0*/

        int number;/*编号*/

        char *name;/*名称*/

        };

    static struct button_irq_desc button_irqs[]={

                {8,2,1,"KEY1"},

                };

    static void hi3515_button_pin_cfg(void)

        {

            /*配置作为普通输入*/

            REG_WRITE(reg_virtual_addr + 0x08,0x1);/*reg2管脚复用配置gpio3_0,按键1*/

            REG_WRITE(reg_virtual_addr + 0x0c,0x1);/*reg3管脚复用配置gpio3_1,按键1*/

            REG_WRITE(reg_virtual_addr + 0x10,0x1);/*reg4管脚复用配置gpio3_2,按键1*/

            REG_WRITE(reg_virtual_addr + 0x14,0x1);/*reg5管脚复用配置gpio3_3,按键2*/

            /*管脚中断配置*/

            REG_WRITE(gpio3_virtual_addr + 0x0400,0x3);/*dir设置管脚为0-1:输出,2-3:输入*/

            REG_WRITE(gpio3_virtual_addr + 0x0404,0xc);/*is边沿触发中断*/

            REG_WRITE(gpio3_virtual_addr + 0x040c,0x0);/*iev低电平触发*/

            REG_WRITE(gpio3_virtual_addr + 0x041c,0xff);/*ic清除中断*/

            REG_WRITE(gpio3_virtual_addr + 0x0410,0x04);/*ie启用中断*/

        }

    /*

    *gpio地址映射

    */

    static int virtual_addr_map(void)

        {

        reg_virtual_addr = (unsigned int)ioremap_nocache(0x200f0000,0x4000);

        if(!reg_virtual_addr)

        {

            printk("0x200f0000 ioremap addr failed ! ");

            return -1;

        }

         gpio3_virtual_addr = (unsigned int)ioremap_nocache(0x20180000,0x4000);

        if(!gpio3_virtual_addr)

            {

                printk("0x20180000 ioremap addr failed ! ");

                return -1;

            }

        }

    /*取消地址映射*/   

    static void virtual_addr_unmap(void)

        {

            iounmap((void*)gpio3_virtual_addr);

            iounmap((void*)reg_virtual_addr);

        }

    /*

    *read调用的具体函数,由它读取键盘输入的结果,

    *实质上就是读取key_values数组的值

    *完成键盘输入设备的核心功能,根据标志位ev_press判断是否可读

    *如果可读,则读取数据到用户buffer中,如果不可读,

    *则进程进入等待队列等待,直到数组可读为止

    *等待队列机制,所中断管理中常用的机制。

    */

    static int button_irq_read(struct file *filp,

                                char __user *buff,

                                size_t count,loff_t *offp)

    {

        unsigned long err;

    #if 1

        if(!ev_press) /*ev_press=0,则表示没有数据可读*/

        {

            if(filp->f_flags & O_NONBLOCK)

                return -EAGAIN;

            else    /*无数据可读时,进程休眠,放进button_waitq等待队列*/

                wait_event_interruptible(button_waitq,ev_press);

                /*

                *wait_event_interruptible()函数将进程置为可中断的挂起状态

                *反复检查ev_press=1是否成立,如果不成立,则继续休眠。

                *条件满足后,即把本程序置为运行态,

                */

        }

        /*ev_press=1之后,进程退出等待队列。从此处开始运行*/

        ev_press = 0;/*置0标志位,表明本次中断已经处理*/

        err = copy_to_user(buff,&key,sizeof(key));

        /*把按键值传会用户空间*/

    #endif

        return 0;

    }

    static irqreturn_t irq_interrupt(int irq,void *dev_id)

    {   

    #if 1

        /*对传入的中断资源进行处理,获得中断控制寄存器的值(即是否有数据)

            取反后赋值给down,为0时说明有数据,

            注意按下依次按钮有两次中断,

            对数组可读标志位进行设置,ev_press=1表示数组已经可以读了*/

        //struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;

        int down;

       

        down = 0x0c & REG_READ(gpio3_virtual_addr + 0x0414);

       

        REG_WRITE(gpio3_virtual_addr + 0x041c,0xff);/*ic清除按键中断*/

        mdelay(5);/**/

        if(down != 0x0c )

        {

            key = (char)down;

            ev_press = 1; /*置1标志位,唤醒等待队列进程,在read函数中使用*/

            /*

            *唤醒休眠的进程,用户空间程序使用调用read函数时,

            *如果没有产生中断,进程就会进入休眠状态,一直等待,直到产生中断

            *中断产生后,通过wake_up_interruptible()函数唤醒休眠进程

            */

            wake_up_interruptible(&button_waitq);

        }

    #endif   

        REG_WRITE(gpio3_virtual_addr + 0x0410,0x0c);/*ie启用按键中断*/

        return IRQ_HANDLED; //IRQ_HANDLED=1

    }

    /*

    *poll调用的具体函数,poll实质上是select的调用函数

    *如果有按键数据,则select会立刻返回

    *如果没有按键数据,则等待

    *实质上这是键盘等待输入的机制

    *poll_wait()会监测进程队列button_waitq里的进程

    *例如button_irq_read所在的进程的标志ev_press置为1了

    *那么就不再等待,这实质上就所select函数的运行机制

    */

    static unsigned int button_irq_poll(struct file *file,                                                                                                          struct poll_table_struct *wait)

    {

    #if 1

        /*

        *poll调用的具体函数,poll实质上是select的调用函数

        *如果有按键数据,则select会立刻返回

        *如果没有按键数据,则等待

        *实质上这是键盘等待输入的机制。

        *select调用是用户程序里面使用的。

        */

        unsigned int mask = 0;

        poll_wait(file,&button_waitq,wait);

            /*poll_wait会检测button_waitq里的进程*/

        if(ev_press)   

            mask |=POLLIN | POLLRDNORM;

        return mask;

    #endif

    }

    static int button_irq_open(struct inode *inode,struct file *file)

    {

    #if  1

        int err;/*中断注册返回值*/

        virtual_addr_map(); /*地址映射*/

        hi3515_button_pin_cfg();/*管脚配置,要先进行地址映射*/

        /*注册中断*/

            err = request_irq(8,irq_interrupt,IRQF_SHARED,

                                    "KEY",(void *)&button_irqs);

        if(err)/*如果注册中断失败,则释放已经成功注册的中断*/

        {   

            return -EBUSY;

        }

        ev_press = 1;

    #endif

        return 0;

    }

    static int button_irq_close(struct inode *inode,struct file *file)

    {   

        free_irq(8,(void *)&button_irqs);

        virtual_addr_unmap();/*取消地址映射*/

        return 0;

    }

    int button_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)

    {   

        unsigned int __user *argp = (unsigned int __user *)arg;

        int value;

        value = *(unsigned int *)arg;

    switch (cmd)

        {

            case 1:

                if(value == 0)    /*led1亮*/

                    REG_WRITE(gpio3_virtual_addr +0x4,0);

                else if(value == 1)    /*led1灭*/

                    REG_WRITE(gpio3_virtual_addr +0x4,1);

                break;

            case 2:

                 if(value == 0)    /*led2亮*/   

                    REG_WRITE(gpio3_virtual_addr +0x8,0);

                else if(value == 1)    /*led2灭*/

                    REG_WRITE(gpio3_virtual_addr +0x8,2);

                break;

            default:

                return -1;

        }

        return 0;

    }

    static struct file_operations dev_fops = {

        .owner = THIS_MODULE,

        .open      = button_irq_open,

        .release = button_irq_close,

        .ioctl   = button_ioctl,

        .read = button_irq_read,

        .poll = button_irq_poll,/*用户程序使用select调用的时候才会用到poll*/

    };

    /*

    *misc混合设备注册和注销

    */

    static struct miscdevice misc = {

        .minor = MISC_DYNAMIC_MINOR,/*次设备号*/

        .name = DEVICE_NAME,/*设备名*/

        .fops = &dev_fops,/*设备文件操作结构体*/

    };

    static int __init button_init(void)

    {

        int ret;

        ret = misc_register(&misc);

        if(0 != ret)

            {

                printk("register device failed! ! ");

                return -1;

            }

        printk("register device success ! ");         

        return 0;   

    }

    static void __exit button_exit(void)

    {

        misc_deregister(&misc);

        printk("unregister device success ! "); 

    }

    module_init(button_init);

    module_exit(button_exit);

    MODULE_LICENSE("GPL");

    MODULE_AUTHOR("Dong");

      第二步。,编写测试程序,test_button.c代码如下

     /*

    *按键中断测试程序

    *按键被按下时,产生中断

    *打印按下信息,切换led显示状态

    */

    #include <stdio.h>

    #include <ctype.h>

    #include <sys/ioctl.h>

    #include <sys/types.h>

    #include <sys/stat.h>

    #include <fcntl.h>

    #include<linux/delay.h>

    int main(int argc , char* argv[])

    {

        int fd = -1;

        unsigned int led1;

        unsigned int led2; ;

        char key;

        fd = open("/dev/BUTTON_irq", 0);

        if (fd<0)

            {

                printf("Open BUTTON_irq dev error! ");

                return -1;

            }

       

      for(;;)

        {

            int ret;

            ret = read(fd,&key,sizeof(key));

                if(ret< 0)

                    {

                        perror("read button:");

                        return -1;           

                    }

            if(key == 4)/*按键1被按下*/

                {

                    printf("K1 is press! ");

                    led1 = (~led1)&0x1;

                    ioctl(fd, 0x01, &led1);    /*切换led1的状态*/

                }

            if(key == 8)/*按键2被按下*/

                {

                    printf("K2 is press! ");

                    led2 = (~led2)&0x1;

                    ioctl(fd, 0x02, &led2);    /*切换led2的状态*/

                }

                //printf(" ");

        }  

        close(fd);

        return 0;

    }

    第三步,makefile文件,代码如下:

    LINUXROOT = /opt/Hi3515_SDK_V1.0.5.1/source/os/linux-2.6.24

    #这是放内核的路径

    CC = arm-hismall-linux-gcc

    obj-m := button.o

    default:

        $(CC) -g -Wall -o test_button test_button.c   

        @make -C $(LINUXROOT) M=$(PWD) modules

        rm -rf *.o *.mod.c *.symvers

    clean:

        @make -C $(LINUXROOT) M=$(PWD) clean

    http://blog.csdn.net/xiufu004/article/details/7198338 按键驱动

    http://blog.csdn.net/mao0514

  • 相关阅读:
    基本二叉搜索树的第K小元素
    sklearn常见分类器(二分类模板)
    python图论包networks(最短路,最小生成树带包)
    PAT 甲级 1030 Travel Plan (30 分)(dijstra,较简单,但要注意是从0到n-1)
    PAT 甲级 1029 Median (25 分)(思维题,找两个队列的中位数,没想到)*
    Oracle 10g ORA-12154: TNS: could not resolve the connect identifier specified 问题解决! 我同事遇到的问题。 username/
    JavaScritpt的DOM初探之Node(一)
    怎样实现动态加入布局文件(避免 The specified child already has a parent的问题)
    Ubuntu 14.04下单节点Ceph安装(by quqi99)
    卡片游戏
  • 原文地址:https://www.cnblogs.com/pengkunfan/p/7944723.html
Copyright © 2020-2023  润新知