单片机
返回首页

用内核定时器来实现的按键驱动代码分析以及测试代码

2025-02-06 来源:cnblogs

驱动代码:

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include      

#include

#include

#include

#include

#include

#include


#include //udev

struct class *key_class;


static int key_major = 0;

struct key_desc_t {

    int pin;    //which gpio pin

    int pin_dir;//input or output

    int number;

    char *name;    

};


/*设备结构体*/

static struct key_dev

{

    struct cdev cdev;    

    struct timer_list key_timer;/*内核定时器*/

};


struct key_dev *key_devp;//设备结构体指针


#define MAX_KEYS    5

/* 用来指定按键所用gpio的编号,方向和名字 */

static struct key_desc_t key_desc [] = {

    {S5PV210_GPH0(3), S3C_GPIO_INPUT, 0, 'KEY1'}, /* K1 */

    {S5PV210_GPH0(4), S3C_GPIO_INPUT, 1, 'KEY2'}, /* K2 */

    {S5PV210_GPH0(5), S3C_GPIO_INPUT, 2, 'KEY3'}, /* K3 */

    {S5PV210_GPH0(6), S3C_GPIO_INPUT, 3, 'KEY4'}, /* K4 */

    {S5PV210_GPH0(7), S3C_GPIO_INPUT, 4, 'KEY5'}, /* K5 */


};


/* 按键被按下的次数*/

static volatile int key_values [MAX_KEYS] = {0, 0, 0, 0, 0};


/* 等待队列: 

 * 当没有按键被按下时,如果有进程调用key_read函数,

 * 它将休眠

 */

static DECLARE_WAIT_QUEUE_HEAD(key_waitq);




#define KEY_TIMER_DELAY    HZ/50   /*20ms*/


/* 事件标志, 有键按下时将它置1,key_read将它清0 */

static volatile int ev_press = 0;



static void key_timer_handle(unsigned long data)

