100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > 【嵌入式Linux】嵌入式Linux驱动开发基础知识之按键驱动框架

【嵌入式Linux】嵌入式Linux驱动开发基础知识之按键驱动框架

时间:2019-01-21 01:53:28

相关推荐

【嵌入式Linux】嵌入式Linux驱动开发基础知识之按键驱动框架

文章目录

前言1、APP怎么读取按键值1.1、查询方式1.2、休眠-唤醒方式1.3、poll方式1.3、异步通知方式1.5、 驱动程序提供能力,不提供策略2、按键驱动程序框架--查询方式2.1、通用驱动2.2、单板个性化驱动2.3、 APP

前言

韦东山嵌入式Linux驱动开发基础知识学习笔记

文章中大多内容来自韦东山老师的文档,还有部分个人根据自己需求补充的内容

视频教程地址:/video/BV14f4y1Q7ti

1、APP怎么读取按键值

APP去读按键的方法有4种:

① 查询方式

② 休眠-唤醒方式

③ poll方式

④ 异步通知方式

APP读取按键值,需要有按键驱动程序。

通过这4种方式的学习,我们可以掌握如下知识:

① 驱动的基本技能:中断、休眠、唤醒、poll等机制。

这些基本技能是驱动开发的基础,其他大型驱动复杂的地方是它的框架及设计思想,但是基本技术就这些。

关于这APP中四种方法的介绍在前面的【嵌入式Linux】嵌入式Linux应用开发基础知识之输入系统应用编程中已经介绍,在此不做介绍

1.1、查询方式

▲查询方式

驱动程序中构造、注册一个file_operations结构体,里面提供有对应的open,read函数。APP调用open时,导致驱动中对应的open函数被调用,在里面配置GPIO为输入引脚。APP调用read时,导致驱动中对应的read函数被调用,它读取寄存器,把引脚状态直接返回给APP。

1.2、休眠-唤醒方式

▲休眠-唤醒方式

驱动程序中构造、注册一个file_operations结构体,里面提供有对应的open,read函数。

APP调用open时,导致驱动中对应的open函数被调用,在里面配置GPIO为输入引脚;并且注册GPIO的中断处理函数。

APP调用read时,导致驱动中对应的read函数被调用,如果有按键数据则直接返回给APP;否则APP在内核态休眠。

当用户按下按键时,GPIO中断被触发,导致驱动程序之前注册的中断服务程序被执行。它会记录按键数据,并唤醒休眠中的APP。

APP被唤醒后继续在内核态运行,即继续执行驱动代码,把按键数据返回给APP(的用户空间)。

1.3、poll方式

上面的休眠-唤醒方式有个缺点:如果用户一直没操作按键,那么APP就会永远休眠。

我们可以给APP定个闹钟,这就是poll方式。

▲poll方式

驱动程序中构造、注册一个file_operations结构体,里面提供有对应的open,read,poll函数。

APP调用open时,导致驱动中对应的open函数被调用,在里面配置GPIO为输入引脚;并且注册GPIO的中断处理函数。

APP调用poll或select函数,意图是“查询”是否有数据,这2个函数都可以指定一个超时时间,即在这段时间内没有数据的话就返回错误。这会导致驱动中对应的poll函数被调用,如果有按键数据则直接返回给APP;否则APP在内核态休眠一段时间。

当用户按下按键时,GPIO中断被触发,导致驱动程序之前注册的中断服务程序被执行。它会记录按键数据,并唤醒休眠中的APP。

如果用户没按下按键,但是超时时间到了,内核也会唤醒APP。

所以APP被唤醒有2种原因:用户操作了按键,超时。被唤醒的APP在内核态继续运行,即继续执行驱动代码,把“状态”返回给APP(的用户空间)。

APP得到poll/select函数的返回结果后,如果确认是有数据的,则再调用read函数,这会导致驱动中的read函数被调用,这时驱动程序中含有数据,会直接返回数据。

1.3、异步通知方式

