一、项目主题与项目介绍
1.1 项目主题
本次设计以STM32F103C8T6为主控芯片,完成一款集-3.3V~+3.3V 连续可调直流电源、DDS 信号发生器、数字示波器、可编程 PWM 波输出、于一体的综合电子测量与调试仪器,可满足电子实训、电路调试、信号测量、偏置电压供给等多场景使用需求。
1.2 核心功能介绍
- -3.3V~+3.3V连续可调直流电源
由 MCP4725 DAC 输出基准电压,经运放电平抬升与放大,配合负压供电电路,实现正负对称、连续可调的直流稳压输出,可作为运放偏置、基准电压、小信号调试电源使用。 - DDS 信号发生器
采用 AD9834 芯片实现正弦波、三角波、方波输出,频率与幅值可通过按键程控调节,满足基础信号激励需求。 - 数字示波器功能
对外部输入信号进行分压、程控放大、ADC 采样,在 TFT 屏上实时绘制波形,支持时基与增益调节。 - PA8 引脚可编程 PWM 输出
利用 STM32 定时器 TIM1_CH1 通道,在 PA8 口输出频率、占空比均可调的方波 PWM 信号,可用于电机调速、LED 调光、数字调制等场景。 - 人机交互与辅助功能
5个独立按键实现模式切换与参数调节,TFT LCD 实时显示波形、电压、频率、占空比等参数,具备硬件自检功能。
二、使用到的所有硬件详细介绍
2.1 主控核心模块
- STM32F103C8T6:Cortex-M3 内核,最高 72MHz 主频,64KB Flash,20KB RAM;负责按键扫描、屏幕刷新、外设通信、AD9834 配置、ADC 采样、DAC 输出、PWM 波形生成、模式调度等全部逻辑控制。
2.2 信号发生器模块
- AD9834:DDS 直接数字频率合成芯片,支持正弦 / 三角 / 方波,最高输出频率 12.5MHz,SPI 通信控制;
- OPA2197:高精度运算放大器,用于信号缓冲、放大、滤波;
- CD74HC4051:8 通道模拟开关,实现信号幅值多档位切换;
- 无源 RC 低通滤波器:滤除 DDS 输出的高频量化噪声。
2.3 示波器采集模块
- MCP41010:10kΩ 数字电位器,SPI 控制,实现程控增益调节;
- LMV331:高速电压比较器,用于信号边沿触发;
- TLP172GM:模拟开关,实现交直流耦合切换;
- 电阻分压网络:1/10 量程衰减,扩展测量范围;
- 板载 12 位 ADC:完成模拟信号数字化采集。
2.4 PA8 PWM 输出硬件
- GPIO PA8:复用为 TIM1_CH1 通道,推挽输出;
- 限流电阻:保护 IO 口,防止短路损坏;
- 输出排针:引出 PWM 信号,方便外部测量与使用。
2.5 -3.3V~+3.3V可调电源模块
- MCP4725:12 位 I2C 接口 DAC,输出 0~3.3V 基准电压;
- OPA2197:精密运放,构成电平平移与放大电路,将单极性 DAC 电压转换为双极性 ±4.4V;
- ICL7660:电荷泵负压芯片,提供 - 5V 负压供电,为双极性输出提供基础;
- 精密电阻网络:设定放大倍数与电平偏移比例,保证输出范围精准落在 - 4.4V~+4.4V;
- 滤波电容:降低输出纹波,提升电压稳定性。
2.6 系统电源模块
- LM1117-5.0:将 12V 输入转为 + 5V,为运放、模拟芯片供电;
- LM1117-3.3:将 + 5V 转为 + 3.3V,为 STM32、数字芯片供电;
- ICL7660:生成 - 5V,供给运放负电源,支持双极性信号处理;
- 滤波与去耦电容:抑制电源噪声,保证模拟电路精度。
2.7 人机交互模块
- 独立按键 5 个:模式切换、步进切换、步进选择、数值加 / 减;
- TFT LCD 显示屏:实时显示波形、电压值、频率、占空比、工作模式。
三、方案框图和项目设计思路介绍
3.1 系统整体方案框图

