ESP32 DAC 真模拟输出入门:dacWrite 与「假 PWM」的区别
前置知识:PWM 完全入门。PWM 调光的本质是「快速开关骗过眼睛」,输出引脚上其实只有 0V 或 3.3V 两种电平。本篇讲的是 DAC——货真价实的连续电压输出。
一、DAC vs PWM:一字之差,本质不同
| 维度 | PWM(占空比) | DAC(真模拟) |
|---|---|---|
| 输出电平 | 只有 0V / 3.3V(高速跳变) | 任意中间电压(连续) |
| 原理 | 时间平均「骗」负载 | 电阻网络直接分压 |
| 可用引脚 | 任意 GPIO | 仅 GPIO25/26(经典 ESP32) |
| 分辨率 | 理论可到 16 位 | 8 位(256 级) |
| 需要滤波吗 | 视负载而定 | 不需要 |
| 典型用途 | LED 亮度、电机调速 | 波形发生、偏置电压、音频 |
一句话:PWM 是「占空比艺术」,DAC 是「电压直出」。驱动 LED 用 PWM 就够;要生成正弦波或给运放提供偏置,必须 DAC。
二、芯片可用性速查(重要)
| 芯片 | DAC | 说明 |
|---|---|---|
| 经典 ESP32 | ✅ 2 路(GPIO25/26,通道 1/2) | 8 位 |
| ESP32-S2 | ✅ 2 路(GPIO17/18) | 8 位 |
| ESP32-S3 | ❌ 无 DAC | 用外置 DAC(如 MCP4725,走 I2C) |
| ESP32-C3/C6 | ❌ 无 DAC | 同上 |
新项目若必须真模拟输出且用 S3/C3:MCP4725 模组(12 位、I2C、约 ¥3)是标准答案——I2C 用法见总线篇。
三、dacWrite 五分钟上手
#define DAC_PIN 25 // 只能是 25 或 26!
void setup() { /* DAC 无需 pinMode 初始化 */ }
void loop() {
dacWrite(DAC_PIN, 0); // 输出 0V
delay(1000);
dacWrite(DAC_PIN, 128); // 输出约 1.65V
delay(1000);
dacWrite(DAC_PIN, 255); // 输出约 3.3V
delay(1000);
}
- 8 位 = 0~255,每级约 12.9mV
- 输出阻抗较高(不适合直接驱动低阻负载),带运放缓冲才稳
- 输出电压是「约」——实测有偏差,精确场合需要标定
四、实战:生成正弦波
#define DAC_PIN 25
void setup() {
Serial.begin(115200);
}
void loop() {
// 生成 100Hz 正弦:每周期 100 点,每点 100µs
for (int i = 0; i < 100; i++) {
float v = (sinf(i * 2 * PI / 100) + 1) / 2 * 255;
dacWrite(DAC_PIN, (int)v);
delayMicroseconds(100);
}
}
用硬件定时器替换
delayMicroseconds可获得稳定的频率输出——定时器篇的四层选型在此直接适用。
性能天花板:DAC 转换速率约 8µs/次 → 理论最高方波 ~1MHz、实用正弦几十 kHz。做音频(语音级)可行,HiFi 不现实。
五、进阶:音频入门(0.5 秒了解)
DAC + 功放(MAX98357A)就是最简 ESP32 音频链路。但现代做法是 I2S 数字音频(精度高、不占 DAC)——属于 L3 知识点,本站后续专文。DAC 音频的价值在于理解原理:把 PCM 采样值按采样率循环 dacWrite 就是裸音频输出。
六、避坑速查表
| 场景 | 结论 |
|---|---|
用 S3/C3 调 dacWrite | 芯片无 DAC,编译可能过但无输出;改用 MCP4725 |
| 想在 GPIO25/26 用 ADC | GPIO25/26 复用 ADC2 通道——开 Wi-Fi 后冲突,别混用 |
| DAC 输出带不动负载 | 输出阻抗高,加运放电压跟随器 |
| 波形频率不稳 | delay 抖动,改硬件定时器驱动 |
| 要 12 位精度 | 外置 MCP4725(I2C),内置只有 8 位 |
| LED 调光该用 DAC 吗 | 不,PWM 足够且任意引脚可用 |
七、延伸阅读
- 🧭 前作:PWM 完全入门——占空比调光
- ⏭️ 下一篇:UART/I2C/SPI 三大总线——外设通信(含外置 DAC 接法)
版权声明:本文采用 CC BY-SA 4.0 协议授权。转载与二创请注明出处:ESPDIY 创客工坊 - espdiy.com
ESP32 DAC True Analog Output Guide: dacWrite vs “Fake PWM”
Prerequisite: PWM Complete Guide. PWM dimming is really “switching fast enough to fool the load” — the pin only ever sits at 0V or 3.3V. This article covers the DAC: genuine continuous voltage output.
1. DAC vs PWM: one letter apart, fundamentally different
| Aspect | PWM (duty cycle) | DAC (true analog) |
|---|---|---|
| Output levels | Only 0V / 3.3V (fast switching) | Any intermediate voltage (continuous) |
| Principle | Time-averaging “fools” the load | Resistor ladder divides directly |
| Pins | Any GPIO | GPIO25/26 only (classic ESP32) |
| Resolution | Up to 16 bits theoretically | 8 bits (256 steps) |
| Filtering needed | Depends on the load | No |
| Typical uses | LED brightness, motor speed | Waveform generation, bias voltage, audio |
One-liner: PWM is duty-cycle art; DAC is voltage direct out. LEDs want PWM; sine waves and op-amp bias voltages want a DAC.
2. Per-chip availability (important)
| Chip | DAC | Notes |
|---|---|---|
| Classic ESP32 | ✅ 2 channels (GPIO25/26) | 8-bit |
| ESP32-S2 | ✅ 2 channels (GPIO17/18) | 8-bit |
| ESP32-S3 | ❌ no DAC | Use an external DAC (e.g. MCP4725 over I2C) |
| ESP32-C3/C6 | ❌ no DAC | Same |
New project on S3/C3 that truly needs analog out: the MCP4725 module (12-bit, I2C, ~¥3) is the standard answer — wiring covered in the buses guide.
3. dacWrite in five minutes
#define DAC_PIN 25 // must be 25 or 26!
void setup() { /* DAC needs no pinMode setup */ }
void loop() {
dacWrite(DAC_PIN, 0); // 0V
delay(1000);
dacWrite(DAC_PIN, 128); // ~1.65V
delay(1000);
dacWrite(DAC_PIN, 255); // ~3.3V
delay(1000);
}
- 8 bits = 0–255, ~12.9mV per step
- The output impedance is high (don’t drive low-impedance loads directly); buffer with an op-amp follower
- Voltages are “approximately” — calibrate if you need precision
4. Project: generating a sine wave
#define DAC_PIN 25
void setup() {
Serial.begin(115200);
}
void loop() {
// 100Hz sine: 100 points per cycle, 100µs each
for (int i = 0; i < 100; i++) {
float v = (sinf(i * 2 * PI / 100) + 1) / 2 * 255;
dacWrite(DAC_PIN, (int)v);
delayMicroseconds(100);
}
}
Replacing
delayMicrosecondswith a hardware timer yields a rock-stable frequency — the four-layer selection from the timers guide applies directly.
Ceiling: DAC conversion takes ~8µs → theoretical max square wave ~1MHz, practical sines in the tens of kHz. Voice-grade audio: fine. HiFi: no.
5. Primer: audio in half a page
DAC + amp (MAX98357A) is the simplest ESP32 audio chain. The modern approach is I2S digital audio (higher precision, doesn’t consume the DAC) — an L3 topic with its own article. DAC audio’s value is understanding the principle: looping dacWrite over PCM samples at the sample rate is bare-metal audio output.
6. Pitfall cheat sheet
| Scenario | Verdict |
|---|---|
Calling dacWrite on S3/C3 | No DAC — may compile but outputs nothing; use MCP4725 |
| Wanting ADC on GPIO25/26 | These are also ADC2 channels — conflict once Wi-Fi starts |
| DAC can’t drive the load | High output impedance — add an op-amp follower |
| Wobbly waveform frequency | delay jitter — drive from a hardware timer |
| Need 12-bit precision | External MCP4725 (I2C); the built-in one is 8-bit |
| Should LEDs use the DAC? | No — PWM is enough and works on any pin |
7. Further reading
- 🧭 Previous: PWM Complete Guide — duty-cycle dimming
- ⏭️ Next: UART/I2C/SPI — the three buses (includes wiring an external DAC)
License: CC BY-SA 4.0. Repost & remix with attribution: ESPDIY Maker Lab - espdiy.com