Using Mega Timer 5 to generate a timed output

Hi,

First post so please be gentle!

I have written a routine that - in theory - uses Timer 5 of an Arduino Mega 2560 to turn on a buzzer for a defined time.

The intended program flow is:

In the main loop

Call myBeep(n). This function does the following:
Checks beepOn, a boolean flag, to see if a beep is in progress and does nothing if it is
Global disable interrupts
Sets up Timer 5 in CTC mode
Sets the prescaler to 1024
Sets the counter value to n, the parameter passed by the function
Multiplies the value in the counter by 16 by left shifting 4 times
Sets beepOn to true to disable further set ups of Timer 5 until the beep has finished
Sets the buzzer output pin high to turn on the buzzer
Enables the Timer 5 compare interrupt
Global enable interrupts

In the Timer 5 ISR generated from the above, the following is supposed to happen
Turns off the buzzer output pin
Sets beepOn false to allow another setup of Timer 5
Turns off Timer 5 by disabling the Timer Compare interrupt

So far, so good. The only snag is that it doesn’t work! I have done lots of checking and each bit I have checked does work, the buzzer goes off when the pin goes high, we do generate an interrupt and get into the ISR, etc etc. So, please, what am I doing wrong? The code is below.

Thanks so much for your help!

Hugh

#include <streaming.h>

#include "Streaming.h"
#include "Switch.h"


/*Referencing the Arduino Mega Pins
  All pins are named using the format pnTypeDescriptiveName.
  Types are Led for Manual Control Panel action indicators,
  Sw for Rotary Switches, Pb for push button switches and 
  Saf for Safety Monitor indicators.
  Names of all the pins used are given in this section.
*/
//**********************Manual Control Panel Indicator LEDs
//All pins are set as OUTPUT
const byte pnLedDomeMoving = 22;
const byte pnLedAtHome = 23;
const byte pnLedShutterOpen = 24;
const byte pnLedMotorTimeOut = 25;
const byte pnLedShutterClosed = 26;
const byte pnLedButtonBuzzer = 27;
const byte pnLedSpare = 28;
const byte pnLedShutterMoving = 29;

/********************Manual Control Panel Switch Inputs
All pins are set as INPUT. All switches are ACTIVE LOW.
All switch pins have internal pull-ups enabled via Switch.h
Switch.h is used in default mode: INPUT_PULLUP, LOW, 50mSec debounce
*/
const byte pnSwSpare = 31;
const byte pnSwModeManual = 30; //This is correct
const byte pnSwModeOff = 37;  //This is correct
const byte pnSwDirCW = 32;  //This is correct
const byte pnSwDirStop = 35;  //This is correct
const byte pnPbHomeAll = 34; //This is correct
const byte pnPbShutterClose = 33; //This is correct
const byte pnPbShutterOpen = 36; //This is correct

//Safety Monitor Indicators
//All pins are set as OUTPUT
const byte pnSafShutterTimeOut = 38;
const byte pnSafShutterCommsFail = 39;
const byte pnSafDomeJammed = 40;
const byte pnSafIsCloudy = 41;
const byte pnSafShutterLatched = 42;
const byte pnSafIsRaining = 43;
const byte pnSafDomeLatched = 44;
const byte pnSafPowerFailure = 45;  

//**********************GLOBAL VARIABLES**********************************

//DEBUG variables here
//Variable for testing Switch.h
int myi;
//End of DEBUG variables

//PROPER variables here
//Volatile Variables used in Timer Interrupt Loops
volatile boolean beepOn = false;
volatile byte mcpFlag;
volatile byte safFlag;

//End of PROPER variables



//Creating Switch instances

Switch pbHomeAll = Switch(pnPbHomeAll,INPUT_PULLUP,LOW); 
Switch pbShutterClose = Switch(pnPbShutterClose,INPUT_PULLUP,LOW);
Switch pbShutterOpen = Switch(pnPbShutterOpen, INPUT_PULLUP, LOW);
Switch swModeManual = Switch(pnSwModeManual, INPUT_PULLUP, LOW);
Switch swModeOff = Switch(pnSwModeOff, INPUT_PULLUP, LOW);
Switch swDirCW = Switch(pnSwDirCW, INPUT_PULLUP, LOW);
Switch swDirStop = Switch(pnSwDirStop, INPUT_PULLUP, LOW);

void setup()
{
  
//Set up Arduino Mega pins as OUTPUTS
//MCP LED indicators
  for (byte i=pnLedDomeMoving; i < (pnLedShutterMoving+1); i++)
    {
    pinMode(i, OUTPUT);      // sets the digital pin as output
    }

//Safety Status LED indicators
  for (byte i=pnSafShutterTimeOut; i < (pnSafPowerFailure+1); i++)
    {
    pinMode(i, OUTPUT);      // sets the digital pin as output
    }

//DEBUG Code from here **********************************************************************
//Used for testing myBeep
Serial.begin(9600);  
//End of DEBUG code **************************************************************************
  
 }

