2核心项目
1. 高精度热导检测器(TCD)研制 电路负责人 涉密·部分脱敏
80万横向项目(与开元仪器合作,本人负责技术方案撰写与电路设计)。以氩气替代氦气作载气,通过热导率差异检测气体组分,面向元素分析仪的通用型热导检测器。负责:信号调理与采集电路全流程(方案设计、器件选型、仿真验证、PCB绘制与调试)+ 热敏电阻温度标定 + 自动测试平台开发。
32位ADC ADS1263
PGA855程控放大
惠斯通电桥
压控电流源
电压跟随器
铼钨丝(5%/25% Re)
NTC Steinhart-Hart标定
恒温控制 ±0.05°C
UART 460800 + 上位机
TINA噪声仿真
STM32F103固件
Python上位机(tcd_host)
✅ 核心指标:检测限 ≤150ppm(氩气)/≤15ppm(氦气) | 基线漂移 ≤±0.6mV/20min | 温度控制 ±0.05°C | 等效分辨率 ≥20位 | 响应时间 ≤300ms | NTC标定 RMS<1°C(Steinhart-Hart 三参数)
📐 系统框图:气路(载气+样气) → 热导池(铼钨丝电桥) ← 压控电流源激励 → 差压信号链(差分RC→电压跟随器→PGA855→抗混叠) → ADS1263 32位ADC → STM32F103(UART 460800) → PC上位机(tcd_host.py)
📊 实测平台与关键元件 R-T 特性
📷 热敏电阻标定测试平台:恒温干燥箱(自动控温 ±0.1°C)+ 数字万用表(高精度电阻测量)+ 测试电路板,通过 USB/RS232 与 PC 通讯,自动采集不同温度下的 NTC 阻值
📈 热导池关键元件 R-T 对比:铼钨丝 5%/25%Re-W(蓝线,线性,T=(R-138.5)/0.4419,工作于惠斯通电桥作为敏感元件)vs NTC 热敏电阻(红点,指数非线性,Steinhart-Hart 1/T=A+B·lnR+C·(lnR)³,用于恒温块温度反馈)
🔑 关键代码 1:下位机命令解析与 ADC 采集(demo126x.c / ADS126X.c,STM32F103)
// demo126x.c:ASCII 命令解析(上位机通过 UART 460800 下发)
if (strncmp(cmd, "PGA=", 4) == 0) {
u8 v = (u8)parse_num(cmd + 4);
if (v==1||v==2||v==4||v==8||v==16||v==32) {
g_pga = v; ADS126X_SetPGA(g_pga); printf("OK\n");
} else printf("ERR\n");
}
else if (strcmp(cmd, "GO") == 0) { g_stream = 1; printf("OK\n"); }
else if (strcmp(cmd, "STOP") == 0) { g_stream = 0; printf("OK\n"); }
// 主循环:等待 DRDY → 读32bit → AVG均值 → 电压换算 → UART输出
raw = ADS126X_ReadADC1();
if (ADS126X_GetDRDYTimeoutCount() != g_last_timeout) continue; // DRDY超时丢弃
acc += raw; cnt++;
if (cnt < g_avg) continue; // 滑动均值
raw = (s32)(acc / g_avg);
if (g_mode == 0) printf("%.7f\n", ADS126X_DataToVoltage(raw));
else printf("%ld\n", (long)raw);
🔑 关键代码 2:上位机实时采集与绘图(tcd_host.py,Tkinter+matplotlib)
# tcd_host.py:后台串口线程按行接收 ADS1263 数据
class SerialThread(threading.Thread):
def run(self):
buf = b""
while not self.stop_evt.is_set():
chunk = self.ser.read(512)
buf += chunk
while b"\n" in buf:
line, buf = buf.split(b"\n", 1)
self.q.put(("line", line.decode().strip()))
# 主界面:下发 ADS1263 配置命令(PGA增益/采样率/滤波器/输入通道)
def _send_all_config(self):
for c in ["PGA={}".format(pga), "SPS={}".format(sps_idx),
"FILT={}".format(filt), "IN={},{}".format(pos, neg),
"MODE={}".format(mode), "AVG={}".format(avg)]:
self._send(c)
# 实时绘图:100 秒滚动窗口 + 增量统计(不重算全量)
def _update_plot(self):
if self.times and self.data:
self.line.set_data(list(self.times), list(self.data))
latest = self.times[-1]
self.ax.set_xlim(max(latest - WINDOW_SECONDS, 0.0), latest)
self.ax.autoscale_view(scalex=False, scaley=True)
self.canvas.draw_idle()
self.root.after(200, self._update_plot)
⚙️ 代码流程图:下位机固件 & 上位机
📋 左:STM32F103 固件(demo126x.c:LCD→UART 460800→ADS126X配置→process_command 解析 ASCII 命令→DRDY 读取 32bit→AVG 均值→printf 电压值);右:PC 上位机(tcd_host.py:Tkinter 扫串口→Connect→下发 PGA/SPS/FILT 命令→后台线程收数→matplotlib 100s 滚动曲线→统计/保存CSV);中间 UART 460800 传输 ASCII 命令与数据流
⚠️ 本项目涉及保密协议(开元仪器委托开发),电路细节、客户信息与核心参数已脱敏;以上仅展示可公开的方案架构、标定算法与测试结果。
2. 硬质合金内部缺陷检测系统 核心成员 研电赛国二 2025
高频超声与硬质合金内部微缺陷相互作用产生回波,检测位错/滑移带/微裂纹。基于 FPGA Spartan-7 + Zynq UltraScale + AD9643 @250MSPS 高速采集,频域滤波法实现希尔伯特变换解调,提取 DSB-SC 包络。
FPGA Spartan-7
Zynq UltraScale
AD9643 @250MSPS
14位ADC差分LVDS
IDDR边沿对齐
跨时钟域FIFO
FFT频域滤波
希尔伯特变换
DSB-SC解调
IFFT重构
Vivado时序约束
DDR4缓存
UART上传
✅ 检测能力:YG20 65×65×50mm 板材中可识别 Ф0.4mm×3mm 最小孔洞
📊 系统框图:激励发射(紫色)→ 回波接收(蓝色)→ FPGA高速采集与解调(绿色)
📷 实际测量夹具:硬质合金试件(YG20 65×65×50mm)夹在金属夹具中(红色矩形标注缺陷检测区),两侧同轴连接器接超声换能器
📷 检测装置整机:左为机箱外观(显示屏+3个BNC接口),右为内部 FPGA 开发板(Xilinx Spartan-7/Zynq + ADC FMC 子卡)
📈 ① 真实ADC采集数据(来自 对比波形.mat 的 iladata5,plot_test.m 图1):250MSPS ADC (AD9643) 采集的超声回波原始数据(基线已扣除)
📈 ② 叠加对比:ADC 原始信号(深蓝,基线扣除)与 iladata1 解调包络(红,抽点0值剔除 + 平滑后归一化到同一量级),包络精确对应回波振荡
⚙️ 代码流程图:FPGA 高速采集与解调
📋 FPGA 循环:上电→初始化 PLL/ADC/FIFO → 发射激励脉冲 → AD9643 @250MSPS 采集回波 → IDDR 差分对齐 → 跨时钟域 FIFO → FFT → 频域滤波 → IFFT 希尔伯特解调(DSB-SC) → 缺陷判断 → UART 上传
🔑 关键代码 1:Vivado 时序约束(PIN.xdc)
# 250MHz ADC数据时钟定义
create_clock -period 4.000 -name adc_dco_p -waveform {0.000 2.000} [get_ports adc_dco_p]
# 14位ADC差分数据输入延迟约束(核心:保证亚稳态与建立/保持时间)
set_input_delay -clock [get_clocks adc_dco_p] -rise -min -add_delay -0.700 adc_data_p[*]
set_input_delay -clock [get_clocks adc_dco_p] -clock_fall -fall -min -add_delay -0.700 adc_data_p[*]
set_input_delay -clock [get_clocks adc_dco_p] -clock_fall -fall -max -add_delay 1.300 adc_data_p[*]
set_input_delay -clock [get_clocks adc_dco_p] -rise -max -add_delay 1.300 adc_data_p[*]
# 异步时钟组:避免跨时钟域假路径
set_clock_groups -name adc_clk_group -asynchronous -group [get_clocks adc_dco_p]
set_clock_groups -name sys_clk_group -asynchronous -group [get_clocks CLK_200_P]
🔑 关键代码 2:FPGA 顶层 ADC 接口(top.v)
// 14位ADC差分信号 → 单端数据对齐(IDDR)
genvar i;
generate
for (i = 0; i < 14; i = i + 1) begin:IBUF_IDDR_DATAS
IBUFDS // LVDS 差分转单端 ( .DIFF_TERM("TRUE"), .IOSTANDARD("LVDS") )
IBUFDS_adc_data ( .O(receive_adc_data[i]), .I(adc_data_p[i]), .IB(adc_data_n[i]) );
IDDRE1 // DDR边沿对齐,输出A/B双相数据
IDDRE1_adc_data (
.Q1(adc_data_A_get[i]), // 上边沿采集
.Q2(adc_data_B_get[i]), // 下边沿采集
.D(receive_adc_data[i]),
.C(adc_clk_Acq), .CB(adc_clk_Acq), .R(1'b0)
);
end
endgenerate
// ADC数据基线校准:减去零点偏移
assign adc_data = adc_data_A_get - 14'd8100;
3. 信号分离装置 核心成员 全国电赛国一 2023
双路信号A和B经加法器合成混合信号C,通过分离电路恢复无失真信号A'和B',要求与原信号同频同相稳定显示。采用 STM32F407 + EP4CE10 异构双核架构:STM32 做 2048 点 FFT(196dB平顶窗)判别波形类型并计算频率/相位参数,经 GPIO 16位并行总线握手传输至 FPGA,FPGA 用 40-bit 相位累加器 + 双端口 ROM 双通道 DDS 重构波形。
STM32F407
EP4CE10
异构双核架构
1MHz ADC采样
CMSIS-DSP库
FFT N=2048
196dB平顶窗 (arm_hft196d)
谐波判别
GPIO 16-bit并行总线
握手通信(read_done)
ROM查表法DDS
40-bit双通道相位累加器
双端口ROM(正弦+三角)
PLL 125M/250M双时钟
125MSPS DAC
✅ 成果:生成信号与原信号在示波器同屏显示 1分钟内无肉眼可见漂移(40-bit相位累加器保证频率分辨率,不漂移)
📊 系统框图:信号A/B → 加法器 → STM32 FFT分析 → GPIO并行总线 → FPGA 双通道DDS重构 → DAC输出 A'/B'
📷 电赛现场:双示波器显示 A/B 与重构后 A'/B' 波形同屏对比(1分钟无肉眼可见漂移)
⚙️ 代码流程图:STM32 频域分析 → FPGA 波形重构
📋 左:STM32 循环(TIM2触发ADC2→DMA采2048点→196dB平顶窗→复数FFT→幅值谱→双峰搜索→判正弦/三角→算频率字+相位字);右:FPGA 循环(GPIO并行接收→data_get_from_32解析→40-bit相位累加→双端口ROM查表→波形选择→125MSPS DAC 输出);中间 16-bit 并行+clk+read_done 传递
🔑 关键代码 1:STM32 - ADC采集 + 196dB平顶窗 + FFT波形判别
// main.c:TIM2触发ADC2,DMA采集2048点后做FFT分析
#define SAMP_NUM 2048
float fft_inputbuf1[SAMP_NUM * 2];
float fft_outputbuf1[SAMP_NUM];
float hanming[SAMP_NUM]; // 196dB汉明平顶窗系数
// 初始化:生成196dB平顶窗,启动DMA采集
arm_hft196d_f32(hanming, SAMP_NUM);
HAL_ADC_Start_DMA(&hadc2, (uint32_t *)ADC1_BUFF_16, SAMP_NUM);
HAL_TIM_Base_Start(&htim2); // TIM2触发ADC采样
// FFT分析:加窗 → 复数FFT → 求幅值 → 基波/谐波峰值判别波形
void fft_math_find(void) {
for (int i = 0; i < SAMP_NUM; i++) {
fft_inputbuf1[i * 2] = ADC1_BUFF_16[i] * 3.3f / 4096.0f; // 实部:ADC码→电压
fft_inputbuf1[i * 2 + 1] = 0; // 虚部
fft_inputbuf1[i * 2] = fft_inputbuf1[i * 2] * hanming[i]; // 加196dB平顶窗抑制频谱泄露
}
arm_cfft_f32(&arm_cfft_sR_f32_len2048, fft_inputbuf1, 0, 1); // 2048点FFT
arm_cmplx_mag_f32(fft_inputbuf1, fft_outputbuf1, SAMP_NUM); // 求幅值谱
sine_val[1] = findMax(fft_outputbuf1, SAMP_NUM); // 第一基波峰值
sine_val[11] = fft_outputbuf1[sine_val[1]];
// 消去第一基波后找第二路信号峰值(谐波抑制)
for (int i = 0; i < 4; i++) { fft_outputbuf1[sine_val[1]+i] = 0; fft_outputbuf1[sine_val[1]-i] = 0; }
sine_val[2] = findMax(fft_outputbuf1, SAMP_NUM);
sine_val[12] = fft_outputbuf1[sine_val[2]];
// 基波幅值>420 → 正弦波;否则三角波(正弦基波能量高)
type1 = (sine_val[11] > 420) ? 1 : 2; // 1=正弦 2=三角
type2 = (sine_val[12] > 420) ? 1 : 2;
}
🔑 关键代码 2:FPGA - 40-bit相位累加器 + 双端口ROM双通道DDS
// DDS_module.v:PLL输出125M/250M双时钟,40-bit相位累加器
wire PLL_125M, PLL_250M;
reg [39:0] Fcnt, Fcnt1; // 双通道40位相位累加器(频率分辨率 125M/2^40)
reg [31:0] Fword, Fword1; // 频率控制字(来自STM32)
// 相位累加:每个PLL_125M时钟步进Fword,40位相位保证不漂移
always @(posedge PLL_125M or negedge reset_n)
if (!reset_n) Fcnt <= 40'd0;
else if (~d2 && d1) Fcnt <= 16'd0; // 相位对齐触发
else Fcnt <= Fcnt + Fword;
// 取相位累加器高13位作ROM地址(双端口ROM:正弦+三角)
assign rom_addr = Fcnt[39:27] + Pword; // 通道A
assign rom_addr1 = Fcnt1[39:27] + Pword1; // 通道B
// 双端口ROM查表:正弦ROM + 三角ROM,125MSPS DAC同步输出
ROM_2PORT_SIN_IP u1 ( .address_a(rom_addr), .address_b(rom_addr1),
.clock_a(PLL_125M), .clock_b(PLL_125M),
.q_a(wave_data1_1), .q_b(wave_data1_2) );
ROM_2PORT_TRANGL_IP u2 ( .address_a(rom_addr), .address_b(rom_addr1),
.clock_a(PLL_125M), .clock_b(PLL_125M),
.q_a(wave_data_r2_1), .q_b(wave_data_r2_2) );
// 波形选择:STM32下发的flag决定输出正弦/三角
always @(posedge PLL_250M or negedge reset_n)
if (!reset_n) wave_data_r <= 14'd0;
else if (flag2_reg==2'd1) wave_data_r <= wave_data1_1; // 正弦
else if (flag2_reg==2'd2) wave_data_r <= wave_data_r2_1; // 三角