Linux下按键驱动实验

news2025/1/10 3:21:45

按键驱动和LED驱动原理上来讲基本都是一样的,都是操作GPIO,只不过一个是读取GPIO的高低电平,一个是从GPIO输出高低电平给LED。
本实验使用的开发板是IMX6DL,实验中使用的按键是通过使用下图中P1的EIM_A20和VCC3.3V两个引脚实现的,P1是开发板上60脚的BUS。
在这里插入图片描述
同样地,要确保复用为按键GPIO的EIM_A20脚没有被其他设备复用,在设备树文件imx6dl-c-sabresd.dts中添加pinctrl引脚信息如下。

pinctrl_key:key_bus{  
       fsl,pins = <
               MX6QDL_PAD_EIM_A20__GPIO2_IO18     0x80000000    //将EIM_A20复用为GPIO2_IO18
       >;
};

然后在设备树文件imx6dl-c-sabresd.dts根节点下添加一个key节点,其内容如下。

key{ 
	    #address-cells = <1>;
        #size-cells = <1>;
        pinctrl-names = "default";
	    compatible = "gpio_bus_key";
		pinctrl-0 = <&pinctrl_key>;   //设置KEY所使用的PIN对应的pinctrl节点
        key-gpio = <&gpio2 18 GPIO_ACTIVE_LOW>;  //P1,即开发板上bus的EIM_A20号口复用
        status = "okay";
}; 

上面这些内容的添加要根据自己的开发板情况而定,添加完成后设备树就修改完成了,然后编译一下设备树,将最新的dtb文件拷贝到/tftp文件夹,然后在开发板启动时,最新编译的设备树文件就加载到开发板中了。
本实验中的按键驱动源代码如下,该代码来自正点原子,本人只做了一点改动

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/platform_device.h>

#define KEY_CNT			    1		 /* 设备号个数 */
#define KEY_NAME		"gpio_key"	/* 在/dev目录下生成的设备名字*/

struct key_dev{
	dev_t devid;			  /* 设备号 	 */
	struct cdev cdev;		  /* cdev 	*/
	struct class *class;	  /* 类 */
	struct device *device;	  /* 设备 	 */
	int major;				  /* 主设备号	  */
	int minor;				  /* 次设备号   */
	struct device_node	*nd;   /* 设备节点 */
	int key_gpio;			   /* key所使用的GPIO编号*/
	atomic_t keyvalue;		   /*按键值 原子操作,互斥访问*/	
};

struct key_dev keydev;		/* key设备 */

static int key_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &keydev; 	/* 设置私有数据 */
	return 0;
}

static ssize_t key_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
	int ret = 0;
	char kbuf[1];     //定义为char类型
	struct key_dev *dev = filp->private_data;

	if (gpio_get_value(dev->key_gpio) == 0)           /* key按下 */
	{ 		
		while(!gpio_get_value(dev->key_gpio));		/* 等待按键释放 */
		atomic_set(&dev->keyvalue,1); 
	} 
	else
		atomic_set(&dev->keyvalue,0);

	kbuf[0] = atomic_read(&dev->keyvalue);   //读出按键值
	ret = copy_to_user(buf,kbuf,sizeof(kbuf));
	printk("Kernel: read value = %d\n",kbuf[0]);
	return ret;
}

static struct file_operations key_fops = {
	.owner = THIS_MODULE,
	.open = key_open,
	.read = key_read
};

const struct of_device_id of_match_table_key[] = {
	{.compatible = "gpio_bus_key"},        //与设备树中的compatible属性匹配
	{}
};

struct platform_driver dts_device = {    
	.driver = {
		.owner = THIS_MODULE,
		.name = "keygpio",
		.of_match_table = of_match_table_key
	}
};

static int __init gpio_key_init(void)
{
	atomic_set(&keydev.keyvalue,0);
	keydev.nd = of_find_node_by_path("/key");
	if (keydev.nd == NULL) 
		return -EINVAL;
	printk("Find node key!\n");
	keydev.key_gpio = of_get_named_gpio(keydev.nd,"key-gpio",0);
	if (keydev.key_gpio < 0) 
	{
		printk("of_get_named_gpio failed!\r\n");
		return -EINVAL;
	}
	printk("key_gpio = %d\r\n", keydev.key_gpio);
	
	gpio_request(keydev.key_gpio,KEY_NAME);	    //申请gpio
	gpio_direction_input(keydev.key_gpio);   	//将gpio设置为输入

	if (keydev.major)
	{		
		keydev.devid = MKDEV(keydev.major, 0);
		register_chrdev_region(keydev.devid, KEY_CNT, KEY_NAME);
	}
	else 
	{						
		alloc_chrdev_region(&keydev.devid,0,KEY_CNT,KEY_NAME);	//申请设备号
		keydev.major = MAJOR(keydev.devid);	   //获取分配号的主设备号 
		keydev.minor = MINOR(keydev.devid);	
	}
	printk("gpiokey major=%d,minor=%d\r\n",keydev.major,keydev.minor);	

	keydev.cdev.owner = THIS_MODULE;
	cdev_init(&keydev.cdev, &key_fops);
	cdev_add(&keydev.cdev, keydev.devid, KEY_CNT);

	keydev.class = class_create(THIS_MODULE, KEY_NAME);
	if (IS_ERR(keydev.class))
		return PTR_ERR(keydev.class);

	keydev.device = device_create(keydev.class,NULL,keydev.devid,NULL,KEY_NAME);
	if (IS_ERR(keydev.device))
		return PTR_ERR(keydev.device);
	
	platform_driver_register(&dts_device); 
	return 0;
}

static void __exit gpio_key_exit(void)
{
	gpio_free(keydev.key_gpio);
	cdev_del(&keydev.cdev);
	unregister_chrdev_region(keydev.devid,KEY_CNT); 
	device_destroy(keydev.class, keydev.devid);
	class_destroy(keydev.class);
	platform_driver_unregister(&dts_device);
	printk("driver exit!\n");
}

module_init(gpio_key_init);
module_exit(gpio_key_exit);
MODULE_LICENSE("GPL");

代码中定义的变量既可以分开逐个定义,像LED驱动实验一样,这样代码看着简单;当然也可以像上面这样将代码中用到的变量都定义在一个结构体里面,这样代码看着更具象。
之后编写Makefile文件,将上面的代码文件编译成驱动模块,Makefile的写法和前面实验的一样,这里不在赘述。
下面的文件就是测试文件,该文件写好后通过交叉编译器编译,然后和驱动文件一起发到开发板上验证。

#include "stdio.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "unistd.h"

int main(int argc, char *argv[])
{
    int fd,ret;
    int count = 0;
    char buf[1];
    fd = open("/dev/gpio_key", O_RDWR);
    if(fd < 0)
    {
        perror("open error!\n");
        return fd; 
    }
    printf("open device gpio_key!\n");

    while(1)
    {
        ret = read(fd, &buf, sizeof(buf));   //循环读取按键值数据
        if(ret < 0)
        {
            printf("read file failed!");
            break;
        }
        else
        {
            if(buf[0] == 1)
            {
                printf("User: key up, value is %d\r\n",buf[0]);
                count++;
            } 
            else
            {
                printf("User: key down, value is %d\r\n",buf[0]);
                count++;
            }
            if(count == 10)   //读取十次键值就退出循环
                break;
            sleep(1);
        }
    } 
    close(fd);  
    return 0;
}

注意上面的测试文件中,为了不让其一直在循环中出不来,我这里设置了按键按下10次后自动退出,因为在测试文件运行后,按Ctrl+C是不管用的,而且我也设置了按键的键值读取至少间隔1秒。
最终在开发板上的运行结果如下图所示。
在这里插入图片描述
按键每按下一次,就打印1;按键一直按下不放,就打印0,而且每隔1秒打印一次。

本文参考文档:
I.MX6U嵌入式Linux驱动开发指南V1.5——正点原子

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1100340.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何选择超声波清洗机、超声波清洗机排行榜

