Pinctrl和GPIO子系统

Pinctrl 子系统负责管理引脚的复用与配置它允许将引脚配置为不同的功能如 GPIOI2CUART 等并设置引脚的电气特性如上拉下拉等
GPIO 子系统 主要负责对通用输入输出引脚的管理它提供 API 来配置引脚为输入或输出并控制引脚的电平状态
层次结构 Pinctrl 子系统位于 GPIO 子系统之上Pinctrl 负责引脚的初步配置和复用而 GPIO 子系统则利用这些配置来实现输入输出操作两者有密切的关系先配置Pinctrl在配置GPIO

  • 引脚配置首先通过 Pinctrl 子系统配置引脚的功能例如将某个引脚设置为 GPIO 功能
  • 状态管理然后GPIO 子系统使用这些配置来管理引脚的电平状态读取或设置引脚电平

Read More

驱动进化之路

1. 面向对象分离硬件资源驱动思想

背景
在早期的嵌入设备和计算机系统中硬件驱动程序通常是直接和硬件紧密耦合的这种方式导致了驱动程序难以维护和扩展因为每种新的硬件都可能需要全新的驱动代码

思想

  • 模块化将硬件相关的代码从系统的其他部分中分离出来形成独立的模块驱动模块
  • 抽象层引入硬件抽象层HAL使得上层软件可以不直接操作硬件而是通过一层抽象的接口来进行
  • 封装使用面向对象的编程思想将硬件的操作封装成类或对象这样可以更容易管理和重用代码

技术细节

  • 使用类的继承和多态来实现不同硬件的抽象和具体实现
  • 提供统一的接口来访问硬件资源降低了对具体硬件的依赖

例子假设有一个LED连接到一个GPIO引脚上

  • 模块化创建一个独立的LED驱动模块而不是将LED操作代码直接嵌入到应用层代码中
  • 抽象层定义一个抽象的LED类包含打开关闭等方法
// LED抽象类
class LED {
public:
    virtual void turnOn() = 0;
    virtual void turnOff() = 0;
};

// GPIO LED 实现
class GPIO_LED : public LED {
private:
    int pin;

public:
    GPIO_LED(int gpioPin) : pin(gpioPin) {}

    void turnOn() override {
        // GPIO设置为高电平
    }

    void turnOff() override {
        // GPIO设置为低电平
    }
};

2. Platform Device 平台总线模型

随着硬件设备的复杂性增加需要一种更系统化的方式来管理设备驱动和硬件资源平台设备总线Platform Bus将所有硬件设备抽象为平台设备Platform Devices平台驱动Platform Drivers这种抽象使得驱动开发者不必关心具体的硬件细节而是通过一套标准的接口来管理设备

通过这种模型驱动开发者可以集中精力在设备的具体操作上而不需要处理复杂的总线协议或硬件初始化流程平台总线为驱动提供了标准的生命周期管理和资源访问方式

思想

  • 平台设备将硬件设备抽象为平台设备Platform Device这些设备通过平台总线Platform Bus连接到系统中
  • 平台驱动相应地驱动程序也被抽象为平台驱动Platform Driver它们通过平台总线匹配到相应的设备

技术细节

  • 在Linux内核中struct platform_devicestruct platform_driver 是关键数据结构
  • 设备和驱动通过设备树或者是硬编码的方式注册到平台总线上
  • 使用总线驱动框架如I2CSPI等来进一步抽象硬件接口
// 定义平台设备的结构
struct platform_device {
    // 设备的名称<span class="bd-box"><h-char class="bd bd-beg"><h-inner>,</h-inner></h-char></span>用于匹配驱动
    const char *name;
    int id;
    bool id_auto;
    struct device dev;
    u32 num_resources;
    // 指向资源数组的指针<span class="bd-box"><h-char class="bd bd-beg"><h-inner>,</h-inner></h-char></span>资源包括内存区域<span class="bd-box"><h-char class="bd bd-beg"><h-inner>、</h-inner></h-char></span>IRQ等
    struct resource *resource;
    /* 其他字段省略 */
};

