Go Down

Topic: Timer1 Single Execution (Read 1 time) previous topic - next topic

Nick Gammon

#15
Feb 21, 2012, 09:16 pm Last Edit: Feb 22, 2012, 05:23 am by Nick Gammon Reason: 1
I've improved the code a bit to make it a little less specific to Timer 1. By putting things into a namespace they are a bit more organized:

Code: [Select]
/* ---------------------------------------------------------------
   Timer 1 setup
   --------------------------------------------------------------- */
 
namespace Timer1
  {
  // TCCR1A, TCCR1B
  const byte Modes [16] [2] =
    {
   
    { 0,                         0 },            // 0: Normal, top = 0xFFFF
    { _BV (WGM10),               0 },            // 1: PWM, Phase-correct, 8 bit, top = 0xFF
    {               _BV (WGM11), 0 },            // 2: PWM, Phase-correct, 9 bit, top = 0x1FF
    { _BV (WGM10) | _BV (WGM11), 0 },            // 3: PWM, Phase-correct, 10 bit, top = 0x3FF
    { 0,                         _BV (WGM12) },  // 4: CTC, top = OCR1A
    { _BV (WGM10),               _BV (WGM12) },  // 5: Fast PWM, 8 bit, top = 0xFF
    {               _BV (WGM11), _BV (WGM12) },  // 6: Fast PWM, 9 bit, top = 0x1FF
    { _BV (WGM10) | _BV (WGM11), _BV (WGM12) },  // 7: Fast PWM, 10 bit, top = 0x3FF
    { 0,                                       _BV (WGM13) },  // 8: PWM, phase and frequency correct, top = ICR1   
    { _BV (WGM10),                             _BV (WGM13) },  // 9: PWM, phase and frequency correct, top = OCR1A   
    {               _BV (WGM11),               _BV (WGM13) },  // 10: PWM, phase correct, top = ICR1A   
    { _BV (WGM10) | _BV (WGM11),               _BV (WGM13) },  // 11: PWM, phase correct, top = OCR1A
    { 0,                         _BV (WGM12) | _BV (WGM13) },  // 12: CTC, top = ICR1   
    { _BV (WGM10),               _BV (WGM12) | _BV (WGM13) },  // 13: reserved
    {               _BV (WGM11), _BV (WGM12) | _BV (WGM13) },  // 14: Fast PWM, TOP = ICR1
    { _BV (WGM10) | _BV (WGM11), _BV (WGM12) | _BV (WGM13) },  // 15: Fast PWM, TOP = OCR1A
   
    };  // end of Timer1::Modes
   
  // Activation
  // Note: T1 is pin 11, Arduino port: D5
  enum { NO_CLOCK, PRESCALE_1, PRESCALE_8, PRESCALE_64, PRESCALE_256, PRESCALE_1024, T1_FALLING, T1_RISING };
 
  // what ports to toggle on timer fire
  enum { NO_PORT = 0,
 
         // pin 15, Arduino port: D9
         TOGGLE_A_ON_COMPARE  = _BV (COM1A0),
         CLEAR_A_ON_COMPARE   = _BV (COM1A1),
         SET_A_ON_COMPARE     = _BV (COM1A0) | _BV (COM1A1),
         
         // pin 16, Arduino port: D10
         TOGGLE_B_ON_COMPARE  = _BV (COM1B0),
         CLEAR_B_ON_COMPARE   = _BV (COM1B1),
         SET_B_ON_COMPARE     = _BV (COM1B0) | _BV (COM1B1),
     };
 
  // choose a timer mode, set which clock speed, and which port to toggle
  void Timer1::setMode (const byte mode, const byte clock, const byte port)
    {
    if (mode < 0 || mode > 15)  // sanity check
      return;
     
    TCCR1A |= (Modes [mode] [0]) | port; 
    TCCR1B |= (Modes [mode] [1]) | clock;
    }  // end of Timer1::setMode
   
  }  // end of namespace Timer1 


My page here has the namespaces for Timer0 and Timer2 as well:

http://www.gammon.com.au/forum/?id=11504

This now lets you write:

Code: [Select]
 Timer1::setMode (4, Timer1::PRESCALE_1, Timer1::CLEAR_A_ON_COMPARE);

Or, for Timer 2 it would just be (for CTC mode):

Code: [Select]
 Timer2::setMode (2, Timer2::PRESCALE_1, Timer2::CLEAR_A_ON_COMPARE);
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

renniejohnson

Got ethernet working on your code.  I can send a single character (r,g,b,i or c).  C means full color RGB simultaneous exposure.  It changes the exposure time and color channel:

Code: [Select]
#include <SPI.h>

#include <Dhcp.h>
#include <Dns.h>
#include <Ethernet.h>
#include <EthernetClient.h>
#include <EthernetServer.h>
#include <EthernetUdp.h>
#include <util.h>

//Serial
int inByte = 0;

//Ethernet

byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(198,0,0,4);



EthernetServer* m_server;
EthernetClient m_client;
uint8_t *m_mac_address;
IPAddress m_ip;
int m_port;
char m_command[40];
int m_commandLength;





const byte SHUTTER = 9;
//const byte START_LED = 7;
//const byte END_LED = 8;

const byte RED = 3;
const byte GREEN = 5;
const byte BLUE = 6;
const byte IR = 7;

void setup() {
 
  m_server = new EthernetServer(12345);
// m_mac_address = mac_address;
// m_ip = ip;
//  m_port = port;
 
  //Initialize Ethernet Library
Ethernet.begin(mac, ip);
//Start Ethernet Server
m_server->begin();


   pinMode (RED, OUTPUT);
   pinMode (GREEN, OUTPUT);
   pinMode (BLUE, OUTPUT);
   pinMode (IR, OUTPUT);
   
   pinMode (SHUTTER, INPUT);

   digitalWrite (RED, LOW);   
   digitalWrite (GREEN, HIGH); 
  digitalWrite (BLUE, LOW);   
   digitalWrite (IR, LOW);
 
   
   digitalWrite (SHUTTER, HIGH);   
   Serial.begin(9600);

}  // end of setup

ISR(TIMER1_COMPA_vect)
{
   TCCR1A = 0;        // reset timer 1
   TCCR1B = 0;
   Serial.println("Interrupt Called");
}  // end of TIMER1_COMPA_vect

// TCCR1A, TCCR1B
const byte timer_modes [16] [2] =
  {
 
  { 0,                         0 }, // 0: Normal
  { _BV (WGM10),               0 },  // 1: PWM, Phase-correct, 8 bit
  { _BV (WGM11),               0 },  // 2: PWM, Phase-correct, 9 bit
  { _BV (WGM10) | _BV (WGM11), 0 },  // 3: PWM, Phase-correct, 10 bit
  { 0,                         _BV (WGM12) },  // 4: CTC, top = OCR1A
  { _BV (WGM10),               _BV (WGM12) },  // 5: Fast PWM, Phase-correct, 8 bit
  { _BV (WGM11),               _BV (WGM12) },  // 6: Fast PWM, Phase-correct, 9 bit
  { _BV (WGM10) | _BV (WGM11), _BV (WGM12) },  // 7: Fast PWM, Phase-correct, 10 bit
  { 0,                         _BV (WGM13) },  // 8: PWM, phase and frequency correct, top = ICR1   
  { _BV (WGM10),               _BV (WGM13) },  // 9: PWM, phase and frequency correct, top = OCR1A   
  { _BV (WGM11),               _BV (WGM13) },  // 10: PWM, phase correct, top = ICR1A   
  { _BV (WGM10) | _BV (WGM11), _BV (WGM13) },  // 11: PWM, phase correct, top = OCR1A
  { 0,                         _BV (WGM12) | _BV (WGM13) },  // 12: CTC, top = ICR1   
  { _BV (WGM10),               _BV (WGM12) | _BV (WGM13) },  // 13: reserved
  { _BV (WGM11),               _BV (WGM12) | _BV (WGM13) },  // 14: Fast PWM, TOP = ICR1
  { _BV (WGM10) | _BV (WGM11), _BV (WGM12) | _BV (WGM13) },  // 15: Fast PWM, TOP = OCR1A
 
  };
 
// timer activation
enum { NO_CLOCK, PRESCALE_1, PRESCALE_8, PRESCALE_64, PRESCALE_256, PRESCALE_1024, CLOCK_T1_FALLING, CLOCK_T1_RISING };

// what ports to toggle on timer fire
enum { NO_PORT = 0,
       TOGGLE_ON_COMPARE  = _BV (COM1A0),
       CLEAR_ON_COMPARE   = _BV (COM1A1),
       SET_ON_COMPARE     = _BV (COM1A0) | _BV (COM1A1) };

// choose a timer mode, set which clock speed, and which port to toggle
void setTimerMode (const byte mode, const byte clock, const byte port)
  {
  TCCR1A |= (timer_modes [mode] [0]) | port; 
  TCCR1B |= (timer_modes [mode] [1]) | clock;
  }  // end of setTimerMode
 
void FireShutter()
{
  TCCR1A = 0;        // reset timer 1
  TCCR1B = 0;
  digitalWrite (SHUTTER, HIGH);   // ready to activate
  pinMode (SHUTTER, OUTPUT);
 
  switch(m_command[0])
  {
  case 'r':
   digitalWrite (RED, HIGH);   
   digitalWrite (GREEN, LOW); 
   digitalWrite (BLUE, LOW);   
   digitalWrite (IR, LOW);
   OCR1A =  50;
   break;
   case 'g':
   digitalWrite (RED, LOW);   
   digitalWrite (GREEN, HIGH); 
   digitalWrite (BLUE, LOW);   
   digitalWrite (IR, LOW);
   OCR1A =  500;
   break;
   case 'b':
   digitalWrite (RED, LOW);   
   digitalWrite (GREEN, LOW); 
   digitalWrite (BLUE, HIGH);   
   digitalWrite (IR, LOW);
   OCR1A =  5000;
   break;
   case 'i':
   digitalWrite (RED, LOW);   
   digitalWrite (GREEN, LOW); 
   digitalWrite (BLUE, LOW);   
   digitalWrite (IR, HIGH);
   break;
   case 'c':
   digitalWrite (RED, HIGH);   
   digitalWrite (GREEN, HIGH); 
   digitalWrite (BLUE, HIGH);   
   digitalWrite (IR, LOW);
   OCR1A =  50000;
   break;
  }

   
// set up Timer 1
  TCNT1 = 0;         // reset counter
 
//  OCR1A =  999;       // compare A register value (1000 * clock speed)

  setTimerMode (4, PRESCALE_256, CLEAR_ON_COMPARE);

  TIFR1 |= _BV (OCF1A);    // clear interrupt flag
  TIMSK1 = _BV (OCIE1A);   // interrupt on Compare A Match 
}

void GetCommand()
{
int tempChar;

m_commandLength = 0;

ClearCommand();

do{
if(m_client.available())
{
tempChar = m_client.read();

if(tempChar != 13)
{
m_command[m_commandLength] = tempChar;
m_commandLength += 1;
}
}
}while(tempChar!=13);

m_server->print("OK");

}

void ClearCommand()
{
for(int x=0;x<40;x++)
m_command[x] = 0;
}


void loop() {
 
   
    m_client = m_server->available();
   
    if(m_client.available())
    {
        GetCommand();
        FireShutter();
    }
}  // end of loop



So what I need to do is figure out how to get the interrupt to fire.  I'll try to code it free of any class stuff.  Is it correct that just putting the ISR() function in my header before the class is enough to load the routine into the Arduino?  It must be, or your sketch wouldn't work.  Here is my original code that worked with my original timer routines:

Code: [Select]
IN HEADER FILE CLASS DECLARATION:

void (*isrCallback)();

void AttachTimerInterrupt(void (*isr)());




IN .CPP FILE:

ISR(TIMER1_OVF_vect)          // interrupt service routine that wraps a user defined function supplied by attachInterrupt
{
  led.isrCallback();
}


void LED_V3::AttachTimerInterrupt(void (*isr)())
{
  isrCallback = isr;                                       // register the user's callback with the real ISR
  TIMSK1 = _BV(TOIE1);                                     // sets the timer overflow interrupt enable bit

  TCCR1B |= m_clockSelectBits;
}


Nick Gammon

Code: [Select]
ISR(TIMER1_OVF_vect)          // interrupt service routine that wraps a user defined function supplied by attachInterrupt
{
  led.isrCallback();
}


Yes, apart from the comment.

attachInterrupt is for the specific cases of pins D2/D3 external interrupts. They already have an ISR declared inside WInterrupts.c, like this:

Code: [Select]
SIGNAL(INT0_vect) {
  if(intFunc[EXTERNAL_INT_2])
    intFunc[EXTERNAL_INT_2]();
}


(ISR is more-or-less synonymous with SIGNAL).

So the attachInterrupt function actually puts a function address into the intFunc array, which is used by the above ISR to call your function.

In the case of timers you don't use attachInterrupt, you just directly declare your ISR.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

renniejohnson

#18
Feb 21, 2012, 10:53 pm Last Edit: Feb 22, 2012, 01:17 am by renniejohnson Reason: 1
I think I've found the problem:

You changed the compare mode to CTC, which changes the call to IRS().  I had the wrong argument:

ISR(TIMER1_OVF_vect)  

I kept the argument the same and just pasted your code inside of the ISR() implementation.

The correct argument is found in your code:

ISR(TIMER1_COMPA_vect)

So, it looks like it's going to work.

I'm sure I'll have some other questions.  

If you would care to send your home address to renniejohnson@adelphia.net, my wife and I would like to express our appreciation in a small way.  Your expertise is valuable and is not ever assumed.  This post was complicated and you're the only one that responded.  

-Rennie

Go Up