嵌入式Linux应用开发-基础知识-第六章 LED驱动程序框架

news2024/11/24 4:51:27

嵌入式Linux应用开发-基础知识-第六章 LED驱动程序框架

  • 第六章 LED 驱动程序框架
    • 6.1 回顾字符设备驱动程序框架
    • 6.2 对于 LED 驱动,我们想要什么样的接口?
    • 6.3 LED 驱动要怎么写,才能支持多个板子?分层。
    • 6.4 写代码
      • 6.4.1 驱动程序
        • 6.4.1.1 把 LED 的操作抽象出一个 led_operations 结构体
        • 6.4.1.2 驱动程序的上层:file_operations 结构体
      • 6.4.2 测试程序
      • 6.4.3 上机测试
    • 6.5 课后作业

第六章 LED 驱动程序框架

在这里插入图片描述

注意:如果做实验安装驱动时提示 version magic 不匹配,请看本文档最后的“常见问题”。

6.1 回顾字符设备驱动程序框架

在这里插入图片描述

6.2 对于 LED 驱动,我们想要什么样的接口?

在这里插入图片描述

6.3 LED 驱动要怎么写,才能支持多个板子?分层。

  1. 把驱动拆分为通用的框架(leddrv.c)、具体的硬件操作(board_X.c):
    在这里插入图片描述
  2. 以面向对象的思想,改进代码:
    抽象出一个结构体:
    在这里插入图片描述
    每个单板相关的 board_X.c 实现自己的 led_operations 结构体,供上层的 leddrv.c 调用:
    在这里插入图片描述

6.4 写代码

使用 GIT 下载所有源码后,本节源码位于如下目录:

01_all_series_quickstart\ 
05_嵌入式 Linux 驱动开发基础知识\source\02_led_drv\01_led_drv_template 

6.4.1 驱动程序

驱动程序分为上下两层:leddrv.c、board_demo.c。
leddrv.c 负责注册 file_operations 结构体,它的 open/write 成员会调用 board_demo.c 中提供的硬
件 led_opr 中的对应函数。

6.4.1.1 把 LED 的操作抽象出一个 led_operations 结构体

首先看看 led_opr.h,它定义了一个 led_operations 结构体,把 LED 的操作抽象为这个结构体:

01 #ifndef _LED_OPR_H 
02 #define _LED_OPR_H 
03 
04 struct led_operations { 
05 int (*init) (int which); /* 初始化 LED, which-哪个 LED */ 
06 int (*ctl) (int which, char status); /* 控制 LED, which-哪个 LED, status:1-亮,0-灭 
*/ 
 07 }; 
08 
09 struct led_operations *get_board_led_opr(void); 
10 
11 
12 #endif 
13 
6.4.1.2 驱动程序的上层:file_operations 结构体

上层是 leddrv.c,它的核心是 file_operations 结构体,首先看看入口函数:

80 /* 4. 把 file_operations 结构体告诉内核:注册驱动程序 */ 
81 /* 5. 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数 */ 
82 static int __init led_init(void) 
83 { 
84 int err; 
85 int i; 
86 
87 printk(%s %s line %d\n”, __FILE__, __FUNCTION__, __LINE__); 
88 major = register_chrdev(0, “xxxxxx_led”, &led_drv); /* /dev/led */ 
89 
90 
91 led_class = class_create(THIS_MODULE, “xxxxxx_led_class”); 
92 err = PTR_ERR(led_class); 
93 if (IS_ERR(led_class)) { 
94 printk(%s %s line %d\n”, __FILE__, __FUNCTION__, __LINE__); 
95 unregister_chrdev(major, “xxxxxx_led”); 
96 return -1; 
97 } 
98 
99 for (i = 0; i < LED_NUM; i++) 
100 device_create(led_class, NULL, MKDEV(major, i), NULL, “xxxxxx_led%d”, 
i); /* /dev/xxxxxx_led0,1,… */ 
101 
102 p_led_opr = get_board_led_opr(); 
103 
104 return 0; 
105 } 


第 88 行向内核注册一个 file_operations 结构体。
第 102 行从底层硬件相关的代码 board_demo.c 中获得 led_operaions 结构体。

再来看看 leddrv.c 中 file_operations 结构体的成员函数:

