STM32MP157驱动开发——按键驱动(中断)

news2024/10/11 2:23:33

文章目录

  • 编写使用中断的按键驱动程序
    • 编程思路
      • 设备树相关
      • 驱动代码相关
    • 代码
      • 修改设备树文件
      • gpio_key_drv.c
      • Makefile
      • 编译测试

编写使用中断的按键驱动程序

  • 对于使用中断的按键驱动,内核自带的驱动程序 drivers/input/keyboard/gpio_keys.c 就可以,需要做的只是修改设备树指定引脚及键值

  • 中断是引入其他基础知识的前提:休眠-唤醒、POLL 机制、异步通知、定时器、中断的线程化处理都离不开中断

编程思路

设备树相关

查看原理图确定按键使用的引脚,再在设备树中添加节点,在节点里指定中断信息

例子:

gpio_keys_first {
	compatible = "first_key,gpio_key";
	gpios = <&gpio5 1 GPIO_ACTIVE_HIGH
			&gpio4 14 GPIO_ACTIVE_HIGH>;
	pinctrl-names = "default";
	pinctrl-0 = <&key1_pinctrl
				&key2_pinctrl>;
};

注意:在设备树里面可以不用设置中断,因为Linux 系统中异常与中断里面提到对于 GPIO,可以在驱动程序里面使用 gpio_to_irq 或 gpiod_to_irq 获得中断号

驱动代码相关

  • 首先要获得中断号
  • 然后编写中断处理函数;
  • 最后 request_irq。

代码

修改设备树文件

在这里插入图片描述
对于一个引脚要用作中断时,

  • a) 要通过 PinCtrl 把它设置为 GPIO 功能;【ST 公司对于 STM32MP157 系列芯片,GPIO 为默认模式 不需要再进行配置Pinctrl 信息】
  • b) 表明自身:是哪一个 GPIO 模块里的哪一个引脚【修改设备树】

打开内核的设备树文件:arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dts

gpio_keys_first {
	compatible = "first_key,gpio_key";
	gpios = <&gpiog 3 GPIO_ACTIVE_LOW
			&gpiog 2 GPIO_ACTIVE_LOW>;
};

与此同时,需要把用到引脚的节点禁用

注意,如果其他设备树文件也用到该节点,需要设置属性为disabled状态,在arch/arm/boot/dts目录下执行如下指令查找哪些设备树用到该节点

grep "&gpiog" * -nr

如果用到该节点,需要添加属性去屏蔽:

status = "disabled"; 

在这里插入图片描述

gpio_key_drv.c

#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>


struct gpio_key{
	int gpio;
	struct gpio_desc *gpiod;
	int flag;
	int irq;
} ;

static struct gpio_key *gpio_keys_first;

//中断处理函数,参数是request_irq函数参数的最后一个
static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{
	struct gpio_key *gpio_key = dev_id;//获取的是引脚的电平,因为设置了上升沿和下降沿都触发
	int val;
	val = gpiod_get_value(gpio_key->gpiod);
	
	printk("key %d %d\n", gpio_key->gpio, val);
	
	return IRQ_HANDLED;
}

/* 1. 从platform_device获得GPIO
 * 2. gpio=>irq
 * 3. request_irq
 */
static int gpio_key_probe(struct platform_device *pdev)
{
	int err;
	struct device_node *node = pdev->dev.of_node;//platform_device 里面有device结构体
	int count;
	int i;
	enum of_gpio_flags flag;
	unsigned flags = GPIOF_IN;
		
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	count = of_gpio_count(node);
	if (!count)
	{
		printk("%s %s line %d, there isn't any gpio available\n", __FILE__, __FUNCTION__, __LINE__);
		return -1;
	}

	gpio_keys_first = kzalloc(sizeof(struct gpio_key) * count, GFP_KERNEL);
	for (i = 0; i < count; i++)
	{
		gpio_keys_first[i].gpio = of_get_gpio_flags(node, i, &flag);
		if (gpio_keys_first[i].gpio < 0)
		{
			printk("%s %s line %d, of_get_gpio_flags fail\n", __FILE__, __FUNCTION__, __LINE__);
			return -1;
		}
		gpio_keys_first[i].gpiod = gpio_to_desc(gpio_keys_first[i].gpio);
		gpio_keys_first[i].flag = flag & OF_GPIO_ACTIVE_LOW;
		//如果设备树里面是低电平有效,则flags或上GPIO子系统里面的低电平有效的宏定义(注意有F)
		if (flag & OF_GPIO_ACTIVE_LOW)
			flags |= GPIOF_ACTIVE_LOW;
		//读出引脚的逻辑值
		err = devm_gpio_request_one(&pdev->dev, gpio_keys_first[i].gpio, flags, NULL);

		
		gpio_keys_first[i].irq  = gpio_to_irq(gpio_keys_first[i].gpio);//从 GPIO 获得中断号,该中断号是使用request_irq的参数之一
	}

	for (i = 0; i < count; i++)
	{
		err = request_irq(gpio_keys_first[i].irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "my_gpio_key", &gpio_keys_first[i]);
	}
        
    return 0;
    
}

static int gpio_key_remove(struct platform_device *pdev)
{
	//int err;
	struct device_node *node = pdev->dev.of_node;
	int count;
	int i;

	count = of_gpio_count(node);
	for (i = 0; i < count; i++)
	{
		free_irq(gpio_keys_first[i].irq, &gpio_keys_first[i]);
	}
	kfree(gpio_keys_first);
    return 0;
}


static const struct of_device_id my_keys[] = {
    { .compatible = "first_key,gpio_key" },
    { },
};

/* 1. 定义platform_driver */
static struct platform_driver gpio_keys_driver = {
    .probe      = gpio_key_probe,
    .remove     = gpio_key_remove,
    .driver     = {
        .name   = "my_gpio_key",
        .of_match_table = my_keys,
    },
};

/* 2. 在入口函数注册platform_driver */
static int __init gpio_key_init(void)
{
    int err;
    
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	
    err = platform_driver_register(&gpio_keys_driver); 
	
	return err;
}

/* 3. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数
 *     卸载platform_driver
 */
static void __exit gpio_key_exit(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

    platform_driver_unregister(&gpio_keys_driver);
}


/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */

module_init(gpio_key_init);
module_exit(gpio_key_exit);

MODULE_LICENSE("GPL");



Makefile

# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
# 2.1 ARCH,          比如: export ARCH=arm64
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
# 2.3 PATH,          比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin 
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
#       请参考各开发板的高级用户使用手册

KERN_DIR =  /home/book/100ask_stm32mp157_pro-sdk/Linux-5.4

all:
	make -C $(KERN_DIR) M=`pwd` modules 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order

# 参考内核源码drivers/char/ipmi/Makefile
# 要想把a.c, b.c编译成ab.ko, 可以这样指定:
# ab-y := a.o b.o
# obj-m += ab.o



obj-m += gpio_key_drv.o


编译测试

首先要设置 ARCH、CROSS_COMPILE、PATH 这三个环境变量后,进入 ubuntu 上板子内核源码的目录,在Linux内核源码根目录下,执行如下命令即可编译 dtb 文件:

make dtbs V=1

编译好的文件在路径由DTC指定,移植设备树到开发板的共享文件夹中,先保存源文件,然后覆盖源文件,重启后会挂载新的设备树,进入该目录查看是否有新添加的设备节点

cd /sys/firmware/devicetree/base 

编译驱动程序,在Makefile文件目录下执行make指令,此时,目录下有编译好的内核模块gpio_key_drv.ko移植到开发板上

确定一下烧录系统:cat /proc/mounts,查看boot分区挂载的位置,将其重新挂载在boot分区:mount /dev/mmcblk2p2 /boot,然后将共享文件夹里面的设备树文件拷贝到boot目录下,这样的话设备树文件就在boot目录下

cp /mnt/stm32mp157c-100ask-512d-lcd-v1.dtb /boot

