基于平台总线的按键设备驱动

平台总线
将设备和驱动分离开来,便于移植,提供设备与驱动的匹配。

设备模块的程序

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

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>

//定义key的控制寄存器
#define GPNCON 0x7f008830

//定义资源
struct resource key_resource[] = {
    [0] = { //基地址->控制寄存器和数据寄存器
        .start = GPNCON,
        .end = GPNCON+4,
        .flags = IORESOURCE_MEM,
    },
    [1] = { //中断号
        .start = IRQ_EINT(0),
        .end = IRQ_EINT(1),
        .flags = IORESOURCE_IRQ,
    },
};

//定义一个平台设备
struct platform_device key_dev ={
    .name = "my_key",
    .id = 0,
    .num_resources = 2,
    .resource = key_resource,
};

int keydev_init()
{
    //平台设备的注册
    platform_device_register(&key_dev);
}

void keydev_exit()
{
    //平台设备的注销
    platform_device_unregister(&key_dev);
}

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

module_init(keydev_init);
module_exit(keydev_exit);

驱动模块程序

/*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>
#include <linux/platform_device.h>

//按键编号
unsigned int key_num = 0;

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

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

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

//定义一个resource_irq
struct resource *res_irq;
//定义一个resource_mem
struct resource *res_mem;
//控制寄存器虚拟地址
unsigned int *key_base;

//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_base+1)&0x01;
    if(0 == key1_val){   //按下为低电平
        key_num = 1;
    }
    else{
        //do nothing
    }

    //check key2是否仍然按下
    key2_val = readl(key_base+1)&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 data;
    //读取控制寄存器的值
    data = readl(key_base);
    //打印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_base);
}

//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,
};

//定义probe函数
int __devinit key_probe(struct platform_device *pdev)
{
    int ret;
    int size;
    //注册miscdevice(混杂设备)
    ret = misc_register(&key_miscdev);
    if(ret != 0){
        printk(KERN_WARNING"register key_misc fail.\n");
    }
    //获取中断号
    res_irq = platform_get_resource(pdev,IORESOURCE_IRQ,0);
    //注册中断->GPN0对应中断号IRQ_EINT(0)
    request_irq(res_irq->start,key_interrupt,IRQF_TRIGGER_FALLING,"key",0);
    //注册中断->GPN1对应中断号IRQ_EINT(1)
    request_irq(res_irq->end,key_interrupt,IRQF_TRIGGER_FALLING,"key",0);

    //获取基地址
    res_mem = platform_get_resource(pdev,IORESOURCE_MEM,0);
    size = res_mem->end - res_mem->start +1;
    key_base = ioremap(res_mem->start,size);
    //硬件初始化
    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 ret;
}

//定义remove函数
int __devinit key_remove(struct platform_device *pdev)
{
    //注销miscdevice
    misc_deregister(&key_miscdev);
    //注销中断
    free_irq(IRQ_EINT(0),0);
}

//定义一个平台驱动
struct platform_driver key_drv = {
    .probe =key_probe ,
    .remove = key_remove,
    .driver = {
        .name   = "my_key",
    },
};

static int button_init()
{
    //注册平台驱动
    return platform_driver_register(&key_drv);
}

static void button_exit()
{
    //注销平台驱动
    platform_driver_unregister(&key_drv);
}

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

module_init(button_init);
module_exit(button_exit);

应用程序

/*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);
}

运行结果
这里写图片描述

————————————
2018.02.10
0:20

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值