Microcontroller: Choosing Between DAC and PWM for Audio Output? Test Data Reveals the Answer!

Hello everyone, I am Xiao Chen. Today, let’s discuss a practical topic: how can microcontrollers output audio signals? Currently, there are two mainstream solutions: directly using a DAC (Digital-to-Analog Converter) or using PWM (Pulse Width Modulation) for analog output.

Both methods have their pros and cons, and many beginners can easily get confused. Today, I will guide you through the principles and practical tests to thoroughly understand the differences between these two solutions and how to choose the most suitable one for different scenarios.

Basic Principles of DAC and PWM

Introduction to DAC Principles

The working principle of a DAC (Digital-to-Analog Converter) is simply like an “electronic faucet”. The microcontroller tells the DAC what voltage to output (for example, any value between 0-3.3V), and the DAC can precisely control the “flow rate” to output the corresponding analog voltage.

For a common 12-bit DAC, it can map digital values from 0 to 4095 to corresponding voltage values.

If the reference voltage is 3.3V, then a digital value of 0 corresponds to 0V, and 4095 corresponds to 3.3V, with intermediate values mapped proportionally. Every change of 1 in the digital value results in an output voltage change of approximately 0.8mV. This precise control makes DACs particularly suitable for applications requiring smooth variations in analog signal output, such as audio.

Introduction to PWM Principles

PWM (Pulse Width Modulation) takes a completely different approach. It is like a light bulb that switches on and off rapidly: by quickly toggling between on and off states and adjusting the time ratio (duty cycle), it creates the perception that the light is continuously illuminated at different brightness levels.

For example, a 50% duty cycle means that the on and off times each occupy half of the cycle. If the switching is fast enough, the human eye perceives a bulb with half brightness. Similarly, the microcontroller can produce an approximate analog voltage by rapidly switching the pin’s high and low levels and controlling the time ratio of the high level, after connecting to a low-pass filter circuit.

Hardware Circuit Comparison

DAC Hardware Implementation

Microcontrollers with built-in DACs (such as the STM32F4 series) can be used directly; you just need to configure the pins correctly:

          ┌────────────┐
          │            │
          │            │     470Ω
          │    MCU     ├────WWW────┬─── Output Signal
          │   DAC Pin  │            │
          │            │           === 100nF
          └────────────┘            │
                                    └─── GND

If the microcontroller does not have a built-in DAC, an external DAC chip (such as MCP4725) can be connected:

Microcontroller: Choosing Between DAC and PWM for Audio Output? Test Data Reveals the Answer!

          ┌────────────┐           ┌────────────┐
          │            │I2C        │            │
          │    MCU     ├───────────┤   MCP4725  │
          │            │           │    DAC     ├── Output Signal
          │            │           │            │
          └────────────┘           └────────────┘

PWM Hardware Implementation

PWM output requires an RC low-pass filter circuit to convert the square wave signal into an analog voltage:

          ┌────────────┐
          │            │           1kΩ
          │    MCU     ├───────WWW───┬─── Output Signal
          │ PWM Output  │               │
          │    Pin     │              === 10nF
          └────────────┘               │
                                       └─── GND

Notes: The choice of filter circuit is crucial. The cutoff frequency of the filter needs to be much lower than the PWM frequency but higher than the highest frequency of the audio signal. For audio applications, I usually recommend setting the PWM frequency to at least 50kHz to avoid audible noise.

Code Examples

STM32 DAC Output Example

// Initialize DAC
void DAC_Init(void) {
    // Enable DAC clock
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);

    // Configure GPIO as analog input
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;  // DAC Channel 1 corresponds to PA4
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // Configure DAC
    DAC_InitTypeDef DAC_InitStruct;
    DAC_InitStruct.DAC_Trigger = DAC_Trigger_None;  // No trigger source, software trigger
    DAC_InitStruct.DAC_WaveGeneration = DAC_WaveGeneration_None;  // No waveform generation
    DAC_InitStruct.DAC_OutputBuffer = DAC_OutputBuffer_Enable;  // Enable output buffer
    DAC_Init(DAC_Channel_1, &DAC_InitStruct);

    // Enable DAC
    DAC_Cmd(DAC_Channel_1, ENABLE);
}

// Set DAC output
void DAC_SetValue(uint16_t value) {
    // Write 12-bit data right aligned
    DAC_SetChannel1Data(DAC_Align_12b_R, value);
}

STM32 PWM Output Example

// Initialize PWM (using TIM2, Channel 1)
void PWM_Init(void) {
    // Enable timer and GPIO clock
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

    // Configure GPIO as alternate function
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;  // TIM2_CH1
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // Connect GPIO to timer
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource0, GPIO_AF_TIM2);

    // Configure timer base unit
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct;
    TIM_TimeBaseStruct.TIM_Prescaler = 0;
    TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseStruct.TIM_Period = 4095;  // 12-bit resolution
    TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStruct);

    // Configure PWM mode
    TIM_OCInitTypeDef TIM_OCInitStruct;
    TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStruct.TIM_Pulse = 0;
    TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OC1Init(TIM2, &TIM_OCInitStruct);

    // Enable preload register
    TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);
    TIM_ARRPreloadConfig(TIM2, ENABLE);

    // Start timer
    TIM_Cmd(TIM2, ENABLE);
}

