02_LinuxLED驱动开发

news2025/1/25 9:24:57

目录

Linux下LED灯驱动原理

地址映射

ioremap函数

iounmap函数

I/O内存访问函数

LED灯驱动程序编写

编写测试APP

编译驱动程序

编译测试APP

运行测试


Linux下LED灯驱动原理

Linux下的任何外设驱动,最终都是要配置相应的硬件寄存器。所以LED灯驱动最终也是对I.MX6ULL的IO口进行配置,与裸机实验不同的是,在Linux下编写驱动要符合Linux的驱动框架。IMX6U-ALPHA开发板上的LED连接到I.MX6ULL的GPIO1_IO03这个引脚上,因此实验的重点就是编写Linux下I.MX6UL引脚控制驱动。

地址映射

在编写驱动之前,我们需要先简单了解一下MMU这个神器,MMU全称叫做 MemoryManage Unit,也就是内存管理单元。在老版本的Linux中要求处理器必须有MMU,但是现在Linux内核已经支持无MMU的处理器了。MMU主要完成的功能如下:

1.完成虚拟空间到物理空间的映射。

2.内存保护,设置存储器的访问权限,设置虚拟存储空间的缓冲特性。

我们重点来看一下第1,也就是虚拟空间到物理空间的映射,也叫做地址映射。首先了解两个地址概念:虚拟地址(VA,Virtual Address)、物理地址(PA, Physcical Address)。对于32位的处理器来说,虚拟地址范围是2^32-4GB,我们的开发板上有512MB的DDR3,这512MB的内存就是物理内存,经过MMU可以将其映射到整个4GB的虚拟空间,如图所示:

 物理内存只有512MB,虚拟内存有4GB,那么肯定存在多个虚拟地址映射到同一个物理地,址上去,虚拟地址范围比物理地址范围大的问题处理器自会处理,这里我们不要去深究,因为MMU是很复杂的一个东西

Linux内核启动的时候会初始化MMU,设置好内存映射,设置好以后CPU访问的都是虚拟地址。

比如I.MX6ULL的GPIO1_I003引脚的复用寄存器IOMUXC_SW-MUX_CTL_PAD_GPIO1_IO03的地址为0X020E0068。如果没有开启MMU的话直接向0X020E0068这个寄存器地址写入数据就可以配置GPIO1_I003的复用功能。现在开启了MMU,并且设置了内存映射,因此就不能直接向0X020E0068这个地址写入数据了。我们必须得到0X020E0068这个物理地址在Linux系统里面对应的虚拟地址,这里就涉及到了物理内存和虚拟内存之间的转换,需要用到两个函数: ioremap和iounmap

ioremap函数

ioremap函数用于获取指定物理地址空间对应的虚拟地址空间,定义在arch/arm/include/asm/io.h文件中,定义如下:

 ioremap是个宏,有两个参数: cookie 和 size,真正起作用的是函数_arm_ioremap,此函数有三个参数和一个返回值,这些参数和返回值的含义如下:

phys_addr:要映射的物理起始地址。

size:要映射的内存空间大小。

mtype: ioremap的类型,可以选择MT_DEVICE、MT_DEVICE_NONSHARED、MT DEVICE CACHED和MT DEVICE WC, ioremap函数选择MT DEVICE。

返回值:_iomem类型的指针,指向映射后的虚拟空间首地址。

假如我们要获取I.MX6ULL的IOMUXC_Sw_MUX_CTL_PAD_GPIO1_IO03寄存器对应的虚拟地址,使用如下代码即可:

 宏SW_MUX_GPIO1_1003_BASE是寄存器物理地址, SW_MUX_GPIO1_1O03是映射后的虚拟地址。对于I.MX6ULL来说一个寄存器是4字节(32位)的,因此映射的内存长度为4.映射完成以后直接对SW_MUX_GPIO1_I003进行读写操作即可。

iounmap函数

卸载驱动的时候需要使用iounmap函数释放掉ioremap函数所做的映射, iounmap函数原型如下:

 iounmap只有一个参数 addr,此参数就是要取消映射的虚拟地址空间首地址。假如我们现在要取消掉IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03寄存器的地址映射,使用如下代码即可:

I/O内存访问函数

这里说的I/O是输入/输出的意思,并不是我们学习单片机的时候讲的GPIO引脚。这里涉及到两个概念:I/O端口和I/O内存。当外部寄存器或内存映射到IO空间时,称为I/O端口。当外部寄存器或内存映射到内存空间时,称为I/O内存。但是对于ARM来说没有I/O空间这个概念,因此ARM体系下只有I/O内存(可以直接理解为内存)。使用ioremap函数将寄存器的物理地址映射到虚拟地址以后,我们就可以直接通过指针访问这些地址,但是Linux内核不建议这么做,而是推荐使用一组操作函数来对映射后的内存进行读写操作。

读操作函数

读操作函数有如下几个:

 readb, readw和readl这三个函数分别对应8bit, 16bit和32bit读操作,参数addr就是要读取写内存地址,返回值就是读取到的数据。

写操作函数

写操作函数有如下几个:

 writeb、writew和writel这三个函数分别对应8bit、16bit和32bit写操作,参数value是要写入的数值, addr是要写入的地址。

LED灯驱动程序编写

新建名为“2_led”文件夹,然后在2_led文件夹里面创建VSCode工程,工作区命名为“led”工程创建好以后新建led.c文件,此文件就是led的驱动文件,在led.c里面输入如下内容:

 第22~26行,定义了一些宏,包括主设备号、设备名字、LED 开/关宏。

第29-33行,本实验要用到的寄存器宏定义。

第36~40行,经过内存映射以后的寄存器地址指针。

第47-59行, led_switch函数,用于控制开发板上的LED灯亮灭,当参数sta为LEDON(1)的时候打开LED灯, sta为LEDOFF(0)的时候关闭LED灯。

第 68-71 行,led_open 函数,为空函数,可以自行在此函数中添加相关内容,一般在此函数中将设备结构体作为参数filp的私有数据(filp->private_data)。

第81~84行,led_read函数,为空函数,如果想在应用程序中读取LED的状态,那么就可以在此函数中添加相应的代码,比如读取GPIO1_DR 寄存器的值,然后返回给应用程序。

第94~114行,led_write函数,实现对LED灯的开关操作,当应用程序调用write函数向led 设备写数据的时候此函数就会执行。首先通过函数copy_from_user获取应用程序发送过来的操作信息(打开还是关闭LED),最后根据应用程序的操作信息来打开或关闭LED灯。

第121-124行, led_release函数,为空函数,可以自行在此函数中添加相关内容,一般关·闭设备的时候会释放掉led_open函数中添加的私有数据。

第127-133行,设备文件操作结构体led_fops的定义和初始化。

第140-185行,驱动入口函数led_init,此函数实现了LED的初始化工作, 147~151行通过ioremap函数获取物理寄存器地址映射后的虚拟地址,得到寄存器对应的虚拟地址以后就可以完成相关初始化工作了。比如使能GPIO1时钟、设置GPIO1_I003复用功能、配置GPIO1_IO03的属性等等。最后,最重要的一步!使用register_chrdev函数注册led这个字符设备。

第192-202行,驱动出口函数led_exit,首先使用函数iounmap取消内存映射,最后使用函数unregister_chrdev注销led这个字符设备。

第205~206行,使用module_init和module_exit这两个函数指定led设备驱动加载和卸载函数。

第207~208行,添加LICENSE和作者信息。

第22~26行,定义了一些宏,包括主设备号、设备名字、LED 开/关宏。

第29-33行,本实验要用到的寄存器宏定义。

第36~40行,经过内存映射以后的寄存器地址指针。

第47-59行, led_switch函数,用于控制开发板上的LED灯亮灭,当参数sta为LEDON(1)的时候打开LED灯, sta为LEDOFF(0)的时候关闭LED灯。

第 68-71 行,led_open 函数,为空函数,可以自行在此函数中添加相关内容,一般在此函数中将设备结构体作为参数filp的私有数据(filp->private_data)。

第81~84行,led_read函数,为空函数,如果想在应用程序中读取LED的状态,那么就可以在此函数中添加相应的代码,比如读取GPIO1_DR 寄存器的值,然后返回给应用程序。

第94~114行,led_write函数,实现对LED灯的开关操作,当应用程序调用write函数向led 设备写数据的时候此函数就会执行。首先通过函数copy_from_user获取应用程序发送过来的操作信息(打开还是关闭LED),最后根据应用程序的操作信息来打开或关闭LED灯。

第121-124行, led_release函数,为空函数,可以自行在此函数中添加相关内容,一般关·闭设备的时候会释放掉led_open函数中添加的私有数据。

第127-133行,设备文件操作结构体led_fops的定义和初始化。

