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

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

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

外设配置详解

本文档详细介绍外设配置的技术细节,是 外设配置教程 的深入补充。

外设节点属性

每个外设节点的配置分散在以下文件中:

info.cjs — 定义外设实例

// chip/GD32F103XX/GD32F103ZET6/info.cjs
ips: [
  { name: 'ADC',  max: 3 },     // 节点名称和最大实例数
  { name: 'GPIO', max: 1 },
  { name: 'TIM',  max: 8 },
  { name: 'USART', max: 5 }
]

code.cjs — 指定模板版本

// chip/GD32F103XX/code.cjs
GPIO: {
  ip_type: 'GPIO',
  version: '1.0'
}

ips/{IP}/ui/*.cjs — 项目配置和功能函数

// ips/GPIO/ui/gpio_base.cjs 中的 project 对象
project: {
  generate_code: { value: true, fixed: false },
  rank: { value: 2, fixed: true },
  function_name: { value: 'Studio_GPIO_Init' },
  firmware: {
    value: 'std',
    options: [
      { label: 'Standard', value: 'std' }
    ]
  },
  file_name: 'gpio',
  ip_type: 'GPIO'
}
属性说明
generate_code是否生成该外设的初始化代码
rank外设在代码中的初始化顺序(数值越小越先初始化)
function_name生成的外设初始化函数名称
firmware支持的固件类型(std/hal/ll)
file_name生成的源文件名(不含扩展名)
ip_type外设类型标识,对应 ips/ 目录名

外设使能控制

外设没有显式的 enable 属性,而是通过 functions 中的 enabled() 函数判断:

// ips/GPIO/ui/gpio_base.cjs
functions: {
  enabled: () => {
    // 返回 true 表示外设可用
    return true
  }
}

初始化脚本

初始化代码的生成逻辑在 ips/{IP}/code/{version}/pre.cjs:

// ips/GPIO/code/1.0/pre.cjs
module.exports = ({ ip, ide, locals }) => {
  const global = locals.global
  let ip_pins = global.GPIO.get_ip_gpios([ip.$name])
  Object.assign(ip, ip_pins)
}

## MODE 节点与 signals

`ips/{IP}/ui/*.cjs` 中通过 `modes` 和 `signals` 两个节点控制外设的使能状态和引脚自动分配。

### modes — 外设工作模式

定义外设的 Mode 参数,每个选项通过 `signals` 数组声明该模式所需的引脚功能。`disable` 选项表示外设禁用。

**USART 示例:**

```javascript
modes: {
  Mode: {
    label: 'Mode',
    type: 'select',
    value: 'disable',          // 默认禁用
    options: [
      { label: 'Disable', value: 'disable' },
      { label: 'Asynchronous', value: 'asynchronous',
        signals: ['TX', 'RX'] },                        // 需要 TX、RX 引脚
      { label: 'Synchronous', value: 'synchronous',
        signals: ['TX', 'RX', 'CK'] },                  // 额外需要 CK 引脚
      { label: 'Single Wire', value: 'single_wire',
        signals: ['TX'] },                              // 仅需 TX 引脚
      { label: 'RTS/CTS', value: 'rts_cts',
        signals: ['RTS', 'CTS'] }                       // 硬件流控引脚
    ]
  }
}

SPI 示例:

modes: {
  Mode: {
    value: 'disable',
    options: [
      { label: 'Disable', value: 'disable' },
      { label: 'Full-Duplex Master', value: 'master_2lines',
        signals: ['MOSI', 'MISO', 'SCK'] },
      { label: 'Half-Duplex Master', value: 'master_1line',
        signals: ['MOSI', 'SCK'] },                     // 半双工只需 MOSI
      { label: 'Receive Only Master', value: 'master_2lines_rx_only',
        signals: ['MISO', 'SCK'] }                      // 仅接收只需 MISO
    ]
  },
  NSSMode: {
    options: [
      { label: 'Software', value: 'soft' },
      { label: 'Hard Input', value: 'hard_input',
        signals: ['NSS'] },                             // 硬件 NSS 需要 NSS 引脚
      { label: 'Hard Output', value: 'hard_output',
        signals: ['NSS'],
        disabled: "@get:$root.Mode.value.includes('slave')" }
    ]
  }
}

ADC 示例(checkbox 模式):

modes: {
  IN0: {
    label: 'IN0',
    type: 'checkbox',
    value: false,
    signals: ['IN0']           // 勾选时自动分配 IN0 引脚
  },
  IN1: { label: 'IN1', type: 'checkbox', value: false, signals: ['IN1'] }
}

signals — 引脚功能定义

顶层 signals 定义每个引脚信号的默认配置:

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

当用户选择某个 Mode 后,系统根据 signals 数组自动查找并分配对应的引脚。取消选择或切换模式时,自动释放不再需要的引脚。

enabled() — 使能判断

functions.enabled() 根据 Mode 值判断外设是否使能:

enabled: () => {
  return $root.Mode.value !== 'disable'
}

当 Mode === 'disable' 时,外设不可用,相关的 NVIC、DMA 等面板也会随之隐藏。

面板类型

Parameter 面板

用于配置外设的基本参数。通常包含下拉选择、输入框、复选框等元素:

Parameter: {
  label: 'Parameter',
  type: 'table',               // 可配置为 table 类型
  head: { /* 表头定义 */ },
  body: { /* 表体数据 */ }
}

