Вопрос по прерываниям
- Войдите на сайт для отправки комментариев
Сб, 06/02/2016 - 21:33
Подаю сигнал на INT0. Настраиваю прерывание на фронт импульса - все работает.
EIMSK = (1<<INT0); EICRA=(1<<ISC01);
Подаю сигнал на INT1. Настраиваю прерывание на фронт импульса - все работает.
EIMSK = (1<<INT1); EICRA=(1<<ISC11);
Вопрос. Как настроить прерывание INT0 по фронту, а INT1 по спаду импульса. При этом INT0 и INT1 соединены между собой. Что прописать в регистр EICRA?
EIMSK = (1<<INT0) | (1<<INT1);// разрешение прерываний INT0 и INT1
Опять вы что-то мудрите :) Один бит ICS01 -это спад импульса. Что бы был подъём нужны оба бита. Вот всё вместе:
EICRA=(1<<ICS01)|(1<<ICS00)|(1<<ICS11);
EIMSK=(1<<INT1)|(1<<INT0);
Спасибо.
Необходимо измерить скважность прямоугольных импульсов. Входную частоту подаем на входы INT0 и INT1. INT0 настроен на срабатывание по фронту импульса. INT1 настроен на срабатывание по спаду импульса. Таймер 1 считает количество тактовых импульсов генератора до значения 4000, а затем сбрасывается и считается количество переполнений. По фронту импульса мы считаем значение переменной : t_start. По спаду импульса мы считаем значение переменной : t_stop. В переменной : t_imp - мы получаем значение суммарного времени длительности импульса. Зная t_imp и длительность импульса + длительность паузы мы можем найти скважность.
Переодически длительность импульса t_imp. становится на значение кратное 4000 больше. Вероятно это связано с тем, что приоритет внешних прерываний выше, чем приорител таймера 1. Допустим сработало INT1 , а после этого прерывание по таймеру. В прерывании INT1 мы считываем значение количества переполнений таймера 1, но поскольку переполнение таймера 1 наступило, а прерывание еще нет - мы теряем 1 импульс переполнения таймера 1.
Как можно обойти данную ситуацию?
//---Прерывание по int 0------------------------ ISR (INT0_vect){ if (start_for_timer_1==1 && flag_LED==0) { TCNT1=0;TCCR1B = (1 << CS10)|(1<<WGM12); t_start=0;t_imp=0; start_for_timer_1=0;ovf_tic_timer_1=0;ovf_tic_int_0=0; } if (stop_for_timer_1==1 && flag_LED==0) { TCCR1B=0; out_TCNT1=TCNT1; TCNT1=0; //сброс счётных регистров out_ovf_tic_int_0=ovf_tic_int_0;ovf_tic_int_0=0; out_ovf_tic_timer_1=ovf_tic_timer_1;ovf_tic_timer_1=0; flag_LED=1;stop_for_timer_1=0; } if (start_for_timer_1==0 && stop_for_timer_1==0 && flag_LED==0) { ovf_tic_int_0=ovf_tic_int_0+1; start_n2=TCNT1;start_n1=ovf_tic_timer_1; } } //---Прерывание по int 1----------------------- ISR (INT1_vect){ stop_n2=TCNT1;stop_n1=ovf_tic_timer_1; t_start=(unsigned long)start_n1*4000+start_n2; t_stop =(unsigned long)stop_n1 *4000+stop_n2; t_imp=t_imp+(t_stop-t_start); } //---Прерывание по таймеру 1------------------- ISR(TIMER1_COMPA_vect) { // timer compare interrupt service routine ovf_tic_timer_1=ovf_tic_timer_1+1; //считать количество переполнений таймера 1 if (ovf_tic_timer_1>=interval_izm && flag_LED==0)stop_for_timer_1=1; }Дальше вопрос по прерываниям.
Допустим я подаю внешний сигнал на INT0 как прерывание с самым высоким приоритетом. Если никакого прерывания в этот момент не исполняется то мы переходим сразу к выполнению INT0. Если выполняется другое прерывание, то INT0 начнет выполняться только после завершения текущего прерывания.
Как мне сделать так, чтобы прерывание по INT0 выполнялось только при отсутствии выполнения в данный момент другого прерывания? А если выполняется другое прерывание (например прерывание от таймера 0), то игнорировать прерывание INT0? Есть ли какой нибудь флаг для этих целей?
Вроде ж у AVR нет приоритета прерываний? (ошибся, приоритет есть у ожидающих в очереди прерываний). Если во время срабатывания прерывания уже обрабатывается другое прерывание - только выставляется флаг сработавшего прерывания и оно ждёт, пока не завершится первое. И уже потом обработается второе, после чего флаг сбросится. То есть, в порядке очереди.
UPD: А, понял. Вы хотите, чтобы прерывание INT0 вообще не обрабатывалось, даже после завершения другого прерывания. Тогда вариант: в конце обработчика уже выполняющегося прерывания проверять флаг прерывания INT0. И если он выставлен - сбрасывать его.
Вроде ж у AVR нет приоритета прерываний? (ошибся, приоритет есть у ожидающих в очереди прерываний). Если во время срабатывания прерывания уже обрабатывается другое прерывание - только выставляется флаг сработавшего прерывания и оно ждёт, пока не завершится первое. И уже потом обработается второе, после чего флаг сбросится. То есть, в порядке очереди.
UPD: А, понял. Вы хотите, чтобы прерывание INT0 вообще не обрабатывалось, даже после завершения другого прерывания. Тогда вариант: в конце обработчика уже выполняющегося прерывания проверять флаг прерывания INT0. И если он выставлен - сбрасывать его.
спасибо за ответ.
Да я имел ввиду именно очерередь в ожидании.
У меня немного другой случай. У меня INT0 вызывается приходом внешнего сигнала. (Если сейчас никакого другого прерывания не выполняется). Если другое прерывание уже выполняется - то прерывание по INT0 не должно обрабатываться. Доступа к текущему прерыванию у меня нет.(например:текущее прерывание по таймеру 0 который считает время. И остановить Таймер 0 мне не нужно.)
Мне надо в обработчик прерываний INT0 вставить проверку, что прерывание по таймеру 0 (в момент подачи сигнала на INT0) в данный момент не выполняется.
Или я чего-то не пойму, или лыжи не едут... У Вас же есть обработчик прерывания от таймера? Почему Вы не хотите в нём проверять и сбрасывать флаг INTF0?
Доступа к текущему прерыванию у меня нет.(например:текущее прерывание по таймеру 0 который считает время. И остановить Таймер 0 мне не нужно.)
В смысле нет доступа? Обработчик прерывания таймера есть, в котором выполняется некий код. В нём и делайте проверку в самом конце, ничего останавливать не нужно.
Вот такой код пока имею. У меня прерывание по INT0 иногда срабатывает на 10 тактов позднее или раньше чем надо. Есть подозрение что переход на прерывание INT0 задерживается из за выполнения какого нибудь другого прерывания.
//Пин 2 вход измерителя частоты. 16.07.2016 // //---------------------------------------------------------------------------------------------------------------------------------// #include <Wire.h> // #include <LiquidCrystal_I2C.h> // //---------------------------------------------------------------------------------------------------------------------------------// LiquidCrystal_I2C lcd(0x27, 16, 2); // Для экрана 16х2 (двухстрочный) // //---------------------------------------------------------------------------------------------------------------------------------// unsigned long interval_izm = 1000; //Время счета входной частоты в миллисекундах // unsigned long interval_LCD = 1000; //Интервал смены показаний на LCD в миллисекундах // unsigned long lastTime_LCD = 0; //Время последнего изменения состояния для LCD в миллисекундах // unsigned long DeltaTime_LCD = 0; //Время с момента последнего вывода на LCD в миллисекундах // unsigned long currentTime = 0; //Текущее время в милисекундах // //---------------------------------------------------------------------------------------------------------------------------------// unsigned int ovf_tik_int_0; // unsigned int ovf_tik_timer_1; // unsigned int interval_timer_1; // unsigned long tik_int_0,tik_timer_1; // boolean stop_for_timer_1,start_for_timer_1,flag_LED; // float f,t1; // //---------------------------------------------------------------------------------------------------------------------------------// void setup() { // Serial.begin(9600); // lcd.init(); //Задаем размерность LCD дисплея // lcd.backlight(); //Включаем подсветку экрана // lcd.clear(); //Очистка экрана // pinMode(2, INPUT); //Вывод 2 в режим ввода // digitalWrite(2, HIGH); //Включаем подтягивающий резистор // //---------------------------------------------------------------------------------------------------------------------------------// delay(1000); //Ждем 1 секунду // lcd.setCursor(0, 0); // lcd.print("Ver 16.08.2016"); // delay(1000); //Ждем 1 секунду // //---Настройка прерывания int 0----------------------------------------------------------------------------------------------------// EIMSK = (1<<INT0); // разрешение прерываний INT0 // EICRA = (1<<ISC01) | (1<<ISC00); //настройка INT0 по фронту // //---Настройка таймера 1-----------------------------------------------------------------------------------------------------------// TCCR1A = 0; // TCCR1B = 0; // TCNT1 = 0; // OCR1A=63999; //отсчитать 64 000 входных импульсов до прерывания // TCCR1B = (1 << CS10)|(1<<WGM12); //(CS10)Коэффициент предделителя 1 (WGM12)CTC mode // TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt // //---------------------------------------------------------------------------------------------------------------------------------// interval_timer_1=(unsigned int)interval_izm/4; // //---------------------------------------------------------------------------------------------------------------------------------// } // //---Прерывание по int 0-----------------------------------------------------------------------------------------------------------// ISR (INT0_vect){ // if (start_for_timer_1==1 && flag_LED==0) {TCNT1=0;TCCR1B =(1<<CS10)|(1<<WGM12);start_for_timer_1=0;ovf_tik_timer_1=0;ovf_tik_int_0=0;}// if ( stop_for_timer_1==1 && flag_LED==0){TCCR1B=0;stop_for_timer_1=0;flag_LED=1;} // if (start_for_timer_1==0 && stop_for_timer_1==0 && flag_LED==0){ovf_tik_int_0=ovf_tik_int_0+1;} // } // //---Прерывание по таймеру 1-------------------------------------------------------------------------------------------------------// ISR(TIMER1_COMPA_vect) { // timer compare interrupt service routine // ovf_tik_timer_1=ovf_tik_timer_1+1; //считать количество переполнений таймера 1 // if (ovf_tik_timer_1>=interval_timer_1 && flag_LED==0)stop_for_timer_1=1; // } // void loop() // { // //---Условия для вывода показаний на LCD-------------------------------------------------------------------------------------------// currentTime=millis(); // DeltaTime_LCD =abs(currentTime - lastTime_LCD); //Время с момента последнего вывода на LCD // if (DeltaTime_LCD >= interval_LCD && flag_LED==1) //Условия вывода на LCD // { // //---------------------------------------------------------------------------------------------------------------------------------// tik_timer_1=(unsigned long)ovf_tik_timer_1*64000+TCNT1; // tik_int_0 =(unsigned long)ovf_tik_int_0; // t1=(float)tik_timer_1/tik_int_0; // f=1000000/(t1*0.0625); // //---------------------------------------------------------------------------------------------------------------------------------// Serial.print("ovf_tic1=");Serial.print(tik_timer_1);Serial.print(" ,"); // Serial.print(" tik_int_0 =") ;Serial.print(tik_int_0) ;Serial.print(" n."); // Serial.print(" f =") ;Serial.print(f,5) ;Serial.println(" Hz"); // //---------------------------------------------------------------------------------------------------------------------------------// lcd.clear(); //Очистка экрана // lcd.setCursor(0, 0);lcd.print(tik_timer_1); // lcd.setCursor(9 ,0);lcd.print(tik_int_0); // lcd.setCursor(0 ,1);lcd.print(f,2);lcd.print(" Hz"); // //---------------------------------------------------------------------------------------------------------------------------------// lastTime_LCD = currentTime; // flag_LED=0; //Сигнал об окончании вывода на индикатор // start_for_timer_1=1; // } // //---------------------------------------------------------------------------------------------------------------------------------// } // //---------------------------------------------------------------------------------------------------------------------------------//Вот такой код пока имею. У меня прерывание по INT0 иногда срабатывает на 10 тактов позднее или раньше чем надо.
Ну позднее понятно - ждёт очереди, если выполняется другое прерывание (от таймера). А как оно может быть раньше?
Есть подозрение что переход на прерывание INT0 задерживается из за выполнения какого нибудь другого прерывания.
Ну у Вас только от таймера другое прерывание, больше ведь нет.
Вот в обработчик прерывания от таймера добавил проверку флага прерывания INT0. Должно быть как Вы и хотели: в самом конце обработчика таймера проверяется, не пришло ли прерывание по INT0. Если пришло - то флаг сбрасывается, соответственно после выхода из обработчика таймера - МК не должен будет перейти в обработчик INT0.
ISR(TIMER1_COMPA_vect) { // timer compare interrupt service routine ovf_tik_timer_1++; //считать количество переполнений таймера 1 if (ovf_tik_timer_1>=interval_timer_1 && flag_LED==0) { stop_for_timer_1=1; } if (EIFR & _BV(INTF0)) // проверяем, установлен ли флаг { EIFR = _BV(INTF0); // если установлен, то сбрасываем записью единички } }Serial тоже на прерываниях, насколько я знаю, и millis() скорее всего не без этого. И чего там в lcd. надо посмотреть.
Прерываний до хрена может быть. Мне кажется, подход надо другой, если задержки не избежать, то данные фильтровать надо. Мне лень код сейчас писать, но что-то типа медианного фильтра. Если автору известно, что прерывание запоздало на 10 тактов, то легко отбросить данные которые не вписываются в предсказаные.
Всем спасибо попробую , что получится.
По поводу того, что прерывание приходит раньше или позже. У меня фронт и спад количества импульсов приходит на INT0. Если прерывание попадает на фронт импульса - то длительность уменьшается на время выполнения выхода из прерывания (обычно 10 тактов). Если попадает на спад импульса - то длительность увеличивается.
По поводу фильтрации это отдельная тема. В том то и дело, что я не знаю насколько тактов произойдет запаздывание. Обычно это от 2 до 10 тактов каждое десятое измерение. Но бывает и запаздывание на 100 тактов примерно раз в минуту.
Предсказания не нужно, это не как у мп3. Я не правильно обрисовал, медианный фильтр работает по мажоритарной системе, те значения которых больше всего- правильные. Остальные можно отбросить. Логика простая, если значения все одинаковые, то выбросив 50% ничего не изменится, а вот если в тех 50% отбрасываемых будут сильно крывые данные, то они не смогут испортить последующее усреднение результата.
http://learn.linksprite.com/programming-languages/c/median-filtering-method-for-pcduinoarduino-2/
Предсказания не нужно, это не как у мп3. Я не правильно обрисовал, медианный фильтр работает по мажоритарной системе, те значения которых больше всего- правильные. Остальные можно отбросить. Логика простая, если значения все одинаковые, то выбросив 50% ничего не изменится, а вот если в тех 50% отбрасываемых будут сильно крывые данные, то они не смогут испортить последующее усреднение результата.
http://learn.linksprite.com/programming-languages/c/median-filtering-method-for-pcduinoarduino-2/
Этот вариант пока не прокатит. Допустим у меня сейчас есть частота 20.004123 гц. Поскольку я подаю ее с кварца. И для этого случая я могу применить фильтр. Теперь представим, что у меня не кварц а ГУН. и мне надо поймать секундное изменение частоты между 20.004123 и 20.004122. В данном случае ваш метод не подходит.
Поменял код.
Данные частоты которые были до исправления кода.
f =20.004123 Hz
f =20.004138 Hz
f =20.004123 Hz
f =20.004123 Hz
f =20.004127 Hz
f =20.004119 Hz
f =20.004123 Hz
f =20.004117 Hz
f =20.004119 Hz
f =20.004123 Hz
f =20.004123 Hz
f =20.004133 Hz
f =20.004123 Hz
f =20.004123 Hz
f =20.004123 Hz
f =20.004081 Hz
f =20.004024 Hz
f =20.004127 Hz
f =20.004121 Hz
f =20.004119 Hz
f =20.004121 Hz
f =20.004129 Hz
f =20.004123 Hz
f =20.004123 Hz
После добавления кода.
f =20.004123 Hz
f =20.004123 Hz
f =20.004123 Hz
f =20.004123 Hz
f =20.004123 Hz
f =20.004123 Hz
f =20.004123 Hz
f =20.004129 Hz
f =20.004133 Hz
f =20.004123 Hz
f =20.004123 Hz
f =20.004127 Hz
f =20.004123 Hz
f =20.004123 Hz
f =20.004123 Hz
f =20.004123 Hz
f =20.004123 Hz
f =20.004123 Hz
f =20.004123 Hz
f =20.004131 Hz
f =20.004123 Hz
f =20.004123 Hz
f =20.004123 Hz
f =20.004123 Hz
f =20.004123 Hz
f =20.004123 Hz
f =20.004137 Hz
f =20.004129 Hz
f =20.004123 Hz
f =20.004133 Hz
f =20.004123 Hz
f =20.004123 Hz
f =20.004123 Hz
f =20.004123 Hz
f =20.004114 Hz
f =20.004123 Hz
f =20.004129 Hz
Результат стал лучше но все равно есть выбросы.
Для точного измерения не надо было связываться с прерываниями вообще. Делается в хардваре, через инпут каптюре (ICP). Есть библиотека здесь. http://interface.khm.de/index.php/lab/interfaces-advanced/frequency-measurement-library/
Для точного измерения не надо было связываться с прерываниями вообще. Делается в хардваре, через инпут каптюре (ICP). Есть библиотека здесь. http://interface.khm.de/index.php/lab/interfaces-advanced/frequency-measurement-library/
По данной библиотеке результат гораздо хуже моего. (Смотря по вашей ссылке рис COM 43 порта) При частоте 100 гц Точность измерения составляет 1 такт от 160000. У меня порядка 1 такта на 1600000. По крайней мере у меня в 10 раз лучше.
Есть подозрение что переход на прерывание INT0 задерживается из за выполнения какого нибудь другого прерывания.
Конечно. Таймер0 отключали? Он строчит прерывания каждые 4 µS для всяких миллисов и микросов
Конечно. Таймер0 отключали? Он строчит прерывания каждые 4 µS для всяких миллисов и микросов
Думаю что ошибку исправил. Потестирую.
У меня был код:
void loop() { currentTime=millis(); DeltaTime_LCD =abs(currentTime - lastTime_LCD); if (DeltaTime_LCD >= interval_LCD && flag_LED==1)Заменил его на такой код:
void loop() { if (ovf_tik_timer_1 >= interval_timer_1 && flag_LED==1)Все заработало.
Никакой таймер 0 при этом не отключал. Вероятнее всего это было из за того, что вызывая
я обращался к Таймеру 0 в цикле Loop. Убрал это обращение и все заработало. Сейчас точность плюс - минус 2 такта от 16000000.
Возможно ли неисправность была была вызвана этим?
ovf_tic1=16796534 , tik_int_0 =21 n. f =20.004127 Hz
ovf_tic1=16796533 , tik_int_0 =21 n. f =20.004129 Hz
ovf_tic1=16796531 , tik_int_0 =21 n. f =20.004129 Hz
ovf_tic1=16796534 , tik_int_0 =21 n. f =20.004127 Hz
ovf_tic1=16796533 , tik_int_0 =21 n. f =20.004129 Hz
ovf_tic1=16796535 , tik_int_0 =21 n. f =20.004123 Hz
ovf_tic1=16796534 , tik_int_0 =21 n. f =20.004127 Hz
ovf_tic1=16796535 , tik_int_0 =21 n. f =20.004123 Hz
ovf_tic1=16796532 , tik_int_0 =21 n. f =20.004129 Hz
ovf_tic1=16796533 , tik_int_0 =21 n. f =20.004129 Hz
ovf_tic1=16796533 , tik_int_0 =21 n. f =20.004129 Hz
ovf_tic1=16796535 , tik_int_0 =21 n. f =20.004123 Hz
ovf_tic1=16796534 , tik_int_0 =21 n. f =20.004127 Hz
ovf_tic1=16796532 , tik_int_0 =21 n. f =20.004129 Hz
ovf_tic1=16796532 , tik_int_0 =21 n. f =20.004129 Hz
ovf_tic1=16796534 , tik_int_0 =21 n. f =20.004127 Hz
ovf_tic1=16796533 , tik_int_0 =21 n. f =20.004129 Hz
ovf_tic1=16796533 , tik_int_0 =21 n. f =20.004129 Hz
Привет всем.
Подскажите,пожалуйста,можно ли подключать два прерывания на один пин - одно по спаду импульса, другое- по фронту, например:
attachInterrupt (0,start,FALLING);
attachInterrupt (0,stop,RISING);
Заранее благодарю за ответы.
Привет всем.
Подскажите,пожалуйста,можно ли подключать два прерывания на один пин - одно по спаду импульса, другое- по фронту, например:
attachInterrupt (0,start,FALLING);
attachInterrupt (0,stop,RISING);
Заранее благодарю за ответы.
Ответ кроется в исходниках attachInterrupt:
if(interruptNum < EXTERNAL_NUM_INTERRUPTS) { intFunc[interruptNum] = userFunc;Если по исходникам неясно, то дополню: ответ на поставленный вопрос - нет.
attachInterrupt (0,start,FALLING);
attachInterrupt (0,stop,RISING);
Если длительность пульса не слишком короткая, и частота не супер, то поставив прерывание на CHANGE, потом внутри самой функции можно проверить уровень пина, высокий - был фронт, низкий - спад.
Два _обработчика_ одного прерывания на одном пине (по разным фронтам сигнала) подключаются так
void start() { detachInterrupt(0); attachInterrupt(0, stop, RISING); ... } void stop() { detachInterrupt(0); attachInterrupt(0, start, FALLING); ... } void setup() { ... attachInterrupt(0, start, FALLING); }если вы успеваете между струйками дождя (выполнить работу до смены фронта сигнала).
Спасибо большое за советы! Я после первого ответа долго раздумывал и решил попробовать в функциях обработки каждого прерывания само прерывание отключать и включать второе, а во втором - отключать второе и включать первое. Таким образом получилось мерять длительность импульса на входе прерывания (как в последнем ответе). Так что все получилось.
Еще раз спасибо за советы и удачной вам работы!
Timur Shapoval, длительность импульса можно измерять в одном прерывании, без всех этих лишних манипуляций. Помимо того есть специальная функция pulsein() для измерения длительности импульса.
Timur Shapoval, длительность импульса можно измерять в одном прерывании, без всех этих лишних манипуляций. Помимо того есть специальная функция pulsein() для измерения длительности импульса.
Спасибо за замечание. Если честно, я с pulseln () и начал, но она надолго останавливала программу - я логировал каждый шаг печатью, и так и не смог её победить. Поэтому решил попробовать прерываниями. Тем более, у меня заранее не известно, когда прилетит импульс, и сколько будет длиться (импульс бензинового впрыска автомобиля).
Однако, если есть где почитать поподробнее о применении этой функции, или кусочек рабочего кода, буду очень признателен.