Hi3515按键中断驱动程序

摸索了一个星期,终于把海思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 !\n");
        return -1;
    }
     gpio3_virtual_addr = (unsigned int)ioremap_nocache(0x20180000,0x4000);
    if(!gpio3_virtual_addr)
        {
            printk("0x20180000 ioremap addr failed !\n");
            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! !\n");
            return -1;
        }
    printk("register device success !\n");         
    return 0;   
}

static void __exit button_exit(void)
{
    misc_deregister(&misc);
    printk("unregister device success !\n");  
}

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!\n");
            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!\n");
                led1 = (~led1)&0x1;
                ioctl(fd, 0x01, &led1);    /*切换led1的状态*/
            }
        if(key == 8)/*按键2被按下*/
            {
                printf("K2 is press!\n");
                led2 = (~led2)&0x1;
                ioctl(fd, 0x02, &led2);    /*切换led2的状态*/
            }
            //printf("\n");
    }   
    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
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值