Доброго времени суток всем! Помогите найти ошибку. Пытаюсь организовать таймер, который создавался бы тактовым генератором микросхемы и отображался в программе (которая пишется в среде 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; Хотел получить счётчик, который "тикал" бы каждую секунду, а получил программу, которая думает несколько секунд и выдаёт, что минута прошла...
В коде паскаля, когда вы читаете данные в строке SdpoSerial1.ReadData, а данных еще нет, то ReadData скорее всего возвращает пустую строку. А пустая строка не равна "no", и поэтому выполняется увеличение секунд и т.д. И не успеет пройти 2мс, как цикл 30000 раз выполнится и завершится. Тут более правильнее сравнивать константу с принятой строкой, а не с отличием константы от принятой строки. Т.е. лучше так: if d = 'Y' then begin Хотя тут можно словить помеху, которая будет принята как "Y". Тут лучше посылать последовательность.
Если позволите, несколько замечаний по коду на Паскале. В данном случае, нет никаких оснований использовать цикл while вместо for. Присвоение d := 'no'; лишено всякого смысла, т. к. переменная тут же будет переписана в строке d := SdpoSerial1.ReadData; потому что функция вернет либо данные прочитанные из порта, либо пустую строку. Вывод значений минут и секунд логичнее делать после изменения соответствующих переменных, а не при каждом проходе цикла.
1. А в чём преимущество цикла For в данной ситуации? 2. Присвоение d := 'no' использовалось для того, чтобы при отсутствии сигнала с Arduino таймер на Компе останавливался. 3. Значения секунд и минут изменяются по ходу цикла, так что делать вывод вне цикла смысла не имеет.
Приведу цитату (опустив примеры кода) из Кернигана и Ритчи: Я просто хотел довести до Вас, что в момент проверки значения этой переменной, она никогда не будет иметь значение '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;
Увы, проблема сохранилась... Программа получает данные из порта непостоянно, отрывками (то не получает ничего, то получает сразу 2 символа). Счётчик в итоге толком не двигается...
Увеличьте задержку на ардуино до 40 мс, ну и соответственно не забыть поменять if p = 500 then на if p = 25 then
Данные передаются, программа думает и выдаёт, что прошло 27 секунд. Она по-прежнему не показывает каждую секунду.
У меня в 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; Вернусь домой могу проверить с ардуино. Но в том коде (для ардуино), который вы привели нет ничего необычного... Должно работать.
Попробуйте чтение порта повесить на обработчик события OnRxData. Пробовал повторить Вашу программу в Delphi. При чтении порта из цикла, данные читались, но на экран не выводились. Программа просто зависала. Повесил чтение порта на обработчик события - все заработало как надо.