Перейти к содержанию
    

вызвать прерывание каждые 50мсек

Приветствую.

 

Продолжаю разборки с atmega162. Вот такой кусок кода, использующий прерывание и таймер (использую WinAVR и AVRstudio для отладки):

 

#include <inttypes.h>

#include <interrupt.h>

#include <io.h>

#include <sig-avr.h>

 

SIGNAL(SIG_OUTPUT_COMPARE1A)

{

// далее обработка ......

// .........

}

 

void Timer1_Init(void)

{

SREG = (1 << SREG_I); // enable global interrupt

TCCR1A = 0x00;

TCCR1B = 0x04; // set prescale

TCCR1B = (1 << CS10) | (1 << WGM12);

 

TIMSK = (1 << OCIE1A);

 

// load value

TCNT1H = 0xFE;

TCNT1L = 0x7A;

 

// set compare register for 50ms

OCR1AH = 0x01;

OCR1AL = 0x86;

}

 

int main(void)

{

Timer1_Init();

 

// DDRB = 0xFF;

while (1)

;

return 1;

}

 

На первый взгляд все правильно, но не сдается мне что не получаю я задержки в 50мсек, а проверить в симуляторе, как предложил IgorKossak, не могу (симулятор не позволяет).

 

Вообщем, я окончательно запутался :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А осциллографа то нет под рукой? (Ну или хотябы светодиода)

осциллографа нет, в на глаз IMHO сложно заметить мерцание светодиода каждые 50мсек :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Попробуй подели генерируемую частоту (~50мс) на 20 и подай этот сигнал на светодиод. Будет мигать с частотой 1 Гц (1с), а это уже можно на глаз определить - сам так делал.

А если нодо определить болле точно, то без осцила не обойтись!

Или замени осцил например компом: подавай свой сигнал на LPT-порт и смотри с помощью ентой проги твой сигнал

dlpt.zip

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А зачем при инициализации таймера в TCNT писать 0xFE7A?

Это конечно понятно что это -0x186 но зачем это делать?

 

И еще при инициализации корректнее с начала выставлять OCR и TCNT и самым последним писать TCCR1B иначе можно получить ложное прерываение

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

void InitTimer (void)
{
 
TCCR1A = 0;
 TCCR1B = ((1<<CS10)|(1<<CS12)); //делим на 1024;
 TCNT1H = TCNT1L = 0;
 
 TIFR  |= (1<<OCF1B);
 Temp.B[0]=TCNT1L;
 Temp.B[1]=TCNT1H;
 Temp.I += PERIOD;
 OCR1BH = TimerOn2Char1.B[1];
 OCR1BL = TimerOn2Char1.B[0];
 TIMSK |= (1<<OCIE1B);  /*разрешаем прерывание по сравнению для таймера на 2 символов*/            
 
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Блин нечаяно нажалось, первое не смотрите....

Вот это код будет все время генерить период в сколько надо, задается PERIODом

#define PERIOD 0x186

#pragma  vector = TIMER1_COMPB_vect
__interrupt void CompareB(void)
{
 union  {unsigned char B[2];unsigned int I} Temp;

 Temp.B[0]=TCNT1L;
 Temp.B[1]=TCNT1H;
 Temp.I +=PERIOD;
 OCR1BH = Temp.B[1];
 OCR1BL = Temp.B[0];
 TIFR  |= (1<<OCF1B);

}



void InitTimer (void)
{

union {unsigned char B[2];unsigned int I} Temp;

Temp.I = 0;


TCCR1A = 0;
TCCR1B = ((1<<CS10)|(1<<CS12)); //делим на 1024;
TIFR  |= (1<<OCF1B); 

Temp.I = PERIOD;

TCNT1H = TCNT1L = 0;
OCR1BH = Temp.B[1];
OCR1BL = Temp.B[0];
 
TIMSK |= (1<<OCIE1B); 
            

}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Еще поправим маленько. Вечер глучу малость :blink:

#pragma  vector = TIMER1_COMPB_vect
__interrupt void CompareB(void)
{
union  {unsigned char B[2];unsigned int I} Temp;

Temp.B[0]=OCR1BL;
Temp.B[1]=OCR1BH;
Temp.I +=PERIOD;
OCR1BH = Temp.B[1];
OCR1BL = Temp.B[0];
TIFR  |= (1<<OCF1B);

}

Вот теперь точно все правильно :) :P

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А зачем при инициализации таймера в TCNT писать 0xFE7A?

Это конечно понятно что это -0x186 но зачем это делать?

 

И еще при инициализации корректнее с начала выставлять OCR и TCNT и самым последним писать TCCR1B иначе можно получить ложное прерываение

Приветствую.

 

Так вот с этим пунктом у меня и была изначально непонятность... Я считал, что в TCNT _обязательно_ нужно заносить инициализационное значение для отсчета тиков (c output compare либо без него). А потом - либо по переполнению, либо при match значения в OCR регистре.

 

Так получается, что если работаем в compare режиме, то TCNT уже обнулен и начинает считать сам по себе, без моего вмешательства? И мне достаточно только проинициализировать OCR ?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Блин нечаяно нажалось, первое не смотрите....

Вот это код будет все время генерить период в сколько надо, задается PERIODом

