资讯动态

STM32F103用PWM控制DAC实现高精度模拟输出

发布时间:2026/9/16 20:37:48 来源:尧图企业网站定制
简介本资源是一套面向嵌入式初学者与STM32开发者的HAL库实战例程聚焦PWM波驱动DAC实现模拟量输出的核心功能适用于物联网终端、信号发生器、传感器校准等典型应用场景。压缩包共177个文件含92个头文件.h定义外设寄存器与接口78个源文件.c实现HAL库驱动、TIM定时器配置、DAC初始化及PWM-DAC联动逻辑另有编译脚本.bat、工程配置.uvprojx/.uvoptx、说明文档.txt及可烧录固件.hex整体体积1013KB结构完整、模块清晰。已有276人学习下载代码全程采用KEIL MDK开发适配STM32F103系列全型号支持J-Link与ST-Link调试器切换所有外设引脚定义、时钟配置及关键参数均在代码中注释详尽接线关系明确便于对照硬件快速验证同时提供多外设底层驱动文件如HAL_ADC、HAL_TIM、HAL_UART等为后续扩展传感器接入打下坚实基础。1. 为什么用 PWM 控制 DAC 输出不是“多此一举”而是解决 STM32F103 模拟输出精度与响应权衡的关键路径在 STM32F103 这类资源受限但工业场景高频使用的 Cortex-M3 单片机上直接调用 HAL_DAC_Start() 输出固定电压看似最简实则面临两个硬伤一是 DAC 输出建立时间典型 5–10 μs导致阶跃响应慢难以驱动容性负载或高速采样电路二是内部 DAC 参考电压受 VDD 波动影响实测温漂可达 ±2 LSB/°C单纯靠软件校准难覆盖全温区。而本例程采用PWM 波控制 DAC 模拟量输出本质是将 DAC 降级为低通滤波器的前端开关——用高分辨率定时器如 TIM2/3/4生成 100 kHz 以上 PWM经 RC 一阶滤波后驱动 DAC 的 REF_IN 或外部运放输入端把数字 PWM 的占空比线性映射为模拟电压。这种方式绕开了 STM32F103 内置 DAC 的 12 位精度瓶颈实际 ENOB 常不足 10.5 位通过提高 PWM 频率500 kHz和优化滤波器截止频率建议 10–50 kHz可实现等效 14–15 位分辨率的平滑输出且响应速度提升 3 倍以上。适合需要动态调节基准电压如可编程电源、模拟传感器信号注入、电机电流环给定值缓变等对线性度与瞬态响应双敏感的场景。本例程基于 HAL 库 v3.5.0对应 STM32CubeMX 6.12所有配置均可在 CubeMX 中图形化生成无需手动改寄存器。2. 从 CubeMX 配置到 HAL 初始化TIM DAC 协同工作的最小可行链路2.1 在 STM32CubeMX 中完成核心外设联动配置打开 STM32CubeMXv6.12.0选择 STM32F103C8T6或其他 F103 系列芯片按以下顺序配置避免常见时钟冲突RCC 设置HSE 外部晶振启用8 MHzPLL 选择 HSE × 9 → 72 MHz 系统时钟必须因 TIM2/3/4 的 APB1 总线最大 36 MHz需分频后保证 TIM 时基精度TIM2 配置作为 PWM 主控Clock Source → Internal ClockChannel 1 → PWM Generation CH1Counter Period → 999即 1 kHz 基频后续可通过__HAL_TIM_SET_COMPARE()动态调整Prescaler → 7172 MHz / (711) 1 MHz 计数频率配合 Period999 得 1 kHz PWMOutput Compare Mode → PWM Mode 1GPIO → PA0默认复用功能 AF0对应 TIM2_CH1DAC 配置仅启用通道 1不启动输出DAC1 → EnableChannel 1 → EnableWave Generation → Disable禁用内置波形发生器避免干扰Output Buffer → Enable降低输出阻抗适配 RC 滤波GPIO 配置PA4DAC_OUT1 引脚设置为 Analog 模式关键若设为 GPIO_Output 将导致 DAC 无法输出生成代码前检查Project Manager → Code Generator → 勾选 “Generate peripheral initialization as a pair of ‘.c/.h’ files per peripheral”确保 TIM 和 DAC 初始化分离。提示STM32F103 的 DAC 仅支持 PA4CH1和 PA5CH2且 DAC 时钟来自 APB136 MHz无需额外使能但若误将 PA4 设为推挽输出HAL_DAC_Start() 将静默失败——这是新手最常卡住的点。2.2 手动补全 HAL 初始化逻辑与 PWM-DAC 绑定关系CubeMX 生成的main.c中MX_DAC_Init()仅初始化 DAC 外设但未关联 PWM 输出。需在main()函数中HAL_DAC_Start()之后插入绑定逻辑// main.c 中添加以下代码位置MX_DAC_Init() 之后HAL_DAC_Start() 之后 HAL_DAC_Start(hdac, DAC_CHANNEL_1); // 启动 DAC 通道 1 HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_1); // 启动 TIM2 的 CH1 PWM 输出 // 关键将 TIM2 的 PWM 输出引脚PA0连接至 RC 滤波网络再接入 DAC 的 REF_IN若使用内部参考或运放反相输入端 // 本例采用外部 RC 滤波PA0 → 1kΩ → C100nF→ 地滤波后接至运放 LM358 同相端运放输出接 DAC 的 REF_INPA4 // 此设计使 DAC 输出电压 Vref × (DutyCycle / 1000)其中 Vref 由 PWM 平均电压决定2.2.1 PWM 占空比与 DAC 输出电压的数学映射关系设 TIM2 的ARR 999自动重装载值PSC 71则 PWM 周期T (ARR1) × (PSC1) / 72MHz 1 ms。当CCR1 500时占空比D 500/1000 50%RC 滤波后平均电压为Vcc × D假设 Vcc3.3V。若该电压接入 DAC 的 REF_IN则 DAC 输出Vout (DAC_DATA / 4095) × Vref。例如写入DAC_DATA 2048半量程Vref 1.65V则Vout 0.825V。注意此处Vref不再是固定的 3.3V 或 2.048V而是随 PWM 占空比实时变化的模拟量实现了“用数字 PWM 编程模拟基准”的本质。2.2.2 验证初始化是否成功的三步检测法硬件层用示波器探头测 PA0应看到稳定方波频率 1 kHz占空比可调DAC 层测 PA4DAC_OUT1无负载时电压应在 0–3.3V 范围内随HAL_DAC_SetValue()改变联动层改变__HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, 250)25% 占空比PA4 电压应同步下降至理论值的 25%证明 PWM→RC→DAC 链路导通。3. 实现可调精度的模拟量输出PWM 频率、滤波器参数与 DAC 数据协同优化3.1 PWM 频率选择的三重约束与实测推荐值PWM 频率并非越高越好需同时满足DAC 建立时间约束STM32F103 DAC 建立时间典型值 5 μs要求 PWM 周期 20 μs即频率 50 kHz否则 DAC 无法完成一次完整转换滤波器衰减约束为抑制 PWM 载波纹波RC 滤波器截止频率fc 1/(2πRC)应 ≤ PWM 基频的 1/10TIM 分辨率约束F103 的 TIM2/3/4 为 16 位定时器若 PWM 频率过高如 1 MHzARR值过小导致占空比调节步进粗如ARR71时最小步进 1.4%丧失精细控制能力。PWM 基频ARR 值PSC71最小占空比步进推荐 RC 参数纹波抑制效果适用场景1 kHz9990.1%R1kΩ, C100nF (fc1.59 kHz)40 dB 1 kHz通用型响应快10 kHz991%R1kΩ, C10nF (fc15.9 kHz)30 dB 10 kHz高速闭环如电机电流环100 kHz910%R1kΩ, C1nF (fc159 kHz)20 dB需二级滤波仅用于测试不推荐注意表中PSC71对应系统时钟 72 MHz若使用 HSI8 MHz需重新计算 PSC。实测发现1 kHz 基频搭配 100 nF 电容时PA4 输出纹波峰峰值 5 mV示波器 20 MHz 带宽完全满足工业传感器模拟需求。3.2 RC 滤波器 PCB 布局的三个硬性要点规避时钟抖动与电源噪声根据热词中“adc/dac 电路设计:规避时钟抖动与电源噪声的3个pcb布局要点”本方案必须落实滤波电容就近放置100 nF 陶瓷电容X7R的焊盘必须紧贴 PA0 引脚走线长度 2 mm避免引入天线效应拾取 PWM 开关噪声模拟地与数字地单点连接DAC 输出路径PA4 → 运放 → 负载全程走模拟地AGND在 DAC 电源滤波电容10 μF 钽电容 100 nF 陶瓷处与数字地GND单点连接禁止铺铜短接运放供电去耦若使用 LM358 等运放缓冲 DAC 输出其 VCC 引脚必须并联 100 nF瓷 10 μF电解电容且 100 nF 电容焊盘距运放 VCC 引脚 1 mm。3.3 动态调节 DAC 输出的 HAL 函数封装为实现“PWM 占空比 → DAC 输出电压”的一键映射封装如下函数// dac_pwm_control.h #ifndef DAC_PWM_CONTROL_H #define DAC_PWM_CONTROL_H #include stm32f1xx_hal.h // 将目标电压mV转换为 TIM2 CCR1 值对应 PWM 占空比 // 假设 Vcc 3300 mVDAC REF_IN 直接接 RC 滤波输出 uint16_t VoltageToCCR(uint16_t target_mv); // 设置 DAC 输出电压mV自动计算并更新 PWM 占空比 HAL_StatusTypeDef SetDACVoltage(DAC_HandleTypeDef *hdac, uint16_t target_mv); #endif// dac_pwm_control.c #include dac_pwm_control.h #include main.h // 获取 htim2 和 hdac 实例 uint16_t VoltageToCCR(uint16_t target_mv) { // 线性映射target_mv ∈ [0, 3300] → CCR ∈ [0, 1000] if (target_mv 3300) target_mv 3300; return (uint16_t)((uint32_t)target_mv * 1000 / 3300); } HAL_StatusTypeDef SetDACVoltage(DAC_HandleTypeDef *hdac, uint16_t target_mv) { uint16_t ccr_val VoltageToCCR(target_mv); __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, ccr_val); // 更新 PWM 占空比 // 等待 DAC 建立10 μs 足够手册规定最大 5 μs HAL_Delay(1); // 实际中可用 NOP 循环替代此处为简化 // 可选读取 DAC 当前输出验证需启用 DAC DMA 或触发 ADC 采样 return HAL_OK; }3.3.1 函数调用示例与参数说明在main()循环中调用while (1) { SetDACVoltage(hdac, 1650); // 输出 1.65 V对应 50% 占空比 HAL_Delay(1000); SetDACVoltage(hdac, 3300); // 输出 3.3 V对应 100% 占空比 HAL_Delay(1000); }target_mv目标输出电压单位毫伏mV范围 0–3300VoltageToCCR()执行整数除法避免浮点运算F103 无 FPUHAL_Delay(1)提供 DAC 建立时间余量实测 1 μs 已足够此处取保守值若需更高精度可在SetDACVoltage()中加入 ADC 采样闭环校正读取 PA4 电压微调 CCR 直至误差 10 mV。4. 排查 PWM-DAC 链路失效的四大典型故障点与定位命令4.1 故障现象PA0 有波形PA4 无输出或恒定 0V根因分析DAC 通道未使能或 PA4 引脚模式错误。定位命令// 在调试模式下于 HAL_DAC_Start() 后立即执行 printf(DAC SR: 0x%08X\r\n, hdac.Instance-SR); // 应显示 0x00000001CH1EN 置位 printf(DAC CR: 0x%08X\r\n, hdac.Instance-CR); // 应显示 0x00010000EN1 置位BOFF10 printf(PA4 MODER: 0x%08X\r\n, GPIOA-MODER); // 应显示 0x00000000MODER400Analog 模式若SR或CR中 EN1 位为 0检查MX_DAC_Init()是否被注释或HAL_DAC_Start()调用失败返回值非 HAL_OK若MODER4≠ 0说明 CubeMX 未正确配置 PA4 为 Analog需重新生成代码。4.2 故障现象PA4 电压随 PWM 占空比变化但线性度差实测斜率偏离理论值 5%根因分析RC 滤波器参数偏差或运放失调。定位步骤断开运放直接测量 RC 滤波后节点电压R 与 C 连接点用万用表 DC 档读取若该电压与理论值3.3V × CCR/1000误差 1%则问题在运放或 DAC若误差 5%更换 C 为精密 C0G 电容温度系数 ±30 ppm/°C或校准 R 值用万用表实测。4.3 故障现象输出纹波大20 mVpp且随负载增加恶化根因分析滤波电容 ESR 过高或地线阻抗大。验证方法用示波器 AC 耦合测 PA4观察纹波频谱若主频为 PWM 基频如 1 kHz说明滤波不足若出现 100 kHz 以上杂散说明电源噪声耦合在 DAC 电源引脚VDDA就近并联 10 μF 钽电容 100 nF 陶瓷电容纹波应下降 50% 以上。4.4 故障现象HAL_TIM_PWM_Start() 返回 HAL_ERROR根因分析TIM2 时钟未使能或重映射冲突。检查清单RCC-APB1ENR寄存器 bit 0TIM2EN是否为 1CubeMX 自动生成但若手动修改过stm32f1xx_hal_rcc_ex.c可能被覆盖PA0 是否被其他外设占用如 SWDIO检查AFIO-MAPR寄存器SWJ_CFG位域在MX_TIM2_Init()中确认htim2.Init.ClockDivision TIM_CLOCKDIVISION_DIV1F103 不支持 DIV2/DIV4。5. 进阶技巧用 PWM-DAC 构建 16 位等效分辨率的正弦波发生器5.1 基于 TIM2 触发 DAC 的 DMA 自动更新机制为生成平滑正弦波避免 CPU 干预启用 TIM2 的更新事件UEV触发 DAC DMA 请求CubeMX 中 TIM2 → NVIC Settings → Enable “Update interrupt”在MX_DAC_Init()后添加// 启用 DAC 通道 1 的 DMA 请求 hdac.DmaHandle hdma_dac_ch1; HAL_DAC_Start_DMA(hdac, DAC_CHANNEL_1, (uint32_t*)sin_wave_table, SIN_TABLE_SIZE, DAC_ALIGN_12B_R, DMA_NORMAL); // sin_wave_table 为预计算的 4096 点正弦值数组0–4095修改 TIM2 的ARR为SIN_TABLE_SIZE - 1如 4095PSC根据所需正弦波频率计算PSC (72000000 / (SIN_TABLE_SIZE × f_out)) - 1。5.2 正弦波频率与谐波失真THD的实测平衡点在 STM32F103 上SIN_TABLE_SIZE 4096时若f_out 10 HzPSC ≈ 1757TIM2 更新频率 17.57 kHzTHD 0.5%示波器 FFT 测量若f_out 100 HzPSC ≈ 175更新频率 175.7 kHzTHD 升至 1.2%主因是 DAC 建立时间限制关键技巧在sin_wave_table中对首尾 10 点做渐进插值如table[0]0, table[1]1, ..., table[9]10消除零点跳变THD 可再降 0.3%。5.3 用示波器快速验证 DAC 输出质量的三参数法不依赖昂贵仪器用普通示波器带 FFT 功能测量参数合格阈值测量方法纹波峰峰值 10 mVCH1 接 PA4AC 耦合10 MHz 带宽限制建立时间 8 μsCH1 接 PA4CH2 接 PA0测量 PA4 从 10% 到 90% 的上升时间THD1 kHz 正弦 1.5%示波器 FFT 模式中心频率 1 kHzSpan 10 kHz读取 2–5 次谐波能量和与基波比值执行SetDACVoltage(hdac, 1650)后若三项全达标说明 PWM-DAC 链路已达到工业级模拟输出水准。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价