Приветствую! Не могу решить казалось бы простую задачу.
Имеем: - Контроллер ATMega 644PA (так же проверялось на 644А)
- Часовой кварц на тактирование таймера 2
- Контроллер тактируется кварцем 8МГц, выставлен фьюз CKDIV8, т.е. тактовая 1МГц.
- Режим энергосбережения: Power-save
- Компилятор CodeVisionAVR 2.05.3 и 3.12
Необходимо: - Максимальное энергосбережение (длительное батарейное питание).
- Получать 3 прерывания OCR2A, OCR2B, T2OVF с частотой 32Гц. Контроллер проснулся по OCR2A, дернул одной ногой, уснул. Проснулся по OCR2B, дернул другой ногой, уснул. Проснулся по T2OVF, изменил счетчик, уснул.
Реализация: - Т2 работает постоянно, прерывание OVF включено постоянно. Делитель частоты кварца PCK/2. Получаем прерывания 128 раз в секунду. Ставим счетчик на переменной от 0 до 3. На каждое 3е значение счетчика разрешаем прерывания OCR2A и OCR2B. В остальные значения счетчика прерывания по совпадению запрещены.
Проблема: - Не срабатывают прерывания OCR2A и OCR2B, или следуют сразу после T2OVF, т.е. не в свое время. Если закомментировать #asm("sleep"), то всё работает как нужно.
Попытки исправить: - Менял фьюзы, не уверен что выставлено правильно ибо понимаю не до конца. Стоит кварц на 8Мгц, но так же фьюз делителя на 8, т.е. тактовая 1 МГц. Соответственно какой фьюз выставить, на 1 или 8Мгц? Пробовал CKSEL3..0 111, 110,101. Разницы не заметил. Далее CKSEL0 и SUT1..0. Пробовал разные варианты. По даташиту CKSEL нужно ставить в 1, т.к. у меня crystal oscillator, а не ceramic resonator. При выборе ceramic потребление ниже, зато с применением SLEEP прерывания OCR2A и OCR2B “колбасятся” влево-вправо. Видимо таймер останавливается или сбивается. Не понимаю как такое может происходить. При изменении на crystal стабильность на порядок лучше, но теряются или некорректно отрабатываются прерывания OCR2A и OCR2B.
- Пытался сбрасывать флаги прерываний OCR2A и OCR2B. В результате они могли просто не появляться или же опять срабатывать не постоянно, а через раз.
- Менял режим энергосбережения. В IDLE таких проблем нет. Если закомментировать SLEEP то всё так же работает как надо.
Работаю в железе. Протеус не использую. Результат смотрю осцилографом.
Читал даташит. Каких-то явных ограничений не увидел. Контроллер должен просыпаться от любого из прерываний Т2. Сильно прошу не пинать, программирование не моя сфера, но есть желание разобраться. И прежде чем написать сюда много раз читал документацию и форумы. Явного решения не увидел. Да и мало кто с энергосбережением заморачивался.
Отвечать смогу только по вечерам, так как на работе интернета нет и все коммуникации запрещены…
Приму любые наставления, исправления и попытки помочь разобраться.
Исходный код:
Спойлер
Fuses: SUT0=0, CKDIV8=0. Остальные не установлены.
Код:
#include <mega644a.h>
#include <delay.h>
#include <sleep.h>
unsigned char c_on=0; // Счетчик входа а прерывания T2OVF
// Timer2 output compare interrupt service routine
interrupt [TIM2_COMPA] void timer2_compa_isr(void)
{
PORTC.2=1; // Контролирую вход в прерывание
delay_us(50); // Различаю прерывание А
PORTC.2=0;
}
// Timer2 output compare interrupt service routine
interrupt [TIM2_COMPB] void timer2_compb_isr(void)
{
PORTC.2=1; // Контролирую вход в прерывание
delay_us(150); // Различаю прерывание B
PORTC.2=0;
}
// Timer2 overflow interrupt service routine
interrupt [TIM2_OVF] void timer2_ovf_isr(void)
{
c_on++;
if (c_on>=4) c_on=0;
if (c_on==3){
TIFR2 =(1<<OCF2B) | (1<<OCF2A) | (1<<TOV2); // Сброс флага о предыдущих прерываниях
TIMSK2=(1<<OCIE2B) | (1<<OCIE2A) | (1<<TOIE2); // Разрешить прерывания OCR2A OCR2B OCR2OVF
}
else {
TIMSK2=0x01; // Запретить прерывания OCR2A OCR2B
}
PORTC.2=1; // Контролирую вход в прерывание
delay_us(100); // Различаю прерывание OVF
PORTC.2=0;
}
void main(void)
{
// Crystal Oscillator division factor: 1
#pragma optsize-
CLKPR=0x80;
CLKPR=0x00;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif
// Port C initialization
// Func7=In Func6=In Func5=In Func4=In Func3=Out Func2=Out Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=0 State2=0 State1=T State0=T
PORTC=0x00;
DDRC=0x0C;
// Timer/Counter 2 initialization
// Clock source: Crystal on TOSC1 pin
// Clock value: PCK2
// Mode: Normal top=0xFF
// OC2A output: Disconnected
// OC2B output: Disconnected
ASSR=0x20;
TCCR2A=0x00;
TCCR2B=0x01;
TCNT2=0x00;
OCR2A=0x57;
OCR2B=0x62;
// Timer/Counter 2 Interrupt(s) initialization
TIMSK2=0x01;
/* Prepare the sleep in POWER-SAVE mode*/
SMCR |= (1<<SE) | (0<<SM2) | (1<<SM1) | (1<<SM0);
// Global enable interrupts
#asm("sei")
while (1)
{
PORTC.3=1; // Для контроля входа в SLEEP
PORTC.3=0;
#asm("sleep");
}
}
Осциллограммы:
Спойлер
