Linux设备驱动-练习

练习要求:

一、设备树

1、配置设备树信息:将3个led灯和1个风扇使用到的设备信息配置到设备树中

二、设备驱动层

1、通过of_find_node_by_name、of_get_named_gpion等内核核心层统一的api接口调用外设;

2、通过udev设备管理器自动注册并创建设备文件

3、通过ioctl控制开启/关闭外设,其中,3个led灯通过传递 设备名称-次设备号minor 控制具体的某栈灯;

三、应用层

1、多线程开发,第一个线程实现三个灯的流水,第二个线程实现风扇的控制

2、通过open、close、ioctl 对应的字符设备文件调用对应的驱动

1、设备树的配置

  • arch/arm/boot/dts/stm32mp157a-fsmp1a.dts  (根目录 / 里面)
leds{led1-gpio = <&gpioe 10 0>;led2-gpio = <&gpiof 10 0>;led3-gpio = <&gpioe 8 0>;
};fans{fan-gpio = <&gpioe 9 0>;
};

2、源码

2.1、应用程序

  • app.c
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include "led_driver.h"
#include "fan_driver.h"void *led_task(void *args)
{// 打开文件int fd1 = open("/dev/myled1", O_RDWR);int fd2 = open("/dev/myled2", O_RDWR);int fd3 = open("/dev/myled3", O_RDWR);// 操作硬件for (int i = 0;; i++){if (i % 3 == 0){ioctl(fd1, CMD_LED_ON);ioctl(fd2, CMD_LED_OFF);ioctl(fd3, CMD_LED_OFF);}else if (i % 3 == 1){ioctl(fd1, CMD_LED_OFF);ioctl(fd2, CMD_LED_ON);ioctl(fd3, CMD_LED_OFF);}else{ioctl(fd1, CMD_LED_OFF);ioctl(fd2, CMD_LED_OFF);ioctl(fd3, CMD_LED_ON);}sleep(1);}// 关闭文件close(fd1);close(fd2);close(fd3);
}// void *beep_task(void *args)
// {
//     // 打开文件
//     int fd = open("/dev/mybeep", O_RDWR);
//     char cmd[3] = {0};
//     unsigned short cnt = 0;
//     printf("beep_task\n");
//     for (int i = 0;; i++)
//     {
//         if (cnt % 5 == 0)
//         {
//             ioctl(fd, CMD_BEEP_ON);
//         }
//         else
//         {
//             ioctl(fd, CMD_BEEP_OFF);
//         }
//         sleep(1);
//     }
//     // 关闭文件
//     close(fd);
// }void *fan_task(void *args)
{// 打开文件int fd = open("/dev/myfan", O_RDWR);for(int i=0;;i++){if (i % 2 == 0){ioctl(fd, CMD_FAN_ON);}else{ioctl(fd, CMD_FAN_OFF);}sleep(5);}// 关闭文件close(fd);
}int main(int argc, char *argv)
{pthread_t pid1, pid2, pid3;pthread_create(&pid1, NULL, led_task, NULL);// pthread_create(&pid2, NULL, beep_task, NULL);pthread_create(&pid3, NULL, fan_task, NULL);pthread_join(pid1, NULL);// pthread_join(pid2, NULL);pthread_join(pid3, NULL);return 0;
}

2.2、led驱动程序 

  • led.h:
#ifndef _LED_H_
#define _LED_H_//
#define CMD_LED_ON _IO('L', 0)
#define CMD_LED_OFF _IO('L', 1)
//#endif
  • led.c:
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device/class.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include "led_driver.h"int major;              // 主设备号
struct device_node *np; // 设备树节点
int led1_gpio_no;       // gpio编号
int led2_gpio_no;
int led3_gpio_no;
struct class *pClass; // udev的文件类指针long file_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{int signal = 0;int minor = (int)file->private_data;int gpio_no;if (minor == 1)gpio_no = led1_gpio_no;else if(minor == 2)gpio_no = led2_gpio_no;else gpio_no = led3_gpio_no;if (cmd == CMD_LED_ON){signal=1;}else{signal=0;}//改变gpio的电平值gpio_set_value(gpio_no, signal);return 0;
}int file_open(struct inode *inode, struct file *file)
{file->private_data = (void*)MINOR(inode->i_rdev);return 0;
}int file_close(struct inode *inode, struct file *file)
{return 0;
}struct file_operations led_fops = {.unlocked_ioctl = file_ioctl,.open = file_open,.release = file_close};int __init led_driver_init(void)
{int minor;// 注册字符设备驱动 register_chrdevmajor = register_chrdev(0, "myled", &led_fops);if (major == 0){printk("register_chrdev failed\n");return -1;}printk("register_chrdev succes. major=[%d]\n", major);// 从设备树获取硬件信息np = of_find_node_by_name(NULL, "leds");if (np == NULL){printk("of_find_node_by_name failed\n");return -1;}printk("of_find_node_by_name success\n");// led1 = 获取gpio操作句柄led1_gpio_no = of_get_named_gpio(np, "led1-gpio", 0);if (led1_gpio_no < 0){printk("of_get_named_gpio failed\n");return -1;}printk("of_get_named_gpio success.  no1=[%d]\n", led1_gpio_no);if (gpio_request(led1_gpio_no, "led1") < 0){printk("gpio_request failed\n");return -1;}printk("gpio_request success\n");gpio_direction_output(led1_gpio_no, 0);// led1 = 获取gpio操作句柄led2_gpio_no = of_get_named_gpio(np, "led2-gpio", 0);if (led2_gpio_no < 0){printk("of_get_named_gpio failed\n");return -1;}printk("of_get_named_gpio success.  no2=[%d]\n", led2_gpio_no);if (gpio_request(led2_gpio_no, "led2") < 0){printk("gpio_request failed\n");return -1;}printk("gpio_request success\n");gpio_direction_output(led2_gpio_no, 0);// led3 = 获取gpio操作句柄led3_gpio_no = of_get_named_gpio(np, "led3-gpio", 0);if (led3_gpio_no < 0){printk("of_get_named_gpio failed\n");return -1;}printk("of_get_named_gpio success.  no3=[%d]\n", led3_gpio_no);if (gpio_request(led3_gpio_no, "led3") < 0){printk("gpio_request failed\n");return -1;}printk("gpio_request success\n");gpio_direction_output(led3_gpio_no, 0);// 初始化gpio// 向设备管理器udev注册设备文件信息pClass = class_create(THIS_MODULE, "myled");if (IS_ERR(pClass)){printk("class_create failed\n");return -1;}printk("class_create success\n");for (minor = 1; minor < 4; minor++){struct device *pDev = device_create(pClass, NULL, MKDEV(major, minor), NULL, "myled%d", minor);if (IS_ERR(pDev)){printk("device_create failed\n");return -1;}printk("device_create success. major=[%d], monor=[%d]\n", major, minor);}return 0;
}void __exit led_driver_exit(void)
{int minor;// 从设备管理器udev注销设备文件信息for (minor = 1; minor < 4; minor++){device_destroy(pClass, MKDEV(major, minor));}class_destroy(pClass);// 释放gpio资源gpio_free(led1_gpio_no);// 注销字符设备驱动 unregister_chrdevunregister_chrdev(major, "myled");
}module_init(led_driver_init);
module_exit(led_driver_exit);
MODULE_LICENSE("GPL");

2.3、fan驱动程序

  • fan.h:
#ifndef _FAN_H_
#define _FAN_H_#define CMD_FAN_ON _IO('F', 0)
#define CMD_FAN_OFF _IO('F', 1)#endif
  • fan.c:
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device/class.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include "fan_driver.h"int major;//主设备号
struct device_node * np;//设备树节点
int fan_gpio_no;//设备树种gpio编号
struct class *pClass;//udev中的文件分类long file_ioctl(struct file * file, unsigned int cmd, unsigned long arg){if(cmd == CMD_FAN_ON){printk("!!!!开风扇\n");gpio_set_value(fan_gpio_no, 1);}else if(cmd == CMD_FAN_OFF){printk("!!!!关风扇\n");gpio_set_value(fan_gpio_no, 0);}return 0;
}int file_open(struct inode * inode, struct file * file){return 0;
}int file_close(struct inode * inode, struct file * file){return 0;
}struct file_operations led_fops={.unlocked_ioctl = file_ioctl,.open = file_open,.release = file_close
};int __init fan_driver_init(void){//注册字符设备驱动 register_chrdevmajor = register_chrdev(0, "myfan", &led_fops);if(major == 0){printk("register_chrdev failed\n");return -1;}printk("register_chrdev succes. major=[%d]\n", major);//从设备树获取硬件信息np = of_find_node_by_name(NULL, "fans");if(np == NULL){printk("of_find_node_by_name failed\n");return -1;  }printk("of_find_node_by_name success\n");//获取gpio操作句柄fan_gpio_no =of_get_named_gpio(np, "fan-gpio", 0);if (fan_gpio_no < 0){printk("of_get_named_gpio failed\n");return -1;}printk("of_get_named_gpio success.  fan_gpio_no=[%d]\n", fan_gpio_no);if(gpio_request(fan_gpio_no, "fan-gpio") < 0){printk("gpio_request failed\n");return -1;}printk("gpio_request success\n");//初始化gpio//设置gpio为输出gpio_direction_output(fan_gpio_no, 0);//向设备管理器udev注册设备文件信息pClass = class_create(THIS_MODULE, "fan_class");if (IS_ERR(pClass)) {printk("class_create failed\n");return -1;}printk("class_create success\n");struct device * pDevice=device_create(pClass, NULL, MKDEV(major, 0), NULL, "myfan");if(IS_ERR(pDevice)){printk("device_create failed\n");return -1;}printk("device_create success\n");return 0;
}void __exit fan_driver_exit(void){//从设备管理器udev注销设备文件信息device_destroy(pClass, MKDEV(major, 0));class_destroy(pClass);//释放gpio资源gpio_free(fan_gpio_no);//注销字符设备驱动 unregister_chrdevunregister_chrdev(major, "myfan");
}module_init(fan_driver_init);
module_exit(fan_driver_exit);
MODULE_LICENSE("GPL");