▲异步通知方式

异步通知的实现原理是:内核给APP发信号。信号有很多种,这里发的是SIGIO。

驱动程序中构造、注册一个file_operations结构体,里面提供有对应的open,read,fasync函数。

APP调用open时,导致驱动中对应的open函数被调用,在里面配置GPIO为输入引脚;并且注册GPIO的中断处理函数。

APP给信号SIGIO注册自己的处理函数:my_signal_fun。

APP调用fcntl函数,把驱动程序的flag改为FASYNC,这会导致驱动程序的fasync函数被调用,它只是简单记录进程PID。

当用户按下按键时,GPIO中断被触发,导致驱动程序之前注册的中断服务程序被执行。它会记录按键数据,然后给进程PID发送SIGIO信号。

APP收到信号后会被打断,先执行信号处理函数:在信号处理函数中可以去调用read函数读取按键值。

信号处理函数返回后,APP会继续执行原先被打断的代码。

1.5、 驱动程序提供能力,不提供策略

我们的驱动程序可以实现上述4种提供按键的方法,但是驱动程序不应该限制APP使用哪种方法。

这就是驱动设计的一个原理:提供能力,不提供策略。就是说,你想用哪种方法都行,驱动程序都可以提供;但是驱动程序不能限制你使用哪种方法。

2、按键驱动程序框架–查询方式

▲GPIO on STM32MP157PRO

2.1、通用驱动

button_drv.c

#include <linux/module.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/major.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/fcntl.h>#include <linux/fs.h>#include <linux/signal.h>#include <linux/mutex.h>#include <linux/mm.h>#include <linux/timer.h>#include <linux/wait.h>#include <linux/skbuff.h>#include <linux/proc_fs.h>#include <linux/poll.h>#include <linux/capi.h>#include <linux/kernelcapi.h>#include <linux/init.h>#include <linux/device.h>#include <linux/moduleparam.h>#include "button_drv.h"static int major = 0;static struct button_operations *p_button_opr;static struct class *button_class;static int button_open (struct inode *inode, struct file *file){int minor = iminor(inode);p_button_opr->init(minor);return 0;}static ssize_t button_read (struct file *file, char __user *buf, size_t size, loff_t *off){unsigned int minor = iminor(file_inode(file));char level;int err;level = p_button_opr->read(minor);err = copy_to_user(buf, &level, 1);return 1;}static struct file_operations button_fops = {.open = button_open,.read = button_read,};void register_button_operations(struct button_operations *opr){int i;p_button_opr = opr;for (i = 0; i < opr->count; i++){device_create(button_class, NULL, MKDEV(major, i), NULL, "100ask_button%d", i);}}void unregister_button_operations(void){int i;for (i = 0; i < p_button_opr->count; i++){device_destroy(button_class, MKDEV(major, i));}}EXPORT_SYMBOL(register_button_operations);EXPORT_SYMBOL(unregister_button_operations);int __init button_init(void){major = register_chrdev(0, "100ask_button", &button_fops);button_class = class_create(THIS_MODULE, "100ask_button");if (IS_ERR(button_class))return -1;return 0;}void __exit button_exit(void){class_destroy(button_class);unregister_chrdev(major, "100ask_button");}module_init(button_init);module_exit(button_exit);MODULE_LICENSE("GPL");

2.2、单板个性化驱动

board_100ask_stm32mp157.c

