Скетч на "Удлинитель поворотов"

Тема в разделе "Arduino & Shields", создана пользователем ArturiK, 20 янв 2017.

  1. ArturiK

    ArturiK Нуб

    Привет всем! Решил я обратиться к Вам за советами, долго мучился, пытался сам разобраться, но мой мозг не осиливает, я больше по железякам.... И так к делу, имеется у меня уже почти рабочий скетч, логика у него такова:
    Должен он при кратковременном нажатии на подрулевой переключатель поворотов (без щелчка чисто коснулись до контакта), задерживать его включенным на 3 секунды (поморгать при перестроении) левый и правый в зависимости какой "клацнули" и если включили его с щелчком, то он уже не должен отрабатывать задержку при выключении. Так же хочу убрать фиксацию с кнопки "Аварийки" сделать при кратковременном нажатии "Спасибо" это тоже задержка включения на 3 секунды и фиксируемое включение/выключение при нажатии на нее более 2 секунд.
    Итого я насчитал 4 состояния:
    1) Все выключено
    2) Включен только левый
    3) Включен только правый
    4) Оба включены "Аварийка"

    Плата Ардуино УНО, используются 2 пина входа (левый/правый), 2 пина выхода (левый/правый).

    Проблемы две:
    1) Не получается реализовать длительное нажатие кнопки включение и выключение "Аварийки", повороты и функция "Спасибо" работают как надо!
    2) Слишком все громоздко, нужно "подпилить" если есть возможность, ибо залить я хочу это все в Тини 13-ю...

    Жду Ваших советов.... Заранее СПАСИБО!

    Код (C++):
    // Комфортные поворотники "Лентяйка" с фунцией "Спасибо"
    // Входы
    byte LeftBut = 8; // Левая сторона "Left Turn Signal"
    int RightBut = 9; // Правая сторона "Right Turn Signal"
    // Выходы
    int LeftOUT = 11; // Левая сторона "Left Turn Out"
    int RightOUT = 12; // Правая сторона "Right Turn Out"

    byte LbState = 0; // Переменная левой кнопки
    byte RbState = 0; // Переменная правой кнопки
    int pause = 2000; // Переменная паузы включения

    //Переменные для хранения значений счетчиков
    long LOnTime;
    long ROnTime;
    long ThsTimeON;
    long ThsTimeOFF;
    // Переменные для хранения состояния кнопок
    bool LTstate;
    bool RTstate;
    bool Ths;

    void setup() {
      // Выставляем входы/выходы
      pinMode(LeftBut, INPUT);
      pinMode(RightBut, INPUT);
      pinMode(LeftOUT, OUTPUT);
      pinMode(RightOUT, OUTPUT);
      // Сбрасываем состояния кнопок
      LTstate = false;
      RTstate = false;
    }

    void loop() {
      // Считываем состояние входов
      LbState = digitalRead(LeftBut);
      RbState = digitalRead(RightBut);
      delay(100);//небольшая защита от "дребезга" контактов кнопки
      byte state = RbState*2+LbState; // Переменная общего состояния входов


    // Левый поворот
      if (state==1) {
        digitalWrite(LeftOUT, HIGH);
        digitalWrite(RightOUT, LOW);
        LTstate = true; // Активируем таймер левого поворота
        LOnTime = millis(); // Переменная начала отсчета таймера
      }
     
    // Правый поворот
      if (state==2) {
        digitalWrite(RightOUT, HIGH);
        digitalWrite(LeftOUT, LOW);
        RTstate = true; // Активируем таймер правого поворота
        ROnTime = millis(); // Переменная начала отсчета таймера
      }

    // "Аварийка"
      if (state==3) {
        ThsTimeON = millis();
        if (state==0) {
          ThsTimeOFF = millis();
        }
        if (ThsTimeON - ThsTimeOFF < 500) {
          digitalWrite(LeftOUT, HIGH);
          digitalWrite(RightOUT,HIGH);
          ThsTimeOFF = millis();
        }
        if (ThsTimeON - ThsTimeOFF >= 500) {
          Ths = true;
          ThsTimeOFF = millis();
        }
      }
     
    // Функция "Спасибо"
      if (Ths) {
          digitalWrite(LeftOUT,HIGH);
          digitalWrite(RightOUT,HIGH);
          LTstate = true; // Активируем таймер левого поворота
          RTstate = true; // Активируем таймер правого поворота
          // Переменные начала отсчета таймера
          ROnTime = millis();
          LOnTime = millis();
          Ths = false;
        }

    // Таймер левого поворота
      if (LTstate) {
        if (millis() - LOnTime > pause) {
          digitalWrite(LeftOUT, LOW);
          LTstate = false;
        }
      }
     
    // Таймер правого поворота
      if (RTstate) {
        if (millis() - ROnTime > pause) {
          digitalWrite(RightOUT, LOW);
          RTstate = false;
        }
      }
     
    }

    Рассмотрю варианты "Платной" помощи!
     
  2. mcureenab

    mcureenab Гуру

    Код (C++):
      if (state==3) {
        ThsTimeON = millis();
        if (state==0) { // Тут state == 3 всегда
     
     
  3. AlexU

    AlexU Гуру

    Состояний будет больше. Не знаком с общей схемой, поэтому могу ошибаться:
    1. кратковременное нажатие лев.поворот -- касание;
    2. включение лев.поворота; (вполне вероятно, что это используется штатная реализация, и поэтому код за это отвечать не должен)
    3. кратковременное нажатие прав.поворот -- касание;
    4. включение прав.поворота; (вполне вероятно, что это используется штатная реализация, и поэтому код за это отвечать не должен)
    5. кратковременное нажатие аварийки
    6. длительное нажатие аварийки.
    В текущий момент состояние вычисляется в момент нажатия кнопок, но, наверно, лучше будет вычислять состояние после отпускания кнопки.
    Да и кнопки лучше повесить на прерывания ...
     
  4. mcureenab

    mcureenab Гуру

    Кнопки на прерываниях - отдельная тема из-за дребезга. Опрашивать кнопки проще.

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

    В кнопках нас интересуют моменты включения и выключения. Если работать с кнопками в режиме опроса нужно помнить их прежнее состояние. Что то делать только когда новое состояние отличается от предыдущего. Таймер удержания сбрасывается при нажатии любой кнопки.

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

    Когда все кнопки отпустили, смотрим на состояние мигалок, на таймер удержания и если надо запускаем таймер отключения.

    Мигалки должны реагировать на сигналы Выключить (приходит таймера отключения), Переключить правый поворотник, Переключить левый поворотник, Переключить Аварийку. Тут надо разобраться какие переходы допустимы в каждом состоянии мигалок.
    Если включена Аварийка, то поворотники наверное не должны её отключать. Но возможно в состоянии Аварийка поворотники тоже должны отрабатывать.

    На счет размера кода проблем не вижу. Во время компиляции посмотрите статистику использования памяти. Наверняка там и для ATtiny13 большой резерв есть.
     
  5. ArturiK

    ArturiK Нуб

    AlexU, ошибку условия понял, попробую исправить, насчет прерываний я уже пробовал с ними, во первых это определенные пины контроллера что мне особо не удобно, во вторых их использование я считаю здесь не уместным, тут нет ничего критичного, это не котел электрический который при аварийном случае должен резко что-то выполнить. Разве что усыплять контроллер и им его "будить", тут можно задействовать. А вот считывать кнопки по их отпусканию это уже интересно, учту.

    mcureenab, по Вашему посту я сделал список примерный, исправьте если что не так понял:
    1) Сделать два таймера (Удержание, Отключение)
    2) Сделать флаги на кнопки
    3) Ну небольшая задержка delay у меня нормально считывает состояние аварийки, нету такого чтобы включился только один
    4) Мигалок тут не надо, будет использоваться штатное электронное реле из RC цепочки или Вы про состояние включения поворотников? Думаю Аварийка должна быть приоритетней чем повороты
    5) Попробовал скомпилировать свой код под Тини 13 действительно он почему то стал меньше в два раза, чем в Уно странно конечно....

    И можно ли обратиться к кому нибудь по написанию рабочего кода, если у меня не выйдет? Я просто уже неделю мучаюсь с этим....
     
  6. AlexU

    AlexU Гуру

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

    mcureenab Гуру

    Это ветка форума
    Закажу проект
     
  8. mcureenab

    mcureenab Гуру

    Определенная польза от прерываний в том, что можно не гонять цикл опроса когда ничего не происходит. Это экономия электроэнергии, что критично для портативных устройств.
    Два потока, это может быть и так:
    loop(){
    thread_bottons();
    thread_lights();
    }
    без всяких прерываний.

    Если на нажатие или отпускание кнопки прерывания нормально работают (бывает на одно движение серия прерываний случается из-зи дребезга), то на переключение без хорошего фильтра нечего соваться. А тут надо и ON и OFF ловить.
     
  9. mcureenab

    mcureenab Гуру

    3) у вас задержка влияет на следующую итерацию цикла. т.е. сначала может включиться поворотник, а на следующей итерации аварийка. Думаю для логики, это не критично, но зачем электронику насиловать лишними командами?

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

    Код (C++):
    enum Command {
       nop, // нет команды.
       off,  // выключить все. (выставляет обработчик таймера Отключения)
       left, // переключить левый
       right, // переключить правый
       alarm //  переключить аварийку.
    };

    Command cmd = nop;
    Если cmd = nop функция thread_offtimer(); пишет в cmd = off; удаляет таймер.
    Если cmd = nop функция thread_buttons(); определяет что с кнопками и пишет в cmd нужную команду из Command. или оставляет без изменений.
    Если cmd != nop thread_offtimer(); и thread_bottons(); ничего не делают. Пропускают свой "ход" до следующей итерации.
    Функция thread_lights(); читает cmd выполняет переход состояния и пишет в cmd = nop; (очередь свободна).

    Таймер удержания hold_timer наверное будет уместным реализовать внутри thread_bottons.
     
  10. AlexU

    AlexU Гуру

    В AVR-ках ни какой экономии таким способом не добиться, контроллер всё равно будет выполнять какой-нибудь код ("гонять цикл") пока его не "усыпишь". В контексте данной задачи усыплять контроллер не нужно от слова "совсем", а о пользе от использования прерываний изложил постом выше.
    Дребезг в прерываниях программно лечится очень легко.
    И в чём проблема -- любой сигнальный пин поддерживает внешнее прерывание при смене уровня сигнала.
     
  11. mcureenab

    mcureenab Гуру

    Поддерживает, но успешно скипает прерывания когда на пин валит серия помех от дребезга. Даже определить что произошло проблематично, потому как состояние пина меняется быстрее чем отрабатывает прерывание. Я подолбился с этой фичей и пришел к выводу, что без "идеальной" кнопки (с триггером Шмитта например) тут делать нечего.
    От чего бы не усыплять? Он или по кнопке работает или по таймеру. Оба могут генерить прерывание. И не обязательно в прерывании что то делать. Достаточно только пробудить цикл.
     
  12. AlexU

    AlexU Гуру

    Теперь понятно почему Вы от такой удобной функциональности отказываетесь.
     
  13. ArturiK

    ArturiK Нуб

    Да с прерываниями очень много заморочек, согласен с mcureenab нужно делать с триггером, а как это делать если цепь силовая? Это уже проводку переделывать, придется разводить гальванически управление и силу, это слишком затратно!

    А как делать команды я вообще что-то не понял, походу это мне еще недели две разбираться придется.... мне железом походу проще было это реализовать транзюки и RC цепочки..... ну можно за место таймеров 555 использовать, косяк лишь в том что затратно получается..... и изучить хочется и времени мало, ладно буду просить тогда в той теме, может на рабочем примере проще будет разобраться...
    Спасибо Вам за уделенное внимание, может спустя время освою материал, но не сейчас!
     
  14. qwone

    qwone Гик

    Ну все решается с помощью модулей из классов и примитивной логики. Вот только разобраться в нюансах задачи у меня не получается. Сколько всего кнопок(есть фиксация или нет)? 1 ,2 или больше . И какие каждая вызывать действия.
     
  15. если ещё актуально могу чего порассказать у меня е39 в прошлом году сделал себе комфортный поворот всё работает как часики все проблемы с дребезгом а также перелётом ручки при выключении поворота решаются программно ну и чють чють пассивными элементами.