Прерывания: есть вопросы - нужны ответы

vk007
Offline
Зарегистрирован: 16.06.2015

Всем привет!

Вопрос первый. Есть программа, в которой loop() выполняется долго. И есть кнопка, обрабатываемая по прерыванию. После обработки прерывания программа возвращается в то место, где ее "отвлекли", и продолжает выполняться дальше. А может есть возможность сделать возврат из прерывания в другое место, например, на начало loop()? Спасибо.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Возможность такая есть, но это "сильно лезть под юбку системе". Это не для чайников - это "высшая лига". Вы программист "высшей лиги"? Если нет - забудьте и считайте, что такой возможности нет. Если же Вы считаете себя таковым, то пожалуйста: - достаточно на стеке подменить адрес возврата после прерывания на любой адрес, который Вам нравится, например, на адрес функции loop. Тогда команда RETI перейдёт туда, куда Вам надо. Правда, стэк при этом Вы должны почистить сами (от возможных адресов возврата других вызываемых функций).

vk007
Offline
Зарегистрирован: 16.06.2015

Нет, я не программист "высшей лиги", но принцип я понял. Спасибо!

vk007
Offline
Зарегистрирован: 16.06.2015

Вопрос второй. Та же программа, та же кнопка.

Чтобы не засиживаться долго в функции обработки прерывания, в ней самой только выставляем флаг, что кнопку нажали и возвращаемся в программу. Но чтобы не было так скучно, кнопку хочется "озвучить". Для этого имеется пищалка (со встроенным генератором) и ее необходимо включить на короткое время. И хоть время короткое, но паузу нужно выдержать, а внутри обработчика прерывания ни delay(), ни millis() не работают. Крутить пустые циклы...? Как-то не то. И хоть такая конструкция:

    digitalWrite(pinBeeper, HIGH);
    delay(1);
    digitalWrite(pinBeeper, LOW);

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

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

vk007 пишет:

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

Это очень грамотно и правильно.

vk007 пишет:

И хоть время короткое, но паузу нужно выдержать, а внутри обработчика прерывания ни delay(), ни millis() не работают. Крутить пустые циклы...? Как-то не то. И хоть такая конструкция:

    digitalWrite(pinBeeper, HIGH);

    delay(1);

    digitalWrite(pinBeeper, LOW);

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

А вот здесь Вы противоречите своему же грамотному подходу. Правильно делать озвучку не в функции-обработчике прерывания, а снаружи, также, как Вы писали в начале – по взведённому флагу. Чем быстрее выполняется обработчик прерывания. Тем лучше.

Я отвечу на Ваш вопрос (как это сделать в обработчике прерываний), но, повторяю, это плохая идея, лучше делайте так, как Вы описали в начале – выставляйте флаг, а пищите уже вне обработчика по флагу.

Итак, внутри обработчика:

Во первых, не надо бояться цикла. Не боитесь же Вы delay()? А это тоже мёртвый цикл в котором только yield() вызывается.

Во-вторых, внутри обработчика Вы можете разрешить посторонние прерывания (функция sei()). Если мне не изменяет память, этого достаточно, чтобы заработала millis() (а тогда и delay заработает). Но если вдруг не заработает, то нужно ещё сбросить флаг «нахожусь в обработке прерывания». Сброс флага в общем случае – прописать 1 в определённый бит. Какой бит – зависит от прерывания.

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

vk007, возьмите пищалку без генератора. В функции tone() можно установить длительность сигнала, а заодно и частоту.  Важно заметить, что фактически tone() работает "в фоне", типа многозадачность)

Logik
Offline
Зарегистрирован: 05.08.2014

ЕвгенийП пишет:

Я отвечу на Ваш вопрос (как это сделать в обработчике прерываний), но, повторяю, это плохая идея, лучше делайте так, как Вы описали в начале – выставляйте флаг, а пищите уже вне обработчика по флагу.

 

Не. Он не сможет в основном цикле писчать. Основной цикл медленный (что и есть первоисточник бед), писчать начнет слишком позно после нажатия. 

Тут надо так: в обработчике прерывания кнопки взводим флаг для действия в основном цикле, начинаем писчать и настраиваем аппаратный таймер на заданую длительность, когда таймер досчитает, то в прерывании таймера выключаем звук. Можно немного упростить если повезет - настроить таймер на прямую работу с пином и  сигнал с этого пина разрешает писк.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

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

vk007
Offline
Зарегистрирован: 16.06.2015

tone() я уже пробовал, но она отъедает пару килобайт кода и будет пищать только после выхода из функции обработки прерывания, вобщем, не нравится она мне )

А что касается "пищать в основной программе", я уже упоминал - loop длинный, пока доберемся до места проверки флага, то и пищать уже не к месту. Логично "нажали кнопку" - "пискнули".

Кстати, отсюда еще один вопрос, как ловить состояние флага не дожидаясь полного цикла loop? Разбросать по всей программе проверки? Бррр... Таймер? Но я пока с ними не дружу и возможностей их не знаю.

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

vk007, тогда можно таймером  подать команду, тоже "многозадачность" , как отпищит сам отключится :)

Logik
Offline
Зарегистрирован: 05.08.2014

Прерывание таймера - не "высшая лига" ни разу, это "кубок области" не выше. От прямая коррекция стека - да сложно.  

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

 

Logik
Offline
Зарегистрирован: 05.08.2014

vk007 пишет:

А что касается "пищать в основной программе", я уже упоминал - loop длинный, пока доберемся до места проверки флага, то и пищать уже не к месту. 

Я ж и пишу что длинный луп - первопричина всех бед. Может проще его сделать быстрым, так до 50мс? Тогда и проверку нажатия и старт-стоп писков, все в нем и без прерываний.

vk007 пишет:

 Бррр... Таймер? Но я пока с ними не дружу и возможностей их не знаю.

Не бррр, а ням-ням. Просто раз надо научится "готовить". Примеров море.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

vk007 пишет:

Разбросать по всей программе проверки? Бррр... 

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

#define PIS if (pisk_flag) { pisk_flag = false; pischalka(); }

А потом хоть через строчку просто пишете

...
PIS
...
PIS
...

Не так уж она и испохабила код.

Ну, разумеется, функцию pischalka() надо написать. В начале скетча где-нибудь её расположите.

 

vk007
Offline
Зарегистрирован: 16.06.2015

Logik пишет:

Не бррр, а ням-ням. Просто раз надо научится "готовить". Примеров море.

:) бррр касалось не таймеров, а разбросать по программе проверки флага

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

Всем спасибо за проявленное внимание, а я полезу-ка неспешно в интернеты освежать и набираться знаний о стеке и его особенностях в отношении к AVR.

Logik
Offline
Зарегистрирован: 05.08.2014

Вот так вот. В "высшую лигу" минуя "кубок области"?! Ну, бог - помощ :) 

ПС. Не забывайте отписатся о ходе и результате.

Datak
Offline
Зарегистрирован: 09.10.2014

vk007 пишет:
Всем спасибо за проявленное внимание, а я полезу-ка неспешно в интернеты освежать и набираться знаний о стеке и его особенностях в отношении к AVR.

Посмотрите заодно про вполне стандартные сишные функции setjmp и longjmp. С их помощью можно решить задачу "выпрыгивания" в определённое место программы из любой точки любой функции. При выпрыгивании из обработчика прерываний будут, по сравнению с обычной функцией, некоторые отличия, но незначительные. Скорее всего, достаточно будет "вручную" разрешить прерывания.

В принципе, вникать при этом во все тонкости работы со стеком совсем не обязательно.
Но, конечно, всё-таки желательно. :)

vk007
Offline
Зарегистрирован: 16.06.2015

Logik пишет:

Вот так вот. В "высшую лигу" минуя "кубок области"?! Ну, бог - помощ :) 

ПС. Не забывайте отписатся о ходе и результате.

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

А отписываться о ходе, то это врядли. Все это будет проходить ооочень неспешно и не факт, что проблема не решится каким-нибудь другим способом.

vk007
Offline
Зарегистрирован: 16.06.2015

Ну что же, вопрос номер два решился: не работает в прерывании delay() зато заработала  _delay_ms().

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Datak пишет:

vk007 пишет:
Всем спасибо за проявленное внимание, а я полезу-ка неспешно в интернеты освежать и набираться знаний о стеке и его особенностях в отношении к AVR.

Посмотрите заодно про вполне стандартные сишные функции setjmp и longjmp. С их помощью можно решить задачу "выпрыгивания" в определённое место программы из любой точки любой функции. При выпрыгивании из обработчика прерываний будут, по сравнению с обычной функцией, некоторые отличия, но незначительные. Скорее всего, достаточно будет "вручную" разрешить прерывания.

В принципе, вникать при этом во все тонкости работы со стеком совсем не обязательно.
Но, конечно, всё-таки желательно. :)

Нет, там ещё бит "перывания" надо сбрасывать, а это порождает несовместимость для разных плат.

Оптимально выходить честным reti но не на loop(), а на чинилку стека. А чинилка - функция из одной строчки "longjump" на заранее определённую setjump'oм метку в начале loop. Так должно работать. 

Datak
Offline
Зарегистрирован: 09.10.2014

Это понятно, да - чем глубже лезешь, тем меньше совместимости.

Насчёт бита прерывания - я примерно о нём и говорил, что надо разрешить прерывания "вручную".
Ещё могут быть определённые биты регистров контроллера прерываний - но это уже действительно зависит от конкретного железа. В ATmega'х, насколько я помню, они сбрасываются сами, уже при входе в обработчик.

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

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

vk007,

пример, как выйти из прерывания в начало функции loop() есть в теме http://arduino.ru/forum/programmirovanie/ch-2-st-119-uk-rf-1960-g

 

vk007
Offline
Зарегистрирован: 16.06.2015

Спасибо, ознакомился.

vk007
Offline
Зарегистрирован: 16.06.2015

Вот набросал на скорую руку:

#include <setjmp.h>

jmp_buf jbuf;
volatile boolean flag = 0;

void btnClick() {
  Serial.println("Button is pressed!!!");
  flag = 1;
  longjmp(jbuf, 1);  // наша песня хороша - начинай сначала
}

void setup() {
  Serial.begin(9600);
  attachInterrupt(0, btnClick, LOW);
}

void loop() {
  setjmp(jbuf);
  if (flag) {
    // обрабатываем нажатие кнопки
    flag = 0;
  }
  Serial.println("Program is working");
  // работаем
  delay(5000);
}

Так можно использовать setjmp/longjmp, оно будет работать или я намутил что-то бредовое?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

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

Дело в том, что  btnClick вызывается в контексте обработчика прерываний, а значит это не обычная функция. Для корректного выхода из неё нужно, как минимум,  установить в логическую единицу бит 7 регистра SREG, иначе никакое другое прерывание не сможет обработаться (вложенные прерывния по умолчанию запрещены). Также, в зависимости от конкретного прерывания, возможны другие необходимые действия. Так же как Вы делаете, управление переходит куда Вам надо, но контроллер всё равно остаётся в режиме обработки прерывания.

vk007
Offline
Зарегистрирован: 16.06.2015

ЕвгенийП пишет:

Ну, вообще-то критерий истины - попробовать.

А вот тут проблема. Скетч компилится, но заливаться не хочет.

avrdude: stk500v2_ReceiveMessage(): timeout
avrdude: stk500v2_getsync(): timeout communicating with programmer

При этом другие скетчи льются нормально.

vk007
Offline
Зарегистрирован: 16.06.2015

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

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Вся беда в длинном loop() как уже прозвучало. Сильно подозреваю, что в нем понатыкано delay() везде где можно. Гуглите "автоматное программирование" и вот эту тему: http://arduino.ru/forum/programmirovanie/novichkam-avtomatnoe-programmir...

и будет вам щастье.

Datak
Offline
Зарегистрирован: 09.10.2014

ЕвгенийП пишет:
Для корректного выхода из неё нужно, как минимум,  установить в логическую единицу бит 7 регистра SREG, иначе никакое другое прерывание не сможет обработаться

Вот кстати... Сам про это писал, а теперь засомневался. Вполне возможно, этот SREG тоже сохраняется в контексте setjmp'а. Если да - может и не придётся больше ничего сохранять.

Короче, надо всё уточнять и пробовать - а мне сейчас пока не на чем.

Datak
Offline
Зарегистрирован: 09.10.2014

Arhat109-2 пишет:
Вся беда в длинном loop() ...

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

SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012

Чтобы не засиживаться долго в функции обработки прерывания, в ней самой только выставляем флаг, что кнопку нажали и возвращаемся в программу. Но чтобы не было так скучно, кнопку хочется "озвучить". Для этого имеется пищалка (со встроенным генератором) и ее необходимо включить на короткое время. И хоть время короткое, но паузу нужно выдержать, а внутри обработчика прерывания ни delay(), ни millis() не работают. Крутить пустые циклы...? Как-то не то.

не проще ли перейти к железу ? 
в обработчике нажатия выставить 1 на 1 миллисекунду на какой-то пин и сбросить.... ( запуск 555 )
на тот пин повесить таймер 555 с пищалкой .....

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

1 миллисекунда в обработчике прерывания .. это "потеря" около 16000 (шестнадцать тысяч!) команд для 16Мгц Меги .. или 8000 неисполненных команд для 8-Мгц ардуины.. не "многовато" за решение? :)

Lomonosov
Lomonosov аватар
Offline
Зарегистрирован: 12.01.2017

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

Добрый день всем! Я пока только начинаю позновать азы, посему прошу сильно не пинать. Пытаюсь для авто написать простенькую прогу по автозапиранию дверей. В ходе разработки потребовалось четкое отслеживание открытости дверей. Решил отслеживать это по прерыванию, а не простом считыванием сигнала.

Имеется андурино НАНО с двумя портами прерывания. Один порт занят под датчик скорости (считывания частоты), по второму порту нужно отслеживать положение дверей. Т.е. нужно точно знать, что был переход в состояние HIGH, и  нужно точно знать что был переход в состаяние LOW.  Попробовал сначала на один и тот же порт посадить две функции прерывания, но по разным фронтам (RISING и FALLING) - не заработало. Код прилагается:

volatile int Impotant_Flag  = 0; //Важный флаг

void setup() {
  pinMode (13, OUTPUT);
  attachInterrupt(1, Break_High, RISING ); //Прерывание по фронту High
  attachInterrupt(1, Break_Low, FALLING);  //Прерывание по фронту Low
}

void loop() {
   digitalWrite(13, Impotant_Flag); // Вывести состояние флага на светодиод
}

void Break_High() {
  Impotant_Flag=1;    //Выставить флаг по фронту High
}
void Break_Low() {
  Impotant_Flag=0;    //Выставить флаг по фронту Low
}

Тогда написал код с прерыванием по смене фронта (CHANGE). По хорошему достаточно при смене фронта просто инвертировать нужный флаг, тогда при RISING -> 1, а при FALLING -> 0, но боюсь что может произойти сбой по какой либо причине, и флаг перескочит через фронт и будет инвертироваться в обратной последовательности RISING -> 0 и FALLING -> 1. Посему код решил написать так:

volatile int Impotant_Flag  = 0;

void setup() {
  pinMode (13, OUTPUT);
  pinMode (3, INPUT);
  attachInterrupt(1, Break, CHANGE); //Прерывание по смене фронта
}

void loop() {
   digitalWrite(13, Impotant_Flag);// Вывести состояние флага на светодиод
}

void Break() {
  Impotant_Flag=digitalRead(3);     //Присвоить флагу состояние пина после смены фронта
}

Этот код заработал нормально. Теперь собстенно вопрос. На сколько с точки зрения програмирования коряво написан код? Меня смущает что по одному и тому же пину идет прерывание и считывание. Или это абсолютно нормально?

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Я конечно, не профессиональный программист- человек зарабатывающий на программировании, но выскажу мнение о аппаратном прерывании. Вот ответьте о месте рукопашного боя в современной войне. Ну да там где боец прое**л все и встрелился с таким же уникумом с таким же материальным обеспечением.  Когда-то давно процессоры были медлеными, место под программы было мало. Не было средств эти программы писать тоже не было. Тогда использовать аппаратное прерывание было оправдано. Ну или наблюдать процессы соизмеримые с частотой процессора.  Конечно, если у вас на машине стоит газовая турбина, и надо точно отслеживать скорость этой турбины. А двери на машине открываются с частотой дверей во время распродажи под скидки перед Новым Годом в единственном супермаркете в радиусе 10 км в центре города миллионика. Тогда да вы поступили правильно.