NVIC 面板

NVIC 面板由两个文件协同定义:

1. nvic.cjs — 中断定义(chip/{系列}/{型号}/nvic.cjs)

定义芯片支持的所有中断及其与外设的关联:

module.exports = {
  USART0_IRQn: {
    label: 'USART0 global interrupt',
    ip_names: ['USART0']             // 关联的外设实例名
  },
  USART1_IRQn: {
    label: 'USART1 global interrupt',
    ip_names: ['USART1']
  },
  TIMER0_BRK_IRQn: {
    label: 'TIMER0 break interrupt',
    ip_names: ['TIM0']
  },
  TIMER0_UP_IRQn: {
    label: 'TIMER0 update interrupt',
    ip_names: ['TIM0']               // 一个 IP 可以有多个中断
  },
  ADC0_1_IRQn: {
    label: 'ADC0 and ADC1 interrupt',
    ip_names: ['ADC0', 'ADC1']       // 多个 IP 共享一个中断
  },
  EXTI0_IRQn: {
    label: 'EXTI line 0 interrupt',
    ip_names: ['GPIO']               // GPIO 的 EXTI 中断
  }
}
字段说明
label中断在 UI 中显示的名称
ip_names关联的外设实例名数组,只有 info.cjs 中存在该实例时,此中断才会生效

2. nvic_base.cjs — NVIC 面板逻辑(ips/NVIC/ui/nvic_base.cjs)

定义 NVIC 面板的生成逻辑和中断过滤:

// NVIC 面板结构
NVIC: {
  label: 'NVIC Settings',
  type: 'table',
  head: {
    irqn:       { label: 'IRQn', type: 'text' },
    enable:     { label: 'Enable', type: 'checkbox' },
    priority:   { label: 'Preemption Priority', type: 'select', options: [...] },
    subpriority:{ label: 'Sub Priority', type: 'select', options: [...] }
  },
  body: { /* 由 get_nvic_table_body() 动态生成 */ }
}

关键函数:

functions: {
  // 为指定外设实例筛选关联的中断
  get_ip_nvic_rows: (instance) => {
    // 遍历 nvic.cjs,找出 ip_names 包含该实例的中断
    // 返回匹配的中断名数组
  },

  // 生成外设的 NVIC 面板 UI
  get_ip_nvic_ui_by_instance: (instance) => {
    const rows = get_ip_nvic_rows(instance)
    // 自动从 NVIC 主面板复制对应的中断行
    // 如果外设有 DMA,自动关联 DMA 通道中断
  },

  // 收集所有已启用且不包含 DMA 的中断
  get_enabled_nvics: (_global, instance) => {
    // 遍历所有外设实例的 NVIC.body
    // 返回已启用的中断列表(含优先级、是否为多实例共享)
  }
}

