飞凌嵌入式OK6410按键设备驱动的模型

飞凌嵌入式OK6410按键设备驱动的模型包含以下几个点
(1)混杂设备驱动模型
按键是一种混杂设备,也是一种字符设备。
(2)linux中断处理技术
按键通过产生中断来让驱动程序加以识别。
(3)按键驱动硬件操作实现
把按键对应的控制寄存器设定为中断模式,通过读取数据寄存器判断是否按键已经按下。
(4)中断分层处理
防止驱动阻塞,通过工作队列将相应的处理工作提交下半部。
(5)按键定时器去抖
防止一次按键产生多次中断,通过定时器延迟加以检测。
(6)驱动支持多按键优化
对多个按键进行硬件处理。
(7)阻塞型驱动设计
当用户态(app)读取或者写入不了数据给驱动层时,让其进入阻塞状态(sleep),当满足相应的条件时再将其唤醒。

硬件设定
key底板原理图
这里写图片描述

key核心板原理图
这里写图片描述

OK6410芯片手册
这里写图片描述

key驱动程序

/*Copyright (c) 2018 Caokaipeng. All rights reserved.*/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/slab.h>     //kmalloc函数需要
#include <asm/uaccess.h>   //copy_to_user函数需要
#include <linux/sched.h>

//定义key的控制寄存器
#define GPNCON 0x7f008830
//定义key的数据寄存器
#define GPNDAT 0x7f008834

//数据寄存器虚拟地址
unsigned int *key_data;
//按键编号
unsigned int key_num = 0;

//定义下半部工作
struct work_struct *work1;

//定义一个定时器
struct timer_list key_timer;

//定义一个等待队列
wait_queue_head_t key_wait_queue;

//work1具体执行的函数
void work1_func()
{
    //启动定时器
    mod_timer(&key_timer,jiffies + HZ/10);   //HZ为1秒
}

//定时器的超时函数
void key_timer_func(unsigned long data)
{
    //数据寄存器第0位的读取值
    unsigned int key1_val;
    //数据寄存器第1位的读取值
    unsigned int key2_val;

    //check key1是否仍然按下
    key1_val = readl(key_data)&0x01;
    if(0 == key1_val){   //按下为低电平
        key_num = 1;
    }
    else{
        //do nothing
    }

    //check key2是否仍然按下
    key2_val = readl(key_data)&0x02;
    if(0 == key2_val){   //按下为低电平
        key_num = 2;
    }
    else{
        //do nothing
    }

    //唤醒队列中进程
    wake_up(&key_wait_queue);
}

//中断处理函数
irqreturn_t key_interrupt(int irq, void *dev_id)
{
    //1.检测是否发生了按键中断
    //->没有采用共享中断,此步骤略

    //2.清除已经发生的按键中断
    //->处理器级别,cpu会进行清除,此步骤略

    //3.提交下半部工作到默认工作队列处理
    schedule_work(work1);

    return IRQ_HANDLED;
}

//硬件(key)初始化函数
void key_hw_init()
{ 
    //控制寄存器虚拟地址
    unsigned int *key_control;
    //控制寄存器的读取值
    unsigned int data;
    //物理地址转换成虚拟地址->控制寄存器地址
    key_control = ioremap(GPNCON,4); //4个字节
    //物理地址转换成虚拟地址->数据寄存器地址
    key_data = ioremap(GPNDAT,4); //4个字节
    //读取控制寄存器的值
    data = readl(key_control);
    //打印GPNCON寄存器设定前的值
    printk(KERN_WARNING"Set_before,GPNCON = 0x%x.\n",data);
    //对控制寄存器进行设定,设定GPF0为中断模式
    data &= ~0b1111;    //寄存器01位设定为00->KEY1对应GPN0,23位设定为00->KEY2对应GPN1
    data |= 0b1010;     //寄存器01设定为10,23位设定为10
    //打印GPNCON寄存器设定后的值
    printk(KERN_WARNING"Set_after,GPNCON = 0x%x.\n",data);
    writel(data,key_control);
}

//open设备操作
int key_open(struct inode *node, struct file *filp)
{   
    return 0;
}
//read设备操作
ssize_t key_read(struct file *filp, char __user *buf, size_t size, loff_t *pos)
{
    //判断是否进入等待队列,condition为key_num
    wait_event(key_wait_queue,key_num);
    //已经唤醒
    printk(KERN_WARNING"in kernel,key_num = %d.\n",key_num);
    //将数据copy to APP
    copy_to_user(buf,&key_num,4);
    //清空数据
    key_num = 0;

    return 4;
}
//定义并初始化file_operations
struct file_operations key_fops =
{
    .open = key_open,
    .read = key_read,
};

//定义并初始化miscdevice结构体
struct miscdevice key_miscdev =
{
    .minor = 200,
    .name = "key",
    .fops = &key_fops,
};

static int button_init()
{
    //注册miscdevice
    misc_register(&key_miscdev);
    //注册中断->GPN0对应中断号IRQ_EINT(0)
    request_irq(IRQ_EINT(0),key_interrupt,IRQF_TRIGGER_FALLING,"key",0);
    //注册中断->GPN1对应中断号IRQ_EINT(1)
    request_irq(IRQ_EINT(1),key_interrupt,IRQF_TRIGGER_FALLING,"key",0);

    //硬件初始化
    key_hw_init();
    //创建工作1
    work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);   //分配空间
    INIT_WORK(work1,work1_func);

    //初始化定时器
    init_timer(&key_timer);
    //设置超时函数
    key_timer.function = key_timer_func;
    //注册定时器
    add_timer(&key_timer);

    //初始化等待队列
    init_waitqueue_head(&key_wait_queue);

    return 0;
}

static void button_exit()
{
    //注销miscdevice
    misc_deregister(&key_miscdev);
    //注销中断
    free_irq(IRQ_EINT(0),0);
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("CaoKaipeng");
MODULE_DESCRIPTION("Key driver");
MODULE_VERSION("V1.0");

module_init(button_init);
module_exit(button_exit);

key应用程序

/*Copyright (c) 2018 Caokaipeng. All rights reserved.*/

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int fd;   //文件指针
    int key_num; //按键编号
    //1.打开设备
    fd = open("/dev/6410key",0);
    if(fd<0){
        printf("Open device error!\n");
    }
    //2.读取设备
    read(fd,&key_num,4);
    printf("key_num is %d.\n",key_num);
    //3.关闭设备
    close(fd);
}

运行结果

->先加载驱动,运行app,再按下按键1

这里写图片描述

————————————————
2018.02.04
23:27

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值