嵌入式Linux驱动开发pinctrl篇(1)——从寄存器到子系统:驱动演进之路

news2026/5/20 9:30:10
嵌入式Linux驱动开发pinctrl篇1——从寄存器到子系统驱动演进之路仓库已经开源所有教程主线内核移植跑新版本imx-linux/uboot都在这里或者一起来尝试跑7.0的Linux欢迎各位大佬观摩喜欢的话点个⭐仓库地址https://github.com/Awesome-Embedded-Learning-Studio/imx-forge静态网页https://awesome-embedded-learning-studio.github.io/imx-forge/前言我们为什么要折腾这个说实话在上一章我们通过ioremap()writel()的方式点亮 LED 的时候我真的感觉很有成就感。你看一行writel(val, GPIO1_DR)就能让 LED 亮起来这种直接操控硬件的感觉真的很爽。但后来我发现一个问题这种方式在简单的 demo 里确实没问题可一旦项目复杂起来代码会变得非常难以维护。你想想如果你的驱动里到处都是writel(0x0209C000, ...)这样的硬编码地址过两个月再回头看你自己都得翻手册才能搞清楚这行代码在干什么。更糟糕的是这种直接操作寄存器的方式有个致命问题它把硬件细节和驱动逻辑混在一起了。如果你明天换了块板子GPIO 引脚重新分配了整个驱动代码得大改。这就像是你家里装修电工把所有电线都裸露在外面每次想改动个开关位置都得重新布线。这时候我就在想有没有一种办法能让驱动开发者不需要知道底层寄存器地址也不需要手动配置引脚复用只要说我要控制 GPIO1_IO03就能用答案就是pinctrl gpio 子系统。从直接操作寄存器说起让我们先回顾一下上一章我们是怎么点灯的。如果你还记得我们做了这么几件事// 1. 映射寄存器地址IMX6U_CCM_CCGR1ioremap(0x020C406C,4);MUX_CTL_GPIO1_IO03ioremap(0x020E0068,4);PAD_CTL_GPIO1_IO03ioremap(0x020E02F4,4);GPIO1_GDIRioremap(0x0209C004,4);GPIO1_DRioremap(0x0209C000,4);// 2. 使能时钟writel(readl(IMX6U_CCM_CCGR1)|(326),IMX6U_CCM_CCGR1);// 3. 配置引脚复用writel(0x5,MUX_CTL_GPIO1_IO03);// 4. 配置电气特性writel(0x10B0,PAD_CTL_GPIO1_IO03);// 5. 设置方向为输出writel(readl(GPIO1_GDIR)|(13),GPIO1_GDIR);// 6. 点灯writel(readl(GPIO1_DR)~(13),GPIO1_DR);这里有什么问题呢让我数一数硬编码的物理地址到处都是。每个寄存器地址都是硬编码的换个芯片就得全部改掉。配置步骤冗长且容易出错。你得记住时钟→复用→电气特性→方向→数据少一步都不行。而且这些步骤的顺序还有讲究搞错了就可能起不来。代码没法复用。每个驱动都得重复这套流程没法共享。没有冲突检测。如果两个驱动都想用同一个引脚没人提醒你最后就是两个驱动打起来。和设备树脱节。我们前面花了那么多功夫学设备树结果驱动里还是用硬编码地址设备树的引脚配置信息根本没用到。说实话这种写法在嵌入式开发的早期确实很常见。那时候芯片简单引脚少驱动也不多这么写还能接受。但现在都 2026 年了我们的系统越来越复杂如果还用这种方式写驱动真的会把自己逼疯。驱动分离与分层的哲学现在让我们来聊聊 Linux 内核是怎么解决这个问题的。核心思想就四个字分离与分层。什么叫分离就是把硬件相关的操作和设备驱动的逻辑分开。硬件相关的操作——配置引脚复用、设置电气特性、使能时钟——这些事情应该由专门的子系统来处理而不是每个驱动都自己写一套。什么叫分层就是在驱动和硬件之间插入一层抽象这层抽象负责屏蔽硬件差异给上层提供统一的接口。你可以把它理解成搬家。传统的方式是你每个房间的东西都自己搬这很累。新的方式是你请了一支专业的搬家队子系统你只需要告诉他们把这台电视搬到新家剩下的打包、运输、拆包、摆放都由他们搞定。你不需要知道搬家车怎么开也不需要知道电视怎么打包你只需要知道我要搬电视这个意图。在我们的 LED 驱动里“搬家队就是 pinctrl 子系统和 gpio 子系统。我们只需要告诉它们我要用 GPIO1_IO03设置为输出”剩下的引脚配置、时钟使能都由它们搞定。这里有个很重要的设计理念驱动不应该知道硬件的细节。驱动只需要知道我要控制哪个 GPIO至于这个 GPIO 的寄存器地址是多少、需要配置哪些位、时钟要不要使能这些都不是驱动该关心的事情。pinctrl gpio 子系统全景图现在让我们来看看这两个子系统是如何协同工作的。我先给你画个全景图有个整体印象┌─────────────────────────────────────────────────────────────────┐ │ 用户空间程序 │ │ open(/dev/AES_LED) │ └─────────────────────────────┬───────────────────────────────────┘ │ 系统调用 ▼ ┌─────────────────────────────────────────────────────────────────┐ │ LED 驱动 (我们的代码) │ │ of_get_named_gpio(led-gpio) │ │ gpio_set_value(gpio, 1) │ └─────────────────────────────┬───────────────────────────────────┘ │ GPIO API ▼ ┌─────────────────────────────────────────────────────────────────┐ │ GPIO 子系统核心层 │ │ (gpiolib.c - 提供统一接口) │ └───────────────────┬─────────────────────────────┬───────────────┘ │ │ ▼ ▼ ┌───────────────────────────────┐ ┌───────────────────────────────┐ │ GPIO 控制器驱动 (mxc) │ │ GPIO 控制器驱动 (其他) │ │ (gpio-mxc.c) │ │ │ └───────────────┬───────────────┘ └───────────────────────────────┘ │ 寄存器操作 ▼ ┌─────────────────────────────────────────────────────────────────┐ │ pinctrl 子系统 │ │ (配置引脚复用和电气特性) │ └─────────────────────────────────────────────────────────────────┘ │ 设备树解析 ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 硬件寄存器 │ │ IOMUXC, GPIO_DR, GPIO_GDIR, CCM_CCGR, ... │ └─────────────────────────────────────────────────────────────────┘从这个图里你可以看到几个关键点我们的驱动只需要调用 GPIO API。像gpio_set_value()这样的函数我们不需要知道底层寄存器在哪也不需要知道怎么操作。GPIO 子系统负责管理所有 GPIO 控制器。无论是 i.MX 的 GPIO 控制器还是其他厂商的都通过统一的接口向上提供能力。pinctrl 子系统负责引脚配置。在我们使用某个 GPIO 之前pinctrl 子系统已经根据设备树配置好了引脚的复用功能和电气特性。设备树是配置的中心。所有的硬件配置信息——引脚复用、电气特性、GPIO 编号——都写在设备树里驱动通过设备树获取这些信息。为什么要用两个子系统你可能会问为什么不能一个子系统搞定非要分成 pinctrl 和 gpio 两个这里有个很重要的设计理念职责分离。pinctrl 子系统负责的事情是这个引脚要配置成什么功能GPIOUARTSPI它的电气特性是什么驱动强度上下拉。gpio 子系统负责的事情是这个 GPIO 引脚的值是 0 还是 1它是输入还是输出。你可以这么理解pinctrl 是装修队进场之前把房间的基础设施搞好gpio 是开关装修好了之后你来控制灯的开关。如果装修没搞好比如引脚没配置成 GPIO 功能你按开关也没用。这种分离的设计还有一个好处可移植性。你的驱动代码只需要调用 gpio 子系统的接口至于底层是什么芯片、引脚怎么配置完全不影响你的代码。换芯片的时候只需要修改设备树和 pinctrl/gpio 控制器驱动你的驱动代码可以完全不动。现在的 LED 驱动是什么样的让我们先看看最终效果有个直观的感受。这是使用子系统之后的 LED 驱动代码硬件抽象层部分// 从设备树获取 GPIO 编号led.gpio_sub_sys_nrof_get_named_gpio(led.device_tree_node,led-gpio,0);// 设置为输出模式初始值为 1gpio_direction_output(led.gpio_sub_sys_nr,1);// 设置 GPIO 值gpio_set_value(led.gpio_sub_sys_nr,0);// 点亮就这么简单没有物理地址没有ioremap没有寄存器操作。我们只需要告诉子系统我要用这个 GPIO剩下的都由子系统搞定。那么子系统是怎么知道这个 GPIO 需要配置什么引脚复用、什么电气特性的呢答案在设备树里iomuxc { pinctrl_aes_led: led_grp { fsl,pins MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0 ; }; }; / { imx_aes_led { compatible imxaes_led; pinctrl-names default; pinctrl-0 pinctrl_aes_led; led-gpio gpio1 3 GPIO_ACTIVE_LOW; status okay; }; };这里有两个关键信息pinctrl-0 pinctrl_aes_led告诉 pinctrl 子系统这个设备需要用哪个引脚配置。led-gpio gpio1 3 GPIO_ACTIVE_LOW告诉 GPIO 子系统这个设备用的是 gpio1 控制器的第 3 号引脚而且它是低电平有效的。子系统会在设备加载的时候自动读取这些配置把引脚配置好。等我们的驱动代码执行的时候引脚已经配置完毕我们可以直接使用了。接下来我们要做什么现在我们对子系统有了整体印象接下来的章节会深入分析每个部分。我们的学习路径是这样的硬件基础先搞清楚 i.MX 6ULL 的引脚复用和 GPIO 模块是怎么工作的这是理解子系统的前提。pinctrl 子系统深入源码分析 pinctrl 子系统是如何工作的它和设备树是如何交互的。gpio 子系统同样深入源码看看 gpio 子系统是如何管理 GPIO 的它的 API 是怎么实现的。设备树配置学习如何在设备树里正确配置 pinctrl 和 gpio。驱动实现编写一个完整的 LED 驱动使用新 API从设备树读取配置控制 LED。编译测试上板验证看看我们的驱动是否能正常工作。内核对比对比主线内核和 imx 内核的差异看看这两个内核在子系统实现上有什么不同。在正式开始之前我需要提醒你一点子系统的源码量很大pinctrl-imx.c 就有两万多行gpio-mxc.c 也有七百多行。我们不可能逐行分析每一段代码那样会迷失在细节里。我们的策略是抓住主线理解核心流程遇到细节再看。另外我会用主线内核third_party/linux_mainline和 imx 内核third_party/linux-imx进行对比让你看看这两个内核在实现上的差异。这对于你以后做内核移植或者驱动兼容会很有帮助。准备好了吗让我们先从硬件基础开始搞清楚我们在操作什么。下一步阅读 02_hardware_foundation.md 了解 i.MX 6ULL 的引脚复用和 GPIO 硬件原理。相关阅读嵌入式Linux嵌入式Linux驱动开发设备树驱动改造——从硬编码到设备树的实战之旅 - 相似度 100%04. OF API 基础与验证——从 DTS 到代码的桥梁 - 相似度 82%嵌入式Linux嵌入式Linux驱动开发板级DTS实操与完整实战演练——从修改设备树到点亮LED的完整闭环 - 相似度 82%

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

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

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…