唐山网站网站建设做设计哪个网站图比较实用

当前位置: 首页 > news >正文

唐山网站网站建设,做设计哪个网站图比较实用,广州城中村,山西省建设监理协会网站文章目录 “休眠-唤醒”机制#xff1a;APP执行过程内核函数休眠函数唤醒函数 休眠与唤醒方式的按键驱动程序(stm32mp157)驱动程序框架button_test.cgpio_key_drv.cMakefile修改设备树文件编译测试 “休眠-唤醒”机制#xff1a; 当应用程序必须等待某个事件发生#xff0c… 文章目录 “休眠-唤醒”机制APP执行过程内核函数休眠函数唤醒函数 休眠与唤醒方式的按键驱动程序(stm32mp157)驱动程序框架button_test.cgpio_key_drv.cMakefile修改设备树文件编译测试 “休眠-唤醒”机制 当应用程序必须等待某个事件发生比如必须等待按键被按下时可以使用“休眠-唤醒”机制 ① APP 调用 read 等函数试图读取数据比如读取按键② APP 进入内核态也就是调用驱动中的对应函数发现有数据则复制到用户空间并马上返回③ 如果 APP 在内核态也就是在驱动程序中发现没有数据则 APP 休眠④ 当有数据时比如当按下按键时驱动程序的中断服务程序被调用它会记录数据、唤醒 APP⑤ APP 继续运行它的内核态代码也就是驱动程序中的函数复制数据到用户空间并马上返回。 APP执行过程 驱动中没有数据时APP1 在内核态执行到 drv_read 时会休眠。所谓休眠就是把自己的状态改为非 RUNNING这样内核的调度器就不会让它运行。当按下按键驱动程序中的中断服务程序被调用它会记录数据并唤醒 APP1。所以唤醒就是把程序的状态改为 RUNNING这样内核的调度器有合适的时间就会让它运行。当 APP1 再次运行时就会继续执行 drv_read 中剩下的代码把数据复制回用户空间返回用户空间。在 APP的read到内核态的drv_read函数中进程上下文也就是在 APP1 的执行过程中它是可以休眠的在中断处理函数中属于中断上下文不能休眠也就是不能调用会导致休眠的函数。 内核调度器负责维护该链表链表里面保存的是线程如果线程的状态为RUNNING则会找到合适的时间就会让它运行如果是非RUNNING内核的调度器就不会让它运行。 内核函数 参考内核源码 include\linux\wait.h 休眠函数 函数说明wait_event_interruptible(wq, condition)休眠直到 condition 为真休眠期间是可被打断的可以被信号打断wait_event(wq, condition)休眠直到 condition 为真退出的唯一条件是 condition 为真信号也不好使wait_event_interruptible_timeout(wq,condition, timeout)休眠直到 condition 为真或超时休眠期间是可被打断的可以被信号打断wait_event_timeout(wq, condition,timeout)休眠直到 condition 为真退出的唯一条件是 condition 为真信号也不好使 比较重要的参数就是 ① wqwaitqueue等待队列 休眠时除了把程序状态改为非 RUNNING 之外还要把进程/进程放入wq 中以后中断服务程序要从 wq 中把它取出来唤醒。没有 wq 的话茫茫人海中中断服务程序去哪里找到你 ② condition 这可以是一个变量也可以是任何表达式。表示“一直等待直到condition 为真”
唤醒函数 函数说明wake_up_interruptible(x)唤醒 x 队列中状态为“TASK_INTERRUPTIBLE”的线程只唤醒其中的一个线程wake_up_interruptible_nr(x, nr)唤醒 x 队列中状态为“TASK_INTERRUPTIBLE”的线程只唤醒其中的 nr 个线程wake_up_interruptible_all(x)唤醒 x 队列中状态为“TASK_INTERRUPTIBLE”的线程唤醒其中的所有线程wake_up(x)唤 醒 x 队 列 中 状 态 为 “ TASK_INTERRUPTIBLE ” 或“TASK_UNINTERRUPTIBLE”的线程只唤醒其中的一个线程wake_up_nr(x, nr)唤 醒 x 队 列 中 状 态 为 “ TASK_INTERRUPTIBLE ” 或“TASK_UNINTERRUPTIBLE”的线程只唤醒其中 nr 个线程wake_up_all(x)唤 醒 x 队 列 中 状 态 为 “ TASK_INTERRUPTIBLE ” 或“TASK_UNINTERRUPTIBLE”的线程唤醒其中的所有线程 休眠与唤醒方式的按键驱动程序(stm32mp157) 驱动程序框架 要休眠的线程放在 wq 队列里中断处理函数从 wq 队列里把它取出来唤醒。 代码编写内容 ① 初始化 wq 队列② 在驱动的 read 函数中调用 wait_event_interruptible 它本身会判断 event 是否为 FALSE如果为 FASLE 表示无数据则休眠。当从 wait_event_interruptible 返回后把数据复制回用户空间。 ③ 在中断服务程序里 设置 event 为 TRUE并调用 wake_up_interruptible 唤醒线程。
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;int 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;}while (1){/ 3. 读文件 */read(fd, val, 4);printf(get button : 0x%x\n, val);}close(fd);return 0; }gpio_key_drv.c 使用环形缓冲区来保存按键值相比于全局变量可以避免被覆盖的问题 #include linux/module.h#include linux/fs.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 linux/gpio/consumer.h #include linux/platform_device.h #include linux/of_gpio.h #include linux/of_irq.h #include linux/interrupt.h #include linux/irq.h #include linux/slab.hstruct gpio_key{int gpio;struct gpio_desc *gpiod;int flag;int irq; } ;static struct gpio_key gpio_keys_first;/ 主设备号 */ static int major 0; static struct class gpio_key_class;/ 环形缓冲区 / #define BUF_LEN 128 static int g_keys[BUF_LEN]; static int r, w;//rw是指针指向读写的位置#define NEXT_POS(x) ((x1) % BUF_LEN)static int is_key_buf_empty(void) {return (r w);//一开始rw都是0表示空 }static int is_key_buf_full(void) {return (r NEXT_POS(w));//下一个写的位置等于r表示满容量为128字节的buffer存储到127表示满了 }static void put_key(int key) {if (!is_key_buf_full()){g_keys[w] key;//把数据放入w位置w NEXT_POS(w);//移动w} }static int get_key(void) {int key 0;if (!is_key_buf_empty()){key g_keys[r];//从r位置读数据r NEXT_POS®;//移动r}return key; }static DECLARE_WAIT_QUEUE_HEAD(gpio_key_wait);//该队列使用宏来初始化/ 实现对应的open/read/write等函数填入file_operations结构体 */ static ssize_t gpio_key_drv_read (struct file *file, char __user *buf, size_t size, loff_t offset) {//printk(%s %s line %d\n, FILE, FUNCTION, LINE);int err;int key;wait_event_interruptible(gpio_key_wait, !is_key_buf_empty());key get_key();err copy_to_user(buf, key, 4);return 4; }/ 定义自己的file_operations结构体 */ static struct file_operations gpio_key_drv {.owner THIS_MODULE,.read gpio_key_drv_read, };static irqreturn_t gpio_key_isr(int irq, void *dev_id) {struct gpio_key gpio_key dev_id;int val;int key;val gpiod_get_value(gpio_key-gpiod);printk(key %d %d\n, gpio_key-gpio, val);key (gpio_key-gpio 8) | val;put_key(key);wake_up_interruptible(gpio_key_wait);return IRQ_HANDLED; }/ 1. 从platform_device获得GPIO* 2. gpioirq* 3. request_irq*/ static int gpio_key_probe(struct platform_device *pdev) {int err;struct device_node *node pdev-dev.of_node;int count;int i;enum of_gpio_flags flag;printk(%s %s line %d\n, FILE, FUNCTION, LINE);count of_gpio_count(node);if (!count){printk(%s %s line %d, there isnt any gpio available\n, FILE, FUNCTION, LINE);return -1;}gpio_keys_first kzalloc(sizeof(struct gpio_key) * count, GFP_KERNEL);for (i 0; i count; i){gpio_keys_first[i].gpio of_get_gpio_flags(node, i, flag);if (gpio_keys_first[i].gpio 0){printk(%s %s line %d, of_get_gpio_flags fail\n, FILE, FUNCTION, LINE);return -1;}gpio_keys_first[i].gpiod gpio_to_desc(gpio_keys_first[i].gpio);gpio_keys_first[i].flag flag OF_GPIO_ACTIVE_LOW;gpio_keys_first[i].irq gpio_to_irq(gpio_keys_first[i].gpio);}for (i 0; i count; i){err request_irq(gpio_keys_first[i].irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 100ask_gpio_key, gpio_keys_first[i]);}/* 注册file_operations /major register_chrdev(0, 100ask_gpio_key, gpio_key_drv); / /dev/gpio_key /gpio_key_class class_create(THIS_MODULE, 100ask_gpio_key_class);if (IS_ERR(gpio_key_class)) {printk(%s %s line %d\n, FILE, FUNCTION, LINE);unregister_chrdev(major, 100ask_gpio_key);return PTR_ERR(gpio_key_class);}device_create(gpio_key_class, NULL, MKDEV(major, 0), NULL, 100ask_gpio_key); / /dev/100ask_gpio_key */return 0;}static int gpio_key_remove(struct platform_device *pdev) {//int err;struct device_node node pdev-dev.of_node;int count;int i;device_destroy(gpio_key_class, MKDEV(major, 0));class_destroy(gpio_key_class);unregister_chrdev(major, 100ask_gpio_key);count of_gpio_count(node);for (i 0; i count; i){free_irq(gpio_keys_first[i].irq, gpio_keys_first[i]);}kfree(gpio_keys_first);return 0; }static const struct of_device_id my_keys[] {{ .compatible first_key,gpio_key },{ }, };/ 1. 定义platform_driver / static struct platform_driver gpio_keys_driver {.probe gpio_key_probe,.remove gpio_key_remove,.driver {.name my_gpio_key,.of_match_table my_keys,}, };/ 2. 在入口函数注册platform_driver / static int __init gpio_key_init(void) {int err;printk(%s %s line %d\n, FILE, FUNCTION, LINE);err platform_driver_register(gpio_keys_driver); return err; }/ 3. 有入口函数就应该有出口函数卸载驱动程序时就会去调用这个出口函数* 卸载platform_driver/ static void __exit gpio_key_exit(void) {printk(%s %s line %d\n, FILE, FUNCTION, LINE);platform_driver_unregister(gpio_keys_driver); }/ 7. 其他完善提供设备信息自动创建设备节点 */module_init(gpio_key_init); module_exit(gpio_key_exit);MODULE_LICENSE(GPL);Makefile

1. 使用不同的开发板内核时, 一定要修改KERN_DIR

2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:

2.1 ARCH, 比如: export ARCHarm64

2.2 CROSS_COMPILE, 比如: export CROSS_COMPILEaarch64-linux-gnu-

2.3 PATH, 比如: export PATH$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin

注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,

请参考各开发板的高级用户使用手册KERN_DIR /home/book/100ask_stm32mp157_pro-sdk/Linux-5.4all:make -C \((KERN_DIR) Mpwd modules \)(CROSS_COMPILE)gcc -o button_test button_test.c

clean:make -C $(KERN_DIR) Mpwd modules cleanrm -rf modules.order button_test# 参考内核源码drivers/char/ipmi/Makefile

要想把a.c, b.c编译成ab.ko, 可以这样指定:

ab-y : a.o b.o

obj-m ab.oobj-m gpio_key_drv.o

修改设备树文件 对于一个引脚要用作中断时 a) 要通过 PinCtrl 把它设置为 GPIO 功能【ST 公司对于 STM32MP157 系列芯片GPIO 为默认模式 不需要再进行配置Pinctrl 信息】b) 表明自身是哪一个 GPIO 模块里的哪一个引脚【修改设备树】 打开内核的设备树文件arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dts gpio_keys_first {compatible first_key,gpio_key;gpios gpiog 3 GPIO_ACTIVE_LOWgpiog 2 GPIO_ACTIVE_LOW; };与此同时需要把用到引脚的节点禁用 注意如果其他设备树文件也用到该节点需要设置属性为disabled状态在arch/arm/boot/dts目录下执行如下指令查找哪些设备树用到该节点 grep gpiog * -nr如果用到该节点需要添加属性去屏蔽 status disabled; 编译测试 首先要设置 ARCH、CROSS_COMPILE、PATH 这三个环境变量后进入 ubuntu 上板子内核源码的目录在Linux内核源码根目录下执行如下命令即可编译 dtb 文件 make dtbs V1编译好的文件在路径由DTC指定移植设备树到开发板的共享文件夹中先保存源文件然后覆盖源文件重启后会挂载新的设备树进入该目录查看是否有新添加的设备节点 cd /sys/firmware/devicetree/base 编译驱动程序在Makefile文件目录下执行make指令此时目录下有编译好的内核模块gpio_key_drv.ko和可执行文件button_test文件移植到开发板上 确定一下烧录系统cat /proc/mounts查看boot分区挂载的位置将其重新挂载在boot分区mount /dev/mmcblk2p2 /boot然后将共享文件夹里面的设备树文件拷贝到boot目录下这样的话设备树文件就在boot目录下 cp /mnt/stm32mp157c-100ask-512d-lcd-v1.dtb /boot重启后挂载运行 insmod -f gpio_key_drv.ko // 强制安装驱动程序 ls /dev/my_gpio_key ./button_test /dev/my_gpio_key //后台运行此时prink函数打印的内容看不到然后按下按键