реклама на сайте
подробности

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> вызвать прерывание каждые 50мсек
romez777
сообщение Nov 19 2004, 07:54
Сообщение #1


Местный
***

Группа: Свой
Сообщений: 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, не могу (симулятор не позволяет).

Вообщем, я окончательно запутался smile.gif
Go to the top of the page
 
+Quote Post
Alex2172
сообщение Nov 19 2004, 08:13
Сообщение #2


Местный
***

Группа: Свой
Сообщений: 242
Регистрация: 25-08-04
Пользователь №: 537



А осциллографа то нет под рукой? (Ну или хотябы светодиода)
Go to the top of the page
 
+Quote Post
romez777
сообщение Nov 19 2004, 08:25
Сообщение #3


Местный
***

Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077



Цитата(Alex2172 @ Nov 19 2004, 11:13 AM)
А осциллографа то нет под рукой? (Ну или хотябы светодиода)

осциллографа нет, в на глаз IMHO сложно заметить мерцание светодиода каждые 50мсек smile.gif
Go to the top of the page
 
+Quote Post
Styv
сообщение Nov 19 2004, 08:59
Сообщение #4


Частый гость
**

Группа: Свой
Сообщений: 133
Регистрация: 16-08-04
Пользователь №: 504



Попробуй подели генерируемую частоту (~50мс) на 20 и подай этот сигнал на светодиод. Будет мигать с частотой 1 Гц (1с), а это уже можно на глаз определить - сам так делал.
А если нодо определить болле точно, то без осцила не обойтись!
Или замени осцил например компом: подавай свой сигнал на LPT-порт и смотри с помощью ентой проги твой сигнал
Прикрепленные файлы
Прикрепленный файл  dlpt.zip ( 431.38 килобайт ) Кол-во скачиваний: 92
 
Go to the top of the page
 
+Quote Post
KRS
сообщение Nov 19 2004, 11:15
Сообщение #5


Профессионал
*****

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



А зачем при инициализации таймера в TCNT писать 0xFE7A?
Это конечно понятно что это -0x186 но зачем это делать?

И еще при инициализации корректнее с начала выставлять OCR и TCNT и самым последним писать TCCR1B иначе можно получить ложное прерываение
Go to the top of the page
 
+Quote Post
lamerok
сообщение Nov 19 2004, 11:55
Сообщение #6


Частый гость
**

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



Код
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 символов*/            
 
}
Go to the top of the page
 
+Quote Post
lamerok
сообщение Nov 19 2004, 12:07
Сообщение #7


Частый гость
**

Группа: Свой
Сообщений: 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);
           

}
Go to the top of the page
 
+Quote Post
lamerok
сообщение Nov 19 2004, 12:10
Сообщение #8


Частый гость
**

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



Еще поправим маленько. Вечер глучу малость blink.gif
Код
#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);

}

Вот теперь точно все правильно smile.gif :P
Go to the top of the page
 
+Quote Post
romez777
сообщение Nov 22 2004, 00:08
Сообщение #9


Местный
***

Группа: Свой
Сообщений: 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 ?
Go to the top of the page
 
+Quote Post
romez777
сообщение Nov 22 2004, 02:14
Сообщение #10


Местный
***

Группа: Свой
Сообщений: 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...
Go to the top of the page
 
+Quote Post
KRS
сообщение Nov 22 2004, 10:27
Сообщение #11


Профессионал
*****

Группа: Модераторы
Сообщений: 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)! в обработчике регистры таймера трогать не надо.
Go to the top of the page
 
+Quote Post
romez777
сообщение Nov 22 2004, 10:48
Сообщение #12


Местный
***

Группа: Свой
Сообщений: 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 уже не вызывется smile.gif

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;
}
Go to the top of the page
 
+Quote Post
KRS
сообщение Nov 22 2004, 11:19
Сообщение #13


Профессионал
*****

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



Цитата(romez777 @ Nov 22 2004, 01:48 PM)
Но вот такой момент: в процедуре обработки прерывания таймер продолжает тикать. И если значение в счетчике достигает OCR, то ISR уже не вызывется smile.gif

Так и должно быть! Таймер должен тикать иначе прерывание будет вызваться реже чем 50 мс! А вот если значение в счетчике достигнет OCR то после выхода из прерывания (разрешения прерываний) прерывание вызовется сново, т.к флаг очищается при входе в обработчик. Но в любом случае обработчик прерывания который работает больше 50 мс это не правильно.
Go to the top of the page
 
+Quote Post
romez777
сообщение Nov 22 2004, 11:52
Сообщение #14


Местный
***

Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077



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

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

А если в обработчике стоит цикл while (...) {...} и нет гарантии что он будет работать меньше 50 мс, как быть? Отказываться от while и искать другую концепцию?
Go to the top of the page
 
