Linux 驱动开发基础知识——编写LED驱动程序(三)

 个人名片:

🦁作者简介:一名喜欢分享和记录学习的在校大学生
🐯个人主页:妄北y

🐧个人QQ:2061314755

🐻个人邮箱:2061314755@qq.com
🦉个人WeChat:Vir2021GKBS
🐼本文由妄北y原创,首发CSDN🎊🎊🎊
🐨座右铭:大多数人想要改造这个世界,但却罕有人想改造自己。

专栏导航:

妄北y系列专栏导航:

C/C++的基础算法:C/C++是一种常用的编程语言,可以用于实现各种算法,这里我们对一些基础算法进行了详细的介绍与分享。🎇🎇🎇

C/C++刷题库:分享一些关于编程的练习基础题,也会后续加入一系列的算法题,分享自己的解题思路和方法。🥰🥰🥰

计算机网络:对计算机网络的基础知识框架有一个简单的学习与认识,对计算机网络中常见的题型进行一个总结与归纳。🍾🍾🍾

QT基础入门学习:对QT的基础图形化页面设计进行了一个简单的学习与认识,利用QT的基础知识进行了翻金币小游戏的制作🤹🤹🤹

Linux基础编程:初步认识什么是Linux,为什么学Linux,安装环境,进行基础命令的学习,入门级的shell编程。🍻🍻🍻

Linux的系统编程+网络编程:IO编程、进程、线程、进程间通讯(包括管道、信号、信号量、共享内存等)网络编程主要就是socket,poll,epoll,以及对TCP/IP的理解,同时要学会高并发式服务器的编写。🙌🙌🙌

Linux应用开发基础开发:分享Linux的基本概念、命令行操作、文件系统、用户和权限管理等,网络编程相关知识,TCP/IP 协议、套接字(Socket)编程等,可以实现网络通信功能。💐💐💐

Linux项目开发:Linux基础知识的实践,做项目是最锻炼能力的一个学习方法,这里我们会学习到一些简单基础的项目开发与应用,而且都是毕业设计级别的哦。🤸🤸🤸


非常期待和您一起在这个小小的互联网世界里共同探索、学习和成长。💝💝💝 ✨✨ 欢迎订阅本专栏 ✨✨ 

文章介绍:

🎉本篇文章对Linux 驱动开发基础知识学习的相关知识进行分享!🥳🥳🥳

如果您觉得文章不错,期待你的一键三连哦,你的鼓励是我创作动力的源泉,让我们一起加油,一起奔跑,让我们顶峰相见!!!💪💪💪

🎁感谢大家点赞👍收藏⭐评论✍️

一、LED 驱动程序编程_基于 IMX6ULL

1.字符设备驱动程序框架

编写驱动程序的套路:

        (1)确定主设备号,也可以让内核分配

        (2)定义自己的 file_operations 结构体

        (3)实现对应的 drv_open/drv_read/drv_write 等函数,填入 file_operations 结构体

        (4)把 file_operations 结构体告诉内核:register_chrdev

        (5) 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这 个入口函数。

        (6)有入口函数就应该有出口函数:卸载驱动程序时,出口函数调用 unregister_chrdev 其他完善:提供设备信息,自动创建设备节点:class_create,device_create

2.驱动怎么操作硬件?

        通过 ioremap 映射寄存器的物理地址得到虚拟地址,读写虚拟地址

3.驱动怎么和 APP 传输数据?

         通过 copy_to_user、copy_from_user 这 2 个函数。

4.实现什么功能

(1)先编写驱动程序:

        实现 led_open 函数,在里面初始化 LED 引脚。

        实现 led_write 函数,在里面根据 APP 传来的值控制 LED。

(2)再编写测试程序。

二、编写代码:

1.驱动程序led_drv.c