#include <linux/module.h>#include <linux/fs.h>#include <linux/io.h>#include <linux/errno.h>#include <linux/miscdevice.h>#include <linux/kernel.h>#include <linux/major.h>#include <linux/mutex.h>#include <linux/proc_fs.h>#include <linux/seq_file.h>#include <linux/stat.h>#include <linux/init.h>#include <linux/device.h>#include <linux/tty.h>#include <linux/kmod.h>#include <linux/gfp.h>#include <asm/io.h>#include "button_drv.h"struct stm32mp157_gpio {volatile unsigned int MODER; /*!< GPIO port mode register,Address offset: 0x00*/volatile unsigned int OTYPER; /*!< GPIO port output type register, Address offset: 0x04*/volatile unsigned int OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08*/volatile unsigned int PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C*/volatile unsigned int IDR;/*!< GPIO port input data register, Address offset: 0x10*/volatile unsigned int ODR;/*!< GPIO port output data register, Address offset: 0x14*/volatile unsigned int BSRR;/*!< GPIO port bit set/reset,Address offset: 0x18*/volatile unsigned int LCKR;/*!< GPIO port configuration lock register, Address offset: 0x1C*/volatile unsigned int AFR[2]; /*!< GPIO alternate function registers,Address offset: 0x20-0x24 */} ;/* RCC_PLL4CR */static volatile unsigned int *RCC_PLL4CR; /* RCC_MP_AHB4ENSETR */static volatile unsigned int *RCC_MP_AHB4ENSETR; static struct stm32mp157_gpio *gpiog; /* KEY1: PG3, KEY2: PG2 */static void board_stm32mp157_button_init (int which) /* 初始化button, which-哪个button */{if (!RCC_PLL4CR){RCC_PLL4CR = ioremap(0x50000000 + 0x894, 4);RCC_MP_AHB4ENSETR = ioremap(0x50000000 + 0xA28, 4);gpiog = ioremap(0x50008000, sizeof(struct stm32mp157_gpio));}if (which == 0){/* 1. enable PLL4 * CG15, b[31:30] = 0b11*/*RCC_PLL4CR |= (1<<0);while((*RCC_PLL4CR & (1<<1)) == 0);/* 2. enable GPIOG */*RCC_MP_AHB4ENSETR |= (1<<6);/* 3. 设置PG3为GPIO模式, 输入模式 */gpiog->MODER &= ~(3<<6);}else if(which == 1){/* 1. enable PLL4 * CG15, b[31:30] = 0b11*/*RCC_PLL4CR |= (1<<0);while((*RCC_PLL4CR & (1<<1)) == 0);/* 2. enable GPIOG */*RCC_MP_AHB4ENSETR |= (1<<6);/* 3. 设置PG2为GPIO模式, 输入模式 */gpiog->MODER &= ~(3<<4);}}static int board_stm32mp157_button_read (int which) /* 读button, which-哪个 */{//printk("%s %s line %d, button %d, 0x%x\n", __FILE__, __FUNCTION__, __LINE__, which, *GPIO1_DATAIN);if (which == 0)return (gpiog->IDR & (1<<3)) ? 1 : 0;elsereturn (gpiog->IDR & (1<<2)) ? 1 : 0;}static struct button_operations my_buttons_ops = {.count = 2,.init = board_stm32mp157_button_init,.read = board_stm32mp157_button_read,};int __init board_stm32mp157_button_drv_init(void){register_button_operations(&my_buttons_ops);return 0;}void __exit board_stm32mp157_button_drv_exit(void){unregister_button_operations();}module_init(board_stm32mp157_button_drv_init);module_exit(board_stm32mp157_button_drv_exit);MODULE_LICENSE("GPL");

button_drv.c

#ifndef _BUTTON_DRV_H#define _BUTTON_DRV_Hstruct button_operations {int count;void (*init) (int which);int (*read) (int which);};void register_button_operations(struct button_operations *opr);void unregister_button_operations(void);#endif

2.3、 APP

button_test.c

#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <stdio.h>#include <string.h>/** ./button_test /dev/100ask_button0**/int main(int argc, char **argv){int fd;char val;/* 1. 判断参数 */if (argc != 2) {printf("Usage: %s <dev>\n", argv[0]);return -1;}/* 2. 打开文件 */fd = open(argv[1], O_RDWR);if (fd == -1){printf("can not open file %s\n", argv[1]);return -1;}/* 3. 写文件 */read(fd, &val, 1);printf("get button : %d\n", val);close(fd);return 0;}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。