一、linux驱动开发-4.1-按键输入

目录

一、前言

二、程序编写

2.1、修改设备树

2.1.1、添加pinctrl节点

2.1.2、添加key节点

2.2、驱动编写

2.3、应用程序编写

三、测试


一、前言

       下面演示下linux下GPIO输入驱动的编写。

       在驱动中使用一个整形变量来表示按键值,应用程序通过read函数来读取按键值,因为驱动要对这个整形变量写入按键值,应用程序要读取按键值,因此该整形变量就是共享资源,需要进行保护,对于整形变量,我们首选就是原子操作,通过原子操作对该变量进行赋值与读取。

二、程序编写

2.1、修改设备树

2.1.1、添加pinctrl节点

       在 iomuxc 节点的 imx6ul-evk 子节点下添加一个pinctrl_key的子节点:

2.1.2、添加key节点

        在根节点“/”下创建key节点,节点名字为gpiokey。

        make dtbs重新编译设备树,然后启动linux系统,在/proc/decive-tree目录下查看“gpiokey”节点是否存在。

2.2、驱动编写

        添加一个原子变量保存按键值。

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>

#define GPIOKEY_CNT     1     
#define GPIOKEY_NAME    "gpiokey"

#define	KEY_ON      0xF0
#define KEY_OFF     0x00

//设备结构体
struct gpiokey_dev{
	dev_t devid;
	struct cdev cdev;
	struct class *class;
	struct device *device;
	int major;
	int minor;
	struct device_node *nd;		//设备节点
	int key_gpio;				//key所使用的GPIO编号
	atomic_t value;    			//按键值
};

struct gpiokey_dev gpiokey;  

static int key_init(void)
{
	int ret = 0;
	//通过节点名字查找节点
	gpiokey.nd = of_find_node_by_name(NULL, "gpiokey");
	if (gpiokey.nd == NULL)
	{
		printk("find %s fail\r\n", "gpiokey");
		goto fail_nd;
	}

	//获取GPIO编号
	gpiokey.key_gpio = of_get_named_gpio(gpiokey.nd, "key-gpio", 0);
	if (gpiokey.key_gpio < 0)
	{
		printk("get gpio fail\r\n");
		goto fail_nd;
	}

	ret = gpio_request(gpiokey.key_gpio, GPIOKEY_NAME);
	if (ret < 0)
	{
		printk("fail to request the key gpio\r\n");
		goto fail_nd;
	}
	//设置key状态
	ret = gpio_direction_input(gpiokey.key_gpio);
	if (ret < 0)
	{
		printk("set key status fail\r\n");
		goto fail_setinput;
	}

fail_setinput:
	gpio_free(gpiokey.key_gpio);
fail_nd:
	return ret;

	return 0;
}

static int key_open(struct inode *inode, struct file *filp)
{
	int ret = 0;
	filp->private_data = &gpiokey;

	ret = key_init();
	if (ret < 0)
		return ret;

	return 0;
}

static ssize_t key_read(struct file *filp, __user char *buf, size_t count, loff_t *ppos)
{	
	int ret = 0;
	int value;
	struct gpiokey_dev *dev = filp->private_data;

	if (gpio_get_value(dev->key_gpio) == 0) 	//按下
	{
		while(!gpio_get_value(dev->key_gpio));	//等待松开
		atomic_set(&dev->value, KEY_ON);
	}
	else {
		//没按下
		atomic_set(&dev->value, KEY_OFF);
	}

	value = atomic_read(&dev->value);
	ret = copy_to_user(buf, &value, sizeof(value));
	return ret;
}

static ssize_t key_write(struct file *filp, const char __user *buf, size_t count, loff_t *oppos)
{
	printk("key write\n");
	return 0;
}

static int key_release(struct inode *inode, struct file *filp)
{
	printk("key release\n");
	
	return 0;
}

static const struct file_operations key_fops = {
	.owner = THIS_MODULE,
	.open  = key_open,
	.read  = key_read,
	.write = key_write,
	.release = key_release,
};

static int __init mykey_init(void)
{
	int ret = 0;

	//按键值初始化
	atomic_set(&gpiokey.value, KEY_OFF);

	//分配设备号
	if ( gpiokey.major) {
		 gpiokey.devid = MKDEV( gpiokey.major, 0);
		register_chrdev_region( gpiokey.devid,  GPIOKEY_CNT,  GPIOKEY_NAME);
	} else {
		alloc_chrdev_region(&gpiokey.devid, 0,  GPIOKEY_CNT,  GPIOKEY_NAME);
		 gpiokey.major = MAJOR(gpiokey.devid);
		 gpiokey.minor = MINOR(gpiokey.devid);
	}
	printk(" gpiokey major=%d, minor= %d\r\n",  gpiokey.major,  gpiokey.minor);
	//初始化cdev
	gpiokey.cdev.owner = THIS_MODULE;
	cdev_init(&gpiokey.cdev, &key_fops);
	//添加cdev
	ret = cdev_add(&gpiokey.cdev, gpiokey.devid,  GPIOKEY_CNT);
	if (ret < 0)
	{
		printk("cdev add fail\r\n");
		goto fail_cdev;
	}
	//创建类
	gpiokey.class = class_create(THIS_MODULE,  GPIOKEY_NAME);
	if (IS_ERR(gpiokey.class))
	{
		printk("creat class fail\r\n");
		ret = PTR_ERR(gpiokey.class);
		goto fail_class;
	}
	//创建设备
	gpiokey.device = device_create(gpiokey.class, NULL, gpiokey.devid, NULL,  GPIOKEY_NAME);
	if (IS_ERR(gpiokey.device))
	{
		printk("creat device fail\r\n");
		ret = PTR_ERR(gpiokey.device);
		goto fail_device;
	}

	return 0;

fail_device:	
	class_destroy(gpiokey.class);
fail_class:
	cdev_del(&gpiokey.cdev);
fail_cdev:
	unregister_chrdev_region(gpiokey.devid,  GPIOKEY_CNT);
	return ret;
}

static void __exit mykey_exit(void)
{
	//删除设备的类
	device_destroy(gpiokey.class,  gpiokey.devid);
	//删除类
	class_destroy(gpiokey.class);
	//删除设备
	cdev_del(&gpiokey.cdev);
	//注销设备号
	unregister_chrdev_region(gpiokey.devid,  GPIOKEY_CNT);
		
	printk(" dev exit\n");
}

module_init( mykey_init);
module_exit( mykey_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZK");

2.3、应用程序编写

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#define KEY0VALUE      0xF0
#define INVAKEY        0x00

/*
@argc:应用程序参数个数
@argv[]:具体的参数内容,字符串形式
@ ./keyAPP /dev/key
*/
int main(int argc, char *argv[])
{
    int ret = 0;
    int fd = 0;
    int value = 0;
    char *filename;

    if (argc != 2){
        printf("Error usage!\r\n");
        return -1;
    }

    filename = argv[1];

    fd = open(filename, O_RDWR);
    if (fd < 0){
        printf("can't open file\r\n", filename);
        return -1;
    }
    
    while(1) {
        read(fd, &value, sizeof(value));
        if (value == KEY0VALUE) {
            printf("KEY0 Press, value = %d\r\n", value);
        }
    }

    /*close*/
    ret = close(fd);
    if (ret < 0){
        printf("close file %s failed\r\n", filename);
    }

    return 0;
}

三、测试

      

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值