嵌入式Linux驱动开发之点灯大法

💡 原文中文,约13600字,阅读约需33分钟。
📝

内容提要

本文介绍了使用驱动开发点亮LED灯的方法,包括查看原理图、确定引脚连接和编程读写GPIO等。

🔎

延伸解读

从裸机到驱动:点灯背后的思维转变

裸机点灯直接操作物理地址,而Linux驱动点灯需经过虚拟地址映射、内核模块加载、设备节点创建等步骤。这种转变的核心在于Linux引入了MMU和虚拟内存管理,CPU只能访问虚拟地址,因此必须用ioremap将物理寄存器地址映射为虚拟地址,再通过readl/writel等I/O内存访问函数操作。这增加了复杂度,但也带来了内存保护和多任务隔离的好处。

GPIO配置的关键步骤与易错点

使用IMX6UL的GPIO前,必须依次完成:使能对应GPIO时钟(CCM_CCGRx)、配置引脚复用为GPIO模式(IOMUXC_SW_MUX)、设置引脚电气属性(IOMUXC_SW_PAD)、设置方向寄存器(GPIOx_GDIR)。其中时钟使能常被忽略,导致寄存器读写无效;复用配置错误则引脚无法输出。此外,操作数据寄存器(GPIOx_DR)时需用读-改-写方式,避免影响同组其他引脚。

驱动开发中的地址映射与volatile必要性

Linux内核启动后开启MMU,CPU访问的都是虚拟地址,因此驱动中不能直接操作物理地址。需用ioremap获取虚拟地址,卸载时用iounmap释放。ioremap按页映射,即使只映射4字节,实际也会映射4096字节。另外,指向寄存器的指针必须加volatile修饰,防止编译器优化掉连续的写操作(如先写0再写1),否则可能导致点灯失败。

测试与部署:NFS挂载和模块操作

驱动编译为.ko模块后,可通过NFS将Ubuntu共享目录挂载到开发板/mnt,实现文件同步。加载模块用insmod,创建设备节点用mknod(需指定主设备号和次设备号),测试程序通过open/write操作设备节点控制LED。卸载模块用rmmod,并确认lsmod中已无该模块。这套流程是后续字符设备驱动的通用模板。

❓

Q&A

IMX6UL的GPIO模块内部有哪些主要寄存器?各自的作用是什么?

GPIO模块内部有三个主要寄存器:GPIOx_GDIR用于设置引脚方向,每位对应一个引脚,1表示输出,0表示输入;GPIOx_DR用于设置输出引脚的电平,1为高电平,0为低电平;GPIOx_PSR用于读取引脚的电平,1为高电平,0为低电平。

在Linux驱动中,如何将GPIO的物理地址映射到虚拟地址?

使用ioremap函数将物理地址映射到虚拟地址,例如:IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4); 映射后通过readl/writel等函数读写虚拟地址。卸载驱动时使用iounmap取消映射。

编写一个点亮LED的字符设备驱动,主要步骤有哪些?

主要步骤包括:1. 确定主设备号;2. 定义file_operations结构体;3. 实现open、read、write、release等函数;4. 使用register_chrdev注册驱动;5. 编写入口函数(module_init)和出口函数(module_exit);6. 使用class_create和device_create自动创建设备节点。

为什么在操作GPIO寄存器时需要使用volatile关键字?

因为编译器可能会对代码进行优化,例如将连续的寄存器写操作优化掉,导致无法正确控制硬件。使用volatile关键字可以告诉编译器该变量是易变的,不要进行优化,确保每次访问都直接读写内存。

如何通过NFS将编译好的驱动和测试程序上传到开发板并测试LED?

在开发板上使用mount命令挂载Ubuntu的NFS共享目录,例如:mount -t nfs -o nolock,vers=3 192.168.10.100:/home/zhiguoxin/myproject/alientek_drv_development_source /mnt。然后进入/mnt目录,使用insmod加载驱动模块,mknod创建设备节点,运行测试程序如./ledApp /dev/led 1打开LED,./ledApp /dev/led 0关闭LED。

IMX6UL的GPIO在使用前需要配置哪些内容?

使用GPIO前需要配置:1. 使能GPIO时钟(通过CCM_CCGRx寄存器);2. 设置IOMUXC将引脚复用为GPIO功能;3. 配置引脚属性如速度、上下拉电阻、驱动能力等(通过IOMUXC_SW_PAD);4. 设置GPIOx_GDIR确定输入/输出方向。

🏷️

标签

➡️

继续阅读