Туда-сюда или мегаблинк!

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

  1. PushKeeN

    PushKeeN Нуб

    Господа, подскажите, пожалуйста, в чем может быть проблема.
    Дано:
    Вальцы, через которые проходит проволока и "гильотина", управляемая двумя электромагнитами на 220В через твердотельные реле Omron G3MB-202P c Arduino Uno. Один электромагнит толкает "нож" в одну сторону, другой - в другую.
    На крыльчатке мотора вальцов установлена "метка", которая приводит к срабатыванию индуктивного датчика (вальцы управляются отдельно частотным регулятором).
    Задача была на через определенное количество срабатываний датчика (к примеру через 10) включить один магнит на 30 мс, еще через 10 второй на 30 мс и так далее.
    В процессе наладки всего этого дела все вроде бы работало, раз 10-15 отрезало, но затем контроллер начинал дергать электромагнитами независим от датчика, как пулемет.
    Решил проверить не связано ли это с самим датчиком.
    Отключил датчик, написал скетч, который просто через каждые 2000 мс по очереди включает магниты на 30 мс. И опять та же фигня - работает 10-20 раз как надо, затем начинает непрерывно дергать магнитами туда-сюда.
    Сейчас вот собрал ту же схему, но вместо магнитов подключил обыкновенные лампочки на 220В.
    Есть экран со счетчиком срабатывания и в монитор порта выводится значение millis() в момент срабатывания. Лампочки вот отморгали уже 5000 с лишним раз, однако было замечено, что оногда лампочки моргают с задержкой.
    Код (C++):
    #include <LiquidCrystal.h>
    #define MAGNET_1 6
    #define MAGNET_2 7
    boolean magnet_switch = false;
    int count;
    LiquidCrystal LCD(8, 9, 10, 11, 12, 13);

    void setup() {
      pinMode(MAGNET_1, OUTPUT);
      pinMode(MAGNET_2, OUTPUT);
      Serial.begin(9600);
      LCD.begin(16, 2);
    }

    void loop() {
      LCD.setCursor(0, 0);
      LCD.print(count);
      unsigned long timer = millis();
      if (millis() % 2000 == 0)
        if (magnet_switch == true)
        {
          Serial.println(millis()/1000);
          while (timer + 30 != millis())
            digitalWrite(MAGNET_1, HIGH);
          digitalWrite(MAGNET_1, LOW);
          magnet_switch = false;
          count++;
        }
        else
        {
          Serial.println(millis()/1000);
          while (timer + 30 != millis())
            digitalWrite(MAGNET_2, HIGH);
          digitalWrite(MAGNET_2, LOW);
          magnet_switch = true;
          count++;
        }
    }
    Стал смотреть в монитор порта и стало ясно, что по каким-то причинам срабатывания пропусаются. log.png
    В общем, помогите понять в чем проблема, пожалуйста! Вроде скетч наипримитивнейший, а в чем проблема понять не в моих силах.
    Заранее спасибо!
    И, да, не понял в какую ветку пихать этот вопрос, так что не пинайте сильно, если что)
     
  2. DIYMan

    DIYMan Guest

    Так делать не стоит, пропустите одну миллисекунду - вот вам и "задержка", т.е. пропуск цикла. Правильно - проверять на интервал:
    Код (C++):
    unsigned long past;
    const int interval = 2000;
    void setup()
    {
    past = millis():
    }

    void loop()
    {
    unsigned long now = millis();
      if(now - past > interval)
      {
        past = now;
       DO WORK;
      }
    }
    По поводу глюков - есть подозрение на помехи по питанию, которые сводят дуньку с ума. Помехи при срабатывании магнитов.
     
    arkadyf нравится это.
  3. PushKeeN

    PushKeeN Нуб

    Спасибо за ответ!
    По поводу проверки на интервал - понял, спасибо!
    Т.е. выходит, условно, что если ардуину питать от батарейки, таких глюков быть не должно?
    Как бороться? Фильтровать питание ардуины как-то?
     
  4. DIYMan

    DIYMan Guest

    1. Не обязательно, могут всё равно быть.
    2. Схемотехника - сложная штука ;) Там целая наука, реально, всё не так просто. Вот, пробегитесь: https://www.google.ru/search?q=поме.....69i57j0l5.3422j0j7&sourceid=chrome&ie=UTF-8
     
  5. PushKeeN

    PushKeeN Нуб

    Да уж)
    Еще раз спасибо за ответы! Буду читать и танцевать с бубном :)
     
  6. PushKeeN

    PushKeeN Нуб

    И снова здравствуйте!
    Отказался от использования датчика индуктивности для подсчета оборотов, заменил на энкодер 400 имп/об, который связан непосредственно с вальцами.
    Проблема с самопроизвольным срабатыванием магнитов ушла, но появилась другая - не могу добиться точности отрезания проволоки.
    Энкодер имеет 4 провода - два сигнальных, питание, земля. На один из сигнальных проводов повешено прерывание, состояние второго "считывается" по срабатыванию прерывания для определения направления вращения вальцов.
    Стал пробовать как это все работает, но столкнулся с тем, что проволока отрезается с разбросом +-7 мм.
    Стал считать: скорость подачи проволоки такова, что прерывание должно срабатывать с частотой примерно 850Гц, что, как я понял допустимо, т.к. на просторах интернета пишут, что
    .
    Код был написан кривовато - в основном цикле и на экран выводил информацию и подсчеты чисел с плавающей точкой производил, в общем, почитав про оптимизацию, от всего этого постарался по возможности избавиться. Результата не принесло.
    Затем наткнулся на статью о том, что digitalWrite и digitalRead - очень медленные функции, которые можно заменить прямой записью и чтением порта. Сделал. Результат стал немного лучше, но все равно не оправдал мои ожидания.
    В конечном итоге код пришел вот к такому виду:
    Код (C++):

    #include <LiquidCrystal.h>
    LiquidCrystal LCD(8, 9, 10, 11, 12, 13);
    unsigned long now;                                                        // Переменная для подсчета разницы во времени
    unsigned long stat_now;                                                   // Переменная для задерки вывода на экран
    volatile float current_length = 0.0;                                      // Текущее "пройденное"
    float round_length = 172.47;                                              // Длина окружности вальцов
    float impulse_length = round_length / 400.0;                              // Длина проволоки на один импульс энкодера
    int current_impulse;
    int desired_count_impulses;
    int desired_length = 1080;                                                // Конечная длина желаемой заготовки
    int strings_qty = 100;                                                    // Количество желаемых заготовок
    int current_string = 0;
    boolean working_status = false;                                           // Статус процесса (изначально остановлен)
    boolean relay = false;                                                    // Переключатель срабатывания магнитов

    float temporary_length = 0;
    int temporary_impulse;

    void setup()
    {
      attachInterrupt(0, encoder_secondary_pin, RISING);                      // Прерывание энкодера
      DDRD = B00110000;
      LCD.begin(16, 2);
      drawMenu();
      reset_magnets();
      stat_now = millis();
    }

    void loop() {
      while (working_status)
      {
        now = millis();
        if (current_impulse >= desired_count_impulses)
        {
          temporary_impulse = current_impulse;                                  // Записываем на какой длине сработал магнит
          if (relay)
          {
            while (now + 40 >= millis())
              PORTD = B00010000; //включаем и выключаем магнит на пине 4
            PORTD = B00000000;
          }
          else
          {
            while (now + 40 >= millis())
              PORTD = B00100000; //включаем и выключаем магнит на пине 5
            PORTD = B00000000;
          }
          relay = !relay;
          current_impulse = 0;
          current_string++;
        }
        if (millis() - stat_now > 200)
          draw_status();
      }
      button_read();
      changeMenu();
      selectMenu();
    }

    // Вызывается по прерыванию на первый провод энкодера для проверки состояния второго и определения направления движения вала
    void encoder_secondary_pin()
    {
      if (bitRead(PORTD, 3) == 0)
        current_impulse++;
      else
        current_impulse--;
    }

    void draw_status()
    {
      temporary_length = temporary_impulse * impulse_length;
      current_length = current_impulse * impulse_length;
      LCD.setCursor(0, 0);
      LCD.print(String(current_length, 1) + String("    "));
      LCD.setCursor(7, 0);
      LCD.print(temporary_length, 3);
      LCD.setCursor(0, 1);
      LCD.print(current_string);
      LCD.setCursor(10, 1);
      if (relay)
        LCD.print(">>>");
      else
        LCD.print("<<<");
      stat_now = millis();
    }
     
    На самом деле там еще понавешено меню с выбором длины заготовки, количества заготовок, функция обработки джойстика для оперирования меню, но приводить все это тут не стал, т.к. оно все находится вне "основного" рабочего цикла.
    Переменная temporary_length используется для определения того, в какой момент сработал магнит и вывода этой информации на экран. Что интересно, разброс чисел довольно велик - от 1079,5 до 1082, при заданной длине 1080 мм. Но на деле, реальные длины нарезанных заготовок имеют разброс 5-7 мм, что тоже мне до конца не понятно.
    В общем, пожалуйста, если кому не лень разобраться, ткните носом в проблему!
    Прошу прощения за сумбур!