Lomonosov
Lomonosov аватар
Offline
Зарегистрирован: 12.01.2017

qwone

Мысли понята, принята и осознана. Не буду завязываться с прерыванием по этому каналу.

Но вопрос был немного в другом: насколько коряво написан сам код с точки зрения программирования? Хотя это уже не важно.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Lomonosov пишет:

Но вопрос был немного в другом: насколько коряво написан сам код с точки зрения программирования? Хотя это уже не важно.

А вот вы и сами себе ответите. Вот мой скетч. Может вам пригодится в будущем.

/* Lomonosov.ino
  #1 дверь правая ->2
     конечник на двери/ закрыт замкнут / подключение земля и вывод без резистора
  #2 дверь левая ->3
     конечник на двери/ закрыт замкнут / подключение земля и вывод без резистора
  Принцип кода:Посылать сигналы открытия и закрытия дверей
*/
//#1 правая дверь
#include "Cl_Do_btn_deBounce.h"
Cl_Do_btn_deBounce Door_right(2); // создать конечник на пине 2
void right_open(void) {
  Serial.println("Right door open");
}
void right_close(void) {
  Serial.println("Right door close");
}
//#2 левая дверь
Cl_Do_btn_deBounce Door_left(3); // создать конечник на пине 3
void left_open(void) {
  Serial.println("Left door open");
}
void left_close(void) {
  Serial.println("Left door close");
}
void setup() {
  Serial.begin(9600);
  //#1 правая дверь
  Door_right.setup();
  //#2 левая дверь
  Door_left.setup();
}
void loop() {
  //#1  правая дверь
  Door_right.loop(& right_close, & right_open);
  //#2 левая дверь
  Door_left.loop( & left_close, & left_open);
}
/*Cl_Do_btn_deBounce.cpp
*/
#include "Arduino.h"
#include "Cl_Do_btn_deBounce.h"
Cl_Do_btn_deBounce::Cl_Do_btn_deBounce(byte _pin) {
  btn_pin = _pin;
}
void Cl_Do_btn_deBounce::setup() {
  pinMode(btn_pin, INPUT_PULLUP);// подключить кнопку 1 с подтяжкой
  btn = digitalRead(btn_pin) ; // прочитать реальное значение на выводе
}
void Cl_Do_btn_deBounce::loop(void (* _func_1)(), void (* _func_2)()) {
  if (! bounce_btn && btn != digitalRead(btn_pin)) { // если прошел фронт изм на выводн
    bounce_btn = 1;                                 // выставить флаг
    past = millis();                         // сделать временую засветку
  }
  else if ( bounce_btn && millis() - past >= 5 ) { // если прошло антидребезговое время
    bounce_btn = 0;      // то снять флаг
    btn_old = btn ;
    btn = digitalRead(btn_pin) ; // прочитать реальное значение на выводе
    if (btn_old && ! btn) _func_1()  ;
    if (! btn_old && btn) _func_2() ;
  }
}
/*Cl_Do_btn_deBounce.h
*/
#ifndef Cl_Do_btn_deBounce_h
#define Cl_Do_btn_deBounce_h

#include "Arduino.h"
class Cl_Do_btn_deBounce {
  public:
    Cl_Do_btn_deBounce(byte _pin);
    void setup();
    void loop(void (* _func_1)(), void (* _func_2)());
  private:
    byte btn_pin ;
    bool btn, btn_old;
    bool bounce_btn = 0; // антидребезговый флаг
    uint32_t past = 0 ;
};
#endif //Cl_Do_btn_deBounce_h

Для работы надо разместить все файлы. Название в первой строчке.

Lomonosov
Lomonosov аватар
Offline
Зарегистрирован: 12.01.2017