Приветствую.

 

Спасибо за пример. Есть вопросы по нему:

 

1) почему используется union а не structure. Это связано с особенностями генерации кода или только в целях экономии памяти?

2) зачем устанавливать принудительно OCF1B, как я понял этот флаг выставится сам при возникновении события совпадения ? И опять же при отладен в студии, этот флаг не выставляется (баг студии?)...

3) почему используется B-compare канал, а не A ?

4) при прогоне и отладке этого кода в AVR studio я не наблюдаю увеличения счетчика в TCNT...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Так получается, что если работаем в compare режиме, то TCNT уже обнулен и начинает считать сам по себе, без моего вмешательства? И мне достаточно только проинициализировать OCR ?

 

 

4) при прогоне и отладке этого кода в AVR studio я не наблюдаю увеличения счетчика в TCNT...

Изначально TCNT=0. при работе Clear Timer on Compare or CTC mode таймер считает от 0 до OCR1A потом сбрасывается. Действительно нужно проинициализироать только OCR. И запустить таймер в нужном режиме и прерывание будет выполнятся переодически

 

По поводу величения счетчика в TCNT в AVR studio - это скорее всего из-за прескалера в 1024 надо ждать 1024 такта

 

А вообще на мой взглад у lamerok слишком навороченный пример.

 

Я бы написал так

инициализация таймера

 

OCR1A=Fclk / 20; // 20 HZ

TCCR1B= (1 << CS10) | (1 << WGM12);

TIMSK = (1 << OCIE1A);

 

после этого разрешить прерывания и обработчик будет вызываться каждые 50 мсек (20HZ)! в обработчике регистры таймера трогать не надо.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Изначально TCNT=0. при работе Clear Timer on Compare or CTC mode таймер считает от 0 до OCR1A потом сбрасывается. Действительно нужно проинициализироать только OCR. И запустить таймер в нужном режиме и прерывание будет выполнятся переодически

 

По поводу величения счетчика в TCNT в AVR studio - это скорее всего из-за прескалера в 1024 надо ждать 1024 такта

 

А вообще на мой взглад у lamerok слишком навороченный пример.

 

Я бы написал так

инициализация таймера

 

OCR1A=Fclk / 20; // 20 HZ

TCCR1B= (1 << CS10) | (1 << WGM12);

TIMSK = (1 << OCIE1A);

 

после этого разрешить прерывания и обработчик будет вызываться каждые 50 мсек (20HZ)! в обработчике регистры таймера трогать не надо.

Таймер заработал в режиме compare, при достижении значения в OCR, вызывается ISR.

 

Но вот такой момент: в процедуре обработки прерывания таймер продолжает тикать. И если значение в счетчике достигает OCR, то ISR уже не вызывется :)

 

IMHO это неправильно и таймер нужно остановить после вызова ISR?

 

Вот нынешний код:

 

SIGNAL(SIG_OUTPUT_COMPARE1A)

{

//...

}

 

void Timer1_Init(void)

{

union {

unsigned char B[2];

unsigned int I;

} Temp;

 

Temp.I = 0;

 

TCCR1A = 0;

TCCR1B = (1 << CS11) | (1 << WGM12);

 

Temp.I = PERIOD;

 

TCNT1L = 0;

TCNT1H = 0;

 

OCR1AH = Temp.B[1];

OCR1AL = Temp.B[0];

 

TIMSK |= (1 << OCIE1A);

}

 

int main(void)

{

Timer1_Init();

sei();

 

while (1)

;

 

return 1;

}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Но вот такой момент: в процедуре обработки прерывания таймер продолжает тикать. И если значение в счетчике достигает OCR, то ISR уже не вызывется :)

Так и должно быть! Таймер должен тикать иначе прерывание будет вызваться реже чем 50 мс! А вот если значение в счетчике достигнет OCR то после выхода из прерывания (разрешения прерываний) прерывание вызовется сново, т.к флаг очищается при входе в обработчик. Но в любом случае обработчик прерывания который работает больше 50 мс это не правильно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Так и должно быть! Таймер должен тикать иначе прерывание будет вызваться реже чем 50 мс! А вот если значение в счетчике достигнет OCR то после выхода из прерывания (разрешения прерываний) прерывание вызовется сново, т.к флаг очищается при входе в обработчик. Но в любом случае обработчик прерывания который работает больше 50 мс это не правильно.

Приветствую.

 

А если в обработчике стоит цикл while (...) {...} и нет гарантии что он будет работать меньше 50 мс, как быть? Отказываться от while и искать другую концепцию?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Так и должно быть! Таймер должен тикать иначе прерывание будет вызваться реже чем 50 мс! А вот если значение в счетчике достигнет OCR то после выхода из прерывания (разрешения прерываний) прерывание вызовется сново, т.к флаг очищается при входе в обработчик. Но в любом случае обработчик прерывания который работает больше 50 мс это не правильно.

Приветствую.

 

А если в обработчике стоит цикл while (...) {...} и нет гарантии что он будет работать меньше 50 мс, как быть? Отказываться от while и искать другую концепцию?

А как можно делать действие каждые 50 мс если длительность самого дейстивя больше 50 мс? они же будут перекрываться.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...