Dear all,
i've working on this project for some time using Nick Gammon's forum hints (great stuff!) and some AVRfreaks threads, but still have issues and would need some guidance here.
I'd like to investigate a pulsed arc with a high velocity camera. Due to the variable light intensity the dynamic range of the camera is insufficient to correctly expose the high and low phase of the pulse, hence i'd like to make the exposure time variable while recording. It's possible as the camera can be controlled by a PWM signal with variable duty cycle. So i thought i use a simple photodiode connected to the ADC of an Arduino Mega2560 board.
Functional principle:
The frequency of a PWM output (i.e. frame rate - from 1.000 up to 25.000 fps), as well as the amount of pulses (i.e. pictures) is being set in the beginning and stays constant during the recording. The duty cycle of the output signal (i.e. exposure time - between 5-95% of the frame rate period) is being varied according to the actual light intensity.
My assumptions so far:
- Timer1 in phase & frequency correct mode for PWM generation
- Pulse counting in Timer1 overflow interrupt handler
- fast ADC conversion (sampling rate of 1 MHz)
So far so good - i can output a defined amount of pulses with a constant frequency and a varying duty cycle...
Issues:
- output changes logic - for a low light intensity the output is high and goes low for a pulse and vice versa, for a high light intensity the output is low and goes high for a pulse
- duty cycle seems to be constant for the first 2 pulses and afterwards varies as it's supposed to do...
I think it has to do with the update of the OCR1A register of the timer, but was unable to figure it out yet. Would it be better to run the ADC in free running mode to cure the constant duty cycle problem in the beginning? Also, i don't have a clue why the output changes the logic.
Thanks for your help,
mike
Code:
#include <math.h>
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
#define INLENGTH 7 // Maximal length of the string of numbers, representing the frequency.
#define INTERMINATOR 'H' // 'H' of 'Hz'
#define MinFreq 2 // Lowest frequency possible.
#define MaxFreq 8000000 // Highest frequency possible.
#define DutyMin 0.05 // Min duty cycle of HV-cam
#define DutyMax 0.95 // Max duty cycle of HV-cam
#define FASTADC 1 // AnalogIN 1 (PF1)
char inString[INLENGTH+2]; // Array, which contains the frequency, plus 'Hz'.
int inCount; // Counter.
int N; // Prescaler.
volatile int cycleLength; // Amount of counting steps.
float frequency; // Frequency.
float current_diode; // Value measured by analogRead.
volatile float current_rel; // Noramlized values of current_diode.
volatile int newOCR;
int frames;
volatile int i=0;
bool enable=0;
int timer_start = 0;
const unsigned int currentMin = round(DutyMin*1024);
const unsigned int currentMax = round(DutyMax*1024);
//---------------------------- ISR ----------------------------------
ISR(TIMER1_OVF_vect) {
// OCR1A = newOCR;
i++;
}
//---------------------------- Setup -------------------------------------
void setup() {
Serial.begin(9600);
#if FASTADC
ADCSRA |= ( (0<<ADPS2) ); // Setting prescaler of the ADC to 16, resulting a sampling rate of 1 MHz.
ADCSRA &= ~( (1<<ADPS1) | (1<<ADPS0) );
#endif
sbi(DDRB, DDB5); // OC1A
pinMode(PB5, OUTPUT);
cbi(TCCR1A, WGM11); // Use phase & frequency correct pwm mode, WGM13:0 = 8,
cbi(TCCR1A, WGM10);
cbi(TCCR1B, WGM12);
sbi(TCCR1B, WGM13);
sbi(TCCR1A,COM1A1); //set OC1A on Compare Match when up-counting
sbi(TCCR1A,COM1A0);
TCNT1 = 0; // Resetting timer 1.
Serial.println("Bitte Anzahl der Frames: ");
frame_count();
Serial.println("Bitte Frequenz eingeben (mit Hz)");
frequency_fct();
if(frequency >= 244)
N=1;
if(frequency >= 30 && frequency < 244)
N=8;
if(frequency >= 3 && frequency < 30)
N=64;
if(frequency >= 1 && frequency < 3)
N=256;
if(frequency < 1)
N=1024;
cycleLength = (16000000 / (2 * N * frequency)); // Calculating the amount of counting steps necessary,
Serial.print("Timer count: "); //considering the chosen frequency and corresponding prescaler.
Serial.println(cycleLength,DEC);
Serial.print("Prescaler: ");
Serial.println(N,DEC);
OCR1A = round(cycleLength/2); // Initial duty cycle: 50%
ICR1 = cycleLength;
// Setting prescaler bits.
switch (N){
case 1: TCCR1B |= ( (1<<CS10) );
TCCR1B &= ~( (1<<CS12) | (1<<CS11) );
break;
case 8: TCCR1B |= ( (1<<CS11) );
TCCR1B &= ~( (1<<CS12) | (1<<CS10) );
break;
case 64: TCCR1B |= ( (1<<CS11) | (1<<CS10) );
TCCR1B &= ~( (1<<CS12) );
break;
case 256: TCCR1B |= ( (1<<CS12) );
TCCR1B &= ~( (1<<CS11)| (1<<CS10) );
break;
case 1024: TCCR1B |= ( (1<<CS12) | (1<<CS10) );
TCCR1B &= ~( (1<<CS11) );
break;
}
timer_start = TCCR1B;
cbi(TCCR1B,CS10); // Timer 1 stop
cbi(TCCR1B,CS11);
cbi(TCCR1B,CS12);
sbi(TIMSK1,TOIE1);
sei(); // Enable all interrupts.
}
//--------------------- Loop ---------------------------------------
void loop(){
if(enable==1) {
if(i<frames) {
InputDiode();
newOCR = round(cycleLength*(1-current_rel/1023));
OCR1A = newOCR;
TCCR1B = timer_start;
}
else {
TCCR1B = 0;
enable = 0;
i=0;
Serial.println("Done!");
}
}
else {
Serial.print("Aufnahme [1: GO!]: ");
enable_fct();
}
}
//--------------------- Subroutines -------------------------
void InputDiode(){
current_diode = analogRead(PF1);
current_rel = constrain( current_diode, currentMin, currentMax);
}
//------------------------
void frequency_fct() {
frequency_input();
frequency=atol(inString); // Converting the array into a string of numbers.
frequency=constrain(frequency, MinFreq, MaxFreq); // Checking if the frequency is part of the value range.
Serial.print("Es wird gesetzt: ");
Serial.print(frequency,DEC);
Serial.println("Hz!");
Serial.println("--------------------------------------");
Serial.println("Bitte Serial Monitor neu starten, um eine andere Frequenz einzugeben.");
}
//------------------------
void frame_count() {
frequency_input();
frames=atol(inString);
Serial.print("Es werden: ");
Serial.print(frames,DEC);
Serial.println(" Frames aufgenommen!");
}
//------------------------
void enable_fct() {
frequency_input();
enable=atol(inString);
Serial.println("Go baby, go!!");
}
//------------------------- Function which generates the array, used above to receive the frequency.
void frequency_input(){
inCount=0;
do
{
while (Serial.available() == 0);
inString[inCount] = Serial.read();
if(inString[inCount] == INTERMINATOR) break;
if( (inString[inCount]<'0') || (inString[inCount]>'9') ){
inCount--;
}
}
while(++inCount < (INLENGTH+1));
Serial.flush();
}



