一、字符设备驱动6-按键之定时器消抖

本文介绍了一种利用定时器实现按键防抖的方法,通过在中断处理中仅修改定时器超时时间,并在定时器回调函数中上报事件,确保一次按键仅触发一次事件上报。详细展示了基于Linux平台的具体实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引入

一次按下,因为抖动会发生多次中断。这样就有可能会引起驱动程序的一次按键按下,多次上报事件,这是不合理的。因此,需要某种手段来确保一次按键按下,仅上报事件一次。

原理

在中断中,先不上报事件,只修改定时器的超时时间,在定时器的回调函数里上报事件。如此一来,只要确保定时器的超时时间大于按键的抖动间隔,即可实现一次按键动作只有一次上报事件。

对于定时器,可以有一个合理的猜测:包含两方面,首先是超时时间,其次就是到时间后做什么,即处理函数。

操作函数

struct timer_list timer;  // 定义一个定时器
init_timer(&timer);  //在入口函数中初始化定时器
timer.data = xxx; // 这就是回调函数的参数  
timer.expires = jiffies + yyy; // 设置超时时间  
timer.function = (void (*)(unsigned long))zzz; // 回调函数  
add_timer(&timer); // 添加定时器  
mod_timer(&timer, jiffies + yyy); // 修改定时器超时时间  
del_timer(&timer); // 删除定时器  

源码

驱动程序:

/*
 * 引脚:PI0,1,2
 */

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/leds.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/module.h>
#include <linux/pinctrl/consumer.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/uaccess.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/timer.h>


struct pin_desc{
	int pin;
	int val;
};

/*
 * 按下时,返回:0x81, 0x82, 0x83
 * 
 */

static struct pin_desc pins_desc[3] = {
	{NUC970_PI0, 0x1},
	{NUC970_PI1, 0x2},
	{NUC970_PI2, 0x3},
};

static unsigned char val = 0;
static DECLARE_WAIT_QUEUE_HEAD(buttons_waitq);
static int evpress = 0;

static struct timer_list button_timer;

static irqreturn_t buttons_handler(int irq, void *dev_id)
{
	struct pin_desc *pin = (struct pin_desc *)dev_id;
	//int res;

	//res = gpio_get_value(pin->pin);
	
	val = 0x80 | pin->val;
	
	mod_timer(&button_timer, jiffies + 13);
	
	return IRQ_HANDLED;
}

static void buttons_timer_func(unsigned long data)
{
	if (val == 0)
		return;
	evpress = 1;
	wake_up_interruptible(&buttons_waitq);
}

static int buttons_open(struct inode *inode, struct file *filp)
{
	// request_irq会自动设置引脚,此处不再配置

	request_irq(gpio_to_irq(pins_desc[0].pin), buttons_handler, IRQF_TRIGGER_FALLING, "S1", &pins_desc[0]);
	request_irq(gpio_to_irq(pins_desc[1].pin), buttons_handler, IRQF_TRIGGER_FALLING, "S2", &pins_desc[1]);
	request_irq(gpio_to_irq(pins_desc[2].pin), buttons_handler, IRQF_TRIGGER_FALLING, "S3", &pins_desc[2]);

	init_timer(&button_timer);
	button_timer.function = buttons_timer_func;
	add_timer(&button_timer);
	
	return 0;
}

static ssize_t buttons_read (struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
	if (count != 1)
	{
		return -EINVAL;
	}

	wait_event_interruptible(buttons_waitq, evpress);
	evpress = 0;

	copy_to_user(buf, &val, 1);
	
	return 1;
}

int buttons_release (struct inode *inode, struct file *filp)
{
	free_irq(gpio_to_irq(pins_desc[0].pin), &pins_desc[0]);
	free_irq(gpio_to_irq(pins_desc[1].pin), &pins_desc[1]);
	free_irq(gpio_to_irq(pins_desc[2].pin), &pins_desc[2]);

	del_timer(&button_timer);
	
	return 0;
}

