STM32F103 DAC双通道输出不同幅度三角波:一个定时器触发两个波形的实战配置
STM32F103 DAC双通道独立控制实战:用定时器触发实现高精度三角波输出
在工业控制、测试测量和音频处理等领域,经常需要同时生成多路独立可控的模拟信号。STM32F103系列MCU内置的双通道DAC配合定时器触发功能,为这类需求提供了经济高效的解决方案。本文将深入探讨如何利用单个定时器同步触发两个DAC通道,实现相位同步但幅度可调的三角波输出,这种技术在电机驱动测试、传感器激励信号生成等场景中尤为实用。
1. 硬件架构与工作原理
STM32F103的DAC模块包含两个独立的12位数字模拟转换器,每个转换器对应一个输出通道(PA4对应DAC1,PA5对应DAC2)。其核心特性包括:
- 双通道独立控制:每个通道可单独配置幅度、波形和触发方式
- 内置波形发生器:支持噪声波和三角波生成,减轻CPU负担
- 灵活触发源:支持定时器、外部引脚和软件触发
- 同步更新功能:确保多通道输出时序一致性
三角波生成原理如下图所示:
DAC工作流程: 定时器触发 → 三角波计数器增减 → 与DHRx值相加 → 输出到DORx → 模拟输出关键寄存器配置要点:
| 寄存器 | 关键位域 | 配置说明 |
|---|---|---|
| DAC_CR | WAVEx[1:0] | 10=三角波模式 |
| MAMPx[3:0] | 设置三角波峰值幅度 | |
| TENx | 必须置1使能触发 | |
| DAC_DHR12RD | DACC1DHR/DACC2DHR | 双通道12位右对齐数据保持寄存器 |
注意:MAMPx必须在DAC使能前配置,否则无法修改。三角波实际输出值为(DHRx + 三角波计数器值) mod (MAMPx+1)
2. 工程环境搭建与基础配置
2.1 硬件连接准备
典型的最小系统连接方式:
- PA4(DAC1_OUT) → 示波器通道1
- PA5(DAC2_OUT) → 示波器通道2
- 确保VREF+引脚接入稳定参考电压(通常3.3V)
2.2 软件初始化流程
以下是完整的初始化代码框架:
#include "stm32f10x.h" void DAC_TriWave_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; DAC_InitTypeDef DAC_InitStruct; TIM_TimeBaseInitTypeDef TIM_InitStruct; // 1. 时钟使能 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 2. GPIO配置(模拟输入模式) GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOA, &GPIO_InitStruct); // 3. 定时器基础配置(TIM2作为触发源) TIM_InitStruct.TIM_Period = 71; // 决定波形频率 TIM_InitStruct.TIM_Prescaler = 0; TIM_InitStruct.TIM_ClockDivision = 0; TIM_InitStruct.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_InitStruct); TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); // 4. DAC通道配置 DAC_InitStruct.DAC_Trigger = DAC_Trigger_T2_TRGO; DAC_InitStruct.DAC_WaveGeneration = DAC_WaveGeneration_Triangle; DAC_InitStruct.DAC_OutputBuffer = DAC_OutputBuffer_Disable; // 通道1配置(全幅度) DAC_InitStruct.DAC_LFSRUnmask_TriangleAmplitude = DAC_TriangleAmplitude_4095; DAC_Init(DAC_Channel_1, &DAC_InitStruct); // 通道2配置(半幅度) DAC_InitStruct.DAC_LFSRUnmask_TriangleAmplitude = DAC_TriangleAmplitude_2047; DAC_Init(DAC_Channel_2, &DAC_InitStruct); // 5. 使能模块 DAC_Cmd(DAC_Channel_1, ENABLE); DAC_Cmd(DAC_Channel_2, ENABLE); TIM_Cmd(TIM2, ENABLE); }关键参数计算公式:
- 输出频率 = TIM2时钟 / (TIM_Period * (TIM_Prescaler+1))
- 幅度分辨率 = VREF+ / 4095(12位模式)
3. 高级配置技巧与实战优化
3.1 双通道相位同步控制
要实现严格的相位同步,必须注意:
- 使用
DAC_DualSoftwareTriggerCmd()同时触发双通道 - 或者通过
TIM_SelectOutputTrigger()确保两个通道使用相同的硬件触发源 - 在初始化最后一步才使能定时器,避免配置过程中的误触发
3.2 动态幅度调整方案
通过修改MAMPx值实时改变波形幅度:
void Set_DAC_Amplitude(DAC_Channel_TypeDef channel, uint32_t amplitude) { DAC_InitTypeDef DAC_InitStruct; // 必须先禁用DAC才能修改MAMP DAC_Cmd(channel, DISABLE); // 重新初始化配置 DAC_InitStruct.DAC_Trigger = DAC_Trigger_T2_TRGO; DAC_InitStruct.DAC_WaveGeneration = DAC_WaveGeneration_Triangle; DAC_InitStruct.DAC_OutputBuffer = DAC_OutputBuffer_Disable; DAC_InitStruct.DAC_LFSRUnmask_TriangleAmplitude = amplitude; DAC_Init(channel, &DAC_InitStruct); DAC_Cmd(channel, ENABLE); }可用幅度常量(对应MAMP[3:0]):
DAC_TriangleAmplitude_1 // 2^1-1 DAC_TriangleAmplitude_3 // 2^2-1 ... DAC_TriangleAmplitude_4095 // 2^12-1 (全幅度)3.3 输出频率精确控制
通过TIM2参数调整输出频率时需考虑:
- APB1时钟通常为36MHz(系统时钟72MHz二分频)
- 实际波形周期 = (TIM_Period+1) * (TIM_Prescaler+1) / APB1_clock
- 最小间隔需大于3个APB1周期(DAC硬件限制)
示例频率配置表:
| 目标频率 | TIM_Prescaler | TIM_Period | 实际频率 |
|---|---|---|---|
| 100kHz | 0 | 359 | 100kHz |
| 10kHz | 0 | 3599 | 10kHz |
| 1kHz | 35 | 999 | 1kHz |
4. 常见问题排查与性能优化
4.1 典型问题诊断
无输出信号
- 检查GPIO是否配置为模拟输入模式
- 确认TIM2和DAC时钟已使能
- 测量VREF+电压是否正常
波形失真
- 确保TIM_Period值≥3(满足DAC硬件要求)
- 尝试启用输出缓冲(DAC_OutputBuffer_Enable)
- 检查电源稳定性,必要时增加滤波电容
通道间相位差
- 使用
DAC_DualSoftwareTriggerCmd()强制同步 - 检查是否意外修改了DAC_CR的TENx位
- 使用
4.2 性能优化建议
- 降低CPU干预:配置DMA自动更新DHR寄存器
- 提高精度:校准VREF+电压,使用12位右对齐模式
- 多通道扩展:通过外部运放组合多个DAC输出
- 低功耗设计:在波形稳定后关闭TIM2时钟
实际项目中,我在电机测试平台上使用这种配置时发现,当需要生成高频信号(>50kHz)时,建议:
- 将TIM_Prescaler设为0以获得最高定时器分辨率
- 适当降低MAMP值可以提高三角波斜率
- 禁用所有未使用的外设时钟以减少噪声干扰