3、疑问

配置设备树中,引用关系并没有搞明白,暂且认为是一种固定的格式。

例如:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/896030.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python应用算法之贪心算法理解和实践

一、什么是贪心算法&#xff1f; 贪心算法&#xff08;Greedy Algorithm&#xff09;是一种简单而高效的算法设计思想&#xff0c;其核心思想是&#xff1a;在每一步选择中&#xff0c;都采取当前状态下最优的选择&#xff08;即“局部最优解”&#xff09;&#xff0c;希望通…

竞争与冒险问题【数电速通】

时序逻辑电路&#xff1a; 组合逻辑电路中的竞争与冒险问题&#xff1a; 在组合逻辑电路中&#xff0c;竞争和冒险是两种常见的时序问题&#xff0c;它们通常由电路的延时特性和不完美的设计引起。下面是这两种现象的详细解释&#xff1a; 1. 竞争&#xff08;Race Condition&…

nasm - BasicWindow_64

文章目录 nasm - BasicWindow_64概述笔记nasm_main.asmmy_build.batEND nasm - BasicWindow_64 概述 学个demo, 这个demo最主要学到了: 不用在调用每个API前都准备阴影区&#xff0c;在API调用后栈平衡。 可以在函数入口处考虑到所用的栈尺寸最大值(16字节对齐&#xff0c;阴…

JavaScript变量的作用域介绍

JavaScript变量的作用域介绍 JavaScript 变量的作用域决定了变量在代码中的可访问性。 var 是 JavaScript 中最早用于声明变量的关键字&#xff0c;它函数作用域或全局作用域。 let 关键字&#xff0c;具有块级作用域、全局作用域。 const关键字&#xff0c;具有块级作用域…

Microsoft 365 Copilot中使用人数最多的是哪些应用

今天在浏览Microsoft 365 admin center时发现&#xff0c;copilot会自动整理过去30天内所有用户使用copilot的概况&#xff1a; 直接把这个图丢给copilot让它去分析&#xff0c;结果如下&#xff1a; 总用户情况 总用户数在各应用中均为 561 人&#xff0c;说明此次统计的样本…

ue5.2.1 quixel brideg显示asset not available in uAsset format

我从未见过如此傻x的bug&#xff0c;在ue5.2.1上通过内置quixel下载资源显示 asset not available in uAsset format 解决办法&#xff1a;将ue更新到最新版本&#xff0c;通过fab进入商场选择资源后add to my library 点击view in launcher打开epic launcher&#xff0c;就可…

当电脑上有几个python版本Vscode选择特定版本python

查看当前vscode用的python版本命令 Import sys print(sys.version) 修改VSCODE解释器 打开 VSCode。 按下 CtrlShiftP打开命令面板。 输入 Python: Select Interpreter 并选择它。 从弹出的列表中选择你安装的 Python 解释器。如果你有多个 Python 版本&#xff08;例如…

Vue 中 nextTick 的原理详解

1. 为什么需要 nextTick Vue 采用 异步渲染机制&#xff0c;当响应式数据发生变化时&#xff0c;Vue 并不会立即更新 DOM&#xff0c;而是将这些变化放入一个 队列 中&#xff0c;并在 同一事件循环&#xff08;Event Loop&#xff09;中合并相同的修改&#xff0c;最后执行批…

Spring面试题2

