Вопрос по прерываниям и таймерам

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

Добрый день, камрады

"Изобретаю" детектор нуля для управления нагрузкой согласно этого апнота https://www.microchip.com/content/dam/mchp/documents/OTH/ApplicationNotes/ApplicationNotes/Atmel-2508-Zero-Cross-Detector_ApplicationNote_AVR182.pdf. Планировал ловить внешнее прерывание, в обработчике вывод переводить в HIGH и запускать таймер, который по 4 мсек переведет вывод в LOW. Код таков:

#define PIN_IN  2
#define PIN_OUT 4

void setup() {
  pinMode(PIN_IN, INPUT);
  pinMode(PIN_OUT, OUTPUT);
  attachInterrupt(0, action, CHANGE);
  Timer2_init();
}

void action()
{
  cli();
  PORTD |= (1 << PD4);
  TCNT2 = 0;
  TIMSK2 |= (1 << OCIE2A);
  sei();
}

void Timer2_init()
{
  TCCR2A = 0;
  TCCR2B = 0;
  TCCR2A |= (1 << WGM21);                    
  TCCR2B = (1 << CS21) | (1 << CS22);        
  OCR2A = 249;
}

ISR (TIMER2_COMPA_vect)
{
  cli();
  TIMSK2 &= ~(1 << OCIE2A);
  PORTD &= ~(1 << PD4);
  sei();
}

void loop()
{
}

 

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

черт, рано кнопку отправить нажал. В общем на экране осциллографа наблюдается следующая картинка:

То есть четкая сработка на растущем фронте, и какие-то слишком короткие импульсы на падающем фронте. 

Если поменять функцию action() так:

void action()
{
  PORTD |= (1 << PD4);
  delay(4);
  PORTD &= ~(1 << PD4);
}

(то есть отказываемся от таймера), то картинка приобретает вполне товарный вид:

Вопрос - что я делаю не так, когда пытаюсь отключать выход по таймеру?

rkit
Offline
Зарегистрирован: 23.11.2016

Останови таймер как положено. Сбрось прескалер. Сбрось флаги прерываний.

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

rkit пишет:
Останови таймер как положено.

А как его положено останавливать? До Вашего сообщения я был уверен что TIMSK2 &= ~(1 << OCIE2A) делает именно это.

rkit пишет:
Сбрось прескалер

TCCR2B = 0; - так? При кажом запуске таймера его нужно заново выставлять?

rkit пишет:
Сбрось флаги прерываний.

TIFR2 &= ~(1 << OCF2A); так? был уверен что вызов обработчика прерывания сбрасывает флаг

rkit
Offline
Зарегистрирован: 23.11.2016

Нет по всем пунктам. Читай даташит, всё расписано.

(ну по третьму пункту сказал правильно, но не учел других нюансов)

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

rkit пишет:

Нет по всем пунктам. Читай даташит, всё расписано.

(ну по третьму пункту сказал правильно, но не учел других нюансов)

1 и 2 TCCR2B &= ~((1 << CS20) | (1 << CS21) | (1 << CS22));

3 как писал ранее TIFR2 &= ~(1 << OCF2A);

Что еще не учел? (вроде как заработало, но судя по оговорке "но не учел других нюансов" чувствую что это не всё)

rkit
Offline
Зарегистрирован: 23.11.2016

GTCCR

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Всё это очень занимательно, но если слепой ведет слепого - оба упадут в яму. ;)) Прескеллер? Ну да, ну да.

Подпишусь на тему.

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

т.е. первый и второй пункты GTCCR |= (1 << PSRASYNCвместо TCCR2B &= ~((1 << CS20) | (1 << CS21) | (1 << CS22))?

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

wdrakula пишет:

Всё это очень занимательно, но если слепой ведет слепого - оба упадут в яму. ;)) Прескеллер? Ну да, ну да.

Подпишусь на тему.

Умными не рождаются, умными становятся. как раз после падений в яму и выкорабкивания из неё :)

 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Dinosaur пишет:

wdrakula пишет:

Всё это очень занимательно, но если слепой ведет слепого - оба упадут в яму. ;)) Прескеллер? Ну да, ну да.

Подпишусь на тему.

