How to implement interrupt timers in arduino loop

Hi! I want to figure out how to have an ATTiny85 that is coded with Arduino, to play a song when push button is pressed. How it works is that it should have an LED that's on to signal that the push button is ready to be pressed, and once it's pushed, the LED will turn off and the song will begin playing. Then, the LED will turn on again after the song is complete. The problem is that the program uses interrupt timers (ISR), and I'm not sure how I can implement it into the loop function to work the same way.

/* Digital Music Box v2

   David Johnson-Davies - www.technoblogy.com - 16th March 2016
   ATtiny85 @ 16 MHz (internal PLL; 4.3 V BOD)
   
   CC BY 4.0
   Licensed under a Creative Commons Attribution 4.0 International license: 
   http://creativecommons.org/licenses/by/4.0/
*/

int Scale[] = { 
  680,  721,  764,  809,  857,  908,  962, 1020, 1080, 1144, 1212, 1284, 
 1361, 1442, 1528, 1618, 1715, 1817, 1925, 2039, 2160, 2289, 2425, 2569,
 2722, 2884, 3055, 3237, 3429, 3633, 3849, 4078 };
 
const int Channels = 4;
const int Tempo = 4;      // 4 = 4 beats per second
const int Decay = 9;      // Length of note decay; max 10
const int led = 7;  // Set for pin 7 of ATTiny85

volatile unsigned int Acc[Channels];
volatile unsigned int Freq[Channels];
volatile unsigned int Amp[Channels];

// Play Happy Birthday

const uint32_t Tune[] PROGMEM = {
//_*_*__*_*_*__*_*__*_*_*__*_*__*_
//C D EF G A BC D EF G A BC D EF G
0b00000000000000000001000000000000,
0b00000000000000000001000000000000,

0b10000000000000000000010000000000,
0b00000000000000000000000000000000,
0b00001001000000000001000000000000,
0b00000000000000000000000000000000,
0b00000000000000000000000010000000,
0b00000000000000000000000000000000,

0b00100000000000000000000100000000,
0b00000000000000000000000000000000,
0b00000101000000000000000000000000,
0b00000000000000000000000000000000,
0b00000000000000000001000000000000,
0b00000000000000000001000000000000,

0b10000000000000000000010000000000,
0b00000000000000000000000000000000,
0b00000101000000000001000000000000,
0b00000000000000000000000000000000,
0b00000000000000000000000000100000,
0b00000000000000000000000000000000,

0b10000000000000000000000010000000,
0b00000000000000000000000000000000,
0b00001001000000000000000000000000,
0b00000000000000000000000000000000,
0b00000000000000000001000000000000,
0b00000000000000000001000000000000,

0b10000000000000000000000000000001,
0b00000000000000000000000000000000,
0b00100000000000000000000000001000,
0b00000000000000000000000000000000,
0b00001000000000000000000010000000,
0b00000000000000000000000000000000,

0b00000100000000000000000100000000,
0b00000000000000000000000000000000,
0b00000000000000000000010000000000,
0b00000000000000000000000000000000,
0b00000000000000000000000000000100,
0b00000000000000000000000000000100,

0b00000001000000000000000000001000,
0b00000000000000000000000000000000,
0b00000000000000000000000010000000,
0b00000000000000000000000000000000,
0b00000101000000000000000000100000,
0b00000000000000000000000000000000,

0b10001001000000000000000010000000,
0xFF}; // End of tune

//Globals persist throughout tune
int TunePtr = 0, Chan = 0;

// Watchdog interrupt plays notes
ISR(WDT_vect) {
  sei();   // Allow interrupts
  WDTCR |= 1<<WDIE;
  unsigned long Chord = pgm_read_dword(&Tune[TunePtr]);
  if (Chord == 0xFF) return;
  TunePtr++;
  // Read the bits in Chord
  for (int Note = 0; Note < 32; Note++) {
    if ((Chord & 0x80000000) != 0) {
      Freq[Chan] = Scale[Note];
      Amp[Chan] = 1<<(Decay+5);
      Chan = (Chan + 1) % Channels;
    }
    Chord = Chord<<1;
  }
}

// Generate square waves on 4 channels
ISR(TIMER0_COMPA_vect) {
  signed char Temp, Mask, Env, Note, Sum=0;
  for (int c = 0; c < Channels; c++) {
    Acc[c] = Acc[c] + Freq[c];  
    Amp[c] = Amp[c] - (Amp[c] != 0);
    Temp = Acc[c] >> 8;
    Mask = Temp >> 7;
    Env = Amp[c] >> Decay;
    Note = (Env ^ Mask) + (Mask & 1);
    Sum = Sum + Note;
  }
  OCR1B = Sum + 128;
}

