基于STM32F103ZET6与TFT彩屏的简易示波器:ADC超频采样与波形识别实战

📅 2026/7/15 6:28:08 👁️ 阅读次数 📝 编程学习
基于STM32F103ZET6与TFT彩屏的简易示波器:ADC超频采样与波形识别实战

1. 项目背景与硬件选型

当我们需要观测50kHz高频信号时,STM32F103ZET6内置ADC的默认6分频模式(采样率1MHz)只能每个周期采集20个点,这会导致波形严重失真。就像用低帧率相机拍摄快速运动的物体,画面会出现卡顿和拖影。通过超频采样技术(如2分频),我们可以将采样率提升至3MHz,使每个周期采样点增加到60个,显著改善波形显示质量。

硬件核心配置

  • 主控芯片:STM32F103ZET6(Cortex-M3内核,72MHz主频)
  • 显示屏:3.5寸TFT彩屏(320x480分辨率,SPI接口)
  • 关键外设
    • ADC1通道2(PA1引脚)
    • 高级定时器TIM8(用于触发ADC)
    • DMA1通道1(实现无CPU干预的数据传输)

实测对比:在50kHz正弦波输入时,6分频模式下波形呈锯齿状,而2分频模式能清晰还原正弦曲线。这类似于用更高像素的传感器拍摄照片,细节表现力大幅提升。

2. 超频采样原理与实现

2.1 ADC时钟树配置

STM32F1的ADC时钟源自APB2总线,默认最高14MHz。通过修改分频系数可突破限制:

// 时钟树重配置(2分频实现) RCC_ADCCLKConfig(RCC_PCLK2_Div2); // 72MHz/2=36MHz RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

安全边界测试

  • 4分频(18MHz):稳定运行
  • 2分频(36MHz):短期工作正常(需加强散热)
  • 1分频(72MHz):采样值异常(超出芯片规格)

2.2 定时器触发配置

TIM8产生精确的采样脉冲,关键参数计算:

// 50kHz信号采样配置(3MHz采样率) TIM_TimeBaseInitTypeDef TIM_InitStruct; TIM_InitStruct.TIM_Period = 23; // 72MHz/(23+1)=3MHz TIM_InitStruct.TIM_Prescaler = 0; TIM_InitStruct.TIM_ClockDivision = 0; TIM_InitStruct.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM8, &TIM_InitStruct); // 触发ADC设置 TIM_SelectOutputTrigger(TIM8, TIM_TRGOSource_Update);

注意:超频时建议关闭其他高优先级中断,避免干扰采样时序。我在初期测试中就因为USB中断导致采样间隔不均匀,波形出现周期性畸变。

3. 波形识别算法优化

3.1 实时斜率检测

通过差分计算识别波形类型,代码片段:

#define SLOPE_THRESHOLD 100 // 斜率阈值 void Waveform_Identify(uint16_t *buf, uint32_t len) { int32_t slope[len-1]; // 计算相邻点斜率 for(int i=0; i<len-1; i++){ slope[i] = buf[i+1] - buf[i]; } // 波形特征判断 uint8_t square_cnt = 0; uint8_t triangle_cnt = 0; for(int i=0; i<len-2; i++){ // 方波检测(突变斜率) if(abs(slope[i]) > SLOPE_THRESHOLD && abs(slope[i+1]) > SLOPE_THRESHOLD){ square_cnt++; } // 三角波检测(恒定斜率变化) else if(abs(slope[i+1]-slope[i]) < 10){ triangle_cnt++; } } // 结果显示 if(square_cnt > len/4) LCD_ShowString(10,10,"Square Wave"); else if(triangle_cnt > len/2) LCD_ShowString(10,10,"Triangular Wave"); else LCD_ShowString(10,10,"Sine Wave"); }

3.2 动态频率测量

采用输入捕获+FFT双保险方案:

方法精度响应速度适用场景
TIM输入捕获±1Hz10ms纯净周期信号
256点FFT±10Hz50ms复杂混合信号

实测在50kHz输入时,两种方法误差小于0.1%,但FFT会额外消耗约15%的CPU资源。

4. TFT显示性能优化

4.1 双缓冲绘图技术

建立前后台缓冲区避免闪烁:

uint16_t wave_buf[2][320]; // 双缓冲区 uint8_t active_buf = 0; // DMA传输完成中断中切换缓冲区 void DMA1_Channel1_IRQHandler() { if(DMA_GetITStatus(DMA1_IT_TC1)){ // 处理非活动缓冲区数据 Process_Wave(wave_buf[!active_buf]); // 触发屏幕刷新 LCD_Refresh(wave_buf[!active_buf]); // 切换缓冲区 active_buf = !active_buf; DMA_SetCurrDataCounter(DMA1_Channel1, 320); } }

4.2 局部刷新策略

只更新变化区域提升帧率:

void LCD_PartialUpdate(uint16_t x, uint16_t y, uint16_t w, uint16_t h) { LCD_SetWindow(x, y, x+w-1, y+h-1); SPI_SendData(SPI2, (uint8_t*)&wave_buf[active_buf][x], w*h*2); }

优化后实测刷屏速度从28fps提升到52fps,波形滚动更加平滑。

5. 常见问题解决方案

问题1:超频后ADC值跳变

  • 检查VDDA电压(建议3.3V±1%)
  • 添加10uF+0.1uF去耦电容
  • 缩短ADC输入走线长度

问题2:高频采样数据错位

  • 将DMA优先级设为最高
  • 在DMA中断中禁用其他中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

问题3:TFT显示拖影

  • 调整SPI时钟相位(CPHA=1)
  • 在LCD初始化中添加5ms延时
LCD_WriteReg(0x11, 0x00); Delay(5); // 关键延时!

这个项目最让我意外的是——STM32F103的ADC在超频到36MHz时依然能保持10位有效精度,虽然数据手册标注最高14MHz。这提醒我们:官方参数往往偏保守,实际性能需要通过实践验证。当然长期超频运行还是建议做好散热措施。