韦东山嵌入式Linux驱动开发基础知识学习笔记
文章中大多内容来自韦东山老师的文档,还有部分个人根据自己需求补充的内容
视频教程地址:https://www.bilibili.com/video/BV14f4y1Q7ti
APP去读按键的方法有4种:
① 查询方式
② 休眠-唤醒方式
③ poll方式
④ 异步通知方式
APP读取按键值,需要有按键驱动程序。
通过这4种方式的学习,我们可以掌握如下知识:
① 驱动的基本技能:中断、休眠、唤醒、poll等机制。
这些基本技能是驱动开发的基础,其他大型驱动复杂的地方是它的框架及设计思想,但是基本技术就这些。
关于这APP中四种方法的介绍在前面的【嵌入式Linux】嵌入式Linux应用开发基础知识之输入系统应用编程中已经介绍,在此不做介绍
▲查询方式
驱动程序中构造、注册一个file_operations结构体,里面提供有对应的open,read函数。APP调用open时,导致驱动中对应的open函数被调用,在里面配置GPIO为输入引脚。APP调用read时,导致驱动中对应的read函数被调用,它读取寄存器,把引脚状态直接返回给APP。
▲休眠-唤醒方式
驱动程序中构造、注册一个file_operations结构体,里面提供有对应的open,read函数。
APP调用open时,导致驱动中对应的open函数被调用,在里面配置GPIO为输入引脚;并且注册GPIO的中断处理函数。
APP调用read时,导致驱动中对应的read函数被调用,如果有按键数据则直接返回给APP;否则APP在内核态休眠。
当用户按下按键时,GPIO中断被触发,导致驱动程序之前注册的中断服务程序被执行。它会记录按键数据,并唤醒休眠中的APP。
APP被唤醒后继续在内核态运行,即继续执行驱动代码,把按键数据返回给APP(的用户空间)。
上面的休眠-唤醒方式有个缺点:如果用户一直没操作按键,那么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函数被调用,这时驱动程序中含有数据,会直接返回数据。
▲异步通知方式
异步通知的实现原理是:内核给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会继续执行原先被打断的代码。
我们的驱动程序可以实现上述4种提供按键的方法,但是驱动程序不应该限制APP使用哪种方法。
这就是驱动设计的一个原理:提供能力,不提供策略。就是说,你想用哪种方法都行,驱动程序都可以提供;但是驱动程序不能限制你使用哪种方法。
▲GPIO on STM32MP157PRO
button_drv.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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");
board_100ask_stm32mp157.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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;
else
return (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_H
struct 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
button_test.c
#include
#include
#include
#include
#include
#include
/*
* ./button_test /dev/100ask_button0
*
*/
int main(int argc, char **argv)
{
int fd;
char val;
/* 1. 判断参数 */
if (argc != 2)
{
printf("Usage: %s \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;
}