Умными не рождаются, умными становятся. как раз после падений в яму и выкорабкивания из неё :)

Ну, так-то - да. Но когда слушать коллегу Ркита станет невмоготу  - напиши. Я пока за компом. ;)

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

rkit пишет:
Сбрось флаги прерываний.

Читаю в описании регистра TIFR2 - бит OCF2A аппаратно очищается при обработке соответствующего вектора прерывания. Какой смысл в принудительном сбросе флага?

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

wdrakula пишет:

Ну, так-то - да. Но когда слушать коллегу Ркита станет невмоготу  - напиши. Я пока за компом. ;)

Так напейсал, дайте пинка в нужном направлении...

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Dinosaur, по скетчу тоже есть замечания. Но интересно другое, у вас похоже ложные срабатывания по INT0 идут. Уберите из скетча всё, связанное с таймером. А в Void action вставьте только PORTD ^= (1 << PD4);  И осциллограммы в студию.

 
rkit
Offline
Зарегистрирован: 23.11.2016

Dinosaur пишет:

rkit пишет:
Сбрось флаги прерываний.

Читаю в описании регистра TIFR2 - бит OCF2A аппаратно очищается при обработке соответствующего вектора прерывания. Какой смысл в принудительном сбросе флага?

Смысл в том, что флаг поднимается, даже когда обработчик выключен. И как только ты его включаешь, он вызывается. Потому и проскакивала игла. Снятие флагов прерывания (всех) входит в остановку таймера.

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

dimax пишет:

Dinosaur, по скетчу тоже есть замечания. Но интересно другое, у вас похоже ложные срабатывания по INT0 идут. Уберите из скетча всё, связанное с таймером. А в Void action вставьте только PORTD ^= (1 << PD4);  И осциллограммы в студию.

 
По скетчу приму замечания с благодарностью. Ложных срабатываний нет по INT0 (пробовал переворачивать состояния порта изначально, все было ок. потом делей в прерывание воткнул - тоже все работало (причем 100 микросекунд достаточно было для корректной сработки MOC3023), камрад rkit прав (ну по крайней мере то, как я его понял - проблему решило). Сейчас работающий вариант выглядит так:
#define PIN_IN  2
#define PIN_OUT 4

void setup() {
  pinMode(PIN_IN, INPUT);
  pinMode(PIN_OUT, OUTPUT);
  attachInterrupt(0, action, CHANGE);
  Timer2_init();
}

void action()
{
  cli();
  PORTD |= (1 << PD4);
  TCNT2 = 0;
  TCCR2B = (1 << CS20) | (1 << CS21) | (1 << CS22);   // CLK/1024
  sei();
}

void Timer2_init()
{
  TCCR2A = 0;
  TCCR2B = 0;
  TCCR2A |= (1 << WGM21);  // CTC
  OCR2A = 15;
  TIMSK2 |= (1 << OCIE2A);
}

ISR (TIMER2_COMPA_vect)
{
  cli();
  GTCCR |= (1 << 1);                                   // сброс прескалера
  TIFR2 |= (1 << OCF2A);                               // сбросили флаг прерывания
  PORTD &= ~(1 << PD4);                                
  sei();
}

void loop()
{
}

 

 

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

rkit пишет:

Смысл в том, что флаг поднимается, даже когда обработчик выключен. И как только ты его включаешь, он вызывается. Потому и проскакивала игла. Снятие флагов прерывания (всех) входит в остановку таймера.

Спасибо за помощь и ликбез. Если еще что то упустил, поошу ткнуть носом.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Dinosaur пишет:

wdrakula пишет:

Ну, так-то - да. Но когда слушать коллегу Ркита станет невмоготу  - напиши. Я пока за компом. ;)

Так напейсал, дайте пинка в нужном направлении...

тыкаю: никто никогда не останавливает таймеры. Иликтричество экономишь? ;)) Прерывания запрещай и разрешай. Перед разрешением - сбрось флаг. Точку прерывания установи на свои +4 мс, по тикам. Или OCR не трогай, а сбрасывайв ноль каунтер. Это от личных вкусов зависит. ;)).

А смеялся я над дебильным советом останавливать таймер. Ркит - он хороший, но сильно теоретик.

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

wdrakula пишет:

тыкаю: никто никогда не останавливает таймеры. Иликтричество экономишь? ;)) Прерывания запрещай и разрешай. Перед разрешением - сбрось флаг. Точку прерывания установи на свои +4 мс, по тикам. Или OCR не трогай, а сбрасывайв ноль каунтер. Это от личных вкусов зависит. ;)).

А смеялся я над дебильным советом останавливать таймер. Ркит - он хороший, но сильно теоретик.

Ок, спасибо, завтра попробую ваши рекомендации.

 

rkit
Offline
Зарегистрирован: 23.11.2016

Прескалер не в тему сбросил, он продолжает крутиться, на момент следующего старта отсчета в неизвестном состоянии, по задержке получается разброс в пределах периода прескалера.

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

rkit пишет:

Прескалер не в тему сбросил, он продолжает крутиться, на момент следующего старта отсчета в неизвестном состоянии, по задержке получается разброс в пределах периода прескалера.

Так вы же ранее писали «Сбрось прескалер» и «GTCCR», как интерпретировать? Не так сбрасываю? И в даташите написано когда прескалер 0, таймер отключён 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Dinosaur, если нет проблем сменить выход PD4 на аппаратную ногу таймера, например PD3, то всё делается вот так просто и изящно:

void setup() {
pinMode(3,OUTPUT);
pinMode(2, INPUT);
attachInterrupt(0, action, CHANGE);
 }

void action() {
TCCR2B=0; TCNT2=0;
OCR2B=61;// timeOut 4mS
TCCR2A=(1<<COM2B0)|(1<<COM2B1)|(1<<WGM22); //pin Up after compare
TCCR2B=1<<FOC2B; //force compare
TCCR2A=(1<<COM2B1)|(1<<WGM22);//pin Down after compare
TCCR2B|=(7<<CS20); // div1024, start
}

void loop() {}

Принцип работы: В прерывании INT0 таймер2 программируется сразу и на поднимание, и на опускание через 4mS своей аппаратной ноги.

 

rkit
Offline
Зарегистрирован: 23.11.2016

Таймер отключен, когда он отключен от часов. Пока часы тикают, тикает прескалер. И если его сбросить в ноль, он продолжит тикать с нуля.

Upper
Offline
Зарегистрирован: 23.06.2020

DIMAX

В строке 10 наверное надо WGM21 ?

И при OCR2B=61 уже имеет смысл сбрасывать prescaler (может быть).

 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Upper пишет:

DIMAX

В строке 10 наверное надо WGM21 ?

Ага, не заметил, да. Забыл убрать, он там вообще не нужен (Mode=0). Можно и на поменьше делить, да. Тады так:


void action() {
TCNT2=0;
OCR2B=249;// timeOut 4mS
TCCR2A=(1<<COM2B0)|(1<<COM2B1); //pin Up after compare
TCCR2B=1<<FOC2B; //force compare
TCCR2A=(1<<COM2B1);//pin Down after compare
TCCR2B=(6<<CS20); // div 256, start
}
Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

Камрады, кажется я Вас начинаю понимать (путаница в терминах в моей голове), прошу поправить мою нить рассуждений если ушел в дебри:

Есть регистр предделителя(он же прескалер), он тикает всегда с частотой микроконтроллера. В регистре TCCR2B битами CS20-CS22 задается с какой частотой относительно предделителя будет увеличиваться значение в регистре TCNT2 (если биты CS20-CS22 сброшены - значение в регистре TCNT2 не увеличивается, хотя предделитель в это время тикает). По совпадению (в моем случае) TCNT2 и OCR2A выставляется флаг прерывания OCF2A в регистре TIFR2 (который сбрасывается при вызове обработчика прерывания, а обработчик прерывания вызывается при установленном бите OCIE2A в регистре TIMSK2). То есть получается картина - TCNT2 дотикал до OCR2A, флаг OCF2A выставлен, но из за сброшенного OCIE2A обработчик прерывания не запускается и ждет установки OCIE2A. И как только я его выставляю (не сбросив предварительно OCF2A), обработчик немедленно выполняется. И получаются странные иголки на выходе вместо импульсов заданной ширины.

