您当前的位置:首页 > 电脑百科 > 软件技术 > 操作系统 > linux

Linux驱动基础篇:LED驱动

时间:2021-03-05 09:53:29  来源:今日头条  作者:嵌入式大杂烩

前言

上一篇我们分享了字符设备驱动框架linux驱动基础篇:hello驱动 ,当时分享的是hello驱动程序。学STM32我们从点灯开始,学Linux驱动我们自然也要点个灯来玩玩,尽量在从这些基础例程中榨取知识,细抠、细抠,为之后更复杂的知识打好基础。

与硬件无关的LED驱动

回顾hello驱动程序,我们的根据实际需求对其进行写字符串与读字符串操作。这里我们当然也要根据实际来思考我们的LED驱动程序。在STM32点灯的时候,一般输出低电平点灯,输出高电平灭灯。在嵌入Linux操作系统的情况下,我们自然也要想到有个写1/0的思想。类比我们上一篇的hello程序:

Linux驱动基础篇:LED驱动

 

我们的LED程序自然要写入的数据为0/1来点亮、熄灭LED。这里我们做的实验室与硬件无关的LED实验:我们的驱动程序在收到应用程序发送过来的0时打印led on、收到1时打印led off。模仿上一篇的hello程序,我们修改得到的与硬件无关的LED程序(核心部分)如下:

LED应用程序:

Linux驱动基础篇:LED驱动

 

LED驱动程序:

Linux驱动基础篇:LED驱动

 


Linux驱动基础篇:LED驱动

 

加载led驱动模块及运行应用程序:

Linux驱动基础篇:LED驱动

 

与硬件有关的LED驱动

上面那一节分享的是与硬件无关的LED驱动实验,主要是为了理清LED驱动的大体思路。这里我们再加入与硬件有关的相关操作以构造与硬件有关的LED驱动程序。

我们在进行STM32的裸机编程的时候,对一些外设进行配置其实就是操作一些地址的过程,这些外设地址在芯片手册中可以看到:

Linux驱动基础篇:LED驱动

 

这是地址映射图,这里图中只是列出的外设的边界地址,每个外设又有很多寄存器,这些寄存器的地址都是对外设基地址进行偏移得到的。同样的,对于NXP的IMX6ULL芯片来说,也是有类似这样的地址的:

Linux驱动基础篇:LED驱动

 

此时我们要编写Linux系统下的led驱动,涉及到硬件操作的地方操作的并不是这些地址(物理地址),而是操作系统给我们提供的地址(虚拟地址)。操作系统根据物理地址来给我们生成一个虚拟地址,我们的led驱动操控这个地址就是间接的操控物理地址。至于这两个地址是怎么联系起来的,里面个原理我们暂且不展开。我们从函数层面来看,内核给我们提供了ioremap 函数,这个函数可以把物理地址映射为虚拟地址。这个函数在内核文件arch/arm/include/asm/io.h 中:

void __iomem *ioremap(resource_size_t res_cookie, size_t size);
  • res_cookie:要映射给的物理起始地址 。
  • size:要映射的内存空间大小。
  • 返回值: 指向映射后的虚拟空间首地址。

与ioremap函数相对应的函数为:

void iounmap (volatile void __iomem *addr)
  • addr:要取消映射的虚拟地址空间首地址。

地址映射完成之后,我们可以直接通过指针来访问虚拟地址,如:

*GPIO5_DR &= ~(1 << 3);  /* GPIO5_IO03输出低电平 */
*GPIO5_DR |= (1 << 3);   /* GPIO5_IO03输出高电平 */

这里简单介绍一下i.MX 6ULL的GPIO。对于i.MX 6ULL来说,以数字来给IO端口(组别)命令,GPIO5为第五组,所以GPIO5_IO03为第五组端口的第3个引脚。而STM32中是以大写字母来表示端口(组别),如PA3表示A端口的第3个引脚。

i.MX 6ULL有 5 组 GPIO(GPIO1~ GPIO5),每组引脚最多有 32 个:

GPIO1 有 32 个引脚: GPIO1_IO0~GPIO1_IO31;
GPIO2 有 22 个引脚: GPIO2_IO0~GPIO2_IO21;
GPIO3 有 29 个引脚: GPIO3_IO0~GPIO3_IO28;
GPIO4 有 29 个引脚: GPIO4_IO0~GPIO4_IO28;
GPIO5 有 12 个引脚: GPIO5_IO0~GPIO5_IO11;

地址映射完成之后,我们不仅可以通过指针来访问虚拟地址,而且还可以使用内核给我们提供的一些读写函数:

/* 写操作函数 */
void writeb(u8 value, volatile void __iomem *addr);
void writew(u16 value, volatile void __iomem *addr);
void writel(u32 value, volatile void __iomem *addr);
/* 读操作函数 */
u8 readb(const volatile void __iomem *addr);
u16 readw(const volatile void __iomem *addr);
u32 readl(const volatile void __iomem *addr);

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

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

此时我们可以把上一节的led_init函数led_drv_write函数进行修改:

Linux驱动基础篇:LED驱动

 


Linux驱动基础篇:LED驱动

 

与STM32一样,对于i.MX 6ULL的GPIO外设来说,也有很多寄存器:

Linux驱动基础篇:LED驱动

 

上面我们只是点一个灯,如果是要点多个灯呢?那就得操控多个GPIO。如果进行地址映射的写法还像上面那样,代码就会显得很臃肿。回想一下我们STM32,GPIO外设通过结构体来管理它的寄存器:

Linux驱动基础篇:LED驱动

 

这里的__IO是个宏,代表C语言的关键字volatile ,为了防止编译器对我们的一些硬件操作进行优化,从而得不到想要的结果。比如:

/* 假设REG为寄存器的地址 */
uint32 *REG;
*REG = 0;  /* 点灯 */
*REG = 1;  /* 灭灯 */

此时若是REG不加volatile进行修饰,则点灯操作将被优化掉,只执行灭灯操作。

在这里,我们也可以模仿STM32那样子,用一个结构体来对i.MX 6ULL的GPIO的寄存器进行管理,如:

struct GPIO_RegDef
{
  volatile unsigned int DR;
  volatile unsigned int GDIR;
  volatile unsigned int PSR;
  volatile unsigned int ICR1;
  volatile unsigned int ICR2;
  volatile unsigned int IMR;
  volatile unsigned int ISR;
  volatile unsigned int EDGE_SEL;
};

结构体里的成员排序是要按照特定顺序来的:

Linux驱动基础篇:LED驱动

 

因为这些寄存器都是相对于GPIO外设的基地址作偏移得到的,比如:

Linux驱动基础篇:LED驱动

 

不能打乱顺序,否则就不能正确访问到对应的寄存器了。用结构体进行管理之后,我们就可以用类似下面的方式进行映射:

struct GPIO_RegDef *GPIO5 = ioremap(0x20AC000, sizeof(struct GPIO_RegDef));

然后就可以向STM32那样来操控GPIO寄存器,如:

GPIO5->DR &= ~(1 << 3);  /* GPIO5_IO03输出低电平 */
GPIO5->DR |= (1 << 3);   /* GPIO5_IO03输出高电平 */

与硬件有关的LED驱动(升级版)

上一节我们分享的LED驱动是一个常规的LED驱动,只能适用于我们当前的开发版,所以是一个专用的LED驱动程序。若是换了另一块板,led所连接的gpio引脚可能不一样了,我们就修改我们的驱动程序led_drv.c里与寄存器相关的操作。有没有更好的办法不用再修改我们的led_drv.c驱动程序了?

若是led_drv.c不用再修改了,那么这个led_drv.c驱动就是一个通用的驱动程序了。具体可查看韦东山老师的《嵌入式Linux应用开发完全手册第2版》第五篇第3~7节进行学习。

下面来简单地梳理一下:

Linux驱动基础篇:LED驱动

 

由于篇幅问题,具体的部分就不贴出来了。

之前的笔记中:C语言、嵌入式重点知识:回调函数 中我也有提到通用与专用的含义,可以了解了解加深对这两个词的认识。

这里我们学到了很重要的思想软件分层的思想及技巧,但也只是点了一下,未来的路还很长,需要持续学习,继续提高。

以上就是本次的分享,如有错误,欢迎指出!谢谢

参考/学习资料:

  • 百问网《嵌入式Linux应用开发完全手册第2版》
  • 正点原子《I.MX6U嵌入式Linux驱动开发指南V1.2》
  • 野火《i.MX Linux开发实战指南》


