FastBond挑战部分—-基于 STM32F103C8T6 的多功能电子仪器
该项目使用了KiCad软件、C语言,实现了STM32F103C8T6 的多功能电子仪器的设计,它的主要功能为:DDS 信号发生器、数字示波器、可编程 PWM 波输出、-3.3V~+3.3V 连续可调直流电源。
标签
测试
显示
DDS
ggb
更新2026-04-13
167
KiCad文件
全屏

一、项目主题与项目介绍

1.1 项目主题

本次设计以STM32F103C8T6为主控芯片,完成一款集-3.3V~+3.3V 连续可调直流电源、DDS 信号发生器、数字示波器、可编程 PWM 波输出、于一体的综合电子测量与调试仪器,可满足电子实训、电路调试、信号测量、偏置电压供给等多场景使用需求。

1.2 核心功能介绍

  1. -3.3V~+3.3V连续可调直流电源
    由 MCP4725 DAC 输出基准电压,经运放电平抬升与放大,配合负压供电电路,实现正负对称、连续可调的直流稳压输出,可作为运放偏置、基准电压、小信号调试电源使用。
  2. DDS 信号发生器
    采用 AD9834 芯片实现正弦波、三角波、方波输出,频率与幅值可通过按键程控调节,满足基础信号激励需求。
  3. 数字示波器功能
    对外部输入信号进行分压、程控放大、ADC 采样,在 TFT 屏上实时绘制波形,支持时基与增益调节。
  4. PA8 引脚可编程 PWM 输出
    利用 STM32 定时器 TIM1_CH1 通道,在 PA8 口输出频率、占空比均可调的方波 PWM 信号,可用于电机调速、LED 调光、数字调制等场景。
  5. 人机交互与辅助功能
    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 项目整体设计思路

  1. 电源优先设计原则
    先完成系统固定电源(+5V/+3.3V/-5V)设计,再基于此搭建 -3.3V~+3.3V 可调电源,保证所有模拟电路拥有稳定、低噪声供电。
  2. 模块化硬件划分
    按功能划分为:主控最小系统、信号发生、信号采集、PWM 输出、可调电源、人机交互六大模块,各模块独立布线、独立调试,减少相互干扰。
  3. 软件状态机调度
    系统采用多模式互斥运行机制,同一时刻只运行一种主功能,通过按键切换:信号源→示波器→PWM 输出→可调电源→自检,避免资源冲突。

四、原理图和 PCB 展示及介绍

4.1 原理图结构与功能分区使用KiCad进行原理图和PCB绘画

本次原理图共分为 3 大页面,所有模块清晰划分:

  1. 第一页:主控与系统电源
    包含 STM32 最小系统、复位电路、时钟电路、12V 输入、LM1117-5V/3.3V、ICL7660 负压电路和MCP4725 DAC + 运放双极性转换电路,实现 -3.3V~+3.3V 输出;是整个系统的供电与控制核心。
  2. 第二页:DDS 信号发生器
    AD9834 及其外围电路、RC 滤波电路、运放放大电路、模拟开关档位切换电路。
  3. 第三页:示波器采集通道
    交直流耦合切换、电阻衰减、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 详细软件执行流程图

image.png

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加减

image.pngimage.png

6.4 模式五:PA8 PWM 输出功能

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

6.5 模式六:示波器功能

可实时采集外部信号,自动适配量程,波形显示流畅,支持时基调节,可测量常见低频 / 中频信号。

按键2 采样时间调节-增益大小调节,按键3步进选择,按键4和5加减

image.pngimage.pngimage.pngimage.pngimage.pngimage.png


七、设计中遇到的难题和解决方法

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 干扰、可调电压不对称等问题,能够结合万用表、示波器定位故障根源,而不是盲目修改代码。整个过程让我认识到,软硬件协同设计远比单一功能实现更重要,合理的程序架构可以有效弥补部分硬件缺陷,提升系统整体可靠性。


附件下载
ProPrj_STM32F103C8T6多功能仪器_2026-04-08.zip
KICad原理图和PCB
1.6示波器全部功能.zip
代码
团队介绍
个人项目
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号