眼镜的日常清洗生活中很多人都会把它给忘记&#xff01;长时间下来眼镜支架就会变成黄色的&#xff0c;非常的难洗掉&#xff0c;从而又浪费了一个眼镜。一副好的眼镜也不便宜的&#xff0c;把换眼镜的钱省下来入一款超声波清洗机&#xff0c;可以大大的减少金钱的支持&#xf…

java基于springboot+vue的厨房预订预约管理系统

历经长达几个月的毕业作品制作&#xff0c;我凭借自己的知识技能&#xff0c;还有大家的指导帮助&#xff0c;能够在学校规定的时间段之内提交毕业作品。虽然已经算是完成了毕业作品&#xff0c;但是付出了这么多心血&#xff0c;还是想把这个作品做得更加完美一点。针对我的毕…

如何通过内网穿透实现远程连接NAS群晖drive并挂载电脑硬盘?

文章目录 前言1.群晖Synology Drive套件的安装1.1 安装Synology Drive套件1.2 设置Synology Drive套件1.3 局域网内电脑测试和使用 2.使用cpolar远程访问内网Synology Drive2.1 Cpolar云端设置2.2 Cpolar本地设置2.3 测试和使用 3. 结语 前言 群晖作为专业的数据存储中心&…

hash join的基本原理是怎样的?

我们知道数据库里面两表关联主要有三种常见的关联方式&#xff0c;即 nested loop joinhash joinmerge join nested loop join在OLTP交易场景占比是最多的&#xff0c;常用于关联字段为主键或索引字段的情况&#xff0c;通过主键或索引以及loop的方式&#xff0c;A表可以快速…

BricsCAD 23 for Mac:轻松驾驭CAD建模的强大工具

如果你正在寻找一款功能强大、操作简便的CAD建模软件&#xff0c;那么BricsCAD 23 for Mac绝对值得你考虑。这款软件将为你提供一套完整的2D和3D设计解决方案&#xff0c;让你在Mac上轻松创建、编辑和修改图形。 一、BricsCAD 23的功能特点 高效的2D和3D建模&#xff1a;Bric…

2652. 倍数求和

2652. 倍数求和 题目方法-【枚举】 & 题目特征-【求计算在给定范围内满足某种条件的整数之和】方法-【容斥原理】 & 题目特征-【计算满足多个条件的元素之和&#xff0c;并且需要避免重复计数】 题目 题目链接&#xff1a;https://leetcode.cn/problems/sum-multiples…

倾斜摄影三维模型的根节点合并的重要性分析

倾斜摄影三维模型的根节点合并的重要性分析 倾斜摄影三维模型的根节点合并是整个模型构建过程中的一个重要环节&#xff0c;具有重要的意义和作用。本文将对倾斜摄影三维模型的根节点合并的重要性进行详细分析。 一、定义和概述 在倾斜摄影三维模型的构建过程中&#xff0c;根…

概率神经网络分类问题程序

欢迎关注“电击小子程高兴的MATLAB小屋” %% 概率神经网络 %% 解决分类问题 clear all; close all; P[1:8]; Tc[2 3 1 2 3 2 1 1]; Tind2vec(Tc) %数据类型的转换 netnewpnn(P,T); Ysim(net,P); Ycvec2ind(Y) %转换回来

一文带你认识高速低侧栅极驱动器 FAN3111ESX 带你深入了解其特点及应用

FAN3111ESX一款低端驱动器产品&#xff0c;是外部 DC 2 至 5 V 参考输入、单通道同相输出、1.4 A 峰值灌电流、1.4 A 源电流低端栅极驱动器。 FAN3111ESX 1A栅极驱动器为驱动一个在低侧开关应用中的 N沟道增强型 MOSFET 而设计。 对于使用低压控制器和其它和驱动器相比使用更…

二维码智慧门牌管理系统:地址管理的现代革命

文章目录 前言一、标准地址的革新二、广泛的应用前景 前言 在科技不断发展和社会进步的背景下&#xff0c;高效、精准、智能的管理系统已经成为当今社会的迫切需求。传统的门牌管理系统在应对这一需求方面已显得力不从心&#xff0c;因此&#xff0c;二维码智慧门牌管理系统的…

