I am programming a standalone ATMEGA328p. My goal is to display on PORTD and PORTC the realtime values of the ADCH and ADCL registers from sampling a voltage on input pin A0. I want to have the ADC triggered when Timer1 reaches the value set in OCR1B and then start over again using CTC mode.
I think I am not getting the auto trigger working right. Also, I'm not sure I need to set up the Timer1 interrupt or not. Here is my code. I basically gets a value at startup and never moves... not the behavior I desire.
Below is my code. Any help is appreciated
#include <avr/interrupt.h>
void setup() {
// Data direction registers (0 = input)
// Outputs =1, inputs =0
DDRD = 0xFF;
DDRC = 0x3E;
PINC= 0xFF;
PRR |= (1<<PRTWI)|(1<<PRTIM0)|(1<<PRTIM2)|(1<<PRSPI)|(1<<PRUSART0); //5.7mA with no Displays on
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B =0;
OCR1B =0;
TCNT1 =0; // Wise to force the count in range
ICR1 = 200; // output compare register used for timer1 sets freq
OCR1B = 200; // controls when ADC sample begins
// ctc, IR1 sets top
TCCR1B |= (1 << WGM12)|(1<<WGM13)|(1<<ICNC1);
TIMSK1 |= (1<<OCIE1B);
TCCR1B &= ~(1<<CS12)&~(1<<CS10);
TCCR1B |= (1<<CS11);
ADCSRA &= ~(1<<ADPS0);
ADCSRA |= (1<<ADPS2)|(1<<ADPS1);
//set reference voltage
ADMUX &= ~(1 << REFS0) & ~(1 << REFS1);
// Right adjust for reading 10 bit value
ADMUX &= ~(1<<ADLAR);
// 0000 selects ADC0 channel
ADMUX &= ~(1<<MUX3) & ~(1<<MUX2) & ~(1<<MUX1) & ~(1<<MUX0);
// Quiet ADC inputs
// Check ATmega328p datasheet
// for more information.
DIDR0 |= (1<<ADC5D)|(1<<ADC4D)|(1<<ADC3D)|(1<<ADC2D)|(1<<ADC1D)|(1<<ADC0D);
// auto trigger on compare match B.
ADCSRB |= (1 << ADTS2)|(1 << ADTS0);
ADCSRA |= (1 << ADEN)|(1<<ADATE)|(1<<ADSC);
}
ISR(TIMER1_COMPB_vect) {
}
void loop() {
while (!(bit_is_set(ADCSRA, ADIF))){
// Getting Data. Sit here and wait
}
PORTD = ADCL;
PORTC = ADCH;
ADCSRA |= (1<<ADIF);
}
with auto-triggering you shouldn't really need to busy wait for the ADC to finish or manually restart it, which appears to be what you are doing in the ISR. you just need to set timer1 duration to guarantee the ADC will have finished sampling before the ISR is called. the data will then just be sitting there in ADCH and ADCL ready for you to grab.
For some unknown for me reason, Atmel designed TIMER 1 channel B to be an auto trigger source of the ADC, the same time to run TIMER 1 itself in CTC mode, channel A must be set. I simply “bind” two channels A and B in “parallel”, so both of them rise interrupt flag at the same moment, only A re-starts a TIMER 1, and B generates “start new conversion” event and calling ISR for “maintenance” – take a new sample waiting in the line and switch a MUX to another channel of the oscilloscope.
http://coolarduino.wordpress.com/2012/09/19/quasi-real-time-oscilloscope-remix/
Second post devoted to AtMega32u4, but functionality ADC_Timer1 pretty much same as AtMega328
Other things, as pointed above, ADC should have enough time to complete conversion, if you need sampling rate >9.6 kHz, you have to change ADC prescaller to run it faster
memotick:
Thank you for that reply. It isn't clear to me from the datasheet what a "timer1 capture event" actually is. Please explain
Um... i can't.. really. The code was "borrowed". In search of a deeper understanding I also checked the data sheets but very confusing. I contacted the author with the same question. Other than "it causes the capture interrupt to be called when timer1 restarts". As far as I remember, I think I avoided the COMP vector as I was using phase-correct mode and didn't want the interrupt to happen until the timer counted down again. It's all a bit of a mess really Maybe someone will correct me if I've got it wrong. If you're using fast/ctc timer mode maybe best to stick with COMP vector as above.
Hope you manage to get it working - autotriggering ADC off timer = very powerful feature. thankyou mr arduino!
Thanks for that reply - I think I'll stick with CTC mode as I am more familiar with it.
I believe the problem I am having is only one ADC conversion is taking place... as if the auto triggering isn't actually happening, or possibly if I read the ADCH/ADCL registers, it doesn't clear a flag.
void loop() {
ADCSRA |= (1 << ADEN)|(1<<ADATE);
ADCSRA |= (1<<ADIF);
// wait until ADC is done obtaining a sample
while(ADIF==0); // ADIF goes high when a conversion is done
PORTC=ADCL;
PORTD=ADCH;
// ADCSRA &= ~(1<<ADEN);// could turn off ADC to save power
// clear ADC done flag
ADCSRA |= (1<<ADIF);
delay(1000);
}