One thing that I notice from your code is that you are acquiring the samples in free running mode with ADC clock at F_CPU/2. According to the Atmega 328P datasheet
In Free Running mode, a new conversion will be started immediately after the conversion com-
pletes, while ADSC remains high.
and the conversion time for free running mode (on table 23-1, page 255) is specified as 13 ADC clock cycles. This means that your ADC ISR is firing every 26 clock cycles with your current prescaler settings. On top of that, your TIMER1_COMPA
ISR is also fired so frequently (every 64 cycles) that your ISR might actually take longer to execute than the desired time. You basically use all your time acquiring data and/or in your timer ISR. An easy solution is to increase the count in OCR1A
to, say, 5 and increase the prescaler of the ADC
ADCSRA |= (1 << ADPS1); // ADC prescaler 8, conversion time 4*26 MCU cycles
OCR1A = 5; // 5*64;
If you want to squeeze every last drop of performance out of the old 328 you should inspect the generated assembly for your interrupt routines, and figure out if you can do something to optimize them. Common performance hogs are, for example, frequent data fetches from SRAM, but if you're using gcc you might find all sorts if groovy stuff happening upon ISR entry.
Keep in mind that the human eye cannot detect insanely fast flickering (hence pwm driven leds work as they do, with no visible flicker). If the slower running rate looks visually unpleasant you can of course try to reduce the prescaler and OCR1A
, but you should also consider dispensing the ISR's altogether and just run your led flashing code in the main loop and have just the ADC ISR acquire data to be processed. This might work better since every function call takes at minimum 8 cycles (might have been 6, memory is a bit fuzzy on this particular detail) PLUS all the stack PUSH
'n an' POP
'n, which depends on your variable usage. So you could go for something like:
while(1) {
checkData();
handleLeds();
}
and have both functions be inlined.
Your code sets the counter for phase correct PWM operation with top value 0x3FF so the first problem you'll face is that the compare match interrupts will not be triggered in equal intervals because of the way that this mode works which is count to the top and then change direction and count backwards.
The other problem is that the counter counts up to 1023 (0x3ff) and back to 0 so there is no way to ever get a match interrupt for a value of 2000 (OCR1A = 2000)
phase correct PWM operation:
This is a minimum code for mega328 running @16MHz that sets Timer 1 to generate a fast PWM with a frequency of about 1KHz (frequency set by ICR1 and duty set by OCR1B) and also gives a timer 1 overflow interrupt with a 1KHz rate
#include <avr/io.h>
#include <avr/interrupt.h>
// Timer1 overflow interrupt service routine
ISR(TIMER1_OVF_vect)
{
PORTB ^= 1; // invert PORTB.0
}
int main(void)
{
// Port B initialization
// Function: Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=Out Bit1=In Bit0=Out
DDRB = 0x05;
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 2000,000 kHz
// Mode: Fast PWM top=ICR1
// OC1A output: Disconnected
// OC1B output: Non-Inverted PWM
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer Period: 1,0245 ms
// Output Pulse(s):
// OC1B Period: 1,0245 ms Width: 0,12806 ms
// Timer1 Overflow Interrupt: On
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A = (0 << COM1A1) | (0 << COM1A0) | (1 << COM1B1) | (0 << COM1B0) | (1 << WGM11) | (0 << WGM10);
TCCR1B = (0 << ICNC1) | (0 << ICES1) | (1 << WGM13) | (1 << WGM12) | (0 << CS12) | (1 << CS11) | (0 << CS10);
TCNT1 = 0;
ICR1 = 2048;
OCR1A = 0;
OCR1B = 256;
// Timer/Counter 1 Interrupt(s) initialization
TIMSK1 = (0 << ICIE1) | (0 << OCIE1B) | (0 << OCIE1A) | (1 << TOIE1);
// Global enable interrupts
sei();
while(1)
{
// Place your code here
}
}
Best Answer
As far as I know, it won't be disabled, both interrupts will be generated according to your timer and EXTI configuration. You can e.g. configure the timer capture interrupt to catch rising edges, and EXTI to catch falling edges.
If both are configured to act e.g. on rising edges, then both interrupt handlers will be called, one after the other. Priority levels in
NVIC
will decide which interrupt handler runs first, and the other is called immediately when the first handler returns.You can use both pins at the same time as interrupt sources.
The source for
EXTI0
is selected by theSYSCFG->EXTICR1
register bits 0-2. If you write001
(binary) to these bitsthen
EXTI0
will be connected toPB0
, andPA0
,PC0
etc will be ignored by EXTI.