Generate Audio Tones


I’d like to use the Spark to generate some audio frequency signals. PWM would be fine but I need to go well above the 500Hz of the AnalogWrite() function.
The microprocessor has the speed and timing functions to modulate at say 20 or 40kHz but I guess this would be too fast to sit inside the loop() function. Would the Spark core software object if I ran an interrupt at 40kHz? The interrupt routine would just be counting and setting a digital output high or low depending on the count value.
If someone has done something similar then great!


Have you seen this thread were @BDub makes some music?

I believe that the tone function has been in for some time now.


tone() and noTone() are currently in the Spark Firmware… undocumented though:


Thank you @Bdub @BKO and @satishgn. That is a good starting point. I’ll take a look at the tone() function in the core software and see whether I can build on that. It has also led me to:
creating-timer-based-interrupts and IntervalTimerLibrary
What I want to do is generate a changing waveform on the fly and without a glitch each time round the loop(). I think there are two parts to this, 1) Using PWM on a digital output as an audio frequency DAC. (And I hear myself saying “why not buy a DAC?”) and 2) Keeping the DAC or SparkPWM DAC function fed with live data.

  1. calls for PWM of a digital pin at up to 40kHz to modulate the output. Ref 1 from @satishgn seems to cover this - certainly up to 10kHz but hopefully strechable to 20-40kHz.
  2. will supply a stream of desired analog values at say 8kHz which would adjust the duty cycle of the PWM. The Interval Timer Library from @BDub seems to cover this - and possibly both 1 and 2?
    Does @Bdub’s library supersede @satishgn’s advice on timer based interrupts?

It may help if I explain what I’m planning to do. I aim to build an active vibration control/anti noise system - initially based on the ancient Bernard Widrow Stanford LMS filter and gradually bringing this up to date. (The adjective refers to the filter technology not its co-inventor).


Hi @phec

Sounds like a great project! I think everything you want to do is very doable with the cloud off. With the cloud on, the variable latency of the cloud connection may force you to lower your overall sample rate.

So in my day job, I work in the signal processing/comms/video department of a major software company and I am very familiar with active noise cancellation using LMS. We had Bernie Widrow out to our shop to give a talk a few years ago since he was the thesis advisor to one of our guys. He gave a great talk, mostly about quantization effects–he was really nice and took questions from the audience over a wide range of subjects at the end.


Thanks @bko. Given that advice I’ll get started. Thanks for the comment about the Cloud. Yes Bernard Widrow is a really nice guy. I attended a course he gave in England many years ago. There was a whole generation of nice guys who did really ground breaking stuff. I’ve attended workshops with Lotfi Zadeh, Igor Aleksander and Vladimir Vapnic. and MIT had a European Lab in Dublin in a disused Guinness maltings. Happy days.


@peekay123 and @satishgn your two pieces of code virtually do the whole job for me. Thank you very much indeed.
Thanks too to @bko and @bdub for pointing me in the right direction.
If the code is doing what I think it is, I’m sampling the ADC at 8kHz and using the measured ADC value to PWM an LED at 40kHz in 100 steps. Now I just need to put in the digital delay line and the LMS algorithm. :sunny: :smile:

//0.001 - read ADC write to LED ....   OK
//0.002 - change frequency of PWM from 500Hz to 40kHz.... OK
//0.003 - blink LED on an interrupt ...OK
//0.004 - getADCvalue on interrupt ... OK
//0.005 - speed up interrupt to 8kHz...      OK

#include "application.h"
#include "SparkIntervalTimer.h"
int LED = D0;
int ADC = A0;
volatile int value;
IntervalTimer myTimer;

// Pre-define ISR callback function
void getADCvalue(void);

void setup(){
    // set up the timer that will obtain ADC values every 125 uSec (thanks peekay123)
    myTimer.begin(getADCvalue, 125, uSec, TIMER3); //v003 v004
    //set up the Spark PWM on pin D0 to operate at 40kHz (thanks satishgn)
    pinMode(LED, OUTPUT);
    //Set the Prescaler value
    TIM4->PSC = 18; // 72M / 18 = 4M counts per sec, 4M / 100 = 40k values per sec
    //Load prescaler immediately
    TIM4->EGR = 1;
    //Set the Autoreload Register value
    TIM4->ARR = 100;
    //Set the Capture Compare2 Register value
    TIM4->CCR2 = 50;//CCR2 is for Channel 2 = D0

// Callback for Timer
void getADCvalue(void){
    value = analogRead(ADC);
    //TODO - put LMS algorithm here!!
    TIM4->CCR2 = value/41;//CCR2 is for Channel 2
void loop(){
    //value = analogRead(ADC);
    //analogWrite(LED,value/16); //v001
    //TIM4->CCR2 = value/41;//CCR2 is for Channel 2 //(4096 / 100 = 41 ish) v002


In order to debug the LMS code I’ve been putting a ‘tone’ through an LED, picking the signal up with a photoresistor (this is the reference signal) and then using the LMS algorithm to modulate a second LED with the target of producing constant illumination of a second photoresistor that can ‘see’ both LEDs:

I have it working at low frequencies where I can see what is going on - there’s still some work to do to get the speed up.

One thing I have noticed is that I get aliasing effects from sampling the output of the PWM LEDs. I made the mistake of using the default 500Hz PWM for the reference signal and 40kHz for the control, both sampled at 8kHz. The LEDs flicker at the PWM frequency - which shouldn’t be a surprise but I hadn’t considered it. When I do it with an audio signal I’ll have to be careful to smooth the PWM output before the audio amplifier - or I suppose I could just switch a power transistor at 40kHz, do away with the audio amplifier and use the speech coil and a small capacitor to filter the output.

One beneficial side effect of PWM control is that the LED brightness varies smoothly across the full 0 - 3.3v range whereas with a smoothing capacitor or other steady DC source the LED is unlit until above its 1.3v threshold.