尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Zephyr BSP: 08-编写第一个SoC驱动

发布时间:2026/9/29 6:21:15

资讯中心
01
ARTICLE

Zephyr BSP: 08-编写第一个SoC驱动

Zephyr BSP: 08-编写第一个SoC驱动
摘要:本文是 Zephyr SoC 移植系列的第 08 篇,目标是带你亲手实现第一个最小 SoC-specific Driver。文章从「为什么第一颗 Driver 不建议直接做 UART」讲起,以company,foo-sysctrl系统控制器为例,完整走通Devicetree Binding → DTS 节点 →DT_DRV_COMPAT→DT_INST_REG_ADDR()→init()→sys_write32()→ 寄存器这条核心链路,并逐步引入 Kconfig、CMakeLists 与 Application 调用方式。最后对比 Devicetree / Driver / Application 三者的职责,为后续 GPIO、UART、Clock 等 Driver 打下基础。第一个 SoC Driver这一篇非常关键。前面你已经从:Architecture → SoC → Board → Devicetree → Driver逐步建立了 Zephyr Hardware Model 的概念。现在不要马上去做一个复杂的 UART、SPI 或 GPIO Driver。对于你的目标——把公司自研 SoC 加入 Zephyr 开发生态——第一颗 Driver 最适合做成一个最小 SoC-specific Driver,用它把:Devicetree ↓ compatible ↓ Driver ↓ SoC Register ↓ 硬件这条链真正跑通。一、这一篇要解决什么问题?建议把这一篇定义成:用一个最小 SoC Driver,把 Devicetree → Driver → Register → Hardware 串起来。最终形成:Zephyr Application │ │ device_get_binding()▼ ┌───────────────┐ │ SoC Driver │ │ │ │ init()│ │ api │ └───────┬───────┘ │ │ register access ▼ ┌───────────────┐ │ SoC Register │ │ │ │ CTRL │ │ STATUS │ └───────┬───────┘ │ ▼ SoC Hardware而 Devicetree 在中间负责告诉 Zephyr:"这个硬件是什么""它的寄存器在哪里""它应该绑定哪个 Driver"二、为什么第一颗 Driver 不建议直接做 UART?因为 UART 会一下子引入很多东西:UART ├── baudrate ├── TX ├── RX ├── interrupt ├── FIFO ├── console ├── clock ├── pinctrl └── serial API对于第一次做 SoC Driver,这些东西会把真正重要的关系淹没。把两者放在一起对比,复杂度差异一目了然:维度UART Driver(第一颗不建议)最小 SoC-specific Driver(推荐)需要掌握的要素baudrate、TX、RX、interrupt、FIFO、console、clock、pinctrl、serial APIcompatible、DEVICE_DT_DEFINE、init、register、hardware核心链路被大量外设细节淹没一条直线,清晰可见寄存器访问需要配置多个寄存器(波特率、FIFO、中断、引脚复用)只需写一个 CTRL.ENABLE中断处理必须处理 TX/RX 中断、FIFO 阈值完全不需要时钟与引脚需要配置 clock 和 pinctrl不需要对外 API需要实现完整的 serial API 接口暂时只需要一个 init()学习目标容易迷失在细节里专注掌握 Devicetree → Driver → Register 的核心关系你现在真正需要掌握的是:compatible ↓ DEVICE_DT_DEFINE()↓ init()↓ register ↓ hardware所以第一颗 Driver 建议做成一个非常简单的:SoC System Control Driver例如:company,foo-sysctrl它只有两个寄存器:SYSCTRL │ ├── CTRL │ └── ENABLE │ └── STATUS └── READYDriver 做的事情也非常简单:sysctrl_init(){REG_WRITE(CTRL,ENABLE);}然后 Application:sysctrl_enable();最后硬件:SYSCTRL.CTRL.ENABLE=1三、先建立完整目录结构假设你的公司 SoC 叫:FooSoC第一颗 Driver 可以组织成:zephyr/ ├── dts/ │ └── bindings/ │ └── soc/ │ └── company,foo-sysctrl.yaml │ ├── drivers/ │ └── soc/ │ └── foo/ │ ├── Kconfig │ ├── CMakeLists.txt │ └── sysctrl.c │ ├── soc/ │ └── company/ │ └── foo/ │ └──... │ └── boards/ └──...这里要特别注意:Driver 和 SoC Port 不是一回事。soc/ ↓ SoC 本身 drivers/ ↓ SoC 外设/硬件 Driver例如:soc/company/foo/ clock interrupt controller cpu startup system init drivers/soc/foo/ sysctrl driver四、第一步:定义 Devicetree Binding建立:dts/bindings/soc/company,foo-sysctrl.yaml例如:description: FooSoC System Controller compatible:"company,foo-sysctrl"include: base.yaml properties: reg: required:true现在先不要增加十几个 properties。第一版只需要:compatible reg也就是:sysctrl: sysctrl@40000000{compatible="company,foo-sysctrl";reg=0x40000000 0x1000;};这里已经完成了非常重要的一步:Devicetree Node │ │ compatible ▼ company,foo-sysctrl │ ▼ Binding五、第二步:在 SoC DTS 中描述硬件假设公司 SoC 的 System Controller 地址:0x40000000那么:sysctrl:sysctrl@40000000{compatible="company,foo-sysctrl";reg=0x400000000x1000;status="okay";};最终 Devicetree 中就是:sysctrl@40000000│ ├── compatible │ company,foo-sysctrl │ ├── reg │0x40000000│0x1000│ └── status okay六、第三步:Driver 获取 Devicetree 信息这一步就是整个系列最重要的连接点。Driver:#defineDT_DRV_COMPATcompany_foo_sysctrl注意:Devicetree:company,foo-sysctrlDriver:company_foo_sysctrlZephyr 会把:company,foo-sysctrl转换成:company_foo_sysctrl然后:# define DT_DRV_COMPAT company_foo_sysctrl告诉 Zephyr:这个 C Driver 就是处理 compatible = “company,foo-sysctrl” 的。七、第四步:获取 Register 地址Driver 可以定义:structfoo_sysctrl_config{uintptr_tbase;};然后:staticconststructfoo_sysctrl_configfoo_sysctrl_config={.base=DT_INST_REG_ADDR(0),};这里:DT_INST_REG_ADDR(0)来自:reg=0x400000000x1000;所以:Devicetree reg=lt;0x40000000 0x1000gt;│ ▼ DT_INST_REG_ADDR(0)│ ▼ 0x40000000这就是:Devicetree → C code八、第五步:定义 Register假设硬件手册规定:SYSCTRL Base=0x40000000 Offset: 0x00 CTRL 0x04 STATUS那么:#defineFOO_SYSCTRL_CTRL0x00#defineFOO_SYSCTRL_STATUS0x04#defineFOO_SYSCTRL_CTRL_ENABLEBIT(0)#defineFOO_SYSCTRL_STATUS_READYBIT(0)Driver:#includelt;zephyr/kernel.hgt;#includelt;zephyr/device.hgt;#includelt;zephyr/sys/sys_io.hgt;#defineDT_DRV_COMPATcompany_foo_sysctrl#defineFOO_SYSCTRL_CTRL0x00#defineFOO_SYSCTRL_STATUS0x04#defineFOO_SYSCTRL_CTRL_ENABLEBIT(0)#defineFOO_SYSCTRL_STATUS_READYBIT(0)九、第六步:写最小 init()最简单的 Driver:staticintfoo_sysctrl_init(conststructdevice\*dev){conststructfoo_sysctrl_config\*config=dev-config;sys_write32(FOO_SYSCTRL_CTRL_ENABLE,config-base+FOO_SYSCTRL_CTRL);return0;}现在发生了:device initialization │ ▼foo_sysctrl_init()│ ▼ base=0x40000000│ ▼0x40000000+0x00│ ▼ SYSCTRL_CTRL │ ▼ ENABLE=1这就是一个真正的:SoC Driver → Register十、为什么使用 sys_write32()?不要第一版就直接:*(volatileuint32_t*)addr=value;虽然这样可以工作,但 Zephyr Driver 通常使用:sys_read32()sys_write32()sys_set_bit()sys_clear_bit()例如:sys_write32(value, address);读取:uint32_t value;value=sys_read32(address);这样以后 Driver 更容易保持 Zephyr 风格。十一、第七步:真正注册 Driver现在最关键的一行来了:DEVICE_DT_INST_DEFINE(0,foo_sysctrl_init,NULL,NULL,foo_sysctrl_config,POST_KERNEL,CONFIG_KERNEL_INIT_PRIORITY_DEVICE,NULL);它把:Devicetree instance0和:foo_sysctrl_init()连接起来。整体关系:DTS │ │ compatible ▼ company,foo-sysctrl │ ▼ DT_DRV_COMPAT │ ▼DT_INST_REG_ADDR(0)│ ▼DEVICE_DT_INST_DEFINE()│ ▼foo_sysctrl_init()│ ▼sys_write32()│ ▼ Register下面我们把这条链路展开成一张完整的 ASCII 流程图,并逐一说明每个环节的作用:┌─────────────────────────────────────────────────────────────────────┐ │ Devicetree 节点(sysctrl@40000000)│ │ │ │ compatible="company,foo-sysctrl"│ │ reg=0x40000000 0x1000│ │ status="okay"│ └───────────────────────────────┬─────────────────────────────────────┘ │ │ ① compatible 声明硬件类型 ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ company,foo-sysctrl │ │ (compatible 字符串) │ └───────────────────────────────┬─────────────────────────────────────┘ │ │ ② 逗号转下划线,绑定 Driver ▼ ┌─────────────────────────────────────────────────────────────────────┐ │#define DT_DRV_COMPAT company_foo_sysctrl ││ (告诉 Zephyr 这个 C 文件处理哪个 compatible) │ └───────────────────────────────┬─────────────────────────────────────┘ │ │ ③ 从 reg 属性提取寄存器基地址 ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ DT_INST_REG_ADDR(0)│ │ → 0x40000000 │ └───────────────────────────────┬─────────────────────────────────────┘ │ │ ④ 注册设备实例 ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ DEVICE_DT_INST_DEFINE(0,...)│ │ (把 instance0和 init 函数绑定) │ └───────────────────────────────┬─────────────────────────────────────┘ │ │ ⑤ 系统初始化时调用 ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ foo_sysctrl_init(dev)│ │ (Driver 的初始化入口) │ └───────────────────────────────┬─────────────────────────────────────┘ │ │ ⑥ 写入控制寄存器 ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ sys_write32(│ │ FOO_SYSCTRL_CTRL_ENABLE, │ │ config-base + FOO_SYSCTRL_CTRL │ │)│ └───────────────────────────────┬─────────────────────────────────────┘ │ │ ⑦ 落到物理地址 ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ SYSCTRL Register │ │(0x40000000 + 0x00=0x40000000)│ │ CTRL.ENABLE=1│ └─────────────────────────────────────────────────────────────────────┘每个环节的作用:① Devicetree 节点 ↓ 用 compatible 声明"这个硬件是什么",用 reg 声明"寄存器在哪里", 用 status="okay"声明"这个设备要启用"。 ② company,foo-sysctrl ↓ compatible 字符串是 Devicetree 和 Driver 之间的"接头暗号", 只有两边完全一致,Zephyr 才能把节点和 Driver 配对。 ③ DT_DRV_COMPAT company_foo_sysctrl ↓ 在 C 源码里声明"本文件就是处理 company,foo-sysctrl 的 Driver"。 注意逗号要变成下划线:company,foo-sysctrl → company_foo_sysctrl。 ④ DT_INST_REG_ADDR(0)↓ 从 Devicetree 的 reg 属性里取出第0个实例的寄存器基地址, 得到 0x40000000,把它填进 config-base。 ⑤ DEVICE_DT_INST_DEFINE(0,...)↓ 真正把 Devicetree instance0注册成 Zephyr 设备, 并绑定 init 函数、config 和 api。 ⑥ foo_sysctrl_init(dev)↓ 系统启动到 POST_KERNEL 阶段时,Zephyr 自动调用这个初始化函数, 它通过 dev-config 拿到寄存器基地址。 ⑦ sys_write32(...)↓ 把 FOO_SYSCTRL_CTRL_ENABLE 写到 base + FOO_SYSCTRL_CTRL, 也就是物理地址 0x40000000。 ⑧ SYSCTRL Register ↓ 最终硬件上的 SYSCTRL.CTRL.ENABLE 被置1, 系统控制器开始工作。这条链就是 Zephyr Driver Model 的核心骨架:Devicetree 描述硬件 → compatible 配对 → 宏提取信息 → 宏注册设备 → init 初始化 → 寄存器访问 → 硬件生效。后面无论做 GPIO、UART 还是 Clock Driver,走的都是同一条路。这张图建议作为这一篇的核心图。十二、完整的第一版 Driver可以先写成:#includelt;zephyr/kernel.hgt;#includelt;zephyr/device.hgt;#includelt;zephyr/sys/sys_io.hgt;#includelt;zephyr/sys/util.hgt;#defineDT_DRV_COMPATcompany_foo_sysctrl#defineFOO_SYSCTRL_CTRL0x00#defineFOO_SYSCTRL_STATUS0x04#defineFOO_SYSCTRL_CTRL_ENABLEBIT(0)#defineFOO_SYSCTRL_STATUS_READYBIT(0)structfoo_sysctrl_config{uintptr_tbase;};staticintfoo_sysctrl_init(conststructdevice\*dev){conststruct
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。