static unsigned int buttons_poll(struct file *file, poll_table *wait)
{
	unsigned int mask = 0;

	poll_wait(file, &buttons_waitq, wait); // 不会立即休眠,只是把进程挂到buttons_waitq队列

	if (evpress)
	{
		mask = POLLIN | POLLRDNORM;
	}

	return mask;
}


static struct file_operations buttons_fops = {
	.owner   = THIS_MODULE,
	.open    = buttons_open,
	.read    = buttons_read,
	.release = buttons_release,
	.poll    = buttons_poll,
};

static int major;
static struct class *buttons_class;
static struct device *button_device;

static int buttons_init(void)
{
	major = register_chrdev(0, "buttons", &buttons_fops);
	
	buttons_class = class_create(THIS_MODULE, "buttons");
	button_device = device_create(buttons_class, NULL, MKDEV(major, 0), NULL, "buttons");

	return 0;
}

static void buttons_exit(void)
{
	device_destroy(buttons_class, MKDEV(major, 0));
	class_destroy(buttons_class);

	unregister_chrdev(major, "buttons");
}

module_init(buttons_init);
module_exit(buttons_exit);
MODULE_LICENSE("GPL");

测试程序:

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

int main(void)
{
	int fd;
	unsigned char key_val;

	fd = open("/dev/buttons", O_RDONLY);
	if (fd < 0)
	{
		printf("Can't open /dev/buttons\n");
		return -1;
	}

	while (1)
	{
		read(fd, &key_val, 1);
		printf("key_val = 0x%x\n", key_val);
	}
	close(fd);
	return 0;
}


好的,让我们来了解一下 ArkTS 和线性布局的基本概念及其使用方法。 ### 什么是ArkTS? ArkTS(Ark TypeScript)是一种专为鸿蒙操作系统设计的前端开发语言。它是基于TypeScript的一种扩展,旨在简化跨平台应用开发过程,尤其是针对HarmonyOS的应用程序编写更为便捷高效。通过结合声明式UI框架和其他工具链的支持,开发者可以利用现代化JavaScript特性及静态类型检查的优势来进行应用程序构建。 ### 线性布局简介 在线性布局中,所有子组件按照单一方向排列——要么水平(一行内依次排列)、要么垂直(从上到下逐行排列)。这种布局方式简单直观,容易理解和实现,并非常适合于那些只需要在一维空间组织控件的情景之下。 #### 主要属性: - `direction`: 设置排列的方向,默认值是`vertical`(竖直),也可以设置成`horizontal`(水平)。 ```xml <LinearLayout direction="horizontal"> ``` - `alignItems`: 控制子项沿次要轴上的对齐方式,例如居中(`center`)、顶部(`flex-start`)或底部(`flex-end`)等。 ```xml <LinearLayout alignItems="center"> ``` - `justifyContent`: 决定主轴上的剩余空间如何分配给子元素们;常见的取值有开始端对其(`flex-start`)、末端对其(`flex-end`)、两端拉伸填充整个容器(`space-between`)等等。 ```xml <LinearLayout justifyContent="space-around"> ``` #### 示例代码片段 (XML): 下面是一个简单的例子展示了两个按钮分别放在横向线性布局两侧的效果: ```xml <LinearLayout xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:width="match_parent" ohos:height="wrap_content" ohos:direction="horizontal" ohos:justifyContent="space-between"> <Button id="buttonLeft" text="左"/> <Button id="buttonRight" text="右"/> </LinearLayout> ``` #### 在ArkTS中的示例代码(类似React JSX语法) 如果你正在使用ArkTS而不是直接操作原生XML,则可以用一种类似于JSX的方式去描述UI结构: ```javascript import { Column, Row, Button } from '@ohos/arkui'; export default function MyComponent() { return ( <Row direction="horizontal" justifyContent="space-between"> <Button>左边</Button> <Button>右边</Button> </Row> ); } ``` 以上就是关于ArkTS以及在其上下文中使用线性布局的一些基础知识了! ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值