#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/mutex.h>
#include <linux/wait.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <asm/io.h>static int major;
static struct class *led_class;/* registers */
// IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 地址:0x02290000 + 0x14
static volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;// GPIO5_GDIR 地址:0x020AC004
static volatile unsigned int *GPIO5_GDIR;//GPIO5_DR 地址:0x020AC000
static volatile unsigned int *GPIO5_DR;static ssize_t led_write(struct file *filp, const char __user *buf,size_t count, loff_t *ppos)
{char val;int ret;/* copy_from_user : get data from app */ret = copy_from_user(&val, buf, 1);/* to set gpio register: out 1/0 */if (val){/* set gpio to let led on */*GPIO5_DR &= ~(1<<3);}else{/* set gpio to let led off */*GPIO5_DR |= (1<<3);}return 1;
}static int led_open(struct inode *inode, struct file *filp)
{/* enable gpio5* configure gpio5_io3 as gpio* configure gpio5_io3 as output */*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 &= ~0xf;*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 |= 0x5;*GPIO5_GDIR |= (1<<3);return 0;
}static struct file_operations led_fops = {.owner		= THIS_MODULE,.write		= led_write,.open		= led_open,
};/* 入口函数 */
static int __init led_init(void)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);major = register_chrdev(0, "100ask_led", &led_fops);/* ioremap */// IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 地址:0x02290000 + 0x14IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = ioremap(0x02290000 + 0x14, 4);// GPIO5_GDIR 地址:0x020AC004GPIO5_GDIR = ioremap(0x020AC004, 4);//GPIO5_DR 地址:0x020AC000GPIO5_DR  = ioremap(0x020AC000, 4);led_class = class_create(THIS_MODULE, "myled");device_create(led_class, NULL, MKDEV(major, 0), NULL, "myled"); /* /dev/myled */return 0;
}static void __exit led_exit(void)
{iounmap(IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3);iounmap(GPIO5_GDIR);iounmap(GPIO5_DR);device_destroy(led_class, MKDEV(major, 0));class_destroy(led_class);unregister_chrdev(major, "100ask_led");
}module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

第15行:系统主设备号

第28~49行:为应用程序write提供驱动程序       

        第35行:从应用程序APP得到数据,读取用户空间传进来的值

                把用户空间的数据buf拷贝到&val内核空间,拷贝1个字节

        第36~42行:根据数据去设置GPIO的寄存器去输出1/0

        第38~48行:根据val判断是将GPIO配置高还是低

第51~63行:为应用程序open提供驱动程序

        启动GPIO

        配置gpio5 io3为gpio              

        配置gpio5 io3为输出

第65~69行:定义一个file_operations结构体,里面每个成员是驱动程序的具体功能 

第72~92行:入口函数

        第74行:监测入口函数是否被执行

                __FILE__:文件

                __FUNCTION:__函数

                __LINE__:第几行

        第76行:调用register_chrdev()来注册某个file_operations结构体,获得系统提供的主设备号

        第79行:映射虚拟地址

        第88~89行:系统为我们创建/dev/myled的设备节点

                第88行:根据系统函数提供设备节点

                第89行:在这个led_class下创建设备

        

第94~104行:出口函数 

        第103行:卸载驱动程序

        第100行:销毁掉创建的设备节点/dev/myled

        第101行:销毁掉创建的类

第106行:修饰入口函数

第107行:修饰出口函数

第108行:获取指定GPL协议

以上为驱动程序的模板部分,下面我们对IMX6ull的硬件操作进行梳理 


 使能GPIO5:本身默认使能,所以我们可以忽视这部分的操作

 第18~26行:设置GPIO5_3为GPIO:

 IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 地址:0x02290000 + 0x14        

        第19~26行:定义变量操作寄存器:使用指针操作读写寄存器地址,物理地址,后面需要把物理地址映射成虚拟地址

                第19行:第一个寄存器地址,GPIO5_3的地址

                第22行:设置GPIO5_GDIR地址用于设置输入输出的方向

                第26行:设置GPIO5_DR地址用于设置输入或输出电平

 第79~86行:在入口函数里将物理地址映射为虚拟地址

        第80行:用ioremap0x02290000 + 0x14物理地址映射为4个字节虚拟地址

        第83行:用ioremap0x020AC004物理地址映射为4个字节虚拟地址

        第86行:用ioremap0x020AC000物理地址映射为4个字节虚拟地址

 第96~98行:在出口函数里清除ioremap的虚拟地址

