【驱动篇】龙芯LS2K0300之按键驱动

news2024/11/19 10:35:57

实验过程

实验目的: 在龙芯开发板上面验证GPIO按键的输入过程

① 根据原理图连接按键板

② 将4个i2c引脚的功能复用为GPIO

③ 注册input设备驱动,绑定中断处理函数,使用定时器消抖

原理图

4个按键引脚:CPU_I2C0_SCL -> GPIO48, CPU_I2C0_SDA -> GPIO49, CPU_I2C1_SCL -> GPIO50, CPU_I2C1_SDA -> GPIO51

在这里插入图片描述

实物连接图:按顺序连接好按键板上面的K1、K2、K3、K4、GND

在这里插入图片描述

设备树

还是把i2c0和i2c1部分代码注释掉,需要把它们当作4个GPIO来使用

在这里插入图片描述

驱动程序

GPIO、中断和KEY的对应关系表

GPIO中断KEY引脚
482516
492618
502715
512817

定义相关按键设备结构体

#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/input.h>

#define KEY_INPUT	"key_input"
#define KEY_NUM	4

struct my_key_dev{
	struct input_dev *idev;
	struct timer_list timer;
	int key[KEY_NUM];
	int irq[KEY_NUM];	
	int index;	
};

struct my_key_dev key_dev;	

GPIO按键初始化

static int __init gpio_key_init(void)
{
	int ret = 0;
	int i = 0;
	
	// timer
	timer_setup(&key_dev.timer, key_timer_function, 0);

	for(i = 0; i < KEY_NUM; i++) {
		// gpio 48 49 50 51
		key_dev.key[i] = 48 + i;

		// request
		ret = gpio_request(key_dev.key[i], "LED-GPIO");
		if (ret) {
			printk(KERN_ERR "key_dev: Failed to request led-gpio\n");
			return ret;
		}

		// input
		ret = gpio_direction_input(key_dev.key[i]);
		if(ret < 0) {
			printk("can't set gpio!\r\n");
		}

		// irq
		key_dev.irq[i] = gpio_to_irq(key_dev.key[i]); 
		printk("key%d -> irq : %d\n", key_dev.key[i], key_dev.irq[i]);
		if(i == 0) {
			ret = request_irq(key_dev.irq[i], key_interrupt0, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "Key0_IRQ", NULL);
			if (ret) {
				gpio_free(key_dev.key[i]);
				return ret;
			}
		}
		else if(i == 1) {
			ret = request_irq(key_dev.irq[i], key_interrupt1, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "Key1_IRQ", NULL);
			if (ret) {
				gpio_free(key_dev.key[i]);
				return ret;
			}
		}
		else if(i == 2) {
			ret = request_irq(key_dev.irq[i], key_interrupt2, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "Key2_IRQ", NULL);
			if (ret) {
				gpio_free(key_dev.key[i]);
				return ret;
			}
		}
		else if(i == 3) {
			ret = request_irq(key_dev.irq[i], key_interrupt3, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "Key3_IRQ", NULL);
			if (ret) {
				gpio_free(key_dev.key[i]);
				return ret;
			}
		}
	}

	// input dev
	key_dev.idev = input_allocate_device();
	key_dev.idev->name = KEY_INPUT;
	key_dev.idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
	input_set_capability(key_dev.idev, EV_KEY, KEY_LEFT | KEY_RIGHT | KEY_UP | KEY_DOWN);
	ret = input_register_device(key_dev.idev);
	if (ret) {
		printk("register input device failed!\r\n");
		goto free_gpio;
	}

	return ret;

free_gpio:
	for(i = 0; i < KEY_NUM; i++) {
		free_irq(key_dev.irq[i],NULL);
		gpio_free(key_dev.key[i]);
		
	}

	del_timer_sync(&key_dev.timer);
	return -EIO;
}

中断处理

static irqreturn_t key_interrupt0(int irq, void *dev_id)
{
	// printk("key_interrupt0 irq %d \n", irq);
	if(irq == key_dev.irq[0]) {
		mod_timer(&key_dev.timer, jiffies + msecs_to_jiffies(15));
		key_dev.index = 0;
	}
    return IRQ_HANDLED;
}