力扣每日一题43:字符串相乘

题目描述&#xff1a; 给定两个以字符串形式表示的非负整数 num1 和 num2&#xff0c;返回 num1 和 num2 的乘积&#xff0c;它们的乘积也表示为字符串形式。 注意&#xff1a;不能使用任何内置的 BigInteger 库或直接将输入转换为整数。 示例 1: 输入: num1 "2"…

强化学习基础(1)- 理论和算法

目录 1.基本概念 1.1组成部分 1.2 马尔可夫决策过程 2 有模型强化学习 2.1状态值函数 2.2动作值函数 2.3二者关系 2.4 探索和利用 2.5动态规划&#xff08;DP&#xff09;&#xff08;有模型求解方法&#xff09; 2.5.1预测任务 2.5.1控制任务 3.无模型强化学习 3.1 Valu…

AI全栈大模型工程师(四)OpenAI API初探

五、它是怎么生成结果的? 其实,它只是根据上文,猜下一个词(的概率)…… OpenAI 的接口名就叫「completion」,也证明了其只会「生成」的本质。 下面用程序演示「生成下一个字」。你可以自己修改 prompt 试试。还可以使用相同的 prompt 运行多次。 import openai import…

哪个牌子的电容笔好用?ipad触控笔推荐平价

有哪些电容笔适合学生党入手&#xff1f;苹果Pencil虽然与普通的电容笔&#xff0c;不同的是&#xff0c;这款电容笔同时具有重力传感器和倾斜传感器&#xff0c;而平替电容笔&#xff0c;只有一种倾斜传感器&#xff0c;但在书写方面的体验很不错&#xff0c;可以用来写字&…

JAMA | 多中心临床试验也用RCS方法分析,卒中患者强化血压管理更有利用康复

郑老师统计课程&#xff0c;欢迎点击报名&#xff1a;临床设计与数据分析 课程 2023年9月&#xff0c;外国学者在《JAMA》&#xff08;一区&#xff0c;IF120.7&#xff09;发表题为&#xff1a;" Intensive vs Conventional Blood Pressure Lowering After Endovascular …

MySQL初级之战

华子目录 什么是数据库&#xff1f;数据库管理系统&#xff08;DBMS&#xff09;数据库系统与文件系统的区别数据库的发展史常见数据库关系性数据库关系型数据库的4大特性关系型数据库的优缺点非关系型数据库非关系数据库的优点和缺点 DBMS支持的数据模型MySQL体系架构连接层SQ…

学生用什么样的台灯比较好?分享最合适学生使用的台灯

随着现在生活水平的提高&#xff0c;越来越多人重视健康的问题。尤其是对于孩子&#xff0c;很多家长对其可谓是百般担心、千般呵护&#xff0c;害怕出现什么问题&#xff0c;其中最主要的就是近视。而如今&#xff0c;我国青少年儿童的近视率可不低的&#xff0c;达到了52.7%&…

qt中的对象树与生命期

1.为什么要使用对象树&#xff1a; GUI 程序通常是存在父子关系的&#xff0c;比如一个对话框之中含有按钮、列表等部件&#xff0c;按钮、列表、对话框等部件其实就是一个类的对象(注意是类的对象&#xff0c;而非类)&#xff0c;很明显这些对象之间是存在父子关系的&#xff…

四、多线程服务器

1.进程的缺陷和线程的优点 1.进程的缺陷 创建进程&#xff08;复制&#xff09;的工作本身会给操作系统带来相当沉重的负担。 而且&#xff0c;每个进程具有独立的内存空间&#xff0c;所以进程间通信的实现难度也会随之提高。 同时&#xff0c;上下文切换&#xff08;Cont…

cmd:读取电脑硬件序列号

一、读取电脑硬件序列号 1.cmd 在没有使用第三方库的情况下&#xff0c;要读取电脑的硬件序列号通常需要使用操作系统提供的工具或命令行。以下是一个示例&#xff0c;展示如何使用Windows操作系统的命令行工具 wmic 来获取硬件序列号&#xff1a; 打开命令提示符&#xff0…