Wednesday, 1 September 2010

Arduino Interruptus

Here is my first shot at a basic two-channel interrupt-driven routine for an Arduino Mega. The example toggles LEDs on and off, but it can be used for stepper motors on a telescope etc. It's just a start. No acceleration yet. More soon...

Arduino - twice interrupted.


// Basic Arduino Two Channel Interrupt Routine
//
// Copyright (C) 2010 www.cncorbust.com
// This program is free software: you can redistribute it
// and/or modify it under the terms of the GNU General
// Public License as published by the Free Software
// Foundation, either version 3 of the License, or (at
// your option) any later version.
// This program is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the 
// implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE.  See the GNU General Public
// License for more details.
// You should have received a copy of the GNU General Public
// License along with this program.  If not, see www.gnu.org.
//
//
// To make this work: Use an Arduino Mega
// connect pin 11  - Resistor (eg 1kOhm) - LED - GND (shows signal for timer 1)
// connect pin 5 - Resistor (eg 1kOhm) - LED - GND (shows signal for timer 3)
//

void setup()
{
  // We do this the long way to be very clear about what happens...
  
  // Define how many clock cycles you want to wait for a full on-off cycle
  // There are limits to these numbers: ticks/1024/4 must be smaller 65356 and greater 1
  
  unsigned long ticksForTimer1 = 16000000L * 1; // 1 second for one off-on-off cycle
  unsigned long ticksForTimer3 = 16000000L * 2.333;
  
  // we use the timer's 1/1024 pre-scaler for this example,
  // so adjust ticks by dividing by 1024
  ticksForTimer1 /= 1024;
  ticksForTimer3 /= 1024;
  
  // we set the timers to work "phase and frequency correct"
  // this means the timer counts up, then down, ie twice the distance
  // also, at TOP, the counter toggles the output.
  // This means it counts 4 times the range for a full off-on cycle.
  // Adjust ticks to take account for this...
  
  ticksForTimer1 /= 4;
  ticksForTimer3 /= 4;
  
  // Now, set up timer 1
  //
  // We basically set three things: Timer mode (wave form), pin output mode, and pre-scaler
  // For timer mode we set "9", which means:
  //       Timer counts up to TOP, then down to zero
  //       Timer takes its TOP value from OCR1A
  //       Setting OCR1A is double-buffered
  // For pin output mode we choose "01", which means the output pin toggles on a compare match.
  // For pre-scaler we choose to divide by 1024 to make things visible
  //  
  
  // TCCR1A is a timer control register with these bits:
  //   COM1A1   - 0  output mode
  //   COM1A0   - 1  output mode
  //   COM1B1   - 0  ignore
  //   COM1B0   - 0  ignore
  //   COM1C1   - 0  ignore
  //   COM1C0   - 0  ignore
  //   WGM11    - 0  timer mode
  //   WGM10    - 1  timer mode
  //
  // TCCR1B is a timer control register with these bits:
  //   ICNC1    - 0  ignore
  //   ICES1    - 0  ignore
  //   reserved - 0  ignore
  //   WGM13    - 1  timer mode
  //   WGM12    - 0  timer mode
  //   CS12     - 1  clock select (pre-scaler division)
  //   CS11     - 0  clock select (pre-scaler division)
  //   CS10     - 1  clock select (pre-scaler division)
  //   
  // TIMSK1 contains the interrupt enable register
  //
  
  cli(); // disable interrupts
  TCCR1A = B01000001;
  TCCR1B = B00010101;
  OCR1AH = ticksForTimer1 >> 8;       // set delay, high byte first
  OCR1AL = ticksForTimer1;
  TCNT1H = 0;  // reset timer to trigger update of TOP from OCR1A
  TCNT1L = 0;
  // Timer 1 writes to pin 11
  pinMode(11,OUTPUT);
  sei(); // enable interrupts

  // Needless to explain, the same goes for timer 3...

  cli(); // disable interrupts
  TCCR3A = B01000001;
  TCCR3B = B00010101;
  TIMSK3 = B00000010;
  OCR3AH = ticksForTimer3 >> 8;   // set delay, high byte first
  OCR3AL = ticksForTimer3;
  TCNT3H = 0;  // reset timer to trigger update of TOP from OCR3A
  TCNT3L = 0;
  // Timer 3 writes to pin 5  
  pinMode(5,OUTPUT);
  sei(); // enable interrupts
  
  // Unrelated to interrupts. Just for the demo...
  Serial.begin(9600);
}

// A counter to keep track of position 3
volatile long pos3;

// Use this to read pos3 so the interrupt handler does not give you garbage
long getPos3()
{
  cli(); // no interrupts
  long r = pos3;
  sei(); // allow interrupts
  return r;
}

ISR(TIMER3_COMPA_vect)
{
  pos3++; // update position (timer3 only)
}

// In the main loop, simply send changes in position to the serial port
long lastP = -1;
void loop()
{
  // indicate that we are active...
   digitalWrite(13,HIGH);
   long p = getPos3();
   if(p!=lastP)
   {
     Serial.println(p);
     lastP = p;
   }
}

No comments: