Обработка прерываний

alexxkr
Offline
Зарегистрирован: 30.12.2012

Доброго времени суток.

Изучаю обработку прерываний Arduino и наткнулся на некоторые интересные эффекты. Плата - Spider Dagu, клон Mega, но с разъемами для сервоприводов, чип AVR Mega 1280. Для прерывания ипользую кнопку на порту D2, соответствующую interrupt(0).

1. Пытаюсь бороться с дребезгом кнопки при прерывании. Процедура, вызываемая по прерыванию:

void mov(){
  detachInterrupt(0);
  delay(2000);
  Serial.println("interrupt");
  attachInterrupt(0, mov, FALLING);
}

двухсекундная задержка установлена для наглядности. проблема в том, что внутри этой процедуры delay не работает. с чем это связано?

2. Если прерывание возникает в момент вывода данных в serial, контроллер виснет наглухо. как с этим бороться?

maksim
Offline
Зарегистрирован: 12.02.2012

Не внимательно читаете (или не читаете) описания функций, attachInterrupt() - где написано:

Замечание по использованию

Внутри функции обработки прерывания не работает delay(), значения возвращаемые millis() не изменяются. Возможна потеря данный передаваемых по последовательному соединению (Serial data) в момент выполнения функциии обработки прерывания. Переменные, изменяемые в функции, должным быть объявлены как volatile.

alexxkr
Offline
Зарегистрирован: 30.12.2012

Блин, этож надо так! Одним шагом на две грабли из трех!

А какие в этом случае есть приемы ввода точной задержки и борьбы с дребезгом? запуск свободного таймера или аасемблерный цикл  с "nop"?

step962
Offline
Зарегистрирован: 23.05.2011

alexxkr пишет:

А какие в этом случае есть приемы ввода точной задержки и борьбы с дребезгом? запуск свободного таймера или аасемблерный цикл  с "nop"?

Необходимо стремиться "задерживать" вне обработчика прерывания.
Ибо пока вы находитесь в одном обработчике прерывания, другие прерывания не могут выполняться. Ждут выхода из текущего обработчика прерывания. В результате запаздывают с исполнением, а то и вовсе пропускаются.

alexxkr
Offline
Зарегистрирован: 30.12.2012

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

 

step962
Offline
Зарегистрирован: 23.05.2011

В ISR устанавливаем флаг: FALLINGFRONT=1 (и больше ничего не делаем)

в loop при каждом проходе проверяем:

if (FALLINGFRONT) { // то ли отпущенная кнопка, то ли дребезг...
  FALLINGFRONT=0;  // сброс флага - подготовка к следующему ниспадающему фронту
  newTimeOfFallingFront=millis(); // время только что зарегистрированного "отпускания" кнопки
  if(newTimeOfFallingFront-oldTimeOfFallingFront>10) { // с последнего ниспадающего фронта прошло более 10 мс - есть надежда, что это уже не дребезг (интервал следует подобраьб опытным путем)
// считаем, что кнопка отпущена (т.е. была нажата)
// делаем то, что требуется при отпускании кнопки по логике программы
  }
  oldTimeOfFallingFront=newTimeOfFallingFront; // устанавливаем время последнего отпускания кнопки
}

 

step962
Offline
Зарегистрирован: 23.05.2011

Хотя нет - при таком варианте будем реагировать как на дребезг при отпускании кнопки, так и на дребезг при ее нажатии. Ниспадающие фронты и там и там есть.

loop-часть следует сделать так:

if (FALLINGFRONT) { // то ли отпущенная кнопка, то ли дребезг...
  FALLINGFRONT=0;  // сброс флага - подготовка к следующему ниспадающему фронту
  delay(10); // с последнего ниспадающего фронта прошло более 10 мс - есть надежда, что это уже не дребезг (интервал следует подобрать опытным путем)
  if(buttonPIN==RELEASED) { // за 10 мс дребезг прекратился и текущее состояние кнопки можно воспринимать как установившееся
// считаем, что кнопка отпущена (т.е. была нажата)
// делаем то, что требуется при отпускании кнопки по логике программы
  }
}

 

nestandart
nestandart аватар
Offline
Зарегистрирован: 15.06.2011

alexxkr, используйте флаг в прерывании а обнулять его будете в loop. Только не забудьте объявить флаг как volatile

alexxkr
Offline
Зарегистрирован: 30.12.2012

nestandart пишет:

В ISR устанавливаем флаг: FALLINGFRONT=1 (и больше ничего не делаем)

по этой теме надо выкурить соответствующий мануал, ибо сейчас даже не понял, о чем речь. ну нуб я :)

nestandart пишет:

alexxkr, используйте флаг в прерывании а обнулять его будете в loop. Только не забудьте объявить флаг как volatile

а зачем? ведь как я понял, в любом случае прерывания не срабатывают внутри функции прерывания, а значит мы все равно вернемся в loop() и сбросим флаг?

а теперь тупой вопрос. внутри процедуры прерывания  случайно нет ограничения на использование do..while?

а то при включении в процедуру кода с этим циклом плата на прерывании виснет наглухо:

void mov(){
  float y;                     //переменная координаты
  float xf;                    //координаты смещения
  float yf;
  float dx;                   
  
  float j;                     //тупые расчеты для визуальной задержки и антидребезга
  float k;                     //тупые расчеты
  for (int i=0; i<1000; i++){  //тупые расчеты
    j = sqrt(i);
    j = asin(j/100);
    k = k+j;
  } 
  
  float x = X;
  xf = random(70);
  yf = random(-70, 70);
  do{
   dx = x-xf;
   if(dx == 0){
      dx++;}
   y = ((yf-Y)*x+(xf*Y-X*yf))/(xf-X);
  // leg(x, y, 65);
   x = x*dx/abs(dx); 
    
  }while(abs(dx) >=1); 
}

здесь X и Y объявляются глобально и вычисляются в loop():

volatile float X;              
volatile float Y;

эта процедура должна перемещать ногу на трех сервах из исходной точки  с координатами (X,Y) в случайную (xf, yf)

процедура leg, собственно, и занимается перемещением ноги , работает корректно и из loop() вызывается без проблем.

 

step962
Offline
Зарегистрирован: 23.05.2011

alexxkr пишет:

nestandart пишет:

В ISR устанавливаем флаг: FALLINGFRONT=1 (и больше ничего не делаем)

по этой теме надо выкурить соответствующий мануал, ибо сейчас даже не понял, о чем речь. ну нуб я :)

nestandart пишет:

alexxkr, используйте флаг в прерывании а обнулять его будете в loop. Только не забудьте объявить флаг как volatile

а зачем? ведь как я понял, в любом случае прерывания не срабатывают внутри функции прерывания, а значит мы все равно вернемся в loop() и сбросим флаг?

ISR - это Interrupt Service Routine или обработчик прерывания по-русски. В вашем случае - это та функция, имя которой вы указали в операторе инициализации прерывания в setup'е. то есть mov.

И я, и nestandart говорим о переменной-флаге, а не об аппаратном флаге, который - действительно - предотвращает запуск обработки новых прерываний, если не завершилась обработка текущего прерывания. моя переменная-флаг - это FALLINGFRONT. Вы устанавливаете ее (его) в 1, чтобы основная программа могла узнать, что вызывалось ваше прерывание. Тогда в loop-е вы можете проверить (программный) флаг и - если он взведен - выполнить необходимы е действия ВНЕ ISR, то есть не внося неразберихи в систему обработки прерываний.

Цитата:

а теперь тупой вопрос. внутри процедуры прерывания  случайно нет ограничения на использование do..while?

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

 

alexxkr
Offline
Зарегистрирован: 30.12.2012

Спасибо за пояснение по поводу ISR.

Мысль по поводу программного флага от netstandart  я так и понял. А вот теперь, похоже, дошло,  что при достаточно длительной процедуре, выполняемой по флагу, можно не бояться дребезга кнопки на прерывании.

а по поводу кода процедуры есть мысли? что там может быть такого криминального, что ложит плату?

step962
Offline
Зарегистрирован: 23.05.2011

alexxkr пишет:

а по поводу кода процедуры есть мысли? что там может быть такого криминального, что ложит плату?