1、compareable和compactor区别 定义与包位置:Comparable是一个接口&#xff0c;位于java.lang包,需要类去实现接口&#xff1b;而Compactor是一个外部比较器&#xff0c;位于java.util包 用法&#xff1a;Comparable只需要实现int compareTo(T o) 方法&#xff0c;比较当前对…

DuodooBMS源码解读之 cncw_statement模块

财务应收应付扩展模组用户使用手册 一、模块概述 财务应收应付扩展模组是一个基于 Odoo18 的扩展模块&#xff0c;主要对财务应收应付相关功能进行了修改和增强。该模块增加了多个功能模块&#xff0c;如预收款单模块、费用类别设置模块等&#xff0c;同时对发票、公司、销售…

JUC并发—9.并发安全集合四

大纲 1.并发安全的数组列表CopyOnWriteArrayList 2.并发安全的链表队列ConcurrentLinkedQueue 3.并发编程中的阻塞队列概述 4.JUC的各种阻塞队列介绍 5.LinkedBlockingQueue的具体实现原理 6.基于两个队列实现的集群同步机制 4.JUC的各种阻塞队列介绍 (1)基于数组的阻塞…

vue项目启动时报错:error:0308010C:digital envelope routines::unsupported

此错误与 Node.js 的加密模块有关&#xff0c;特别是在使用 OpenSSL 3.0 及以上版本时。Vue 项目在启动时可能会依赖一些旧的加密算法&#xff0c;而这些算法在 OpenSSL 3.0 中默认被禁用&#xff0c;导致 error:0308010C:digital envelope routines::unsupported 错误。 解决…

ncDLRES:一种基于动态LSTM和ResNet的非编码RNA家族预测新方法

现有的计算方法主要分为两类&#xff1a;第一类是通过学习序列或二级结构的特征来预测ncRNAs家族&#xff0c;另一类是通过同源序列之间的比对来预测ncRNAs家族。在第一类中&#xff0c;一些方法通过学习预测的二级结构特征来预测ncRNAs家族。二级结构预测的不准确性可能会导致…

爱普生 SG-8101CE 可编程晶振在笔记本电脑的应用

在笔记本电脑的精密架构中&#xff0c;每一个微小的元件都如同精密仪器中的齿轮&#xff0c;虽小却对整体性能起着关键作用。如今的笔记本电脑早已不再局限于简单的办公用途&#xff0c;其功能愈发丰富多样。从日常轻松的文字处理、网页浏览&#xff0c;到专业领域中对图形处理…

SPRING10_getBean源码详细解读、流程图

文章目录 ①. getBean方法的入口-DefaultListableBeanFactory②. DefaultListableBeanFactory调用getBean③. 进入到doGetBean方法④. getSingleton三级缓存方法⑤. getSingleton()方法分析⑥. createBean创建对象方法⑦. 对象创建、属性赋值、初始化⑧. getBean最详细流程图 ①…

IDEA中查询Maven项目的依赖树

在Maven项目中&#xff0c;查看项目的依赖树是一个常见的需求&#xff0c;特别是当你需要了解项目中直接或间接依赖了哪些库及其版本时。你可以通过命令行使用Maven的dependency:tree插件来做到这一点。这个命令会列出项目中所有依赖的树状结构。 打开idea项目的终端&#xff…

深入xtquant:财务数据获取与应用的实战指南

深入xtquant&#xff1a;财务数据获取与应用的实战指南 在量化交易领域&#xff0c;虽然技术分析和市场情绪分析占据了主导地位&#xff0c;但财务数据作为评估公司基本面的重要依据&#xff0c;同样不可或缺。xtquant作为一个强大的Python库&#xff0c;提供了便捷的接口来获…

windows 安装 stable diffusion

在windows上安装 stable diffusion&#xff0c;如果windows没有nvidia显卡&#xff0c;想只使用CPU可在webui-user.bat中添加命令 set COMMANDLINE_ARGS--no-half --skip-torch-cuda-test 可正常使用stable diffusion&#xff0c;但速度较慢

Kubernetes控制平面组件:APIServer 基于 引导Token 的认证机制

云原生学习路线导航页&#xff08;持续更新中&#xff09; kubernetes学习系列快捷链接 Kubernetes架构原则和对象设计&#xff08;一&#xff09;Kubernetes架构原则和对象设计&#xff08;二&#xff09;Kubernetes架构原则和对象设计&#xff08;三&#xff09;Kubernetes控…

DeepSeek 助力 Vue 开发:打造丝滑的缩略图列表(Thumbnail List)

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…