重启后挂载,运行

insmod gpio_key_drv.ko // 装载驱动程序

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

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

相关文章

❛‿˂̵✧ THU数据结构(上)(2023spring) 完成啦(⑅˃◡˂⑅)

实际上来讲&#xff0c;是课程截止到今天晚上( ◔︎ ‸◔︎) 总结一下&#xff0c;放假十来天&#xff0c;边敲边听。后期有些地方应该再复习复习。 接下来的关于数据结构的目标 〇 把&#xff08;上&#xff09;中没敲完的结构敲完 〇 ZJU的配套题目写完 〇 学习&#xff…

linux系统GHOST备份方案

linux系统GHOST备份方案 Windows系统备份可以用ghost工具软件完成&#xff0c;Linux系统不能完全依赖于ghost工具&#xff0c;一则是ghost本身是有版权的软件&#xff0c;二则ghost只支持ext2、ext3文件系统的Linux分区&#xff0c;不支持reiserfs、xfs等比较高级的文件系统&a…

9.python设计模式【外观模式】

内容&#xff1a;为子系统中的一组接口提供一个一致的界面&#xff0c;外观模式定义了一个高层接口&#xff0c;这个接口使得这一个子系统更加容易使用。 角色&#xff1a; 外观&#xff08;facade&#xff09;子类系统&#xff08;subsystem classes&#xff09; UML图 举…

拉密

拉密&#xff0c;又称以色列麻将。 游戏人数为2-4人。 和扑克牌一样&#xff0c;一套是52张&#xff0c;4个花色&#xff0c;每个花色1到13。 拉密数字牌由106张塑料牌组成&#xff0c;包含2套牌和2张万能牌。 一张是可以指定成暖色任意牌&#xff0c;一张可以指定成冷色任意…

草稿#systemverilog# 说说《队列》 那些事儿

最近不断使用到systemverilog 语言编程中的队列语法。今天抽时间,尽肯能全的整理一下队列的相关用法,一是加固基础打牢,而是在代码编写过程中,提高代码水平。 队列是一种复合数据结构,可以用来存储多个数据的容器,但同时又拥有自己的优势特性。正如绿皮书所言,队列的引…

1-2 AUTOSAR分层架构

目录 一、简介 二、基础软件层 BSW 2.1 微控制器抽象层 2.2 ECU抽象层 2.3 复杂的驱动程序 2.4 服务层 三、运行时环境 RTE 四、应用软件层 SWC 一、简介 AUTOSAR架构在最高的抽象级别上区分了三个软件层&#xff1a; 应用程序层&#xff08;APPL&#xff09;运行时环…

macOS Monterey 12.6.8 (21G725) Boot ISO 原版可引导镜像

macOS Monterey 12.6.8 (21G725) Boot ISO 原版可引导镜像 本站下载的 macOS 软件包&#xff0c;既可以拖拽到 Applications&#xff08;应用程序&#xff09;下直接安装&#xff0c;也可以制作启动 U 盘安装&#xff0c;或者在虚拟机中启动安装。另外也支持在 Windows 和 Lin…

我的个人项目

项目介绍&#xff1a; 项目主要是使用vue3开发&#xff0c;利用前端技术&#xff0c;实现一些功能和游戏&#xff0c;并不是真正意义上的项目 功能概况介绍&#xff1a; 1. 记账&#xff08;app端页面记录日常消费和收入&#xff09; 2. 常用网站的管理 3. 文本内容检索 4. …

iptables安全技术和防火墙

防火墙&#xff1a;隔离功能 位置&#xff1a;部署在网络边缘或主机边缘&#xff0c;在工作中&#xff0c;防火墙的主要作用是决定哪些数据可以被外网访问以及哪些数据可以进入内网访问&#xff0c;主要在网络层工作 其他类型的安全技术&#xff1a;1、入侵检测系统 2、入侵…

vue2生命周期图

