ИК паяльная станция на Arduino Mega 2560. Доработка скетча "ARS_v2_Lilium_JSN"

Тема в разделе "Глядите, что я сделал", создана пользователем Jalnin, 2 ноя 2012.

Статус темы:
Закрыта.
  1. bamik

    bamik Нерд

    Я еще кое что нарыл. Не отношу себя к программистам, давно не отношу, хотя когда-то давно учился этому. Очень давно. Но пришлось всю жизнь заниматься ремонтом железа. Так вот. Нашел кусок, который явно может приводить к ошибкам. У кого-то он будет работать, а у кого-то нет.
    Кусок кода находится в sketch_feb07a1_work. Но большинство скетчей тут основаны на одном коде. Кусок такой:

    Код (C++):
    void loadProfile()//this function loads whichever profile currentProfile variable is set to
    {
      profileSteps = EEPROM.read((currentProfile - 1) * 29);
      Setpoint2 = EEPROM.read((currentProfile - 1) * 29 + 1);
      for (int i = 0; i < 9; i + 1) {
        rampRateStep[i] = EEPROM.read((currentProfile - 1) * 29 + i + 2);
        i++;
      }
      for (int i = 0; i < 9; i + 1) {
        dwellTimerStep[i] = EEPROM.read((currentProfile - 1) * 29 + i + 11) * 5;
        i++;
      }
      for (int i = 0; i < 9; i + 1) {
        temperatureStep[i] = EEPROM.read((currentProfile - 1) * 29 + i + 20);
        i++;
      }
      kp1 = EEPROM.read((currentProfile - 1) * 6 + 122);
      ki1 = EEPROM.read((currentProfile - 1) * 6 + 123);
      kd1 = EEPROM.read((currentProfile - 1) * 6 + 124);
      kp2 = EEPROM.read((currentProfile - 1) * 6 + 125);
      ki2 = EEPROM.read((currentProfile - 1) * 6 + 126);
      kd2 = EEPROM.read((currentProfile - 1) * 6 + 127);
      pwr_TOP = EEPROM.read((currentProfile - 1) * 6 + 128);
      pwr_BOTTOM = EEPROM.read((currentProfile - 1) * 6 + 129);

      return;
    }
     
    Все бы ничего, но полная неразбериха откуда какие ноги растут. А такая инициализация коэффициэнтов ограничивает реальное количество профилей. Видно, что есть базовый адрес начала профиля, а далее выбирается значение по относительному адресу. Зачем 3 цикла, все можно сделать в одном, количество значений одинаковое. Далее идет определение коэффициентов, но формула вычисления адреса другая, что затрудняет восприятие, и, кроме того, ограничивает в возможностях. Коэффициенты занимают 6 байт. Так не проще ли в смещении задать это, а формулу вычисления оставить прежней? И профайлов можно будет делать до полного заполнения флеши.
    Например, так:
    Код (C++):
    void loadProfile()//Эта функция загружает переменные заданными значениями профиля
    {
      profileSteps = EEPROM.read((currentProfile - 1) * 37);
      Setpoint2 = EEPROM.read((currentProfile - 1) * 37 + 1);
      for (int i = 0; i < 9; i + 1) {
        rampRateStep[i] = EEPROM.read((currentProfile - 1) * 37 + i + 2);
        dwellTimerStep[i] = EEPROM.read((currentProfile - 1) * 37 + i + 11) * 5;
        temperatureStep[i] = EEPROM.read((currentProfile - 1) * 37 + i + 20);
       i++;
      }
      kp1 = EEPROM.read((currentProfile - 1) * 37 + 29);
      ki1 = EEPROM.read((currentProfile - 1) * 37 + 35);
      kd1 = EEPROM.read((currentProfile - 1) * 37 + 41);
      kp2 = EEPROM.read((currentProfile - 1) * 37 + 47);
      ki2 = EEPROM.read((currentProfile - 1) * 37 + 53);
      kd2 = EEPROM.read((currentProfile - 1) * 37 + 59);
      pwr_TOP = EEPROM.read((currentProfile - 1) * 37 + 65);
      pwr_BOTTOM = EEPROM.read((currentProfile - 1) * 37 + 71);

      return;
    }
     
    Сразу видно, где что находится относительно начала профиля.
     
  2. bamik

    bamik Нерд

    Еще по настройке портов (пинов).
    pinMode(pin, mode)

    Устанавливает режим вывода (вход или выход).

    Аргументы: pin и mode.

    • pin – номер вывода;
    • mode – режим вывода.
    mode = INPUT вывод определен как вход, подтягивающий резистор отключен
    mode = INPUT_PULLUP вывод определен как вход, подтягивающий резистор подключен
    mode = OUTPUT вывод определен как выход
    Функция не возвращает ничего.


    Поэтому кусок кода:
    Код (C++):
      //setup pins as input for buttons
      pinMode (upSwitchPin, INPUT);
      digitalWrite(upSwitchPin, HIGH); //подключаем подтягивающий резистор
      pinMode (downSwitchPin, INPUT);
      digitalWrite(downSwitchPin, HIGH); //подключаем подтягивающий резистор
      pinMode (cancelSwitchPin, INPUT);
      digitalWrite(cancelSwitchPin, HIGH); //подключаем подтягивающий резистор
      pinMode (okSwitchPin, INPUT);
      digitalWrite(okSwitchPin, HIGH); //подключаем подтягивающий резистор
     
    вроде как ошибочен. Должно быть так:

    Код (C++):
      //setup pins as input for buttons
      pinMode (upSwitchPin, INPUT_PULLUP); //подключен подтягивающий резистор
      pinMode (downSwitchPin, INPUT_PULLUP); //подключен подтягивающий резистор
      pinMode (cancelSwitchPin, INPUT_PULLUP); //подключен подтягивающий резистор
      pinMode (okSwitchPin, INPUT_PULLUP); //подключен подтягивающий резистор
     
    Может быть я ошибаюсь. Поправьте.
     
  3. bamik

    bamik Нерд

    Еще.
    Код (C++):
      //Считываем состояние кнопок управления
      upSwitchState = digitalRead(upSwitchPin);
      downSwitchState = digitalRead(downSwitchPin);
      cancelSwitchState = digitalRead(cancelSwitchPin);
      okSwitchState = digitalRead(okSwitchPin);
      //----------------------------------------------------------------------------------
      if (upSwitchState == HIGH) {
        upSwitchState = LOW;
      } else {
        upSwitchState = HIGH;
     
      }
      if (downSwitchState == HIGH) {
        downSwitchState = LOW;
      } else {
        downSwitchState = HIGH;
      }
      if (cancelSwitchState == HIGH) {
        cancelSwitchState = LOW;
      } else {
        cancelSwitchState = HIGH;
      }
      if (okSwitchState == HIGH) {
        okSwitchState = LOW;
      } else {
        okSwitchState = HIGH;
      }
     
    Тут куча проверок, результатом которых является инверсия считанного состояния. Кроме того, переменные логические, а значение числовое. Неявное преобразование типов не есть хорошо. Можно сделать проще:

    Код (C++):
      //Считываем состояние кнопок управления
      upSwitchState = !digitalRead(upSwitchPin);
      downSwitchState = !digitalRead(downSwitchPin);
      cancelSwitchState =!digitalRead(cancelSwitchPin);
      okSwitchState = !digitalRead(okSwitchPin);
     
    В данном случае результат выражения логического типа.
    А вообще, лучше тут использовать какую-нибудь библиотеку по чтению кнопок. С защитой от дребезга. В теле самой программы решать такую тривиальную задачу не есть хорошо. Трудно в понимании становится. Текста много, запоминать тяжело.
     
    Последнее редактирование: 25 апр 2018
    Spilberg88, SOLOway и Dmitrysh нравится это.
  4. bamik

    bamik Нерд

    Еще ньюанс. Эту ошибку допускают многие. В коде много проверок типа:

    Код (C++):
     (millis() - ms_button)>500
    Но счетчик времени конечен. Что будет при переполнении счетчика? В нете есть пути решения данной проблемы.Требуется специальная функция для проверки, учитывающая данное обстоятельство.

    Не в качестве рекламы, но кое-что я тут подчерпнул: http://mypractic.ru/uroki-programmirovaniya-arduino-navigaciya-po-urokam Тут есть своя библиотека, дублирующая некоторые функции, например millis(), но работающие несколько быстрее. А также функции delay(), не останавливающей весь процесс.
     
  5. bamik

    bamik Нерд

    Еще бросилось в глаза отсутствие модульности. Например, вывод на экран желательно делать в одном блоке. В других блоках только подготавливать данные. Да и сам вывод желательно выделить в отдельную функцию с параметрами (столбец, строка, данные, формат). Читаемость программы улучшится.
     
    Иван_В. нравится это.
  6. bamik

    bamik Нерд

    int curCount = 0

    Это текущее положение курсора на экране?
     
  7. bamik

    bamik Нерд

    Еще есть предложение сохранять во флеше по 0 адресу номер активного профиля. Тогда при перезагрузке или повторном включении станция сможет выходить в готовность уже с требуемым профилем.
     
  8. bamik

    bamik Нерд

    Еще момент. Точность считывания аналогового сигнала - 10-14 разрядов, в зависимости от выбранного АЦП. Считанные показания типа int. Дальнейшее преобразование в double ведет к увеличению времени работы программы. Правильнее все действия выполнять в целых числах. Точности в 3 разряда вполне должно хватить. Можно использовать тип long. А все значения умножить на 1000 или 10000, что обеспечит необходимую точность. И лишь при выводе на экран ставить точку в нужном месте.
     
    Иван_В. нравится это.
  9. Dmitrysh

    Dmitrysh Гуру

    Ошибаетесь, код правильный, но ваш вариант на 10 байт меньше, а это только плюс.

    Код (C++):
    upSwitchState = !digitalRead(upSwitchPin);
      downSwitchState = !digitalRead(downSwitchPin);
      cancelSwitchState =!digitalRead(cancelSwitchPin);
      okSwitchState = !digitalRead(okSwitchPin);
    Шикарно, и код экономит.
     
    Последнее редактирование: 25 апр 2018
    SOLOway нравится это.
  10. Dmitrysh

    Dmitrysh Гуру

    Да тут что-то замудрил автор. У меня вопрос - почему коэффициенты занимают 6 байт?
     
  11. Dmitrysh

    Dmitrysh Гуру

    Разработчики утверждают, что счетчика на 50 суток хватит. Я думаю никто столько "жарить" не будет.
     
  12. Dmitrysh

    Dmitrysh Гуру

    Правильнее переписать библиотеку под целые или long. У max6675 12бит АЦП, но согласно документации для измерения температуры 0-700 градусов достаточно 8 младших бит.
     
  13. bamik

    bamik Нерд

    Тут я ошибся. в исходном коде:
    Код (C++):
      kp1 = EEPROM.read((currentProfile - 1) * 6 + 122);
      ki1 = EEPROM.read((currentProfile - 1) * 6 + 123);
      kd1 = EEPROM.read((currentProfile - 1) * 6 + 124);
      kp2 = EEPROM.read((currentProfile - 1) * 6 + 125);
      ki2 = EEPROM.read((currentProfile - 1) * 6 + 126);
      kd2 = EEPROM.read((currentProfile - 1) * 6 + 127);
      pwr_TOP = EEPROM.read((currentProfile - 1) * 6 + 128);
      pwr_BOTTOM = EEPROM.read((currentProfile - 1) * 6 + 129);
     
    Что полная чушь. Если профайл первый, то читаются байты по адресам 122-129. А коэффициенты double.
    Тип double занимает 4 байта. И значение переменной должно считываться командой EEPROM.get(), а записываться командой EEPROM.put().
     
  14. bamik

    bamik Нерд

    А если захочется использовать индикатор станции как стационарные часы? Без подогрева она почти ничего не потребляет, потому вполне логично. Но тогда скажется переполнение через 50 суток...
     
  15. bamik

    bamik Нерд

    Не разбирался с библиотеками, потому ничего не могу сказать. Пока проглядываю основной код и что-то исправляю, а что-то намечаю к дальнейшей работе.
     
  16. Dmitrysh

    Dmitrysh Гуру

    Нет, коэффициенты определены как Int, занимают 1 байт, поэтому все нормально пишет eeprom.read/write.
    А вот здесь:
    Код (C++):
    void loadProfile()//Эта функция загружает переменные заданными значениями профиля
    {
      profileSteps = EEPROM.read((currentProfile - 1) * 37);
      Setpoint2 = EEPROM.read((currentProfile - 1) * 37 + 1);
      for (int i = 0; i < 9; i + 1) {
        rampRateStep[i] = EEPROM.read((currentProfile - 1) * 37 + i + 2);
        dwellTimerStep[i] = EEPROM.read((currentProfile - 1) * 37 + i + 11) * 5;
        temperatureStep[i] = EEPROM.read((currentProfile - 1) * 37 + i + 20);
       i++;
      }
      kp1 = EEPROM.read((currentProfile - 1) * 37 + 29);
      ki1 = EEPROM.read((currentProfile - 1) * 37 + 35);
      kd1 = EEPROM.read((currentProfile - 1) * 37 + 41);
      kp2 = EEPROM.read((currentProfile - 1) * 37 + 47);
      ki2 = EEPROM.read((currentProfile - 1) * 37 + 53);
      kd2 = EEPROM.read((currentProfile - 1) * 37 + 59);
      pwr_TOP = EEPROM.read((currentProfile - 1) * 37 + 65);
      pwr_BOTTOM = EEPROM.read((currentProfile - 1) * 37 + 71);

      return;
    }
    должно быть так:
    Код (C++):
    void loadProfile()//Эта функция загружает переменные заданными значениями профиля
    {
      profileSteps = EEPROM.read((currentProfile - 1) * 37);
      Setpoint2 = EEPROM.read((currentProfile - 1) * 37 + 1);
      for (int i = 0; i < 9; i + 1) {
        rampRateStep[i] = EEPROM.read((currentProfile - 1) * 37 + i *3+ 2);
        dwellTimerStep[i] = EEPROM.read((currentProfile - 1) * 37 + i *3+ 3) * 5;
        temperatureStep[i] = EEPROM.read((currentProfile - 1) * 37 + i*3 + 4);
       i++;
      }
      kp1 = EEPROM.read((currentProfile - 1) * 37 + 29);
      ki1 = EEPROM.read((currentProfile - 1) * 37 + 30);
      kd1 = EEPROM.read((currentProfile - 1) * 37 + 31);
      kp2 = EEPROM.read((currentProfile - 1) * 37 + 32);
      ki2 = EEPROM.read((currentProfile - 1) * 37 + 33);
      kd2 = EEPROM.read((currentProfile - 1) * 37 + 34);
      pwr_TOP = EEPROM.read((currentProfile - 1) * 37 + 35);
      pwr_BOTTOM = EEPROM.read((currentProfile - 1) * 37 + 36);

      return;
    }
     
  17. bamik

    bamik Нерд

    Не верно. Коэффициенты определены как double:
    Код (C++):
    //Коэффициенты PID регулятора
    double kp1;
    double ki1;
    double kd1;
    double kp2;
    double ki2;
    double kd2;
    //Начальная температура
    double startTemp;
     
  18. bamik

    bamik Нерд

    Вообще, переменные для термопрофиля лучше загнать в массив или структуру. Тогда можно будет в цикле до длины структуры заполнять значения термопрофиля и, соответственно, сохранять их, вычисляя следующий адрес по длине предыдущего элемента. Так можно не ограничивать себя в количестве шагов. Еще можно было бы подумать о переменной длине термопрофиля.
    И значения правильней записывать и читать командами put() и get(), а не write() и read(). Особенно вредна write(). Флеш память имеет ограниченное количество перезаписей (около 100 000). Конечно, вряд ли кто достигнет такого количества, но лучше использовать в крайнем случае команду update(). По этой команде происходит запись только в том случае, если значение отличается от содержимого во флеше. По write() запись происходит всегда.
    http://radioprog.ru/post/117
     
  19. Yurik-o

    Yurik-o Нерд

    Насколько мне помнится int занимает 2 байта (0-65535, и аналогично word), а один байт занимает типы char, byte.
    Тогда уж пользоваться встроенными типами uint8_t и uint16_t.
    Вот здесь и возможны ошибки
     
  20. Dmitrysh

    Dmitrysh Гуру

    Возможно мы смотрим немного разные скетчи. На первой странице исходник "sketch_feb07a1" там так:
    Код (C++):
    int dwellTimerStep[9];

    int kp1;
    int ki1;
    int kd1;
    int kp2;
    int ki2;
    int kd2;

    int setpointRamp;
    int startTemp;
    И в наших доработанных также. Для коэффициентов ПИД, я считаю, больше и не надо. У себя я вообще ограничил их до значения 99(число).
     
    Последнее редактирование: 26 апр 2018
Статус темы:
Закрыта.