DMA 中断自动关联:

当外设配置了 DMA 通道时,NVIC 面板自动包含对应的 DMA 中断:

// DMA 通道 → DMA 中断映射
DMA1_Channel1 → DMA1_Channel1_IRQn
DMA1_Channel2 → DMA1_Channel2_IRQn (或 DMA1_Channel2_3_IRQn)
DMA1_Channel3/4 → DMA1_Channel3_IRQn (或 DMA1_Channel3_4_IRQn)

DMA 面板

DMA 面板由两个文件协同定义:

1. dma.cjs — DMA 请求映射(chip/{系列}/{型号}/dma.cjs)

定义芯片中所有外设的 DMA 请求:

module.exports = {
  USART0_TX: {},    // USART0 发送 DMA 请求
  USART0_RX: {},    // USART0 接收 DMA 请求
  ADC0: {},         // ADC0 DMA 请求
  SPI0_TX: {},      // SPI0 发送 DMA 请求
  SPI0_RX: {},      // SPI0 接收 DMA 请求
  TIM0_CH0: {},     // TIM0 通道0 DMA 请求
  TIM0_UP: {}       // TIM0 更新 DMA 请求
}

每个键为 {外设实例名}_{通道/方向} 格式。外设的 DMA 面板根据此映射筛选可用的 DMA 请求。

2. dma_base.cjs — DMA 面板逻辑(ips/DMA/ui/dma_*_base.cjs)

定义 DMA 的参数和面板生成:

// DMA 面板参数
parameters: {
  channel_dma: {          // DMA 通道选择
    label: 'Channel',
    type: 'select',
    options: '@get:$root.dma_channels'
  },
  direction: {            // 传输方向
    label: 'Direction',
    type: 'select',
    value: 'm2m',
    options: [
      { label: 'Memory To Memory', value: 'm2m' },
      { label: 'Memory To Peripheral', value: 'm2p' },
      { label: 'Peripheral To Memory', value: 'p2m' }
    ]
  },
  priority: {             // DMA 优先级
    label: 'Priority',
    type: 'select',
    value: 'Low',
    options: [
      { label: 'Low', value: 'Low' },
      { label: 'Medium', value: 'Medium' },
      { label: 'High', value: 'High' },
      { label: 'Very High', value: 'VeryHigh' }
    ]
  },
  periph_inc: {           // 外设地址自增
    label: 'PeriphInc',
    type: 'select',
    value: 'Disable',
    options: [
      { label: 'Disable', value: 'Disable' },
      { label: 'Enable', value: 'Enable' }
    ]
  },
  memory_inc: {           // 内存地址自增
    label: 'MemoryInc',
    type: 'select',
    value: 'Disable',
    options: [
      { label: 'Disable', value: 'Disable' },
      { label: 'Enable', value: 'Enable' }
    ]
  },
  periph_data_alignment: { // 外设数据宽度
    label: 'PeriphDataAlignment',
    type: 'select',
    value: 'Byte',
    options: [
      { label: 'Byte', value: 'Byte' },
      { label: 'Half Word', value: 'HalfWord' },
      { label: 'Word', value: 'Word' }
    ]
  },
  mem_data_alignment: {    // 内存数据宽度
    label: 'MemDataAlignment',
    type: 'select',
    value: 'Byte',
    options: [
      { label: 'Byte', value: 'Byte' },
      { label: 'Half Word', value: 'HalfWord' },
      { label: 'Word', value: 'Word' }
    ]
  },
  mode: {                  // DMA 模式
    label: 'Mode',
    type: 'select',
    value: 'Normal',
    options: [
      { label: 'Normal', value: 'Normal' },
      { label: 'Circular', value: 'Circular' }
    ]
  }
}

关键函数:

functions: {
  // 检查外设是否有 DMA 能力(根据 dma.cjs 映射)
  has_ip_dma: (instance) => {
    // 检查 dma.cjs 中是否有该实例对应的 DMA 请求
  },

  // 为外设生成 DMA 面板 UI(自动包含该外设的 DMA 请求行)
  get_ip_dma_ui: (instance) => {
    if (!has_ip_dma(instance)) return ''
    // 返回该外设的 DMA 表格,包含 channel/direction/priority 等列
  },

  // 获取已启用的 DMA 配置(用于代码生成)
  getDMA: (ip) => {
    // 筛选 channel.value 不为空且不为 'Disable' 的行
  },

  // 获取 DMA 配置并映射到固件宏(含固件参数适配)
  get_ip_dmas: (ip, defineMap) => {
    // 额外将 channel/priority/direction/mode 等通过 defineMap 映射为 SDK 宏
  },

  // MemToMem 专用 DMA 获取
  get_mem_dmas: () => {
    // 获取 MemToMem 面板中 channel 不为 Disable 的行
  }
}

GPIO 面板

GPIO 面板由 GPI〇 UI 配置文件(ips/GPIO/ui/*.cjs)定义,不依赖独立的芯片级配置文件。

GPIO 表结构:

// GPIO 面板以表格形式呈现
GPIO: {
  label: 'GPIO',
  type: 'table',
  head: {
    name:      { label: 'Pin', type: 'text' },
    mode:      { label: 'Mode', type: 'select', options: [...] },
    speed:     { label: 'Speed', type: 'select', options: [...] },
    level:     { label: 'Level', type: 'select', options: [...] },
    alternate: { label: 'Alternate', type: 'text' }
  },
  body: { /* 引脚数据由脚本动态填充 */ }
}

关键函数:

functions: {
  // 手动选择引脚复用功能
  headPeripheralPinAf: (pin, af) => {
    // af.name === 'Reset_State' → 删除引脚
    // 唯一复用功能 → 删除旧绑定,更新新绑定
  },

  // 自动设置/释放引脚(系统调用,函数名不可修改)
  auto_set_pins: (target, name) => {
    // target=true → addPinByAlternate 自动查找空闲引脚
    // target=false → 删除未锁定的引脚
  },

  // 根据复用功能添加引脚(自动选择空闲引脚)
  addPinByAlternate: (alternate) => {
    // 查找支持该功能的空闲引脚
    // 过滤系统引脚(NRST、BOOT0、SWDIO、SWCLK)
    // 按 port 和 num 排序
  },

  // 获取 GPIO 初始化数据(核心函数)
  get_ip_gpios: (gpioTypes) => {
    // 1. 过滤符合类型的引脚
    // 2. 生成引脚描述(用于注释)
    // 3. 按 PORT 分组
    // 4. 按 mode/speed 合并同配置引脚
    // 5. 检查 EXTI 中断
    // 6. 生成 resetPins / setPins
  },

  // 固件参数映射
  set_gpio_define_map: (defineMap, firmware, pins) => {
    // 将 UI 参数值映射为特定固件的宏定义
  },

  // 引脚查找
  getPinByName: (name) => {},
  getPinByAlternate: (alternate) => {},   // 支持 ${num} 匹配
  isFreeByAlternate: (alternate) => {}    // 检查是否有空闲引脚
}

## 配置最佳实践

1. **pre.cjs 是代码生成入口**:每个外设必须在 `ips/{IP}/code/{version}/pre.cjs` 中导出入口函数,用于处理配置数据并生成初始化代码
2. **enabled() 控制使能状态**:通过 `functions.enabled()` 返回值控制外设在 UI 中的可用状态,无需单独的 enable 属性
3. **面板命名规范**:使用英文 ID(如 `Parameter`、`NVIC`),中文标签(如"参数配置"、"中断配置")
4. **元素 ID 命名**:使用有意义的驼峰命名(如 `BaudRate`、`WordLength`)
5. **代码注释**:在生成代码中包含注释,提高可读性
6. **错误处理**:使用 `ShowNotification` 提示配置错误
Prev
引脚配置详解
Next
外设元素参考