Wednesday, April 8, 2015

20141114 Frank's Challenge - Arduino using Interupt, Blink LED, and play PWM sound

At last Thursdays meeting Frank put out a challenge to have a LED blink at about once per second, have the arduino play a continuous 1Khz sound, and have a button that will turn on another LED.

The random factor would be when you hit the Button, and it should be an instant on.

I took the challenge up, and made a program that works almost like Frank wanted - it’s not quite what he asked for, but it is close and with a little more tweeking and playing around with can get very close to what he asked.


 

So, it blinks the LED at about 2 seconds per, plays “twinkle twinkle little star” pretty close to being right, and will almost instantly turn on the LED if the button is on - It takes it a second or so to realize the button has been released.  And one of the strange things that I wasn’t aware of was that while the interrupt is triggered it does stop counting millis value.  This will end up causing some issues the longer the sketch runs.

The sketch can be found here:

The sketch works more or less how we talked about at the meeting.

int speakerPin = 11;
int ledPin = 8;
int length = 15; // the number of notes
char notes[] = "ccggaagffeeddc "; // a space represents a rest
int beats[] = { 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 4 };
int tempo = 300;
int ledState = LOW;
long previousMillis = 0;
long interval = 900;
int intruptpin = 9;
volatile int state = LOW;

The above of course sets up variables, the long interval variable is the one set for timing of the blinking led.

Below sets up plays the tune, it’s just a PWM and a slight delay between notes, the tune is played from two different routines: “playTone” and “playNote”

void playTone(int tone, int duration) {
 for (long i = 0; i < duration * 1000L; i += tone * 2) {
    digitalWrite(speakerPin, HIGH);
    delayMicroseconds(tone);
    digitalWrite(speakerPin, LOW);
    delayMicroseconds(tone);
 }
}

void playNote(char note, int duration) {
 char names[] = { 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C' };
 int tones[] = { 1915, 1700, 1519, 1432, 1275, 1136, 1014, 956 };

 // play the tone corresponding to the note name
 for (int i = 0; i < 8; i++) {
    if (names[i] == note) {
     playTone(tones[i], duration);
    }
 }
}

This is to blink the LED and is called when the interrupt is triggered. The interrupt jumps from the programs current location anytime the tigger happends.  The best method is to keep this subroutine very small - it can be larger then what I have, but it needs to be able to execute quickly.

void blink() {
   state = !state;
   digitalWrite(intruptpin, state);
   

}

More setup stuff - what is important here thou is setting up the Interrupt - the UNO has 2 that can be used, 0 is on Pin 2 and 1 is on Pin 3 -

void setup() {
 pinMode(speakerPin, OUTPUT);
pinMode (ledPin, OUTPUT);
pinMode(intruptpin, OUTPUT);
attachInterrupt(0, blink, HIGH);
}

This is where the magic happens.
First we set a variable called currentMillis to equal millis();
then a for loop will start to play the notes, also inside the for loop, we check if currentMillis is greater the interval, and if so we setup previousMillis to equal currentMillis, check the led state, and  change the LED
void loop() {
   unsigned long currentMillis = millis();
 for (int i = 0; i < length; i++) {
    if (notes[i] == ' ') {
     delay(beats[i] * tempo); // rest
    } else {
     playNote(notes[i], beats[i] * tempo);
    }

    // pause between notes
    if (currentMillis - previousMillis > interval) { previousMillis = currentMillis;
      if (ledState == LOW) {ledState = HIGH;} else {ledState = LOW;}
    digitalWrite (ledPin, ledState);
   
    }
 }
}

So, the sketch is small, it run quickly, and it works pretty well.

No comments:

Post a Comment