37 /* write(fd, &val, 1); */ 
38 static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, 
loff_t *offset) 
39 { 
40 int err; 
41 char status; 
42 struct inode *inode = file_inode(file); 
43 int minor = iminor(inode); 
44 
45 printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); 
46 err = copy_from_user(&status, buf, 1); 
47 
48 /* 根据次设备号和 status 控制 LED */ 
49 p_led_opr->ctl(minor, status); 
50 
51 return 1; 
52 } 
53 
54 static int led_drv_open (struct inode *node, struct file *file) 
55 { 
56 int minor = iminor(node); 
57 
58 printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); 
59 /* 根据次设备号初始化 LED */ 
60 p_led_opr->init(minor); 
61 
62 return 0; 
63 } 
64 
65 static int led_drv_close (struct inode *node, struct file *file) 
66 { 
67 printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); 
68 return 0; 
69 } 
70 
71 /* 2. 定义自己的 file_operations 结构体 */ 
72 static struct file_operations led_drv = { 
73 .owner = THIS_MODULE, 
74 .open = led_drv_open, 
75 .read = led_drv_read, 
76 .write = led_drv_write, 
77 .release = led_drv_close, 
78 };


第 49 行、第 60 行,会调用 led_operations 结构体中对应的函数。

6.4.2 测试程序

测试程序为 ledtest.c:

01 
02 #include <sys/types.h> 
03 #include <sys/stat.h> 
04 #include <fcntl.h> 
05 #include <unistd.h> 
06 #include <stdio.h> 
07 #include <string.h> 
08 
09 /* 
10 * ./ledtest /dev/xxxxxx_led0 on 
11 * ./ledtest /dev/xxxxxx_led0 off 
12 */ 
13 int main(int argc, char **argv) 
14 { 
15 int fd; 
16 char status; 
17 
18 /* 1. 判断参数 */ 
19 if (argc != 3) 
20 { 
21 printf("Usage: %s <dev> <on | off>\n", argv[0]); 
22 return -1; 
23 } 
24 
25 /* 2. 打开文件 */ 
26 fd = open(argv[1], O_RDWR); 
27 if (fd == -1) 
28 { 
29 printf("can not open file %s\n", argv[1]); 
30 return -1; 
31 } 
32
33 /* 3. 写文件 */ 
34 if (0 == strcmp(argv[2], "on")) 
35 { 
36 status = 1; 
37 write(fd, &status, 1); 
38 } 
39 else 
40 { 
41 status = 0; 
42 write(fd, &status, 1); 
43 } 
44 
45 close(fd); 
46 
47 return 0; 
48 } 
49 


第 26 行打开设备节点。
如果用户想点亮 LED,第 37 行会把值“1”通过 write 函数写入驱动程序。
如果用户想熄灭 LED,第 42 行会把值“0”通过 write 函数写入驱动程序。

6.4.3 上机测试

这只是一个示例程序,还没有真正操作硬件。测试程序操作驱动程序时,只会导致驱动程序中打印信息。
首先设置交叉工具链,修改驱动 Makefile 中内核的源码路径,编译驱动和测试程序。
启动开发板后,通过 NFS 访问编译好驱动程序、测试程序,就可以在开发板上如下操作了:

# insmod xxxxxx_led.ko // 装载驱动程序 
[13449.134044] /home/book/source/02_led_drv/01_led_drv_template/leddrv.c led_init line 87 
# ls /dev/xxxxxx_led* -l // 可以得到 2 个设备节点 
crw------- 1 root root 235, 0 Jan 18 12:34 /dev/xxxxxx_led0 
crw------- 1 root root 235, 1 Jan 18 12:34 /dev/xxxxxx_led1 
# ./ledtest /dev/xxxxxx_led0 on // 点亮 LED 
[13463.176987] /home/book/source/02_led_drv/01_led_drv_template/leddrv.c led_drv_open 
line 58 
[13463.197877] /home/book/source/02_led_drv/01_led_drv_template/board_demo.c 
board_demo_led_init line 22, led 0 
[13463.216232] /home/book/source/02_led_drv/01_led_drv_template/leddrv.c led_drv_write 
line 45 
[13463.232889] /home/book/source/02_led_drv/01_led_drv_template/board_demo.c 
board_demo_led_ctl line 28, led 0, on // 可以看到这句“on”打印 
[13463.247977] /home/book/source/02_led_drv/01_led_drv_template/leddrv.c led_drv_close 
line 67 
# ./ledtest /dev/xxxxxx_led0 off // 熄灭 LED 
[13464.540637] /home/book/source/02_led_drv/01_led_drv_template/leddrv.c led_drv_open 
line 58 
[13464.554380] /home/book/source/02_led_drv/01_led_drv_template/board_demo.c 
board_demo_led_init line 22, led 0 
[13464.569671] /home/book/source/02_led_drv/01_led_drv_template/leddrv.c led_drv_write 
line 45 
[13464.580615] /home/book/source/02_led_drv/01_led_drv_template/board_demo.c 
board_demo_led_ctl line 28, led 0, off // 可以看到这句“off”打印 
[13464.593397] /home/book/source/02_led_drv/01_led_drv_template/leddrv.c led_drv_close 
line 67 