void setup() {
  pinMode(2, INPUT_PULLUP); // Set pushbutton to pin 2 as a pullup resistor
  pinMode(led, OUTPUT);
  // Enable 64 MHz PLL and use as source for Timer1
  PLLCSR = 1<<PCKE | 1<<PLLE;     

  // Set up Timer/Counter1 for PWM output
  TIMSK = 0;                     // Timer interrupts OFF
  TCCR1 = 1<<CS10;               // 1:1 prescale
  GTCCR = 1<<PWM1B | 2<<COM1B0;  // PWM B, clear on match

  OCR1B = 128;
  DDRB = 1<<DDB4;                // Enable PWM output on pin 4

  // Set up Timer/Counter0 for 20kHz interrupt to output samples.
  TCCR0A = 3<<WGM00;             // Fast PWM
  TCCR0B = 1<<WGM02 | 2<<CS00;   // 1/8 prescale
  OCR0A = 99;                    // Divide by 100
  TIMSK = 1<<OCIE0A;             // Enable compare match, disable overflow
 
  // Set up Watchdog timer for 4 Hz interrupt for note output.
  WDTCR = 1<<WDIE | Tempo<<WDP0; // 4 Hz interrupt
}

void loop() {
  int buttonState = digitalRead(2);
  if (buttonState == HIGH){
     digitalWrite(led, HIGH);
  } else { 
     digitalWrite(led, LOW);
     // Code for song here
}

Why do you think that you need to convert ISR to anything else?

You need to add some code here to play a specific song. What do the instructions say?

I tried implementing the ISR into the loop function before, but it only comes off as an error. I want the song to play only when the push button is pressed, which means having to put it into the void loop function at the "// Code for song here" area. The error is "expected unqualified-id before string constant" at the ISR(IMER0_COMPA_vect) method if I try and move those 2 ISR methods into the comment area.

The ISR methods should play the song, I just want to add them to the void loop function at the "Code for song here", but whenever I do it just brings up an error. I was wondering how I could implement those 2 methods into that loop, so this way it'll play whenever the push button is pressed, not just run once. The error is "expected unqualified-id before string constant" at the ISE(TIMER0_COMPA_vect) method if I try and move those 2 ISR methods into the comment area.

That makes no sense. You should not be making any changes to the ISR. It will do what it is intended to do, as it is currently placed in the code.

Where did you find this code, and how is it supposed to be used? Look for a working example and follow that.

"//code for song here" means you have to put in some song data and code.

There is no way to implement one function inside the other, C language forbids it.
And you don't need that. This is ready-to-go code for playing songs in the loop.

Why did you decided that the song will play once? Are you understand why the main arduino function called "loop" ? It loops again and again infinitely

The (partially quoted) data below apparently code for the tune "Happy Birthday", but you will need some instructions to figure out how to enter other tunes.

Unless you can find those instructions, it will be pretty difficult to figure out how to use this code. There must be many other, better places to start!

const uint32_t Tune[] PROGMEM = {
//_*_*__*_*_*__*_*__*_*_*__*_*__*_
//C D EF G A BC D EF G A BC D EF G
0b00000000000000000001000000000000,
0b00000000000000000001000000000000,

0b10000000000000000000010000000000,
0b00000000000000000000000000000000,

It looks like a source of project
http://www.technoblogy.com/show?11TQ

No no, perhaps I explained it wrong. the "// code for song here" is a comment I made myself. Because of how the current code works (yes, this one: Technoblogy - Digital Music Box [Updated]) it only plays once. I want to make it play every time the push button is pressed (modifications I've added myself). But trying to move those functions into the loop doesn't work.

Of course, I understand that the loop function means that it does it infinitely. I've tested the code before, and it only plays the song once. I want to make it so that it plays at the press of a push button (such as the modifications I've added to the code compared to the original one found at: Technoblogy - Digital Music Box [Updated]).

This is ready-to-go code for playing songs in the loop.

How would I put the song playing into the loop? Or any other code necessary

To start the tune over, I think it will be enough to set these values back to zero in the loop function.

//Globals persist throughout tune
int TunePtr = 0, Chan = 0;

e.g. in loop(): add two lines

  if (buttonState == HIGH){
     digitalWrite(led, HIGH);
     TunePtr = 0;  //reset pointer to start of tune data
     Chan = 0;

Edit: Come to think of it, it is more complicated than that. You need to wait until the tune is finished, and hang somewhere, before restarting.

1 Like

Thank you! I'll be sure to try this, I'll let you know if everything goes well tomorrow.

Try this instead:

void loop() {
  int buttonState = digitalRead(2);
  if (buttonState == HIGH){
     digitalWrite(led, HIGH);
  } else { 
     digitalWrite(led, LOW);
     TunePtr = 0;  //reset pointer to start of tune data
     Chan = 0;
     delay(10000);  //replace with value long enough for tune to finish
     }
}

Thank you for the help, your solution works (except for the delay, which I had to remove because it was causing the interrupts to break, making the loop stop working). I instead used the TunePtr as another condition to act as a "cooldown".

Glad it works! The delay() function does not affect interrupts, but loop() will hang for 10 seconds during the delay, which was intended.

It would be helpful to others if you post the final working code.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.