第140-185行,驱动入口函数led_init,此函数实现了LED的初始化工作, 147~151行通过ioremap函数获取物理寄存器地址映射后的虚拟地址,得到寄存器对应的虚拟地址以后就可以完成相关初始化工作了。比如使能GPIO1时钟、设置GPIO1_I003复用功能、配置GPIO1_IO03的属性等等。最后,最重要的一步!使用register_chrdev函数注册led这个字符设备。

第192-202行,驱动出口函数led_exit,首先使用函数iounmap取消内存映射,最后使用函数unregister_chrdev注销led这个字符设备。

第205~206行,使用module_init和module_exit这两个函数指定led设备驱动加载和卸载函数。

第207~208行,添加LICENSE和作者信息。

编写测试APP

编写测试APP, led驱动加载成功以后手动创建/dev/led节点,应用APP通过操作/dev/led文件来完成对LED设备的控制。向/dev/led 文件写0表示关闭LED灯,1表示打开LED灯。新建ledApp.c文件,在里面输入如下内容:

 

编译驱动程序

编写Makefile文件,本实验的Makefile文件上一篇基本一样,只是将obj-m变量的值改为led.o, Makefile内容如下所示:

第4行,设置obj-m变量的值为led.o。

输入如下命令编译出驱动模块文件:

 

编译测试APP

输入如下命令编译测试ledApp.c这个测试程序:

 编译成功以后就会生成ledApp这个应用程序。

运行测试

 驱动加载成功以后创建“/dev/led”设备节点,命令如下:

 驱动节点创建成功以后就可以使用ledApp软件来测试驱动是否工作正常,输入如下命令打开 LED灯:

 输入上述命令以后观察I.MX6U-ALPHA开发板上的红色LED灯是否点亮,如果点亮的话说明驱动工作正常。在输入如下命令关闭LED灯:

 输入上述命令以后观察I.MX6U-ALPHA开发板上的红色LED灯是否熄灭,如果熄灭的话说明我们编写的LED驱动工作完全正常!至此,我们成功编写了第一个真正的Linux驱动设备程序。

如果要卸载驱动的话输入如下命令即可:

 

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

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

相关文章

前端面试题---事件循环机制和异步编程

一.JavaScript 中的事件循环(Event Loop)机制。 JavaScript 中的事件循环(Event Loop)是一种用于处理异步操作和事件的机制,它确保代码按照正确的顺序执行,并防止阻塞和死锁。 事件循环的工作原理如下&am…

2.Java概述|Java学习笔记

文章目录 Java的诞生Java重要特点JDK、JRE、JVMJava开发注意事项和细节说明注释Java 中的注释类型 Java的诞生 https://www.oracle.com/java/technologies/java-se-support-roadmap.html Java 技术体系平台: Java重要特点 Java 语言是面向对象的(oop)Java 语言…

Dcloud开发者注册,uniCloud服务空间创建。

一、注册dcloud开发者 1、登录dcloud官网,注册开发者账号,官网地址:https://www.dcloud.io/ 二、创建uniCloud服务空间 1、注册完成开发者后,点击开发者后台,点击uniCloud,进入uniCloud服务空间列表&…

shell脚本入门-多命令处理与变量

目录 1.多命令处理2.Shell变量Shell变量的介绍2.1 系统环境变量2.2自定义变量自定义局部变量查询变量值语法变量删除 自定义全局变量父子Shell环境介绍 2.3 自定义常量 1.多命令处理 多命令处理的介绍: 就是在Shell脚本文件中编写多个Shell命令 我们现在通过一些需…

自然语言处理实战10-文本处理过程与输入bert模型后的变化

大家好,我是微学AI,今天给大家介绍一下自然语言处理实战10-文本处理过程与输入bert模型后的变化,通过一段文本看看他的整个变化过程,经过怎样得变化才能输入到模型,输入到模型后文本又经过怎样的计算得到最后的结果。看…

逆向工程的未来在哪里?掌握逆向技能,开创新的职业道路!

前言 随着移动互联网的兴起,“APP”成了99%的互联网企业主要运营的产品,知名的例如“支付宝”、“美团”、“滴滴”、“抖音”等。用户基数的不断变大,安全性也经历着巨大的挑战。 app越来越多,也离不开我们的生活,而…

团队管理之性能实施团队日志10

在这一周中基本上遇到了性能实施过程中应该遇得到的复杂的问题。 像堆外内存引发OOM Killer,C coredump,负载该均衡不均衡,主机资源不够用,数据引发TPS抖动,IO引发TPS抖动之类的。 在这个项目中几乎碰到了我之前遇到…

2023Fiddler抓包学习笔记 -- 环境配置及工具栏介绍