第53~60行:使用虚拟地址操作寄存器 

        第57行:清除掉最低的4位,防止其他驱动程序调用

        第58行:设置为101,将引脚配置为GPIO

        第60行:设置GPI05_3为1,设置为输出的方向

实现点灯操作:将GPI05_3输出低电平

第31行:设置GPI05_3为输出0, 实现点亮led

第46行:设置GPI05_3为输出1,实现关闭led

2.应用程序 ledtest.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>// ledtest /dev/myled on
// ledtest /dev/myled offint main(int argc, char **argv)
{int fd;char status = 0;if (argc != 3){printf("Usage: %s <dev> <on|off>\n", argv[0]);printf("  eg: %s /dev/myled on\n", argv[0]);printf("  eg: %s /dev/myled off\n", argv[0]);return -1;}// openfd = open(argv[1], O_RDWR);if (fd < 0){printf("can not open %s\n", argv[0]);return -1;}// writeif (strcmp(argv[2], "on") == 0){status = 1;}write(fd, &status, 1);return 0;	
}

第15行:监测当前状态值,根据 on和off进行判断

 第17~23行:当输入不正确时打印用法

        第19行:具体用法

        第20行:打开led的用法

        第21行:关闭led的用法

第23行:打印文件 

        第26~30行:打开文件失败

 第32~36行:打开成功,写入数据

3.Makefile 


# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
# 2.1 ARCH,          比如: export ARCH=arm64
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-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_imx6ull-sdk/Linux-4.9.88all:make -C $(KERN_DIR) M=`pwd` modules $(CROSS_COMPILE)gcc -o ledtest ledtest.c clean:make -C $(KERN_DIR) M=`pwd` modules cleanrm -rf modules.orderrm -f ledtestobj-m	+= led_drv.o

第10行:编译时用到的Linux内核路径

第12~19行:编译测试程序

第21行:编译内核程序

三、上机实验

1.编译

编译程序,把代码上传代服务器后执行 make 命令。

2.挂载到开发板 

在开发板上挂载 NFS 

 

最后在开发板上加载驱动程序,执行测试程序,如下: 

[root@100ask:~]# echo "7 4 1 7" > /proc/sys/kernel/printk // 打开内核的打印信息,有些
板子默认打开了
[ 1426.505931] /home/book/05_嵌入式Linux驱动开发基础知识/source/02_led_drv/00_led_drv_simple/imx6ull/led_drv.c led_init 75    //安装后打印的信息
[root@100ask:~]# insmod /mnt/led_drv.ko    //安装驱动
[root@100ask:~]# lsmod                     //查询驱动
[root@100ask:~]# rmmod /mnt/led_drv.ko     //卸载驱动
/mnt/ledtest /dev/myled on                 // 点灯
/mnt/ledtest /dev/myled off                // 关灯

 3.实验效果

点亮的是红灯旁边的黄色灯,照片可能看的效果不是很明显

        执行/mnt/ledtest /dev/myled on 这里有一个黄色小灯会亮起来

  执行/mnt/ledtest /dev/myled off 这里有一个黄色小灯会熄灭

大佬觉得有用的话点个赞 👍🏻 呗。
❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!😄😄😄

💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!👍 👍 👍

🔥🔥🔥任务在无形中完成,价值在无形中升华,让我们一起加油吧!🌙🌙🌙

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

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

相关文章

边裁边招,2024大厂往哪“卷”?