+Quote Post
KRS
сообщение Nov 22 2004, 12:04
Сообщение #15


Профессионал
*****

Группа: Модераторы
Сообщений: 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 мс? они же будут перекрываться.
Go to the top of the page
 
+Quote Post
romez777
сообщение Nov 22 2004, 14:37
Сообщение #16


Местный
***

Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077



Цитата(KRS @ Nov 22 2004, 03:04 PM)
А как можно делать действие каждые 50 мс если длительность самого дейстивя больше 50 мс? они же будут перекрываться.

Ну тут я палку-то наверное перегнул smile.gif
В моем случае, необходимо каждые 50мс по SPI шине в режиме слейва отправлять данные. По логике такое действие не должно занимать больше 50мс. А может быть есть какие-то стандартные (классические) ходы, чтобы предусмотреть такие ситуации?
Go to the top of the page
 
+Quote Post
lamerok
сообщение Nov 22 2004, 15:17
Сообщение #17


Частый гость
**

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



romez777
1. В режиме слейва данные передаются только по запросу!!!!
Куды вы их будете выплевывать????? Если запроса нет??? И главное как, если вас мастер не тактирует???Кроме того, они атоматом сами выплевываются из регистра передачи при запросе. Нужно только его вовремя заполнять (регистр передачи)!

2. Еще раз поясните для какой цели вам задержка на 50 мс???

Если вы в режиме мастера и вам каждые 50 мс надо че-то выкидывать, то в обрабочике По сравнению можете просто вставить вывод первого байта буфера запроса, остальное автоматом можно выплюнуть по прерыванию по передаче.

Я че-то уже ничего не понимаю.. объясните задачу в целом!!!
Go to the top of the page
 
+Quote Post
IgorKossak
сообщение Nov 22 2004, 15:34
Сообщение #18


Шаман
******

Группа: Модераторы
Сообщений: 3 064
Регистрация: 30-06-04
Из: Киев, Украина
Пользователь №: 221



Цитата(KRS @ Nov 22 2004, 02:19 PM)
Но в любом случае обработчик прерывания который работает больше 50 мс это не правильно.

Вот именно.
Это ж какой работой надо процессору заниматься, чтобы сидеть в прерывании аж 50 мс!!! sad.gif
Go to the top of the page
 
+Quote Post
Fish
сообщение Nov 23 2004, 11:42
Сообщение #19


Участник
*

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



Цитата(lamerok @ Nov 19 2004, 03:10 PM)
Еще поправим маленько. Вечер глучу малость  blink.gif
Код
#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);

}

Вот теперь точно все правильно smile.gif  :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.
Хм... Туплю видимо unsure.gif
2. Зачем флаг прерывания вручную сбрасывается?
Go to the top of the page
 
+Quote Post
romez777
сообщение Nov 23 2004, 12:18
Сообщение #20


Местный
***

Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077



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

Да, я на эти моменты тоже обратил внимание, но автор оставил мои вопросы без ответов smile.gif наверное в качестве домашнего задания smile.gif

Продолжаем думать и изучать даташиты...
Go to the top of the page
 
+Quote Post
lamerok
сообщение Nov 24 2004, 04:33
Сообщение #21


Частый гость
**

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



Fish
1. Это я взял, из программы, где необходимо было проверять младший байт счетчика сравнения, оставил без изменения, просто для примера.
Кроме того, если быть совсем правильным, то самое лучшее
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. Флаг тут скидывается вручную, так как изначально этот код был не в обработчике прерывания, Да и так для надежности smile.gif) Си компилятор вон два раза подряд джампы вставляет без включенной оптимизации. Считаем, что у меня оптимизация была выключена smile.gif
Go to the top of the page
 
+Quote Post
lamerok
сообщение Nov 24 2004, 05:20
Сообщение #22


Частый гость
**

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



Там везде должно быть

SBCI R17,0xYY вместо SBCI R16,0xYY

Куда блин, все время тороплюсь????
Go to the top of the page
 
+Quote Post
Fish
сообщение Nov 24 2004, 05:33
Сообщение #23


Участник
*

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



Согласен, написал глупость
OCR1B = TCNT1 + PERIOD;
У самого в программе работает
OCR1B += PERIOD;
OCR1B = TCNT1 + PERIOD; - используется при инициализации
blush.gif
По-хорошему бы после модификации OCR сравнить его с TCNT на предмет того, что TCNT обогнал OCR, но... wink.gif
Go to the top of the page
 
+Quote Post

2 страниц V   1 2 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 21st July 2025 - 04:20
Рейтинг@Mail.ru


Страница сгенерированна за 0.01556 секунд с 7
ELECTRONIX ©2004-2016