{

    struct key_desc_t *key_p = (struct key_desc_t *)data;

    int down;

    static unsigned char pressed[MAX_KEYS] = {0};

    int i;

    

    for(i=0;i    {

            down = gpio_get_value(key_p->pin);

        if(!down) //key pressed

        {

            pressed[i] ++;

            if(pressed[i] > 2) pressed[i] = 3;

        }

        if((!down) && (2 == pressed[i]))//要等待20ms再检查一下该按键确实是被按下,而不是抖动

        {

            printk('pressedn');

            key_values[i] = i + 1;

                ev_press = 1;                  /* 表示按键被按下了 */

                wake_up_interruptible(&key_waitq);   /* 唤醒休眠的进程 */

        }

        if(down && pressed[i]) //如果该键被释放

        {

            pressed[i] = 0;

        }

    }


    /*调度定时器再次执行*/

    key_devp->key_timer.expires = jiffies + KEY_TIMER_DELAY;

    add_timer(&key_devp->key_timer);


}



/* 应用程序对设备文件/dev/key执行open(...)时,

 * 就会调用key_open函数

 */

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

{

    return 0;

}



/* 应用程序对设备文件/dev/key执行close(...)时,

 * 就会调用key_close函数

 */

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

{    

    int i=0;

    for(i=0;i        gpio_free(key_desc[i].pin);

    return 0;

}



/* 应用程序对设备文件/dev/key执行read(...)时,

 * 就会调用key_read函数

 */

static int key_read(struct file *filp, char __user *buff, 

                        size_t count, loff_t *offp)

{

    unsigned long err;


    if (!ev_press) {

        if (filp->f_flags & O_NONBLOCK)

            return -EAGAIN;

        else

            /* 如果ev_press等于0,休眠 */

            wait_event_interruptible(key_waitq, ev_press);

    }

    

    /* 执行到这里时,ev_press等于1,将它清0 */

    ev_press = 0;


    /* 将按键状态复制给用户,并清0 */

    err = copy_to_user(buff, (const void *)key_values, min(sizeof(key_values), count));

    memset((void *)key_values, 0, sizeof(key_values));


    return err ? -EFAULT : min(sizeof(key_values), count);

}


/**************************************************

* 当用户程序调用select函数时,本函数被调用

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

* 如果没有按键数据,本函数使用poll_wait等待

**************************************************/

static unsigned int key_poll(struct file *file,

                     struct poll_table_struct *wait)

{

    unsigned int mask = 0;

        poll_wait(file, &key_waitq, wait);

        if (ev_press)

            mask |= POLLIN | POLLRDNORM;

        return mask;

}



/* 这个结构是字符设备驱动程序的核心

 * 当应用程序操作设备文件时所调用的open、read、write等函数,

 * 最终会调用这个结构中的对应函数

 */

static struct file_operations key_fops = {

    .owner   =   THIS_MODULE,    /* 这是一个宏,指向编译模块时自动创建的__this_module变量 */

    .open    =   key_open,

    .release =   key_close, 

    .read    =   key_read,

    .poll    =   key_poll,

};



void set_gpio_mode_for_key(struct key_desc_t * key)

{

    int i;


    for(i = 0; i < MAX_KEYS; i ++,key ++)

    {

        //s3c_gpio_pullup(key->pin, 1);

        s3c_gpio_cfgpin(key->pin, key->pin_dir);

    }


}


/*

 * Set up the cdev structure for a device.

 */

static void key_setup_cdev(struct key_dev *dev, int minor)

{        

        printk('in the key_setup_cdev!!n');

        int err, devno = MKDEV(key_major, minor);


        cdev_init(&dev->cdev, &key_fops);

        //dev->cdev.owner = THIS_MODULE;

        //dev->cdev.ops = &key_fops;

        printk('cdev_init success!!n');

        err = cdev_add (&dev->cdev, devno, 1);

        /* Fail gracefully if need be */

        if (err)

                printk (KERN_NOTICE 'Error %d adding key%d', err, minor);

        printk('key_setup_cdev success!!n');

}



/*

 * We export one key device.  There's no need for us to maintain any

 * special housekeeping info, so we just deal with raw cdev.

 */

//static struct cdev key_cdev;



/*

 * 执行“insmod *.ko”命令时就会调用这个函数

 */

static int __init button_init(void)

{

        int result;

        dev_t dev = MKDEV(key_major, 0);

        char dev_name[]='key';

        /*申请gpio*/

        int ret;

        int i=0;

        for(i=0;i            {

                ret=gpio_request(key_desc[i].pin,'KEY');

                if(ret)

                    {

                        printk('fail to request gpio %d !!n',key_desc[i].pin);

                        return 0;

                    }

            }

        printk('request gpio success!!n');                                                                                                

        /* Figure out our device number. */

        if (key_major)

                result = register_chrdev_region(dev, 1, dev_name);

        else {

                result = alloc_chrdev_region(&dev, 0, 1, dev_name);

                key_major = MAJOR(dev);

        }

        if (result < 0) {

                printk(KERN_WARNING 'key: unable to get major %dn', key_major);

                return result;

        }

        if (key_major == 0)

                key_major = result;

        printk('alloc_chrdev_region success !!n');



        /* 动态申请设备结构体的内存*/

        key_devp = kmalloc(sizeof(struct key_dev), GFP_KERNEL);//kmalloc()内核空间;malloc()用户空间。返回起始地址

        if (!key_devp)    /*申请失败*/

        {

          result =    - ENOMEM;

          printk('key_devp kmalloc failed!!n');

        }

        memset(key_devp, 0, sizeof(struct key_dev));//把此内存空间清零

        


                                                                                                       

        /* Now set up cdev. */

        key_setup_cdev(key_devp, 0);

        printk('key device installed, with major %dn', key_major);

    printk('The device name is: /dev/%sn', dev_name);


    /*set gpio mode*/

    //set_gpio_mode_for_key(key_desc);

    

    /*init kernel timer*/

    init_timer(&key_devp->key_timer);

    key_devp->key_timer.function = &key_timer_handle;

    key_devp->key_timer.data = (unsigned long)key_desc;

    key_devp->key_timer.expires = jiffies + KEY_TIMER_DELAY;

    add_timer(&key_devp->key_timer);

    /*用udev机制自动添加设备结点*/

     key_class=class_create(THIS_MODULE,'key_class');

    device_create(key_class,NULL,key_devp->cdev.dev,NULL,dev_name);


    return 0;

}


/*

 * 执行rmmod key_drv.ko”命令时就会调用这个函数 

 */

static void __exit button_cleanup(void)

{


        cdev_del(&key_devp->cdev);

        unregister_chrdev_region(MKDEV(key_major, 0), 1);

    del_timer_sync(&key_devp->key_timer);

        printk('key device uninstalledn');

}


/* 这两行指定驱动程序的初始化函数和卸载函数 */

module_init(button_init);

module_exit(button_cleanup);


/* 描述驱动程序的一些信息,不是必须的 */

MODULE_AUTHOR('mhb@SEU');             // 驱动程序的作者

MODULE_DESCRIPTION('KEY Driver');   // 一些描述信息

MODULE_LICENSE('GPL');                              // 遵循的协议


测试代码:


#include

#include

#include

#include

#include

#include

#include

#include

#include

#include


int main(void)

{

    int i;

    int key_fd;

    int key_value[5];


    /*打开键盘设备文件*/

    key_fd = open('/dev/key', 0);

    if (key_fd < 0) {

        perror('open device key');

        exit(1);

    }


    for (;;) {

        fd_set rds;

        int ret;


        FD_ZERO(&rds);

        FD_SET(key_fd, &rds);


        /*使用系统调用select检查是否能够从/dev/key设备读取数据*/

        ret = select(key_fd + 1, &rds, NULL, NULL, NULL);

        

        /*读取出错则退出程序*/

        if (ret < 0) {

            perror('select');

            exit(1);

        }

        

        if (ret == 0) {

            printf('Timeout.n');

        } 

        /*能够读取到数据*/

        else if (FD_ISSET(key_fd, &rds)) {

            /*开始读取键盘驱动发出的数据,注意key_value和键盘驱动中定义为一致的类型*/

            int ret = read(key_fd, key_value, sizeof key_value);

            if (ret != sizeof key_value) {

                if (errno != EAGAIN)

                    perror('read keyn');

                continue;

            } else {

                /*打印键值*/

                for (i = 0; i < 5; i++)

                    printf('K%d %s, key value = 0x%02xn',

                       i+1, (key_value[i] & 0x80) ? 'released':

                                   key_value[i] ? 'pressed down' : '', key_value[i]);

            }

                

        }

    }

    /*关闭设备文件句柄*/

    close(key_fd);

    return 0;

}


进入单片机查看更多内容>>
相关视频
  • 【TI MSPM0 应用实战】智能小车+工业角度编码器+血氧仪+烟雾探测器!硬核参考设计详解!

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

  • 直播回放: Microchip Timberwolf™ 音频处理器在线研讨会

  • 基于灵动MM32W0系列MCU的指夹血氧仪控制及OTA升级应用方案分享

精选电路图
  • 1瓦四级调频发射机

  • 500W MOS场效应管电源逆变器,12V转110V/220V

  • 12V 转 28V DC-DC 变换器(基于 LM2585)

  • 红外开关

  • 12V转110V/220V 500W逆变器

  • DS1669数字电位器

    相关电子头条文章