548|1

53

帖子

0

TA的资源

一粒金砂(中级)

楼主
 

【玄铁杯第三届RISC-V应用创新大赛】lpi4a 5. 按键驱动程序 [复制链接]

 

licheepi 4a开发板,提供了4个按键,REC,SHUT,PWR,WAKE,我试了,PWR关机键,是用来开关机的,不能编写程序,那么另外3个对应的GPIO为

SHUT     GPIO1_0
WAKE    AOGPIO_2
REC      GPIO1_9

使用命令sudo cat /sys/kernel/debug/gpio,查看GPIO1_0对应的GPIO号码为448,驱动SHUT按键的代码如下:

驱动程序

#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/gpio.h>
#include <linux/device.h>
#include <linux/semaphore.h>
#include <linux/irq.h>


// GPIO号,GPIO1_0
#define SHUT_GPIO 448

/*------------------ 字符设备内容 ----------------------*/
#define SHUTKEY_NAME "shutkey"
#define SHUTKEY_CNT (1)

/*------------------ 设备数据结构体 ----------------------*/
struct key_dev_t
{
	dev_t devid;			  // 设备号
	struct cdev cdev;		  // cdev
	struct class *class;	  // 类
	struct device *device;	  // 设备
	struct device_node *nd;	  // 设备节点
	int irq_num;			  // 中断号
	int gpio;				  // 数据接收引脚
	atomic_t value;			  // 按键值
	atomic_t iskey;			  // 有键按下或松开
	wait_queue_head_t r_wait; // 定义等待队列头
};
struct key_dev_t key_dev; // 设备数据结构体

static void timer_func(struct timer_list *t); // 声明定时器消抖函数

DEFINE_TIMER(timer, timer_func); // 定义一个定时器, 内部有static struct timer_list timer_test;

static void timer_func(struct timer_list *t)
{
	//long long now = ktime_to_us(ktime_get());
	if (gpio_get_value(SHUT_GPIO)) // 松开
	{
		//printk("key up %lld\n", now);
		atomic_set(&key_dev.value, 0);
	}else{ // 按下
		//printk("key down %lld\n", now);
		atomic_set(&key_dev.value, 1);
	}

	atomic_set(&key_dev.iskey, 1);//有键产生
	wake_up(&key_dev.r_wait); // 呼醒进程
}

// 中断回调函数
static irqreturn_t key_interrupt(int irq, void *dev_id)
{
	mod_timer(&timer, jiffies + msecs_to_jiffies(50)); // 50毫秒定时器

	return IRQ_HANDLED;
}

// 按键初始化
static int shut_init(void)
{
	int res;

	/* 申请 GPIO 资源 */
	key_dev.gpio = SHUT_GPIO;
	res = gpio_request(key_dev.gpio, SHUTKEY_NAME);
	if (res)
	{
		pr_err("key dev: Failed to request gpio\n");
		return res;
	}

	/* 将 GPIO 设置为输入模式 */
	gpio_direction_input(key_dev.gpio);

	/* 申请中断 */
	key_dev.irq_num = gpio_to_irq(key_dev.gpio);
	res = request_irq(key_dev.irq_num, key_interrupt, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, SHUTKEY_NAME, NULL); // 双边沿触发
	if (res)
	{
		gpio_free(key_dev.gpio);
		return res;
	}else{
		printk("irq_num: %d\n", key_dev.irq_num);
	}

	return 0;
}

// 打开设备
static int key_open(struct inode *inode, struct file *filp)
{
	/* 将设备数据设置为私有数据 */
	filp->private_data = &key_dev;

	printk("key_open\n");
	return 0;
}

// 从设备读取数据
static ssize_t key_read(struct file *filp, char __user *buf, size_t count, loff_t *offt)
{
	int res = 0;
	struct key_dev_t *dev = filp->private_data;
	char value;

	wait_event_interruptible(dev->r_wait, atomic_read(&key_dev.iskey)); // 没有按键,进入阻塞

	atomic_set(&key_dev.iskey, 0);//按键读取后,清0

	value = atomic_read(&key_dev.value);//重新读取按键值
	res = copy_to_user(buf, &value, sizeof(value));
	if (res != 0)
	{
		return -1;
	}

	return 0;
}

// 关闭/释放设备
static int key_release(struct inode *inode, struct file *filp)
{
	int res = 0;
	printk("key_release\n");

	return res;
}

/* 设备操作函数结构体 */
static struct file_operations key_ops = {
	.owner = THIS_MODULE,
	.open = key_open,
	.read = key_read,
	.release = key_release,
};