void loop()
{
//DEBUG Code from here**************************************************************************
//for (byte i=minPin;i < (maxPin+1); i++)
//  {
//End of DEBUG code **************************************************************************

//  byte i = 45;
//  digitalWrite(i, HIGH);   // sets the LED on
//  delay(500);                  // waits for a second
//  digitalWrite(i, LOW);    // sets the LED off
//  delay(500);  
//  }
//End of DEBUG code ************************************************************************** 


//Loop for testing Switch.h
//Poll switches
  pbHomeAll.poll();
  pbShutterClose.poll();
  pbShutterOpen.poll();
  swModeManual.poll();
  swModeOff.poll(); 
  swDirCW.poll();
  swDirStop.poll();

  //Switch testing
  if(pbHomeAll.pushed()) 
  {
  Serial << "Home All pushed" << ++myi << "\n"; 
  myBeep(400);
  }  
  
  
  if(pbShutterClose.pushed())
  {
  Serial << "Close Shutter pushed" << ++myi << "\n"; 
  myBeep(700);
  }
    
  if(pbShutterOpen.pushed())
  {
  Serial << "Open Shutter pushed" << ++myi << "\n";
  myBeep(1000);
  }   
  
  /* Ignore the Rotary Switches for now
  if(swModeManual.on()) Serial << "Manual Mode On" << endl; 
  if (swModeOff.on()) Serial << "Mode Selection Off" << endl;
  if (!swModeManual.on() && !swModeOff.on()) Serial << "Auto Mode On" << endl; 
  
  if(swDirCW.on()) Serial << "Direction CW" << endl; 
  if(swDirStop.on()) Serial << "Stop Rotation" << endl;
  if (!swDirCW.on() && !swDirStop.on()) Serial << "Direction CCW" << endl;  
  */
}

void myBeep(int t)
{
  
//DON'T initialise the timer if we are in the middle of a beep
if (!beepOn)
{
// initialize Timer5
cli();          // disable global interrupts
TCCR5A = 0;     // set entire TCCR1A register to 0
TCCR5B = 0;     // same for TCCR1B

// set compare match register to desired timer count:
OCR5A = t;

//DEBUG Code from here **********************************************************************
//Serial.println(OCR5A, BIN);
//End of DEBUG code **************************************************************************

//Multiply compare match register by 16 by left shifting 4 times
//Syntax is a <<= b
OCR5A <<= 4;

//DEBUG Code from here **********************************************************************
//Serial.println(OCR5A, BIN);
//End of DEBUG code **************************************************************************

// turn on CTC mode:
TCCR5B |= (1 << WGM52);

// Set CS10 and CS12 bits for 1024 prescaler:
TCCR5B |= (1 << CS50);
TCCR5B |= (1 << CS52);

//Start buzzer going
digitalWrite(pnLedButtonBuzzer, HIGH);

//Set beepOn to prevent resetting Timer 5 until the beep event is ended
//The beepOn flag is a volatile variable and is reset in the ISR

beepOn = true;

// enable timer compare interrupt:
TIMSK5 |= (1 << OCIE5A);

// enable global interrupts:
sei();

//DEBUG code from here ****************************************************************************
//Serial << "We are in myBeep" << "\n";
//End of DEBUG code here ***************************************************************************
}
}


ISR(TIMER5_COMPA_vect)
{
//Stop the buzzer
digitalWrite(pnLedButtonBuzzer, LOW);

//Reset beepOn (a volatile variable) to allow the next beep to start
beepOn = false;

//Stop Timer 5
/*To clear a bit in C, NOT the bit value mask so that the bit of interest is the only bit cleared, and then AND that with the value.
uint8_t a = 0x0F; // 00001111 
                  // clear bit 2 
a &= ~(1<<2);     // 00001011

Use multiple OR operators to clear multiple bits.
uint8_t a = 0x0F;      // 00001111
                       // clear bit 1 and 2
a &= ~((1<<2)|(1<<1)); // 00001001
*/

TIMSK5 &= ~(1 << OCIE5A);
Serial << "We are in Timer 5 ISR" << "\n";
}
Serial << "We are in Timer 5 ISR" << "\n";

In an ISR? NO!

I know - it was just an attempt at debugging. My program didn't work before adding the serial call. So, please, can anyone see what is wrong with my code?

Regards, Hugh

What happens if you set TCNT5 to 0 when you initialize Timer 5?

// initialize Timer5
cli();          // disable global interrupts
TCCR5A = 0;     // set entire TCCR5A register to 0
TCCR5B = 0;     // same for TCCR5B
TCNT5 = 0;

Hi Cattledog,

Good thought but unfortunately it didn't make any difference. I'm still not getting a beep.

I know that my routine is generating an interrupt but for some reason I can't get it to work.

Any more ideas please ?

Regards, Hugh

OK, I think I have semi-nailed it. I was using the wrong method to deactivate the timer/counter. In my ISR, I changed from

TIMSK5 &= ~(1 << OCIE5A);

to

TCCR5A = 0; // set entire TCCR1A register to 0
TCCR5B = 0; // same for TCCR1B

and now it works - sort of! It DOESN’T work for the first button push but after that it works as intended.
Just need to find out whythe first push doesn’t work…

Regards, Hugh