别再死记硬背了!用51单片机的AD/DA和PWM,亲手做个简易示波器信号发生器

张开发
2026/4/18 0:03:21 15 分钟阅读

分享文章

别再死记硬背了!用51单片机的AD/DA和PWM,亲手做个简易示波器信号发生器
用51单片机打造迷你示波器信号发生器PWM与AD/DA的实战融合当你在学习51单片机的AD/DA和PWM功能时是否觉得这些概念过于抽象本文将带你通过一个有趣的项目——自制简易示波器信号发生器将这些理论知识转化为看得见、摸得着的实践成果。这个项目不仅能让你深入理解这些核心概念还能获得一个实用的工具用于后续的电子实验和调试。1. 项目概述与核心原理1.1 什么是简易示波器信号发生器简易示波器信号发生器是一种能够产生基本波形如方波、三角波、正弦波并同时测量这些波形的设备。在我们的实现中将使用51单片机的PWM功能作为简易DAC数模转换器来生成波形同时利用外部ADC芯片如XPT2046来测量这些波形并显示在LCD上。这种二合一设备特别适合电子爱好者和初学者因为它成本低廉仅需常见元件功能实用可满足基础实验需求教学价值高直观展示AD/DA和PWM原理1.2 核心硬件组成我们的系统主要由以下部件构成组件功能备注51单片机主控制器STC89C52等常见型号PWM输出波形生成通过RC低通滤波模拟DACXPT2046 ADC信号测量SPI接口12位精度LCD1602结果显示显示波形参数和测量值按键/旋钮用户输入调节频率、幅度等参数RC低通滤波器PWM平滑将PWM转换为模拟电压1.3 技术原理框图[51单片机] --PWM-- [RC低通滤波] -- [输出信号] ↑ | | ↓ | [被测信号] | | ↓ | [XPT2046 ADC] ----------- | ↓ [LCD显示]2. 硬件设计与电路搭建2.1 PWM作为DAC的实现51单片机本身没有内置DAC但我们可以巧妙地利用PWM和低通滤波来模拟DAC功能// PWM初始化代码示例 void PWM_Init() { TMOD 0xF0; // 设置定时器模式 TMOD | 0x01; // 定时器0模式1 TH0 0xFF; // 初始值 TL0 0x9C; ET0 1; // 使能定时器中断 EA 1; // 开总中断 TR0 1; // 启动定时器 } // 中断服务程序 void Timer0_ISR() interrupt 1 { static unsigned char counter 0; TH0 0xFF; // 重装初值 TL0 0x9C; counter; if(counter duty_cycle) PWM_PIN 1; else PWM_PIN 0; if(counter 100) counter 0; }关键参数选择PWM频率建议10kHz左右太高会导致滤波困难太低会有明显纹波占空比分辨率8位0-255或10位0-1023取决于需求滤波电路二阶RC低通滤波截止频率约1kHz2.2 信号测量电路设计使用XPT2046 ADC芯片进行信号测量时需注意输入保护添加限流电阻和钳位二极管防止过压损坏ADC参考电压使用稳定基准源如TL431提高测量精度抗干扰设计在ADC输入引脚添加0.1μF去耦电容使用屏蔽线连接被测信号保持模拟地和数字地分离// XPT2046读取代码示例 unsigned int XPT2046_Read(unsigned char cmd) { unsigned int data 0; XPT2046_CS 0; for(unsigned char i0; i8; i) { XPT2046_DIN (cmd (0x80i)) ? 1 : 0; XPT2046_CLK 1; XPT2046_CLK 0; } for(unsigned char i0; i16; i) { XPT2046_CLK 1; XPT2046_CLK 0; if(XPT2046_DOUT) data | (0x8000i); } XPT2046_CS 1; return data8; }2.3 整体电路连接完整电路连接示意图----- | |--P1.0--[RC滤波]-- 信号输出 | 51 | | MCU |--P3.4~P3.7--[XPT2046]--被测信号输入 | | | |--P2.0~P2.7--[LCD1602] -----提示在实际布线时注意将模拟部分和数字部分分开布局电源走线要足够粗高频信号线尽量短。3. 软件设计与实现3.1 波形生成算法方波生成方波是最简单的波形只需定时切换PWM占空比为0%或100%void SquareWave(unsigned int freq) { static unsigned int counter 0; unsigned int half_period 1000000/(2*freq); // 微秒为单位 if(counter half_period) { counter 0; duty_cycle (duty_cycle 0) ? 100 : 0; } }三角波生成三角波需要线性变化占空比void TriangleWave(unsigned int freq) { static unsigned int counter 0; static char direction 1; unsigned int steps 1000000/(freq*200); // 200100*2(上升下降) if(counter steps) { counter 0; duty_cycle direction; if(duty_cycle 100) direction -1; else if(duty_cycle 0) direction 1; } }正弦波生成使用查表法实现正弦波const unsigned char sin_table[64] { 128,140,152,165,176,188,199,209,218,226,234,240,245,250,253,255, 255,253,250,245,240,234,226,218,209,199,188,176,165,152,140,128, 115,103, 90, 79, 67, 56, 46, 37, 29, 21, 15, 10, 5, 2, 0, 0, 0, 2, 5, 10, 15, 21, 29, 37, 46, 56, 67, 79, 90,103,115,128 }; void SineWave(unsigned int freq) { static unsigned int phase 0; unsigned int step (freq*64)/1000; // 每毫秒前进的相位 phase step; if(phase 64) phase - 64; duty_cycle sin_table[phase]; }3.2 测量与显示实现测量部分需要定期采样并计算信号参数void MeasureSignal() { static unsigned int samples[100]; static unsigned char index 0; // 采集100个样本 samples[index] XPT2046_Read(0x90); // 读取通道0 if(index 100) index 0; // 计算最大值、最小值和平均值 unsigned int max 0, min 4095, sum 0; for(unsigned char i0; i100; i) { if(samples[i] max) max samples[i]; if(samples[i] min) min samples[i]; sum samples[i]; } unsigned int avg sum / 100; // 计算峰峰值和频率(简化版) unsigned int vpp max - min; // 显示结果 LCD_ShowNum(1, 1, avg, 4); LCD_ShowNum(1, 6, vpp, 4); }3.3 用户界面设计使用按键和LCD构建简单用户界面按键功能分配K1: 波形选择(方波/三角波/正弦波)K2: 频率增加K3: 频率减少K4: 幅度调整LCD显示布局波形:方波 频率:1kHz 幅度:2.5V 峰峰:5.0V菜单状态机实现enum {MODE_SELECT, FREQ_ADJ, AMPL_ADJ} menu_state; void HandleKeys() { unsigned char key GetKey(); switch(menu_state) { case MODE_SELECT: if(key K1) ChangeWaveform(); else if(key K2) menu_state FREQ_ADJ; else if(key K3) menu_state AMPL_ADJ; break; case FREQ_ADJ: if(key K2) IncreaseFreq(); else if(key K3) DecreaseFreq(); else if(key K1) menu_state MODE_SELECT; break; case AMPL_ADJ: if(key K2) IncreaseAmpl(); else if(key K3) DecreaseAmpl(); else if(key K1) menu_state MODE_SELECT; break; } }4. 系统校准与性能优化4.1 PWM DAC校准步骤占空比与输出电压关系校准设置PWM占空比为0%测量实际输出电压Vmin设置PWM占空比为100%测量实际输出电压Vmax建立占空比-电压查找表修正非线性低通滤波器优化尝试不同RC组合(如1kΩ0.1μF, 10kΩ0.01μF)用示波器观察纹波选择最佳组合可考虑使用有源滤波器提高性能频率响应测试在不同频率下测量输出幅度确定系统可用带宽(通常为截止频率的1/10)4.2 ADC测量精度提升技巧软件滤波算法移动平均滤波中值滤波卡尔曼滤波(简化版)// 移动平均滤波实现 #define FILTER_SIZE 8 unsigned int MovingAverage(unsigned int new_sample) { static unsigned int buffer[FILTER_SIZE] {0}; static unsigned char index 0; static unsigned long sum 0; sum - buffer[index]; buffer[index] new_sample; sum buffer[index]; index (index 1) % FILTER_SIZE; return sum / FILTER_SIZE; }参考电压稳定使用专用基准电压芯片添加LC滤波网络避免大电流负载温度补偿测量环境温度根据温度特性曲线修正读数4.3 系统性能测试数据以下是一组实测性能数据供参考参数PWM DAC专用DAC芯片分辨率8-10位12-16位建立时间1-10ms1-10μs纹波10-50mV1mV成本极低中等适用场景教育、简单应用精密仪器注意PWM DAC的纹波会随着负载变化而变化建议在输出端添加电压跟随器缓冲。5. 项目扩展与进阶应用5.1 添加红外遥控功能利用红外接收头(如HS0038)和NEC协议实现遥控硬件连接红外接收器OUT引脚接单片机外部中断引脚添加解码电路软件实现外部中断捕获下降沿定时器测量脉冲宽度状态机解码NEC协议void IR_Init() { IT0 1; // 下降沿触发 EX0 1; // 使能外部中断0 EA 1; // 开总中断 } void EX0_ISR() interrupt 0 { static unsigned int last_time; unsigned int current_time Timer_GetCount(); unsigned int pulse_width current_time - last_time; last_time current_time; // 解码逻辑... }5.2 多波形合成与任意波形生成波形合成技术叠加多个正弦波产生复杂波形使用傅里叶级数近似任意波形任意波形实现方法预存波形数据在ROM中使用插值算法平滑波形DDS(直接数字合成)技术// 任意波形生成示例 void ArbitraryWave() { static unsigned int phase; phase phase_step; if(phase WAVE_TABLE_SIZE) phase - WAVE_TABLE_SIZE; duty_cycle wave_table[phase]; }5.3 上位机通信与数据可视化通过串口与PC通信实现更强大的功能通信协议设计定义简单的ASCII协议或使用二进制协议提高效率PC端软件功能波形参数设置测量数据显示与记录数据导出与分析void UART_SendWaveData() { unsigned int sample XPT2046_Read(0x90); SBUF sample 8; // 发送高字节 while(!TI); TI 0; SBUF sample 0xFF; // 发送低字节 while(!TI); TI 0; }在项目开发过程中我发现PWM DAC的输出稳定性很大程度上取决于滤波电路的设计。经过多次实验使用二阶有源滤波器配合软件校准可以将纹波控制在20mV以内这对于大多数教学和实验用途已经足够。另一个实用技巧是在测量环节添加自动量程功能通过程序自动调整参考电压或分压比可以显著提高测量范围和精度。

更多文章