// 定义平台驱动的结构
struct platform_driver {
    // 探测函数<span class="bd-box"><h-char class="bd bd-beg"><h-inner>,</h-inner></h-char></span>当设备被发现时调用<span class="bd-box"><h-char class="bd bd-beg"><h-inner>,</h-inner></h-char></span>负责初始化设备
    int (*probe)(struct platform_device *);
    // 移除设备时调用<span class="bd-box"><h-char class="bd bd-beg"><h-inner>,</h-inner></h-char></span>用于清理资源
    int (*remove)(struct platform_device *);
    void (*shutdown)(struct platform_device *);
    int (*suspend)(struct platform_device *, pm_message_t state);
    int (*resume)(struct platform_device *);
    // 内嵌的通用设备驱动结构<span class="bd-box"><h-char class="bd bd-beg"><h-inner>,</h-inner></h-char></span>包含了驱动信息如驱动名称<span class="bd-box"><h-char class="bd bd-beg"><h-inner>、</h-inner></h-char></span>所有者等
    struct device_driver driver;
    const struct platform_device_id *id_table;
    /* 其他字段省略 */
};

例子同上LED连接到GPIO

  • 平台设备定义一个平台设备来代表这个LED
static struct platform_device led_device = {
    .name = "led",
    .id = -1,
    .dev = {
        .platform_data = &gpio_pin_number, // 假设这个GPIO引脚号作为平台数据
    },
};
  • 平台驱动编写一个平台驱动来操作这个LED
static struct platform_driver led_driver = {
    .probe      = led_probe,
    .remove     = led_remove,
    .driver     = {
        .name  = "led",
        .owner = THIS_MODULE,
    },
};

static int led_probe(struct platform_device *pdev)
{
    // 获取设备的GPIO引脚信息并初始化LED
    return 0;
}

static int led_remove(struct platform_device *pdev)
{
    // 清理LED相关的资源
    return 0;
}

技术细节

  • 通过平台设备和平台驱动系统可以自动匹配设备和驱动led_probe 函数在匹配成功后初始化LEDled_remove 函数在设备卸载时清理资源

匹配过程

平台设备与驱动的匹配过程大致如下

  1. 设备注册

    • 当内核启动或模块加载时平台设备struct platform_device被注册到平台总线platform_bus
  2. 驱动注册

    • 平台驱动struct platform_driver也需要注册到平台总线上
  3. 设备树解析

    • 如果使用设备树内核会解析设备树文件.dts或.dtb从中获取设备信息并创建相应的平台设备实例
  4. 匹配尝试

    • 内核尝试将注册的驱动与平台总线上的设备进行匹配
    • 匹配过程主要遵循以下顺序
      • 设备树匹配优先尝试使用设备树匹配规则检查设备的compatible属性与驱动的of_match_table是否匹配
      • ID匹配如果设备树匹配失败尝试使用设备ID匹配
      • 名称匹配最后尝试使用驱动和设备的名称匹配
  5. 成功匹配

    • 一旦找到匹配的设备和驱动内核会调用驱动的probe方法probe方法将接收到匹配的struct device实际上是一个struct platform_device驱动可以在这个方法中初始化设备
  6. 资源分配

    • probe方法中驱动可以访问设备的资源如IO内存IRQ等这些资源通常是通过设备树或平台设备注册时提供的
  7. 设备绑定

    • 如果probe方法成功设备将绑定到驱动设备现在可以正常工作
  8. 失败处理

    • 如果匹配失败或probe方法失败内核会记录错误信息驱动将不会绑定到设

3. 设备树Device Tree

背景
随着SoCSystem on Chip的发展硬件配置变得极为复杂和多样化硬编码的方式变得难以维护

思想

  • 解耦硬件描述将硬件的描述从内核代码中分离出来形成独立的设备树描述文件.dts, .dtsi
  • 动态配置设备树提供了一种动态的可配置的方式来描述系统硬件

技术细节

  • 设备树使用一种类似JSON的格式来描述硬件结构包括节点nodes表示设备属性properties表示设备的特性
  • 内核中的设备树编译器dtc将设备树源文件编译成二进制格式DTB内核启动时加载DTB来配置硬件
  • 驱动程序通过设备树中的信息来获取设备的地址IRQ等信息实现即插即用

例子同上一个LED连接到GPIO

  • 设备树描述
/ {
    leds {
        compatible = "gpio-led";
        gpios = <&gpio 17 GPIO_ACTIVE_HIGH>; // 假设LED连接到GPIO17
        label = "led";
    };
};
  • 驱动代码现在驱动可以从设备树中获取LED的配置信息