裁员只是大厂踌躇、转型的缩影&#xff0c;无论是林平还是背后的公司、行业&#xff0c;都亟需给未来一个确定的答案。 从2021年冬天开始&#xff0c;大厂裁员的消息有如“击鼓传花”般&#xff0c;不断冲击着圈内外网友们的神经&#xff0c;那可能是“裁员”这个话题自互联网…

css中>>>、/deep/、::v-deep的作用和区别,element-ui自定义样式

文章目录 一、前言1.1、/deep/1.2、::v-deep1.3、>>> 二、区别三、总结四、最后 一、前言 1.1、/deep/ 在style经常用scoped属性实现组件的私有化时&#xff0c;要改变element-ui某个深层元素&#xff08;例如.el-input__inner&#xff09;或其他深层样式时&#xf…

JOSEF约瑟 大容量中间继电器 RXMH2 RK223 067 DC220V 带底座

系列型号 RXMH2 RK 223 067大容量中间继电器&#xff1b; RXMH2 RK 223 068大容量中间继电器&#xff1b; RXMH2 RK 223 069大容量中间继电器&#xff1b; RXMH2 RK 223 070大容量中间继电器&#xff1b; 一、用途 RXMH2系列大容量中间继电器用于工业自动化控制及电力系统二次…

app重新签名之后,teamID会改变吗?

哈喽大家好&#xff0c;我是咕噜美乐蒂&#xff0c;很高兴又见面啦&#xff01; 在iOS和macOS开发中&#xff0c;我们经常需要对应用进行重新签名&#xff0c;以便在发布或测试时更改应用的证书或代理。在重新签名应用程序时&#xff0c;可能会出现teamID变化的情况。本文将探…

冷知识:COS上传文件时可以同步获取文件信息

背景介绍 本文将介绍如何在上传文件到 COS 时同步获取文件信息&#xff0c;如图片的宽高、格式等。 目前&#xff0c;可以通过 COS 上传接口&#xff0c;如 PUT Object、CompleteMultipartUploads 等将文件存储至 COS 存储桶中&#xff0c;我们针对以下三种场景提供上传时同步…

Windows主机Navicat远程连接到Ubuntu18.04虚拟机MySQL

1. 在虚拟机上安装MySQL sudo apt-get install mysql-server sudo apt-get install libmysqlclient-dev 2. 检查安装 sudo netstat -tap | grep mysql 3. 查看默认密码 sudo cat /etc/mysql/debian.cnf 4. 用查看到的密码登录MySQL server&#xff0c;修改root用户的密码 …

javaWebssh宠物基地管理系统myeclipse开发mysql数据库MVC模式java编程计算机网页设计

一、源码特点 java ssh宠物基地管理系统是一套完善的web设计系统&#xff08;系统采用ssh框架进行设计开发&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用 B/S模式开发。开发环境为TOMCAT7.…

JS进阶-内置构造函数(二)

小提示&#xff1a;这些内置函数在开发使用的频率非常的频繁&#xff0c;建议认真看一下&#xff0c;并背一下 目录 知识回顾&#xff1a; • Object 三个常用静态方法&#xff08;静态方法就是只有构造函数Object可以调用的&#xff09; Object.keys Object.values Obj…

梯度的计算

文章目录 import torch as tc# 以函数f(x) x^2 为例子&#xff0c;求梯度 x1 tc.tensor(1,dtypetc.float64,requires_gradTrue) g1 tc.autograd.grad(x1**2,x1)[0] print(f"The grad of x**2 at {x1} is {g1}")(x1 ** 2).backward() print(f"The grad of x*…

浅谈WPF之UI布局

一个成功的软件&#xff0c;离不开人性化的UI设计&#xff0c;如何抓住用户第一视觉&#xff0c;让用户产生依赖感&#xff0c;合适优雅的布局必不可少。本文以一些简单的小例子&#xff0c;简述WPF中布局 面板 控件的使用&#xff0c;仅供学习分享使用&#xff0c;如有不足之处…

