linux驱动之按键

本文详细阐述了如何在Linux内核中实现按键设备,包括设备注册、中断处理和定时功能,通过insmod加载模块并使用cat/proc/devices查看设备号,mknod创建设备节点以供应用程序访问。

key.c

<span style="font-size:14px;">#include <linux/gpio.h>
#include <linux/timer.h>
#include <linux/slab.h>
#include <linux/module.h>		/* For module specific items */
#include <linux/moduleparam.h>		/* For new moduleparam's */
#include <linux/types.h>		/* For standard types (like size_t) */
#include <linux/errno.h>		/* For the -ENODEV/... values */
#include <linux/kernel.h>		/* For printk/panic/... */
#include <linux/fs.h>			/* For file operations */
#include <linux/ioport.h>		/* For io-port access */
#include <linux/platform_device.h>	/* For platform_driver framework */
#include <linux/init.h>			/* For __init/__exit/... */
#include <linux/uaccess.h>		/* For copy_to_user/put_user/... */
#include <linux/io.h>			/* For inb/outb/... */
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/cdev.h>

struct timer_list button_timer;
struct cdev cdev;
dev_t devno;
volatile unsigned long gpncon;
volatile unsigned long gpndat;
unsigned char key;//键值
struct key_desc {
	int irq;
	char *name;
	int pin;           /* GPNDAT哪位 */
	unsigned char val; /* 按键值 */
};

static DECLARE_WAIT_QUEUE_HEAD(button_waiq);

static struct key_desc key_desc[] = {
	{IRQ_EINT(0), "S2", 0, 2},
	{IRQ_EINT(1), "S3", 1, 3},
	{IRQ_EINT(2), "S4", 2, 4},
	{IRQ_EINT(3), "S5", 3, 5},
	{IRQ_EINT(4), "S6", 4, 6},
	{IRQ_EINT(5), "S7", 5, 7},
};

static ssize_t button_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	wait_event_interruptible(button_waiq, key);

	copy_to_user(buf, &key, 1);
	key = 0;
	return 1;
}

static struct file_operations button_fops = 
{
	.owner	=	THIS_MODULE,
	.read	=	button_read,
};

static irqreturn_t button_function(int irq, void *dev_id)
{
	struct key_desc *key_desc = (struct key_desc *)dev_id;
	disable_irq_nosync(key_desc->irq);//关闭按键中断
	mod_timer(&button_timer, jiffies + HZ/100);// 定时10ms
	button_timer.data = (unsigned long)dev_id;
	return IRQ_HANDLED;
}

static int button_time_function(unsigned long data)
{
	unsigned long value = readl(gpndat);
	struct key_desc *key_desc = (struct key_desc *)data;
	if(value & (1 << key_desc->pin))//如果已经松开,说明是抖动
	{
		enable_irq(key_desc->irq);//打开按键中断
		return 0;
	}
	else
	{
		wake_up_interruptible(&button_waiq);//唤醒等待队列
		key = key_desc->val;//记录键值	
	}
	return 1;
}

static int button_init()
{
	int i = 0;
	cdev_init(&cdev, &button_fops);//初始化字符设备
	alloc_chrdev_region(&devno, 0, 6, "mykey");//动态分配设备号,6个次设备号
	cdev_add(&cdev, devno, 6);//设备注册

	for(; i < 6; i++)//注册中断
	{
		request_irq(key_desc[i].irq, button_function, IRQF_TRIGGER_FALLING, key_desc[i].name, &key_desc[i]);
	}

	gpncon = ioremap(0x7f008830, 4);
	gpndat = gpncon + 1;

	init_timer(&button_timer);
	button_timer.function = button_time_function;
	add_timer(&button_timer);

	return 0;
}

static void button_exit()
{
	int i = 0;
	del_timer(&button_timer);

//	iounmap(gpncon);
//	iounmap(gpndat);
	
	for(; i < 6; i++)//注销中断
	{
		free_irq(key_desc[i].irq, &key_desc[i]);
	}
	
	cdev_del(&cdev);//卸载设备
	unregister_chrdev_region(devno, 6);//释放设备号

}

