当前位置: 首页 > news >正文

用STM32F103的DAC和ADC做个简易信号发生器:从PA4输出,PA1读取并串口显示

STM32F103 DAC与ADC闭环系统实战:构建可交互信号发生器

在嵌入式开发中,数字模拟转换(DAC)和模拟数字转换(ADC)是两个基础但极其重要的外设功能。许多开发者虽然能独立使用它们,却很少尝试将二者结合形成完整的闭环系统。本文将带您用STM32F103的DAC和ADC构建一个实时可调的信号发生器,通过PA4输出模拟信号,同时用PA1读取反馈值并通过串口显示,实现"设置-输出-测量-验证"的全流程。

1. 系统架构设计与核心组件

1.1 硬件资源规划

STM32F103ZET6芯片内置了两个12位精度的DAC模块和一个12位ADC模块,这是我们项目的硬件基础。系统需要合理分配以下资源:

  • DAC通道1:使用PA4引脚作为模拟输出
  • ADC通道1:使用PA1引脚作为模拟输入
  • USART1:用于PC通信和数据显示
  • GPIO按键:用于交互控制输出电压

关键参数对比表

模块分辨率参考电压转换时间输出范围
DAC12位3.3V0-3.3V
ADC12位3.3V1μs0-3.3V

1.2 软件架构设计

整个系统采用分层设计思想,各功能模块通过清晰的接口交互:

主控制循环 ├─ 用户输入处理(按键扫描) ├─ DAC输出控制 ├─ ADC采样处理 └─ 数据显示输出(串口)

这种架构保证了系统的可扩展性,未来可以方便地添加新功能如波形生成、数据存储等。

2. 硬件初始化与配置

2.1 时钟与GPIO配置

所有外设使用前必须使能相应时钟,这是STM32编程的基本要求:

// 使能各外设时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);

PA4(DAC输出)和PA1(ADC输入)都需要配置为模拟模式:

GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_4; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; // 模拟输入模式 GPIO_Init(GPIOA, &GPIO_InitStructure);

注意:即使DAC是输出功能,其引脚也必须配置为模拟输入模式,这是STM32的特殊设计要求。

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通道

输出缓冲的取舍

  • 启用缓冲:提高驱动能力,但会引入约20mV的偏移
  • 禁用缓冲:输出阻抗较高,但精度更好

对于需要高精度的测量系统,建议禁用输出缓冲。

3. 核心功能实现

3.1 电压输出控制函数

设计一个可直接设置输出电压的函数,隐藏底层寄存器操作:

// 设置DAC输出电压 // vol: 目标电压值,单位mV,范围0-3300 void DAC_SetVoltage(uint16_t vol) { // 电压值转换为DAC数字量 uint16_t dacValue = (uint32_t)vol * 4095 / 3300; DAC_SetChannel1Data(DAC_Align_12b_R, dacValue); }

这个封装函数使得电压设置更加直观,开发者只需关心需要的电压值,而不必了解底层DAC寄存器的细节。

3.2 ADC采样与数据处理

ADC配置需要考虑采样时间和滤波算法:

// 初始化ADC ADC_InitTypeDef ADC_InitStructure; ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel = 1; ADC_Init(ADC1, &ADC_InitStructure); // ADC时钟配置 RCC_ADCCLKConfig(RCC_PCLK2_Div6); // 72MHz/6=12MHz ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5);

为提高测量精度,实现了一个带平均滤波的ADC读取函数:

// 获取ADC采样平均值 uint16_t ADC_ReadAverage(uint8_t ch, uint8_t times) { uint32_t sum = 0; for(uint8_t i=0; i<times; i++) { sum += ADC_ReadSingle(ch); delay_ms(1); // 适当延时保证采样间隔 } return sum / times; }

4. 系统集成与性能优化

4.1 主控制逻辑实现

主循环中整合了按键扫描、DAC控制、ADC采样和数据显示功能:

while(1) { key = KEY_Scan(0); // 扫描按键 // 按键处理 if(key == KEY_UP) { voltage += 100; // 电压增加0.1V if(voltage > 3300) voltage = 3300; DAC_SetVoltage(voltage); } else if(key == KEY_DOWN) { voltage -= 100; // 电压减少0.1V if(voltage < 0) voltage = 0; DAC_SetVoltage(voltage); } // 定期采样和显示 if(timer_count >= 100) { // 约1秒间隔 uint16_t adcValue = ADC_ReadAverage(ADC_Channel_1, 10); float measuredVoltage = (float)adcValue * 3.3f / 4095; printf("设定电压: %.2fV, 测量电压: %.2fV\r\n", voltage/1000.0f, measuredVoltage); timer_count = 0; } delay_ms(10); timer_count++; }

4.2 精度优化技巧

通过实践发现几个提高系统精度的关键点:

  1. 电源稳定性:为AVDD和VDDA提供干净稳定的3.3V电源
  2. 参考电压:确保VREF+引脚有良好的去耦电容(100nF+10μF)
  3. PCB布局
    • 模拟信号走线远离数字信号
    • 使用地平面隔离敏感信号
  4. 软件校准
    • 在代码中实现零点校准和增益校准
    • 存储校准参数到Flash或EEPROM

典型误差来源分析

误差类型影响程度解决方法
量化误差±0.8mV软件平均滤波
参考电压波动±10mV使用精密基准源
PCB噪声±5mV优化布局布线

5. 进阶功能扩展

5.1 波形生成功能

基于现有框架,可以扩展DAC的波形生成能力:

// 生成正弦波 void DAC_GenerateSineWave(uint16_t freq) { static const uint16_t sineTable[] = {2048,2447,2831,3185,...}; // 预计算正弦表 static uint8_t index = 0; DAC_SetChannel1Data(DAC_Align_12b_R, sineTable[index]); index = (index + 1) % sizeof(sineTable); // 使用定时器控制频率 TIM_SetAutoreload(TIM2, SystemCoreClock / (freq * sizeof(sineTable))); }

5.2 上位机交互设计

通过串口协议实现更丰富的控制功能:

协议帧格式: [头字节][命令][数据长度][数据][校验和] 示例命令: 0x01 - 设置输出电压 0x02 - 读取当前电压 0x03 - 开始波形生成

在STM32端实现简单的协议解析:

void USART_ProcessCommand(uint8_t* buf) { switch(buf[1]) { // 命令字节 case 0x01: DAC_SetVoltage((buf[3]<<8)|buf[4]); break; case 0x02: USART_SendVoltage(); break; // 其他命令处理... } }

6. 调试技巧与常见问题

6.1 典型问题排查指南

问题1:DAC无输出或输出不正确

  • 检查PA4模式是否为GPIO_Mode_AIN
  • 确认DAC已使能(DAC_Cmd)
  • 测量VREF+电压是否正常

问题2:ADC读数不稳定

  • 增加采样次数使用平均滤波
  • 检查模拟电源是否干净
  • 适当增加采样时间(ADC_SampleTime)

问题3:串口数据显示异常

  • 确认波特率设置匹配
  • 检查串口初始化代码
  • 确保USART时钟已使能

6.2 性能测试方法

建立系统化的测试流程:

  1. 静态精度测试

    • 设置DAC输出从0到3.3V,步进0.1V
    • 记录ADC测量值和万用表实测值
    • 计算误差并绘制曲线
  2. 动态响应测试

    • 快速改变DAC输出
    • 测量ADC跟踪速度
    • 评估系统响应时间
  3. 长期稳定性测试

    • 固定输出某电压值
    • 连续监测8小时以上
    • 观察漂移情况

通过这个项目,您不仅掌握了DAC和ADC的基本用法,更重要的是理解了如何将多个外设有机结合,构建完整的嵌入式系统。在实际产品开发中,这种闭环控制思想随处可见,比如智能家居中的温控系统、工业设备中的伺服控制等。

http://www.jsqmd.com/news/946673/

相关文章:

  • 多代理协同编码系统:原理、优化与实践
  • 手把手教你用Postman调试天地图OGC服务(WMS/WFS/WMTS接口实战)
  • UWB厘米级定位原理与停车场无感解锁实战
  • 播客AI化不是升级,是重构:3类不可逆架构决策清单(附Gartner 2024成熟度评估矩阵)
  • 【AI+MR融合实战指南】:20年专家亲授5大不可绕过的系统级整合陷阱与避坑清单
  • 移动创意工作流构建指南:从云端同步到专业工具链整合
  • OpenArk反Rootkit工具完整使用指南:5大核心功能深度解析
  • GPT-5不存在?当前最先进AI模型真相与GPT-4 Turbo实战指南
  • 别再问师兄了!手把手教你从3GPP官网精准下载V2X协议(附TR 36.885实例)
  • 从硬盘磁铁到角度传感器:拆解日常设备中的永磁体磁场秘密
  • 终极指南:使用开源脚本永久激活IDM并解决30天试用期限制
  • 用STM32F103RCT6和OLED屏,我DIY了一个能控制空调风扇的万能遥控器(附完整代码)
  • 别再手动敲变量了!用Python脚本批量处理施耐德Control Expert的XSY变量表
  • CVE-2026-0257深度解析:Palo Alto GlobalProtect认证绕过漏洞原理、POC复现与完整防御体系|CISA KEV限期6.19修复
  • Delphi 11/12可用的DOCX文档处理组件(VCL+FMX双支持)
  • 为什么92%的AI外呼项目6个月内停摆?——头部银行私有化部署失败复盘(含架构拓扑图)
  • Stearic acid-PEG-Rhodamine 硬脂酸-聚乙二醇-罗丹明 SA-PEG-RB 科研应用
  • WinUtil:Windows系统优化的终极免费解决方案,让你的电脑焕然一新
  • 基于 Harmony 6.0 应用的校友联络平台首页实现
  • 别再自己写数码管驱动了!用STM32CubeMX+TM1640,5分钟搞定LED显示模块
  • 大模型研发依赖系统性工程能力而非个体迁移
  • 别再死记公式!用几何动画直观理解6轴机械臂正逆解(以Gluon-6L3为例)
  • camembert-ner-openmind开发者深度指南:自定义训练与模型调优
  • 3分钟学会GitHub精准下载:告别臃肿克隆,只取所需文件
  • iPhone本地运行Gemma-2B:端侧大模型实战全解析
  • 免费开源AMD Ryzen调试神器:SMUDebugTool完整使用教程与性能优化指南
  • 如何快速掌握OpenCore EFI配置:3个简单步骤完成智能自动化部署
  • 终极指南:如何让普通鼠标在macOS上超越苹果触控板
  • DC NXT的SPG流程里,那些容易被忽略的“黑科技”:从adaptive retiming到TNS-Driven布局
  • 别再对着‘Segmentation fault (core dumped)’发呆了:手把手教你用GDB调试Linux C程序崩溃