6.5 课后作业

实现读 LED 状态的功能:涉及 APP 和驱动。
在这里插入图片描述

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

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

相关文章

车厂造手机旗舰版售价6899,不只是个“车钥匙”

大伙应该发现了这两年的一个奇怪现象&#xff1a;造手机的跑去造车&#xff0c;而车厂又跑来造手机。 这也好解释&#xff0c;毕竟别人碗里的更香、别人老氵…板先挑衅是吧&#xff1f; 昨日&#xff0c;蔚来在其创新科技日活动中正式发布了传闻已久的手机产品 NIO Phone 。 …

最全百科:什么是精益生产?有哪些难点?怎么解决?

什么是生产管理&#xff1f;什么是精益生产&#xff1f;企业生产管理都有哪些难点&#xff1f;企业生产管理又有哪些解决方案&#xff1f;如何高效的进行生产管理&#xff1f;精益生产有哪些管理工具&#xff1f;本篇&#xff0c;我们将为细细盘点企业生产管理的那些事&#xf…

电脑桌面自动提醒的闹钟软件是哪个?

在繁忙的工作中&#xff0c;我们需要保持高效率和准时完成任务。同时&#xff0c;我们也希望能够在电脑桌面上一直有提醒的闹钟&#xff0c;以确保我们不会错过任何重要的会议、任务和活动。然而&#xff0c;传统的设置电脑桌面闹钟提醒方法却过于复杂&#xff0c;让人烦恼不已…

一文教你学会ArcGIS Pro地图设计与制图系列全流程(1)

ArcGIS Pro做的成果图及系列文章目录&#xff1a; 系列文章全集&#xff1a; 《一文教你学会ArcGIS Pro地图设计与制图系列全流程&#xff08;1&#xff09;》《一文教你学会ArcGIS Pro地图设计与制图系列全流程&#xff08;2&#xff09;》《一文教你学会ArcGIS Pro地图设计与…

项目进展(二)-配置MCT8316ZR芯片,主要学习如何根据数据手册配置外设等

近几天一直和师兄在配置驱动芯片&#xff0c;目前的进展是可以通过SPI配置、读取寄存器等&#xff0c;但是电机一直未被驱动起来(这个问题困扰了我们一两天了&#xff0c;希望有大佬可以帮忙解决一下)。 配置SPI要先根据数据手册的说明&#xff0c;确认好SPI如何配置&#xff0…

探索轻量级模型性能上限,基于GhostNet模型开发构建多商品细粒度图像识别系统

商品图像数据的细粒度识别有别于传统的图像识别任务&#xff0c;本身细粒度识别对于模型特征提取计算能力要求就比较高&#xff0c;在我们前面的一些项目中&#xff0c;涉及到的细粒度识别大多是同一物种下不同亚种的识别&#xff0c;比如&#xff1a;鸟类细粒度识别、狗类细粒…

面试打底稿② 专业技能的第二部分

简历原文 抽查部分 比较熟悉Nacos、Feign、SpringCloud Gateway等微服务的使用&#xff0c;有实际上手项目使用的经验&#xff1b;基本掌握Linux常用命令&#xff0c;了解Linux系统管理、网络管理、生产环境等必用服务&#xff0c;了解Docker的使用&#xff0c;在博客中多有关…

基于微信小程序的宠物用品商城设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言系统主要功能&#xff1a;具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计…

echarts 地图 visualMap 图例点击事件监听

一、切换位置 二、切换监听 // 切换事件监听 this.myChart.off(datarangeselected); // 为了不触发两次 this.myChart.on(datarangeselected,(e) > {// visualMap change });// 配置如下 visualMap: {type: piecewise,showLabel: true,inverse: true,pieces: [{value: 1,…

2023测试开发必知必会:Pytest框架实战!

