Прошу помощи - прерывания по таймеру-2
- Войдите на сайт для отправки комментариев
Прошу помочь. Составил программу для обработки прерываний по таймеру и по ней есть вопросы:
1) Есть ли готовая функция типа attachInterrupt() , которая запишет адрес программы обработчика ISR_A в таблицу прерываний?
2) Если готовой функции нет, помогите синтаксически грамотно записать: "в ячейку, адрес который содержит PTR_T2OVER записать адрес функции обработчика прерывания ISR_A" ?
3) Начальное значение указателю PTR_T2OVER присвоить не удалось, компилятор ничего не знает про "_VECTOR(9)" , а файла <iom328p.h> у меня нет. Что делать?
4) Правильно ли расчитал значение tcnt2=6250 для 40 прер/сек при тактовой 16 Мгц?
5) Не могу найти в какой строке кода инициализирован прескалер 64 для формулы 16000000/64/f=tcnt2
или такое значение при включении питания по умолчанию?
6) Нужно при манипуляциях с регистрами (прогр. setup ) запрещать все прерывания или достаточно запрета обработки только прерываний таймера2 ?
#include <iom328p.h> volatile unsigned int tcnt2; volatile bool flag1=false ; volatile unsigned long cnt=0; uint32_t * PTR_T2OVER=TIMER2_OVF_vect; // нашел в описанияХ что якобы в файле <iom328p.h> есть определение: // #define TIMER2_OVF_vect _VECTOR(9) /* Timer/Counter2 Overflow*/ void setup() { pinMode(7, OUTPUT); digitalWrite(7,LOW); noInterrupts(); // запретить все прерывания TIMSK2 &= ~(1<<TOIE2); //запрещение прерывания по переполнению таймера/счетчика Т2 TCCR2A &= ~((1<<WGM21) | (1<<WGM20));// Режим работы таймера/счетчика TCCR2B &= ~(1<<WGM22);// Режим работы таймера/счетчика ASSR &= ~(1<<AS2); //Выбор источника синхронизации таймера если AS2=0 от системного генератора tcnt2 = 6250; // 16000000/64/f=tcnt2 // значение 6250 рассчитано для 40 прерываний в секунду для системного генератора 16 Мгц // -------------------------------------------------- в какой строке кода установлен прескалер=64 ??????????????? TIMSK2 |= (1<<TOIE2);//Разрешение прерывания по переполнению Т2. // как по адресу, на который указывает PTR_T2OVER записать адрес функции ISR_A ? interrupts(); // раззрешить все прерывания } ///////////////////////////////////////////////////////////////////////////////////////////////// void loop() { if(flag1) digitalWrite(7, HIGH); else digitalWrite(7, LOW); } ///////////////////////////////////////////////////////////////////////////////////////////////// void ISR_A(void) { noInterrupts(); // запретить все прерывания cnt++; if (cnt >= 10000000) {cnt=0; flag1=!flag1;} else; TCNT2 = tcnt2; interrupts(); // разрешить все прерывания }
начальный код для примера взял здесь http://arduino.ru/forum/programmirovanie/preryvaniya-po-taimeru немного изменил и дополнил его, м.б. добавил ошибок ?
PS:
c cnt >= 1млн в строке 37 перемудрил, правильно ( cnt >= 4 )
Алек-сей, не обижайтесь, но 1..3 -это сущий бред. Обьясните подробно что вы хотите. 4 -не правильно, значение может быть не более 0xFF. 5 - ни в какой. А должен быть в регистре TCCR2B , и уберите из кода все обнуления отдельных битов по типу: TCCR2B &= ~(1<<WGM22); и не используйте их без полного понимания того, что вы делаете.);<>
Хочу научиться работать с прерыванием таймера и для этого понять какие действия произвести. Полностью прочел даташит на Atmega 328, но не все смог логически увязать тк не все жаргоны смог перевести.
Сейчас имеются общие представления о шагах решения задачи:
1) рассчитать с использованием прескалера и частоты значение таймера, скорее всего придется взять максимальное FF и нужное время отмерять на каком-то количестве прерываний при помощи дополнительного счетчика cnt, а может быть увеличить прескалер . Какой конкретно установить прескалер ?
2) написать программу обработки прерывания - уже написал ISR_A()
3) запретив все прерывания, установить регистры таймера2 в нужные значения,
4) записать в таблицу прерываний для таймера 2 (по условию переполнения или сравнения с нулем ?) адрес программы ISR_A
Алек-сей , начинать изучение таймеров по даташиту -слишко жёстко. Почитайте русскоязычные материалы, Евстифеева например "Микроконтроллеры AVR семейства Mega". Там тоже не разжевывают как первоклашкам, но хотя бы это нормальный перевод на русский. В принципе когда вы прочтёте вы сами ответите на свои вопросы. Но подскажу (1) что бы настроить таймер2 на прерывания n раз в секунду нужно тактовую частоту разделить на n и на нужный прескалер. К примеру нужно нам 100 раз в секунду. Делим 16000000 / 100 =160000 -> Много, нужно использовать прескалер. Бёрём 1024. 160000/1024=156. Отлично, попали в допустимый диапазон. Только у регистров счёт с ноля, стало быть рассчётное значение будет 155.
(2) у прерываний таймеров есть свои номера, жёстко определённые в даташите. Есть дефайны на эти номера в компиляторе для более наглядного восприятия. И выдумывать другие названия нет особого смысла. У второго таймера есть три прерывания. По переполению счётного регистра и по совпадению с регистрами сравнения а и б.
(3) запрещать прерывания без острой на то необходимости -нет смысла. Для корректного конфигурирования таймера удобнее его стачала остановить, настроить все регистры, а последней командой запустить. Скетч для примера.
Строки 3-6 в принципе не нужны, могут понадобится только в случае когда нельзя допустить бесконтрольного срабатывания прерываний при запуске программы.
dimax, cпасибо за ответ и пример, хочу задать несколько вопросов:
Если я правильно понял, для разных МК биты флагов могут стоять на разных местах. Обнаружил несоответствие номера бита WGM21 в рекомендованной книге (WGM21 указан как бит 3 для MCU 32Х ) тому, что реально выдает программа исполненная на Atmega328P . Еще исполнил программу (те посмотрел значение WGM21 ) на Atmega2560, он тоже =1, но 2560 не описан в этой книге.
вопрос 1: В каком файле описаны #define для WGM21 , _VECTOR7(), PORTB и других?
вопрос 2: при побитовом умножении на маску 0xFF я ожидал неизменного результата в TIFR2 послде присваивания TIFR2=TIFR2&0xFF , но результат другой. Почему?
вопрос 3: помогите понять запись, что за оператор "?" это сокращенная форма "if" и где прочесть про нее ?
n++; n=n& (1<<7); PORTB=PORTB | (1<<5); if (PORTB) PORTB=PORTB & ~ (1<<5);
n++& (1<<7)) ? PORTB|=1<<5 : PORTB&=~(1<<5);
вопрос 4: где прочитать про оператор ISR и что это - если #define, то в каком файле искать его?
вопрос 5: возможно он отпадет, когда получу ответ на в.4 - в какой момент в программе происходит запись нового ветора обработчика программы прерывания _VECTOR(7)?
dimax, кстати, пример программы из Вашего поста #4 обработки прерываний по таймеру2 на МК 2560 не управляет LED на ножке 13. Причину не знаю, может быть несовместимость регистров Atmega328 и 2560 ?
LED и порт вывода проверены:
PS: как в сообщении форума спрятать текст программы в спойлер, чтобы укоротить пост ?
Форма записи тернарной операции в C++
"условие" ? "выражение 1" : "выражение 2";
Если условие истинно, то выполняется выражение 1, иначе (условие ложно) выполняется выражение 2.
min = a<b ? a : b;
переменной min присваивается значение меньшее из a и b
вопрос 1: В каком файле описаны #define для WGM21 , _VECTOR7(), PORTB и других?
В девайнишенс -фалах, которые ставятся в комплекте с компилятором. Например iom328p.h для меги328
вопрос 2: при побитовом умножении на маску 0xFF я ожидал неизменного результата в TIFR2 послде присваивания TIFR2=TIFR2&0xFF , но результат другой. Почему?
Не все регистры одинаковы в процедурах чтения/записи в них. Почитайте повнимательнее про регистр TIFR в даташите, он один из таких "особых".
вопрос 4: где прочитать про оператор ISR и что это - если #define, то в каком файле искать его?
На вопрос 3 уже ответили. ISR -это макрос компилятора avr-gcc , описан в interrupt.h Не знаю где почитать. Гугль? Я сам так делеко не заходил, что б разбирать макросы компилятора )
dimax, кстати, пример программы из Вашего поста #4 обработки прерываний по таймеру2 на МК 2560 не управляет LED на ножке 13. Причину не знаю, может быть несовместимость регистров Atmega328 и 2560 ?
Естессно, в ардуино меге он сидит на PB7
Вот это полная документация на avr-libc.
Вот, например, про прерывания.
И да, все, конечно, не по русски ;).
В TIFR2 некоторые биты защищены от записи , возможно только чтение. Побитное умножение 8-битного регистра на 0xFF не должно было изменить его значение (побитное умножение любого 8 битного числа Z на 0xFF дает результатом Z), но если посмотреть печать значений регистра по ходу программы, то его поведение непонятно.
Посмотрите, я добавил в программу присвоение значений TIFR2, но его содержимое не меняется .
Алек-сей, Это хорошо, что вы так скурпулёзно всё разбираете, но плохо что при этом всё равно не понимаете :) В даташите крайне скупо сказано об особенностях этого регистра. Если вы внимательно читали, то знаете что установленная единица в нём обнуляется записью единицы. А вот записать единицу, если в регистре ноль -нельзя. В этом его особенности. Обнулять так же можно красивой командой TIFR2=TIFR2. :)
dimax, cпасибо , Вы правы - регистр оказался "хитрым". Что ни запишешь в биты разрешенные для чтения, приводит к их обнулению. Вы правы - я не смог понять смысл из английского описания этого регистра. В даташите написано о трегистре слишком кратко.
Решил протестировать стабильность частоты, исполнения прерываний, взял любую с целым количеством отсчетов таймера (50 кГц) . В теле прерывания счетчик циклов, а в основной программе через каждые 500 000 циклов, те 10 сек измерение времени.
Почему прерывания исполняются чаще, чем было назначено? Ладно бы какая-то программа занимала время своими прерываниями и в ISR были бы пропуски, но тогда время на фиксированное количество циклов увеличилось бы, но как понять 9175 мсек при ожидаемых 10000 с периодичностью повтора прибл. 320 сек ? (см лог в нижних строках программы, строки 288, 124, 92, 65 и др.).
Алек-сей, очень хороший вопрос. Вы ушли по ложному следу. Переменная cnt по сути такой же миллис, и проверять точность одного счётчика по другому в принципе бесполезное занятие, т.к. у них одинаковая точность. А дело тут в другом. Пока ваша программа начинает вычислять if (cnt>=500000) эта самая cnt не стоит, а считает дальше. Контроллер как мы знаем 8-битный, поэтому число сравнивается в 4 захода. Если у cnt текущее число например 0x6FFFF, а сравниваем мы его с 7A120 (в десятичном 500000) и сначала сравнились младшие байты. Младшие байты больше чем надо, -ок. Потом счётчик счёлкнул в 0x70000 (458752 в десятичном счёте) и тут сравнились старшие байты, -опять ок. Но для нашей операции сравнения число было не как по факту - 0x70000, а 0x7FFFF поэтому имеем ту ошибку, которую вы наблюдали. Выход простой -либо сравнивать в том-же прерывании, либо брать в работу число, скопированное в другую переменную при запрещённых прерываниях.
Как измерить текущее время в мксек в начале прерывания, если нельзя использовать micros()?
Каким методом обеспечить тестовую задержку входа в прерывание таймера2 ? Пока думаю организовать синхронное конкурирующее прерывание, срабатывающее чуть раньше имеющегося "подопытного" прерывания и периодически меняющее длительность своего исполнения так, чтобы отодвигать начало исполнения "подопытного". Для синхронности логично сделать оба прерывания на одном и том же таймере, но по разным условиям - это допустимо ?
хотел посмотреть всегда ли прерывания выполняются строго по таймеру (интервалы между прерываниями одинаковые) и могут ли быть задержки из-за работы других прерываний? могут ли быть прпопущенные прерывания. Вроде бы для прерываний при невозможности выполнить устанавливается флаг и оно будет исполнено позже ?
Если мыслить тактами МК, то любое прерывание выполняется позже. В нашем случае таймер выставляет флаг OCF2A, а дальше как получится. Пропустится прерывание может только если пришло время второго, а первое всё еще не выполнилось. Если не злоупотреблять командами noInterrupts и не делать слишком частыми сами прерывания - то такое в общем случае маловероятно.
Как измерить текущее время в мксек в начале прерывания, если нельзя использовать micros()?