Вы можете сказать, сколько миллисекунд занимает выполнение тех операций, которые вы напихали в функцию mov() (т.е.в ISR)?

alexxkr пишет:

процедура leg, собственно, и занимается перемещением ноги , работает корректно и из loop() вызывается без проблем.

А, собственно, где вы привели код функции leg()?

 

alexxkr
Offline
Зарегистрирован: 30.12.2012

задержка на выполнение тупого кода получается на глаз меньше полсекунды,  заметно по остановке ноги.

а что, есть ограничение на время выполнения ISR?

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

исходя из того, что ногой все-таки он крутит как надо, думаю leg написан более-менее сносно.

#include <Servo.h>
Servo sa;              //название сервы 1
Servo sb;              //название сервы 2
Servo sg;              //название сервы 3
float a = 55;          //плечо
float b = 76;          //рука
volatile float X;               //координаты ноги
volatile float Y;
//****************************************************
void setup(){
  sa.attach(46);       //назначение пинов для серв
  sb.attach(47);
  sg.attach(48);
  sa.write(90);        //установка серв в начальное положение
  sb.write(90);
  sg.write(75); 
  Serial.begin(9600);
  attachInterrupt(0, mov, FALLING);
  Serial.println("setup complete");
  delay(4000);
}
//****************************************************
void mov(){
  float y;                     //переменная координаты
  float xf;                    //координаты смещения
  float yf;
  float dx;                   
  
  float j;                     //тупые расчеты
  float k;                     //тупые расчеты
  for (int i=0; i<1000; i++){  //тупые расчеты
    j = sqrt(i);
    j = asin(j/100);
    k = k+j;
  } 
  
  float x = X;
  xf = random(70);
  yf = random(-70, 70);
  do{
   dx = x-xf;
   if(dx == 0){
      dx++;}
   y = ((yf-Y)*x+(xf*Y-X*yf))/(xf-X);
  // leg(x, y, 65);
   x = x*dx/abs(dx); 
    
  }while(abs(dx) >=1); 
}

//***************************************************
void leg(float x, float y, float h){
  float l2 = x * x + y * y;
  float l = sqrt(l2);
  float c2 = h * h + l2;
  float c = sqrt(c2);
  float A = 0;
  float B3 = 0;
  float B2 = 0;
  float B = 0;
  float G = 0;
  float a2 = a * a;
  float b2 = b * b;
 
  if (x==0){                                //угол сервы 1 в градусах
   A = 90; 
   }
   else{ 
   if (y<0){
   A = - 57.3 * atan(x / y); 
   }
   else{
   A = 180 - 57.3 * atan(x / y); 
   }
  }
   
  G=180-57.3*acos((a2+b2-c2)/(2*a*b));              //угол сервы 3
  B2 = atan(l/h);                           //угол сервы 2
  B3 = acos((a2+c2-b2)/(2*a*c));
  B = 57.3*(B2+B3);     
   
  sa.write(A);                             //двигаем ногу
  sb.write(B);
  sg.write(G);
}
//*******************************************************************
void loop(){

  for (float f = 0; f <= 6.2; f = f + 0.1){
    X = 45 + 40 * cos(f);
    Y = 40*sin(f);
  /*  Serial.print("x=");
    Serial.print(X);
    Serial.print(" y=");
    Serial.print(Y);
    Serial.print(" f=");
    Serial.println(f);*/
    leg(X, Y, 65);
    delay(50);
  }  
  delay(1000);
 }



 

step962
Offline
Зарегистрирован: 23.05.2011

т.е. leg() это не то, что стоит в mov(). Уже хорошо. Причина зацикливания в mov() (и подвисания микроконтроллера) в том, что моделируемый в do {} while() процесс является расходящимся при том условии его окончания, которое вы прописали. Оперируя реалиями сегодняшнего дня, у вас получилась модель того астероида, который пройдет сегодня в 23 часа с копейками в 27000 километров от Земли. А должен получиться тот камень, который грохнулся в Челябинске...

