时钟树组件
时钟树组件是一个可视化的图形编辑器,用于配置芯片内部的时钟网络。每个时钟树版本保存为一个 UI 配置文件,位于 ips/_CLOCK/ui/{系列}/ 目录下。
文件结构
ips/_CLOCK/ui/{系列}/
├── ui.cjs # 时钟树图形布局(节点和连线)
└── data.cjs # 时钟数据函数(挂载到 RCU 外设)
时钟树节点类型
时钟树中的每个节点是一个 cell,通过 shape: 'vue-shape' 渲染,核心数据定义在 _data 属性中。
时钟源(type: 'text')
显示固定的时钟源,如内部振荡器、外部晶振:
{
shape: 'vue-shape',
_data: {
id: 'IRC8M', // 唯一标识,通过 $clock['IRC8M'] 引用
type: 'text', // 只读文本类型
label: 'IRC8M', // 显示标签
inclk: '8000000', // 输入频率 (Hz)
outclk: '', // 输出频率
outmin: '8000000', // 最小输出频率
outmax: '8000000', // 最大输出频率
disabled: true, // 是否禁用(内部振荡器通常不可关闭)
value: '8000000' // 当前值
}
}
常用时钟源:
| ID | 说明 | 典型频率 |
|---|---|---|
IRC8M | 内部 8MHz RC 振荡器 | 8 MHz |
HXTAL | 外部高速晶振 | 4-16 MHz |
LXTAL | 外部低速晶振 | 32.768 kHz |
IRC40K | 内部 40kHz RC 振荡器 | 40 kHz |
分频器(type: 'select')
对输入时钟进行分频,用户可选择分频系数:
{
_data: {
id: 'HXTAL_DIV',
type: 'select',
label: 'HXTAL/DIV',
inclk: "@get: $clock['HXTAL'].outclk", // 输入时钟引用
outclk: '@get: $parent.inclk/parseInt($parent.selected.label.replace("/1",""))', // 输出计算
disabled: "@get: $clock['HXTAL'].disabled", // 联动禁用
value: '1',
options: [
{ label: '/1', value: '1' },
{ label: '/2', value: '2' }
]
}
}
| 属性 | 说明 |
|---|---|
inclk | 输入时钟,通过 @get: 引用其他节点的 outclk |
outclk | 输出时钟,通过 @get: 计算表达式 |
disabled | 联动禁用条件 |
options | 分频选项列表 |
多路选择器(type: 'mux')
从多个时钟源中选择一路输出:
{
_data: {
id: 'PLLSEL', // PLL 时钟源选择
type: 'mux',
label: 'PLLSEL',
value: 'value',
options: [
{
label: '0',
value: '0',
inclk: "@get: $clock['IRC8M_DIV2'].outclk" // 选项 0:内部时钟 /2
},
{
label: '1',
value: '1',
inclk: "@get: $clock['HXTAL_DIV'].outclk", // 选项 1:外部时钟分频
disabled: "@get: $clock['HXTAL'].disabled"
}
]
}
}
每个选项的 inclk 定义该通道的输入时钟。mux 的输出时钟为当前选中选项的 inclk。
PLL 倍频器(type: 'select')
{
_data: {
id: 'PLLMF',
type: 'select',
label: 'PLLMF',
inclk: "@get: $clock['PLLSEL'].outclk",
outclk: "@get: $parent.inclk * parseFloat($parent.selected.label.replace('x', ''))",
options: [
{ label: 'x2', value: '0' },
{ label: 'x3', value: '1' },
{ label: 'x9', value: '7' },
{ label: 'x16', value: '14' }
]
}
}
时钟树连线
节点之间通过 shape: 'edge' 的 cell 连接:
{
shape: 'edge',
source: {
cell: 'b933ecea-...', // 源节点 ID
port: 'd4d43fb8-...' // 源端口 ID
},
target: {
cell: 'e2a881d3-...', // 目标节点 ID
port: '3cf1f689-...' // 目标端口 ID
}
}
总线预分频器
系统时钟经过 AHB、APB1、APB2 预分频器分配到各外设总线:
| 节点 ID | 说明 | 连接 |
|---|---|---|
AHBPSC | AHB 预分频器 | SYSCLK → HCLK |
APB1PSC | APB1 预分频器(低速外设) | HCLK → PCLK1 |
APB2PSC | APB2 预分频器(高速外设) | HCLK → PCLK2 |
data.cjs — 时钟数据函数
data.cjs 将时钟树的输出挂载到 RCU 外设,供其他外设查询时钟频率:
// 状态查询
RCU.pll_enabled = () => $clock.SYSSEL.value === 'pll'
RCU.hse_enabled = () => RCU.HSE.value !== 'disable'
RCU.lse_enabled = () => RCU.LSE.value !== 'disable'
// 频率查询
RCU.clock_hse = () => $clock.HXTAL.outclk // HSE 频率
RCU.clock_hsi = () => $clock.IRC8M.outclk // HSI 频率
// 外设时钟查询 — 根据外设名返回其总线时钟
RCU.get_ip_clock = (per) => {
if (per.includes('TIM')) {
// TIM1-6 用 CK_TIMER_GROUP1x, TIM0,7-10 用 CK_TIMER_GROUP2x
const num = per.replace('TIM', '')
if (['1','2','3','4','5','6','11','12','13'].includes(num))
return $clock.CK_TIMER_GROUP1x.outclk
if (['0','7','8','9','10'].includes(num))
return $clock.CK_TIMER_GROUP2x.outclk
}
if (per.includes('ADC')) return $clock.CK_ADCx.outclk
if (per.includes('SPI') || per.includes('I2S')) return $clock.CK_PCLK2.outclk
if (per.includes('CAN') || per.includes('UART') || per.includes('USART'))
return $clock.CK_PCLK1.outclk
return 0
}
通过 @get: 引用时钟
在 ui.cjs 的 _data 中,使用 @get: 表达式动态引用时钟值:
// 引用 HXTAL 的输出频率
inclk: "@get: $clock['HXTAL'].outclk"
// 引用 PLLSEL 选中选项的输出频率
outclk: "@get: $clock['PLLSEL'].outclk"
// 计算 PLL 倍频输出
outclk: "@get: $parent.inclk * parseInt($parent.selected.label.replace('x', ''))"
// 联动禁用:HXTAL 禁用时,HXTAL_DIV 也禁用
disabled: "@get: $clock['HXTAL'].disabled"
