|
вызвать прерывание каждые 50мсек |
|
|
|
Nov 19 2004, 07:54
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Приветствую. Продолжаю разборки с 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, не могу (симулятор не позволяет). Вообщем, я окончательно запутался
|
|
|
|
|
Nov 19 2004, 08:25
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Цитата(Alex2172 @ Nov 19 2004, 11:13 AM) А осциллографа то нет под рукой? (Ну или хотябы светодиода) осциллографа нет, в на глаз IMHO сложно заметить мерцание светодиода каждые 50мсек
|
|
|
|
|
Nov 19 2004, 08:59
|
Частый гость
 
Группа: Свой
Сообщений: 133
Регистрация: 16-08-04
Пользователь №: 504

|
Попробуй подели генерируемую частоту (~50мс) на 20 и подай этот сигнал на светодиод. Будет мигать с частотой 1 Гц (1с), а это уже можно на глаз определить - сам так делал. А если нодо определить болле точно, то без осцила не обойтись! Или замени осцил например компом: подавай свой сигнал на LPT-порт и смотри с помощью ентой проги твой сигнал
Прикрепленные файлы
dlpt.zip ( 431.38 килобайт )
Кол-во скачиваний: 92
|
|
|
|
|
Nov 19 2004, 12:07
|

Частый гость
 
Группа: Свой
Сообщений: 135
Регистрация: 22-06-04
Из: Челябинск
Пользователь №: 88

|
Блин нечаяно нажалось, первое не смотрите.... Вот это код будет все время генерить период в сколько надо, задается 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); }
|
|
|
|
|
Nov 19 2004, 12:10
|

Частый гость
 
Группа: Свой
Сообщений: 135
Регистрация: 22-06-04
Из: Челябинск
Пользователь №: 88

|
Еще поправим маленько. Вечер глучу малость Код #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
|
|
|
|
|
Nov 22 2004, 00:08
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Цитата(KRS @ Nov 19 2004, 02:15 PM) А зачем при инициализации таймера в TCNT писать 0xFE7A? Это конечно понятно что это -0x186 но зачем это делать?
И еще при инициализации корректнее с начала выставлять OCR и TCNT и самым последним писать TCCR1B иначе можно получить ложное прерываение Приветствую. Так вот с этим пунктом у меня и была изначально непонятность... Я считал, что в TCNT _обязательно_ нужно заносить инициализационное значение для отсчета тиков (c output compare либо без него). А потом - либо по переполнению, либо при match значения в OCR регистре. Так получается, что если работаем в compare режиме, то TCNT уже обнулен и начинает считать сам по себе, без моего вмешательства? И мне достаточно только проинициализировать OCR ?
|
|
|
|
|
Nov 22 2004, 02:14
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Цитата(lamerok @ Nov 19 2004, 03:07 PM) Блин нечаяно нажалось, первое не смотрите.... Вот это код будет все время генерить период в сколько надо, задается PERIODом Приветствую. Спасибо за пример. Есть вопросы по нему: 1) почему используется union а не structure. Это связано с особенностями генерации кода или только в целях экономии памяти? 2) зачем устанавливать принудительно OCF1B, как я понял этот флаг выставится сам при возникновении события совпадения ? И опять же при отладен в студии, этот флаг не выставляется (баг студии?)... 3) почему используется B-compare канал, а не A ? 4) при прогоне и отладке этого кода в AVR studio я не наблюдаю увеличения счетчика в TCNT...
|
|
|
|
|
Nov 22 2004, 10:27
|

Профессионал
    
Группа: Модераторы
Сообщений: 1 951
Регистрация: 27-08-04
Из: Санкт-Петербург
Пользователь №: 555

|
Цитата(romez777 @ Nov 22 2004, 03:08 AM) Так получается, что если работаем в 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)! в обработчике регистры таймера трогать не надо.
|
|
|
|
|
Nov 22 2004, 10:48
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Цитата(KRS @ Nov 22 2004, 01:27 PM) Изначально 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; }
|
|
|
|
|
Nov 22 2004, 11:52
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Цитата(KRS @ Nov 22 2004, 02:19 PM) Так и должно быть! Таймер должен тикать иначе прерывание будет вызваться реже чем 50 мс! А вот если значение в счетчике достигнет OCR то после выхода из прерывания (разрешения прерываний) прерывание вызовется сново, т.к флаг очищается при входе в обработчик. Но в любом случае обработчик прерывания который работает больше 50 мс это не правильно. Приветствую. А если в обработчике стоит цикл while (...) {...} и нет гарантии что он будет работать меньше 50 мс, как быть? Отказываться от while и искать другую концепцию?
|
|
|
|
|
Nov 22 2004, 12:04
|

Профессионал
    
Группа: Модераторы
Сообщений: 1 951
Регистрация: 27-08-04
Из: Санкт-Петербург
Пользователь №: 555

