驱动代码如下:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/input.h>
#include <linux/jiffies.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <linux/timer.h>#include <asm/irq.h>
#include <asm/io.h>
#include <asm/uaccess.h>#include <mach/hardware.h>//按键映射
//KEY1   -   GPIO6_1
//KEY2   -   GPIO5_7
//KEY3   -   GPIO5_5#define MY_KEY1 1
#define MY_KEY2 2#define WRITE_REG(Addr, Value) ((*(volatile unsigned int *)(Addr)) = (Value))
#define READ_REG(Addr)         (*(volatile unsigned int *)(Addr))//Hi3531复用寄存器基地址
#define MUXCTRL_BASE_ADDR 0x200F0000
//Hi3531 GPIO5基地址
#define GPIO_0_BASE_ADDR  0x20140000//方向控制寄存器,配置输入或输出
#define GPIO_DIR_OFFSET_ADDR    0x400
//中断触发寄存器,配置边沿或电平触发
#define GPIO_IS_OFFSET_ADDR     0x404
//双沿触发中断寄存器,配置单边沿或双边沿触发方式
#define GPIO_IBE_OFFSET_ADDR    0x408
//触发中断条件寄存器,配置下降沿/低电平或上升沿/高电平触发
#define GPIO_IEV_OFFSET_ADDR    0x40C
//中断屏蔽寄存器,用来屏蔽或使能中断
#define GPIO_IE_OFFSET_ADDR     0x410
//原始中断状态寄存器,用来查询 GPIO 管脚是否发生中断(0:未发生,1:发生)
#define GPIO_RIS_OFFSET_ADDR    0x414
//屏蔽状态中断寄存器,用来查询 GPIO 管脚屏蔽后的中断是否有效
#define GPIO_MIS_OFFSET_ADDR    0x418
//中断清除寄存器,用来清除管脚产生的中断,同时清除GPIO_RIS和GPIO_MIS
#define GPIO_IC_OFFSET_ADDR     0x41Cunsigned int muxctrl_virtual_addr = 0;
unsigned int gpio_0_virtual_addr = 0;//定义一个结构体用来对输入按键进行描述
struct my_buttons_desc {int gpio;         // 表示对应的按键引脚int irq;          // 表示对应的中断位char *name;       // 表示对应的按键请求中断时的中断名int  key_code;    // 表示按键在输入子系统中对应的键值
};//定义一个描述按键的数组
//根据hisi SDK文档得知:GPIO6的中断号是111,GPIO5的中断号是110
static struct my_buttons_desc buttons_desc[] = {{MY_KEY1, 31, "my_buttons_A", KEY_A},{MY_KEY2, 31, "my_buttons_B", KEY_B},
};//定义一个输入子系统的结构体指针变量
static struct input_dev *buttons_dev;
static struct my_buttons_desc *irq_buttons_desc = NULL;
static struct timer_list buttons_timer;//地址映射
static int hi3531_virtual_addr_map(void)
{muxctrl_virtual_addr = (unsigned int)ioremap_nocache(MUXCTRL_BASE_ADDR, 0x10000);if(!muxctrl_virtual_addr){printk("MUXCTRL_BASE_ADDR ioremap addr failed !\n");return -1;}gpio_0_virtual_addr = (unsigned int)ioremap_nocache(GPIO_0_BASE_ADDR, 0x10000);if(!gpio_0_virtual_addr){printk("GPIO_0_BASE_ADDR ioremap addr failed !\n");return -1;}return 0;
}//取消地址映射
static void hi3531_virtual_addr_unmap(void)
{iounmap((void*)muxctrl_virtual_addr);iounmap((void*)gpio_0_virtual_addr);
}//海思官方提供的中断操作
//如果要产生中断,且避免假中断,则必须按照下面的初始化顺序:
//1. 配置 GPIO_IS,选择边沿触发或电平触发。
//2. 配置 GPIO_IEV,选择下降沿/上升沿触发和高电平/低电平触发。
//3. 如果选择边沿触发,需配置 GPIO_IBE,选择单沿或双沿触发方式。
//4. 保证 GPIO 数据线在以上操作过程中保持稳定。
//5. 向寄存器 GPIO_IC 写 0xFF,清中断。
//6. 配置 GPIO_IE 为 1,使能中断。
static int hi3531_button_gpio_config(void)
{unsigned int u32Reg = 0;//配置为gpioWRITE_REG(muxctrl_virtual_addr + 0xC4, 0x1);//KEY1   -   GPIO6_1WRITE_REG(muxctrl_virtual_addr + 0xBC, 0x1);//KEY3   -   GPIO5_7//配置为输入u32Reg = READ_REG(gpio_0_virtual_addr + GPIO_DIR_OFFSET_ADDR);u32Reg &= (~0x06);WRITE_REG(gpio_0_virtual_addr + GPIO_DIR_OFFSET_ADDR, u32Reg);//GPIO5_7//配置中断u32Reg = READ_REG(gpio_0_virtual_addr + GPIO_IS_OFFSET_ADDR);u32Reg &= (~0x06);WRITE_REG(gpio_0_virtual_addr + GPIO_IS_OFFSET_ADDR, u32Reg); //GPIO5_7,GPIO5_5: 边沿触发中断u32Reg = READ_REG(gpio_0_virtual_addr + GPIO_IBE_OFFSET_ADDR);u32Reg |= (0x06);WRITE_REG(gpio_0_virtual_addr + GPIO_IBE_OFFSET_ADDR, u32Reg); //GPIO5_7,GPIO5_5: 双边沿触发中断WRITE_REG(gpio_0_virtual_addr + GPIO_IC_OFFSET_ADDR, 0xFF); //GPIO5: 清除中断u32Reg = READ_REG(gpio_0_virtual_addr + GPIO_IE_OFFSET_ADDR);u32Reg |= (0x06);WRITE_REG(gpio_0_virtual_addr + GPIO_IE_OFFSET_ADDR, u32Reg); //GPIO5_7,GPIO5_5: 使能中断return 0;
}//GPIO按键中断处理函数
static irqreturn_t my_buttons_irq(int irq, void *dev_id)
{unsigned int u32Reg = 0;struct my_buttons_desc *tmp_desc = (struct my_buttons_desc *)dev_id;//因为是一组GPIO(8个pin)共享一个中断号,所以这里一开始就要判断到底是哪个中断来了//通过读中断状态寄存器来判断if(tmp_desc->gpio == MY_KEY1){u32Reg = READ_REG(gpio_0_virtual_addr + GPIO_RIS_OFFSET_ADDR);if(!(u32Reg & 0x02)) //GPIO6_1{//MY_KEY1 interrupt not happenedreturn IRQ_HANDLED;}WRITE_REG(gpio_0_virtual_addr + GPIO_IC_OFFSET_ADDR, 0xFF); //GPIO6: 清除中断}else if(tmp_desc->gpio == MY_KEY2){u32Reg = READ_REG(gpio_0_virtual_addr + GPIO_RIS_OFFSET_ADDR);if(!(u32Reg & 0x04)) //GPIO5_7{//MY_KEY2 interrupt not happenedreturn IRQ_HANDLED;}WRITE_REG(gpio_0_virtual_addr + GPIO_IC_OFFSET_ADDR, 0xFF);//GPIO5: 清除中断}//按键IO发生边沿中断时重新设置定时间隔,用于按键消抖//20ms之后触发定时器中断,执行my_buttons_timer_function(),并将buttons_timer.data传过去irq_buttons_desc = (struct my_buttons_desc *)dev_id;buttons_timer.data = irq_buttons_desc->gpio;mod_timer(&buttons_timer, jiffies+msecs_to_jiffies(20));return IRQ_HANDLED;
}static unsigned int my_buttons_read_gpio(unsigned int gpio)
{unsigned int gpio_level = 0;switch(gpio){case MY_KEY1:gpio_level = READ_REG(gpio_0_virtual_addr + (0x02 << 2)); //GPIO6_1gpio_level = gpio_level >> 1;break;case MY_KEY2:gpio_level = READ_REG(gpio_0_virtual_addr + (0x04 << 2)); //GPIO5_7gpio_level = gpio_level >> 2;break;default:break;}return gpio_level;
}//定时器中断处理函数
static void my_buttons_timer_function(unsigned long data)
{unsigned int gpio_level;if (!irq_buttons_desc){// 初始化定时器会走进该function一次printk("irq_buttons_desc == NULL, return\n");return;}//获取按键IO状态gpio_level = my_buttons_read_gpio((unsigned int)data);
//    printk("my_buttons_timer_function: gpio = %ld, gpio_level = %d\n", data, gpio_level);//根据按键IO状态上报按键事件if (gpio_level){//上报按键弹起input_event(buttons_dev, EV_KEY, irq_buttons_desc->key_code, 0);input_sync(buttons_dev);}else{//上报按键按下input_event(buttons_dev, EV_KEY, irq_buttons_desc->key_code, 1);input_sync(buttons_dev);}
}//入口函数
static int __init my_buttons_init(void)
{int i = 0;int ret = 0;printk("my_buttons_init start\n");//1、分配一个input_dev结构体buttons_dev = input_allocate_device();if(!buttons_dev){printk("input_allocate_device error!\n");return -ENOMEM;}//2、设置input_dev结构体//2.1、设置支持的事件类型set_bit(EV_KEY, buttons_dev->evbit);set_bit(EV_REP, buttons_dev->evbit); //支持长按//2.2、设置支持该类事件中的事件码for(i = 0; i < sizeof(buttons_desc)/sizeof(buttons_desc[0]); i++){set_bit(buttons_desc[i].key_code, buttons_dev->keybit);}//2.3、硬件相关的操作hi3531_virtual_addr_map();hi3531_button_gpio_config();//3、中断相关的操作//为每个按键申请一个中断,共用中断处理函数my_buttons_irq()//按键触发方式为双边沿触发for(i = 0; i < sizeof(buttons_desc)/sizeof(buttons_desc[0]); i++){//Hi3531的一组GPIO只有一个中断号,一组GPIO有8个pin,所以这里得是共享中断ret = request_irq(buttons_desc[i].irq, my_buttons_irq, IRQF_SHARED, buttons_desc[i].name, (void*)&buttons_desc[i]);printk("request_irq %s\n", ret==0?"succeed":"failed");}//4、注册input_dev结构体input_register_device(buttons_dev);//初始化定时器,用于按键消抖init_timer(&buttons_timer);buttons_timer.function = my_buttons_timer_function;add_timer(&buttons_timer);printk("my_buttons_init end\n");return 0;
}//出口函数
static void __exit my_buttons_exit(void)
{int i;printk("my_buttons_exit start\n");hi3531_virtual_addr_unmap();//释放申请的按键中断for(i = 0; i < sizeof(buttons_desc)/sizeof(buttons_desc[0]); i++){free_irq(buttons_desc[i].irq, (void*)&buttons_desc[i]);}//删除定时器del_timer(&buttons_timer);//注销输入设备input_unregister_device(buttons_dev);//释放输入设备内存空间input_free_device(buttons_dev);printk("my_buttons_exit end\n");
}module_init(my_buttons_init);
module_exit(my_buttons_exit);
MODULE_LICENSE("GPL");

使用Makefil编译成模块,为了防止自己忘记,Makefile也贴出来

ifeq ($(PARAM_FILE), )PARAM_FILE:=../../Makefile.paraminclude $(PARAM_FILE)
endifobj-m := scan_key.o
hi_scan_key-y := scan_key.oEXTRA_CFLAGS += -I$(REL_INC)
EXTRA_CFLAGS += $(DRV_CFLAGS)
all:make -C $(LINUX_ROOT) M=$(PWD) modulesclean:@make -C $(LINUX_ROOT) M=$(PWD) clean

将生成的scan_key.ko,在开发板上insmod scan_key.ko,模块就可以使用了。

下面是应用代码:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>#include <linux/input.h>// 定义长按键的TICK数, 以及连发间隔的TICK数, 单位是毫秒ms
//#define KEY_DEBOUNCE_PERIOD     20   // 延时消抖已经在kernel驱动做了,应用层不需要做了
#define KEY_LONG_PERIOD         1000
#define KEY_CONTINUE_PERIOD     200#define TRUE    1
#define FALSE   0typedef unsigned int Bool;
typedef void (*pf)(void);typedef enum
{APP_KEY1 = 0,APP_KEY2,APP_KEY_MAX_NUM
} KEY_NUM_E;typedef enum {APP_KEY_STATE_INIT = 0,APP_KEY_STATE_WOBBLE,APP_KEY_STATE_PRESS,APP_KEY_STATE_LONG,APP_KEY_STATE_CONTINUE,APP_KEY_STATE_RELEASE
} KEY_STATE_E;void key1DownAction(void)
{printf("%s\n", __FUNCTION__);
}void key1LongAction(void)
{printf("%s\n", __FUNCTION__);
}void key1ContinueAction(void)
{printf("%s\n", __FUNCTION__);
}void key1DownUpAction(void)
{printf("%s\n", __FUNCTION__);
}void key1LongUpAction(void)
{printf("%s\n", __FUNCTION__);
}void key2DownAction(void)
{printf("%s\n", __FUNCTION__);
}void key2LongAction(void)
{printf("%s\n", __FUNCTION__);
}void key2ContinueAction(void)
{printf("%s\n", __FUNCTION__);
}void key2DownUpAction(void)
{printf("%s\n", __FUNCTION__);
}void key2LongUpAction(void)
{printf("%s\n", __FUNCTION__);
}typedef struct
{KEY_NUM_E eKeyId;unsigned int downTick;unsigned int upTick;KEY_STATE_E eKeyCurState;      //key cur state(fsm)Bool bStateChangedFlag;        //state changed flagpf keyDownAction;pf keyLongAction;pf keyContinueAction;pf keyDownUpAction;pf keyLongUpAction;
} KEY_HANDLE_T;KEY_HANDLE_T keyList[APP_KEY_MAX_NUM] =
{{APP_KEY1, 0, 0, APP_KEY_STATE_INIT, FALSE, key1DownAction, key1LongAction, key1ContinueAction, key1DownUpAction, key1LongUpAction},{APP_KEY2, 0, 0, APP_KEY_STATE_INIT, FALSE, key2DownAction, key2LongAction, key2ContinueAction, key2DownUpAction, key2LongUpAction},
};void keyScan(unsigned long cur_ms, int value, KEY_HANDLE_T *key)
{if(key == NULL){printf("key == NULL, return\n");return;}switch(key->eKeyCurState){case APP_KEY_STATE_INIT:if(value == 1){key->downTick = cur_ms;key->keyDownAction(); //短按}if(value){if((cur_ms - key->downTick) >= KEY_LONG_PERIOD){key->bStateChangedFlag = TRUE;key->eKeyCurState = APP_KEY_STATE_LONG;}}else{key->upTick = cur_ms;key->bStateChangedFlag = TRUE;key->eKeyCurState = APP_KEY_STATE_INIT;key->keyDownUpAction(); //短按抬起}break;case APP_KEY_STATE_LONG:if(TRUE == key->bStateChangedFlag){key->bStateChangedFlag = FALSE;key->keyLongAction(); //长按}if(value){if((cur_ms - key->downTick) >= (KEY_LONG_PERIOD + KEY_CONTINUE_PERIOD)){key->downTick = cur_ms;key->bStateChangedFlag = TRUE;key->eKeyCurState = APP_KEY_STATE_CONTINUE;}}else{key->upTick = cur_ms;key->bStateChangedFlag = TRUE;key->eKeyCurState = APP_KEY_STATE_INIT;key->keyLongUpAction(); //长按抬起}break;case APP_KEY_STATE_CONTINUE:if(TRUE == key->bStateChangedFlag){key->bStateChangedFlag = FALSE;key->keyContinueAction(); //连发}if(value){if((cur_ms - key->downTick) >= KEY_CONTINUE_PERIOD){key->downTick = cur_ms;key->bStateChangedFlag = TRUE;key->eKeyCurState = APP_KEY_STATE_CONTINUE;}}else{key->upTick = cur_ms;key->bStateChangedFlag = TRUE;key->eKeyCurState = APP_KEY_STATE_INIT;key->keyLongUpAction(); //长按抬起}break;default:break;}
}/*
struct input_event {struct timeval time;__u16 type; //EV_SYN=0x00,EV_KEY=0x01__u16 code; //KEY_A,KEY_B,KEY_C__s32 value;//抬起=0,按下=1,长按=2
};
*/int scan_key_main()
{int fd = 0;struct input_event buttons_event;KEY_HANDLE_T *curKey = NULL;unsigned long cur_ms = 0;//fd = open("/dev/event0", O_RDWR | O_NONBLOCK);fd = open("/dev/input/event0", O_RDWR);if (fd < 0){printf("can't open!\n");return -1;}while (1){read(fd, &buttons_event, sizeof(struct input_event));if(buttons_event.type == EV_SYN)continue;cur_ms = (buttons_event.time.tv_sec * 1000) + (buttons_event.time.tv_usec/1000);//打印时间,事件类型,事件码,事件值printf("cur_ms:%ld type:0x%x code:%d value:%d\n",cur_ms,buttons_event.type,buttons_event.code,buttons_event.value);// match key structswitch(buttons_event.code){case KEY_A:curKey = &keyList[APP_KEY1];break;case KEY_B:curKey = &keyList[APP_KEY2];break;default:curKey = NULL;break;}keyScan(cur_ms, buttons_event.value, curKey);}return 0;
}

在主代码中,使用一个线程专门用来检测按键:pthread_create(&key_ThreadId, NULL, scan_key_main, NULL);

当产生按键事件后,只设置一个变量

void key1DownAction(void)
{
    global_step_record = 1;
    printf("%s\n", __FUNCTION__);
}
 
void key1LongAction(void)
{
    global_step_record = 0;
    printf("%s\n", __FUNCTION__);
}

HI3518E按键驱动和应用代码相关推荐

  1. 按键驱动的思路分析和代码实现

    文章目录 1 按键驱动的思路分析 2 按键驱动的代码实现 1 按键驱动的思路分析 为了方便实现低功耗,我们的按键需要使用外部中断实现,实现的流程如下: 按键检测状态机如下: 2 按键驱动的代码实现

  2. 飞凌 ok6410 按键驱动源码及测试代码

    2019独角兽企业重金招聘Python工程师标准>>> 由于OK6410的GPIO按键中断已经被飞凌自带的按键驱动注册,所以运行我们编写的按键驱动前要先去掉飞凌自带的按键驱动,方法: ...

  3. linux 按键驱动代码分析

    原文地址:http://blog.csdn.NET/woshidahuaidan2011/article/details/51695147 二.按键驱动 1.对按键驱动添加设备信息 linux-3.1 ...

  4. S3C6410开发板按键驱动代码分析及测试代码分析

    在本文中,我们对S3C6410开发板按键驱动代码的实现过程进行分析,然后通过一个实例对按键功能进行测试.在本文的资源中包含了设备驱动的源码和测试的源码. 一.设备驱动源码分析 按键的设备驱动主要实现了 ...

  5. WINCE 按键驱动编写

    该按键驱动摘自WINCE的FM1702读写器中的按键代码,按键中断的触发将会通过发送消息传递到标题为"FM1702测试界面[wenziqi@hotmail.com]"的对话框中. ...

  6. linux按键驱动中的结构体,linux 驱动之input子系统(gpio-keys)实现

    1.概述 Gpio-keys 是基于input子系统实现的一个通用按键驱动,该驱动也符合linux驱动实现模型,即driver和device分离模型.一般按键驱动,都是基于gpio-keys进行开发的 ...

  7. linux下被遗忘的gpio_keys按键驱动

    我们新项目硬件设计上使用gpio口做按键,所以我就需要搞定这个驱动,本来想自己写一个gpio口的按键驱动,然后看了下内核下面的代码,已经有现成的了.Linux内核下游很多很多的现成驱动,只要你想得到的 ...

  8. 关于《详解》第12.1节按键驱动的进一步阐述

    <详解>12.1的按键驱动,是本书的最大失误,应大家的要求,我们很有必要对其进行再次阐述.          注意标题是"按键"驱动而非"键盘"驱动 ...

  9. linux内核按键驱动,嵌入式Linux按键驱动框架

    前言 本文将通过轮询.中断.poll机制.异步通知和同步互斥阻塞等方式编写按键驱动程序.本节的驱动框架是在<嵌入式Linux驱动框架的搭建>的基础上进行改进的,所以本文只讲解修改的部分. ...

最新文章

  1. iOS 设置View阴影
  2. 卸载linux系统装win,如何在计算机上删除 Linux 并安装 Windows
  3. Jenkins+Git+Maven持续集成经典教程
  4. excel打印预览在哪里_打印小心机:轮页眉页脚的重要性
  5. 数塔(hdoj 2084,动态规划递推)
  6. 启动T0运行的C语言语句是,单片机填空题期末复习。、
  7. 使用Redis bitmaps进行快速、简单、实时统计
  8. Hello Blazor:(8)启用深色模式
  9. 存储过程与函数oracle
  10. Emlog文章特色图生成插件分享
  11. (90)FPGA仿真计数器激励
  12. opencv 离线文档下载地址在哪里?
  13. sqlserver200864位下载_Microsoft SQL Server 2008 R2 官方简体中文正式版下载
  14. 简单说说路由器和交换机的区别
  15. 如何在Word文档中加入水印
  16. 微信公众号检测本地App, 安装则打开,未安装则下载的实现方式
  17. 利用Python解决掉谷歌人机验证,全自动识别真的牛啊
  18. 关于《十天学会AVR单片机》的教程头文件AVR_PQ1A.h
  19. 【流媒体性能测试常用指标】
  20. 【BZOJ4379】[POI2015]Modernizacja autostrady 树形DP

热门文章

  1. TiDB中的混沌测试实践
  2. Manjaro 21安装搜狗输入法
  3. 文化娱乐免费的经典PPT模板哪里可以下载?经典大气PPT模板大全
  4. html抽奖原理,js实现抽奖功能
  5. 变电站/配电房智能轨道巡检机器人系统
  6. Vue3 移动端浏览器底部工具栏挡住部分网页,以及ios网页放大滑动问题。
  7. ad safe5.0.630.9901懂你版补丁包
  8. SD 卡卡座封装尺寸以及接线图 (带自弹 )
  9. OSI7层模型数据分层传输的过程
  10. Kotlin的型变解析(协变、逆变和不变)