3.2 项目整体设计思路
- 电源优先设计原则
先完成系统固定电源(+5V/+3.3V/-5V)设计,再基于此搭建 -3.3V~+3.3V 可调电源,保证所有模拟电路拥有稳定、低噪声供电。 - 模块化硬件划分
按功能划分为:主控最小系统、信号发生、信号采集、PWM 输出、可调电源、人机交互六大模块,各模块独立布线、独立调试,减少相互干扰。 - 软件状态机调度
系统采用多模式互斥运行机制,同一时刻只运行一种主功能,通过按键切换:信号源→示波器→PWM 输出→可调电源→自检,避免资源冲突。
四、原理图和 PCB 展示及介绍
4.1 原理图结构与功能分区使用KiCad进行原理图和PCB绘画
本次原理图共分为 3 大页面,所有模块清晰划分:
- 第一页:主控与系统电源
包含 STM32 最小系统、复位电路、时钟电路、12V 输入、LM1117-5V/3.3V、ICL7660 负压电路和MCP4725 DAC + 运放双极性转换电路,实现 -3.3V~+3.3V 输出;是整个系统的供电与控制核心。 -

- 第二页:DDS 信号发生器
AD9834 及其外围电路、RC 滤波电路、运放放大电路、模拟开关档位切换电路。
- 第三页:示波器采集通道
交直流耦合切换、电阻衰减、MCP41010 程控放大、比较器触发、ADC 采样输入电路。
4.2 PCB 打版版本与重打原因
本次项目共打样3 版 PCB,迭代过程如下:
(1)V1.0 版本:2 层板
- 设计目标:验证基础功能可行性;
- 存在问题:
① 模拟地与数字地完全共地,电源噪声严重,示波器波形杂波大;
② 可调电源模块布线过密,-5V 与 + 5V 线间距过小,出现电压漂移、输出不对称;
③ AD9834 高频信号线无阻抗匹配,10MHz 以上波形失真;
④ PWM 输出线靠近模拟电路,干扰可调电压精度; - 结论:功能可勉强实现,但性能差,无法满足稳定使用要求,必须重打。
(2)V2.0 版本:4 层板
- 叠层结构:顶层信号 → 内层地层 → 内层电源层 → 底层信号;
- 优化内容:
① 严格分割模拟地与数字地,单点共地,降低串扰;
② 可调电源模块单独布局,远离高频信号线,增加滤波电容;
③ AD9834 布线做等长与 50Ω 阻抗匹配;
④ PWM 输出增加 RC 滤波,布线远离运放与 DAC; - 存在问题:可调电源零点偏移仍偏大,-3.3V~+3.3V端线性度不一致,运放反馈电阻布局不合理。
(3)V3.0 版本:最终定型 4 层板
- 优化内容:
① 微调可调电源运放反馈电阻位置与布线,提升正负电压对称性;
② 增加多处去耦电容,进一步降低电源纹波;
③ 优化接口丝印与测试点,方便调试与测量;
④ 修复 V2.0 中轻微的线宽与间距问题,提升可靠性; - 最终效果:所有功能稳定,可调电源精度高,波形无明显噪声,PWM 与各模块互不干扰。