qwone пишет:
Вот мой скетч. Может вам пригодится в будущем.
Огромное спасибо! Про антидребезг очень полезное замечание. Сразу в коде не разобрался (объектно-ориентированного программирования у меня как такового не было, а то что было - было последний раз в институте в 2000г.), но я обязательно вникну и осознаю. Пока антидребезг планировал организовать аппаратно - на конденсаторах по типу как делают в бортовых компьютерах.

П.С. Формально вход с концевиков у меня один, т.к. опятьже аппаратно все концевики дверей для сигналки объеденены в один через диодную развязку.

П.П.С. Пока у меня вырисовывался такой код:

  boolean DPPA = 0;                             //Флаг педали газа: 0 - педаль не нажата, 1 - педаль нажата
  boolean DS = 0;                               //Флаг датчика скорости: 0 - скороть недостаточна, 1 - скорость в норме
  boolean LOCK = 0;                             //Флаг запертых дверей: 1 - двери заперты, 0 - незаперты
  volatile unsigned long TM = 0;                //Время предудущего прерывания датчика скорости
  volatile unsigned int HZ = 0;                 //Частота датчика скорости
  
  void setup() {                                //Вход педали газа на аналоговом входе 5
    pinMode(7, INPUT);                          //Вход концевика дверей: 1 - двери закрыты, 0 - открыты
    pinMode(13, OUTPUT);                        //Выходной сигнал на закрытие
    attachInterrupt(1, Speed, RISING);          //Прерывание по датчику скорости, пин 3
  }

  void loop() {                                
    while(LOCK) {                               //Пока двери заперты ничего не делать
      if(!digitalRead(7)) LOCK=0;               //Если двери открыты, то они не заперты
      delay(50);
    }                  
    if(digitalRead(7) && !LOCK) LOCK = Zakrdv();//Попытаться запереть двери, если они закрыты и не заперты
    delay(50);
  }

  int Zakrdv() {                                
    while(digitalRead(7)){                      // Пока двери закрыты мониторим датчики:
      if(analogRead(5) > 230) DPPA = 1;         // Проверка педали газа с установка флага (U*204.8)
      if(HZ > 25) DS = 1;                       // Проверка датчика скорости на 15 км/ч с установка флага (HZ = V*6/3.6)
      if(DPPA && DS){                           // Процесс запирание
        delay(500);                             // Задержка на полное защелкивание замков (если двери закрылись на ходу)
        if (!digitalRead(7)) {
          DPPA = 0;  DS = 0;                    // Сброс флагов и возврат  
          return 0;                             // при внезапном открытии
        }
        digitalWrite(13, 1);                    // Подать импульс 0,5 сек. на запирание
        delay(500); 
        digitalWrite(13, 0);
        DPPA = 0;  DS = 0;                      // Сброс флагов
        delay(1000);                            // Подождать запирание дверей (из-за ступенчатой работы активаторов)     
        return 1;
      }
      delay(50);
    }
    DPPA = 0;  DS = 0;                          // Сброс флагов и возврат
    return 0;                                   // после неудачной попытки (двери открылись в процессе мониторинга датчиков)
  }

  void Speed() {                                
    HZ = 1000000/(micros() - TM);               //определение частоты датчика скорости
    TM = micros();
  }

Задержка в 50 мс в каждом цикле для имитирования уменьшения тактовой частоты до 20гц и меньшей нагрузки на процессор и стабилизатор напряжения (как мне кажется) :smile_bigsmile: Т.к. это все итак будет греться до 50°С т.к. размещение планируется под торпедой.
 

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Lomonosov пишет:

П.С. Формально вход с концевиков у меня один, т.к. опятьже аппаратно все концевики дверей для сигналки объеденены в один через диодную развязку.

http://greenoakst.blogspot.com/2012/06/arduino.html 

А об этом не знали? 