AWS 云监控工具

Amazon Web Services&#xff08;AWS&#xff09;是 Amazon 提供的云计算平台&#xff0c;为企业提供技术服务&#xff0c;包括计算能力、存储和数据库。借助 AWS&#xff0c;企业无需购买、拥有或维护物理数据中心和服务器&#xff0c;而 AWS 的即用即付方法使企业能够避免与 …

vcruntime140.dll找不到的多种解决方法分享,修复vcruntime140.dll文件

当操作电脑软件的过程中弹出一个错误提示“vcruntime140.dll已加载,但找不到入口点”&#xff0c;这通常意味着程序所依赖的vcruntime140.dll文件可能已经丢失、遭到破坏&#xff0c;或者该文件的路径及位置在软件开发时未被正确配置。此外&#xff0c;这种情况还有可能是因为电…

Windows11 Copilot助手开启教程(免费GPT-4)

Windows11上开启Copilot助手教程踩坑指南 Copilot介绍Copilot开启步骤1、更新系统2、更改语言和区域3、下载 ViVeTool 工具4、开启Copilot 使用 Copilot介绍 Windows Copilot 是 Windows 11 中的一个新功能&#xff0c;它可以让你与一个智能助理进行对话&#xff0c;获取信息&…

应用协议漏洞

应用协议漏洞 一、rsync rsync是Linux下一款数据备份工具&#xff0c;支持通过rsync协议、ssh协议进行远程文件传输。其中rsync协议默认监听873端口 1.未授权访问 打开靶场 判断漏洞是否存在 rsync rsync://目标ip:端口读取文件 rsync rsync://47.99.49.128:873/src/tmp/下…

浏览器打开本地应用和程序制作安装包

1、引言 最近使用python开发一款windows应用&#xff0c;有一个需求是从浏览器打开本地应用。从网上查到可以通过注册表的方法完成需求。所以就需要往注册表写内容。因此应用需要在安装的时候写注册表。 2、安装包制作工具&#xff1a;NSISVNISEdit NSIS可以制作安装包&…

JVM内存问题排查

本文又名《对JVM一窍不通的我快速开始排查应用内存问题》。主要系统性地整理了排查思路&#xff0c;为大家遇到问题时提供全面的排查流程&#xff0c;不至于漏掉某些可能性误入歧途浪费时间。 基本原则 由于本文的定位是Cookbook,基本原则是让整个流程能够系统化规范化的同时将…

Vue2学习之第六、七章——vue-router与ElementUI组件库

路由 理解&#xff1a; 一个路由&#xff08;route&#xff09;就是一组映射关系&#xff08;key - value&#xff09;&#xff0c;多个路由需要路由器&#xff08;router&#xff09;进行管理。前端路由&#xff1a;key是路径&#xff0c;value是组件。 1.基本使用 安装vue-…

李沐《动手学深度学习》深度学习计算

系列文章 李沐《动手学深度学习》预备知识 张量操作及数据处理 李沐《动手学深度学习》预备知识 线性代数及微积分 李沐《动手学深度学习》线性神经网络 线性回归 李沐《动手学深度学习》线性神经网络 softmax回归 李沐《动手学深度学习》多层感知机 模型概念和代码实现 李沐《…

使用 LinkAi 打造自己的知识库和数字人

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、LinkAi 介绍 二、文档库 2.1 创建知识库 2.2 配置知识库 2.3 Ai配置 2.4 导入文档 2.5 接入微信 三、扩展 四、总结…

结合CSS一些样式属性和属性值进行案例演示

案例演示 图1 1.结构分析 文字部分用<p>标签定义&#xff0c;对于特殊显示的文本&#xff08;如导语、详情&#xff09;等可以用<em>、<strong>等格式化标签来定义&#xff0c;效果图如图2。 图2 2.样式分析 &#xff08;1&#xff09;控制段落文本的字体…