// 注册字符设备驱动
static int key_register(void)
{
	int ret = -1; // 保存错误状态码

	/* GPIO 中断初始化 */
	ret = shut_init();

	/* 1、创建设备号 */
	/* 采用动态分配的方式,获取设备编号,次设备号为0 */
	/* 设备名称为 SHUTKEY_NAME,可通过命令 cat /proc/devices 查看 */
	/* SHUTKEY_CNT 为1,只申请一个设备编号 */
	ret = alloc_chrdev_region(&key_dev.devid, 0, SHUTKEY_CNT, SHUTKEY_NAME);
	if (ret < 0)
	{
		pr_err("%s Couldn't alloc_chrdev_region, ret = %d \r\n", SHUTKEY_NAME, ret);
		goto fail_region;
	}

	/* 2、初始化 cdev */
	/* 关联字符设备结构体 cdev 与文件操作结构体 file_operations */
	key_dev.cdev.owner = THIS_MODULE;
	cdev_init(&key_dev.cdev, &key_ops);

	/* 3、添加一个 cdev */
	/* 添加设备至cdev_map散列表中 */
	ret = cdev_add(&key_dev.cdev, key_dev.devid, SHUTKEY_CNT);
	if (ret < 0)
	{
		pr_err("fail to add cdev \r\n");
		goto del_unregister;
	}

	/* 4、创建类 */
	key_dev.class = class_create(THIS_MODULE, SHUTKEY_NAME);
	if (IS_ERR(key_dev.class))
	{
		pr_err("Failed to create device class \r\n");
		goto del_cdev;
	}

	/* 5、创建设备,设备名是 SHUTKEY_NAME */
	/*创建设备 SHUTKEY_NAME 指定设备名,*/
	key_dev.device = device_create(key_dev.class, NULL, key_dev.devid, NULL, SHUTKEY_NAME);
	if (IS_ERR(key_dev.device))
	{
		goto destroy_class;
	}

	atomic_set(&key_dev.value, 0); // 初始化元子变量
	atomic_set(&key_dev.iskey, 0);

	init_waitqueue_head(&key_dev.r_wait); // 初始化等待队列

	return 0;

destroy_class:
	device_destroy(key_dev.class, key_dev.devid);
del_cdev:
	cdev_del(&key_dev.cdev);
del_unregister:
	unregister_chrdev_region(key_dev.devid, SHUTKEY_CNT);
fail_region:
	/* 释放个人初始化申请的资源,如del_init(); */
	free_irq(key_dev.irq_num, NULL);
	gpio_free(key_dev.gpio);
	return -EIO;
}

// 注销字符设备驱动
static void key_unregister(void)
{
	/* 1、删除 cdev */
	cdev_del(&key_dev.cdev);
	/* 2、注销设备号 */
	unregister_chrdev_region(key_dev.devid, SHUTKEY_CNT);
	/* 3、注销设备 */
	device_destroy(key_dev.class, key_dev.devid);
	/* 4、注销类 */
	class_destroy(key_dev.class);

	/* 释放中断 */
	free_irq(key_dev.irq_num, NULL);
	/* 释放 IO */
	gpio_free(key_dev.gpio);

	// 删除定时器
	del_timer(&timer);
}

// 驱动入口函数
static int __init key_driver_init(void)
{
	pr_info("key_driver_init\n");
	return key_register();
}

// 驱动出口函数
static void __exit key_driver_exit(void)
{
	pr_info("key_driver_exit\n");
	key_unregister();
}

/* 将上面两个函数指定为驱动的入口和出口函数 */
module_init(key_driver_init);
module_exit(key_driver_exit);

/* LICENSE 和作者信息 */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("乘简");

 

应用程序 

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char* argv[]){
    int fd,ret;
    char value;

    fd=open("/dev/shutkey", O_RDWR);
    if(fd<0){
        printf("can't open file %s",argv[1]);
        return -1;
    }

    while(1){
        ret=read(fd,&value,sizeof(value));
        if(ret<0){
            printf("read error\n");
            return -1;
        }else{
            if(value){
                printf("shut key down!\n");
            }else{
                printf("shut key up!\n");
            }
        }
    }

    ret=close(fd);
    return 0;
}

按键后执行结果:

 

最新回复

感谢楼主提供的技术分享,先收藏学习再发表个人意见,顶起来   详情 回复 发表于 2023-11-23 19:10
点赞 关注
 
 

回复
举报

745

帖子

5

TA的资源

纯净的硅(高级)

沙发
 

感谢楼主提供的技术分享,先收藏学习再发表个人意见,顶起来

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表