Linux驱动基础篇:LED驱动

前言上一篇我们分享了字符设备驱动框架: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);


推荐阅读