无标题
- 当前代码中,NSU event channel 2 没有配置“周期触发”。
- CCUH_StartNsuTrigger(2UL, 500000UL) 只给 NSU channel 2 设置了一个未来约 500 µs 的比较时间,因此它的作用是“一次性启动整条采集时序”。
- 当前真正的采集触发周期不是 NSU channel 2 产生的,而是后面的 ACQCMN acquisition trigger counter 产生的。
- 当前默认配置下,实际采集触发间隔约为 5.0016 µs,目标值是 5 µs。
- ACQCMN 是 Acquisition Common,中文可以理解为“采集公共控制/触发模块”,它负责把 NSU、CAPCOM、采集计数器以及 TXSLZR、HSADC、HSREF 等模块连接起来。
———
一、当前 NSU channel 2 的“周期”是多少
当前调用代码:
CCUH_StartNsuTrigger(2UL, 500000UL);
位置:app/src/tasks/acq_task.c:198。
对应函数:
void CCUH_StartNsuTrigger(uint32_t aChannel, uint32_t aDelayNs)
{
uint32_t cur_cntr_val_l = NSU_REG->bin_l_nanosec_counter_rd;
uint32_t cur_cntr_val_u = NSU_REG->bin_u_nanosec_counter_rd;
uint32_t new_cntr_val_l =
cur_cntr_val_l + (aDelayNs / 10UL);
uint32_t new_cntr_val_u =
(new_cntr_val_l < cur_cntr_val_l) ?
(cur_cntr_val_u + 1UL) :
cur_cntr_val_u;
NSU_REG->nsuevtgenchan[aChannel].evtgen_ch0_cmptime_ns_l =
new_cntr_val_l;
NSU_REG->nsuevtgenchan[aChannel].evtgen_ch0_cmptime_ns_u =
new_cntr_val_u;
NSU_REG->evtgen_control |= 1UL << aChannel;
}
这里的 aDelayNs 为:
500000 ns = 500 µs
NSU 当前使用的时间计数单位是 10 ns,因此:
500000 ns / 10 ns
= 50000 个 NSU counter tick
所以 channel 2 被设置成:
比较时间 = 当前 NSU 时间 + 500 µs
而不是:
每隔 500 µs 触发一次
———
当前代码中没有配置周期寄存器
NSU event generator 的寄存器结构主要包括:
NSU_EVTGEN_CONTROL_TYPE evtgen_control;
NSU_EVTGEN_TYPE nsuevtgenchan[8];
每个 event channel 只有一个比较时间:
evtgen_ch0_cmptime_ns_l
evtgen_ch0_cmptime_ns_u
没有看到:
period
reload
repeat interval
auto-increment compare time
之类的周期配置寄存器。
因此,从当前实现可以确定:
NSU channel 2 被配置的是一个绝对比较时间点。
当前源码没有配置 channel 2 的自动周期重装。
更准确地说:
NSU channel 2:一次性事件,用于启动采集
ACQCMN acq_trg counter:连续产生每次采集的周期触发
NSU 事件生成控制寄存器只有 channel enable 位:
#define NSU_EVTGEN_CONTROL_CH_EN_MASK (0xffUL)
启用 channel 2 的代码:
NSU_REG->evtgen_control |= 1UL << 2;
也就是:
EVTGEN_CONTROL.CH_EN[2] = 1
它使能 channel 2 的事件比较,但没有设置周期重载。
———
二、NSU channel 2 实际负责什么
当前采集启动流程:
初始化 ACQCMN
->
初始化 HSAFE、PLL、ADC 时钟
->
初始化 HSADC
->
配置 TXSLZR
->
配置 CAPCOM 和 ACQCMN 触发路由
->
CCUH_StartNsuTrigger(2, 500000)
最后一步将 NSU channel 2 安排在未来 500 µs 产生事件。
因此 channel 2 的作用是:
延迟启动整条采集时序
它相当于一个启动同步脉冲:
系统初始化完成
|
| 等待 500 µs
v
NSU_TRG2 event
|
v
启动/同步 Timer1
|
v
产生采集有效窗口
|
v
产生周期性 acq_trg
这里的 500 µs 不是 ADC 采样间隔,而是系统启动延迟。
———
三、NSU event channel 2 如何转换成 ACQCMN 的 acq_trg
完整路径如下:
NSU channel 2 compare event
|
| 输出 NSU_TRG2
v
ACQCMN Timer1 Event0 输入选择
|
v
CAPCOM1 / Timer1
|
| 产生 TMR1_COUT0
v
ACQCMN acquisition trigger source mux
|
v
ACQ trigger counter
|
| 每约 5 µs 产生一个 acq_trg
v
ACQCMN trigger distribution
|
+–> TXSLZR
+–> HSADC
+–> HSREF
下面按代码逐步展开。
———
四、第一步:NSU 产生 NSU_TRG2
CCUH_StartNsuTrigger() 使用 NSU 的自由运行纳秒计数器:
uint32_t cur_cntr_val_l =
NSU_REG->bin_l_nanosec_counter_rd;
uint32_t cur_cntr_val_u =
NSU_REG->bin_u_nanosec_counter_rd;
然后计算目标时间:
new_cntr_val_l = cur_cntr_val_l + (aDelayNs / 10UL);
当前:
aDelayNs = 500000 ns
aDelayNs / 10 = 50000 tick
将目标时间写入 channel 2:
NSU_REG->nsuevtgenchan[2].evtgen_ch0_cmptime_ns_l =
new_cntr_val_l;
NSU_REG->nsuevtgenchan[2].evtgen_ch0_cmptime_ns_u =
new_cntr_val_u;
最后使能 channel 2:
NSU_REG->evtgen_control |= 1UL << 2;
当 NSU 当前计数器同时匹配:
upper 32 bit
lower 32 bit
时,NSU 产生 channel 2 事件:
NSU event channel 2
ACQCMN 中将这个事件命名为:
NSU_TRG2
———
五、第二步:ACQCMN 将 NSU_TRG2 接到 Timer1 Event0
在 CCUH_SetupAcqTrigger() 中:
regVal = ACQCMN_REG->tmr1_event0_src_ctrl &
~ACQCMN_TMR1_EVENT0_SRC_CTRL_NSU_TRG2_MASK;
ACQCMN_REG->tmr1_event0_src_ctrl =
regVal |
(4UL << ACQCMN_TMR1_EVENT0_SRC_CTRL_NSU_TRG2_SHIFT);
然后:
ACQCMN_REG->tmr1_event0_src_sel |=
ACQCMN_TMR1_EVENT0_SRC_SEL_NSU_TRG2_EN_MASK;
对应寄存器字段:
#define ACQCMN_TMR1_EVENT0_SRC_CTRL_NSU_TRG2_MASK (0xFUL)
#define ACQCMN_TMR1_EVENT0_SRC_SEL_NSU_TRG2_EN_MASK (0x1UL)
当前配置结果:
TMR1_EVENT0_SRC_CTRL.NSU_TRG2 = 4
TMR1_EVENT0_SRC_SEL.NSU_TRG2_EN = 1
代码注释说明 4 的含义是:
NSU_TRG2 falling edge
所以实际路由是:
NSU_TRG2 falling edge
|
v
ACQCMN Timer1 Event0
这里不需要 GPIO 参与,也不需要把某个 GPIO 物理连接到另一个 GPIO。它是 ACQCMN 内部硬件信号路由。
———
六、第三步:Timer1/CAPCOM1 根据 Event0 生成行时序
同一个函数先配置 CAPCOM1:
CCUH_CapComConfigure(
1UL,
CCUH_CAPCOM_PRE_SCALAR,
lineTimeUs,
1UL);
默认:
#define CCUH_CAPCOM_PRE_SCALAR (100UL)
默认应用参数:
aTrigInterval = 5 us
aNumSamples = 2000
aLineGap = 1000 us
计算:
activeLineTimeUs = aTrigInterval * aNumSamples;
lineTimeUs = activeLineTimeUs + aLineGap;
结果:
activeLineTimeUs = 5 × 2000
= 10000 µs
= 10 ms
lineTimeUs = 10000 + 1000
= 11000 µs
= 11 ms
CAPCOM1 的时钟配置是:
100 MHz / 100 = 1 MHz
所以:
1 CAPCOM tick = 1 µs
自动重装周期:
ARR = lineTimeUs - 1
= 11000 - 1
= 10999
因此 CAPCOM1 的行周期约为:
11000 × 1 µs = 11 ms
———
CAPCOM1 的 compare 窗口
代码:
uint32_t startTime = 1UL;
uint32_t endTime = startTime + activeLineTimeUs;
默认值:
startTime = 1
endTime = 1 + 10000 = 10001
然后:
CCUH_CapComCompareConfigure(
1UL,
startTime,
endTime,
1UL,
0UL);
也就是:
CCR0 A compare = 1 µs
CCR0 B compare = 10001 µs
A_EN_XOR = 1
B_EN_XOR = 0
代码设计意图是让 TMR1_COUT0 在每行中的有效采集窗口内处于 active:
行周期:0 ~ 11000 µs
0 1 10001 11000
|——-|==============================|———–|
<—— active window ——-> <— gap —>
有效窗口长度:
10001 - 1 = 10000 µs = 10 ms
行间 gap:
11000 - 10001 = 999 µs
从概念上按配置值看是约 1 ms;由于 compare/counter 使用减一编码,实际边界可能存在一个计数 tick 的偏差。
CAPCOM1 的 COUT0 输出被使能:
CCUH_CapComOutputEnable(1UL, 0UL, 0UL, 0UL);
该函数设置:
CAPCOM1.OUT_CTRL bit0 = 1
CAPCOM1.CTRLA.CCT_EN = 1
所以:
CAPCOM1 channel 0 / sub-channel A
|
v
TMR1_COUT0
———
七、第四步:ACQCMN 将 TMR1_COUT0 作为 acq_trg 源
代码:
regVal = ACQCMN_REG->acq_trg_src_ctrl0 &
~ACQCMN_CAPCOM_CMN_SRC_CTRL_TMR1_COUT0_MASK;
ACQCMN_REG->acq_trg_src_ctrl0 =
regVal |
(0UL << ACQCMN_CAPCOM_CMN_SRC_CTRL_TMR1_COUT0_SHIFT);
然后:
ACQCMN_REG->acq_trg_src_sel |=
ACQCMN_ACQ_TRG_SRC_SEL_TMR1_COUT0_EN_MASK;
相关字段:
#define ACQCMN_CAPCOM_CMN_SRC_CTRL_TMR1_COUT0_MASK (0xf0000UL)
#define ACQCMN_CAPCOM_CMN_SRC_CTRL_TMR1_COUT0_SHIFT (16UL)
#define ACQCMN_ACQ_TRG_SRC_SEL_TMR1_COUT0_EN_MASK (0x10UL)
#define ACQCMN_ACQ_TRG_SRC_SEL_TMR1_COUT0_EN_SHIFT (4UL)
当前含义:
TMR1_COUT0 的 source select 编码 = 0
TMR1_COUT0 路由使能 = 1
代码注释明确说明:
Select Timer1 COUT0 as the source for the main acquisition trigger (acq_trg).
因此内部连接为:
TMR1_COUT0
|
v
ACQCMN acq_trg source mux
|
v
acq_trg trigger counter
再次强调,这里走的是 ACQCMN 内部连线,不是:
TMR1_COUT0 -> GPIO61 -> 外部导线 -> GPIO输入
GPIO61 只是可选的观测输出。
———
八、第五步:ACQCMN 的 acquisition trigger counter 产生真正的周期触发
代码:
uint32_t acqTrigInterCycles =
(aTrigInterval * 10000UL + 16UL) / 32UL;
注释说明:
acquisition trigger interval 使用 3.2 ns cycle
换算关系:
1 µs = 1000 ns
1 counter tick = 3.2 ns
counter ticks
= aTrigInterval × 1000 / 3.2
= aTrigInterval × 10000 / 32
加 16 是为了四舍五入。
———
默认 5 µs 的计算
aTrigInterval = 5 µs
因此:
acqTrigInterCycles
= (5 × 10000 + 16) / 32
= 50016 / 32
= 1563
然后写入:
PROG_CNT = acqTrigInterCycles - 1
= 1562
代码:
ACQCMN_REG->acq_trg_cntr_ctrl =
regVal |
((acqTrigInterCycles - 1UL) <<
ACQCMN_ACQ_TRG_CNTR_CTRL_PROG_CNT_SHIFT);
最后使能:
ACQCMN_REG->acq_trg_cntr_ctrl |=
ACQCMN_ACQ_TRG_CNTR_CTRL_ACQ_TRG_CNTR_EN_MASK;
寄存器定义:
#define ACQCMN_ACQ_TRG_CNTR_CTRL_PROG_CNT_MASK (0xffffUL)
#define ACQCMN_ACQ_TRG_CNTR_CTRL_ACQ_TRG_CNTR_EN_MASK (0x10000UL)
所以当前默认结果是:
PROG_CNT = 1562
实际周期计数 = 1562 + 1 = 1563 ticks
实际时间:
1563 × 3.2 ns
= 5001.6 ns
= 5.0016 µs
因此:
目标周期:5.0000 µs
实际周期:5.0016 µs
误差:+1.6 ns
实际频率约为:
1 / 5.0016 µs ≈ 199.936 kHz
目标频率:
1 / 5 µs = 200 kHz
———
九、所以真正的采集触发周期是谁决定的
当前系统有三种不同的时间概念,不能混淆。
1. NSU channel 2 延迟
当前时间 + 500 µs
由:
CCUH_StartNsuTrigger(2UL, 500000UL)
决定。
它是启动延迟,不是采样周期。
2. 行周期
11 ms
由:
lineTimeUs = aTrigInterval * aNumSamples + aLineGap
以及 CAPCOM1 的 ARR 决定。
默认:
5 µs × 2000 + 1000 µs = 11 ms
3. 单次 acquisition trigger 周期
约 5.0016 µs
由:
ACQCMN_REG->acq_trg_cntr_ctrl.PROG_CNT
决定。
这才是每次 TXSLZR、HSADC、HSREF acquisition event 之间的周期。
可以总结为:
时间参数 当前值 作用
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ━━━━━━━━━━━━━ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
NSU channel 2 delay 500 µs 系统初始化完成后,延迟多久开始第一行时序
────────────────────────────── ───────────── ──────────────────────────────────────────
acquisition trigger interval 5 µs 目标值 两次采集之间的间隔
────────────────────────────── ───────────── ──────────────────────────────────────────
实际 ACQ counter period 5.0016 µs 3.2 ns counter 量化后的真实周期
────────────────────────────── ───────────── ──────────────────────────────────────────
active line time 10 ms 一行中允许产生采集触发的有效时间
────────────────────────────── ───────────── ──────────────────────────────────────────
line period 11 ms 整行重复周期
────────────────────────────── ───────────── ──────────────────────────────────────────
line gap 约 1 ms 两行之间的空闲时间
———
十、ACQCMN 到底是什么
ACQCMN 是:
Acquisition Common
即:
采集公共模块
在代码中使用的寄存器类型是:
ACQCMN_RDBType
寄存器基地址:
#define ACQ_COMMON_BASE ((uintptr_t)(0xE0600000UL))
应用层通过:
#define ACQCMN_REG
((volatile ACQCMN_RDBType *)ACQ_COMMON_BASE)
访问它。
底层驱动名称也是:
ACQCMN driver
初始化位置:
ACQCMN_DrvInit(hsacq_cmn_id);
见 app/src/bcm/BCM8915X_BareMetal_helper.c:107。
———
ACQCMN 的主要职责
ACQCMN 不是某一路 ADC,而是采集子系统的公共控制与互联模块,主要负责:
- 连接 NSU event;
- 连接 CAPCOM/CCU Timer0、Timer1;
- 选择 acquisition trigger source;
- 产生 acquisition trigger counter;
- 向 HSADC、HSREF、TXSLZR 等模块分发触发;
- 配置触发输出 delay、pulse width、polarity;
- 提供 acquisition status 输出;
- 管理部分采集同步和轴/位置相关功能。
可以把它看成:
采集系统的触发交换机 + 定时器接口 + 触发分频器
当前使用到的 ACQCMN 功能包括:
NSU_TRG2
->
Timer1 Event0
Timer1 COUT0
->
acq_trg source
acq_trg counter
->
周期性 acquisition trigger
acq trigger outputs
->
TXSLZR / HSADC / HSREF
———
十一、ACQCMN 不等于 HSADC
这几个模块的层次不同:
NSU
= 提供全局纳秒时间基准和事件生成
CAPCOM / CCU
= 提供行周期、compare window、COUT 输出
ACQCMN
= 连接和调度采集触发
HSADC
= 执行 ADC 采样并保存采样数据
TXSLZR
= 响应 TX trigger,输出 ToF 发射 pattern
HSREF
= 响应参考触发,执行参考采样/同步功能
完整关系:
NSU:什么时候启动一行
|
v
CAPCOM:一行持续多久、有效窗口多长
|
v
ACQCMN:在有效窗口内多久发一次 acquisition trigger
|
+–> TXSLZR:输出发射脉冲
+–> HSADC:采集 ADC 数据
+–> HSREF:采集参考信号
———
十二、当前代码下的完整时间线
以系统初始化完成的时刻记为 T0。
T0
|
| 配置完成
|
+—- 500 µs —-> NSU channel 2 产生 NSU_TRG2
|
v
Timer1 Event0
|
v
CAPCOM1 行时序同步
|
+—- 约 1 µs —-> active window 开始
|
+—- 每约 5.0016 µs —-> acq_trg
| |
| +–> TXSLZR
| +–> HSADC
| +–> HSREF
|
+—- 约 10 ms —-> active window 结束
|
+—- 约 1 ms —-> line gap
|
v
下一行,周期约 11 ms
如果只看 NSU channel 2:
T0 + 500 µs:产生一次事件
如果看每次 ADC 采集:
T0 + 500 µs + 若干硬件延迟
之后每约 5.0016 µs 一次
如果看每行:
每约 11 ms 开始一行
———
十三、关于“NSU event channel 2 是否会重复”的准确表述
根据当前代码,最稳妥的结论是:
当前软件只给 channel 2 写入一个比较时间点,
没有配置软件重装或周期值,
因此不能把它视为 500 µs 周期源。
在实际硬件中,channel enable 是否在事件命中后自动清除、以及比较命中后是否可能保持事件状态,需要以 NSU TRM 的 event generator 行为定义为准。
但是无论硬件在命中后如何维持 enable 状态,当前代码都没有更新下一个 compare time,因此:
当前设计不能依赖 NSU channel 2 持续地产生 5 µs 周期事件。
5 µs 周期明确是由:
ACQCMN acq_trg counter
负责。
如果以后希望 NSU channel 2 自己周期触发,需要额外设计,例如:
- 使用 NSU system tick;
- 每次事件后软件重新写入下一个 compare time;
- 使用另一个具有周期/重载功能的定时器;
- 让 NSU 只负责行级同步,内部 ACQCMN 负责采样级周期。
当前代码选择的是最后一种架构:
NSU:一次性行启动同步
ACQCMN:采集周期产生
CAPCOM:行窗口和行周期管理
———
最终总结
当前工程中的触发层次是:
NSU channel 2
= 一次性启动事件,约在 500 µs 后产生
ACQCMN Timer1 Event0
= 接收 NSU_TRG2 falling edge
CAPCOM1
= 建立约 11 ms 的行周期和约 10 ms 的 active window
TMR1_COUT0
= 被 ACQCMN 选为主 acq_trg source
ACQCMN acq trigger counter
= 将触发节拍变成约 5.0016 µs 一次
TXSLZR / HSADC / HSREF
= 响应 acq_trg,并叠加各自的输出 delay
所以对你的问题最直接的回答是:
NSU event channel2 当前没有 5 µs 或 11 ms 的周期。
它只在启动后约 500 µs 产生一次同步事件。
真正的 5 µs 采集触发周期由 ACQCMN 的 acq_trg counter 产生;
真正的 11 ms 行周期由 CAPCOM1 产生。