|
Цитата(romez777 @ Nov 22 2004, 02:52 PM) Цитата(KRS @ Nov 22 2004, 02:19 PM) Так и должно быть! Таймер должен тикать иначе прерывание будет вызваться реже чем 50 мс! А вот если значение в счетчике достигнет OCR то после выхода из прерывания (разрешения прерываний) прерывание вызовется сново, т.к флаг очищается при входе в обработчик. Но в любом случае обработчик прерывания который работает больше 50 мс это не правильно. Приветствую. А если в обработчике стоит цикл while (...) {...} и нет гарантии что он будет работать меньше 50 мс, как быть? Отказываться от while и искать другую концепцию? А как можно делать действие каждые 50 мс если длительность самого дейстивя больше 50 мс? они же будут перекрываться.
|
|
|
|
|
Nov 22 2004, 14:37
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Цитата(KRS @ Nov 22 2004, 03:04 PM) А как можно делать действие каждые 50 мс если длительность самого дейстивя больше 50 мс? они же будут перекрываться. Ну тут я палку-то наверное перегнул  В моем случае, необходимо каждые 50мс по SPI шине в режиме слейва отправлять данные. По логике такое действие не должно занимать больше 50мс. А может быть есть какие-то стандартные (классические) ходы, чтобы предусмотреть такие ситуации?
|
|
|
|
|
Nov 23 2004, 11:42
|

Участник

Группа: Новичок
Сообщений: 30
Регистрация: 7-07-04
Из: Novosibirsk
Пользователь №: 281

|
Цитата(lamerok @ Nov 19 2004, 03:10 PM) Еще поправим маленько. Вечер глучу малость Код #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 2 lamerokЭтот код без сомнения работает, но... пара вопросов: 1. Нафига объединение? Так ведь проще: OCR1B = TCNT1 + PERIOD; Ведь, как видно из кода инициализации, таймер работает в нормальном режиме + прерывание по совпадению. Я использую этот режим, хотя Atmel и не рекомендует: Using the Output Compare to generate waveforms in Normal mode is not recommended, since this will occupy too much of the CPU time.Хм... Туплю видимо 2. Зачем флаг прерывания вручную сбрасывается?
|
|
|
|
|
Nov 23 2004, 12:18
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Цитата(Fish @ Nov 23 2004, 02:42 PM) 2 lamerokЭтот код без сомнения работает, но... пара вопросов: 1. Нафига объединение? Так ведь проще: OCR1B = TCNT1 + PERIOD; Хм... Туплю видимо 2. Зачем флаг прерывания вручную сбрасывается? Да, я на эти моменты тоже обратил внимание, но автор оставил мои вопросы без ответов  наверное в качестве домашнего задания  Продолжаем думать и изучать даташиты...
|
|
|
|
|
Nov 24 2004, 04:33
|

Частый гость
 
Группа: Свой
Сообщений: 135
Регистрация: 22-06-04
Из: Челябинск
Пользователь №: 88

|
Fish1. Это я взял, из программы, где необходимо было проверять младший байт счетчика сравнения, оставил без изменения, просто для примера. Кроме того, если быть совсем правильным, то самое лучшее OCR1B = OCR1B + PERIOD; Так как к таймеру прибавлять нельзя, из-за того, что его регистры 8 разрядные, пока вы читаете один регистр таймера, это отнимет время, может изменится другой, или вообще произойти переполнение таймера. Именно, чтобы показать, что запись OCR1B = OCR1B + PERIOD; Реально в проце происходит примерно так IN R16,OCR1BL IN R17,OCR1BH SUBI R16,0xXX SBCI R16,0xYY OUT OCR1BH,R17 OUT OCR1BL,R16 Я так и написал в Сях, чтобы показать что не все так просто Если мы сделаем OCR1B = TCNT1 + PERIOD; то IN R16,TCNT1L //Вот тут пока мы его читаем IN R17,TCNT1H //Значение таймера уже может поменятся, вплоть до того, что произойдет переполнение и TCNT1H станет 0 вместо скажем 0xFF SUBI R16,0xXX SBCI R16,0xYY OUT OCR1BH,R17 OUT OCR1BL,R16 2. Флаг тут скидывается вручную, так как изначально этот код был не в обработчике прерывания, Да и так для надежности  ) Си компилятор вон два раза подряд джампы вставляет без включенной оптимизации. Считаем, что у меня оптимизация была выключена
|
|
|
|
|
Nov 24 2004, 05:33
|

Участник

Группа: Новичок
Сообщений: 30
Регистрация: 7-07-04
Из: Novosibirsk
Пользователь №: 281

|
Согласен, написал глупость OCR1B = TCNT1 + PERIOD; У самого в программе работает OCR1B += PERIOD; OCR1B = TCNT1 + PERIOD; - используется при инициализации По-хорошему бы после модификации OCR сравнить его с TCNT на предмет того, что TCNT обогнал OCR, но...
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|