当前位置: 首页 > news >正文

嵌入式Linux中的按键中断控制

前面讨论的都是输出控制,这里结合Pinctrl和GPIO子系统,以野火i.MX6ULL开发板为例,通过引脚中断方式去获取开发板上的一个按键状态。

先来看一下按键部分的电路原理图,如下所示。

111

从上图中可以看到,按键接在了SNVS_TAMPER1(GPIO5_1)端口。按键未按下时端口为低电平,按下时为高电平。

下面给出了本例使用的Pinctrl子系统节点内容。

&iomuxc_snvs {pinctrl-names = "default_snvs";pinctrl-0 = <&pinctrl_hog_2>;pinctrl_hog_2: hoggrp-2 {fsl,pins = <MX6ULL_PAD_SNVS_TAMPER0__GPIO5_IO00      0x80000000>;};pinctrl_spi4: spi4grp {fsl,pins = <MX6ULL_PAD_BOOT_MODE0__GPIO5_IO10         0x70a1MX6ULL_PAD_BOOT_MODE1__GPIO5_IO11         0x70a1MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07       0x70a1MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08       0x80000000>;};//以下为本次LED的追加内容
        pinctrl_button: buttongrp {fsl,pins = <MX6UL_PAD_SNVS_TAMPER1__GPIO5_IO01        0x10b0>;};
};

上述中,pinctrl_button: buttongrp即为本次追加的子节点。需要特别注意,SNVS_TAMPER1(GPIO5_1)端口的Pinctrl子节点要追加在iomuxc_snvs节点下,并不在iomuxc节点下。

下面给出本例GPIO子系统的节点内容。

button_interrupt {compatible = "fire,button";pinctrl-names = "default";pinctrl-0 = <&pinctrl_button>;button_gpio = <&gpio5 1 GPIO_ACTIVE_LOW>;interrupt-parent = <&gpio5>;nterrupts = <1 IRQ_TYPE_EDGE_RISING>;status = "okay";
};

把上述节点内容添加到imx6ull-mmc-npi.dts设备树文件的根节点内,完成后就可以编译设备树了,编译完成后替换开发板上的设备树文件,具体操作可参见“嵌入式Linux中的LED驱动控制(设备树方式)”一文中的相关部分。

接下来看平台驱动程序部分,下面是驱动的全部代码,文件名为led.c。

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/io.h>
#include <linux/of_irq.h>
#include <linux/device.h>
#include <linux/platform_device.h>
static unsigned char button_GPIO_number;  //GPIO引脚编号
static unsigned int  interrupt_number;    //引脚中断编号
static unsigned int  interrupt_type;      //中断类型
static dev_t devid;                       //设备号
static struct cdev btn_cdev;              //定义字符型结构体
static struct class *btn_class;           //类结构体
static struct device *device_button;      //创建的设备
static struct device_node *button_device_node;//定义按键设备节点结构体
atomic_t button_status = ATOMIC_INIT(0);  //定义整型原子变量,保存按键状态,设置初始值为0
//实现open函数,为file_oprations结构体成员函数
static int btn_open(struct inode *inode, struct file *filp)
{return 0;
}
//实现read函数,为file_oprations结构体成员函数
static int btn_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{int ret;int button = 0;button = atomic_read(&button_status);//读取按键状态值ret = copy_to_user(buf, &button, sizeof(button));//结果拷贝到用户空间if(ret < 0){printk("copy_to_user ret\n");return -EFAULT;}atomic_set(&button_status,0);//清零按键状态值return 0;
}
//实现release函数,为file_oprations结构体函数
static int btn_release(struct inode *inode, struct file *filp)
{return 0;
}
//填充一个file_oprations类型的结构体,名为button_chr_dev_fops,包含上述声明的成员函数
static struct file_operations button_chr_dev_fops = {.owner = THIS_MODULE,.open = btn_open,.read = btn_read,.release = btn_release,
};
//实现按键中断服务函数
static irqreturn_t btn_irq_hander(int irq, void *dev_id)
{atomic_inc(&button_status);    //按键状态值加1return IRQ_HANDLED;
}
/*----------------平台驱动函数集-----------------*/
static int btn_pdrv_probe(struct platform_device *pdv)
{int ret;//获取button_interrupt的设备树节点button_device_node = of_find_node_by_path("/button_interrupt");if(button_device_node == NULL){printk(KERN_ERR "get button_interrupt failed!\n");return -EFAULT;}//获取按键使用的GPIObutton_GPIO_number = of_get_named_gpio(button_device_node ,"button_gpio", 0);if(button_GPIO_number == 0){printk("of_get_named_gpio failed!\n");return -EFAULT;}//申请GPIO, 后面记得要释放ret = gpio_request(button_GPIO_number, "button_gpio");if(ret < 0){printk("gpio_request failed!\n");gpio_free(button_GPIO_number);return -EFAULT;}ret = gpio_direction_input(button_GPIO_number);//设置引脚为输入模式interrupt_number = irq_of_parse_and_map(button_device_node, 0);//获取中断号printk("Interrupt number is:%d\n",interrupt_number);interrupt_type = irq_get_trigger_type(interrupt_number);//获取中断类型printk("Interrupt type is:%d\n", interrupt_type);//申请中断, 后面记得要释放ret = request_irq(interrupt_number,btn_irq_hander,interrupt_type,"button_interrupt",device_button);if(ret != 0){printk("request_irq failed!\n");free_irq(interrupt_number, device_button);return -EFAULT;}//注意,上述申请成功之后中断已经开启了,不要再去执行enable_irq(interrupt_number)开启//下面申请主设备号if (alloc_chrdev_region(&devid, 0, 1, "btn") < 0){printk("failed to alloc devid\n");return -EFAULT;}btn_cdev.owner = THIS_MODULE;//绑定前面声明的file_oprations类型的结构体到字符设备cdev_init(&btn_cdev, &button_chr_dev_fops);//填充上面申请到的主设备号到字符设备if ( cdev_add(&btn_cdev, devid, 1) < 0){printk("failed to add cdev\n");return -EFAULT;}//创建一个类btn_class = class_create(THIS_MODULE, "my_btn");//创建一个设备节点device_create(btn_class, NULL, devid, NULL, "button");printk("platform driver probed!\n");return 0;
}
//remove函数中,删除设备并释放设备号
static int btn_pdrv_remove(struct platform_device *pdev)
{//释放申请的引脚和中断
  gpio_free(button_GPIO_number);free_irq(interrupt_number, device_button);unregister_chrdev_region(devid, 1);        //释放主设备号cdev_del(&btn_cdev);                       //删除字符设备device_destroy(btn_class, devid);          //销毁设备节点class_destroy(btn_class);                  //销毁类printk("platform driver removed!\n");return 0;
}
//填充of_device_id结构体,名为button,用于指明匹配表
static const struct of_device_id button[] = {{.compatible = "fire,button"},        //匹配内容{/* sentinel */}
};
//以下填充一个platform_driver结构体
struct platform_driver btn_platform_driver = {.probe = btn_pdrv_probe,        //指定probe函数成员.remove = btn_pdrv_remove,      //指定remove函数成员.driver = {.name = "button-platform",      //指定设备名称.owner = THIS_MODULE,.of_match_table = button,        //指定匹配表名称
    }
};
//以下定义模块的入口函数
static int __init btn_driver_init(void)
{platform_driver_register(&btn_platform_driver); //注册一个platform驱动printk("button platform driver initted!\n");return 0;
}
//以下定义模块的出口函数
static void __exit btn_driver_exit(void)
{platform_driver_unregister(&btn_platform_driver); //释放一个platform驱动printk("button platform driver exited!\n");
}
module_init(btn_driver_init);
module_exit(btn_driver_exit);
MODULE_INFO(intree,"Y");
MODULE_LICENSE("GPL");

配套的Makefile文件内容如下。

KERNEL_DIR=/opt/ebf_linux_kernel/build_image/build
ARCH=arm
CROSS_COMPILE=arm-linux-gnueabihf-
export  ARCH  CROSS_COMPILE
obj-m := led.o
all:$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules
modules clean:$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean

把上述驱动程序编译后,把生成的btn.ko文件通过NFS加载到开发板中,可查看到以下信息。

111

接下来是应用程序,文件名为app.c。

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{int fd, ret, button_status;fd = open("/dev/button", O_RDWR);    //打开设备节点if (fd < 0){printf("can`t open\n");return -1;}printf("Waitting button down...\n");do{//读取按键状态ret = read(fd, &button_status, sizeof(button_status));if (ret < 0)printf("read file error!\n");usleep(1000 * 100); //延时100毫秒}while(button_status == 0);printf("Button Down !\n");ret = close(fd);                    //关闭设备节点return 0;
}

在上述应用程序中,由于需要循环读取驱动中的按键状态,所以在循环体内需要加入一定的延时,以免程序无响应。把程序交叉编译后,通过NFS在开发板上运行,如下图所示。

111

可看到程序正在等待按键按下,这时按下开发板上的按键,程序立刻捕获到按键动作,显示按键已按下并结束程序,如下图所示。

111

本例通过捕获按键的的上升沿来设置按键状态,并未获取按键的值。而且只记录按键状态,所以只使用了中断“上半部分”,并未启用“下半部分”。如果在按键按下后要处理的事情较多,需要启用“下半部分”来进行。 

http://www.gsyq.cn/news/1568535.html

相关文章:

  • 义乌青阳路西福变速箱专修,19 年连锁老店,全车型变速箱一站式维修 联系电话:13735634594 地址:义乌青口工业区青阳路179号 - 速递信息
  • PUBG雷达系统终极指南:5分钟免费实现战场全透视
  • 郴州黄金回收哪家靠谱?2026本地正规门店排名+2026年6月21日黄金、铂金、钯金实时报价 - 小仙贝贝
  • Claude 3.5 Sonnet中文工作流实战:PDF解析、合同校验与Notion自动化
  • 郴州黄金回收哪家靠谱?2026最新避坑指南+正规门店排名(附速查卡) - 小仙贝贝
  • Zotero-SciHub插件技术深度解析:自动化文献获取的架构设计与实现
  • 5分钟完成Word到LaTeX转换:docx2tex终极指南
  • 防水行业拐点已至:告别低价内卷,全链路服务才是终局(郑州防水哪家好?怎么选?) - 速递信息
  • Ubuntu 20.04服务器初始配置:sudo加固、UFW零信任与服务精简
  • GLM-5开源:工程师级AI编码基座实战指南
  • 双A100部署Qwen 3.6-27B:128K长上下文推理实战优化
  • 2026郴州黄金回收靠谱门店推荐:防坑攻略及正规机构盘点 - 小仙贝贝
  • 5分钟上手Whisky:在Mac上无缝运行Windows软件的终极指南
  • 基于CAN总线的嵌入式Flash编程:原理、协议与工程实践
  • Fable 5 被关停前 72 小时,到底做出了哪些不像 AI 能做的应用?
  • 跨省电动车托运2026避坑指南 选对专线不被坑 - 快递物流资讯
  • Photoshop图层批量导出终极指南:如何快速免费导出所有图层
  • 破解户外智慧设施痛点:常州旗硕智慧科技有限公司光智物联三元驱动模型如何赋能智慧城市升级? - 速递信息
  • Cesium 天空盒教程
  • 用户代理切换工具:5个技巧轻松实现多设备统一管理
  • 天堂寨好吃的吊锅推荐哪家靠谱 本地人实测正宗测评榜 - 速递信息
  • RAG实战:从概念到生产级系统的7个关键环节
  • Ubuntu 20.04 LAMP生产就绪:Apache MySQL PHP兼容性配置指南
  • 零经验大学生简历模板:4个带案例的简历模板网站 - HR小张
  • 基于MC9S08AC16的无传感器BLDC电机控制:反电动势过零检测实战解析
  • ModTheSpire终极指南:如何轻松为《杀戮尖塔》安装和管理数百个创意模组
  • 抖店下单软件全解析,详解功能流程与技巧,覆盖采集、加密解密等用法 - 速递信息
  • 185、计算摄影的视频应用:AI EIS、AI 降噪、AI 超分在视频实时处理中的挑战
  • Ubuntu 18.04 + Unison 实现大目录双向安全同步
  • BSC9131异构多核调试实战:以太网TAP配置与CodeWarrior多核调试指南