McuStudioMcuStudio
指南
开发者
组件
API
  • 版本发布
指南
开发者
组件
API
  • 版本发布
  • 概述
  • 快速开始
  • 添加厂商的芯片到平台
  • 芯片模板教程

    • 芯片模板开发教程
    • 创建引脚封装
    • 配置时钟树
    • 配置 GPIO 外设
    • 配置 ADC 外设
    • 配置通信外设
    • 配置 TIM 外设
  • 开发参考

    • 框架与规则
    • 芯片包结构
    • 芯片描述
    • 芯片包配置
    • 引脚配置详解
    • 外设配置详解
    • 外设元素参考
    • GPIO 开发参考
    • ADC 开发参考
    • TIM 开发参考
    • 固件库开发指南
    • 版本管理机制
    • Define 映射指南
    • 多语言(i18n)

框架与规则

本章定义芯片模板框架的核心架构、数据流和通用规则,是理解整个系统运作方式的基础。

系统架构

芯片模板框架将一个 MCU 芯片的完整开发生态转化为一组可复用、可分发的配置文件。数据流向如下:

芯片定义层(chip/)
    │
    ├── info.cjs    → 芯片元数据和 IP 列表
    ├── pins.cjs    → 引脚复用映射
    ├── nvic.cjs    → 中断与 IP 关联
    ├── dma.cjs     → DMA 请求映射
    ├── code.cjs    → IP 版本选择
    └── ide.cjs     → IDE 平台配置
    │
    ↓
