# Multislope ADC方案学习分析 **Repository Path**: leapsecond/Multislope_ADC ## Basic Information - **Project Name**: Multislope ADC方案学习分析 - **Description**: 学习https://github.com/Multi-slope-ADC 实现 - **Primary Language**: 汇编 - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-02-14 - **Last Updated**: 2026-02-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 多斜积分ADC (Multislope ADC) 代码分析 ## 1. 概述 eevblog上这个ADC项目实现了一个基于Atmel AVR MCU(ATmega系列)的高精度多斜积分ADC系统,配合Python上位机进行数据采集和校准处理。 [固件代码:](https://github.com/Multi-slope-ADC/Firmware) [硬件设计:](https://github.com/Multi-slope-ADC/PCB) [上位机:](https://github.com/Multi-slope-ADC/Python-adc-control) ### 1.1 系统架构 ``` ┌─────────────────┐ UART ┌──────────────────┐ │ Python上位机 │◄───────────►. │ ATmega MCU │ │ (control.py) │ 9600 baud │ (main.asm) │ └─────────────────┘ └────────┬─────────┘ │ ┌──────────────────────┼──────────────────────┐ │ │ │ PD2 PD3 PD4 (Signal输入) (小参考/负参考) (大参考/正参考) │ │ │ └──────────────────────┴──────────────────────┘ │ 4053模拟开关 │ 积分器电路 │ 比较器(过零检测) │ MCU内部ADC (残余电荷测量) ``` ### 1.2 硬件引脚定义 ```asm ; 端口D控制信号 (4053模拟开关) .equ control_pos = 16 ; PD4: 大参考电压 (负向积分) .equ control_neg = 8 ; PD3: 小参考电压 (正向积分) .equ control_Sig = 4 ; PD2: 被测信号输入 ; 端口C多路复用器控制 (DG408) .equ mux0 = 8*7 ; 通道7: 0V参考(GNDS) .equ mux7 = 8*6 ; 通道6: 7V参考 .equ mux1 = 8*2 ; 通道2: 输入信号3 ; 比较器输入 ; AN0/AN1 (PD6/PD7): 过零比较器 ``` --- ## 2. 多斜积分工作原理 ### 2.1 基本多斜积分概念 多斜积分ADC是一种高精度模数转换技术,通过**电荷平衡**原理实现高精度测量。核心思想是: 1. **Run-up阶段**:在固定时间内,用被测信号和参考电压共同对积分器充电 2. **Run-down阶段**:用参考电压将积分器放电回零,计数放电时间 3. **残余电荷测量**:用高精度ADC测量最终残余电压 ### 2.2 电荷平衡方程 $$Q_{in} + Q_{ref} = Q_{residual}$$ 即: $$V_{in} \cdot T_{runup} + V_{ref+} \cdot T_{pos} - V_{ref-} \cdot T_{neg} = V_{res} \cdot C$$ ### 2.3 详细时序流程 ``` 积分器输出电压 │ │ ╭─╮ ╭──╮ ╭──╮ ╭──╮ │ ╱ ╲ ╱ ╲ ╱ ╲ ╱ ╲ │ ╱ ╲─╱ ╲╱ ╲─────╱ ╲────── │ ╱ \ │ ╱ \ ────┼─╱ ╲──── │ \ │ \ └──┬──────────┬──────────┬──────────┬──────────┬──────────┬──► t │ │ │ │ │ │ Run-up Variable Fixed Fixed Slow Residual (Signal) Phase1 Neg Pos Slope ADC Read ``` --- ## 3. 运行阶段详解 (Firmware) ### 3.1 Run-up阶段 (积分上升阶段) Run-up阶段是多斜积分的核心,通过在固定周期内交替施加信号和参考电压,实现电荷累积。 #### 3.1.1 基本Run-up模式 (3步模式) ```asm ; 典型的3步Run-up循环 (runup_P3n) runupP3n_loop: ; 1. 正向相位 (Fixed Pos) nop; nop; nop; nop ; 固定延时 skipCompNeg ; 测试比较器 ldi t2, control_Signeg ; 根据比较器结果改变方向 rcall variphase ; 可变相位 ; 2. 负向相位 (Fixed Neg) ldi t2, control_Signeg out portSW, t2 nop; nop; nop; nop; nop ; 固定延时 ; 3. 下一个循环 in t3, ACSR ; 读取比较器状态 sbiw ZH:ZL, 1 ; 循环计数减1 ldi t2, control_Sigpos out portSW, t2 BRNE runupP3n_loop ``` #### 3.1.2 Run-up变体 (P, Q, R, S, T, U, V, W) 固件实现了8种不同的Run-up模式,通过`par_runup_ver`参数选择: | 模式 | 代码 | 周期时间 | 特点 | |-----|------|---------|------| | P | runup_P3f | 51 cycles | 高速模式 | | Q | runup_P3n | 102 cycles | 正常模式 | | R | runup_P3s | 102 cycles | 短脉冲模式 | | S | runup_P4nV | 204 cycles | 4步模式 | | T | runup_PdV | ~204 cycles | 虚拟模式(无输入) | | U | runup_P40V | 204 cycles | 4步零相位模式 | | V | runup_P3l | 102 cycles | 长脉冲模式 | | W | runup_P3sl | 204 cycles | 慢速模式 | ```asm runup_var: ; 根据参数选择Run-up模式 rcall ADC_sync ; 同步ADC采样 rcall runup_prepare ; 初始化计数器 ldi t2, control_Sigpos ; 初始状态 lds temp, par_runup_ver ; 读取模式参数 tst temp breq runup_P3f ; 0: P模式 dec temp breq jpp3n ; 1: Q模式 dec temp breq jpp3s ; 2: R模式 ; ... ``` #### 3.1.3 Run-up计数原理 在Run-up过程中,固件记录**正向参考相位的次数**: ```asm variphase1: ; 带比较器测试的可变相位 skipCompPos ; 测试比较器符号 ldi t2, control_Sigpos ; 如果比较器指示,切换方向 variphase: ; 入口点 out portSW, t2 ; 启动可变相位 clr temp bst t2, 3 ; 将bit 3 (pos输出) 传送到T标志 bld temp, 0 ; T -> bit 0 add coutAL, temp ; 计数正向相位次数 adc coutAH, reg0 rjmp uart_send ; 同时发送UART数据 ``` ### 3.2 Run-down阶段 (积分下降阶段) Run-down阶段将积分器输出回到零点附近,并记录各阶段时间。 ```asm rundown: ldi t2, control_pos out portSW, t2 ; 1. 大参考阶段 (快速放电) LDI temp, 1 STS TCCR1B, temp ; 启动Timer1 Lrd1: ; 等待过零点 in t3, ACSR skipCompPos rjmp Lrd1 out portSW, t2 ; 2. 切换到小参考 lds t1negL, TCNT1L ; 记录时间戳1 lds t1negH, TCNT1H ldi t2, control_slow ; 准备慢速模式 Lrd2: ; 等待再次过零 in t3, ACSR skipCompNeg rjmp Lrd2 out portSW, t2 ; 3. 慢速模式 (双参考) lds t1posL, TCNT1L ; 记录时间戳2 lds t1posH, TCNT1H ldi t2, control_hold ; 准备停止 Lrd3: ; 等待最终过零 in t3, ACSR skipCompPos rjmp Lrd3 out portSW, t2 ; 4. 停止 lds t1slowL, TCNT1L ; 记录时间戳3 lds t1slowH, TCNT1H ``` **Run-down三阶段计时**: - **T1 (t1neg)**: 大参考阶段结束时间 - **T2 (t1pos)**: 小参考阶段结束时间 - **T3 (t1slow)**: 慢速阶段结束时间 ### 3.3 残余电荷测量 使用MCU内部10位ADC测量积分器最终输出电压: ```asm ; 启动同步ADC转换 ADC_sync: ldi temp, 1 << TOV1 out TIFR1, temp ; 清除溢出标志 ldi temp, 4 sts ADCSRB, temp ; 选择自动触发源 ldi temp, ADcontr + (1 << ADATE) - (1 << ADSC) sts ADCSRA, temp ; 配置自动触发 ldi temp, 3 sts ADCSRB, temp ; 选择OC1A作为触发源 ; ... 延时和手动触发 ``` --- ## 4. 测量工作模式 ### 4.1 Mode A - 自动归零模式 (Auto-Zero) ```asm mslopeA: ; 带自动归零的多斜模式 ldi nextmux, mux0 ; 下次转换MUX设置 rcall mslope1 ; 第1次转换: 被测信号 ldi temp, 254 ; 同步标记 0xFFFE st x+, temp ldi temp, 255 st x+, temp lds nextmux, par_muxchan rcall mslope1 ; 第2次转换: 0V rcall control rjmp mslopeA ``` 测量序列: **Signal → 0V** (循环) ### 4.2 Mode C - 三读数模式 (推荐) ```asm mslopeC: ; 3次读数: 信号, 0V, 7V参考 ldi nextmux, mux0 rcall mslope1 ; 第1次: 被测信号 ldi temp, 250 ; 同步标记 0xFFFA st x+, temp ldi temp, 255 st x+, temp ldi nextmux, mux7 rcall mslope1 ; 第2次: 0V lds nextmux, par_muxchan rcall mslope1 ; 第3次: 7V参考 rcall control rjmp mslopeC ``` 测量序列: **Signal → 0V → 7V** (循环) 这是推荐的正常工作模式,可同时测量被测信号、零点偏移和参考电压。 ### 4.3 Mode D - 四读数模式 ```asm mslopeD: ; 4次读数模式 ; Signal → Channel1 → 0V → 7V ``` ### 4.4 Mode E - 双通道伪差分模式 ```asm mslopeE: ; 2次读数: 信号1, 信号2 (伪差分) ; Signal1 → Signal2 ``` --- ## 5. 校准流程详解 ### 5.1 校准参数定义 ```asm ; 校准相关常量 #define K2_loops 120 ; K2校准循环次数 #define K2_puleslength 3 ; K2校准脉冲长度 #define K1_long 25 ; K1校准长脉冲 #define K1_short 5 ; K1校准短脉冲 ``` ### 5.2 K1校准 - 斜率比测量 **目的**: 测量小参考与慢速参考的斜率比。 **原理**: 慢速参考 = 大参考 + 小参考 (同时施加正负参考) $$K_1 = \frac{V_{ref-slow}}{V_{ref-small}} = \frac{V_{ref+} + V_{ref-}}{V_{ref-}}$$ ```asm K1_measure: ldi temp, 12 sts OCR1AH, temp ; 设置循环长度上限 ldi temp, 150 sts OCR1AL, temp ; 约312μs ldi temp, K1_short rcall K1_meas_cycle ; 短脉冲测试 ldi temp, K1_long rcall K1_meas_cycle ; 长脉冲测试 ldi temp, K1_short rcall K1_meas_cycle ; 短脉冲测试 ldi temp, K1_long rcall K1_meas_cycle ; 长脉冲测试 ; ... 多次交替测量 ``` **测量过程** (K1_meas_cycle): 1. 先执行rundown归零 2. 施加固定长度的负参考脉冲 3. 切换到慢速模式,等待过零 4. 记录慢速模式持续时间 5. 重复128次循环并累加 ```asm K1_meas_cycle: mov LoopCntH, temp ; 保存脉冲长度参数 rcall runup_prepare rcall rundown ; 归零 rcall readAD_wait rcall runup_prepare ldi LoopCntL, 128 ; 128次循环 abgl_loop: rcall Reset_counter LDI temp, 1 STS TCCR1B, temp ; 启动Timer1 ldi t2, control_neg out portSW, t2 ; 负参考脉冲 mov temp, LoopCntH rcall delay0 ; 延时 ldi t2, control_slow out portSW, t2 ; 切换到慢速模式 lds t1posL, TCNT1L ; 记录开始时间 lds t1posH, TCNT1H abgl1: ; 等待过零 in t3, ACSR skipCompPos rjmp abgl1 out portSW, t2 ; 停止 lds t1slowL, TCNT1L ; 记录结束时间 lds t1slowH, TCNT1H sub t1slowL, t1posL ; 计算慢速模式持续时间 sbc t1slowH, t1posH add coutAL, t1slowL ; 累加 adc coutAH, t1slowH adc coutBL, reg0 ; 第3字节进位 ; 等待固定循环时间... DEC LoopCntL BRNE abgl_loop ``` **上位机计算K1** (control.py): ```python # K1 = 慢斜率 / 小斜率 # 通过长短脉冲的差异计算 u = (sum25 / n25 - sum5 / n5) # 长短脉冲平均差异 k1_raw = u / (3 * (k1_puls - 5) * 128) # 计算斜率比 k1 = 1.0 / median(k1values) # 最终校准值 ``` ### 5.3 K2校准 - ADC增益测量 **目的**: 测量MCU内部ADC的增益,建立"时钟周期"与"ADC LSB"之间的关系。 ```asm K2_measure: ldi temp, 4 sts OCR1AH, temp ldi temp, 228 sts OCR1AL, temp ; 设置循环长度 ldi temp, K2_puleslength rcall K1_meas_cycle ; 获取基准时间 ; 计算延迟值 mov temp, coutAH ldi t2, control_slow SBRS temp, 0 ; 检查最低位 ldi t2, control_hold mov w2, t2 LSR temp ; 除以2 subi temp, 3 ; 减去固定偏移 mov slow_length, temp ; 保存计算值 ; 执行反馈循环测试... ``` K2测量使用**反馈循环**来精确找到使ADC读数在中间的延迟值: ```asm AdK2_loop: rcall Reset_counter LDI temp, 1 STS TCCR1B, temp ldi t2, control_neg out portSW, t2 ; 负参考脉冲 ldi temp, K2_puleslength rcall delay0 ldi t2, control_slow out portSW, t2 ; 慢速模式 mov temp, slow_length ADK_del: ; 可变延迟 nop dec temp brne ADK_del lds t1negL, adcL ; 读取ADC值 lds t1negH, adcH cpi temp, 2 ; 与中间值比较 brcs ADK2_1 ; ADC < 0x0200 ; 根据ADC值调整延迟... ``` **上位机计算K2** (control.py): ```python # K2 = ADC LSB / 时钟周期 # 通过上下步进测量计算 k2_raw = (u + u2) / 2 # u: 上步ADC变化, u2: 下步ADC变化 k2 = 4.0 / median(k2values) # 最终校准值 ``` ### 5.4 比例因子(SF)校准 **目的**: 建立输出读数与电压值之间的最终比例关系。 ```python # 在Mode C中使用7V参考校准 if abs(u3 - u2) > 1000: du = (u1 - u2) / (u3 - u2) # 归一化读数 sf = scale / (u3 - u2) # 更新比例因子 # scale = 参考电压(mV) = 6951.926 # sf0 = (2 - k1) * scale / (adcclock * 0.02) # 初始估计 ``` --- ## 6. 上位机数据处理 (control.py) ### 6.1 ADC结果计算公式 ```python def readADC(k_0): # 读取一次ADC结果 ru = readbytes(2) # Run-up计数 small = readbytes(2) # 小参考(负)周期数 large = readbytes(2) # 大参考(正)周期数 adc1 = readbytes(2) # 残余电荷ADC读数(后) adc2 = readbytes(2) # 残余电荷ADC读数(前) adcdiff = adc2 - adc1 ru0 = round(adcclock * 0.02 / (k_0 + 16) / 2) # 近似零点 # 多斜积分结果公式 result = (k_0 * (ru - ru0)) * (2 + k1) + \ (small - large * (1 + k1) + adcdiff * k1 * k2) return result ``` ### 6.2 最终电压计算 ```python # 原始读数转换为电压 voltage = du * sf # du: 归一化差值, sf: 比例因子 # 在Mode C中 if abs(u3 - u2) > 1000: du = (u1 - u2) / (u3 - u2) # 使用参考归一化 voltage = du * scale ``` --- ### 6.3 ADC计算过程逐步分解 - 从原始数据到电压值 这一节详细解释ADC如何从原始计数值计算出被测电压,以及基准电压在计算中的作用。 #### 6.3.1 核心概念:比例测量 多斜积分ADC的本质是**比例测量工具**: ``` 被测电压 N_被测 ───────── = ────────── 基准电压 N_基准 ``` 其中N表示"等效电荷量",以时钟周期为单位。由于直接测量电荷困难,我们通过积分过程将电压转换为时间/计数的比例。 #### 6.3.2 固件提供的原始数值 每次转换,固件向上位机发送6个关键数值(16位无符号整数): | 数值 | 符号 | 来源 | 物理意义 | |-----|------|------|---------| | **ru** | Run-up计数 | Run-up循环中计数 | 正向参考相位的循环次数 | | **small** | 小参考周期 | Timer1时间戳 | 小参考(负)放电时间 = t1slow - t1neg | | **large** | 大参考周期 | Timer1时间戳 | 大参考(正)放电时间 = t1neg + t1slow - t1pos | | **adc1** | ADC后读数 | MCU ADC | Run-down结束后残余电荷 | | **adc2** | ADC前读数 | MCU ADC | Run-down开始前残余电荷(前一周期) | | **adcdiff** | ADC差值 | 计算: adc2-adc1 | 残余电荷变化量 | #### 6.3.3 计算步骤分解 **步骤1: 计算Run-up阶段的净电荷量** ```python # 零点校正 - ru0是理论上的"零输入"Run-up计数 ru0 = round(adcclock * 0.02 / (k_0 + 16) / 2) # 对于12MHz时钟,20ms周期: ru0 ≈ 1175 (取决于k_0) # Run-up净贡献 Q_runup = k_0 * (ru - ru0) ``` 说明: - `k_0`: 每个Run-up循环的时钟周期数 (如102 cycles) - `(ru - ru0)`: 相对于零点的净Run-up计数 - `k_0 * (ru - ru0)`: 转换为时钟周期数 **步骤2: 计算Run-down阶段的总电荷量** ```python # 小参考(负)贡献 - 直接就是small值 Q_small = small # 大参考(正)贡献 - 需要乘以斜率比系数(1+k1) Q_large = large * (1 + k1) # 净Run-down电荷 Q_rundown = Q_small - Q_large ``` 说明: - `k1`: 慢斜率与小斜率的比值系数 (约 1/20 = 0.05) - `1+k1 ≈ 1.05`: 将大参考周期转换为小参考等效周期 **步骤3: 计算残余电荷贡献** ```python # 将ADC差值转换为时钟周期等效值 Q_residual = adcdiff * k1 * k2 ``` 说明: - `k1`: 斜率比系数 - `k2`: ADC增益系数 (4/ADC_LSB_per_cycle,约4/20=0.2) - `k1*k2`: 综合转换系数,将ADC LSB转换为时钟周期 **步骤4: 合并所有贡献** ```python # 总结果(以小参考周期为单位) result = Q_runup * (2 + k1) + Q_rundown + Q_residual # 展开形式: # result = k_0*(ru-ru0)*(2+k1) + (small - large*(1+k1)) + adcdiff*k1*k2 ``` 系数 `(2+k1)` 说明: - 数字"2"代表Run-up阶段的双极性(正向+负向)贡献 - `k1` 是对斜率不对称性的修正 #### 6.3.4 校准系数与基准电压的关系 现在解释三个关键校准系数 `k1`, `k2`, `sf` 如何建立与基准电压的联系。 **k1校准:建立斜率之间的比例** ``` 物理意义: k1 = (V_ref_large / V_ref_small) - 1 ≈ V_ref_large / V_ref_small (因为 V_ref_large >> V_ref_small) 典型值: k1 ≈ 0.05 (大参考是小参考的约20倍) 计算过程: 1. 施加固定长度的小参考脉冲(5 cycles和25 cycles) 2. 测量切换到慢速参考后的放电时间差 3. 时间差 ∝ (25-5) * k1 4. 计算: k1_raw = ΔT_slow / (20 * 3 * 128) 5. 存储: k1 = 1.0 / median(k1_values) ``` **k2校准:建立ADC与时间的关系** ``` 物理意义: k2 = ADC_LSB / (时钟周期 * 小参考斜率) = 单位时间对应的ADC变化量 典型值: k2 ≈ 0.2 (每个时钟周期对应0.2个ADC LSB) 计算过程: 1. 在积分器平衡点附近施加微小脉冲 2. 测量脉冲前后的ADC读数变化 3. 变化量 = 脉冲长度 * k2 4. 计算: k2_raw = (ADC_up + ADC_down) / 2 / pulse_length 5. 存储: k2 = 4.0 / median(k2_values) ``` **sf校准:建立与基准电压的最终比例** ```python # 初始估计值 (基于硬件参数) sf0 = (2 - k1) * scale / (adcclock * 0.02) # scale: 参考电压(mV) = 6951.926 # adcclock * 0.02: 20ms周期的时钟数 = 240000 # sf0 ≈ 0.058 mV/单位 # 精确校准 (使用7V参考) # 在Mode C中测量: u1 = readADC(signal) # 被测信号读数 u2 = readADC(zero) # 0V读数 u3 = readADC(ref7v) # 7V参考读数 # 7V对应的读数差 reading_7v = u3 - u2 # 计算比例因子 sf = scale / reading_7v # mV/单位 # 典型值: sf ≈ 0.058 (如果scale=6951.926, reading_7v≈120000) ``` #### 6.3.5 最终电压计算 - 模式对比 **Mode A/B/E (双读数模式):** ```python # 测量被测信号和0V u1 = readADC() # 被测信号 u2 = readADC() # 0V (Auto-Zero) # 差值对应净电压 du = u1 - u2 # 转换为电压 voltage = du * sf # 物理意义: # du 是相对于0V的读数差 # sf 建立了读数差与电压的比例 # voltage 就是被测电压值(mV) ``` **Mode C (三读数模式 - 推荐):** ```python # 三个连续测量 u1 = readADC() # 被测信号 u2 = readADC() # 0V (GNDS) u3 = readADC() # 7V参考 # 使用参考归一化消除增益误差 du = (u1 - u2) / (u3 - u2) # 转换为电压 voltage = du * scale # 物理意义: # (u1-u2)/(u3-u2) = 被测电压/7V 的比例 # 分子分母相除消除了系统增益误差 # scale = 7V (实际值6951.926mV) # 结果直接就是被测电压(mV) ``` **Mode D (四读数模式):** ```python # 用于比例测量或差分测量 u1 = readADC(ch1) u2 = readADC(ch2) u3 = readADC(zero) u4 = readADC(ref7v) du = (u2 - u3) / (u4 - u3) # 归一化 voltage = du * scale ``` #### 6.3.6 校准链总结 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 校准链建立过程 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 物理基准 (7V参考) │ │ │ │ │ ▼ │ │ K1校准: 大参考/小参考比值 ────────────────────────────────┐ │ │ │ │ │ │ ▼ ▼ │ │ K2校准: ADC/时间关系 ────────────────────────────────┐ 使用 │ │ │ │ k1 │ │ ▼ ▼ │ │ SF校准: 读数/电压比例 ◄──────────────────────────── 读数转换 │ │ │ │ │ ▼ │ │ 最终电压 = 读数 × sf (或 归一化读数 × scale) │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` #### 6.3.7 数值示例 假设测量一个3.5V的电压: ```python # 已知校准参数 k1 = 0.05 # 斜率比 k2 = 0.2 # ADC增益 sf = 0.058 # 比例因子 mV/单位 scale = 6951.926 # 参考电压 mV # 固件返回的原始数据 ru = 1500 # Run-up计数 small = 5000 # 小参考周期 large = 3000 # 大参考周期 adcdiff = 100 # ADC差值 # 计算步骤 ru0 = 1175 # 预计算的零点 Q_runup = 102 * (1500 - 1175) * (2 + 0.05) = 67965 Q_rundown = 5000 - 3000 * 1.05 = 1850 Q_residual = 100 * 0.05 * 0.2 = 1 result = 67965 + 1850 + 1 = 69816 # 转换为电压 voltage = 69816 * 0.058 = 4049 mV ≈ 4.05V # 在Mode C中验证: # 假设读数为: u1=69816, u2=0, u3=119861 (对应7V) du = (69816 - 0) / (119861 - 0) = 0.5825 voltage = 0.5825 * 6951.926 = 4050 mV ≈ 4.05V ``` --- ## 7. 数据通信协议 ### 7.1 UART通信格式 - **波特率**: 9600 bps - **数据位**: 8位 - **停止位**: 1位 - **校验**: 无 ### 7.2 数据包格式 **同步标记** (第1-2字节): ``` 0xFF 0xFE (254) - Mode A/E 同步 0xFF 0xFB (251) - Mode B 同步 0xFF 0xFA (250) - Mode C 同步 0xFF 0xF8 (248) - Mode D 同步 0xFF 0xF1 (241) - DA Test 同步 0xFF 0xFD (253) - K1校准数据 0xFF 0xFC (252) - K2校准数据 ``` **数据字段** (小端格式): | 字节 | 内容 | |-----|------| | 0-1 | Run-up计数 (coutA) | | 2-3 | 小参考周期数 (t1slow - t1neg) | | 4-5 | 大参考周期数 (t1neg + t1slow - t1pos) | | 6-7 | 辅助ADC读数 | | 8-9 | 残余电荷ADC (后) | | 10-11 | 残余电荷ADC (前) | ### 7.3 控制命令 ```python # 键盘命令映射 'A' - Mode A (自动归零) 'B' - Mode B (双速度线性度测试) 'C' - Mode C (3读数模式) 'D' - Mode D (4读数模式) 'E' - Mode E (双通道伪差分) 'L' - 校准循环 (K1+K2) 'K' - 旧版校准 'I' - 无限Run-up测试 'P'-'W' - 选择Run-up版本 (0-7) '0'-'7' - 选择MUX通道 'M' - 降低速度(双倍积分时间) 'F' - 恢复快速模式 'G' - DA测试 'Z' - 设置比例因子 'X' - 退出 ``` --- ## 8. 关键性能参数 ### 8.1 时序参数 ```asm #define F_CPU 12000000 ; 12MHz MCU时钟 #define BAUD 9600 ; UART波特率 #define xdelay 12 ; 额外延时 ; Run-up循环长度 ru_loop_length = F_CPU / 50 / (204 + 12 * xdelay) ≈ 600 ; 约20ms一个转换周期 ``` ### 8.2 各模式周期时间 | 模式 | 单循环时间 | 20ms PLC循环数 | 积分时间 | |-----|-----------|--------------|---------| | P (Fast) | 51 cycles | ~4700 | ~8ms | | Q (Normal) | 102 cycles | ~2350 | ~16ms | | W (Slow) | 204 cycles | ~1175 | ~20ms | ### 8.3 分辨率计算 ``` Run-up分辨率: ~2350 counts × 2(正负) ≈ 4700 levels Run-down分辨率: 取决于时钟计数 残余电荷分辨率: 10-bit ADC × K2校准 总分辨率可达: 20+ bits ``` --- ## 9. 代码架构总结 ### 9.1 固件模块结构 ``` main.asm ├── 初始化 │ ├── 端口配置 │ ├── UART初始化 │ ├── ADC初始化 │ └── Timer配置 ├── 主控制循环 (control) │ └── 命令解析分发 ├── Run-up模块 (8种变体) │ ├── runup_P3f (高速) │ ├── runup_P3n (正常) │ ├── runup_P3s (短脉冲) │ ├── runup_P4nV (4步) │ ├── runup_PdV (虚拟) │ ├── runup_P40V (4步零) │ ├── runup_P3l (长脉冲) │ └── runup_P3sl (慢速) ├── Run-down模块 │ ├── rundown (三阶段下降) │ └── rundown_data (数据保存) ├── 测量模式 │ ├── mslopeA (自动归零) │ ├── mslopeB (双速度) │ ├── mslopeC (3读数) │ ├── mslopeD (4读数) │ └── mslopeE (伪差分) ├── 校准模块 │ ├── K1_measure (斜率比) │ ├── K2_measure (ADC增益) │ └── TestK2 (备用K2) └── 辅助功能 ├── UART通信 ├── ADC读取 └── 延时函数 ``` ### 9.2 上位机模块结构 ``` control.py ├── ADC类 │ ├── 串口通信 │ ├── 数据读取 (readADC) │ ├── 校准处理 │ │ ├── skalefactor1 (K1) │ │ └── skalefactor2 (K2+SF) │ ├── 测量模式 │ │ ├── read2 (2读数) │ │ ├── read3 (3读数) │ │ └── read4 (4读数) │ └── 键盘控制 └── main() - 主程序 ``` --- ## 10. 关键公式汇总 ### 10.1 多斜积分结果公式 ``` Nrunup = Run-up计数 (正向参考相位数) Tsmall = 小参考(负)周期数 Tlarge = 大参考(正)周期数 ADCres = 残余电荷ADC差值 (adc2 - adc1) Result = k0 × (Nrunup - N0) × (2 + k1) + Tsmall - Tlarge × (1 + k1) + ADCres × k1 × k2 其中: - k0: Run-up步长时间 (cycles) - k1: 慢斜率/小斜率比 - k2: ADC LSB/时钟周期 - N0: Run-up零点偏移 ``` ### 10.2 校准公式 **K1 (斜率比)**: ``` k1_raw = (Tslow_long - Tslow_short) / (3 × (Plong - Pshort) × 128) k1 = 1 / median(k1_values) ``` **K2 (ADC增益)**: ``` k2_raw = (ADC_up_step + ADC_down_step) / 2 k2 = 4 / median(k2_values) ``` **比例因子SF**: ``` SF = Vref / (Reading_7V - Reading_0V) = 6951.926 mV / (u3 - u2) ``` ### 10.3 电压输出公式 ``` Mode A/B/E: Vout = (u1 - u2) × SF Mode C/D: Vout = (u1 - u2) / (u3 - u2) × Scale ``` --- ## 11. 设计要点与技巧 ### 11.1 提高精度的关键技术 1. **电荷平衡**: 通过Run-up阶段的可变参考相位,使积分器输出始终保持在有限范围内 2. **多斜Run-down**: 使用三阶段下降(强→弱→慢),兼顾速度和分辨率 3. **残余电荷测量**: 用ADC测量最终残余电压,避免完美归零的需求 4. **连续自校准**: K1/K2/SF的持续更新,消除温漂和时漂 5. **同步采样**: 使用Timer OC1A触发ADC,确保时序精确 ### 11.2 噪声抑制措施 ```asm ; 1. 固定循环时间 (ADC_sync) ; 2. 多次平均 (K1: 128次, K2: 120次) ; 3. 中位数滤波 (Python端) ; 4. 延迟等待 (longdelay用于稳定) ``` ### 11.3 线性度优化 ```asm ; Mode B专门用于线性度测试 ; 交替使用两种Run-up速度,检测线性度误差 ``` --- ## 12. 使用流程建议 ### 12.1 首次上电 1. 固件自动执行初始K1/K2测量 2. 上位机连接后进入Mode C 3. 输入'Z'命令进行比例因子校准 4. 等待校准完成即可开始测量 ### 12.2 正常运行 ``` 1. Mode C循环测量 Signal→0V→7V 2. 上位机实时计算并显示电压值 3. 定期(或温度变化时)执行'L'命令更新K1/K2 4. 定期(或需要时)执行'Z'命令更新SF ``` ### 12.3 校准频率建议 | 参数 | 建议校准频率 | 触发条件 | |-----|------------|---------| | K1 | 每小时或温度变化5°C | 'L'命令 | | K2 | 每小时或温度变化5°C | 'L'命令 | | SF | 每天或温度变化10°C | 'Z'命令 | --- *文档生成时间: 基于源代码分析* *分析对象: ATmega多斜积分ADC固件 + Python上位机*