Tags:Linux驱动   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
前言上一篇我们分享了字符设备驱动框架:Linux驱动基础篇:hello驱动 ,当时分享的是hello驱动程序。学STM32我们从点灯开始,学Linux驱动我们自然也要点个灯来玩玩,尽量在从这些基础...【详细内容】
2021-03-05  Tags: Linux驱动  点击:(178)  评论:(0)  加入收藏
互斥体概述信号量是在并行处理环境中对多个处理器访问某个公共资源进行保护的机制,mutex用于互斥操作。 信号量的count初始化为1,down()/up()也可以实现类似mutex的作用。mute...【详细内容】
2020-09-04  Tags: Linux驱动  点击:(153)  评论:(0)  加入收藏
使用休眠-唤醒的方式等待某个事件发生时,有一个缺点:等待的时间可能很久。我们可以加上一个超时时间,这时就可以使用poll机制。...【详细内容】
2020-09-03  Tags: Linux驱动  点击:(105)  评论:(0)  加入收藏
关注“技术简说”,带你一步一步学习linux内核驱动。在linux操作系统中,一切皆是文件:文件是文件,目录是文件,设备是文件,socket套接字是文件,管道也是文件。linux操作系统用文件抽...【详细内容】
2020-07-16  Tags: Linux驱动  点击:(144)  评论:(0)  加入收藏
Linux系统上每一个设备的运行都有其固定的规则,而且设备种类、型号众多,将每一个设备的固定规则写入内核是不现实的,也是没有意义的。所以每一个设备都会有相对应的驱动,小到一...【详细内容】
2020-03-20  Tags: Linux驱动  点击:(69)  评论:(0)  加入收藏
▌简易百科推荐
作用显示文件或目录所占用的磁盘空间使用命令格式du [option] 文件/目录命令功能显示文件或目录所占用的磁盘空间一些写法的区别du -sh xxx 显示总目录的大小,但是不会列出...【详细内容】
2021-12-23  mitsuhide1992    Tags:du命令   点击:(12)  评论:(0)  加入收藏
什么是linux内核linux就像是一个哲学的最佳实践。如果非要对它评价,我真的不知道该怎么赞叹,我只能自豪地说着:“linux的美丽简直让人沉醉。”我只能说是我处在linux学习的修炼...【详细内容】
2021-12-23  linux上的码农    Tags:linux内核   点击:(15)  评论:(0)  加入收藏
本文将比较 Linux 中 service 和 systemctl 命令,先分别简单介绍这两个命令的基础用法,然后进行比较。从 CentOS 7.x 开始,CentOS 开始使用 systemd 服务来代替 service服务(dae...【详细内容】
2021-12-23  软件架构    Tags:systemctl   点击:(13)  评论:(0)  加入收藏
mv是move的缩写,可以用来移动文件或者重命名文件名,经常用来备份文件或者目录。命令格式mv [选项] 源文件或者目录 目标文件或者目录命令功能mv命令中第二个参数类型的不同(...【详细内容】
2021-12-17  入门小站    Tags:mv命令   点击:(23)  评论:(0)  加入收藏
大数据技术AI Flink/Spark/Hadoop/数仓,数据分析、面试,源码解读等干货学习资料 98篇原创内容 -->公众号 Linux sed 命令是利用脚本来处理文本文件。sed 可依照脚本的指令来处...【详细内容】
2021-12-17  仙风道骨的宝石骑士    Tags:sed命令   点击:(21)  评论:(0)  加入收藏
Node是个啥?  写个东西还是尽量面面俱到吧,所以有关基本概念的东西我也从网上选择性地拿了下来,有些地方针对自己的理解有所改动,对这些概念性的东西有过了解的可选择跳过这段...【详细内容】
2021-12-15  linux上的码农    Tags:node   点击:(21)  评论:(0)  加入收藏
难道只有我一个人觉得Ubuntu的unity桌面非常好用吗?最近把台式机上面的Ubuntu 16.04格式化了,装了黑苹果用了一周,不得不说,MacOS确实很精美,软件生态比Linux丰富很多,比Windows简...【详细内容】
2021-12-14  地球末日村    Tags:ubuntu   点击:(34)  评论:(0)  加入收藏
简介Netstat 命令用于显示各种网络相关信息,如网络连接,路由表,接口状态 (Interface Statistics),masquerade 连接,多播成员 (Multicast Memberships) 等等。输出信息含义执行net...【详细内容】
2021-12-13  窥镜天    Tags:Linux netstat   点击:(26)  评论:(0)  加入收藏
对于较多数量的文件描述符的监听无论是select还是poll系统调用都显得捉襟见肘,poll每次都需要将所有的文件描述符复制到内核,内核本身不会对这些文件描述符加以保存,这样的设计...【详细内容】
2021-12-13  深度Linux    Tags:Linux   点击:(16)  评论:(0)  加入收藏
今天,我们来了解下 Linux 系统的革命性通用执行引擎-eBPF,之所以聊着玩意,因为它确实牛逼,作为一项底层技术,在现在的云原生生态领域中起着举足轻重的作用。截至目前,业界使用范...【详细内容】
2021-12-10  架构驿站    Tags:eBPF   点击:(24)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条