Software Class D amplifier

Background: A class D amplifier drives a load/speaker by two mossfets in a push-pull configuration with their gates being controlled digitally, with pulse-width modulation.

The class D amplifier changes the signal to digital, it is typically between 250kHz and 1.5MHz (maximintegrated) With a Photon being 120MHz and a low level pin toggle in 4-6 cycles there seems to be enough room to do calculations as well. Is there has already been a library written to do what I want, or do you know of a good reason not to? A single MOSFET, resistor, and speaker controlling an analog audio signal from a single digital pin sounds like my kind of fun.

Resources:
http://www.georgegardner.info/electronics/class-d-avr.html
Wi-Fi Walkie Talkies, genius!

I usually research things to death before I ask, but my general keywords aren’t turning up anything.

When this is running in main, I can get 8-bit audio to 25kHz, but I’d need 10x that speed to start. Thanks.

while (playPosition < RECORDING_MAX) {
    playPosition += 1;
    ampBitMask = 0x80;

    for(uint8_t playTiming = 0xFF; playTiming > 0; playTiming -= 1) {   // 8-bit loop
        
        if (audio[playPosition] & ampBitMask) {  
            pinSetFast(SPEAKER_PIN);
        } else {
            pinResetFast(SPEAKER_PIN);
        }
        
        if (playTiming == ampBitMask) {           // shifts towards LSB after it is done masking every value of that bit
            ampBitMask >>= 1;
        }
    }
}

Is this for() loop meant to produce BCM (Binary Coded Modulation) signal?
If so, then you might want to not do anything in that loop than cycle - the pin setting and bit shifting should be done only once outside of that loop.

But even better would be a non-blocking, possibly timer interrupt driven, approach, similar to the way it’s done in the BCM controlled RGBmatrixPanel library that uses SparkIntervalTimer lib to do the timing.

1 Like

Exactly! Didn’t know the name sorry. I’ll research BMC and see how much it can improve.

Just tried SparkIntervalTimer again and it doesn’t call anything less than 10us, so it can be blocking for now until speed isn’t an issue.

You could try this

for(playPosition = 0; playPosition < RECORDING_MAX; playPosition++) {
  for(ampBitMask = 0x80; ampBitMask; ampBitMask >>= 1) {
    digitalWriteFast(SPEAKER_PIN, audio[playPosition] & ampBitMask);
    for(uint8_t playTiming = ampBitMask; playTiming; playTiming--); // BCM timing loop
 }
}
2 Likes

Very elegant ScruffR, thanks.
I’ll test it more tomorrow but I already see it executes really fast, more than 10x the speed. To test I probably have to add delays temporarily given the speed of my scope :pensive: I had a mask directly in digitalWriteFast as well but an if and set/reset was faster. I’ll test both again, the old way the pin toggling overhead was very evident.

I knew there was going to be a faster way, the bit in 2^7 position is set or not, so the timing loop just keeps it the same for the next 128 cycles, instead of setting it every time.

2 Likes