// Set PWM output
void PWM_SetValue(uint16_t value) {
    TIM_SetCompare1(TIM2, value);
}

Comparative Testing of DAC and PWM Audio Output

Microcontroller: Choosing Between DAC and PWM for Audio Output? Test Data Reveals the Answer!

Last year, during a smart doorbell project, I conducted a detailed comparative test. The system required playing several short prompt sounds, with low audio quality requirements but a need to minimize cost and resource consumption.

Test Platform

  • MCU: STM32F407VGT6 (with built-in 12-bit DAC)
  • Audio Sampling Rate: 8kHz
  • Output Methods:
  1. Direct output from built-in DAC
  2. TIM2 PWM output (72MHz, 50kHz PWM frequency)
  • Test Audio: 500Hz sine wave, 1kHz sine wave, and short human voice phrases
  • Test Results

    Parameter DAC Solution PWM Solution
    Total Harmonic Distortion (THD) 0.3% 1.2%
    Signal-to-Noise Ratio (SNR) 72dB 61dB
    CPU Usage 15% 10%
    Output Stability Very stable Affected by PWM frequency
    Circuit Complexity Simple Requires additional filtering
    Applicable Scenarios High-quality audio output Simple prompt sounds, low-cost solutions

    An interesting finding was that, when the PWM frequency is below 20kHz, even after filtering, the waveform on the oscilloscope appears smooth, but the ear can still clearly hear high-frequency “hissing” background noise. This is because the human ear is very sensitive to high-frequency noise, and even small residual high-frequency components can be detected.

    Practical Application Scenarios for Both Solutions

    Scenarios Suitable for DAC

    1. Music Players: Devices that require high-quality audio output
    2. Medical Devices: Such as ultrasound devices that require precise waveform generation
    3. Professional Audio Equipment: Audio interfaces, recording devices, etc.
    4. Sensor Signal Simulation: Testing devices that require precise simulation of sensor output signals

    Once, while working on an ECG monitor simulator, I had to use a DAC to accurately simulate various weak waveforms of arrhythmias. The PWM solution was completely unfeasible in this application, as even slight waveform distortion could lead to misdiagnosis.

    Scenarios Suitable for PWM

    1. Simple Prompt Sounds: Such as electronic doorbells, appliance operation prompts
    2. Toy Sound Effects: Simple sound effects that do not require high fidelity
    3. Low-Cost Voice Devices: Prompt systems with low audio quality requirements
    4. Using Microcontrollers Without Built-in DACs: Such as the Arduino series

    In a low-cost industrial controller project, I used the ATmega328P microcontroller (which does not have a built-in DAC) to output alarm sounds. By properly setting the PWM frequency and filter circuit, I created an alarm that cost less than 5 yuan, which was sufficient for simple “beep” prompt sounds.

    Common Issues and Solutions

    Common Issues with DAC

    1. Limited Output Voltage Range:

    • Problem: The typical DAC output range is 0-3.3V or 0-5V
    • Solution: An operational amplifier can be added for amplification or level shifting if needed
  • High Output Impedance:

    • Problem: Directly connecting to a low-impedance load can cause voltage drop
    • Solution: Add an op-amp buffer stage or ensure load impedance ≥ 10kΩ

    Common Issues with PWM

    1. Difficult Frequency Selection:

    • Problem: A low PWM frequency can cause noticeable noise, while a high frequency can reduce resolution
    • Solution: For audio applications, it is recommended to set the PWM frequency ≥ 50kHz, and it should be much higher than the audio sampling rate
  • Poor Filter Design:

    • Problem: Insufficient filtering leads to high-frequency noise, while excessive filtering causes useful frequency attenuation
    • Solution: The cutoff frequency formula for an RC filter is f = 1/(2πRC), and for audio applications, it is usually set at 15-20kHz
  • DC Offset:

    • Problem: PWM output may have a DC offset after filtering
    • Solution: Add a capacitor to block DC, or compensate for the offset in software

    Implementation Recommendations

    If you are a beginner looking to try audio output with microcontrollers, here are some suggestions:

    1. First, assess your audio quality needs – high-fidelity music requires a DAC, while simple prompt sounds are sufficient with PWM
    2. Check if your microcontroller has a built-in DAC – many STM32 and ESP32 series do
    3. When starting testing, use a simple sine wave as the output signal
    4. Use an oscilloscope to measure the output waveform, do not rely solely on your ears for judgment
    5. Remember to add output buffering and filtering circuits, especially when connecting to speakers
    6. Consider adding an audio amplifier (such as LM386) to drive the speakers

    For practical exercises, try starting with a simple tone generator, then gradually transition to playing simple monophonic melodies, and finally challenge yourself with more complex polyphonic audio. Record the impact of different parameters (PWM frequency, filter component values, etc.) on audio quality to build your own experience database.

    Most importantly, always control the volume when testing audio output! I once almost burned out a small speaker because I forgot to limit the output level, which also gave my ears quite a “surprise”. Safety first, protect your equipment and hearing!

    Leave a Comment