static int led_probe(struct platform_device *pdev)
{
    struct device_node *node = pdev->dev.of_node;
    int gpio;

    if (of_property_read_u32(node, "gpios", &gpio))
        return -ENODEV;

    // 使用GPIO进行LED操作
    return 0;
}

设备树描述了硬件配置驱动通过解析设备树节点来获取硬件信息如GPIO引脚号这种方式使得硬件配置与内核代码解耦硬件变化只需修改设备树文件即可

进化意义

  • 从面向对象到平台总线模型再到设备树的演变过程中硬件资源的管理变得越来越抽象和灵活每个阶段都使得驱动开发和维护更加高效减少了对具体硬件的依赖增强了系统的可移植性和可维护性通过LED点灯这个简单的例子我们可以看到这种进化带来的实际好处从最初的硬编码到设备树的动态配置使得系统对硬件变化的适应性大大增强
  • 从面向对象到平台总线模型再到设备树硬件资源的管理和配置变得越来越抽象和通用减少了对具体硬件的依赖提升了系统的可移植性和可维护性
  • 设备树的引入大大简化了内核的硬件描述方式使得硬件支持的添加和修改变得更加灵活和高效

这个过程展示了操作系统在面对越来越复杂的硬件环境时不断优化和抽象硬件管理方法的演进过程

常用依赖包管理工具换源方法大全

在国内使用一些依赖包管理工具时由于网络问题下载速度往往比较慢通过切换到国内的镜像源可以显著加快依赖包的下载速度本文详细整理了常用依赖包管理工具切换到国内镜像的方法帮助开发者提高效率


1. npm 切换国内源

国内常用的 npm 镜像源是 淘宝镜像它同步了官方 npm 仓库的内容且下载速度较快

设置淘宝镜像为默认源

npm config set registry https://registry.npmmirror.com

验证是否设置成功

npm config get registry
# 如果输出 https://registry.npmmirror.com<span class="bd-box"><h-char class="bd bd-beg"><h-inner>,</h-inner></h-char></span>说明设置成功<span class="bd-box"><h-char class="bd bd-beg"><h-inner>。</h-inner></h-char></span>

临时使用淘宝镜像

npm install --registry=https://registry.npmmirror.com

2. Yarn 切换国内源

Yarn 同样可以直接使用淘宝镜像源

设置淘宝镜像为默认源

yarn config set registry https://registry.npmmirror.com

验证是否设置成功

yarn config get registry
# 输出 https://registry.npmmirror.com 表示设置成功<span class="bd-box"><h-char class="bd bd-beg"><h-inner>。</h-inner></h-char></span>

临时使用淘宝镜像

yarn add package-name --registry=https://registry.npmmirror.com

3. pip 切换国内源

Python 的包管理工具 pip 可以通过指定国内的镜像源来加速下载推荐使用豆瓣源

临时使用豆瓣源

pip install package-name -i https://pypi.douban.com/simple

永久设置国内镜像

在用户目录下创建或修改 pip 配置文件

  • Windows: C:\Users\你的用户名\pip\pip.ini
  • macOS/Linux: ~/.pip/pip.conf

然后添加以下内容

[global]
index-url = https://pypi.douban.com/simple

4. Conda 切换国内源

Conda 是 Anaconda 的包管理工具设置国内镜像可以通过清华大学的镜像源

临时使用清华源

conda install package-name -c https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/

永久设置清华大学源

conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --set show_channel_urls yes

5. Composer 切换国内源

Composer 是 PHP 的包管理工具可以使用中国全量镜像由 Laravel China 社区维护

设置国内镜像

composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

恢复默认源

composer config -g --unset repos.packagist

6. RubyGems 切换国内源

Ruby 的包管理工具 gem 可以使用淘宝镜像

设置淘宝镜像

gem sources --add https://gems.ruby-china.com/ --remove https://rubygems.org/

确认源是否配置正确

gem sources -l

7. Maven 切换国内源

Maven 是 Java 的包管理工具可以通过修改 settings.xml 文件来使用国内镜像

修改 settings.xml

~/.m2/settings.xml 中添加以下内容

<mirrors>
  <mirror>
    <id>alimaven</id>
    <mirrorOf>central</mirrorOf>
    <name>aliyun maven</name>
    <url>https://maven.aliyun.com/repository/central</url>
    <layout>default</layout>
  </mirror>
</mirrors>

