Передача данных с Arduino в Lazarus

Тема в разделе "Arduino & Shields", создана пользователем Eugene Light, 11 май 2016.

Метки:
  1. Eugene Light

    Eugene Light Нуб

    Доброго времени суток всем!
    Помогите найти ошибку. Пытаюсь организовать таймер, который создавался бы тактовым генератором микросхемы и отображался в программе (которая пишется в среде Lazarus).
    В Arduino IDE использую millis.
    Код (C++):
    unsigned long currentMillis = millis(); // текущее время в миллисекундах
     
      if(currentMillis - previousMillis > interval) {
        // сохраняем последний момент, когда менялось состояние светодиода
        previousMillis = currentMillis;

        Serial.write("Y");                    // отправлем сигнал таймера на Комп
        Serial.flush();                       // Очищаем буфер
    В Lazarus использую SdpoSerial1.ReadData.
    Код (C++):
    while i<30000 do begin
        d := SdpoSerial1.ReadData;      // записываем в D данные с COM-порта
        if d <> 'no' then begin         // если с COM-порта пришли данные
           p := p + 1;                  // увеличиваем счётчик P
           if p = 500 then begin        // если накапливается 1000 мс
             s := s + 1;                // увеличиваем количество секунд
             if s = 60 then begin       // если накапливается 60 с
               m := m + 1;              // увеличиваем количество минут
               s := 0;                  // обнуляем количество секунд
             end;
             p := 0;                    // обнуляем счётчик P
           end;
        end;
        Label1.Caption := IntToStr(s);  // выводим секунды на экран
        Label2.Caption := IntToStr(m);  // выводим минуты на экран
        i := i + 1;                     // увеличиваем значение счётчика
        d := 'no';                      // обнуляем значение D
      end;
    Хотел получить счётчик, который "тикал" бы каждую секунду, а получил программу, которая думает несколько секунд и выдаёт, что минута прошла...
     
  2. Volk65

    Volk65 Нуб

    А ардуина через какой интервал выдает "Y" ? Чему равна переменная/константа interval?
     
  3. Eugene Light

    Eugene Light Нуб

    Интервал составляет 2 мс.
     
  4. Volk65

    Volk65 Нуб

    В коде паскаля, когда вы читаете данные в строке SdpoSerial1.ReadData, а данных еще нет, то ReadData скорее всего возвращает пустую строку. А пустая строка не равна "no", и поэтому выполняется увеличение секунд и т.д. И не успеет пройти 2мс, как цикл 30000 раз выполнится и завершится.
    Тут более правильнее сравнивать константу с принятой строкой, а не с отличием константы от принятой строки. Т.е. лучше так: if d = 'Y' then begin

    Хотя тут можно словить помеху, которая будет принята как "Y". Тут лучше посылать последовательность.
     
  5. fogary

    fogary Гик

    Если позволите, несколько замечаний по коду на Паскале.

    В данном случае, нет никаких оснований использовать цикл while вместо for.

    Присвоение d := 'no'; лишено всякого смысла, т. к. переменная тут же будет переписана в строке d :=
    SdpoSerial1.ReadData; потому что функция вернет либо данные прочитанные из порта, либо пустую строку.

    Вывод значений минут и секунд логичнее делать после изменения соответствующих переменных, а не при каждом проходе цикла.
     
  6. Eugene Light

    Eugene Light Нуб

    1. А в чём преимущество цикла For в данной ситуации?
    2. Присвоение d := 'no' использовалось для того, чтобы при отсутствии сигнала с Arduino таймер на Компе останавливался.
    3. Значения секунд и минут изменяются по ходу цикла, так что делать вывод вне цикла смысла не имеет.
     
  7. Eugene Light

    Eugene Light Нуб

    Спасибо, сейчас попробую =)
     
  8. fogary

    fogary Гик

    Приведу цитату (опустив примеры кода) из Кернигана и Ритчи:
    Я просто хотел довести до Вас, что в момент проверки значения этой переменной, она никогда не будет иметь значение 'no'.

    Вот смотрите: перед циклом while, Вы наверняка присваиваете переменной d значение 'no'. Дальше начинается цикл. Первый оператор в теле цикла присваивает переменной d значение. которое возвращает функция SdpoSerial1.ReadData. Эта функция возвращает либо строку, с данными из буфера порта, либо пустую строку, если буфер порта пуст. Таким образом, на момент проверки содержимого переменной d, переменная будет содержать или значение 'Y', или пустую строку. В конце цикла у Вас снова стоит присвоение d := 'no'. Надеюсь понятно, что произойдет в начале следующей итерации цикла?

    Вы меня не правильно поняли. Я имел ввиду примерно следующее:
    Код (C++):
        if d <> 'no' then begin
           p := p + 1;
           if p = 500 then begin
             s := s + 1;
             if s = 60 then begin
               m := m + 1;
               Label2.Caption := IntToStr(m);
               s := 0;
             end;
             Label1.Caption := IntToStr(s);
             p := 0;
           end;
        end;
        i := i + 1;
     
  9. Eugene Light

    Eugene Light Нуб

    Увы, проблема сохранилась... Программа получает данные из порта непостоянно, отрывками (то не получает ничего, то получает сразу 2 символа). Счётчик в итоге толком не двигается...
     
  10. Eugene Light

    Eugene Light Нуб

    Да, я уже понял =)

    Теперь понял. Спасибо за вариант, учту =)
     
  11. Volk65

    Volk65 Нуб

    Увеличьте задержку на ардуино до 40 мс, ну и соответственно не забыть поменять if p = 500 then на
    if p = 25 then
     
  12. Eugene Light

    Eugene Light Нуб

    Данные передаются, программа думает и выдаёт, что прошло 27 секунд. Она по-прежнему не показывает каждую секунду.
     
  13. Volk65

    Volk65 Нуб

    У меня в Delphi мой код работает (правда я эмулирую прием данных из COM порта каждые 40мс):
    Код (C++):
     
      while m<5 do begin
       // Функция ReadData - получает раз в 40 мс "Y", в остальное время ""
        d := ReadData;      // записываем в D данные с COM-порта
        if d = 'Y' then begin         // если с COM-порта пришли данные
           p := p + 1;                  // увеличиваем счётчик P
           if p = 25 then begin        // если накапливается 1000 мс
             s := s + 1;                // увеличиваем количество секунд
             if s = 60 then begin       // если накапливается 60 с
               m := m + 1;              // увеличиваем количество минут
               s := 0;                  // обнуляем количество секунд
             end;
             Label1.Caption := IntToStr(s);  // выводим секунды на экран
             Label2.Caption := IntToStr(m);  // выводим минуты на экран
             p := 0;                    // обнуляем счётчик P
           end;
        end;
    //    Application.ProcessMessages;
      end;
     
    Вернусь домой могу проверить с ардуино. Но в том коде (для ардуино), который вы привели нет ничего необычного... Должно работать.
     
  14. fogary

    fogary Гик

    Попробуйте чтение порта повесить на обработчик события OnRxData.

    Пробовал повторить Вашу программу в Delphi. При чтении порта из цикла, данные читались, но на экран не выводились. Программа просто зависала. Повесил чтение порта на обработчик события - все заработало как надо.
     
  15. Eugene Light

    Eugene Light Нуб

    Большое спасибо, появился ожидаемый прогресс =)