Ну и мое добавление https://yadi.sk/d/7Uj4xEn038zpqk   Код выложу постом ниже.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016
/* class_press_analog_5AnBtn.ino
  #1 кнопка ->A0
  #2 модуль выполнения
  Принцип кода:запустить функцию  по нажатию кнопки
*/
//#1 кнопка
#include "Cl_press_analog_5Btn.h"
#define ABtn_PIN A0
Cl_press_analog_5btn AnBtn(ABtn_PIN); // создать кнопку на пине 2
bool a; // переменная состояние на кнопке без дребезга
//#2 модуль выполнения
void func_1(void) {
  Serial.println("Do Right");
}
void func_2(void) {
  Serial.println("Do Up");
}
void func_3(void) {
  Serial.println("Do Down");
}
void func_4(void) {
  Serial.println("Do Left");
}
void func_5(void) {
  Serial.println("Do Select");
}
void setup() {
  //#1 кнопка
  AnBtn.setup();
  //#2 модуль выполнения
  Serial.begin(9600);
}
void loop() {
  //#1,#2  кнопка и модуль выполнения
  AnBtn.loop(& func_1, & func_2, & func_3, & func_4, & func_5);
}
/*Cl_press_analog_5btn.cpp
*/
#include "Arduino.h"
#include "Cl_press_analog_5btn.h"
Cl_press_analog_5btn::Cl_press_analog_5btn(byte _pin) {
  abtn_pin = _pin;
}
void Cl_press_analog_5btn::setup() {
  pinMode(abtn_pin, INPUT_PULLUP);// подключить кнопку 1 с подтяжкой
  abtn = read() ; // прочитать реальное значение на выводе
}
byte Cl_press_analog_5btn::read() {
  const int Right  =  60; // ниже = нажата кнопка  Right
  const int Up     = 200; // ниже = нажата кнопка  Up
  const int Down   = 400; // ниже = нажата кнопка  Down
  const int Left   = 600; // ниже = нажата кнопка  Left
  const int Select = 800; // ниже = нажата кнопка  Select
  static int a ;
  a = analogRead(abtn_pin);
  if (a < Right) return 1;
  else if (a < Up) return 2;
  else if (a < Down) return 3;
  else if (a < Left) return 4;
  else if (a < Select) return 5;
  else return 0;
}
void Cl_press_analog_5btn::loop( void (* _func_1)(), void (* _func_2)(), void (* _func_3)(), void (* _func_4)(), void (* _func_5)() ) {
  if (! bounce_abtn && abtn != read()) { // если прошел фронт изм на выводн
    bounce_abtn = 1;                     // выставить флаг
    past = millis();                     // сделать временую засветку
  }
  else if ( bounce_abtn && millis() - past >= 5 ) { // если прошло антидребезговое время
    bounce_abtn = 0;      // то снять флаг
    abtn_old = abtn ;
    abtn = read() ; // прочитать реальное значение на выводе
    if (abtn_old == 0) {
      if (abtn == 1) _func_1()  ;
      if (abtn == 2) _func_2()  ;
      if (abtn == 3) _func_3()  ;
      if (abtn == 4) _func_4()  ;
      if (abtn == 5) _func_5()  ;
    }
  }
}
/*Cl_press_analog_5btn.h
Принцип кода : При подключении аналоговой клавиатуры идет вызов нужной функции по нажатой клавише
*/
#ifndef Cl_press_analog_5btn_h
#define Cl_press_analog_5btn_h
#include "Arduino.h"
class Cl_press_analog_5btn {
  public:
    Cl_press_analog_5btn(byte _pin);
    void setup(); // вставить в setup()
    void loop(void (* _func_1)(), void (* _func_2)(), void (* _func_3)(), void (* _func_4)(), void (* _func_5)()); // вставить в loop()
  private:
    byte read();
    byte abtn_pin ; // нога аналог клавиатуры
    byte abtn, abtn_old; // старый и новый номер нажатой клавиши
    bool bounce_abtn = 0; // антидребезговый флаг
    uint32_t past = 0 ; // временной отчет
};
#endif //Cl_press_analog_5btn_h

 

Lomonosov
Lomonosov аватар
Offline
Зарегистрирован: 12.01.2017

qwone пишет:
http://greenoakst.blogspot.com/2012/06/arduino.html

А об этом не знали?

Знал, но диодная развязка уже есть. Она был сделана при установке СИГНАЛКИ. Почему ей не воспользоваться, если она уже есть?

Или я не понял Вашу мысль?