module_init(button_init);
module_exit(button_exit);
MODULE_LICENSE("GPL");</span>
当我们执行insmod key.ko时,程序执行button_init()函数,首先注册设备,可以通过cat /proc/devices查看所分配的设备号,执行mknod /dev/mykey c 252 0创建设备节点,应用程序以文件操作的方式处理该节点。

以struct cdev *cdev方式创建的是指针,只是在全局区或栈分配指针的空间,需要为struct cdev动态分配空间,在内核里使用kmalloc和kfree。

使用ctags查看内核源码:安装yum install ctags,ctags -R递归生成该目录下的所有函数,vi -t function查找函数,crtl+]跳到光标所在函数源码,ctrl+t返回,shift+K跳到光标所在函数手册。

按键可以使用这些中断号,这些中断号是VIC0、VIC1所分配的64个中断号之外的中断号。

主要知识:

1.字符设备结构体

<span style="font-size:14px;">struct cdev
{
	struct kobject kobj;
	struct module *owner;
	const struct file_operations *ops;
	struct list_head list;
	dev_t dev;
	unsigned int count;
};</span>
<span style="font-size:14px;">void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
    memset(cdev, 0, sizeof *cdev); 
    INIT_LIST_HEAD(&cdev->list);
    kobject_init(&cdev->kobj, &ktype_cdev_default);
    cdev->ops = fops; 
}
</span>
cdev_init()为cdev结构体绑定操作函数fops

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
            const char *name)
{
    struct char_device_struct *cd;
    cd = __register_chrdev_region(0, baseminor, count, name);
    if (IS_ERR(cd))
        return PTR_ERR(cd);
    *dev = MKDEV(cd->major, cd->baseminor);
    return 0;
}
int register_chrdev_region(dev_t from, unsigned count, const char *name)
{
    struct char_device_struct *cd;
    dev_t to = from + count;
    dev_t n, next;

    for (n = from; n < to; n = next) {
        next = MKDEV(MAJOR(n)+1, 0);
        if (next > to)
            next = to;
        cd = __register_chrdev_region(MAJOR(n), MINOR(n),
                   next - n, name);
        if (IS_ERR(cd))
            goto fail;
    }
    return 0;
fail:
    to = n;
    for (n = from; n < to; n = next) {
        next = MKDEV(MAJOR(n)+1, 0);
        kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
    }
    return PTR_ERR(cd);
}
alloc_chrdev_region动态分配设备号,register_chrdev_region指定分配的设备号,两者最终都将调用__register_chrdev_region
dev_t是一个32位的数,高12位表示主设备号,其余20位表示次设备号,可以使用MKDEV得到dev_t,也可以通过MAJOR(dev_t dev);和MINOR(dev_t dev);得到主次设备号
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
    p->dev = dev;
    p->count = count;
    return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
}

2.时钟

3.中断

key_app.c

<span style="font-size:14px;">#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <poll.h>
#include <signal.h>
#include <sys/types.h>

int main(int argc, char **argv)
{
	int fd = open("/dev/mykey", O_RDWR);
	char buf[2];//用于采集信息
	while(1)
	{
		read(fd, buf, 1);
		printf("the key %d is on\n",(int)buf[0]);
	}
	return 0;
}</span>

Makefile

<span style="font-size:14px;">obj-m := key.o
KDIR := /home/f/Desktop/share/linux-ok6410/
all:
	make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm
clean:
	rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.bak *.order
</span>

KDIR是编译目录,PWD是当前路径