static irqreturn_t key_interrupt1(int irq, void *dev_id)
{
	// printk("key_interrupt1 irq %d \n", irq);
	if(irq == key_dev.irq[1]) {
		mod_timer(&key_dev.timer, jiffies + msecs_to_jiffies(15));
		key_dev.index = 1;
	}
    return IRQ_HANDLED;
}

static irqreturn_t key_interrupt2(int irq, void *dev_id)
{
	// printk("key_interrupt2 irq %d \n", irq);
	if(irq == key_dev.irq[2]) {
		mod_timer(&key_dev.timer, jiffies + msecs_to_jiffies(15));
		key_dev.index = 2;
	}
    return IRQ_HANDLED;
}

static irqreturn_t key_interrupt3(int irq, void *dev_id)
{
	// printk("key_interrupt3 irq %d \n", irq);
	if(irq == key_dev.irq[3]) {
		mod_timer(&key_dev.timer, jiffies + msecs_to_jiffies(15));
		key_dev.index = 3;
	}
    return IRQ_HANDLED;
}

定时器消抖

int key_array[] = {KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN};

static void key_timer_function(struct timer_list *arg)
{
	int val = gpio_get_value(key_dev.key[key_dev.index]);
	printk("key_timer_function %d -> %d\n", key_dev.key[key_dev.index], val);
	// if(val == 0) {
		input_report_key(key_dev.idev, key_array[key_dev.index], !val);
		input_sync(key_dev.idev);

		// input_report_key(key_dev.idev, key_array[key_dev.index], 0);
		// input_sync(key_dev.idev);
		// printk("key %d press %d\n", key_dev.key[key_dev.index], key_array[key_dev.index]);
	// }
}

驱动模块卸载函数:回收相关的设备资源,如GPIO、中断、定时器等

static void __exit gpio_key_exit(void)
{
	int i;
	for(i = 0; i < KEY_NUM; i++) {
		free_irq(key_dev.irq[i],NULL);
		gpio_free(key_dev.key[i]);
	}

	del_timer_sync(&key_dev.timer);
	input_unregister_device(key_dev.idev);	
}

整合代码

#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/input.h>

#define KEY_INPUT	"key_input"
#define KEY_NUM	4

struct my_key_dev{
	struct input_dev *idev;
	struct timer_list timer;
	int key[KEY_NUM];
	int irq[KEY_NUM];	
	int index;	
};

struct my_key_dev key_dev;	

int key_array[] = {KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN};

static void key_timer_function(struct timer_list *arg)
{
	int val = gpio_get_value(key_dev.key[key_dev.index]);
	printk("key_timer_function %d -> %d\n", key_dev.key[key_dev.index], val);
	// if(val == 0) {
		input_report_key(key_dev.idev, key_array[key_dev.index], !val);
		input_sync(key_dev.idev);

		// input_report_key(key_dev.idev, key_array[key_dev.index], 0);
		// input_sync(key_dev.idev);
		// printk("key %d press %d\n", key_dev.key[key_dev.index], key_array[key_dev.index]);
	// }
}

static irqreturn_t key_interrupt0(int irq, void *dev_id)
{
	// printk("key_interrupt0 irq %d \n", irq);
	if(irq == key_dev.irq[0]) {
		mod_timer(&key_dev.timer, jiffies + msecs_to_jiffies(15));
		key_dev.index = 0;
	}
    return IRQ_HANDLED;
}

static irqreturn_t key_interrupt1(int irq, void *dev_id)
{
	// printk("key_interrupt1 irq %d \n", irq);
	if(irq == key_dev.irq[1]) {
		mod_timer(&key_dev.timer, jiffies + msecs_to_jiffies(15));
		key_dev.index = 1;
	}
    return IRQ_HANDLED;
}

static irqreturn_t key_interrupt2(int irq, void *dev_id)
{
	// printk("key_interrupt2 irq %d \n", irq);
	if(irq == key_dev.irq[2]) {
		mod_timer(&key_dev.timer, jiffies + msecs_to_jiffies(15));
		key_dev.index = 2;
	}
    return IRQ_HANDLED;
}

static irqreturn_t key_interrupt3(int irq, void *dev_id)
{
	// printk("key_interrupt3 irq %d \n", irq);
	if(irq == key_dev.irq[3]) {
		mod_timer(&key_dev.timer, jiffies + msecs_to_jiffies(15));
		key_dev.index = 3;
	}
    return IRQ_HANDLED;
}

static int __init gpio_key_init(void)
{
	int ret = 0;
	int i = 0;
    
    // timer
	timer_setup(&key_dev.timer, key_timer_function, 0);

	for(i = 0; i < KEY_NUM; i++) {
		// gpio 48 49 50 51
		key_dev.key[i] = 48 + i;

		// request
		ret = gpio_request(key_dev.key[i], "LED-GPIO");
		if (ret) {
			printk(KERN_ERR "key_dev: Failed to request led-gpio\n");
			return ret;
		}

		// input
		ret = gpio_direction_input(key_dev.key[i]);
		if(ret < 0) {
			printk("can't set gpio!\r\n");
		}

		// irq
		key_dev.irq[i] = gpio_to_irq(key_dev.key[i]); 
		printk("key%d -> irq : %d\n", key_dev.key[i], key_dev.irq[i]);
		if(i == 0) {
			ret = request_irq(key_dev.irq[i], key_interrupt0, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "Key0_IRQ", NULL);
			if (ret) {
				gpio_free(key_dev.key[i]);
				return ret;
			}
		}
		else if(i == 1) {
			ret = request_irq(key_dev.irq[i], key_interrupt1, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "Key1_IRQ", NULL);
			if (ret) {
				gpio_free(key_dev.key[i]);
				return ret;
			}
		}
		else if(i == 2) {
			ret = request_irq(key_dev.irq[i], key_interrupt2, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "Key2_IRQ", NULL);
			if (ret) {
				gpio_free(key_dev.key[i]);
				return ret;
			}
		}
		else if(i == 3) {
			ret = request_irq(key_dev.irq[i], key_interrupt3, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "Key3_IRQ", NULL);
			if (ret) {
				gpio_free(key_dev.key[i]);
				return ret;
			}
		}
	}

	// input dev
	key_dev.idev = input_allocate_device();
	key_dev.idev->name = KEY_INPUT;
	key_dev.idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
	input_set_capability(key_dev.idev, EV_KEY, KEY_LEFT | KEY_RIGHT | KEY_UP | KEY_DOWN);
	ret = input_register_device(key_dev.idev);
	if (ret) {
		printk("register input device failed!\r\n");
		goto free_gpio;
	}

	return ret;

free_gpio:
	for(i = 0; i < KEY_NUM; i++) {
		free_irq(key_dev.irq[i],NULL);
		gpio_free(key_dev.key[i]);
		
	}

	del_timer_sync(&key_dev.timer);
	return -EIO;
}

static void __exit gpio_key_exit(void)
{
	int i;
	for(i = 0; i < KEY_NUM; i++) {
		free_irq(key_dev.irq[i],NULL);
		gpio_free(key_dev.key[i]);
	}

	del_timer_sync(&key_dev.timer);
	input_unregister_device(key_dev.idev);	
}

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

Makefile文件

obj-m += key.o 
KDIR:=/home/asensing/loongson/linux-4.19
ARCH=loongarch 
CROSS_COMPILE=loongarch64-linux-gnu-
PWD?=$(shell pwd) 
all:
	make -C $(KDIR) M=$(PWD) modules 

构建脚本

export PATH=$PATH:/home/asensing/loongson/loongson-gnu-toolchain-8.3-x86_64-loongarch64-linux-gnu-rc1.3-1/bin
make -j8
# loongarch64-linux-gnu-gcc test.c -o test
FILE=$PWD/$(basename $PWD).ko
scp $FILE root@192.168.137.11:/home/root

实验效果

插入驱动后,按下按键立即打印4个按键值

在这里插入图片描述

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

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

相关文章

全栈人工智能工程师:现代博学者

任何在团队环境中工作过的人都知道&#xff0c;每个成功的团队都有一个得力助手——无论你的问题性质如何&#xff0c;他都能帮助你。在传统的软件开发团队中&#xff0c;这个人是一个专业的程序员&#xff0c;也是另一种技术的专家&#xff0c;可以是像Snowflake这样的数据库技…

【windows|010】OSI七层模型和TCP/IP五层模型详解

&#x1f341;博主简介&#xff1a; &#x1f3c5;云计算领域优质创作者 &#x1f3c5;2022年CSDN新星计划python赛道第一名 &#x1f3c5;2022年CSDN原力计划优质作者 ​ &#x1f3c5;阿里云ACE认证高级工程师 ​ &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社…

植物大战僵尸杂交版2024最新官方原版iOS手机+mac电脑版下载

&#x1f331; 绿意盎然的游戏体验&#xff0c;《植物大战僵尸杂交版》带你开启全新战斗模式 亲爱的小伙伴们&#xff0c;今天我要和大家分享一款让人眼前一亮的游戏——《植物大战僵尸杂交版》&#xff01;这款游戏在经典的基础上进行了大胆创新&#xff0c;给玩家带来了前所未…

手把手带你从零构建一个用于讲故事的 LLM

教程 LLM101&#xff1a;创建能讲故事的 LLM LLM101n 是 llm.c 作者开的新坑&#xff0c;一个系列教程&#xff0c;手把手带你从零构建一个用于讲故事的 LLM&#xff0c;目前只写了目录已经斩获 5.8k star。

户外龙头边城体育签约实在智能,打造财务数字化转型标杆!

财务工作在企业中是一项极其重要且复杂的任务&#xff0c;需要具备高度的准确性和及时性。尽管应用财务软件&#xff0c;企业实现了财务数据的电子化和数字化&#xff0c;但仍然面临着数据采集与处理的实时性和全面性问题&#xff0c;以及由此带来的风险控制和决策支持的不足。…

运动蓝牙耳机哪个口碑最好?五大高口碑顶尖单品推荐

在这个快节奏时代&#xff0c;智能手机的普及使得运动开放式耳机逐渐成为我们日常出行的必备单品。运动开放式耳机凭借独特的外形设计&#xff0c;赢得了众多消费者的喜爱。它们不同于传统的入耳式设计&#xff0c;以舒适佩戴为核心&#xff0c;有效缓解了长时间佩戴对耳部造成…

【机器学习】自然语言处理的新前沿:GPT-4与Beyond

&#x1f4dd;个人主页&#xff1a;哈__ 期待您的关注 目录 &#x1f525;引言 背景介绍 文章目的 一、GPT-4简介 GPT-4概述 主要特性 局限性和挑战 二、自监督学习的新进展 自监督学习的原理 代表性模型和技术 三、少样本学习和零样本学习 少样本学习的挑战 先…

zkWASM:ZK+zkVM的下一站?

1. 引言 ZK技术具备极大通用性&#xff0c;也帮助以太坊从去中心化投资走向去信任化的价值观。“Don’t trust, Verify it!”&#xff0c;是ZK技术的最佳实践。ZK技术能够重构链桥、预言机、链上查询、链下计算、虚拟机等等一系列应用场景&#xff0c;而通用型的ZK协处理器就是…

Adaptive Server Connection Failed on Windows

最近在使用pymssql &#xff08;版本2.3.0&#xff09;连接SQL Server2012遇到如下问题&#xff1a; pymssql._mssql.MSSQLDatabaseException: (20002, bDB-Lib error message 20002, severity 9:\nAdaptive Server connection failed (localhost)\nDB-Lib error message 2000…

前端也需要知道的一些常用linux命令

前端也需要知道的一些常用linux命令 1.问题背景2.连接工具&#xff08;SecureCRT_Portable&#xff09;a.下载工具b.连接服务器c.登录到root账户 3.基本命令a.cd命令和cd ..b.ll命令和ls命令c:cp命令d.rm命令e:rz命令f.unzip命令g.mv命令h.pwd命令&#xff08;这里没有用到&…

Linux基础二

目录 一&#xff0c;tail查看文件尾部指令 二&#xff0c;date显示日期指令 三&#xff0c;cal查看日历指令 四&#xff0c;find搜索指令 五&#xff0c;grep 查找指令 六&#xff0c;> 和>> 重定向输出指令 七&#xff0c; | 管道指令 八&#xff0c;&&逻辑控…

如何发现Redis热Key,有哪些解决方案?

什么是 hotkey&#xff1f; 如果一个 key 的访问次数比较多且明显多于其他 key 的话&#xff0c;那这个 key 就可以看作是 hotkey&#xff08;热 Key&#xff09;。例如在 Redis 实例的每秒处理请求达到 5000 次&#xff0c;而其中某个 key 的每秒访问量就高达 2000 次&#x…

【AI大模型】驱动的未来:穿戴设备如何革新血液、皮肤检测与营养健康管理

文章目录 1. 引言2. 现状与挑战3. AI大模型与穿戴设备概述4. 数据采集与预处理4.1 数据集成与增强4.2 数据清洗与异常检测 5. 模型架构与训练5.1 高级模型架构5.2 模型训练与调优 6. 个性化营养建议系统6.1 营养建议生成优化6.2 用户反馈与系统优化 7. 关键血液成分与健康状况评…

grpc教程——proto文件转go

【1】编写一个proto文件 syntax "proto3"; package myproto;service NC{rpc SayStatus (NCRequest) returns (NCResponse){} }message NCRequest{ string name 1; } message NCResponse{string status 1; } 【2】转换&#xff1a;protoc --go_out. myservice.pro…

LLM Agent提效进阶:反思工作流——91%精度大超GPT-4 24%

1. 相关研究 反思依赖于LLM对自己之前提出的工作进行反思并提出改进的方法&#xff0c;有三篇典型论文详细描述了这种模式&#xff0c;我们先来看一下。 2. Self-Refine 顾名思义&#xff0c;它是一种自我精炼的LLM优化技术&#xff0c;使用单一的LLM作为生成器、改进器和反…

go语言day4 引入第三方依赖 整型和字符串转换 进制间转换 浮点数 字符串

Golang依赖下载安装失败解决方法_安装go依赖超时怎么解决-CSDN博客 go安装依赖包&#xff08;go get, go module&#xff09;_go 安装依赖-CSDN博客 目录 go语言项目中如何使用第三方依赖&#xff1a;&#xff08;前两步可以忽略&#xff09; 一、安装git&#xff0c;安装程序…

Python编程技巧:如何正确使用with语句(Python中with用法详解)

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 文章内容 📒📝 基本语法📝 处理文件📝 处理网络连接📝 管理线程锁📝 管理数据库连接📝 管理临时目录和文件📝 使用上下文装饰器📝 自定义上下文管理器🎯 示例1🎯 示例2📝 使用多个上下文管理器📝 上下…

格雷码计数器

目录 描述 输入描述&#xff1a; 输出描述&#xff1a; 参考代码 描述 实现4bit位宽的格雷码计数器。 电路的接口如下图所示。 输入描述&#xff1a; input clk, input rst_n 输出描述&#xff1a; output reg [3:0] gray_out 参考代码 timescale 1ns/1nsmod…

Apple创始人斯蒂夫乔布斯2005年在斯坦福大学的毕业典礼演讲:Steve Jobs‘ 2005 Stanford Commencement Address

Steve Jobs’ 2005 Stanford Commencement Address Link: https://www.youtube.com/watch?vUF8uR6Z6KLc and https://www.youtube.com/watch?vHd_ptbiPoXM 文章目录 Steve Jobs 2005 Stanford Commencement AddressSummaryVocabularyTranscriptConnecting the dotsLove and …

LeetCode35.搜索插入位置

LeetCode刷题记录 文章目录 &#x1f4dc;题目描述&#x1f4a1;解题思路⌨C代码 &#x1f4dc;题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。 如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须…