Pages: [1] 2   Go Down
Author Topic: Timer1 Single Execution  (Read 1518 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Jr. Member
**
Karma: 1
Posts: 77
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I apologize that this post is large, but I can't make it smaller and be able to articulate my application and question.

I have a custom circuit board I created that uses an Arduino Nano and Wiznet WIZ811MJ ethernet interface.  The board controls a TTL critical time shuttering circuit of an LED board made up of discreet red, green and blue LED's.  I use Timer1 pin 9 for the exposure integration control.

My code is derived from the "Timer1" example on the Arduino site.  It puts Timer1 into "phase and frequency correct pwm" mode, which is how the Timer1 example worked.  I don't need PWM, but if I disable it on the registers, the timer only turns the lamps on and they never turn off.  I set the duty cycle to 3, which means that the pin goes low for 99.7% (the exposure period) of the pwm cycle and returns to high for 0.03%.   If I set it lower than three, I cannot get exposures shorter than 1-5ms without the output sticking.

My application requires a single execution of the timer (LOW during shuttering,HIGH when off).  Based on my limited understanding of the Atmel timer registers, it is unclear to me if I can program the registers into an alternate mode of operation to make the timer execute just one LOW/HIGH cycle and then go idle.  Presently, I am turning the timer off in the ISR() software interrup routine, but it is causing problems for exposure times under 200 microseconds and is not very reliable.  I'd like to be able to get down to just a few microseconds, if possible, and I must be able to generate thousands of continuous executions without any failure or incorrect exposure times, as this is a motion picture application.

I used to work with AMD 186 processors, which worked a little differently.  They had separate interrupts for each side of the PWM, and you could more easily program them for a single execution.
 
Here is my code (most of which comes from Timer1 example):

Timer Functions:

Code:
void LED_V3::InitializeTimer()
{
//Initialize Timer1
TCCR1A = 0;                 // clear control register A
TCCR1B = _BV(WGM13);        // set mode 8: phase and frequency correct pwm, stop the timer
DDRB |= _BV(PORTB1);        // sets data direction register for pwm output pin
    TCCR1A |= _BV(COM1A1);     // activates the output pin
}

void LED_V3::SetTimerPeriod(long microseconds)
{
// unsigned char clockSelBits;
// char oldSReg;
// unsigned int pwPeriod;

long cycles = (F_CPU / 2000000) * microseconds;                                // the counter runs backwards after TOP, interrupt is at BOTTOM so divide microseconds by 2
  if(cycles < RESOLUTION)              m_clockSelectBits = _BV(CS10);              // no prescale, full xtal
  else if((cycles >>= 3) < RESOLUTION) m_clockSelectBits = _BV(CS11);              // prescale by /8
  else if((cycles >>= 3) < RESOLUTION) m_clockSelectBits = _BV(CS11) | _BV(CS10);  // prescale by /64
  else if((cycles >>= 2) < RESOLUTION) m_clockSelectBits = _BV(CS12);              // prescale by /256
  else if((cycles >>= 2) < RESOLUTION) m_clockSelectBits = _BV(CS12) | _BV(CS10);  // prescale by /1024
  else        cycles = RESOLUTION - 1, m_clockSelectBits = _BV(CS12) | _BV(CS10);  // request was out of bounds, set as maximum
 
  m_oldSREG = SREG;
  cli(); // Disable interrupts for 16 bit register access
  ICR1 = m_pwmPeriod = cycles;                                          // ICR1 is TOP in p & f correct pwm mode
  SREG = m_oldSREG;
 
  TCCR1B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12));
}

void LED_V3::StartTimer()
{
unsigned int tcnt1;

do { // Nothing -- wait until timer moved on from zero - otherwise get a phantom interrupt
m_oldSREG = SREG;
cli();
tcnt1 = TCNT1;
SREG = m_oldSREG;
  } while (tcnt1==0);


TCCR1B |= m_clockSelectBits;

}
void LED_V3::SetTimerPWM(int duty) 
{
// expects duty cycle to be 10 bit (1024)/Output Pin Must Be 9

unsigned long dutyCycle = m_pwmPeriod * duty;

//Pin 9
    DDRB |= _BV(PORTB1);                                   // sets data direction register for pwm output pin
    TCCR1A |= _BV(COM1A1);                                 // activates the output pin


  dutyCycle *= duty;
  dutyCycle >>= 10;

 // Serial.println("LED_VE:SetTimerPWM()");
 // Serial.println(dutyCycle);
 
  m_oldSREG = SREG;
  cli();
  OCR1A = dutyCycle; //Pin 9
  SREG = m_oldSREG;

  TCCR1B |= m_clockSelectBits;

}
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;
}



Interrupt Function:

void StopExposure()
{
TCCR1B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12));
gBusy = 0;
}


This is my program setup code:


Code:
//Timer1 Initialization
InitializeTimer();
SetTimerPeriod(1000);
SetTimerPWM(3);
AttachTimerInterrupt(StopExposure);



Finally, these are the functions I call to set up an exposure:

Code:
SetTimerPeriod(m_exposureRed);
StartTimer();

I would appreciate any guidence as to to how it might be possible to automate a single exposure without having to kill the timer during the interrupt routine.  I have read many of the Atmel docs and Arduino posts, but timer registers are still very confusing to me.

Thank you.
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 497
Posts: 19055
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Is the start time critical, or do you start whenever you want to?
Logged


Offline Offline
Jr. Member
**
Karma: 1
Posts: 77
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Nick, I'm always grateful to hear from you.

No, the start time is of no consequence.  I send an ethernet text command from the windows host application (written as a Windows Forms Application in Visual C++ .Net 2008) to trigger an exposure.  A delay of a few milliseconds is common and acceptable.

Here is my sketch:

Code:
#include <LED_3.h>

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


void setup() {

  led.Init(mac,ip,12345);
  Serial.begin(9600);
  Serial.println("LED Controller Vers. 3.1");
  Serial.println("Copyright 2006 - 2012 Rennie Johnson & Associates, Inc.");

}

void loop() {

  // wait for a new client:
  led.WaitClient();   
  if(led.m_client.available())
  {
     commandReturn = led.GetCommand();
     commandReturn = led.ProcessCommand();
    delay(1);
  }
}

GetCommand() retrieves simple text based commands from the ethernet client.  For instance, a command to set the red exposure value to a millisecond would be "r=1000".  The command to execute a red exposure would be "xr".

The complete class function to execute an exposure is:

Code:
int LED_V3::Expose(int channel)
{
if(gBusy)
{
SendBusy();
return SYSTEM_BUSY;
}

SetBusy(1);

switch(channel)
{
case CHANNEL_SELECTED:
break;
case CHANNEL_RED:
SetChannel(CHANNEL_RED,0);
break;
case CHANNEL_GREEN:
SetChannel(CHANNEL_GREEN,0);
break;
case CHANNEL_BLUE:
SetChannel(CHANNEL_BLUE,0);
break;
case CHANNEL_IR:
SetChannel(CHANNEL_IR,0);
break;
case CHANNEL_RGB:
SetChannel(CHANNEL_RGB,0);
break;
default:
SendIllegal();
SetBusy(0);
return ILLEGAL_COMMAND;
break;
}

StartTimer();
SendOK();
return RETURN_SUCCESS;
}

Hope this helps.
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 497
Posts: 19055
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Well I've been playing with it, and am not totally happy with the results, but perhaps this will help ...

Code:
const byte LED = 9;

void setup() {
}  // end of setup

volatile byte isrCount;

ISR(TIMER1_COMPA_vect)
{
 if (++isrCount)
   {
   digitalWrite (LED, HIGH);
   pinMode (LED, INPUT);
   }
}  // end of TIMER1_COMPA_vect

void loop() {
  delay (100);

  TCCR1A = 0;        // reset timer 1
  TCCR1B = 0;
  TIFR1 |= _BV (OCF1A);    // clear interrupt flag
  TCNT1 = 0;         // reset counter
  GTCCR = _BV (PSRASY);        // reset prescaler now

 // digitalWrite (LED, HIGH);  // ensure high
  isrCount = 0;
  digitalWrite (LED, HIGH);   // ready to activate
  pinMode (LED, OUTPUT);      // activate

 // set up Timer 1
  TCCR1A = _BV (COM1A0) | _BV(WGM10) | _BV(WGM11);  // toggle OC1A on Compare Match
  OCR1A =  199;       // compare A register value (200 * clock speed) - ZERO RELATIVE!
  TCCR1C |= FOC1A;    // force compare
  TCCR1B =  _BV(WGM13) | _BV(CS10);   // PWM phase correct, no prescaling

  TIMSK1 = _BV (OCIE1A);             // interrupt on Compare A Match 

}  // end of loop

This sets up a test where, at 100 mS intervals (ie. the delay(100) ) it does its stuff.

What I am trying to do is get the timer itself to toggle the shutter, which should be highly accurate, and just use the ISR to turn the timer off, thus making it single-shot.

I can't really complain about this test run:



The theory is it should be open for 200 x 62.5 nS (namely 12.5 uS) which it is, exactly.

But it's not consistent. Sometimes it opens for 11 uS (even when I have the time set to 999 which should be 62.5 uS).

The documentation for timers is a bit unclear as to whether you should set the count before or after activating the timer, and how the force compare works.

So it's half there, more work is needed to make it reliable.
Logged


Offline Offline
Jr. Member
**
Karma: 1
Posts: 77
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Wow, I'll be studying this all day!

I'm guessing you're asleep now, but here are just a few quick questions:

1) It appears you're using pin 9 just as a generic output pin, and not as the hardware output pin of Timer1.  Is that because of the loop design of your interrupt routine, and the PWM toggling of the timer hardware pin would not work?  I understood it was more accurate to use a hardware output pin, which doesn't require the overhead of a call to "digitalWrite()".  Would your design be just as accurate with any output pin?

2) Why do you change the LED pin to an input pin inside the interrupt function?

3) If you have the time, could you run a test at a few longer intervals, like 1,000us, 10,0000us, 100,000us and 1,000,000us, to see if the error you showed (+/-3us) multiples also?  If it stays within 5-6us at longer times, I think this accuracy may be OK.  It doesn't matter if the actual shutter period is different from what you asked for (11us vs 12.5us).  It matters that each successive iteration be consistent with each other, so there is no flicker in the camera motion capture.  I don't expect to be shuttering under 50us, probably not under 100us.  It is also critical that the timer always shut off when it is supposed to, which it appears your interrupt has achieved. 

4) What is that Oscilliscope you've got?  It is a Windows based system?  I think I need it, and I don't want to wait until Christmas!  Are they affordable?

5) Last time I posted, you sent to a link to your own forum with an explanation of hardware timers.  It was very helpful.  Is there a link that explains the programming of the Atmel AVR registers that a weekend warrior can understand?  Something like "Atmel Timers For Dummies."

I'll be playing around with your code today and post again this evening (after you've had your breakfast).

You really spent a lot of time on this.  Thank God there are gurus like you out there who give back so much to others.  If it weren't for people like you on this site, as well as the OpenGL and Visual Studio forums, my project would never be possible.

Until tonight....
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 497
Posts: 19055
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

1) It appears you're using pin 9 just as a generic output pin, and not as the hardware output pin of Timer1. 

No, it is changed in hardware or it wouldn't be accurate:

Code:
  TCCR1A = _BV (COM1A0) | _BV(WGM10) | _BV(WGM11);  // toggle OC1A on Compare Match

I think the comment might be wrong, but it is changing in hardware. I tried so many things at the time.

Quote
2) Why do you change the LED pin to an input pin inside the interrupt function?

My objective was to wrest control of the LED pin back from the timer to my good self. The timer only affects the pin if it is configured for output. I also tried stopping the timer but I think that was less satisfactory.

I'll address the other questions after breakfast...
Logged


Offline Offline
Jr. Member
**
Karma: 1
Posts: 77
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi, Nick.

I hope you slept well.

I realize you're spending a lot of time on this.  If it is too much, I understand.  If there is anything I can do to say thank you, please let me know.  I had a Kodak Cineon expert overseas for whom I used to send obscure music CD's to that he couldn't get there (southern California is the capital of WEIRD).  So please let me know if I may return the favor to you in some way.

I tried creating an exposure function out of your code.  It seems to work in your delay(1000) loop, but I can only call it once in my class, after which I can't get a response from ethernet on the second call.  So I wonder if the timer is still running?  The goal is to turn the timer off after one iteration and reset it to a state where I can make another call.   Is there a way to reset the timer in the code so it will execute in multiple calls like the first call?

I tried adding the following call to the end of the Interrupt Function:

TCCR1B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12));   

It didn't fix the freezing problem.

The only thing I really added was the gBusy variable.  I wonder if that is causing a problem?

Code:

Code:
GLOBAL VARIABLES (OUTSIDE OF LED CLASS):

static volatile int   gBusy;
static volatile byte  isrCount;




EXPOSURE FUNCTION:

int LED_V3::NickGammonExposure()
{
  TCCR1A = 0;        // reset timer 1
  TCCR1B = 0;
  TIFR1 |= _BV (OCF1A);    // clear interrupt flag
  TCNT1 = 0;         // reset counter
  GTCCR = _BV (PSRASY);        // reset prescaler now

 // digitalWrite (LED, HIGH);  // ensure high
  isrCount = 0;
  digitalWrite (PIN_EXPOSE, HIGH);   // ready to activate
  pinMode (PIN_EXPOSE, OUTPUT);      // activate

 // set up Timer 1
  TCCR1A = _BV (COM1A0) | _BV(WGM10) | _BV(WGM11);  // toggle OC1A on Compare Match
  OCR1A =  199;       // compare A register value (200 * clock speed) - ZERO RELATIVE!
  TCCR1C |= FOC1A;    // force compare
 
  TCCR1B =  _BV(WGM13) | _BV(CS10);   // PWM phase correct, no prescaling

  //Nick, I tried modifying the prescaling, which created the toggling effect described in the post
 // TCCR1B =  _BV(WGM13) | _BV(CS12);   // PWM phase correct, pres

  TIMSK1 = _BV (OCIE1A);             // interrupt on Compare A Match 

  return RETURN_SUCCESS;
}



INTERRUPT ROUTINE:

void StopExposure()
{
   if (++isrCount)
   {
   digitalWrite (PIN_EXPOSE, HIGH);
   pinMode (PIN_EXPOSE, INPUT);
   gBusy = 0;
   }
}



I also wanted to ask you a little about modifying your code to generate a user-defined exposure timed.  With the Timer1 source code, I was able to specify exposure times between 1us (crash city!) and 10 seconds.  The SetTimerPeriod() function seems to accomplish this.  I experimented with changing the prescaling the OCR1A values, but it seemed to cause a toggling output between your original 12.5us exposure and the new exposure length, 1 second apart from each other.  How would I modify registers to generate a single exposure
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 497
Posts: 19055
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm going to take another look at it today, now I am more alert (too much wine!), because I think the problem should be solvable. The problem is that there are 16 timer modes (page 136 of the datasheet) and I think the first thing is to choose the most appropriate one. Secondly it should be possible to get the timers to reset to some "initial" state. It isn't totally clear (maybe it will be after more reading) which registers have to be initialized, to what, and in what order.

I'll throw some debugging digitalWrites in so I can see where, in relation to when I try to start doing it, the timer does its stuff.

I think the problem is, partially at least, that if you get the hardware to force the output pin low, it is put high immediately, not when the timer starts. This adds a slight extra amount to the time. I suppose if it is a fixed length you could compensate, but it looks messy.

As for lengthy exposures (like 10 seconds) I would skip all this timer stuff and just do something like a simple delay (or check the elapsed time with micros() ).  I can only see exact intervals being important for very quick exposures.

In fact, isn't even a 1 mS exposure very short?  (like 1/1000 of a second). You don't really need microsecond accuracy do you?

The screen shot was from my Saleae "Logic" analyzer. Extremely useful:

http://www.saleae.com/logic/

I'll look into the freezing problem later. Let's get the core stuff working first.
Logged


Offline Offline
Jr. Member
**
Karma: 1
Posts: 77
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
I think the problem is, partially at least, that if you get the hardware to force the output pin low, it is put high immediately, not when the timer starts.

Would it help if I made the exposure occur during the HIGH period?  The reason I designed the TTL circuitry to shutter on LOW signals is that is how the timer seemed to work when set up with the Timer1.cpp sample code.  It went LOW for the first phase of the PWM and HIGH on the second phase. When I turned off the timer in the interrupt, it just stayed HIGH.  I see in your code that you specifically set the pin high before you disable the pin.  I understand that it might be possible to invert the PWM outputs.  I can mod my board and bypass the inverter, and change the board design when I order more.

Let me know if this give you more options.

Wine with breakfast?  I'd go with a Mimosa.
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 497
Posts: 19055
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Would it help if I made the exposure occur during the HIGH period?  The reason I designed the TTL circuitry to shutter on LOW signals is that is how the timer seemed to work when set up with the Timer1.cpp sample code. 

I'm not too concerned with that part, because you can always invert the output in hardware or something.

Wine with breakfast?  I'd go with a Mimosa.

No, that was last night. smiley



I'm curious though about the short exposure times. Can you give a more specific metric about the target performance? Any particular reason why millisecond accuracy (exposure time) isn't good enough, give or take a couple of microseconds?
Logged


Offline Offline
Jr. Member
**
Karma: 1
Posts: 77
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I need to expose 35mm film at 24fps in real time, which requires very rapid exposure (less than 1ms) with a very high power light source.  If the exposure are too slow, I will get smear in capture, as the film is moving continuously on a servo driven sprocket.

I don't know exactly what the times will be, but I need to design the LED control to be as accurate as possible, just in case it is needed.

I'm off to workout with my daughter.

G'Day
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 497
Posts: 19055
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I seem to be getting more consistent results now. For example, this should be 62.5 uS:



Code to produce the above:

Code:
const byte SHUTTER = 9;
const byte START_LED = 7;
const byte END_LED = 8;

void setup() {
   pinMode (START_LED, OUTPUT);
   pinMode (END_LED, OUTPUT);
   pinMode (SHUTTER, INPUT);

   digitalWrite (START_LED, LOW);  
   digitalWrite (END_LED, LOW);  
   digitalWrite (SHUTTER, HIGH);  

}  // end of setup

ISR(TIMER1_COMPA_vect)
{
   // toggle "end" LED
   PORTB |= _BV (0);  // pin D8
   PORTB &= ~_BV (0);
  
   TCCR1A = 0;        // reset timer 1
   TCCR1B = 0;
}  // 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 loop() {
  delay (250);   // debugging

  TCCR1A = 0;        // reset timer 1
  TCCR1B = 0;

  digitalWrite (SHUTTER, HIGH);   // ready to activate
  pinMode (SHUTTER, OUTPUT);
  
  // toggle "start" LED
  PORTD |= _BV (7);   // pin D7
  PORTD &= ~_BV (7);

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

  setTimerMode (4, PRESCALE_1, CLEAR_ON_COMPARE);

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

}  // end of loop

Those bit patterns were driving me mad, so I made a table of them and a couple of enums.

Now you can choose any of the 16 timer modes by just selecting it from the table. I am now using mode 4 (CTC with top at OCR1A).

There seems to be a fixed lag of around 2 uS, which I assume is the port being toggled slightly before the timer starts counting. Still, you can compensate for that by subtracting that from the desired exposure.



This shows trying to get 10 mS exposure:



As you can see, only about 8 uS out (low this time, interestingly). However that's only about 0.1% which shouldn't make a huge difference when taking a photo. Actually I did that one by using a prescaler of 64 and a count of 250, so using a prescaler would introduce some element of error (that is, the count is coarser).

This shows trying to get 5 uS exposure:



This one is about 1.6 uS too long, but still not too bad.



The extra LEDs are for debugging, so I could see when I was trying to turn on the timer, and when the ISR is entered, which you can see displayed in the logic analyzer.
Logged


Offline Offline
Jr. Member
**
Karma: 1
Posts: 77
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hey Nick:

Wow, that's amazing!!!  I'm now easily able to change the exposure times by changing your prescaler enum, and the value in OCR1A.

I'm having some trouble integrating this code into my project.  As it is written, I can't get the interrupt function to trigger within my class implementation.  I'm going to play around with it today.  I also have ethernet and LCD code in there that may be messing things up.  I think I've traced the problem to the fact that I'm in over my head and have no idea what I'm doing, but I'll do some controlled tests later to confirm that.

I'm also going to modify your sketch to fire on a serial character input from the serial monitor, as a human triggering test.

Do you have an ethernet shield (or the equivalent)?  If you do, you could actually run my project.  I use the Wiznet WIZ811MJ, but it works with the standard Ethernet library, so an Arduino shield should work.  If you're comfortable with giving me an email address, I could link a .zip file of the library and project.  No big deal if you're not, I can just post the files in code boxes on the forum.  I use a terminal program I wrote in Visual C++ to communicate with the Arduino.  I set its IP to 198.0.0.4 and port to 12345.  I set the host computer to 198.0.0.1 and subnet to 255.255.255.0.  It would be best to test with that, because I use the same Socket class library in my Film Scanner application.  I suppose you could use the command prompt to send commands and receive responses.   Personally, I don't know how to do that.

I'll post again this evening when I've spent more time with this.  

Much appreciation,
Rennie



« Last Edit: February 21, 2012, 12:24:30 pm by renniejohnson » Logged

Offline Offline
Jr. Member
**
Karma: 1
Posts: 77
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Progress Update:

No problem inegrating a serial input with your Sketch.  The code has been modified to trigger my green exposure channel instead of your start/end LED's.  Also increased the shutter time.

Code:
int inByte = 0;


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() {
   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)
{
   // toggle "end" LED
 //  PORTB |= _BV (0);  // pin D8
 //  PORTB &= ~_BV (0);
 
   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);
   
 // set up Timer 1
  TCNT1 = 0;         // reset counter
 
 // OCR1A =  999;       // compare A register value (1000 * clock speed)
 OCR1A =  65535;       // compare A register value (1000 * clock speed)

 // setTimerMode (4, PRESCALE_1, CLEAR_ON_COMPARE);
  setTimerMode (4, PRESCALE_256, CLEAR_ON_COMPARE);

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

void loop() {
 
  if (Serial.available() > 0)
  {
    // get incoming byte:
    inByte = Serial.read();
    FireShutter();
  }
}  // end of loop


I need to figure out what in my program is interfering with your code.

So I'm off to it.....
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 497
Posts: 19055
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Wow, that's amazing!!!  I'm now easily able to change the exposure times by changing your prescaler enum, and the value in OCR1A.

Good. I thought you would be able to do that easily.

I'm having some trouble integrating this code into my project.  As it is written, I can't get the interrupt function to trigger within my class implementation.

Interrupts and class functions tend not to mix as the classes normally require an instance variable (ie. the "this" variable) unless you declare them static. But you will be able to work around it one way or another.

Don't worry about sending me the code for the minute, but I'll get back to you if I want a copy, thanks!
Logged


Pages: [1] 2   Go Up
Jump to: