当前位置:首页>Linux>【简单嵌入式】 Linux 内核驱动

【简单嵌入式】 Linux 内核驱动

  • 2026-10-11 08:29:40
【简单嵌入式】 Linux 内核驱动

【简单嵌入式】 Linux 内核驱动


📦 一、先看完整编译安装流程(心中有图)

在动手写代码前,先搞清楚整个构建链:



1
2
3
4
5
6
7
8
9
10

# 1. 配置内核(生成 .config)
make defconfig          # 或 make menuconfig
# 2. 编译内核镜像(以 ARM64 为例)
make Image -j$(nproc)
# 3. 编译模块
make modules -j$(nproc)
# 4. 安装模块到目标根文件系统(可选)
make modules_install INSTALL_MOD_PATH=/path/to/rootfs
# 5. 手动复制内核镜像(嵌入式常用)
cp arch/arm64/boot/Image /path/to/rootfs/boot/


💡 注意:make install 在桌面 Linux 上可用,但在交叉编译嵌入式系统时,通常手动复制Image 和 .dtb 文件。


🗂️ 二、内核配置系统:Kconfig 是如何“找”到你的驱动的?

Linux 内核使用 Kconfig + Kbuild 构建系统。很多人以为只要写了 Kconfig 就能自动生效——大错特错!

✅ 正确做法(必须手动!):

假设你要添加一个新驱动目录:drivers/mydrv/

  1. 1. 创建子目录的 Kconfig


    1
    2
    3
    4
    5

    # drivers/mydrv/Kconfig
    config MYDRV
        tristate "My Awesome Driver"
        help
          This is my driver.


  2. 2. 在上层 Kconfig 中显式引入
    比如在 drivers/Kconfig 末尾加上:


    1

    source "drivers/mydrv/Kconfig"


⚠️ 如果你忘了这一步,即使写好了 Kconfig,menuconfig 里也完全看不到你的选项!Kconfig 不会自动递归扫描子目录,必须靠 source 显式引入。


🛠️ 三、开发环境拓扑(别再搞混了!)

角色
用途
主机(Host)
写代码、跑 Git
编译容器/虚拟机
执行 make,交叉编译工具链在此
开发板(Target)
运行最终系统

常见挂载方式:

  • • 主机代码目录 → 挂载到编译环境 /opt/linux-src
  • • 开发板根文件系统 → 挂载到编译环境 /mnt/rootfs

这样,make modules_install INSTALL_MOD_PATH=/mnt/rootfs 就能直接部署。


🌳 四、设备树三剑客:.dtsi、.dts、.dtb 到底谁改谁?

文件
谁写?
谁用?
可读?
要不要改?
.dtsi
芯片厂商(如 Rockchip)
所有基于该 SoC 的板子
✅ 是
❌ 一般不改
.dts
板级工程师
你的具体硬件
✅ 是
✅ 必须定制
.dtb
编译工具链(dtc)
Bootloader + 内核
❌ 二进制
❌ 只读

示例:你在 RK3588 开发板上接了一个 PCF85063,就在你的 .dts 文件里加:



1
2
3
4
5
6
7
8
9

&i2c6 {
    status = "okay";

    pcf85063: rtc@51 {
        compatible = "nxp,pcf85063";
        reg = <0x51>;
        quartz-load-femtofarads = <12500>;  // 晶振负载电容
    };
};



🔧 五、驱动代码分层设计:两类函数,各司其职

以 drivers/rtc/rtc-pcf85063.c 为例,所有优秀驱动都遵循 “面向芯片” vs “面向系统” 的分层思想。

第一类:面向芯片的函数(Chip-Oriented)

核心问题:怎么操作 PCF85063 这个硬件?

这些函数直接和寄存器打交道,处理 BCD 编码、I2C 通信、中断标志等。

函数
作用
pcf85063_rtc_read_time
从寄存器读 BCD 时间 → 转为 struct rtc_time
pcf85063_rtc_set_time
设置时间(需先停振荡器!)
pcf85063_rtc_set_alarm
配置闹钟 + 中断使能
pcf85063_ioctl
处理 RTC_VL_READ,检测是否掉电(查 OS 位)
pcf85063_load_capacitance
根据设备树设置晶振电容

✅ 特点:调用 regmap_read/write,懂芯片手册,处理硬件细节。


第二类:面向系统的函数(System-Oriented)

核心问题:怎么让 Linux 内核认识这个硬件?

这些函数对接内核框架,注册设备,处理探测、电源管理等。

结构/函数
作用
pcf85063_rtc_ops关键桥梁!
 将芯片函数打包成 rtc_class_ops
pcf85063_probe
设备探测入口:初始化 regmap、注册 rtc、申请中断
pcf85063_rtc_handle_irq
中断处理:通知 RTC 子系统
pcf85063_driver
I2C 驱动描述符(含匹配表 + probe 函数)
module_i2c_driver(...)
注册驱动到 I2C 总线

✅ 特点:使用 devm_*、of_match_table、rtc_register_device 等内核标准 API。


🌉 关键桥梁:pcf85063_rtc_ops



1
2
3
4
5
6
7

static conststruct rtc_class_ops pcf85063_rtc_ops = {
    .read_time  = pcf85063_rtc_read_time,  // ← 芯片层
    .set_time   = pcf85063_rtc_set_time,   // ← 芯片层
    .read_alarm = pcf85063_rtc_read_alarm,
    .set_alarm  = pcf85063_rtc_set_alarm,
    .ioctl      = pcf85063_ioctl,
};


  • • 对内核 RTC 子系统:它只看到 .read_time(),不知道背后是 I2C 还是 SPI。
  • • 对 PCF85063 芯片:pcf85063_rtc_read_time() 只关心寄存器地址和 BCD 转换。

这就是经典的 适配器模式(Adapter Pattern) —— 解耦硬件与系统!


🧩 六、驱动注册:i2c_driver 结构体详解



1
2
3
4
5
6
7
8
9
10

staticstruct i2c_driver pcf85063_driver = {
    .driver = {
        .name = "rtc-pcf85063",
        .of_match_table = of_match_ptr(pcf85063_of_match),
    },
    .probe_new = pcf85063_probe,
    .id_table  = pcf85063_ids,
};

module_i2c_driver(pcf85063_driver);


逐行解读:

  • • .name → 内核中驱动的名字,影响 /sys/bus/i2c/drivers/ 目录名。
  • • .of_match_table → 设备树匹配表,现代嵌入式 Linux 的标配:


    1
    2
    3
    4
    5

    static conststruct of_device_id pcf85063_of_match[] = {
        { .compatible = "nxp,pcf85063a" },
        { .compatible = "microcrystal,rv8263" },
        { }
    };


  • • .probe_new → 设备匹配成功后调用的初始化函数(新版 API)。
  • • module_i2c_driver() → 自动完成 i2c_add_driver(),省去 init/exit 模板代码。

⚙️ 七、Kconfig 与 Makefile 配置

1. drivers/rtc/Kconfig



1
2
3
4
5
6

config RTC_DRV_PCF85063
    tristate "NXP PCF85063"
    select REGMAP_I2C
    help
      Support for the PCF85063 RTC chip.
      This driver can also be built as a module (rtc-pcf85063.ko).


  • • tristate → 支持 y(内置)、m(模块)、n(不编译)
  • • select REGMAP_I2C → 自动依赖 regmap I2C 子系统

2. drivers/rtc/Makefile



1

obj-$(CONFIG_RTC_DRV_PCF85063) += rtc-pcf85063.o


3. 上层集成(drivers/Kconfig & Makefile)



1
2

# drivers/Kconfig
source "drivers/rtc/Kconfig"




1
2

# drivers/Makefile
obj-$(CONFIG_RTC_LIB) += rtc/


注意:RTC_LIB 是一个布尔开关,控制是否编译整个 rtc/ 子目录。


✅ 总结:写一个驱动,你只需要做这几件事

  1. 1. 写芯片操作函数(读写寄存器、处理中断)
  2. 2. 写系统接口函数(probe、ops 结构体)
  3. 3. 配置 Kconfig + Makefile(别忘了 source!)
  4. 4. 在设备树中描述硬件
  5. 5. 编译 → 部署 → 测试

最新文章

随机文章