Если вы вынесете код, используемый в mov(),  в обычную функцию и поиграетесь различными граничными условиями, то можете - как и я - выяснить, что минимальное условие, при котором ваш цикл начинает завершаться, это "dx>6". Это, так сказать, тот минимальный радиус вашей "Земли" при котором тело попадает-таки в нее (по крайней мере, при моих начальных координатах X и Y - 1 и что-то около полутора)

 

alexxkr
Offline
Зарегистрирован: 30.12.2012

за ошибку в алгоритме - спасибо, буду разбираться.

только всвязи с этим возникает другой вопрос.

немного раньше у меня вместо  всего кода процедуры была заглушка с выводом в Serial слова "interrupt"  для визуального контроля прерывания. оно выводилось по 1-3 раза. отсюда началсь борьба с дребезгом.

дальше появился этот цикл do..while, в нем был вывод значений координат. после этого я не видел на первого "interrupt", ни хоть какого-то вывода изнутри цикла.  это явление мне уже непонятно...

потом где-то на этом форуме я увидел информацию, что Serial.print() с прерываниями несовместим и я его на всякий случай удалил, хоть и не помогло.

p.s. опыты с переносом кода в тело loop() смогу продолжить только в понедельник. к сожалению.

alexxkr
Offline
Зарегистрирован: 30.12.2012

спасибо, алгоритм действительно был кривой. перенес в loop() и допилил.

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017
String messageotpr="";
void setup() {
attachInterrupt(0, cliuch, RISING);
Serial.begin(115200);
}

void sendsms()
{
if (messageotpr){
Serial.println(messageotpr);  
messageotpr=""; 
}
}


void cliuch()
{
messageotpr="Net cliucha";sendsms();
//detachInterrupt(0);
}

void loop() {
}

В симуляторе код работает идеально

По факту одновременно можно получить

Net cliucha
Net cliucha
 
Net cliucha
 
Net cliucha
Net cliucha
 
Как это исправить?
SW1 - это датчик вставленного ключа в замок зажигания
 

 

b707
Онлайн
Зарегистрирован: 26.05.2017

Иринка, пишете яснее. В окне симулятора видно, что там точно так же выводится "Нет клюитча"  :)

Так чего нужно от скетча и в чем проблема?

sadman41
Offline
Зарегистрирован: 19.10.2016

Дребезг контактов у нее и залетные на пин помехи.

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Нет ключа -- на 2 пине ардуино 1

Есть ключ  -- на 2 пине ардуино 0

Нет ключа -- на 2 пине ардуино 1

Переход с 0 на 1 (извлечение ключа) контролирую функцией

attachInterrupt(0, cliuch, RISING);

Всё подключила, вставляю ключ, извлекаю ключ и раз через раз в порт вмето одной записи 

Net cliucha

могу получить по две записи

Net cliucha

Net cliucha

Т.е. функция cliuch срабатывает неодин раз.

b707
Онлайн
Зарегистрирован: 26.05.2017

Иринка, слушайте sadman41

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

То, что дребезг контактов, это я поняла, поставила конденсатор, но не считаю это правильным вариантом

b707
Онлайн
Зарегистрирован: 26.05.2017

Irinka пишет:

То, что дребезг контактов, это я поняла, поставила конденсатор, но не считаю это правильным вариантом

погуглите "антидребезг" - вы смышленая. думаю сама справитесь :)

sadman41
Offline
Зарегистрирован: 19.10.2016

Конденсатор тоже не считает существование кислородной жизни правильным вариантом....

Поставьте триггер Шмитта, сделайте санационную задержку, откажитесь от прерываний - всё в ваших руках.

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Спасибо

xDriver
xDriver аватар
Offline
Зарегистрирован: 14.08.2015

загнать в прерывание работу с String,  да еще от туда позвать функцию, да еще в которорой работа с Serial ....

в прерывание выставить флаг, остальная обработка в loop...

мне так каааца :))

pyroprinter
Offline
Зарегистрирован: 16.02.2016

delay(2000);
Serial.println("interrupt");

Тут все подробно описано почему не работают в прерывании http://robotosha.ru/arduino/arduino-interrupts.html

xDriver верно предложил реализовать.

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Спасибо. Прочитала. Поняла.