前言

上一篇我们分享了字符设备驱动框架:【Linux笔记】驱动基础篇,当时分享的是hello驱动程序。

学STM32我们从点灯开始,学Linux驱动我们自然也要点个灯来玩玩,尽量在从这些基础例程中榨取知识,细抠、细抠,为之后更复杂的知识打好基础。

与硬件无关的LED驱动

回顾hello驱动程序,我们的根据实际需求对其进行写字符串与读字符串操作。这里我们当然也要根据实际来思考我们的LED驱动程序。

在STM32点灯的时候,一般输出低电平点灯,输出高电平灭灯。在嵌入Linux操作系统的情况下,我们自然也要想到有个写1/0的思想。

类比我们上一篇的hello程序:

我们的LED程序自然要写入的数据为0/1来点亮、熄灭LED。

这里我们做的实验室与硬件无关的LED实验:我们的驱动程序在收到应用程序发送过来的0时打印led on、收到1时打印led off

模仿上一篇的hello程序,我们修改得到的与硬件无关的LED程序(核心部分)如下:

LED应用程序:

LED驱动程序:

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

与硬件有关的LED驱动

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

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

这是地址映射图,这里图中只是列出的外设的边界地址,每个外设又有很多寄存器,这些寄存器的地址都是对外设基地址进行偏移得到的。

同样的,对于NXP的IMX6ULL芯片来说,也是有类似这样的地址的:

此时我们要编写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函数进行修改:

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

上面我们只是点一个灯,如果是要点多个灯呢?那就得操控多个GPIO。如果进行地址映射的写法还像上面那样,代码就会显得很臃肿。

回想一下我们STM32,GPIO外设通过结构体来管理它的寄存器:

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

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

此时若是REG不加volatile进行修饰,则点灯操作将被优化掉,只执行灭灯操作。关于volatile关键字更多的解释可以查看往期笔记:《来看一看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;
};

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

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

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

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节进行学习

下面来简单地梳理一下:

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

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

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

最后

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

原创不易,如果觉得文章不错,转发、在看,也是我们继续更新得动力。

参考/学习资料:

  • 百问网《嵌入式Linux应用开发完全手册第2版》

  • 正点原子《I.MX6U嵌入式Linux驱动开发指南V1.2》

  • 野火《i.MX Linux开发实战指南》

回复「 篮球的大肚子」进入技术群聊

回复「1024」获取1000G学习资料

【Linux笔记】LED驱动程序相关推荐

  1. 嵌入式linux/鸿蒙开发板(IMX6ULL)开发(二十七)具体单板的LED驱动程序

    文章目录 1.1 怎么写LED驱动程序? 1.2 百问网IMX6ULL的LED驱动程序 1.2.1 led原理图 1.2.2 所涉及的寄存器操作 1.2.3 写程序 1.2.4 上机实验 1.2.5 ...

  2. 友善之臂 mini2440 linux led 驱动代码,mini2440 led驱动程序

    这个led驱动程序只在linux-2.6.32.2内核中测试通过,至于其他的内核可能头文件有一些改动就不能 编译成功了.下面给出源程序: 这是友善之臂提供的源码: #include #include ...

  3. linux-2.6.32在mini2440开发板上移植(16)之LED 驱动程序移植

    LED 驱动程序移植 编者:对于led的驱动程序,很多文章都有详细的介绍,我的博客里面有一篇专门详解这个的.需要看的,可以找下.led灯的驱动其实就代表了I/O口的驱动.在linux系统下,操作一个I ...

  4. Linux NAND FLASH驱动程序分析(mini2440)

    Linux NAND FLASH驱动程序分析(mini2440) 一.Linux-MTD Subsystem介绍 FLASH在嵌入式系统中是必不可少的,它是bootloader.linux内核和文件系 ...

  5. OS和Linux笔记

    OS和Linux笔记 操作系统 基本概念 进程管理 进程和线程 协程 同步互斥 死锁 CAS技术 IPC 线程间通信 内存管理 Linux 基础知识 守护进程 系统监测 编译调试 文件管理 零拷贝技术 ...

  6. 嵌入式I.MX6ULL裸机开发学习(一)汇编LED驱动程序

    一.学习之路的开始 购买了I.MX6ULL mini开发板进行学习,开发环境为Linux,上学期的Linux课程中,我已将电脑配置好Ubuntu,熟悉了Linux基本操作.之前并没有学过怎么在物理机与 ...

  7. Linux驱动——LED驱动的编写与实验

    文章目录 1. LED驱动的原理分析 1.1 地址映射 1.1.1 ioremap函数 1.1.2 iounmap函数 1.2 I/O内存访问函数 1.3 读操作函数 1.4写操作函数 2. 硬件原理 ...

  8. i.MX283开发板第一个Linux驱动-LED驱动

    字符设备驱动开发 字符设备是 Linux 驱动中最基本的一类设备驱动,字符设备就是一个一个字节,按照字节流进行读写操作的设备,读写数据是分先后顺序的.比如我们最常见的点灯.按键.IIC.SPI,LCD ...

  9. linux内核led驱动开发,从Linux内核LED驱动来理解字符设备驱动开发流程

    目录 博客说明 开发环境 1. Linux字符设备驱动的组成 1.1 字符设备驱动模块加载与卸载函数 1.2 字符设备驱动的file_operations 结构体中的成员函数 2. 字符设备驱动--设 ...

最新文章

  1. Jquery获取select,dropdownlist,checkbox下拉列表框的值
  2. hbase报错:org.apache.hadoop.hbase.PleaseHoldException: Master is initializing
  3. HTTP请求报文和响应报文中的实体数据
  4. 2017计算机三级试卷,2017年计算机三级《网络技术》测试题及答案
  5. elfutils cc1: all warnings being treated as errors
  6. java中常用的坑_Java技术开发中的坑
  7. 规模比互联网大 30 倍的物联网,入门太难了!
  8. python之高性能网络编程并发框架eventlet实例
  9. 原生mysql 怎么创表_Mysql的基础使用之SQL原生语句的使用:表的 创建 删除 修改 (一)...
  10. ASP.NET 订餐系统-程序+配置文档
  11. java 进度条实现原理_java进度条功能的实现原理是什么?实例展示
  12. 服务器数字显示器,数字显示器
  13. AndroidStudio高级计算器三角函数对数
  14. 致CSDN读者的一些话:感恩这十年的陪伴,不负遇见,短暂消失
  15. 李时珍食物疗法150条 百年难遇的养生千古名方
  16. echars、象棋、飞机大战、五子棋
  17. AssertionError: View function mapping is overwriting an existing endpoint function: inner
  18. 一本通 1335:【例2-4】连通块
  19. 2542: 弟弟的作业
  20. Nodejs+Express项目使用JWT

热门文章

  1. python-main
  2. iOS开发那些事--创建基于故事板的iOS 6的HelloWorld
  3. 多域资源整合之基础准备--DNS配置
  4. java 程序中打开文件和文件夹
  5. forward和redirect区别
  6. 如何选择面向对象语言
  7. java利用子类求正方形_Java程序设计实验2011
  8. 用计算机怎样给文章分解,计算机试卷6分解..pdf
  9. 如何理解操作系统的不确定性_温度最低-273度,最高却能有1.4亿亿亿亿度,如何定义的?...
  10. 【机器学习】 关联规则Apriori和mlxtend——推荐算法