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


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

目录: 

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

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

2.驱动怎么操作硬件?

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

4.实现什么功能

(1)先编写驱动程序:

(2)再编写测试程序。

二、编写代码:

1.驱动程序led_drv.c

2.应用程序 ledtest.c

3.Makefile 

三、上机实验

1.编译

2.挂载到开发板 

3.实验效果


文章介绍:

🎉本篇文章对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.rhkb.cn/news/245638.html

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

相关文章

ICSpector:一款功能强大的微软开源工业PLC安全取证框架

关于ICSpector ICSpector是一款功能强大的开源工业PLC安全取证框架&#xff0c;该工具由微软的研究人员负责开发和维护&#xff0c;可以帮助广大研究人员轻松分析工业PLC元数据和项目文件。 ICSpector提供了方便的方式来扫描PLC并识别ICS环境中的可疑痕迹&#xff0c;可以用于…

微信小程序(十五)自定义导航栏

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 1.组件文件夹创建方法 2.自定义组件的配置方法 3.外部修改组件样式&#xff08;关闭样式隔离或传参&#xff09; 创建组件文件夹 如果是手动创建建议注意在json文件声明&#xff1a; mynav.json {//声明为组件可…

【Kafka】开发实战和Springboot集成kafka

目录 消息的发送与接收生产者消费者 SpringBoot 集成kafka服务端参数配置 消息的发送与接收 生产者 生产者主要的对象有&#xff1a; KafkaProducer &#xff0c; ProducerRecord 。 其中 KafkaProducer 是用于发送消息的类&#xff0c; ProducerRecord 类用于封装Kafka的消息…

数据结构(绪论+算法的基本概念)

文章目录 一、绪论1.1、数据结构的基本概念1.2、数据结构三要素1.2.1、逻辑结构1.2.2、数据的运算1.2.3、物理结构&#xff08;存储结构&#xff09;1.2.4、数据类型和抽象数据类型 二、算法的基本概念2.1、算法的特性2.2、“好”算法的特质2.2.1、算法时间复杂度2.2.2、算法空…

【医学图像隐私保护】联邦学习:密码学 + 机器学习 + 分布式 实现隐私计算,破解医学界数据孤岛的长期难题

联邦学习&#xff1a;密码学 机器学习 分布式 提出背景&#xff1a;数据不出本地&#xff0c;又能合力干大事联邦学习的问题 分布式机器学习&#xff1a;解决大数据量处理的问题横向联邦学习&#xff1a;解决跨多个数据源学习的问题纵向联邦学习&#xff1a;解决数据分散在多…

SQL查询数据库环境(dm8达梦数据库)

SQL查询数据库环境dm8达梦数据库 环境介绍 环境介绍 某些环境没有图形化界面,可以使用sql语句查询达梦数据库环境情况 SELECT 实例名称 数据库选项,INSTANCE_NAME 数据库选项相关参数值 FROM V$INSTANCE UNION ALL SELECT 授权用户,(SELECT AUTHORIZED_CUSTOMER FROM V$LICE…

【Linux】从新认识Linux 服务(Service)

文章目录 Linux中service的概念Linux中常见的service常见的服务管理方式Linux中列出serviceLinux中service的特点推荐阅读 Linux中service的概念 在Linux操作系统中&#xff0c;服务&#xff08;Service&#xff09;是一个基本概念&#xff0c;它通常指的是运行在后台的、持续…

(大众金融)SQL server面试题(3)-客户已用额度总和

今天&#xff0c;面试了一家公司&#xff0c;什么也不说先来三道面试题做做&#xff0c;第三题。 那么&#xff0c;我们就开始做题吧&#xff0c;谁叫我们是打工人呢。 题目是这样的&#xff1a; DEALER_INFO经销商授信协议号码经销商名称经销商证件号注册地址员工人数信息维…

Scratch与信息学奥赛的交汇点—C++编程在蓝桥杯青少组题库中的应用

随着信息技术的不断发展&#xff0c;编程教育已经成为了青少年科学素养的重要组成部分。在这个数字化的时代&#xff0c;掌握一门编程语言不仅仅是为了解决实际问题&#xff0c;更是打开智能世界大门的钥匙。今天&#xff0c;6547网就来探讨一下如何通过Scratch入门编程&#x…

基于卡尔曼滤波的平面轨迹优化

文章目录 概要卡尔曼滤波代码主函数代码CMakeLists.txt概要 在进行目标跟踪时,算法实时测量得到的目标平面位置,是具有误差的,连续观测,所形成的轨迹如下图所示,需要对其进行噪声滤除。这篇博客将使用卡尔曼滤波,对轨迹进行优化。 优化的结果为黄色线。 卡尔曼滤波代码…

解决Windows系统本地端口被占用

目录 一、被程序占用端口 1.通过终端杀掉占用端口的进程 2.任务管理器 二、被系统列为保留端口 前言&#xff1a; 首先了解为什么会出现端口被占用的情况 端口被占用的情况可能出现的原因有很多&#xff0c;主要有以下几点&#xff1a; 1.多个应用程序同时启动&…

架构篇25:高可用存储架构-双机架构

文章目录 主备复制主从复制双机切换主主复制小结存储高可用方案的本质都是通过将数据复制到多个存储设备,通过数据冗余的方式来实现高可用,其复杂性主要体现在如何应对复制延迟和中断导致的数据不一致问题。因此,对任何一个高可用存储方案,我们需要从以下几个方面去进行思考…

Database history tablesupgraded

zabbix升级到6之后&#xff0c;配置安装完成会有一个红色输出&#xff0c;但是不影响zabbix使用&#xff0c;出于强迫症&#xff0c;找到了该问题的解决方法。 Database history tables upgraded: No. Support for the old numeric type is deprecated. Please upgrade to nume…

[SUCTF 2019]CheckIn1

黑名单过滤后缀’ph&#xff0c;并且白名单image类型要有对应文件头 对<?过滤&#xff0c;改用GIF89a<script languagephp>eval($_POST[cmd]);</script>&#xff0c;成功把getshell.gif上传上去了 尝试用.htaccess将上传的gif当作php解析&#xff0c;但是失败…

idea中使用带provide修饰的依赖,导致ClassNotFound

1、provide修饰的依赖作用&#xff1a; 编译时起作用&#xff0c;而运行及打包时不起作用。程序打包到Linux上运行时&#xff0c;若Linux上也有这些依赖&#xff0c;为了在Linux上运行时避免依赖冲突&#xff0c;可以使用provide修饰&#xff0c;使依赖不打包进入jar中 2、可能…

《WebKit 技术内幕》学习之十四(1):调式机制

第14章 调试机制 支持调试HTML、CSS和JavaScript代码是浏览器或者渲染引擎需要提供的一项非常重要的功能&#xff0c;这里包括两种调试类型&#xff1a;其一是功能&#xff0c;其二是性能。功能调试能够帮助HTML开发者使用单步调试等技术来查找代码中的问题&#xff0c;性能调…

2. MySQL 数据库

重点&#xff1a; MySQL 的 三种安装方式&#xff1a;包安装&#xff0c;二进制安装&#xff0c;源码编译安装。 MySQL 的 基本使用 MySQL 多实例 DDLcreate alter drop DML insert update delete DQL select 2.5&#xff09;通用 二进制格式安装 MySQL 2.5.1&#xff…

如何进行H.265视频播放器EasyPlayer.js的中性化设置?

H5无插件流媒体播放器EasyPlayer属于一款高效、精炼、稳定且免费的流媒体播放器&#xff0c;可支持多种流媒体协议播放&#xff0c;可支持H.264与H.265编码格式&#xff0c;性能稳定、播放流畅&#xff0c;能支持WebSocket-FLV、HTTP-FLV&#xff0c;HLS&#xff08;m3u8&#…

eNSP 实验 两台AR配置同网段

实验1&#xff1a;eNSP 两台AR配置同网段 目的&#xff1a;创建两台AR&#xff0c;配置IP互相ping通 拓扑结构&#xff1a; 首先创建一个AR3260 然后创建一个AR2220 然后同轴电缆连接一下 先配置AR2220。 1、切管理员&#xff1a;system-view 进入千兆位以太网 0/0/0 interf…

基于静态顺序表实现通讯录

目录 一、设计框架 1、功能要求​ 2、菜单函数的实现 二、头文件实现​ Contact.h SeqList.h 三、Test.h 四、通讯录的初始化和销毁 五、增加通讯录 六、在通讯录中查找姓名下标 七、删除通讯录 八、显示通讯录 九、查找通讯录 一、设计框架 test.c&#xff1a;通…