应用场景&#xff1a; pytest 框架可以解决我们多个测试脚本一起执行的问题。 它提供了测试用例的详细失败信息&#xff0c;使得开发者可以快速准确地改正问题。它兼容最新版本的 Python。它还兼容 unittest、doctest 和 nose&#xff0c;开箱即用。接下来我们详细了解下pyte…

数字孪生在智慧城市应用场景中的五大特点

数字孪生城市提出至今&#xff0c;已从概念、框架走向落地深耕&#xff0c;逐渐演变成为城市变革新动力和城市转型新路径&#xff0c;是智慧城市发展演进的重要方向。 数字孪生城市建设现已加速步入“技术多维集成、场景创新重构、市场成效导向”的落地实施时期。这一时期&…

HTML的学习 Day02(列表、表格、表单)

文章目录 一、列表列表主要分为以下三种类型&#xff1a;1. 无序列表&#xff08;Unordered List&#xff09;&#xff1a;2. 有序列表&#xff08;Ordered List&#xff09;&#xff1a;将有序列表的数字改为字母或自定义内容li.../li 列表项标签中value属性&#xff0c;制定列…

【RabbitMQ实战】06 3分钟部署一个RabbitMQ集群

一、集群的安装部署 我们还是利用docker来安装RabbitMQ集群。3分钟安装一个集群&#xff0c;开始。 前提条件&#xff0c;docker安装了docker-compose。如果没安装的话&#xff0c;参考这里 docker-compose文件参考bitnami官网&#xff1a;https://github.com/bitnami/contai…

JetBrains 产品安装插件(plugins)的两种方式

安装分为在线、离线两种方式&#xff1a; 在线方式&#xff1a; File > Settings > Plugins 搜索插件 Install 即可 离线方式&#xff1a; 官网&#xff1a;https://plugins.jetbrains.com/ 搜索到插件后&#xff0c;点击 "Get"&#xff0c;选择自己安装的…

obsidian加git备份,同时忽略掉自己不想同步的文件夹

最近想用这个语雀进行知识库的分享&#xff0c;但是这个语雀的会员费太贵了&#xff0c;思来想去还是用 git 比较好&#xff0c;因为这个知识库的内容都是自己的笔记&#xff0c;为了能够访问的更加方便我选择了这个 gitte&#xff0c;而不是 github 我的知识库链接 knowledge …

报错 - 找不到模块“@/...“或其相应的类型声明

项目是vue3jsvite&#xff0c;在 vite.config.js 和 jsconfig.json 的配置无误的情况下&#xff0c;报错找不到模块。 解决方法&#xff1a;卸载重新安装 Error Lens 插件 报错没有啦。可能是插件内部错误&#xff1f;

opencv之利用gpu进行编程

很多朋友工作中会遇到需要使用gpu的例子&#xff0c;gpu就是显卡&#xff0c;大部分对gpu有个模糊的概念&#xff0c;即图像渲染会使用到他&#xff0c;但是它是如何生效的&#xff0c;又说不清楚。本篇文章主要介绍opencv下如何使用gpu进行编程。 基本介绍 opencv下存在gpu可…

软件测试:全链路追踪工具 Zipkin导入、安装(Windows版本)

1.0全链路追踪技术出现的原因 公司内部一个功能的实现&#xff0c;底层可能调用多个应用系统 在调用这个功能的同时&#xff0c;可能会出现多种情况&#xff0c;比如访问较慢&#xff0c;出现错误&#xff0c;可能需要进行定位 所以&#xff0c;我们需要快速定位服务错误点 大…

外汇天眼:SEC起诉“现金流之王”播客主持人涉嫌1100万美元庞氏骗局

美国证券交易委员会&#xff08;SEC&#xff09;今天指控了“现金流之王”播客主持人马修莫蒂尔&#xff0c;涉嫌通过一个庞氏骗局欺骗超过50名投资者&#xff0c;非法筹集了约1100万美元&#xff0c;涉及的票据据称由住宅物业支持。 根据SEC的投诉&#xff0c;俄亥俄州北奥尔姆…

openstack中通过主机mac地址查询主机id

问题&#xff1a; 客户给vm配置了子接口&#xff0c;并且接口上的ip可以ping通&#xff0c;客户需要从众多的主机中找到这台主机 解决办法 一 、通过子接口开放的端口让客户自行匹配自己业务进行查找 此处让客户自行查找即可 二 、通过数据库查找 注意&#xff1a;由于子接…