本篇总结
使用 DAC + DMA + TIM,输出连续正弦波形。
🔑 KEIL MDK、UART调试、DAC、DMA、TIM
硬件平台:本例程基于 W55MH32L-EVB 开发板运行。
采购推荐:点击前往 WIZnet 官方淘宝店铺 选购 W55MH32L-EVB
本示例演示 W55MH32 内置 DAC 的使用,通过 DAC 双通道(PA4 + PA5 引脚)配合 DMA 和 TIM2 定时器,通过 32 点正弦波查找表循环输出连续正弦波。TIM2 每次溢出触发 DAC 转换,DMA 自动将双通道查表数据搬运到 DAC 数据寄存器,无需 CPU 干预。
前置工具准备
开始本例程前,请确认以下工具已安装:
- WIZ UartTool V1.0:串口调试工具:点击下载
硬件连接
本例程使用 DAC 的通道 1(PA4)和通道 2(PA5)输出模拟正弦波。PA4/PA5 均配置为模拟输出模式(AIN),可通过示波器探头测量波形。USART1 用于输出调试信息。
| 引脚 | 功能 | 方向 | 模式 |
| PA4 | DAC1_OUT(正弦波输出) | 开发板 → 示波器 | 模拟输入 (AIN) |
| PA5 | DAC2_OUT(正弦波输出) | 开发板 → 示波器 | 模拟输入 (AIN) |
| PA9 | USART1_TX | 开发板 → PC | 复用推挽输出 (AF_PP) |
| PA10 | USART1_RX | PC → 开发板 | 浮空输入 (IN_FLOATING) |
工程结构
工程位于 SDK 内 1.SDK\ModuleDemo\DAC\,核心文件如下:
DAC/
├── USER/
│ ├── main.c ← DAC配置 + DMA配置 + TIM触发 + 正弦查表
│ ├── w55mh32_conf.h ← 外设头文件集中包含
│ ├── w55mh32_it.c/h ← 中断服务函数
│ ├── system_w55mh32.c/h← 系统初始化(时钟配置)
│ └── startup_w55mh32.s ← 启动文件
└── SYSTEM/
└── delay/
└── delay.c/h ← SysTick 延时函数
步骤1:GPIO 与 DAC 配置
函数:GPIO_Configuration() + DAC_Configuration() — 配置 PA4/PA5 为模拟输出,DAC 双通道均使用 TIM2 触发、12 位右对齐、使能 DMA。
/* 正弦波查找表(32点,12位) */
uint16_t Sine12bit[POINT_NUM] = {
2048, 2460, 2856, 3218, 3532, 3786, 3969, 4072,
4093, 4031, 3887, 3668, 3382, 3042, 2661, 2255,
1841, 1435, 1054, 714, 428, 209, 65, 3,
24, 127, 310, 564, 878, 1240, 1636, 2048
};
/* 双通道合并数据(高16位=DAC2,低16位=DAC1) */
uint32_t DualSine12bit[POINT_NUM];
/* DAC 寄存器地址宏 */
#define DAC1_DHR12RD_ADDRESS (DAC_BASE + 0x00000008 + DAC_Align_12b_R)
#define DAC2_DHR12RD_ADDRESS (DAC_BASE + 0x00000014 + DAC_Align_12b_R)
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/* PA4 + PA5: DAC1_OUT + DAC2_OUT → 模拟输出 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void DAC_Configuration(void)
{
DAC_InitTypeDef DAC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
/* DAC1 + DAC2: DMA使能 + TIM2触发 + 12位右对齐 */
DAC_InitStructure.DAC_Trigger = DAC_Trigger_T2_TRGO;
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_TriangleAmplitude_4095;
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
DAC_Init(DAC_Channel_1, &DAC_InitStructure);
DAC_Init(DAC_Channel_2, &DAC_InitStructure);
DAC_Cmd(DAC_Channel_1, ENABLE);
DAC_Cmd(DAC_Channel_2, ENABLE);
DAC_DMACmd(DAC_Channel_1, ENABLE);
DAC_DMACmd(DAC_Channel_2, ENABLE);
}
32 点正弦查找表覆盖一个完整周期,每个点为 12 位值(0~4095)。双通道输出时,将同一正弦波数据合并到 32 位整数 DualSine12bit 中(高 16 位对应 DAC2,低 16 位对应 DAC1),由 main() 中的合并循环完成:DualSine12bit[i] = (Sine12bit[i] << 16) + Sine12bit[i]。
如果你想自定义
- 改波形频率 → 修改 TIM2 的
TIM_Period,频率 = 72M / (Period+1) / 32
- 改波形类型 → 替换
Sine12bit[] 为三角波、方波等查表数据
- 改分辨率 → 修改查表点数(如 64 点、128 点),同时修改 DMA_BufferSize
步骤2:TIM2 定时触发配置
函数:TIM_Configuration() — TIM2 定时器溢出时产生 TRGO 信号触发 DAC 转换。
void TIM_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/* TIM2 定时触发 DAC 输出 */
/* DAC输出频率 = TIM_CLK / (Period+1) / 32(查表点数) */
TIM_TimeBaseStructure.TIM_Period = (20 - 1); /* 72MHz / 20 / 32 = 112.5kHz */
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);
TIM_Cmd(TIM2, ENABLE);
}
输出频率 = TIM_CLK / (Period+1) / 查表点数 = 72MHz / 20 / 32 = 112.5kHz。TIM_TRGOSource_Update 表示每次定时器溢出(更新事件)时输出触发信号。
步骤3:DMA 双通道搬运配置
函数:DMA_Configuration() — 配置 DMA2 通道 3 和通道 4,分别将双通道查表数据循环搬运到 DAC1 和 DAC2 的数据寄存器。
void DMA_Configuration(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);
/* DMA2 Channel4 → DAC2 (PA5) */
DMA_InitStructure.DMA_PeripheralBaseAddr = DAC2_DHR12RD_ADDRESS;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&DualSine12bit;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = POINT_NUM;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA2_Channel4, &DMA_InitStructure);
DMA_Cmd(DMA2_Channel4, ENABLE);
/* DMA2 Channel3 → DAC1 (PA4) */
DMA_InitStructure.DMA_PeripheralBaseAddr = DAC1_DHR12RD_ADDRESS;
DMA_Init(DMA2_Channel3, &DMA_InitStructure);
DMA_Cmd(DMA2_Channel3, ENABLE);
}
DMA2 通道 4 对应 DAC2(PA5),通道 3 对应 DAC1(PA4)。两个通道均使用循环模式(DMA_Mode_Circular),数据宽度为 32 位(Word),因为 DualSine12bit 是 32 位数组(高 16 位 = DAC2,低 16 位 = DAC1)。
运行验证
下载固件并按 RESET 后,用示波器探头分别测量 PA4(DAC1)和 PA5(DAC2)引脚,均可观察到连续的正弦波形,频率约 112.5kHz,峰峰值约 3.3V。串口打印系统时钟和测试提示信息。如无示波器,可用万用表测量 PA4/PA5 平均电压约为 1.65V(正弦波中点)。
由于 DAC 输出为模拟信号,需使用示波器观察波形。请在 PA4 和 PA5 引脚分别连接示波器验证双通道正弦波输出。
串口打印系统时钟和测试提示信息常见问题
Q: PA4 无波形输出
确认 PA4 配置为 GPIO_Mode_AIN(模拟输入模式,DAC 输出时使用此模式)。检查 DAC_Cmd(DAC_Channel_1, ENABLE) 和 DMA_Cmd(DMA2_Channel3, ENABLE) 是否调用。确认 TIM2 已使能。
Q: 波形频率不正确
输出频率 = TIM2_CLK / (Period+1) / 查表点数。TIM2 时钟为 APB1 时钟(默认 36MHz),若 APB1 预分频不为 1,则 TIM2 时钟为 APB1 × 2 = 72MHz。调整 TIM_Period 改变频率。
Q: 波形失真或有台阶
增加查表点数(如 64 点或 128 点)可提高波形精度。确保 DAC_OutputBuffer_Enable 已使能以增强驱动能力。查表数据应均匀覆盖一个完整正弦周期。
总结
本文基于 W55MH32 的 DAC 双通道(DAC1 + DAC2)配合 DMA 和 TIM2 实现了双路正弦波输出:
- 32点正弦查表:12 位精度(0~4095),覆盖一个完整周期
- DMA2 Channel3 + Channel4 循环模式自动搬运双通道查表数据到 DAC 数据寄存器,无需 CPU 干预
- TIM2 定时触发:每次溢出产生 TRGO 信号触发 DAC 转换,输出频率 ≈ 112.5kHz
- PA4(DAC1)和 PA5(DAC2)双通道输出连续正弦波,峰峰值约 3.3V,可通过示波器观察验证