linux按键驱动设计(V3S开发板) 键盘在linux驱动层属于什么设备
csdh11 2024-12-30 03:05 3 浏览
1.前言
本文描述了基于全志V3S开发板的按键驱动程序和测试应用程序的设计流程。
本次设计系统内核是基于linux3.4。
2.设计流程概述
本次设计的步骤是:
步骤一、编写一个driver_button.c的驱动程序。
步骤二、编写makefile文件,编译得到ko。
步骤三、编写一个app_button.c的测试应用程序。
步骤四、在V3S开发板中安装demo_driver驱动程序,并测试app_button应用程序。
3.编写驱动程序
3.1硬件电路
V3S开发板按键的硬件电路原理图如下:
根据原理图可知不同的按键按下对应不同的电压值,使用ADC采样LRADC0处的电压就可以识别是否有按键被按下,例如按键KEY3按下时LRADC0处的电压为0.6V。
我们查看V3S芯片数据手册,看看如何操作V3S芯片LRADC,V3S芯片关于LRADC的关键信息如下(详细数据请参考数据手册):
LRADC的控制寄存器信息如下(LRADC寄存器详细数据请参考数据手册):
我们只需要把LRADC配置成连续采样模式即可,具体配置如下:
LRADC_CTRL = 0x00C00041
LRADC_INTC = 0x00000013
3.2设计要点
本次按键驱动设计使用了以下两个知识点:
1、内核定时器使用
2、阻塞操作 (等待队列)
我们先学习一下这两个知识点。
3.2.1内核定时器
Linux内核定时器是一种基于时间点的计时方式,它以当前时刻为时间起点,以未来的某一时刻为终点,这种策略类似于闹钟。
Linux内核定时器的精度不高,不能作为高精度定时器使用。内核定时器并不是周期性运行,如果要想实现周期性的定时,就需要在定时中断处理函数中重新开启定时器。
使用Linux内核定时器需要增加如下头文件:
#include <linux/timer.h>
#include <linux/jiffies.h>
Linux内核定时器使用分为以下4个步骤:
1、声明定义一个定时器
struct timer_list timer;
2、初始化定时器
/* 初始化定时器 */
init_timer(&timer);
/* 设置定时处理函数 */
timer.function = time_callback;
/* 设置定时时间 */
timer.expires=jiffies + msecs_to_jiffies(100);
3、声明定义定时器中断回调函数
在回调函数中再次设置定时时间,实现周期定时
void time_callback(unsigned long arg)
{
/* 设置定时器 */
mod_timer(&timer, jiffies + msecs_to_jiffies(TIME_NUM));
}
4、启动定时器
add_timer(&timer);
通过以上的4个步骤,我们就可以正常使用Linux内核定时器了。
3.2.2阻塞操作
相信大家去吃一些美味小食时都排队的经历吧,这种漫长的等待经历是不是还历历在目?除了在队伍里傻站着和刷刷手机,啥也干不了。
但是有些商场就有一种很好的等待机制:无线提示器。客户点餐后就可以在一定范围内自由活动,当你点的餐做好了,无线提示器会提示你去取餐。这种方式可以极大的减轻客户站立排队的低效和痛苦。
回到上面排队的例子,第一种站立式排队模式,当你处在这种等待模式下时你必须一直保存这种排队等待的状态,直到你买到你需要的商品。这种等待方式属于非阻塞式等待。
第二种无线提示器模式,当你完成商品付款后,就可以在一定范围内自由活动,直到收到无线提示器会提示你。这种等待方式属于阻塞式等待。
非常明显在大多数情况下阻塞式等待会更加高效。
我们使用linux等待队列实现阻塞操作,当事件未准备好时任务进入挂起状态,当事件准备好时唤醒任务。
使用等待队列需要包含如下头文件:
#include <linux/wait.h>
#include <linux/sched.h>
linux等待队列操作分为以下4个步骤:
1、定义等待队列和等待标志
wait_queue_head_t button_wait;
bool key_ready = false;
2、初始化等待队列
init_waitqueue_head(&button_wait);
3、进入等待
wait_event(button_wait, key_ready);
4、唤醒任务
需要在另外一处可以执行到的代码出执行唤醒代码
key_ready = true;
wake_up(&button_wait);
通过以上的4个步骤,我们就可以正常使用Linux等待队列进行阻塞操作了。
3.3按键驱动实现
描本次按键驱动程序策略如下:
1、应用程序通过驱动程序的read函数读取按键值,当检测有按键被按键时read函数返回按键值,当无按键按下时read函数使用等待队列让应用程序挂起(不继续运行)。
2、驱动程序中设置一个周期为10ms内核定时器,在定时器中断回调函数中检测按键是否被按下,并做去抖操作。如果在定时器中断回调函数中确定有按键被按下,此时唤醒因为等待按键被挂起的应用程序。
策略框图如下:
3.4驱动代码
编写一个demo_driver.c的驱动程序,驱动程序源码如下(代码不再作解释说明,可以参数代码注释):
/**
*********************************************************************************************************
* driver_button
* (c) Copyright 2021-2031
* All Rights Reserved
*
* @File :
* @By : liwei
* @Version : V0.01
*
*********************************************************************************************************
**/
/**********************************************************************************************************
Includes
**********************************************************************************************************/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/cdev.h>
#include <linux/io.h> /* io remap */
#include <linux/ioport.h>
#include <linux/timer.h> /* time */
#include <linux/jiffies.h>
#include <linux/uaccess.h> /* copy */
#include <linux/wait.h> /* wait */
#include <linux/sched.h>
/**********************************************************************************************************
Define
**********************************************************************************************************/
#define DRIVER_MAJOR 232
#define DEVICE_NAME "driver_button"
#define BASE_ADDRESS 0x01C22800
#define IOC_MAGIC 'w'
#define IOCTL_TEST_ON _IO(IOC_MAGIC,0)
#define IOCTL_TEST_OFF _IO(IOC_MAGIC,1)
#define BUTTON_NULL (0X3F)
#define BUTTON_HOME (0X04)
#define BUTTON_NEXT (0X0B)
#define BUTTON_PREV (0X11)
#define BUTTON_ENTER (0X18)
#define TIME_NUM (10)
#define FILTER_NUM (5)
/**********************************************************************************************************
Typedef
**********************************************************************************************************/
/**********************************************************************************************************
Variables
**********************************************************************************************************/
/* gpio寄存器 */
static volatile unsigned char __iomem *base_res;
static volatile unsigned char __iomem *adc_base;
static volatile unsigned char __iomem *adc_data;
static volatile unsigned char __iomem *adc_ctl;
static volatile unsigned char __iomem *adc_irq;
struct timer_list timer; /* 定义定时器 */
wait_queue_head_t button_wait; /* 等待队列 */
bool key_ready = false;
static unsigned char user_key = BUTTON_NULL;
static unsigned char last_key = BUTTON_NULL;
static unsigned char handle_key = BUTTON_NULL;
/**********************************************************************************************************
Function
**********************************************************************************************************/
/***********************************************************************************************************
* @描述 : 键值处理
***********************************************************************************************************/
void get_key_value(unsigned char value)
{
switch(value)
{
case BUTTON_HOME:
user_key = 1;
break;
case BUTTON_NEXT:
user_key = 2;
break;
case BUTTON_PREV:
user_key = 3;
break;
case BUTTON_ENTER:
user_key = 4;
break;
default :
break;
}
}
/***********************************************************************************************************
* @描述 : 按键处理函数
***********************************************************************************************************/
void button_handle(void)
{
static unsigned char null_key_num = 0;
static unsigned char valid_key_num = 0;
/*判断按键是否为空*/
if(*adc_data != BUTTON_NULL)
{
null_key_num = 0;
last_key = *adc_data;
/*判断与上次有效键值是否一致*/
if(handle_key != last_key)
{
/*多次判断,去抖操作*/
valid_key_num++;
if(valid_key_num > FILTER_NUM)
{
valid_key_num = 0;
handle_key = last_key;
/*获取键值*/
get_key_value(handle_key);
/*唤醒等待任务*/
key_ready = true;
wake_up(&button_wait);
}
}
}
else
{
/*多次判断,去抖操作*/
null_key_num++;
valid_key_num = 0;
if(null_key_num > FILTER_NUM)
{
/*按键为空*/
null_key_num = 0;
last_key = BUTTON_NULL;
handle_key = BUTTON_NULL;
}
}
}
/***********************************************************************************************************
* @描述 : 定时器回调函数
***********************************************************************************************************/
void time_callback(unsigned long arg)
{
/* 设置定时器 */
mod_timer(&timer, jiffies + msecs_to_jiffies(TIME_NUM));
/* 根据ADC数值 获取按键值 */
button_handle();
}
/***********************************************************************************************************
* @描述 : 初始化函数
***********************************************************************************************************/
void time_init(void)
{
/* 初始化定时器 */
init_timer(&timer);
/* 设置定时处理函数 */
timer.function = time_callback;
/* 超时时间 2 秒 */
timer.expires=jiffies + msecs_to_jiffies(TIME_NUM);
/* 启动定时器 */
add_timer(&timer);
}
/***********************************************************************************************************
* @描述 :
***********************************************************************************************************/
static int ioremap_init(void)
{
base_res = request_mem_region(BASE_ADDRESS , 0x10 , "GPIOE_MEM");
if(base_res == NULL)
{
printk("request_mem_region BASE_ADDRESS,0x28 fail\n");
}
else
printk(KERN_EMERG DEVICE_NAME " ======================request_mem_region ok ======================\n");
/* IO映射 */
adc_base = (unsigned char*)ioremap(BASE_ADDRESS , 0x10);
if(adc_base == NULL)
{
printk("ioremap BASE_ADDRESS,0x28 fail\n");
}
else
printk(KERN_EMERG DEVICE_NAME " ======================ioremap ok ======================\n");
/* 寄存器地址映射 */
adc_ctl =(unsigned int*)(adc_base + 0x00);
adc_irq =(unsigned int*)(adc_base + 0x04);
adc_data =(unsigned int*)(adc_base + 0x0c);
return 0;
}
/***********************************************************************************************************
* @描述 :
***********************************************************************************************************/
static int adc_init(void)
{
*adc_ctl = 0x00C00041;
*adc_irq = 0x00000013;
return 0;
}
/***********************************************************************************************************
* @描述 :
***********************************************************************************************************/
static int button_open(struct inode *inode, struct file *file)
{
printk(KERN_EMERG "======================open======================\n");
/* IO资源申请 */
ioremap_init();
/* 初始化adc */
adc_init();
/* 初始化定时器 */
time_init();
/* 初始化等待队列 */
init_waitqueue_head(&button_wait);
return 0;
}
/***********************************************************************************************************
* @描述 :
***********************************************************************************************************/
static ssize_t button_write(struct file *file, const char __user * buf, size_t count, loff_t *ppos)
{
printk(KERN_EMERG "======================write======================\n");
return 0;
}
/***********************************************************************************************************
* @描述 :
***********************************************************************************************************/
static ssize_t button_read(struct file *file, char __user * buf, size_t count, loff_t *ppos)
{
char buff[20];
char num;
/* 判断按键是否有效 */
if(user_key == BUTTON_NULL )
{
/* 无有效按键时,任务进入等待 */
printk(KERN_EMERG "====================== wait : null key ======================\n");
wait_event(button_wait, key_ready);
}
key_ready = false;
/* 有效按键唤醒任务 */
printk(KERN_EMERG "====================== wake up :valid key ======================\n");
num = sprintf(buff,"%s%d","key = ", user_key);
copy_to_user(buf , buff , num);
/* 清空按键数值 */
user_key = BUTTON_NULL;
memset(buff,0,20);
return 0;
}
/***********************************************************************************************************
* @描述 :
***********************************************************************************************************/
static int button_close(struct inode *inode, struct file *file)
{
/* 删除定时器 */
del_timer(&timer);
printk(KERN_EMERG "======================close ======================\n");
return 0;
}
/***********************************************************************************************************
* @描述 :
***********************************************************************************************************/
static int button_ioctl( struct file *file, unsigned int cmd, unsigned long arg)
{
printk(KERN_EMERG "======================ioctl ======================\n");
return 0;
}
/***********************************************************************************************************
* @描述 :
***********************************************************************************************************/
static struct file_operations driver_flops =
{
.owner = THIS_MODULE,
.open = button_open,
.write = button_write,
.read = button_read,
.unlocked_ioctl = button_ioctl,
.release = button_close,
};
static struct cdev dev;
static dev_t devno;
/***********************************************************************************************************
* @描述 :
***********************************************************************************************************/
static int __init driver_button_init(void)
{
int ret;
/* 申请设备号 */
ret = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME);
if(ret < 0)
{
pr_err("alloc_chrdev_region failed!");
return ret;
}
printk("MAJOR is %d\n", MAJOR(devno));
printk("MINOR is %d\n", MINOR(devno));
/* 注册设备 */
cdev_init(&dev, &driver_flops);
ret = cdev_add(&dev , devno, 1);
if (ret < 0)
{
pr_err("cdev_add failed!");
return ret;
}
printk(KERN_EMERG DEVICE_NAME " ======================driver_button_init======================\n");
return 0;
}
/***********************************************************************************************************
* @描述 :
***********************************************************************************************************/
static void __exit driver_button_exit(void)
{
/* 注销设备 */
cdev_del(&dev);
/* 释放设备号 */
unregister_chrdev_region(devno, 1);
printk(KERN_EMERG DEVICE_NAME " ======================driver_button_exit======================\n");
}
/***********************************************************************************************************
* @描述 :
***********************************************************************************************************/
module_init(driver_button_init);
module_exit(driver_button_exit);
MODULE_LICENSE("GPL");
/***********************************************END*******************************************************/
4.编写makefile
本次设计通过单编ko的方式得到驱动ko,单编ko的详细说明请参考我的另外一篇文章《如何编译linux驱动ko》。本次设计的Makefile代码如下(根据开发环境指定内核路径和编译工具路径):
.PHONY: main clean
KERNELDIR := /home/liwei/v3_work/project/linux-3.4
PWD := $(shell pwd)
CROSS_ARCH := /home/liwei/v3_work/tools/host/bin/arm-buildroot-linux-gnueabihf-gcc
obj-m += driver_button.o
main:
$(MAKE) $(CROSS_ARCH) -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.symvers *.d *.markers *.order
将driver_button.c和上述的Makefile文件放在同一个目录下(路径为任意路径,不需要一定放在内核目录中),执行make指令,最终我们得到了驱动ko 。
5.编写应用程序
编写一个demo_app.c的应用程序,程序源码如下:
/**
*********************************************************************************************************
* demo
* (c) Copyright 2021-2031
* All Rights Reserved
*
* @File :
* @By : liwei
* @Version : V0.01
*
*********************************************************************************************************
**/
/**********************************************************************************************************
Includes
**********************************************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/***********************************************************************************************************
* @描述 :
***********************************************************************************************************/
int main(int arvc, char *argv[])
{
int fd;
int buff[20] ;
int num;
printf("==========button test==================\n");
//打开驱动
fd = open("/dev/driver_button",O_RDWR);
while(1)
{
//执行驱动读操作
num= read(fd,buff,15);
printf("run:%s\r\n",buff);
//延时等待串口发送数据 ,清发送BUFF
sleep(1);
memset(buff,0,20);
}
return 0;
}
/***********************************************END*******************************************************/
应用程序源码用于测试按键驱动功能,在测试程序在一直循环读取按键驱动数据,并将按键数据打印出来。
我们将应用程序源码放在虚拟机的任意一个目录中,并在该目录下执行如下gcc编译指令:
arm-buildroot-linux-gnueabihf-gcc app_button.c -o app_button
于是我们得到一个可执行文件app_button
6.安装驱动及运行应用程序
目前我们得到了driver_button.ko和app_button两个文件,我们使用SecureCRTPortable工具将这两个文件传输到V3开发板中。
执行安装驱动指令:
insmod driver_button.ko
执行创建文件节点指令:
mknod /dev/driver_button c 248 0
执行修改app_button文件权限指令:
chmod 777 app_button
执行运行demo_app指令:
./app_button
测试按键结果如下:
7.总结
本文本文描述了基于全志V3S开发板的按键驱动程序和测试应用程序的设计流程,本次设计细节如下:
1、硬件电路通过ADC识别按键。
2、使用内核定时器周期查询按键是否按下。
3、使用等待队列休眠等待按键的应用。
创作不易希望朋友们点赞,转发,评论,关注。
您的点赞,转发,评论,关注将是我持续更新的动力
作者:李巍
Github:liyinuoman2017
CSDN:liyinuo2017
今日头条:程序猿李巍
相关推荐
- ubuntu22.04安装NVIDIA、CUDA、CUDNN详细步骤
-
1.查看GPU及驱动版本号查看GPU:命令:lspci|grep-invidia查看驱动版本...
- 黑苹果10.13.6(17G66)如何升级到17G10021安装nVidia官方显卡驱动
-
近期从苹果AppStore下载的macOSHighSierra10.13.6系统安装文件版本号为17G66:对于使用nVidia显卡的用户来说是个不小的麻烦,因为这个版本号没有对应的NV官方驱...
- 能跑源码,还提供数据集:这里有一个入门企业级验证码识别项目
-
机器之心专栏作者:kerlomz网上关于验证码识别的开源项目众多,但大多是学术型文章或者仅仅是一个测试demo,那么企业级的验证码识别究竟是怎样的呢?1.前言网上关于验证么识别的开源项目众多,但大...
- GPU卡驱动和CUDA安装教程
-
前置操作,禁用nouveau%新建一个配置文件sudovim/etc/modprobe.d/blacklist-nouveau.conf%在该配置文件中输入i,写入以下内容bl...
- OpenCV下载和安装(包含所有平台)
-
在OpenCV官方网站可以下载最新的且完整的源码以及大部分的release版本源码。安装...
- GPU 要下载哪个驱动?
-
安装正确合适的GPU驱动至关重要,关系着是否能正常使用GPU显示或者是加速运算等工作。各型号GPU皆可从NVIDIA官方网站下载到适合的GPU版本。通过选择GPU型号、操作系统,即可找到适合自己系统...
- 【Python深度学习系列】Win10下CUDA+cuDNN+Tensorflow安装与配置
-
这是我的第292篇...
- Ubuntu 20.04 CUDA&cuDNN安装方法
-
1、安装显卡驱动输入nvidia-smi命令查看支持的cuda版本如果有驱动显示以下信息:如果没有,则会显示以下信息:如果无法查看,则说明尚未安装nvidia驱动,点击附加驱动,选择对应版本的驱动即可...
- windows下安装大模型加速包flash-attn
-
Flash-attn(FlashAttention),作为一种用于神经网络模型的注意力机制,在处理序列数据时有显著的优势,可以提高计算效率并减少内存使用,特别是Transformer模型。它通过优化...
- NVIDIA App上线 映众显卡最佳实用工具箱
-
本周,NVIDIA发布了全新正式版的NVIDIAApp,带来了一系列优化和功能更新,让用户体验更加流畅。无论是游戏玩家还是内容创作者,这款App都能提供更便捷的操作和更强的性能。优化与整合:界面简洁,...
- tensorflow GPU环境安装踩坑日记
-
前言:最近做一个TensorFlow的开源项目,用CPU跑的话,要消耗太多的时间,于是有了这篇配置GPU环境的踩坑日志...
- 小白也能搞定!Windows10上CUDA9.0+CUDNN7.0.5的完美安装教程
-
前言:为什么要在本地电脑安装CUDA,CUDA是什么的,用来做什么?我想,点击标题进来的小伙伴,应该都清楚这些。不管你是用来做什么,或者跟我一样为了跑Tensorflow的ObjectDe...
- CUDA安装以及CUDNN安装-基于Windows10
-
PC端配置深度学习环境本身不太麻烦,但是如果想要使用GPU加速,那么就有点麻烦了。一是因为CUDA以及CUDNN的下载很麻烦,因为服务器在国外;二是因为各种版本的匹配问题,因为各种版本匹配的涉及到硬件...
- 腾讯版Sora开源后被提速8倍!官方点赞并预告:下月上新图生视频
-
提速8倍!速度更快、效果更好的混元视频模型——...
- 快贴——让iPhone与Windows之间的协作变得高效
-
写在前面:1.软件宣称采用了端对端加密技术,但本人不是开发者,所以不对软件的安全性做评价,仅从功能角度考虑,如果你手持iPhone,日常办公电脑是Windows,或者需要安卓手机与MAC电脑之间进行实...
- 一周热门
-
-
一文读懂关于MySQL Datetime字段允许插入0000-00-00无效日期
-
一文看懂mysql时间函数now()、current_timestamp() 和sysdate()
-
一款全能的看图软件,速度快、功能强、免费用
-
PhotoShop通道
-
Boston Dynamics Founder to Attend the 2024 T-EDGE Conference
-
IDC机房服务器托管可提供的服务
-
新版腾讯QQ更新Windows 9.9.7、Mac 6.9.25、Linux 3.2.5版本
-
详解PostgreSQL 如何获取当前日期时间
-
流星蝴蝶剑:76邵氏精华版,强化了流星,消失了蝴蝶
-
Serv-u 提权
-
- 最近发表
- 标签列表
-
- huaweiupdateextractor (27)
- thinkphp6下载 (25)
- mysql 时间索引 (31)
- mydisktest_v298 (34)
- sql 日期比较 (26)
- document.appendchild (35)
- 头像打包下载 (61)
- oppoa5专用解锁工具包 (23)
- acmecadconverter_8.52绿色版 (39)
- oracle timestamp比较大小 (28)
- unity shader入门精要pdf (22)
- word文档批量处理大师破解版 (36)
- pk10牛牛 (22)
- server2016安装密钥 (33)
- mysql 昨天的日期 (37)
- 加密与解密第四版pdf (30)
- pcm文件下载 (23)
- jemeter官网 (31)
- parsevideo (33)
- 个人网站源码 (37)
- ckeditor4中文文档 (27)
- exe4j_java_home (30)
- centos7.4下载 (33)
- xlsx.full.min.js下载 (32)
- 深度学习 pdf (28)