10. DAC 输出
📝本节您将了解 DAC(数模转换)的工作原理,学习如何配置 MSPM0G3519 的 DAC 外设,实现输出指定大小的模拟电压。
🏆本章目标
1️⃣ 理解 DAC 的工作原理和输出电压计算
2️⃣ 了解 MSPM0G3519 的 DAC 外设资源
3️⃣ 通过 SysConfig 配置 DAC 输出
4️⃣ 实现输出固定电压和简易正弦波形
10.1 DAC 基本概念
DAC(Digital-to-Analog Converter,数模转换器)是 ADC 的逆操作——将数字值转换为对应的模拟电压输出。
MCU 内部的运算结果是数字量,某些应用需要将其转换为模拟信号输出到外部电路(如音频信号、控制电压、波形发生等),这就需要 DAC。
输出电压计算
输出电压 = DAC数字值 × Vref / (2^分辨率 - 1)对于 12-bit DAC,Vref = 3.3V:
输出电压 = DAC值 × 3.3 / 4095| DAC 数字值 | 输出电压 |
|---|---|
| 0 | 0V |
| 1024 | ≈ 0.825V |
| 2048 | ≈ 1.65V |
| 4095 | 3.3V |
反向计算(目标电压 → DAC 值)
DAC值 = 目标电压 × 4095 / 3.3例如需要输出 1.5V:DAC 值 = 1.5 × 4095 / 3.3 ≈ 1861
10.2 MSPM0G3519 DAC 资源
MSPM0G3519 内置 12-bit DAC 模块:
| 参数 | 规格 |
|---|---|
| 分辨率 | 12-bit(0~4095) |
| 输出通道 | 1 路 |
| 参考电压 | VDDA(3.3V)/ 内部参考 |
| 输出引脚 | 固定引脚(见数据手册) |
| 建立时间 | 数微秒级 |
| 输出驱动 | 内置缓冲运放(可选) |
DAC vs PWM 模拟输出
DAC 和 PWM+滤波都可以实现模拟电压输出,但有本质区别:
- DAC:真正的模拟输出,无纹波,响应快,适合波形生成
- PWM+RC 滤波:数字方波经低通滤波后近似模拟,有纹波残留,适合简单的直流电平控制
10.3 SysConfig 配置 DAC
10.3.1 添加 DAC 外设
- 打开
.syscfg文件 - 左侧面板找到 DAC12 选项卡
- 点击 ADD 添加 DAC 实例
10.3.2 配置参数
| 参数 | 设置值 | 说明 |
|---|---|---|
| Name | DAC12_0 | 实例名称 |
| Resolution | 12 Bits | 12 位分辨率 |
| Voltage Reference | VDDA | 参考电压 3.3V |
| Output Enable | Enable | 使能输出引脚 |
| Output Buffer | Enable | 使能输出缓冲(增强驱动能力) |
保存并编译。
输出缓冲
DAC 输出缓冲(内置运放)可以增强输出驱动能力,使其能直接驱动较大负载。如果 DAC 输出仅接到高阻抗输入(如 ADC 输入、运放输入),可以不使能缓冲以降低功耗。
10.4 DAC 相关函数
常用 DAC DriverLib 函数
设置 DAC 输出值:
void DL_DAC12_output12(DAC12_Regs *dac, uint16_t value);设置 12-bit DAC 输出值(0~4095)。
使能 DAC 输出:
void DL_DAC12_enable(DAC12_Regs *dac);禁止 DAC 输出:
void DL_DAC12_disable(DAC12_Regs *dac);10.5 实验一:输出固定电压
输出 1.65V(VDD 的一半):
#include "ti_msp_dl_config.h"
int main(void)
{
SYSCFG_DL_init();
// 使能 DAC
DL_DAC12_enable(DAC12_0_INST);
// 输出 1.65V:4095 × 1.65 / 3.3 = 2048
DL_DAC12_output12(DAC12_0_INST, 2048);
while (1) {
// DAC 持续输出,无需循环操作
__WFI();
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
使用万用表测量 DAC 输出引脚电压,应接近 1.65V。
可变电压输出
通过串口接收指令,动态调整 DAC 输出电压:
#include "ti_msp_dl_config.h"
#include <stdio.h>
#include <stdlib.h>
void uart_send_string(const char *str)
{
while (*str) {
while (DL_UART_Main_isBusy(UART_0_INST));
DL_UART_Main_transmitData(UART_0_INST, *str++);
}
}
volatile uint8_t rx_buf[8];
volatile uint8_t rx_idx = 0;
volatile uint8_t rx_done = 0;
int main(void)
{
SYSCFG_DL_init();
NVIC_EnableIRQ(UART_0_INT_IRQN);
DL_DAC12_enable(DAC12_0_INST);
char buf[64];
uart_send_string("DAC Ready. Send voltage in mV (0-3300):\r\n");
while (1) {
if (rx_done) {
rx_done = 0;
rx_buf[rx_idx] = '\0';
// 解析输入的毫伏值
uint32_t mv = atoi((char *)rx_buf);
if (mv > 3300) mv = 3300;
// 换算为 DAC 值
uint16_t dac_val = (uint16_t)(mv * 4095 / 3300);
DL_DAC12_output12(DAC12_0_INST, dac_val);
sprintf(buf, "Set: %lu mV (DAC: %u)\r\n", mv, dac_val);
uart_send_string(buf);
rx_idx = 0;
}
}
}
void UART0_IRQHandler(void)
{
switch (DL_UART_Main_getPendingInterrupt(UART_0_INST)) {
case DL_UART_MAIN_IIDX_RX: {
uint8_t ch = DL_UART_Main_receiveData(UART_0_INST);
if (ch == '\r' || ch == '\n') {
rx_done = 1;
} else if (rx_idx < 7) {
rx_buf[rx_idx++] = ch;
}
break;
}
default:
break;
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
10.6 实验二:输出正弦波
利用 DAC 快速更新输出值,可以生成简单的波形。以下示例生成正弦波(使用查找表避免实时计算三角函数):
#include "ti_msp_dl_config.h"
// 64 点正弦波查找表(0~4095 范围,中心 2048)
const uint16_t sin_table[64] = {
2048, 2249, 2447, 2642, 2831, 3013, 3185, 3347,
3496, 3631, 3750, 3854, 3940, 4007, 4056, 4086,
4095, 4086, 4056, 4007, 3940, 3854, 3750, 3631,
3496, 3347, 3185, 3013, 2831, 2642, 2447, 2249,
2048, 1847, 1649, 1454, 1265, 1083, 911, 749,
600, 465, 346, 242, 156, 89, 40, 10,
0, 10, 40, 89, 156, 242, 346, 465,
600, 749, 911, 1083, 1265, 1454, 1649, 1847
};
int main(void)
{
SYSCFG_DL_init();
DL_DAC12_enable(DAC12_0_INST);
uint8_t idx = 0;
while (1) {
DL_DAC12_output12(DAC12_0_INST, sin_table[idx]);
idx = (idx + 1) & 0x3F; // 0~63 循环
// 延时控制输出频率
// 64点 × 每点延时 → 一个完整周期
delay_cycles(500); // 约 15.6μs/点 → 周期约 1ms → 频率约 1kHz
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
波形频率计算
波形频率 = 1 / (采样点数 × 每点间隔时间)64 个采样点,每点间隔约 15.6μs:
- 周期 = 64 × 15.6μs ≈ 1ms
- 频率 ≈ 1kHz
调整 delay_cycles 的参数可改变输出频率。更快的更新速率需配合定时器中断或 DMA。
可以用示波器观察 DAC 输出引脚的波形,应看到近似正弦波。
10.7 DAC + ADC 联动验证
将 DAC 输出引脚用杜邦线连接到 ADC 输入引脚,实现自测:DAC 输出已知电压,ADC 采集后验证读数是否一致。
#include "ti_msp_dl_config.h"
#include <stdio.h>
void uart_send_string(const char *str)
{
while (*str) {
while (DL_UART_Main_isBusy(UART_0_INST));
DL_UART_Main_transmitData(UART_0_INST, *str++);
}
}
int main(void)
{
SYSCFG_DL_init();
DL_DAC12_enable(DAC12_0_INST);
char buf[64];
uint16_t dac_values[] = {0, 1024, 2048, 3072, 4095};
while (1) {
for (int i = 0; i < 5; i++) {
// DAC 输出
DL_DAC12_output12(DAC12_0_INST, dac_values[i]);
delay_cycles(3200000); // 等待 100ms 稳定
// ADC 采集
DL_ADC12_enableConversions(ADC12_0_INST);
DL_ADC12_startConversion(ADC12_0_INST);
while (!DL_ADC12_isSampleReady(ADC12_0_INST, DL_ADC12_MEM_IDX_0));
uint16_t adc_val = DL_ADC12_getMemResult(ADC12_0_INST, DL_ADC12_MEM_IDX_0);
DL_ADC12_disableConversions(ADC12_0_INST);
uint32_t dac_mv = (uint32_t)dac_values[i] * 3300 / 4095;
uint32_t adc_mv = (uint32_t)adc_val * 3300 / 4095;
sprintf(buf, "DAC: %u (%lu mV) -> ADC: %u (%lu mV)\r\n",
dac_values[i], dac_mv, adc_val, adc_mv);
uart_send_string(buf);
}
uart_send_string("---\r\n");
delay_cycles(32000000); // 1 秒后重复
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
DAC 输出值和 ADC 读数应非常接近(误差通常在几个 LSB 以内)。
10.8 知识总结
| 内容 | 说明 |
|---|---|
| DAC 功能 | 数字值 → 模拟电压 |
| 分辨率 | 12-bit(0~4095) |
| 输出范围 | 0V ~ Vref(3.3V) |
| 电压计算 | V = DAC值 × 3.3 / 4095 |
| 反向计算 | DAC值 = V × 4095 / 3.3 |
| 波形生成 | 查找表 + 定时更新 |
| 输出缓冲 | 驱动低阻负载时需使能 |
DAC 使你能够输出精确可控的模拟电压。下一章将学习 DMA(直接内存访问),实现数据在外设和内存之间的自动搬运,进一步释放 CPU 资源。