一、Fiddler介绍 Fiddler是位于客户端和服务器端的HTTP代理,常用来抓http数据包,可以监控浏览器所有的http和https流量,查看分析请求数据包和响应数据包,伪造请求和响应等功能。 二、下载安装 1、下载地址 https://www.teleri…

企业——缺省路由

缺省路由是目的地址和掩码全为0的特殊路由 如果报文的目的地址无法匹配路由表中的任何一项,路由器将选择依照缺省路由来转发报文。 ip route-static 0.0.0.0 0.0.0.0 实验要求: 1、按照图中的要求配置IP 2、要求使用静态协议缺省实现访问2.0、3.0、…

linux时间同步,ntpd、ntpdate

linux时间同步,ntpd、ntpdate 一.Linux系统时间的设置二.Linux硬件时间的设置三.系统时间和硬件时间的同步四.不同机器之间的时间同步(重点)五.ntpd服务的设置六.ntp服务的启动与观察七.具体实践:7.1 NTP的配置7.1.1 配置/etc/ntp.conf7.1.2配置/etc/ntp…

VHDL语法

VHDL完整的、可综合的程序结构,必须包含实体和结构体两个最基本的语言结构。 具体取名由设计者自定,由于实体名实际上表达的是该设计电路的器件名,所以最好根据相应电路的功能来确定, 标识符命名规则: (1)标识符主要由字母、数字…

2023年中职组“网络安全”赛项南昌市竞赛任务书

2023年中职组“网络安全”赛项 南昌市竞赛任务书 网络空间安全赛项规程 一、赛项名称 赛项名称:网络空间安全 赛项组别:中职组 二、竞赛目的 通过竞赛,检验参赛选手对网络、服务器系统等网络空间中各个信息系统的安全防护能力&#xff0…

重用Playbook

文章目录 重用Playbookinclude语句如何写role默认变量和普通变量的区别tasks/main.yaml 如何使用变量、静态文件和模板使用x/*/main.yaml中的变量使用x/*/other_but_main.yaml中的资源 role的依赖 重用Playbook Ansible支持的两种重用机制是Roles和Includes。 Roles是一种可重…

【RuoYi-Cloud-Plus】学习笔记 06 - Sentinel(一)关于 StatisticSlot 以及 LeapArray

文章目录 前言参考目录学习笔记1、Sentinel 简介2、Sentinel 架构图3、Sentinel 源码学习3.1、包结构3.2、 LeapArray (滑动窗口算法的实现)3.3、StatisticSlot3.3.1、StatisticSlot#entry3.3.2、StatisticSlot#exit3.4、StatisticNode、StatisticSlot、…

Linux——基础网络设置

个人简介:云计算网络运维专业人员,了解运维知识,掌握TCP/IP协议,每天分享网络运维知识与技能。座右铭:海不辞水,故能成其大;山不辞石,故能成其高。 个人主页:小李会科技的…

几个事件的问题

1.PC端的click是点击事件,移动端的lick会存在300ms延迟 移动端的click是单击事件,单击事件:第一次点击后,监测300ms, 看是否有第二次点击操作,如果有就是单击,如果有就是双击。 如何解决: 单手指事件模型…

Linux 操作系统原理 — tc 流量控制技术解析

目录 文章目录 目录Traffic ControlTraffic Control 的基本实现原理流量处理的三个层面流量处理的关键流程流量队列的类型FIFO 队列PFIFO_FAST 队列SFQ 队列令牌桶队列 Kernel Traffic Control 的工作原理Qdisc(队列描述)Class(分类&#xff…

电子元器件解析之电容(二)——电容分类与应用场景

书接上文:电子元器件解析之电容(一)——定义与性能参数:https://blog.csdn.net/weixin_42837669/article/details/131142286 摘要 本文总结了各种不同介质电容的特性,包括陶瓷电容、电解电容、薄膜电容等;同时对一些特殊场合的电容…

Transformer Block运算量

参考:Swin Transformer论文精读【论文精读】_哔哩哔哩_bilibili 在看朱毅老师讲解Swin Transformer论文时,里面有一个Transformer Block的计算复杂度的推导计算,感觉清晰明了,这里做一下记录,先说一下结果,…

Android:Handler

参考来源 参考来源 参考来源 参考来源 Handler机制(面试版) Binder/Socket用于进程间通信,而Handler消息机制用于同进程的线程间通信 handler机制是android系统运行的基础,它采用生产者,消费者模式进行设计。其中生产…