五、调试软件、软件流程图和代码位置说明
5.1 调试与开发软件
- 开发环境:Keil MDK v5(ARMCC V5.06 build 528)
- 下载调试器:ST-Link V2
- 硬件调试工具:数字示波器、万用表、台式直流电源
- 通信协议:SPI(AD9834、MCP41010)、I2C(MCP4725)、ADC、TIM1(PWM)
5.2 详细软件执行流程图
5.3 代码存放位置说明
5个按键菜单逻辑代码,按键1切换模式,按键2切换调节对象,在DDS发生器可以自检,按键3各种模式的步进调节,按键4和5实现加减
void Key_Process(void) {
//自检模式按键逻辑
if(self_test_running) {
// 只检测按键2
if(KEY_Scan(2) == KEY_PRESSED) {
// 退出自检模式
SelfTest_Exit();
// 将调节项切换到频率(退出后的默认选项)
dev_params.curr_adj = ADJ_FREQ;
dev_params.step_gear = 0;
LCD_Display_All();
delay_ms(100);
}
// 其他按键不做任何响应,直接返回
return;
}
// 示波器模式独立按键处理
//static uint8_t osc_adj_mode = 0; // 0:增益调节, 1:时基调节
if (dev_params.curr_mode == MODE_OSCILLOSCOPE) {
// KEY2: 切换调节模式(增益 <-> 时基)
if (KEY_Scan(2) == KEY_PRESSED) {
osc_adj_mode = !osc_adj_mode;
LCD_Display_All();
delay_ms(100);
return;
}
// KEY3: 步进选择(仅增益模式有效,时基模式可固定步进或循环档位)
if (KEY_Scan(3) == KEY_PRESSED) {
if (osc_adj_mode == 0) {
step_index = (step_index + 1) % STEP_COUNT;
} else {
timebase_index = (timebase_index + 1) % TIMEBASE_COUNT;
timebase = timebase_steps[timebase_index];
// 重新配置 ADC 采样率以实现时基变化(见下文)
SetSampleRateByTimebase(timebase);
}
LCD_Display_All();
delay_ms(100);
return;
}
// KEY4: 增加当前调节项的值
if (KEY_Scan(4) == KEY_PRESSED) {
if (osc_adj_mode == 0) {
float step = step_values[step_index];
float new_gain = current_gain + step;
SetGainAndUpdate(new_gain);
} else {
// 时基增加(时间窗口变大,采样率降低)
if (timebase_index < TIMEBASE_COUNT-1) timebase_index++;
timebase = timebase_steps[timebase_index];
SetSampleRateByTimebase(timebase);
}
LCD_Display_All();
delay_ms(100);
return;
}
// KEY5: 减少当前调节项的值
if (KEY_Scan(5) == KEY_PRESSED) {
if (osc_adj_mode == 0) {
float step = step_values[step_index];
float new_gain = current_gain - step;
SetGainAndUpdate(new_gain);
} else {
if (timebase_index > 0) timebase_index--;
timebase = timebase_steps[timebase_index];
SetSampleRateByTimebase(timebase);
}
LCD_Display_All();
delay_ms(100);
return;
}
}
// KEY1 模式切换保留原有功能,交给通用逻辑
float step = Get_Curr_Step();
if(KEY_Scan(1) == KEY_PRESSED) {
// 保存旧模式
WorkMode old_mode = dev_params.curr_mode;
dev_params.curr_mode = (WorkMode)((dev_params.curr_mode % 6) + 1);
// 如果从示波器模式切换到其他模式,停止示波器ADC
if(old_mode == MODE_OSCILLOSCOPE && dev_params.curr_mode != MODE_OSCILLOSCOPE) {
OSC_ADC_DMA_Stop();
}
// 如果从其他模式切换到示波器模式,初始化示波器ADC
if(old_mode != MODE_OSCILLOSCOPE && dev_params.curr_mode == MODE_OSCILLOSCOPE) {
OSC_ADC_DMA_Init();
}
dev_params.curr_adj = Filter_AdjTarget(dev_params.curr_mode, dev_params.curr_adj);
dev_params.step_gear = 0;
Update_DDS_Hardware();
LCD_Display_All();
delay_ms(100);
}
if(KEY_Scan(2) == KEY_PRESSED) {
AdjTarget next_adj;
switch(dev_params.curr_mode) {
case MODE_POWER:
next_adj = ADJ_POWER;
break;
case MODE_SINE_WAVE:
case MODE_TRIANGLE_WAVE:
// 正弦波/三角波:频率 -> 增益 -> 自检 -> 频率
if(dev_params.curr_adj == ADJ_FREQ)
next_adj = ADJ_GAIN;
else if(dev_params.curr_adj == ADJ_GAIN)
next_adj = ADJ_SELF_TEST;
else if(dev_params.curr_adj == ADJ_SELF_TEST)
next_adj = ADJ_FREQ;
else
next_adj = ADJ_FREQ;
break;
case MODE_SQUARE_WAVE:
// 方波:只允许频率和增益循环(无自检)
if(dev_params.curr_adj == ADJ_FREQ)
next_adj = ADJ_GAIN;
else if(dev_params.curr_adj == ADJ_GAIN)
next_adj = ADJ_FREQ;
else
next_adj = ADJ_FREQ;
break;
case MODE_PWM:
next_adj = (dev_params.curr_adj == ADJ_FREQ) ? ADJ_DUTY : ADJ_FREQ;
break;
case MODE_OSCILLOSCOPE:
default:
next_adj = ADJ_NONE;
break;
}
// 如果即将退出自检模式,调用退出函数
if(dev_params.curr_adj == ADJ_SELF_TEST && next_adj != ADJ_SELF_TEST) {
SelfTest_Exit();
}
// 如果即将进入自检模式,调用进入函数
if(next_adj == ADJ_SELF_TEST && dev_params.curr_adj != ADJ_SELF_TEST) {
SelfTest_Enter();
}
dev_params.curr_adj = next_adj;
dev_params.step_gear = 0;
LCD_Display_All();
delay_ms(100);
}
if(KEY_Scan(3) == KEY_PRESSED) {
if(dev_params.curr_adj == ADJ_GAIN) {
// 仅当处于 DDS 波形模式时,才允许修改增益
if(dev_params.curr_mode >= MODE_SINE_WAVE && dev_params.curr_mode <= MODE_SQUARE_WAVE) {
dev_params.gain_val = (dev_params.gain_val % 3) + 1;
}
// 如果不在波形模式,不执行任何操作(或可自动切换调节项,根据需要)
} else {
dev_params.step_gear++;
}
LCD_Display_All();
delay_ms(100);
}
if(KEY_Scan(4) == KEY_PRESSED) {
switch(dev_params.curr_adj) {
case ADJ_POWER:
// 你的原逻辑
dev_params.power_val += step;
break;
case ADJ_FREQ:
dev_params.freq_val += (u32)step;
// 更新 DDS 频率
if(dev_params.curr_mode >= MODE_SINE_WAVE && dev_params.curr_mode <= MODE_SQUARE_WAVE) {
AD9834_Set_Freq(FREQ_0, dev_params.freq_val);
}
if(dev_params.curr_mode == MODE_PWM)
PWM_Update(dev_params.freq_val, dev_params.duty_val);
// 如果当前处于自检模式,自动配置 ADC 采样率
if(self_test_running) {
ADC_AutoConfigByFreq(dev_params.freq_val);
}
break;
case ADJ_DUTY:
// 你的原逻辑
dev_params.duty_val += step;
// 仅新增:PWM模式下更新占空比(不影响其他模式)
if(dev_params.curr_mode == MODE_PWM) {
PWM_Update(dev_params.freq_val, dev_params.duty_val);
}
break;
case ADJ_GAIN:
dev_params.gain_val = (dev_params.gain_val % 3) + 1;
if(dev_params.curr_mode >= MODE_SINE_WAVE &&
dev_params.curr_mode <= MODE_SQUARE_WAVE) {
AD9834_SetGain(gain_map[dev_params.gain_val - 1]);
}
break;
case ADJ_NONE:
break;
}
// 你的原逻辑
Limit_Params();
// ========== 新增:Power模式更新输出电压 ==========
if(dev_params.curr_mode == MODE_POWER) {
Power_Update(); // 调用电源更新函数,输出对应电压
}
LCD_Display_All();
delay_ms(100);
}
if(KEY_Scan(5) == KEY_PRESSED) {
switch(dev_params.curr_adj) {
case ADJ_POWER:
// 你的原逻辑
dev_params.power_val -= step;
break;
case ADJ_FREQ:
dev_params.freq_val -= (u32)step;
// 更新 DDS 频率
if(dev_params.curr_mode >= MODE_SINE_WAVE && dev_params.curr_mode <= MODE_SQUARE_WAVE) {
AD9834_Set_Freq(FREQ_0, dev_params.freq_val);
}
if(dev_params.curr_mode == MODE_PWM)
PWM_Update(dev_params.freq_val, dev_params.duty_val);
// 如果当前处于自检模式,自动配置 ADC 采样率
if(self_test_running) {
ADC_AutoConfigByFreq(dev_params.freq_val);
}
break;
case ADJ_DUTY:
// 你的原逻辑
dev_params.duty_val -= step;
// 仅新增:PWM模式下更新占空比(不影响其他模式)
if(dev_params.curr_mode == MODE_PWM) {
PWM_Update(dev_params.freq_val, dev_params.duty_val);
}
break;
case ADJ_GAIN:
// 你的原逻辑
dev_params.gain_val--;
if(dev_params.gain_val < 1) dev_params.gain_val = 3;
if(dev_params.curr_mode >= MODE_SINE_WAVE && dev_params.curr_mode <= MODE_SQUARE_WAVE)
AD9834_SetGain(gain_map[dev_params.gain_val - 1]);
break;
case ADJ_NONE:
break;
}
// 你的原逻辑
Limit_Params();
// ========== 新增:Power模式更新输出电压 ==========
if(dev_params.curr_mode == MODE_POWER) {
Power_Update(); // 调用电源更新函数,输出对应电压
}
LCD_Display_All();
delay_ms(100);
}
}
可调电源
// 核心电压输出函数
void MCP4725_SetVoltage(float display_val)
{
uint16_t dac_val;
if(display_val < -6.7f) display_val = -6.7f;
if(display_val > 6.7f) display_val = 6.7f;
// 精准线性映射:-3.3V~3.3V → 0~4095
dac_val = (uint16_t)((display_val + 3.3f) * 4095.0f / 9.9f);
if(dac_val > 4095) dac_val = 4095;
// 发送数据
I2C_Start();
I2C_WriteByte(0xC0);
I2C_WriteByte(0x40);
I2C_WriteByte((dac_val >> 4) & 0xFF);
I2C_WriteByte((dac_val << 4) & 0xF0);
I2C_Stop();
delay_us(50);
}
void Power_Update(void) {
// 目标输出电压:dev_params.power_val(范围-5V~+5V)
float v_out = dev_params.power_val;
// 反推DAC输出电压:V_dac = (V_out / Gain) + Offset
float v_dac = (v_out / POWER_GAIN) + POWER_OFFSET;
// 边界保护
if(v_dac < -6.7f) v_dac = -6.7f;
if(v_dac > 6.7f) v_dac = 6.7f;
// 设置DAC输出
MCP4725_SetVoltage(v_dac);
}
AD9834主要代码
// ===================== AD9834 写16位 =====================
static void AD9834_Write_16Bits(unsigned int data)
{
uint8_t i;
AD9834_FSYNC_0; // 拉低片选
for(i=0; i<16; i++)
{
// 在SCLK高电平期间设置数据
if(data & 0x8000)
AD9834_SDATA_1;
else
AD9834_SDATA_0;
// 产生下降沿,锁存数据
AD9834_SCLK_0;
// 增加短延时,确保数据稳定(根据总线速度调整)
for(volatile int j=0; j<5; j++); // 约0.5μs @72MHz
// 恢复高电平
AD9834_SCLK_1;
data <<= 1;
}
AD9834_FSYNC_1;
// 额外延时,确保FSYNC拉高后SCLK稳定
for(volatile int j=0; j<10; j++);
}
// ===================== 选择波形 =====================
void AD9834_Select_Wave(unsigned int initdata)
{
GPIO_SetBits(AD9834_RESET_PORT, AD9834_RESET_PIN);
delay_ms(1);
GPIO_ResetBits(AD9834_RESET_PORT, AD9834_RESET_PIN);
delay_ms(1);
AD9834_Write_16Bits(initdata);
delay_ms(5);
}
void AD9834_Reset(void)
{
AD9834_RESET_1;
delay_ms(10);
AD9834_RESET_0;
delay_ms(10);
}
// ************************* 芯片初始化 *************************
void AD9834_Init(void)
{
AD9834_GPIO_Init();
AD9834_Reset(); // 硬件复位,避免上电杂散输出
// 写入控制寄存器:复位状态,B28=1(28位频率分两次写入)
AD9834_Write_16Bits(AD9834_CTRL_RESET | AD9834_CTRL_B28);
delay_ms(10);
}
void AD9834_WakeUp(void)
{
// 清除复位位,保持B28=1,DAC和MCLK全功率工作
// 因SLEEP引脚硬件下拉,无需软件配置休眠相关位
AD9834_Write_16Bits(AD9834_CTRL_B28);
delay_ms(10);
}
// ===================== 设置频率(25MHz 计算)=====================
void AD9834_Set_Freq(unsigned char freq_number, unsigned long freq)
{
uint32_t freq_reg;
uint16_t freq_lsb, freq_msb;
// 频率上下限限制,避免超出滤波器和芯片规格
if(freq > 12500000) freq = 12500000;
if(freq < 1) freq = 1;
// 28位频率寄存器值计算:FreqReg = fOUT * 2^28 / fMCLK
freq_reg = (uint32_t)((float)freq * AD9834_2POW28 / AD9834_MCLK);
// 拆分28位数据为两个14位,分两次写入
freq_lsb = freq_reg & 0x3FFF;
freq_msb = (freq_reg >> 14) & 0x3FFF;
// 写入FREQ0频率寄存器
// AD9834_Write_16Bits(AD9834_CTRL_B28);
AD9834_Write_16Bits(AD9834_FREQ0_ADDR | freq_lsb);
AD9834_Write_16Bits(AD9834_FREQ0_ADDR | freq_msb);
// 写入相位寄存器0,默认0相位偏移
AD9834_Write_16Bits(AD9834_PHASE0_ADDR | 0x0000);
}
// ===================== 增益 =====================
void AD9834_SetGain(uint8_t gain)
{
GPIO_ResetBits(AD9834_GAIN_A1_PORT, AD9834_GAIN_A1_PIN);
GPIO_ResetBits(AD9834_GAIN_A0_PORT, AD9834_GAIN_A0_PIN);
switch(gain)
{
case 1:
GPIO_ResetBits(AD9834_GAIN_A1_PORT, AD9834_GAIN_A1_PIN);
GPIO_ResetBits(AD9834_GAIN_A0_PORT, AD9834_GAIN_A0_PIN);
break;
case 2:
GPIO_ResetBits(AD9834_GAIN_A1_PORT, AD9834_GAIN_A1_PIN);
GPIO_SetBits(AD9834_GAIN_A0_PORT, AD9834_GAIN_A0_PIN);
break;
case 3:
GPIO_SetBits(AD9834_GAIN_A1_PORT, AD9834_GAIN_A1_PIN);
GPIO_ResetBits(AD9834_GAIN_A0_PORT, AD9834_GAIN_A0_PIN);
break;
default:
AD9834_SetGain(1);
break;
}
delay_ms(5);
}
// ===================== 【新增】全波形切换:正弦 / 三角 / 方波 =====================
void AD9834_SetWaveMode(uint8_t mode)
{
uint16_t ctrl_reg = AD9834_CTRL_B28; // 必须保持B28=1
switch(mode)
{
case 0: // 正弦波
ctrl_reg &= ~(1<<1); // MODE=0
ctrl_reg &= ~(1<<5); // OPBITEN=0
break;
case 1: // 三角波
ctrl_reg |= (1<<1); // MODE=1
ctrl_reg &= ~(1<<5); // OPBITEN=0
break;
case 2: // 方波(必须这样配置!)
ctrl_reg &= ~(1<<1); // MODE=0
ctrl_reg |= (1<<5); // OPBITEN=1 方波使能
ctrl_reg |= (1<<3); // DIV2=1 让方波更标准
break;
}
AD9834_Write_16Bits(ctrl_reg);
}
示波器主要代码
void FREQ_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 时钟使能
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// PA1 浮空输入(外部已有上拉电阻)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 定时器基础配置:72MHz / 72 = 1MHz 计数频率
TIM_TimeBaseStructure.TIM_Period = 0xFFFF;
TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
// 输入捕获配置:通道2(PA1)
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2; // 关键修改
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0x0;
TIM_ICInit(TIM2, &TIM_ICInitStructure);
// 中断配置
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_ITConfig(TIM2, TIM_IT_CC2, ENABLE); // 使能通道2中断
TIM_Cmd(TIM2, ENABLE);
// 2. 配置 PA4 为 DAC 输出 (设置比较器阈值)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
DAC_InitTypeDef DAC_InitStructure;
DAC_InitStructure.DAC_Trigger = DAC_Trigger_None;
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_TriangleAmplitude_1;
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable; // 关键修改
DAC_Init(DAC_Channel_1, &DAC_InitStructure);
DAC_Cmd(DAC_Channel_1, ENABLE);
// 默认阈值 1.65V (中点)
FREQ_SetThreshold(0.1f);
}
uint32_t FREQ_Get(void)
{
return frequency;
}
void FREQ_SetThreshold(float voltage) {
if(voltage < 0) voltage = 0;
if(voltage > DAC_VREF) voltage = DAC_VREF;
uint16_t dac_value = (uint16_t)(voltage * 4095 / DAC_VREF * DAC_CALIB);
DAC_SetChannel1Data(DAC_Align_12b_R, dac_value);
}
static float current_gain = 1.0f; // 默认增益为 1.0(对应 D=255)
void OSC_Set_Gain(float gain) {
current_gain = gain;
}
float OSC_Get_Voltage_From_Value(uint16_t adc_val) {
float adc_voltage = (float)adc_val * 3.28f / 4095.0f/2.0f;
// 实际输入电压 = ADC电压 × 10倍前级补偿 ÷ 当前运放增益
return adc_voltage * 11.0f / current_gain;
// float adc_voltage = ((float)adc_val * 3.3f / 4095.0f)* 10.0f / current_gain;
}
void OSC_ADC_DMA_Stop(void)
{
ADC_SoftwareStartConvCmd(ADC1, DISABLE);
ADC_DMACmd(ADC1, DISABLE);
DMA_Cmd(DMA1_Channel1, DISABLE);
ADC_Cmd(ADC1, DISABLE);
}
static void SPI_Delay(void)
{
uint8_t i = 10; while(i--); // 简单延时,可根据需要调整
}
static void SPI_WriteByte(uint8_t byte)
{
for(uint8_t i=0; i<8; i++) {
if(byte & 0x80) GPIO_SetBits(MCP41010_MOSI_PORT, MCP41010_MOSI_PIN);
else GPIO_ResetBits(MCP41010_MOSI_PORT, MCP41010_MOSI_PIN);
GPIO_ResetBits(MCP41010_SCK_PORT, MCP41010_SCK_PIN);
SPI_Delay();
GPIO_SetBits(MCP41010_SCK_PORT, MCP41010_SCK_PIN);
SPI_Delay();
byte <<= 1;
}
}
// 初始化 TLP172 控制引脚
static void TLP172_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = TLP172_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(TLP172_PORT, &GPIO_InitStructure);
}
// 设置耦合模式(0=直流,1=交流)
void TLP172_SetCoupling(uint8_t mode)
{
if(mode == COUPLING_AC) {
GPIO_SetBits(TLP172_PORT, TLP172_PIN); // 高电平:交流耦合
} else {
GPIO_ResetBits(TLP172_PORT, TLP172_PIN); // 低电平:直流耦合
}
}
void MCP41010_Init(void)
{
SPI_GPIO_Init();
TLP172_Init();
// 默认设置为交流耦合模式
TLP172_SetCoupling(COUPLING_AC);
// 可选:设置一个默认的电位器阻值
// MCP41010_SetResistance(128); // 中间值
}
void MCP41010_SetResistance(uint8_t value)
{
GPIO_ResetBits(MCP41010_CS_PORT, MCP41010_CS_PIN);
SPI_WriteByte(0x11); // 写电位器0命令
SPI_WriteByte(value);
GPIO_SetBits(MCP41010_CS_PORT, MCP41010_CS_PIN);
SPI_Delay();
}
六、硬件功能展示图及说明
6.1 系统电源功能
- 输入:DC 12V;
- 固定输出:+5V、+3.3V、-5V,纹波<15mV,供电稳定,指示灯正常。
6.2 模式一 : -3.3V~+3.3V可调电源功能
- 输出范围:-3.3V~+3.3V 连续可调;
- 调节方式:按键步进调节,最小步进约 0.1V;
- 显示:TFT 屏实时显示当前输出电压;
- 特性:无明显漂移,带载能力满足小信号电路使用。
- 按键3步进更改,按键4和5加减