IP 模板层(ips/)
    │
    ├── ui/*.cjs    → 配置界面定义(project/parameters/modes/signals/functions)
    └── code/{version}/ → 代码生成(pre.cjs + *.ejs)
    │
    ↓
通用层(common/)
    │
    ├── define.cjs  → 全局寄存器宏映射
    ├── gpio/       → GPIO 通用模板
    ├── dma/        → DMA 通用模板
    └── nvic/       → NVIC 通用模板
    │
    ↓
构建层
    │
    └── 合并所有配置 → 渲染 EJS 模板 → 输出工程代码

配置优先级

当多个层级定义了相同配置时,按以下优先级覆盖:

芯片型号(chip/{系列}/{型号}/)    ← 最高
    ↓ 覆盖
系列(chip/{系列}/)              ← 中
    ↓ 覆盖
通用(common/)                    ← 最低(默认值)
场景示例
芯片 define 覆盖通用 definechip/AT32F403/define.cjs 覆盖 common/define.cjs 中的同名条目
型号 ui.cjs 覆盖系列配置chip/GD32F103XX/GD32F103ZET6/ui.cjs 可覆盖系列级的外设设置
系列 code.cjs 指定版本chip/GD32F103XX/code.cjs 中 ADC: { version: '1.0' } 决定使用哪个 code 模板

文件约定

导出格式

所有 .cjs 配置文件必须使用 CommonJS 导出:

// ✅ 正确
module.exports = { ... }

// ✅ 正确(exports 别名)
exports.ADC = { ... }

// ❌ 错误:不支持 ES Module
export default { ... }

文件角色

文件角色必须?定义什么
chip/{系列}/{型号}/info.cjs芯片元数据✅Flash、RAM、IP 列表
chip/{系列}/{型号}/pins.cjs引脚映射✅引脚复用功能
chip/{系列}/{型号}/nvic.cjs中断定义✅中断与 IP 的关联
chip/{系列}/code.cjs版本映射✅IP → 模板版本
chip/{系列}/ide.cjsIDE 配置✅支持的 IDE 和平台
chip/{系列}/define.cjs寄存器宏-覆盖 common 的系列特有宏
ips/{IP}/ui/*.cjsUI 配置✅project/parameters/modes/signals/functions
ips/{IP}/code/{version}/pre.cjs代码入口✅数据处理逻辑
ips/{IP}/code/{version}/*.ejs代码模板✅按固件类型输出代码
common/define.cjs全局宏映射✅ADC/DMA/I2C/SPI 等参数到 SDK 宏

UI 配置规则

ips/{IP}/ui/*.cjs 文件必须导出以下五个节点之一或多个。每个节点的存在决定了系统如何处理该外设的 UI 和行为。

导出节点一览

节点类型说明
projectObject外设项目元配置
parametersObject用户可配置的参数定义
modesObject外设工作模式(含 signals 映射)
signalsObject引脚信号的默认电气配置
functionsObject功能函数(生命周期、使能判断、数据处理)

project — 必须导出

project: {
  generate_code: { value: true },       // 是否生成代码
  rank: { value: 2 },                   // 初始化顺序,数字越小越先
  function_name: { value: 'xxx' },      // 生成的函数名,支持 @get: 动态计算
  firmware: {                           // 支持的固件
    value: 'std',
    options: [{ label: 'Standard', value: 'std' }]
  },
  file_name: 'xxx',                     // 生成的文件名(不含扩展名)
  ip_type: 'XXX'                        // 对应 ips/ 目录名
}

parameters — 用户配置参数

定义了外设在 UI 中可配置的所有参数。每个参数包含 label、type、value,可选 visible、disabled、options(select 类型)等。

modes — 工作模式与引脚关联

Mode 参数必须定义在 modes 节点中(而非 parameters),因为 Mode 选项中的 signals 数组会触发系统的自动引脚管理。

规则:

规则说明
Mode disable 值每个外设的 Mode 必须包含 value: 'disable' 选项,表示禁用状态
signals 数组每个非 disable 选项声明该模式需要的信号名
信号名一致性signals 数组中的名称必须与顶层 signals 节点的键一致
自动引脚分配选择 Mode 时,系统根据 signals 数组自动查找并绑定对应引脚
modes: {
  Mode: {
    value: 'disable',
    options: [
      { label: 'Disable', value: 'disable' },
      { label: 'Asynchronous', value: 'async', signals: ['TX', 'RX'] }
    ]
  }
}

signals — 引脚信号默认配置

顶层 signals 必须定义每个信号名的默认电气配置:

signals: {
  TX: { mode: 'AF_PP', pull: 'NOPULL', speed: 'High', level: 'NA' },
  RX: { mode: 'AF_PP', pull: 'NOPULL', speed: 'High', level: 'NA' }
}

functions — 功能函数

函数签名说明
enabled() => boolean外设是否使能。通常判断 $root.Mode.value !== 'disable'
on__mounted() => void外设加载完成后的初始化回调
on__change(params) => void任何配置项变化时触发
on__enableChange() => void外设使能状态变化时触发

响应式系统

框架基于 @get / @set / @value 实现响应式数据绑定。

@get — 响应式读取

建立依赖追踪,当引用的值变化时自动重新计算:

// 引用当前外设参数
disabled: "@get: $root.NbrOfConversion.value <= 1"

// 引用时钟树节点
inclk: "@get: $clock['HXTAL'].outclk"

// 引用其他外设
visible: "@get: ADC0.enabled() && ADC1.enabled()"

// 计算表达式
outclk: "@get: $parent.inclk * parseInt($parent.selected.label.replace('x', ''))"

@set — 响应式写入

将计算结果写回指定路径:

// 双向绑定
value: "@set: $parent.inclk"

// 条件写入
semaphore: "@set: '@get: $parent.value === \"Disable\" ? \"\" : $parent.value'"

@value — 一次性取值

取值但不建立依赖追踪:

value: "@value: $pins.pins.length"

生命周期

外设从加载到代码生成的完整时序:

1. on__mounted        ← 外设加载完成,初始化数据(如构建 pinsMap)
    ↓
2. 用户配置交互       ← Mode 切换、参数修改、引脚选择
    ↓
3. on__change         ← 每次配置变化触发(含 id/value/path)
    ↓
4. on__enableChange   ← 使能状态变化时触发
    ↓
5. 构建触发           ← 用户点击生成/构建
    ↓
6. pre.cjs 执行       ← 数据处理:get_ip_gpios / get_ip_dmas 等
    ↓
7. EJS 模板渲染       ← 根据 firmware 选择对应 .ejs 文件渲染
    ↓
8. 输出工程文件        ← .c / .h 文件写入工程目录

代码生成管线

从用户配置到输出代码的完整链路:

用户 UI 配置数据 (ip 对象)
    │
    ↓ pre.cjs 入口
数据预处理(GPIO/DMA/NVIC 函数调用)
    │
    ├── GPIO.get_ip_gpios()      → 引脚数据分组合并
    ├── GPIO.set_gpio_define_map() → 参数映射为固件宏
    ├── DMA.get_ip_dmas()         → DMA 配置 + 固件映射
    ├── NVIC.get_enabled_nvics()  → 中断列表 + 优先级
    └── RCU.get_ip_clock()        → 外设时钟频率
    │
    ↓ ip 对象扩展完成
EJS 模板渲染(std.c.ejs / hal.c.ejs / ll.c.ejs)
    │
    ↓ 模板引擎替换变量
最终 C/H 代码输出

pre.cjs 约定

  • 入口函数签名:module.exports = ({ ip, ide, locals }) => {...}
  • ip:当前外设的完整配置数据
  • locals.global:所有外设的函数集合
  • locals.defineMap:合并后的 define 映射表
  • 通过 Object.assign(ip, ...) 将处理结果合并回 ip 对象
Next
芯片包结构