8. Gradle 切换国内源

对于 Android 开发者Gradle 的国内源配置也可以显著加快依赖下载速度

修改 build.gradle 文件

allprojects {
    repositories {
        maven { url 'https://maven.aliyun.com/repository/public' }
        maven { url 'https://maven.aliyun.com/repository/jcenter' }
        maven { url 'https://maven.aliyun.com/repository/google' }
    }
}

9. Node.js 镜像加速

如果你需要安装 Node.js 本身也可以通过淘宝镜像加速

nvm install node --registry=https://registry.npmmirror.com

10. Docker 镜像加速

Docker 在国内下载镜像时可能会遇到速度缓慢的问题可以使用国内的镜像源加速

设置 Docker 国内镜像

/etc/docker/daemon.json 中添加以下内容

{
  "registry-mirrors": [
    "https://registry.docker-cn.com",
    "https://mirror.ccs.tencentyun.com"
  ]
}

然后重启 Docker

sudo systemctl restart docker

11. apt 换源Ubuntu/Debian

apt 是 Ubuntu 和 Debian 系统常用的包管理工具切换到国内镜像可以显著加快软件包的下载速度常用的国内镜像源有 阿里云清华大学中科大 镜像源

步骤 1备份原有的源列表

先备份 /etc/apt/sources.list 文件

sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak

步骤 2编辑 sources.list

使用文本编辑器打开 sources.list 文件

sudo nano /etc/apt/sources.list

步骤 3选择一个国内源替换原有的源

你可以选择以下任意一个国内镜像源来替换原有的源

  • 阿里云镜像
deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse
  • 清华大学镜像
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-updates main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-backports main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-security main restricted universe multiverse
  • 中科大镜像
deb https://mirrors.ustc.edu.cn/ubuntu/ focal main restricted universe multiverse
deb https://mirrors.ustc.edu.cn/ubuntu/ focal-updates main restricted universe multiverse
deb https://mirrors.ustc.edu.cn/ubuntu/ focal-backports main restricted universe multiverse
deb https://mirrors.ustc.edu.cn/ubuntu/ focal-security main restricted universe multiverse

注意focal 是 Ubuntu 20.04 的代号如果你使用的是其他版本请将 focal 替换为对应版本的代号

步骤 4更新软件包列表

保存并退出编辑器后运行以下命令更新软件包列表

sudo apt update

12. 总结

通过切换国内镜像源你可以在很多常用的包管理工具中加速依赖的下载和安装过程省下不少时间以下是常见工具切换国内源的方法总结

  • npm淘宝镜像npm config set registry https://registry.npmmirror.com
  • Yarn淘宝镜像yarn config set registry https://registry.npmmirror.com
  • pip豆瓣源pip install package-name -i https://pypi.douban.com/simple
  • Conda清华源conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
  • Composer阿里云源composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
  • RubyGems淘宝镜像gem sources --add https://gems.ruby-china.com/ --remove https://rubygems.org/
  • Maven阿里云源修改 ~/.m2/settings.xml
  • Gradle阿里云源修改 build.gradle
  • Node.js淘宝镜像nvm install node --registry=https://registry.npmmirror.com
  • Docker配置国内镜像加速容器镜像下载
  • apt使用阿里云清华大学或中科大镜像加速软件包下载

小小吐槽——CSDN 为什么要强制登录

在开发过程中大家应该都有过这样的经历你在 CSDN 上找到了一段有用的代码然而当你准备复制时结果 CSDN 突然弹出一个登录框让你必须登录才能复制有些情况下甚至还会提示你充会员才能无障碍地复制

这种体验真的让人心累作为一个开发者最需要的是高效地获取信息而不是被迫跳过这些繁琐的登录步骤CSDN 的这种行为让人感觉像是被卡脖子逼迫你为基础的功能付费

这就像你走进了一家图书馆看到一本书的精彩片段正准备抄下笔记却被管理员拦住想抄先交钱 这种强行限制内容获取的做法似乎违背了互联网的初衷——信息的自由传播

话是说回来在如今信息发达的时代GitHubStack Overflow 这样的平台提供丰富的开源资源比起被限制的 CSDN更值得推荐


通过切换到国内镜像源你可以显著加快下载依赖的速度节省时间与精力而至于某些平台的强制登录和会员制度真的应该反思一下知识的自由传播难道不应该是互联网的初心吗