生命周期全过程如下&#x1f447;详解 一:生命周期之创建阶段 1.创建一个Vue实例【new Vue()】 2.初始化Vue实例,第一次初始化,初始化Vue当中的事件和生命周期方法【Init Events Lifecycle】 ☆☆☆调用生命周期方法当中的beforCreate,调用这个方法的时候有一个特点,就是在调…

自动化运维工具--saltstack部署及使用

目录 一、saltstack简介 1、介绍 2、Salt的核心功能 3、saltstack通信机制 二、saltstack部署 1、部署环境 2、配置yum源 3、安装master与minion 4、连接认证master和minion 三、salt运行 1、执行格式 2、实操演示 一、saltstack简介 1、介绍 saltstack是一个配置管…

[护网杯 2018]easy_tornado1

进入环境后有三个路径链接 访问/flag提示会说flag在/fllllllllllag里 值得注意的是访问后网页url发生的变化&#xff0c;这里居然有两个参数 替换掉filename的值后页面url再次发生变化&#xff0c;网页只有一个msg传参的数据 访问welcome.txt&#xff0c;只是回显了一个render…

一个灵活、现代的Android应用架构

一个灵活、现代的Android应用架构 学习Android架构的原则&#xff1a;学习原则&#xff0c;不要盲目遵循规则。 本文旨在通过示例演示实际应用&#xff1a;通过示范Android架构来进行教学。最重要的是&#xff0c;这意味着展示出如何做出各种架构决策。在某些情况下&#xff0…

qt登录框

#include "widget.h"Widget::Widget(QWidget *parent): QWidget(parent) {//窗口的设置this->setFixedSize(500, 400); //设置固定大小this->setWindowOpacity(0.95); //设置透明度this->setWindowTitle("鹏哥快聊"); // this->setWin…

【node-1】node validation exception. bootstrap checks failed

记录ElasticSearch 内存分配不足报错 背景做出的改变说在最后&#xff1a;最后访问es&#xff1a; 背景 从报错信息中看到&#xff0c;文件&#xff0c;虚拟内存的最大值太低&#xff0c;我们需要调整设置虚拟内存大小&#xff0c;以满足ElasticSearch 运行需求。 做出的改变 …

WPF icon的设置

想给控件设置个圆形图片&#xff0c;代码如下&#xff1a; ​<Setter Property"Icon"><Setter.Value><Image Source"/WpfApp1;component/Resource/1.ico" Width"16" Height"16"/></Setter.Value></Setter&…

面向对象编程:从创建类到封装与构造方法的探索

1. 代码如何创建类&#xff1f; 在面向对象编程中&#xff0c;类是对一类事物的抽象&#xff0c;包含了静态的属性&#xff08;成员变量&#xff09;和动态的行为&#xff08;成员方法&#xff09;。在Java中&#xff0c;创建类的格式如下&#xff1a; 修饰词 class 类名 {//…

react实现页面动态表单设计器(自定义推拽表单)

react实现页面动态表单设计器&#xff08;自定义推拽表单&#xff09; 实现效果安装插件使用组件介绍基本设置&#xff0c;可设置控件标签&#xff0c;是否必填&#xff0c;校验规则校验规则有如下几种多选&#xff0c;下拉&#xff0c;单选可动态设置每个选择的label以及值 实…

ROS Neotic(Ubuntu 20.04)如何正确安装OpenCV

自定义OpenCV版本 一、ROS的安装二、OpenCV编译安装三、cv_bridge编译安装四、温馨提示 一、ROS的安装 鱼香ROS提供了一个全面且方便的脚本&#xff0c;可以直接运行来安装系统对应版本的ROS&#xff0c;同时也可以使用该命令来安装许多有趣的东西 wget http://fishros.com/i…

Android 实现阅读用户协议的文字控件效果

开发中&#xff0c;经常要用到一些阅读隐私协议的场景&#xff0c;原生的textview控件很难做到在一个控件里有两个点击事件&#xff0c;那现在就来安利一个强大的组件——SpannableStringBuilder。 先看看效果&#xff1a; 直接上代码&#xff0c;布局文件&#xff1a; <Li…