【SCI一区复现】基于配电网韧性提升的应急移动电源预配置和动态调度(下)—MPS动态调度(Matlab代码实现)内容概要:本文档围绕“基于配电网韧性提升的应急移动电源预配置和动态调度”主题,重点介绍MPS(Mobile Power Sources)动态调度的Matlab代码实现,是SCI一区论文复现的技术资料。内容涵盖在灾害或故障等极端场景下,如何通过优化算法对应急移动电源进行科学调度,以提升配电网在突发事件中的恢复能力与供电可靠性。文档强调采用先进的智能优化算法进行建模求解,并结合IEEE标准测试系统(如IEEE33节点)进行仿真验证,具有较强的学术前沿性和工程应用价值。; 适合人群:具备电力系统基础知识和Matlab编程能力,从事电力系统优化、配电网韧性、应急电源调度等相关领域研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①用于复现高水平期刊(SCI一区、IEEE顶刊)中关于配电网韧性与移动电源调度的研究成果;②支撑科研项目中的模型构建与算法开发,提升配电网在故障后的快速恢复能力;③为电力系统应急调度策略提供仿真工具与技术参考。; 阅读建议:建议结合前篇“MPS预配置”内容系统学习,重点关注动态调度模型的数学建模、目标函数设计与Matlab代码实现细节,建议配合YALMIP等优化工具包进行仿真实验,并参考文中提供的网盘资源获取完整代码与数据。
一款AI短视频生成工具,只需输入一句产品卖点或内容主题,软件便能自动生成脚本、配音、字幕和特效,并在30秒内渲染出成片。 支持批量自动剪辑,能够实现无人值守的循环生产。 一键生成产品营销与泛内容短视频,AI批量自动剪辑,高颜值跨平台桌面端工具。 AI视频生成工具是一个桌面端应用,旨在通过AI技术简化短视频的制作流程。用户可以通过简单的提示词文本+视频分镜素材,快速且自动的剪辑出高质量的产品营销和泛内容短视频。该项目集成了AI驱动的文案生成、语音合成、视频剪辑、字幕特效等功能,旨在为用户提供开箱即用的短视频制作体验。 核心功能 AI驱动:集成了最新的AI技术,提升视频制作效率和质量 文案生成:基于提示词生成高质量的短视频文案 自动剪辑:支持多种视频格式,自动化批量处理视频剪辑任务 语音合成:将生成的文案转换为自然流畅的语音 字幕特效:自动添加字幕和特效,提升视频质量 批量处理:支持批量任务,按预设自动持续合成视频 多语言支持:支持中文、英文等多种语言,满足不同用户需求 开箱即用:无需复杂配置,用户可以快速上手 持续更新:定期发布新版本,修复bug并添加新功能 安全可靠:完全本地本地化运行,确保用户数据安全 用户友好:简洁直观的用户界面,易于操作 多平台支持:支持Windows、macOS和Linux等多个操作系统
源码来自:https://pan.quark.cn/s/2bb27108fef8 **MetaTrader 5的智能交易系统(EA)**MetaTrader 5(MT5)是由MetaQuotes Software Corp公司研发的一款广受欢迎的外汇交易及金融市场分析软件。 该平台具备高级图表、技术分析工具、自动化交易(借助EA,即Expert Advisor)以及算法交易等多项功能,使交易参与者能够高效且智能化地开展市场活动。 **抛物线SAR(Parabolic SAR)技术指标**抛物线SAR(Stop and Reverse)是由技术分析专家Wells Wilder所设计的一种趋势追踪工具,其目的在于识别价格走势的变动并设定止损及止盈界限。 SAR值的计算依赖于当前价格与前一个周期的SAR数值,随着价格的上扬或下滑,SAR会以一定的加速系数逐渐靠近价格轨迹,一旦价格走势发生逆转,SAR也会迅速调整方向,从而发出交易提示。 **Parabolic SAR EA的操作原理**在MetaTrader 5环境中,Parabolic SAR EA借助内嵌的iSAR工具来执行交易决策。 iSAR工具通过计算得出的SAR位置,辅助EA判断入市与离市时机。 当市场价位触及SAR点时,EA将产生开仓指令,倘若价格持续朝同一方向变动,SAR将同步移动,形成动态止损与止盈参考点。 当价格反向突破SAR时,EA会结束当前仓位并可能建立反向仓位。 **智能交易系统(EA)的优越性**1. **自动化交易**:EA能够持续监控市场,依据既定策略自动完成买卖操作,减少人为情感对交易的影响。 2. **精确操作**:EA依照预设规则操作,无任何迟疑,从而提升交易成效。 3. **风险管控**:借助SA...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值