6.3模式二,三,四:信号发生器功能
分别输出正弦波、三角波、方波,频率范围宽,波形完整无明显失真,幅值可多级切换。
正弦波1000Hz,3000Hz,2倍增益,增益自检
按键2 频率调节-增益调节-自检,按键3步进选择,按键4和5加减




三角波3000Hz,2000Hz,2倍增益,增益自检
按键2 频率调节-增益调节-自检,按键3步进选择,按键4和5加减




方波2500Hz,1500Hz
按键3步进选择,按键4和5加减


6.4 模式五:PA8 PWM 输出功能
- 输出引脚:PA8;
- 频率范围:1Hz ~ 100kHz 可调;
- 占空比范围:0%~100% 可调;
- 波形:标准方波,上升沿陡峭,无明显干扰。
- 1500Hz,2500Hz,65%占空比,35%占空比
- 按键2 频率调节-占空比调节,按键3步进选择,按键4和5加减




6.5 模式六:示波器功能
可实时采集外部信号,自动适配量程,波形显示流畅,支持时基调节,可测量常见低频 / 中频信号。
按键2 采样时间调节-增益大小调节,按键3步进选择,按键4和5加减






七、设计中遇到的难题和解决方法
7.1 可调电源 - 3.3V~+3.3V 输出不对称、零点漂移
- 问题:输出电压在接近正负极限值时线性度差,零点不在 0V 附近;
- 原因:运放失调电压、负压供电不稳、电阻精度不足、DAC 基准误差;
- 解决方法:更换高精度电阻;软件加入 DAC 零点校准;优化 ICL7660 滤波电路;调整运放电路拓扑结构。
7.2 电源噪声导致示波器波形杂波多
- 问题:2 层板模拟地与数字地共地,开关噪声与数字噪声耦合进采集通道;
- 解决方法:改为 4 层板,分割模拟地与数字地,单点共地;在电源端口增加 LC 滤波电路。
7.3 PWM 输出干扰可调电源与采集电路
- 问题:PA8 输出高频 PWM 时,可调电压出现小幅波动,示波器波形有毛刺;
- 解决方法:PWM 输出端增加 RC 低通滤波;PWM 布线远离运放、DAC、ADC 等弱信号区域;铺地隔离。
7.4 AD9834 高频信号失真
- 问题:10MHz 以上波形出现振铃、畸变;
- 解决方法:优化 DDS 输出滤波电路;高频信号线做 50Ω 阻抗匹配;缩短布线长度。
7.5 模式切换时功能卡死
- 问题:快速切换模式时,屏幕卡死或输出异常;
- 解决方法:软件增加模式互斥锁,每次切换先关闭上一模式外设,再初始化新模式;加入延时消抖与状态判断。
八、心得体会与意见建议
8.1 设计心得体会
通过本次基于 STM32F103C8T6 的多功能电子测量仪器设计与制作,我完整经历了从方案论证、原理图绘制、PCB 设计打样、软硬件调试到系统联调的全过程,不仅巩固了专业知识,也切实提升了工程实践与问题解决能力,收获十分深刻。
在硬件设计上,我真正体会到电源与接地是模拟系统的核心。项目中需要同时支撑数字电路、DDS 高频信号、示波器弱信号采集以及 - 3.3V~+3.3V 双极性可调电源,初期因为地平面未合理分割、负压供电滤波不足,出现了噪声大、电压漂移、波形失真等问题。经过多版 PCB 迭代,从双层板改为四层板、分割模拟地与数字地、优化去耦与布线后,系统稳定性显著提升,让我对高速混合信号电路的布局布线、阻抗匹配、噪声抑制有了更直观的理解。同时,PA8 PWM 输出、可调电源、信号发生器、示波器多个功能模块共存,也让我学会了如何在有限空间内进行模块化布局,减少模块间相互干扰。
在软件与调试方面,本项目涉及 SPI、I2C、ADC、定时器等多种外设驱动,需要通过状态机实现多模式切换与互斥运行。在调试过程中,我逐渐养成了 “先电源、后模块,先硬件、后软件” 的排查思路,面对波形杂波、PWM 干扰、可调电压不对称等问题,能够结合万用表、示波器定位故障根源,而不是盲目修改代码。整个过程让我认识到,软硬件协同设计远比单一功能实现更重要,合理的程序架构可以有效弥补部分硬件缺陷,提升系统整体可靠性。
