文章目录
- 一、DAC 简介
- 1、什么是 DAC
- 2、DAC 工作原理
- 3、STM32F103 DAC 核心参数
- 4、DAC 核心特点
- 5、DAC 典型应用场景
- 二、DAC 模块详解
- 1、DAC功能框图
- 2、标号① 模拟电源基准区(绿色)
- 引脚功能:
- 作用:
- 硬件注意:
- 3、标号② 数模转换输出单元(橙色)
- 模块:数字至转换器 x + DAC_OUTx 引脚
- 特性:
- 4、标号③ 数字控制逻辑区(蓝色,核心功能)
- 寄存器与模块拆解:
- 触发控制 TENx:
- DMAENx:DMA 使能
- 内置波形发生器(LFSRx /trianglex):
- DORx:DAC 输出寄存器:
- DAC 控制寄存器:
- 数据流流程:
- 5、标号④ 触发源选择区(黄色 TSELx 多路选择器)
- 全部可选触发源(TSELx [2:0] 寄存器位选择):
- 工作逻辑:
- 6、三种典型工作模式
- 模式 1:固定直流输出(无 DMA、无硬件波形)
- 模式 2:DMA 自动波形输出(自定义正弦 / 方波表)
- 模式 3:硬件内置三角 / 噪声波(无需 DMA、无需波形表)
- 三、DAC编程模块
- 1、DAC 结构体
- 1.DAC_HandleTypeDef:DAC 顶层句柄(外设总控制器)
- 2.DAC_InitTypeDef:DAC 通道初始化配置(触发、波形、输出缓冲)
- 3.DAC_ChannelConfTypeDef:通道独立配置(多通道时分配置)
- 4.DAC_DMAHandleTypeDef:配套 DMA 句柄(DMA 波形输出专用)
- 2、HAL_DAC_API
- 1.初始化/去初始化
- 2.IO操作函数
- 3.外围控制函数
- 4.外设状态和错误功能
- 5.专用回调函数(中断 / DMA 完成事件,用户重写)
- 四、DAC 应用实例
- 1、静态可调电压输出
- 1.CubeMX配置
- 2.电压转换函数
- 3.完整参考代码
- 2、DAC+DMA 正弦波发生器
- 1.CubeMX配置
- 2.正弦波形表生成(12 位,中点 2048,幅值 2047,0~4095)
- 3.完整代码
- 4.可选DMA中断回调(动态修改波形 / 状态监测)
- 五、DAC 核心要点与避坑大全
- 1、必考核心知识点
- 2、高频坑点
- 3、工程优化技巧
- 4、工程万能搭配方案
- 5、一句话总结
- 六、全篇总结
一、DAC 简介
1、什么是 DAC
DAC(Digital-to-Analog Converter,数模转换器),是 ADC 的反向外设,核心作用是将单片机内部离散的数字量转换成连续变化的模拟电压信号。
单片机本身只能输出数字高低电平(0V/3.3V),依靠 DAC 可以输出任意中间电压、模拟波形,实现信号生成、音量调节、模拟控制等功能。
2、DAC 工作原理
数字量寄存器赋值 → DAC 内核权重电阻网络转换 → 模拟电压输出 → 引脚输出连续电压信号。
STM32 DAC 采用电阻权重网络数模转换原理,12位精度分级输出电压,数值越大,输出电压越高。
3、STM32F103 DAC 核心参数
分辨率:12位 DAC,数字范围 0 ~ 4095。
输出电压范围:0V ~ VDDA(默认0~3.3V)。
电压计算公式:输出电压 = DAC设定值 × 3.3 / 4095。
通道数量:2路独立DAC通道。
输出速度:高速转换,支持波形快速刷新。
对应引脚:DAC1_OUT PA4、DAC2_OUT PA5。
4、DAC 核心特点
支持双通道独立输出,可输出两路不同模拟电压。
支持 DMA 自动刷新,无需CPU干预生成连续波形。
支持定时器触发采样,精准生成正弦波、三角波、方波。
支持噪声波形、三角波内置生成模式。
输出电压线性度高,模拟信号稳定。
5、DAC 典型应用场景
模拟电压输出、可调基准电压。
信号发生器:正弦波、三角波、方波输出。
音响音量调节、音频信号播放。
电机模拟调压控制、传感器模拟校准。
配合ADC实现闭环模拟信号调试。
二、DAC 模块详解
1、DAC功能框图
2、标号① 模拟电源基准区(绿色)
引脚功能:
VDDA:模拟电源 3.3V,为 DAC 转换电路供电。
VSSA:模拟地。
VREF+:参考电压输入,决定 DAC 输出量程 0 ~ VREF+。
作用:
提供纯净模拟电压标尺,DAC 输出电压计算公式:
硬件注意:
电源引脚就近并联 0.1μF 陶瓷滤波电容,数字地与模拟地单点共地,降低输出噪声。
3、标号② 数模转换输出单元(橙色)
模块:数字至转换器 x + DAC_OUTx 引脚
输入 12 位数字信号,内部电阻网络转换成连续模拟电压。
片内集成输出缓冲运放(可软件开关),提升带载驱动能力。
对外引脚:DAC_OUT1 (PA4) / DAC_OUT2 (PA5)。
特性:
输出单极性电压,无法输出负压。
输出阶梯波形可外接 RC 低通滤波平滑。
4、标号③ 数字控制逻辑区(蓝色,核心功能)
寄存器与模块拆解:
DHRx:数据保持寄存器
CPU/DMA 写入 12 位目标数值,暂存待转换;支持 12 位左 / 右对齐。
触发控制 TENx:
触发使能位,打开触发更新通路。
DMAENx:DMA 使能
开启后每次触发自动发起 DMA 请求,DMA 自动刷新 DHRx,实现波形全自动输出。
内置波形发生器(LFSRx /trianglex):
WAVENx:波形使能开关。
MAMPx:三角波幅值 / 噪声随机数掩码配置。
LFSR:伪随机噪声发生器;trianglex:硬件三角波生成,无需 CPU 计算波形表。
DORx:DAC 输出寄存器:
触发到来时,将 DHRx 数值锁存送入 DAC 转换单元,避免转换中途数值突变。
DAC 控制寄存器:
统一配置通道使能、缓冲、对齐、波形模式等全局参数。
数据流流程:
DHRx → 触发锁存 → DORx → 12 位 DAC 转换单元 → DAC_OUTx.
5、标号④ 触发源选择区(黄色 TSELx 多路选择器)
全部可选触发源(TSELx [2:0] 寄存器位选择):
SWTRIGx 软件触发:寄存器置位手动更新输出。
定时器 TRGO 事件:TIM2/TIM4/TIM5/TIM6/TIM7/TIM8 更新事件,精准定时刷新 DAC(波形发生器首选)。
EXTI_9 外部引脚触发:外部电平边沿触发更新输出。
工作逻辑:
多路选择器仅选中一路触发信号送入控制逻辑;每收到一次触发,执行一次:DHRx→DORx 更新 + DMA 请求(DMA 开启时)。
6、三种典型工作模式
模式 1:固定直流输出(无 DMA、无硬件波形)
CPU 直接写 DHRx。
软件触发 SWTRIGx 更新 DORx。
DAC 转换输出恒定电压。
通路:CPU → DHRx → SWTRIG → DORx → DAC_OUTx。
模式 2:DMA 自动波形输出(自定义正弦 / 方波表)
DMA 循环搬运波形表数据到 DHRx。
TIMx_TRGO 定时触发更新。
持续循环输出波形。
通路:内存波形表 → DMA → DHRx → TIM 触发 → DORx → DAC_OUTx。
模式 3:硬件内置三角 / 噪声波(无需 DMA、无需波形表)
配置 WAVENx 开启硬件波形、MAMPx 设置幅值。
TIM 定时触发,硬件自动递增 / 随机生成数值覆盖 DHRx。
通路:硬件 LFSR/triangle 模块 → DHRx → TIM 触发 → DORx → DAC_OUTx。
三、DAC编程模块
1、DAC 结构体
1.DAC_HandleTypeDef:DAC 顶层句柄(外设总控制器)
全局唯一外设句柄,绑定寄存器、通道配置、DMA、状态锁。 typedef struct { DAC_TypeDef *Instance; // DAC寄存器基地址:DAC DAC_InitTypeDef Init; // 通道全局初始化参数 DMA_HandleTypeDef *DMA_Handle1; // 通道1 DMA句柄(DMAEN1) DMA_HandleTypeDef *DMA_Handle2; // 通道2 DMA句柄(DMAEN2) HAL_LockTypeDef Lock; // 互斥锁,多任务防冲突 __IO uint32_t State; // DAC运行状态机 __IO uint32_t ErrorCode; // 错误标志:DMA溢出、硬件故障 } DAC_HandleTypeDef;2.DAC_InitTypeDef:DAC 通道初始化配置(触发、波形、输出缓冲)
控制触发源、硬件波形发生器、输出缓冲,CubeMX 直接映射该结构体。 typedef struct { uint32_t DAC_Trigger; // TSELx触发源选择 uint32_t DAC_WaveGeneration; // WAVENx波形使能 uint32_t DAC_LFSRUnmask_TriangleAmplitude; // MAMPx幅值/噪声掩码 uint32_t DAC_OutputBuffer; // 输出缓冲运放开关 } DAC_InitTypeDef;3.DAC_ChannelConfTypeDef:通道独立配置(多通道时分配置)
STM32F1 有 DAC1_CH1 (PA4)、DAC1_CH2 (PA5) 双通道,每个通道独立配置。 typedef struct { uint32_t DAC_Channel; // 选择通道1/2 uint32_t DAC_DataAlignment; // DHRx寄存器12位对齐方式 } DAC_ChannelConfTypeDef;4.DAC_DMAHandleTypeDef:配套 DMA 句柄(DMA 波形输出专用)
DMA 波形输出必备,循环搬运波形表到 DHRx 寄存器。 typedef struct { DMA_TypeDef *Instance; DMA_InitTypeDef Init; HAL_LockTypeDef Lock; __IO uint32_t State; __IO uint32_t ErrorCode; } DMA_HandleTypeDef; // DMA_InitTypeDef 关键DAC配置 typedef struct { uint32_t Direction; // DMA_MEMORY_TO_PERIPH 内存→DAC外设 uint32_t PeriphInc; // DMA_PINC_DISABLE DHRx地址固定不变 uint32_t MemInc; // DMA_MINC_ENABLE 波形表内存自增 uint32_t PeriphDataAlignment; // DMA_PDATAALIGN_HALFWORD 16位 uint32_t MemDataAlignment; // DMA_MDATAALIGN_HALFWORD uint32_t Mode; // DMA_CIRCULAR 循环模式,持续输出波形 uint32_t Priority; } DMA_InitTypeDef;2、HAL_DAC_API
1.初始化/去初始化
HAL_DAC_Init(); 原型: HAL_StatusTypeDef HAL_DAC_Init(DAC_HandleTypeDef *hdac); 作用: 上层外设初始化,读取 hdac.Init 结构体配置,写入 DAC 控制寄存器(触发源、波形发生器、输出缓冲)。 调用位置: CubeMX 自动生成在 MX_DAC_Init() 中,先执行 HAL_DAC_MspInit 再执行本函数。 依赖: 必须提前填充 DAC_InitTypeDef 全局配置。 HAL_DAC_DeInit(); 原型: HAL_StatusTypeDef HAL_DAC_DeInit(DAC_HandleTypeDef *hdac); 作用: 复位 DAC 所有控制寄存器,关闭外设,调用底层 HAL_DAC_MspDeInit 释放时钟与引脚资源。 使用场景: 低功耗休眠前、动态切换功能时释放 DAC。2.IO操作函数
HAL_DAC_Start() 原型: HAL_StatusTypeDef HAL_DAC_Start(DAC_HandleTypeDef *hdac, uint32_t DAC_Channel); 功能: 使能指定 DAC 通道模拟输出通路,硬件上电就绪,支持直流 / 硬件三角波 / 噪声波。 示例: HAL_DAC_Start(&hdac, DAC_CHANNEL_1); HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 2048); HAL_DAC_Trigger_Software(&hdac, DAC_CHANNEL_1); HAL_DAC_Stop() 原型: HAL_StatusTypeDef HAL_DAC_Stop(DAC_HandleTypeDef *hdac, uint32_t DAC_Channel); 功能: 关闭通道输出,断开数模转换通路,输出变为高阻。 HAL_DAC_Start_DMA() 原型: HAL_StatusTypeDef HAL_DAC_Start_DMA(DAC_HandleTypeDef *hdac, uint32_t DAC_Channel, uint32_t *pData, uint32_t Length, uint32_t Alignment); 核心 DMA 波形输出 API,使能 DMAENx 通路,循环搬运内存波形表到 DHRx。 参数: pData:波形数组首地址。 Length:采样点数量。 Alignment:12 位左 / 右对齐。 HAL_DAC_Stop_DMA() 原型: HAL_StatusTypeDef HAL_DAC_Stop_DMA(DAC_HandleTypeDef *hdac, uint32_t DAC_Channel); 功能: 关闭 DMA 传输,同时停止 DAC 通道输出。 HAL_DAC_SetValue() 原型: HAL_StatusTypeDef HAL_DAC_SetValue(DAC_HandleTypeDef *hdac, uint32_t DAC_Channel, uint32_t Alignment, uint16_t Data); 功能: 写入 12 位数字值到 DHRx 保持寄存器,设定目标输出电压;数值范围 0~4095。 电压换算: Vout = Data * VDDA / 4096。 HAL_DAC_GetValue() 原型: uint16_t HAL_DAC_GetValue(DAC_HandleTypeDef *hdac, uint32_t DAC_Channel); 功能: 读取 DORx 锁存寄存器,获取当前正在转换输出的 12 位数值。3.外围控制函数
HAL_DAC_ConfigChannel() 原型: HAL_StatusTypeDef HAL_DAC_ConfigChannel(DAC_HandleTypeDef *hdac, DAC_ChannelConfTypeDef *sConfig, uint32_t DAC_Channel); 功能: 单通道独立配置 API,填充通道对齐方式、选择通道 1/2。 HAL_DAC_SetValue() 重复说明:同时属于 IO 操作与外设控制,负责写入输出数值,是修改 DAC 输出的核心接口。4.外设状态和错误功能
HAL_DAC_GetState() 原型: HAL_StateTypeDef HAL_DAC_GetState(DAC_HandleTypeDef *hdac); 返回 DAC 全局状态: HAL_DAC_STATE_RESET:未初始化。 HAL_DAC_STATE_READY:空闲就绪。 HAL_DAC_STATE_BUSY:DMA / 转换运行中。 HAL_DAC_STATE_ERROR:硬件故障。 HAL_DAC_GetError() 原型: uint32_t HAL_DAC_GetError(DAC_HandleTypeDef *hdac); 读取错误标志: HAL_DAC_ERROR_DMA、HAL_DAC_ERROR_TIMEOUT,定位故障类型。5.专用回调函数(中断 / DMA 完成事件,用户重写)
HAL_DAC_ConvCpltCallbackCh1() 原型: void HAL_DAC_ConvCpltCallbackCh1(DAC_HandleTypeDef *hdac); DMA 整段波形传输完成、单次触发更新完成后进入;用于一轮波形结束处理。 HAL_DAC_ConvHalfCpltCallbackCh1() 原型: void HAL_DAC_ConvHalfCpltCallbackCh1(DAC_HandleTypeDef *hdac); DMA 缓冲区一半数据传输完成,用于双缓冲实时刷新波形表,无输出断层。 HAL_DAC_ErrorCallbackCh1() 原型: void HAL_DAC_ErrorCallbackCh1(DAC_HandleTypeDef *hdac); DMA 传输错误、外设硬件故障时触发,用于故障告警处理。四、DAC 应用实例
1、静态可调电压输出
实现DAC固定输出、阶梯调压,输出0~3.3V连续电压。
1.CubeMX配置
| 配置项 | 参数值 | 说明 |
|---|---|---|
| Trigger | Software trigger | 软件触发,手动更新输出电压 |
| Wave generation mode | Disabled | 关闭硬件三角 / 噪声波,纯自定义直流输出 |
| Output Buffer | Enable | 开启输出缓冲,带载能力更强 |
2.电压转换函数
void Set_DAC_Voltage(float voltage) { // 确保输入电压在有效范围内 if (voltage < 0) voltage = 0; if (voltage > 3.3) voltage = 3.3; // 将目标电压转换为12位数字量 uint16_t dac_value = (uint16_t)(voltage / 4095 * 3.3); // 设置DAC输出值(12位右对齐_ HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, dac_value); }3.完整参考代码
#include "main.h" #include "dac.h" int main(void) { HAL_Init(); SystemClock_Config(); MX_DAC_Init(); // 使能DAC通道1输出 HAL_DAC_Start(&hdac, DAC_CHANNEL_1); // 模式1:固定静态电压输出(示例1.65V) Set_DAC_Voltage(1.65f); while(1) { // 保持恒定输出,无需操作 } // 模式2:阶梯连续调压 0~3.3V循环 float vol_step = 0.1f; // 阶梯步长0.1V float current_vol = 0.0f; while(1) { DAC_Set_Voltage(current_vol); current_vol += vol_step; // 到达3.3V重置归零 if(current_vol >= V_REF) { current_vol = 0.0f; } HAL_Delay(500); // 每500ms升一档 } }2、DAC+DMA 正弦波发生器
预先存储正弦波数据表,DMA自动刷新,输出连续正弦波形。
1.CubeMX配置
1)外设与引脚开启
DAC:开启DAC1,自动占用 PA4(DAC1_OUT1),引脚模式为Analog。
TIM 触发源:选择TIM6(基础定时器,无 IO 占用,推荐用于 DAC 触发)。
DMA:添加 DAC1 Channel1 DMA 请求,DMA1_Channel3。
RCC:系统时钟 72MHz,开启 APB1、APB2、DMA1 时钟。
2)DAC Parameter Settings(核心配置)
| 参数项 | 设置值 | 说明 |
|---|---|---|
| DAC Trigger | TIM6 Triggr Outevent | TIM6 更新事件定时触发 DAC 刷新电压 |
| Wave generation mode | Disabled | 关闭硬件三角波,使用 DMA 自定义正弦表 |
| Output Buffer | Enable | 开启输出缓冲,提升带载能力 |
3)DMA Settings 配置(DAC1_CH1)
Mode:Circular 循环模式(波形循环不间断)。
Direction:Memory To Peripheral 内存波形表→DAC 外设。
PeriphInc:Disable(DHR 寄存器地址固定)。
MemInc:Enable(波形数组自动递增读取)。
Periph Data Width:Half Word。
Memory Data Width:Half Word。
Priority:Medium。
4)TIM6 定时触发配置(控制正弦波频率)
Prescaler:71,Counter Period:999。
定时器时钟 APB1=72MHz,分频后计数时钟 = 72M/(71+1)=1MHz。
更新事件 TRGO 周期 = (999+1)/1MHz = 1ms,DAC 每 1ms 刷新一次波形点。
Trigger Output:Update Event(更新事件作为 TRGO 触发 DAC)。
关闭 TIM6 输出通道,仅内部触发使用。
5)NVIC 设置
可选:开启 DMA2 Channel3 中断,用于波形半满 / 全满回调;无动态波形修改可关闭中断。
2.正弦波形表生成(12 位,中点 2048,幅值 2047,0~4095)
代码: #define WAVE_POINT_NUM 64 // 一个周期64个采样点 // 正弦表:偏移2048,范围0~4095,对应0~3.3V const uint16_t sin_wave_table[WAVE_POINT_NUM] = { 2048,2248,2444,2630,2796,2937,3048,3127,3170,3174,3140,3068, 2964,2834,2683,2518,2344,2169,1997,1834,1684,1551,1438,1347, 1283,1247,1240,1263,1315,1393,1494,1616,1754,1905,2048,2190, 2339,2477,2598,2698,2774,2823,2844,2836,2799,2736,2648,2539, 2414,2277,2132,1984,1840,1703,1577,1468,1378,1312,1270,1249, 1249,1271,1314,1376,1456,1551 }; 频率计算公式: 单周期刷新总时间 = WAVE_POINT_NUM × TIM6_TRGO周期。 本例:64 × 1ms = 64ms / 周期 → 正弦波频率 ≈ 15.625Hz。 修改 TIM 预分频 / 重载值可调整输出频率。3.完整代码
#include "main.h" #include "dac.h" #include "dma.h" #include "tim.h" extern DAC_HandleTypeDef hdac; extern TIM_HandleTypeDef htim6; #define WAVE_POINT_NUM 64 const uint16_t sin_wave_table[WAVE_POINT_NUM] = { 2048,2248,2444,2630,2796,2937,3048,3127,3170,3174,3140,3068, 2964,2834,2683,2518,2344,2169,1997,1834,1684,1551,1438,1347, 1283,1247,1240,1263,1315,1393,1494,1616,1754,1905,2048,2190, 2339,2477,2598,2698,2774,2823,2844,2836,2799,2736,2648,2539, 2414,2277,2132,1984,1840,1703,1577,1468,1378,1312,1270,1249, 1249,1271,1314,1376,1456,1551 }; int main(void) { HAL_Init(); SystemClock_Config(); MX_DMA_Init(); MX_DAC_Init(); MX_TIM6_Init(); // 1. 启动DAC DMA循环传输,绑定正弦表 HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*)sin_wave_table, WAVE_POINT_NUM, DAC_ALIGN_12B_R); // 2. 启动TIM6,输出TRGO定时触发DAC刷新波形 HAL_TIM_Base_Start(&htim6); while (1) { // 硬件全自动输出正弦波,CPU无占用,无需处理 } }4.可选DMA中断回调(动态修改波形 / 状态监测)
开启 DMA 中断后,重写回调函数实现双缓冲更新波形: // DMA缓冲区一半传输完成 void HAL_DAC_ConvHalfCpltCallbackCh1(DAC_HandleTypeDef *hdac) { if(hdac->Instance == DAC) { // 前32点传输完成,可修改前半段波形数据 } } // 完整一轮波形传输完成 void HAL_DAC_ConvCpltCallbackCh1(DAC_HandleTypeDef *hdac) { if(hdac->Instance == DAC) { // 64点全部输出完毕,可修改后半段波形数据 } } // DMA传输错误处理 void HAL_DAC_ErrorCallbackCh1(DAC_HandleTypeDef *hdac) { Error_Handler(); }五、DAC 核心要点与避坑大全
1、必考核心知识点
DAC是ADC反向外设,12位精度,输出范围0~3.3V。
核心公式:输出电压 = DAC值 × 3.3 / 4095。
F103 DAC双通道:PA4、PA5模拟输出。
静态电压用软件赋值,动态波形用 TIM+DMA 组合。
DAC无驱动能力,仅输出信号,需运放放大驱动负载。
2、高频坑点
直接用DAC驱动喇叭、电机等负载,导致输出失真、烧坏引脚。
未开启HAL_DAC_Start,赋值后无电压输出。
波形输出未配定时器,波形频率紊乱、抖动严重。
DMA缓冲区使用局部变量,导致波形错乱死机。
忽略VDDA电源波动,导致输出电压精度偏差。
3、工程优化技巧
DAC输出后端增加 RC 滤波电路,平滑波形、减少毛刺。
大功率负载搭配运算放大器、功率放大电路。
高频波形采用多点数据表,提升波形平滑度。
固定基准电压输出,可配合ADC做硬件校准。
4、工程万能搭配方案
静态调压:单纯HAL_DAC_SetValue软件赋值。
动态波形:TIM触发 + DMA循环 + 波形数据表。
高精度基准:DAC输出+RC滤波+运放跟随。
5、一句话总结
DAC是STM32数模转换外设,可将04095数字量转为03.3V模拟电压,支持静态电压输出和TIM+DMA动态波形生成,是模拟信号输出、波形发生器、电压基准校准的核心外设。
六、全篇总结
DAC 是嵌入式模拟信号输出的唯一核心外设,与ADC成对互补。
掌握DAC静态调压、DMA动态波形输出、定时器触发配置,即可完成信号发生器、模拟调压、音频输出、闭环校准等高级项目功能,是进阶嵌入式开发的必备技能。