Также прочитал что сброс прескалера (регистр GTCCR, биты PSRASY и PSRSYNC) не самая правильная процедура, т.к. оказывает влияние на другие таймеры.

Таким образом при запуске таймера при пересечении нуля: нужно сбросить счетчик TCNT2 = 0, сбросить флаг прерывания TIFR2 |= (1 << OCF2A), разрешить прерывание по совпадению TIMSK2 |= (1 << OCIE2A). А в обработчике прерывания по таймеру просто запретить прерывание по совпадению TIMSK2 &= ~(1 << OCIE2A).

Правда меня смущает замечание камрада rkit (в моей задаче эта ошибка некритична, но для понимания темы хотелось бы разобраться до конца) - "Прескалер не в тему сбросил, он продолжает крутиться, на момент следующего старта отсчета в неизвестном состоянии, по задержке получается разброс в пределах периода прескалера". То есть первый тик в TCNT2 пойдет не точно через 1 / 1024 от частоты МК, а через совершенно непредсказуемый период, зависящий от значения прескалера на момент запуска таймера. Насколько это ужос-ужос, и как с этим бороться? На ум приходит - уменьшать делитель (чтобы ошибка на первом тике была меньше), либо при большом делителе - сбрасывать прескалер перед запуском таймера.

Напоследок выкладываю что у меня получлось по мотивам "озарения":

#define PIN_IN  2
#define PIN_OUT 4

void setup() {
  pinMode(PIN_IN, INPUT);
  pinMode(PIN_OUT, OUTPUT);
  attachInterrupt(0, action, CHANGE);
  Timer2_init();
}

void action()
{
  cli();
  PORTD |= (1 << PD4);
  TCNT2 = 0;
  TIFR2 |= (1 << OCF2A);                               // сбросили флаг прерывания
  TIMSK2 |= (1 << OCIE2A);
  sei();
}

void Timer2_init()
{
  TCCR2A = 0;
  TCCR2B = 0;
  TCCR2A = (1 << WGM21);                             // CTC
  TCCR2B = (1 << CS20) | (1 << CS21) | (1 << CS22);   // CLK/1024
  OCR2A = 30;
}

ISR (TIMER2_COMPA_vect)
{
  cli();
  TIMSK2 &= ~(1 << OCIE2A);
  PORTD &= ~(1 << PD4);
  sei();
}

void loop()
{
}

и картинку с осциллографа (с симулятора, железный поленился подключать):

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

dimax пишет:

Dinosaur, если нет проблем сменить выход PD4 на аппаратную ногу таймера, например PD3, то всё делается вот так просто и изящно:


void action() {
TCCR2B=0; TCNT2=0;
OCR2B=61;// timeOut 4mS
TCCR2A=(1<<COM2B0)|(1<<COM2B1)|(1<<WGM22); //pin Up after compare
TCCR2B=1<<FOC2B; //force compare
TCCR2A=(1<<COM2B1)|(1<<WGM22);//pin Down after compare
TCCR2B|=(7<<CS20); // div1024, start
}

Принцип работы: В прерывании INT0 таймер2 программируется сразу и на поднимание, и на опускание через 4mS своей аппаратной ноги.

Спасибо за наводку, как раз смотрел на эти биты и думал "чего я дурак печатку развел не под те ноги"... В будущем учту этот момент. Но вопрос - почему ногу поднимаем так сложно (строки 4-5) всесто записи единички в порт? Нам же сразу ее поднять нужно...

Upper
Offline
Зарегистрирован: 23.06.2020

cli() в прерываниях бесполезен (прерывания и так запрещены), а sei() не нужен (т.к. они разрешатся автоматически при выходе), и теоретически может быть вреден.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Dinosaur, потому, что com-биты отключают ноги от gpio.

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

dimax пишет:
Dinosaur, потому, что com-биты отключают ноги от gpio.

Если вместо строк 4-5, поднимем пин записью в порт единички, то после шестой строки будет с пином творится хз что?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Dinosaur, то следующей строкой пин отключится и будет low (предполагаю).

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

dimax пишет:
Dinosaur, то следующей строкой пин отключится и будет low (предполагаю).

Ок, дошло. Спасибо.

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

wdrakula пишет:

тыкаю: никто никогда не останавливает таймеры. Иликтричество экономишь? ;)) Прерывания запрещай и разрешай. Перед разрешением - сбрось флаг. Точку прерывания установи на свои +4 мс, по тикам. Или OCR не трогай, а сбрасывайв ноль каунтер. Это от личных вкусов зависит. ;)).

Спасибо, поизучал вопрос, передалал по Вашим наставлениям. Работает.

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

Камрады, напоследок под шквал критики представлю функцию регулировки мощности нагревателя (пропуском периодов). Вызывается по прерыванию INT0 (при пересечении нуля). Критика по всем моментам приветствуется.

void zeroCrossAction()
{
  const uint16_t powerTable[11] = {
    0b0000000000000000, // 0%
    0b0000000000000001, // 10%
    0b0000000000100001, // 20%
    0b0000000010001001, // 30%
    0b0000000100101001, // 40%
    0b0000000101010101, // 50%
    0b0000001011010110, // 60%
    0b0000000110111011, // 70%
    0b0000001111011110, // 80%
    0b0000000111111111, // 90%
    0b0000001111111111 // 100%
  };

  static uint16_t mask = (1 << 0);

  if (powerTable[powerValue] & mask) {
    PORTD |= (1 << PD4);
    TCNT2 = 0;
    TIFR2 |= (1 << OCF2A);                          // сбросили флаг прерывания
    TIMSK2 |= (1 << OCIE2A);
  }

  static bool halfPeriod;

  if (halfPeriod) {
    mask = mask << 1;
    if (mask & 0b0000010000000000) mask = (1 << 0);
  }
  halfPeriod = !halfPeriod;
}

 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Dinosaur пишет:

Напоследок выкладываю что у меня получлось по мотивам "озарения":

#define PIN_IN  2
#define PIN_OUT 4

void setup() {
  pinMode(PIN_IN, INPUT);
  pinMode(PIN_OUT, OUTPUT);
  attachInterrupt(0, action, CHANGE);
  Timer2_init();
}

void action()
{
  cli();
  PORTD |= (1 << PD4);
  TCNT2 = 0;
  TIFR2 |= (1 << OCF2A);                               // сбросили флаг прерывания
  TIMSK2 |= (1 << OCIE2A);
  sei();
}

void Timer2_init()
{
  TCCR2A = 0;
  TCCR2B = 0;
  TCCR2A = (1 << WGM21);                             // CTC
  TCCR2B = (1 << CS20) | (1 << CS21) | (1 << CS22);   // CLK/1024
  OCR2A = 30;
}

ISR (TIMER2_COMPA_vect)
{
  cli();
  TIMSK2 &= ~(1 << OCIE2A);
  PORTD &= ~(1 << PD4);
  sei();
}

void loop()
{
}

ОК. Все правильно, кроме cli() и sei() в ISR. "Масло - масляное" получается. Тебе уже Upper про это написал.

 

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Если в момент выполнения кода по прерыванию произойдёт другое (например) прерывание, то как это будет обработано? Прерывание (второе) будет пропущено, помещено в какую-то очередь или как?

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

Upper пишет:
cli() в прерываниях бесполезен (прерывания и так запрещены), а sei() не нужен (т.к. они разрешатся автоматически при выходе), и теоретически может быть вреден.

Спасибо за комментарий, почитал про этот момент, действительно лишнее.

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

wdrakula пишет:
ОК. Все правильно, кроме cli() и sei() в ISR. "Масло - масляное" получается. Тебе уже Upper про это написал.

Спасибо, поправил.

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

BOOM пишет:
Если в момент выполнения кода по прерыванию произойдёт другое (например) прерывание, то как это будет обработано? Прерывание (второе) будет пропущено, помещено в какую-то очередь или как?

Вы INT0 имеете ввиду? Да, убегать в новое прерывание по выходу из текущего было бы нехорошо... В моей ситуации не так часто события сыпятся чтобы это было возможно, но я так понимаю неплохо бы проверить перед выходом из прерывания в регистре EIFR флаг INTF0 и при необходимости сбросить его? Верно думаю?

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Я в общем спрашивал, к примеру - Вхожим в обработку прерывания по внешнему прерыванию, а в это же время происходит прерывание по таймеру и тоже должно быть обработано. То есть мы уже вошли в обработку первого и ещё не вышли, происходит второе. Как себя мк поведёт? 

