Simple sketch for "analog" output from digital pins using PDM

Hiya all! XD

I've written a simple function to output analog samples through the arduino uno's digital pins using pulse density modulation.

To use, just call it from the loop() section of your sketch. As it works fully in software, the more times you can call it per second, the better it will work!... heh, heh... hopefully your loop runs really fast :cold_sweat: :stuck_out_tongue:

So, without further ado:

/*
    playPdmSample8( pin, sample, sampleRate_ms )
    
    Plays back the appropriate sample on the indicated pin
    and returns the number of samples to increment for playback
    at the given sample rate.
    
    Maintains data for each pin -- you could use all fourteen at once!

    pin              - the digital pin to use as output
    sample           - the 8-bit sample to play
    sampleRate_ms    - number of samples to play per ***MILLISECOND***
*/

static unsigned long playPdmSample8(int pin, byte sample, unsigned long sampleRate_ms) {
    // Pulse density modulation data (pin-dependent)
    static byte            err[14]    = {};
    static byte            out[14]    = {};
    
    // Sample rate data
    static unsigned long   time[14]   = {micros()};   // Relative starting time ***IN MICROSECONDS***
    static unsigned long   sampleIncr = 0;            // Number of samples to be incremented to maintain sample rate
    
    sampleIncr = (sampleRate_ms * (micros() - time[pin])) >> 10; // The bit shift is a hackish divide-by-1000
    
    if(sampleIncr >= 1) {
        // Output a sample using pulse density modulation
        
        if(sample > err[pin]) {
            if(out[pin] == 0) {
                digitalWrite(pin, HIGH);
                out[pin] = 255;
            }
        } else {
            if(out[pin] == 255) {
                digitalWrite(pin, LOW);
                out[pin] = 0;
            }
        }
        
        err[pin] += (out[pin] - sample);
        
        
        // Reset the time
        time[pin] = micros();
    }
    
    return(sampleIncr);
}

Hopefully you find this marginally useful or at least marginally interesting! :slight_smile:

Just a demo :slight_smile: :

const int    LED = 13;
byte         sinLUT[256];

void setup() {
    pinMode(LED, OUTPUT);
    
    for(long i = 0; i < sizeof(sinLUT); i++) {
        sinLUT[i] = (255.0 * (sin((6283.0 * i) / ((sizeof(sinLUT)-1) * 1000.0)) + 1.0)) / 2;
    }
}

void loop() {
    // output at 1 kHz sample rate
    playPdmSample8(LED, sinLUT[byte(millis() >> 2)], 1);
}

/* ... code for the playPdmSample8 function ... */

You should see your onboard LED fade at a 1 Hz sine wave :slight_smile: