用STM32F103的DAC和ADC给自己做个简易信号发生器(标准库版)

用STM32F103的DAC和ADC给自己做个简易信号发生器(标准库版) 用STM32F103打造交互式信号发生器DAC与ADC的闭环实践在嵌入式开发领域理论知识的掌握固然重要但真正让技术活起来的往往是那些能够亲手实现的项目。本文将带您深入探索如何利用STM32F103的DAC和ADC功能构建一个完整的信号发生与采集系统这不仅是一个简单的功能演示更是一个融合了硬件交互、软件控制和实时反馈的微型工程实践。1. 项目架构与硬件设计1.1 系统整体设计思路这个DIY信号发生器的核心在于形成一个完整的控制闭环[用户输入] → [DAC输出] → [ADC采集] → [数据显示] ↑____________[误差分析]_________↓我们选择STM32F103ZET6作为主控芯片主要考虑其丰富的外设资源和广泛的应用基础。系统将使用PA4作为DAC通道1的输出引脚PA1作为ADC通道1的输入引脚三个用户按键用于电压调节增/减/复位USART1用于串口调试输出1.2 关键硬件连接下表列出了主要硬件连接关系元件/功能连接引脚备注DAC输出PA4接示波器或万用表测量ADC输入PA1直接连接PA4进行闭环测试按键1PC0电压增加按键2PC1电压减少按键3PA0系统复位串口TXPA9连接USB转TTL注意实际布线时DAC输出与ADC输入之间建议加入一个100Ω电阻进行隔离避免直接短路可能带来的风险。2. 软件架构与核心代码实现2.1 外设初始化流程系统初始化遵循以下顺序确保各模块正确配置时钟系统初始化GPIO端口配置DAC模块初始化ADC模块初始化USART初始化按键扫描初始化void Hardware_Init(void) { RCC_Configuration(); GPIO_Configuration(); DAC_Configuration(); ADC_Configuration(); USART_Init(); KEY_Init(); }2.2 DAC输出模块精要DAC配置的关键在于理解几个核心参数DAC_InitTypeDef DAC_InitStructure; DAC_InitStructure.DAC_Trigger DAC_Trigger_None; // 不使用硬件触发 DAC_InitStructure.DAC_WaveGeneration DAC_WaveGeneration_None; // 不产生波形 DAC_InitStructure.DAC_OutputBuffer DAC_OutputBuffer_Disable; // 关闭输出缓冲 DAC_Init(DAC_Channel_1, DAC_InitStructure); DAC_Cmd(DAC_Channel_1, ENABLE);电压设置函数将直观的电压值转换为DAC寄存器值void Set_DAC_Voltage(float voltage) { if(voltage 3.3f) voltage 3.3f; if(voltage 0.0f) voltage 0.0f; uint16_t dacValue (uint16_t)((voltage / 3.3f) * 4095); DAC_SetChannel1Data(DAC_Align_12b_R, dacValue); }2.3 ADC采集模块优化为提高采集精度我们实现了多点采样和软件滤波#define SAMPLE_TIMES 32 // 采样次数 float Get_Voltage_ADC(void) { uint32_t sum 0; for(uint8_t i0; iSAMPLE_TIMES; i){ sum Get_Adc(ADC_Channel_1); Delay_us(10); // 适当延时保证采样间隔 } float adcValue (float)(sum / SAMPLE_TIMES); return (adcValue * 3.3f) / 4095.0f; }3. 系统交互逻辑实现3.1 按键控制策略设计了三档电压调节方案兼顾调节效率和精度按键短按动作长按动作(2s)KEY_UP0.1V0.5VKEY_DOWN-0.1V-0.5VKEY_RST归零无动作按键处理核心逻辑void Key_Process(void) { static float targetVoltage 0.0f; uint8_t key KEY_Scan(0); switch(key){ case KEY_UP_SHORT: targetVoltage 0.1f; break; case KEY_UP_LONG: targetVoltage 0.5f; break; case KEY_DOWN_SHORT: targetVoltage - 0.1f; break; case KEY_DOWN_LONG: targetVoltage - 0.5f; break; case KEY_RST_PRESS: targetVoltage 0.0f; break; } // 边界检查 if(targetVoltage 3.3f) targetVoltage 3.3f; if(targetVoltage 0.0f) targetVoltage 0.0f; Set_DAC_Voltage(targetVoltage); }3.2 数据可视化输出通过串口输出格式化的测量结果便于分析[系统信息] DAC设定值: 1.650V | ADC测量值: 1.643V | 误差: 0.007V (0.42%)实现代码void Send_Measurement_Data(float setVoltage, float measuredVoltage) { float error setVoltage - measuredVoltage; float errorPercent (error / setVoltage) * 100.0f; printf([系统信息] DAC设定值: %.3fV | ADC测量值: %.3fV | 误差: %.3fV (%.2f%%)\r\n, setVoltage, measuredVoltage, error, errorPercent); }4. 系统校准与性能优化4.1 误差来源分析在实际测试中我们发现主要误差来自DAC非线性误差±2LSBADC量化误差±1LSB参考电压波动约±0.5%PCB布局引入的噪声4.2 软件校准技术通过两点校准法提高系统精度零点校准将DAC输出设置为0记录ADC读数应接近0满量程校准将DAC输出设置为3.3V记录ADC读数应接近4095校准系数计算typedef struct { float scale; // 比例系数 float offset; // 偏移量 } Calibration_Params; Calibration_Params Calibrate_System(void) { Calibration_Params params; // 零点校准 Set_DAC_Voltage(0.0f); Delay_ms(100); float adcZero Get_Voltage_ADC(); // 满量程校准 Set_DAC_Voltage(3.3f); Delay_ms(100); float adcFullScale Get_Voltage_ADC(); // 计算校准参数 params.scale 3.3f / (adcFullScale - adcZero); params.offset -adcZero * params.scale; return params; }4.3 进阶功能扩展基于现有框架可以轻松实现更多实用功能波形生成通过定时器触发DAC输出产生正弦波、三角波等自动扫描电压从0-3.3V自动扫描绘制DAC-ADC特性曲线上位机通信通过串口接收PC指令实现远程控制void Generate_Sine_Wave(void) { static uint16_t phase 0; float voltage 1.65f 1.65f * sin(2 * PI * phase / 360.0f); Set_DAC_Voltage(voltage); phase (phase 5) % 360; // 5°步进 Delay_ms(10); // 控制波形频率 }这个项目最有趣的部分在于当我把DAC和ADC连接起来形成一个闭环时第一次看到串口打印出误差: 0.003V的那种成就感。在实际调试中发现简单的软件滤波就能将测量误差降低约60%这比单纯阅读数据手册更能让人理解噪声处理的重要性。