nik182
Offline
Зарегистрирован: 04.05.2015

Зависит от МК. Для 328го по выходу из прерывания, если были другие прерывания, то вызовуться по очереди, согласно преоритету. Если внутри прерывания разрешить прерывания, то прерывание прервётся и вызовется другое. В stm32 завит от приоритета прерываний. Если у прерывания более высокий приоритет, то текущее прерывание прервётся. Но это в общих чертах. Всё сложнее. Надо читать святцы для конкретного случая.

Upper
Offline
Зарегистрирован: 23.06.2020

Dinosaur пишет:

... но я так понимаю неплохо бы проверить перед выходом из прерывания в регистре EIFR флаг INTF0 и при необходимости сбросить его? Верно думаю?

По вашему алгоритму с битовой маской, если по INT0 нет "помех", то как вы и написали, ничего дополнительно делать не надо. Если помехи возможны, то может лучше проверять в обработчике INT0 уровень сигнала на PIN_OUT, и если он высокий - значит это "срабатывание в неожиданный момент" и что делать в этом случае - решать вам. По крайней мере на этапе отладки можно подсчитывать число таких случаев и выводить в Serial.

П.С. (Оптимальность самого алгоритма с битовой маской обсуждать не могу - нет практического опыта управления нагревателями).

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

BOOM пишет:
Я в общем спрашивал, к примеру - Вхожим в обработку прерывания по внешнему прерыванию, а в это же время происходит прерывание по таймеру и тоже должно быть обработано. То есть мы уже вошли в обработку первого и ещё не вышли, происходит второе. Как себя мк поведёт?

Насколько я понял из даташита, 328 запоминает событие, и после выхода из обработчика идем в другое прерывание (согласно приоритету, если их несколько в очереди). Но я прямо не иксперд, надеюсь камрады поправят если соврал.

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

Upper пишет:
По вашему алгоритму с битовой маской, если по INT0 нет "помех", то как вы и написали, ничего дополнительно делать не надо. Если помехи возможны, то может лучше проверять в обработчике INT0 уровень сигнала на PIN_OUT, и если он высокий - значит это "срабатывание в неожиданный момент" и что делать в этом случае - решать вам. По крайней мере на этапе отладки можно подсчитывать число таких случаев и выводить в Serial.

С ложной сработкой как ни странно проблем нет, пробовал на реальном устройстве гонять. Хотя в апноте 182 тоже пишут мол проверьте сотояние пина 5 раз чтобы убедиться в том что сработка не ложная, но это хорошо когда прерывание по одному из фронтов, а когда по каждому и реакция нужна сразу, решил не усложнять в общем. Я думал имеется ввиду когда мы находимся в обработчике INT0, и за это время происходит еще одно прерывание, обработчик которого будет запущен сразу после выхода из обработчика. Как с этим бороться? (как я выше писал - проверить перед выходом из прерывания в регистре EIFR флаг INTF0 и при необходимости сбросить его) или другие методы?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013
Dinosaur, кстати по поводу сброса флага -  конструкция TIFR2 |= (1 << OCF2A);  делает не совсем то, что вы думаете. Она сбросит в ноль все установленные биты в TIFR2, а не один. Т.е. например если в TIFR2 все три бита подняты, то произойдёт что-то вроде этого:
00000111 |00000010 = 000000111 , и вторая операция, которую выполнит МК   TIFR2 = 00000111, и TIFR2 в итоге становится 00000000 Что бы этого не случилось нужно просто присваивать значение TIFR2 = 1<<OCF2A .
Конечно в данном скетче это не играет никакого рояля, но если б вы обрабатывали два прерывания -были бы проблемы..
Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

dimax пишет:
конструкция TIFR2 |= (1 << OCF2A);  делает не совсем то, что вы думаете. Она сбросит в ноль все установленные биты в TIFR2, а не один. Т.е. например если в TIFR2 все три бита подняты, то произойдёт что-то вроде этого: 00000111 |00000010 = 000000111 , и вторая операция, которую выполнит МК   TIFR2 = 00000111, и TIFR2 в итоге становится 00000000 Что бы этого не случилось нужно просто присваивать значение TIFR2 = 1<<OCF2A .

 
Хм, а почему так? В даташите вроде как написано Alternatively, OCF2A is cleared by writing a logic one to the flag.
Флаг я интерпритирую как бит. А если сделаем TIFR2 = 1 << OCF2A, то по идее TIFR2 должен стать 00000010, то есть сбросятся все флаги. А с другой стороны написано чтобы сбросить флаг, запишите в него единицу. Голова кругом как сие понять. Можете ткнуть где человеческим (русским желательно) языком про это почитать?
 
Нашел Ваше же сообщение "В даташите крайне скупо сказано об особенностях этого регистра. Если вы внимательно читали, то знаете что установленная единица в нём обнуляется записью единицы. А вот записать единицу, если в регистре ноль -нельзя. В этом его особенности. Обнулять так же можно красивой командой TIFR2=TIFR2.  :)" Стало понятнее. Вопрос где про эти особенности почитать в силе (в даташите не нашел описание этого нюанса)
dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Dinosaur, я же разобрал принцип на примере, и вроде вполне человеческим языком. Ещё раз: TIFR2 = 1 << OCF2A - это не одна,  а две операции МК (см. пример). Первая получает во временном регистре 111, вторая записывая результат в TIFR вызовет его обнуление. Проще уже некуда.. 

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

dimax пишет:
Dinosaur, я же разобрал принцип на примере, и вроде вполне человеческим языком. Ещё раз: TIFR2 = 1 << OCF2A - это не одна,  а две операции МК (см. пример). Первая получает во временном регистре 111, вторая записывая результат в TIFR вызовет его обнуление. Проще уже некуда..

Да, спасибо, дошло. Просто в даташите ничего не увидел про две операции, думал что в один бит записал единичку, и конец мучениям. А тут оказалось что весь регистр переписать надо, а дальше еще и магия происходит )))

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Dinosaur, всё таки я несколько поспешил про TIFR2|=, всё сказанное верно, но есть одна особенность  -регистр TIFR попадает  в диапазон адресов, для которых работает ассемблерная команды sbi, которая умеет записать бит в регистр не читая его предварительно.  И компилятор сам догадывается вместо традиционной обработки  TIFR|=  (прочесть, изменить, и записать обратно) дать короткую команду sbi, соответссно другие биты не сбросятся.  Я сам про проблему TIFR|= вычитал когда-то на easielectronics, и просто отложил в голове не проверяя. А тут вот вспомнил, проверил, и оба-на,  а компилятор сам догадался и исправил.  Но всё равно, нужно отложить в голове, что правильно сбрасывать флаги без "OR".

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

dimax пишет:
Но всё равно, нужно отложить в голове, что правильно сбрасывать флаги без "OR".

Но только в некоторых (особенных) регистрах, верно?

P.S. Это нелзя панять, но нужна запомнить - слава "вилька" и "тарелька" пишутся без мягкого знака, а слова "кон" и "сол" пишутся с мягким знаком )))

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Dinosaur пишет:

Хм, а почему так? В даташите вроде как написано Alternatively, OCF2A is cleared by writing a logic one to the flag.

Флаг я интерпритирую как бит. А если сделаем TIFR2 = 1 << OCF2A, то по идее TIFR2 должен стать 00000010, то есть сбросятся все флаги. А с другой стороны написано чтобы сбросить флаг, запишите в него единицу. Голова кругом как сие понять. Можете ткнуть где человеческим (русским желательно) языком про это почитать?
 
Это потому, что у Вас сложилось ложно впечатление, что регистр должен обладать свойствами ячейки памяти.
Это в ячейку памяти, если мы что-то записали, то она должна это хранить без искажений.
А с регистром - совсем не так, он на запись должен отвечать действием. И это действие может быть любым. Например, поменять все биты в регистре на противоположные. Причем, этот регистр, в котором изменяются биты, может быть совсем не тем, куда мы пишем.
Просто откажитесь от заблуждения "регистр должен хранить записанную в него информацию", и все встанет на свои места.
 
PS. Кстати, регистр может отвечать действием не только на запись, но и на чтение. Т.е. прочитали мы что-то из регистра, а в нем от этого что-то поменялось.