Здравствуйте. Прошу помощи/совета по коду. Имеется Arduino Uno, текстовый LCD экран 2х16 и сейсмодатчик выдающий сигналы от 0 до 5V. Хочется, чтобы сигналы поступающие от сейсмодатчика отображались визуально. Для начала пробовал сделать это с десятью светодиодами: Код (C++): #define FIRST_LED_PIN 2 #define LED_COUNT 10 void setup() { for (int i=0; i < LED_COUNT; ++i) pinMode(i + FIRST_LED_PIN, OUTPUT); } void loop() { int voltage = analogRead(A0)/1024*25; // 0V=0, 1v=5, 2V=10,..., 5V=25 for (int i = 0; i < LED_COUNT; ++i) { boolean enableSegment = (voltage >= i); digitalWrite(i+FIRST_LED_PIN, enableSegment); } } Все работало как надо. Теперь решил вместо светодиодов использовать LCD экран: Код (C++): #include <LiquidCrystal.h> LiquidCrystal lcd(8, 9, 10, 11, 12, 13); void setup() { lcd.begin(16, 2); } void loop() { int voltage = analogRead(A0)/1024*25; // 0V=0, 1v=5, 2V=10,..., 5V=25 for (int i = 0; i < 16; ++i) { if (voltage>i) { lcd.setCursor(i,0); lcd.print("\xFF"); //FF - закрашенная клетка } else { lcd.setCursor(i,0); lcd.print("\x20"); //20 - пустая клетка } } } Этот код не работает - просто ничего не отображается на экране. Как мне кажется, дальше одной итерации в void loop дело не идет, так как при изменении условия в if (voltage>i) на if (voltage>=i) загорается только первый символ. Пробовал так же программу следующего вида: Код (C++): #include <LiquidCrystal.h> #define LED_COUNT 16 LiquidCrystal lcd(8, 9, 10, 11, 12, 13); void setup() { lcd.begin(16, 2); } void loop() { float voltage = analogRead(A0) / 1024.00 * 5.00; lcd.setCursor(0,0); if (voltage <= 0.02) {lcd.print("\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20");}; if (voltage > 0.02 && voltage <= 0.04) {lcd.print("\xFF\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20");}; if (voltage > 0.04 && voltage <= 0.06) {lcd.print("\xFF\xFF\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20");}; if (voltage > 0.06 && voltage <= 0.08) {lcd.print("\xFF\xFF\xFF\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20");}; if (voltage > 0.08 && voltage <= 0.10) {lcd.print("\xFF\xFF\xFF\xFF\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20");}; if (voltage > 0.10 && voltage <= 0.12) {lcd.print("\xFF\xFF\xFF\xFF\xFF\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20");}; if (voltage > 0.12 && voltage <= 0.14) {lcd.print("\xFF\xFF\xFF\xFF\xFF\xFF\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20");}; if (voltage > 0.14 && voltage <= 0.16) {lcd.print("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x20\x20\x20\x20\x20\x20\x20\x20\x20");}; if (voltage > 0.16 && voltage <= 0.18) {lcd.print("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x20\x20\x20\x20\x20\x20\x20\x20");}; if (voltage > 0.18 && voltage <= 0.20) {lcd.print("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x20\x20\x20\x20\x20\x20\x20");}; if (voltage > 0.20 && voltage <= 0.22) {lcd.print("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x20\x20\x20\x20\x20\x20");}; if (voltage > 0.22 && voltage <= 0.24) {lcd.print("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x20\x20\x20\x20\x20");}; if (voltage > 0.24 && voltage <= 0.26) {lcd.print("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x20\x20\x20\x20");}; if (voltage > 0.26 && voltage <= 0.28) {lcd.print("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x20\x20\x20");}; if (voltage > 0.28 && voltage <= 0.30) {lcd.print("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x20\x20");}; if (voltage > 0.30 && voltage <= 0.32) {lcd.print("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x20");}; if (voltage > 0.32) {lcd.print("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF");}; } Минус этого кода - не успевают прорисоваться клетки при наличии сигнала. Например если появляется сигнал соответствуемый 8 клеткам, то первая клетка закрашена только в верхней части ячейки, вторая клетка чуть ближе к середине, третья в средней части и т.д. Прошу подсказать, где есть ошибка в коде или в каком направлении двигаться дальше.
Код (C++): #include <LiquidCrystal.h> #define MAX_CELLS_COUNT 16 // количество закрашенных клетов при максимальном заполнении #define MAX_SENSOR_VOLTAGE 25 // максимально возможное значение с сенсора LiquidCrystal lcd(8, 9, 10, 11, 12, 13); void setup() { lcd.begin(16, 2); } void loop() { int voltage = analogRead(A0)/1024*25; // 0V=0, 1v=5, 2V=10,..., 5V=25 byte cellsCount = map(voltage, 0, MAX_SENSOR_VOLTAGE, 0, MAX_CELLS_COUNT); // масштабируем диапазон 0...25 с датчика в количество клеток 0...16 for (byte i = 0; i < MAX_CELLS_COUNT; i++) { lcd.setCursor(i,0); if (i <= cellsCount) lcd.print("\xFF"); //FF - закрашенная клетка else lcd.print("\x20"); //20 - пустая клетка } }
Это первое, что я пытался сделать. И это не годится. Так как получается, что значения сигнала берутся раз в 5000 мс, а это очень низкая частота, т.е. допустим, если на начало 0 мс было значение 0.2V, на 2500 мс - 3V, а на 5000 мс - снова 0.2V. То отображаться будет только 0.2V, но никак не 3V. Меньшие значение delay тоже не годятся. Экран перестает "тормозить" при значениях больше 100мс, но эти значения уже не годятся для отображения сигналов с датчика (слишком большая задержка). Здесь я так понял добавили функцию пропорционального переноса значений map() (я не стал ее использовать, так как значения voltage больше 16 не так важны), используется переменная типа byte. Но эта программа работает точно так же, как и моя первоначальная - т.е. никак, загорается только первая ячейка экрана. Согласен. Хорошо, что в данном случае эта погрешность несильно влияет.
Экран быстрее работать не будет. Так что либо ищите вариант на свтодиодах. Либо оптимизируйте алогоритм индикации. Опрашивать датчик можно часто, а при выводе на индикатор выводить максимум из значений, полученных за последнюю секунду. Этот алгоритм и для светодиодов полезен будет, вместо едва заметной вспышки будет отчетливая индикация каждого всплеска.
Что то никак не получается реализовать расчет максимального значения за определенный промежуток времени. Для начала я упростил код до следующего вида: Код (C++): #include <LiquidCrystal.h> LiquidCrystal lcd(8, 9, 10, 11, 12, 13); float maxvol=0; void setup() { lcd.begin(16,2); } void loop() { float voltage = analogRead(A0)/1023.00*5.00; //voltage - текущие значения в V maxvol = max(maxvol, voltage); //maxvol - максимальное значение voltage lcd.setCursor(0,0); lcd.print(maxvol); lcd.setCursor(0,1); lcd.print(voltage); delay (100); } Здесь на первой строке LCD-экрана отображается максимальное значение которое когда-либо было с момента запуска, а на второй строке - текущее значение в реальном времени. Выводить маскимальное значение за промежуток времени никак не получается. Вот например код с использованием цикла while: Код (C++): #include <LiquidCrystal.h> LiquidCrystal lcd(8, 9, 10, 11, 12, 13); float maxvol=0; void setup() { lcd.begin(16,2); } void loop() { float voltage = analogRead(A0)/1023.00*5.00; //voltage - текущие значения в V unsigned long currentMillis = millis(); //текущее время с момента запуска long previousMillis = 0; // предыдущее значение времени while(currentMillis-previousMillis < 1000) //пока не пройдет 1000 мс { maxvol = max(maxvol, voltage); //вычисляем максимальное значение voltage } previousMillis = currentMillis; //по завершению цикла while устанавливаем в previousMillis значение currentMillis, lcd.setCursor(0,0); //т.е. должны присваиваться значения 1000, 2000, 3000,... lcd.print(maxvol); lcd.setCursor(0,1); lcd.print(voltage); // delay (100); } С этим кодом на экране ничего не отображается. Так же пытался написать код с использованием if...else. Выходит та же самая ситуация. Что может быть неверным в приведенном мною коде? Или может есть другие способы вычисления за промежуток времени?
В первом коде нет сброса максимального значения в ноль каждую секунуду. Первый код можно исправить, добавив счетчик измерений (целочисленную переменную). Как только счетчик достигнет десяти, обнулить максимальное значение и обнулить счетчик. Но. В первом коде используется delay(), а это задержка при которой микроконтроллер ничего не делает. Правильный подход во втором примере. Только посмотрите внимательно на тело цикла while, в нем выполняется единственное действие - поиск максимума из двух величин, а сами величины не изменяются. Должно быть два действия - 1. чтение датчика 2. поиск максимума
Может я чего не понял, но почему нельзя максимально значение получать более простым путем? Код (C++): if (voltage > maxvol) {maxvol = voltage} Может имеет смысл замер вынести из loop и повесить на прерывание от таймера? В обработчке прерывания написать Код (C++): voltage = analogRead(A0)/1023.00*5.00;//voltage - текущие значения в V А переменную объявить как глобальную volatile int voltage = 0;
Чем этот путь легче? Это просто эквивалентная запись. Тогда и поиск максимума надо в прерывании делать. Но я бы не советовал, отлаживать прерывания сложнее, какого-то преимущества они не дадут, тем более что ТС в двух циклах пока путается.
По поводу того, что в первом коде не было сброса значений - это изначально было такой целью, показать что поиск максимального значения работает. А вот уже во втором коде я пытался реализовать подсчет за промежуток времени. По Вашему замечанию изменил второй код, но результат остается прежним, ничего не отображается. Такое ощущение что программа не выходит из цикла while. Код (C++): #include <LiquidCrystal.h> LiquidCrystal lcd(8, 9, 10, 11, 12, 13); float maxvol = 0; float voltage = 0; unsigned long currentMillis = 0; long previousMillis = 0; void setup() { lcd.begin(16,2); } void loop() { unsigned long currentMillis = millis(); //текущее время с момента запуска while(currentMillis-previousMillis < 1000) //пока не пройдет 1000 мс { float voltage = analogRead(A0)/1023.00*5.00; //voltage - текущие значения в V maxvol = max(maxvol, voltage); //вычисляем максимальное значение voltage } previousMillis = currentMillis; //по завершению цикла while устанавливаем в previousMillis значение currentMillis, lcd.setCursor(0,0); //т.е. должны присваиваться значения 1000, 2000, 3000,... lcd.print(maxvol); lcd.setCursor(0,1); lcd.print(voltage); }
Внутри while у вас ни currentMillis ни previousMillis не меняется вообще. К тому же currentMillis объявлен дважды, как глобальный и как локальный.
Можно и таким способом получать максимальное значение, но результат один и тот же. По поводу выноса из loop: эта ситуация простая, я полагаю возможно сделать это в рамках одной функции, не усложняя код.
Точно! Дело сдивнулось с мертвой точки: Код (C++): #include <LiquidCrystal.h> LiquidCrystal lcd(8, 9, 10, 11, 12, 13); float maxvol; float voltage; void setup() { lcd.begin(16,2); } void loop() { maxvol = 0; unsigned long currentMillis = 0; unsigned long previousMillis = 0; while(currentMillis-previousMillis < 1000) //пока не пройдет 1000 мс { currentMillis = millis(); //текущее время с момента запуска float voltage = analogRead(A0)/1023.00*5.00; //voltage - текущие значения в V maxvol = max(maxvol, voltage); //вычисляем максимальное значение voltage } previousMillis = currentMillis; //по завершению цикла while устанавливаем в previousMillis значение currentMillis, lcd.setCursor(0,0); //т.е. должны присваиваться значения 1000, 2000, 3000,... lcd.print(maxvol); lcd.setCursor(0,1); lcd.print(voltage); } Теперь стали отображаться максимальное значение на 1 строчке и значение 0.00 на второй строчке (здесь понятно, нужно объявить как найти значение voltage в теле void loop, но по моему предположению, в случае с этим кодом voltage должен быть с последним значением выведенном в цикле while, или нет?). Но, максимальное значение отображается не корректно - оно не ищет максимум за промежуток времени, а показывает значение в реальном времени. Так как изменяя значение в while(currentMillis-previousMillis < 1000) с 1000 на 5000, и загружая ее в Arduino первые 5000 мс (5 секунд) ничего не происходит, а затем значения "дергаются" в реальном времени, а не изменяясь каждые 5 секунд. Здесь чтото не так с циклом while. Как мне кажется этот цикл запускатеся один раз пока currentMillis-previousMillis действительно меньше 5000, но уже при следующей повторной итерации это условие выполняется сразу. Предполагается вычислять значение maxvol за 1 сек (или за любой другой промежуток времени). Т.е. показывать максимальное значение voltage, которое было в течении одной секунды.
Именно. И поскольку большую часть времени его значение равно нулю, за исключением очень коротких всплесков, которые и надо поймать, на индикаторе виден ноль. Уберите переменную currentMillis, вместо неё используйте прямое обращение к millis(), это упростит скетч и уберет логическую ошибку. А переменная previousMillis должна быть глобальной, т.е. её объявление надо вынести из тела функции.
Не вникая в логику того, чего вы хотите получить, может так понятнее код станет для вас? Код (C++): #include <LiquidCrystal.h> LiquidCrystal lcd(8, 9, 10, 11, 12, 13); float maxvol; float voltage; unsigned long previousMillis = 0; void setup() { lcd.begin(16,2); } void data2display() { lcd.setCursor(0,0); //т.е. должны присваиваться значения 1000, 2000, 3000,... lcd.print(maxvol); lcd.setCursor(0,1); lcd.print(voltage); } void measure() { maxvol = 0; while(millis()-previousMillis < 1000) //пока не пройдет 1000 мс { voltage = analogRead(A0)/1023.00*5.00; //voltage - текущие значения в V maxvol = max(maxvol, voltage); //вычисляем максимальное значение voltage } previousMillis = millis(); } void loop() { measure(); data2display(); } Вы почему то упорно дважды объявляете переменные.
Скорректировал код по авшим замечаниям: Код (C++): #include <LiquidCrystal.h> LiquidCrystal lcd(8, 9, 10, 11, 12, 13); float maxvol; float voltage; unsigned long previousMillis = 0; void setup() { lcd.begin(16,2); } void loop() { maxvol = 0; while(millis()-previousMillis < 2000) //пока не пройдет 1000 мс { voltage = analogRead(A0)/1023.00*5.00; //voltage - текущие значения в V maxvol = max(maxvol, voltage); //вычисляем максимальное значение voltage } previousMillis = millis(); lcd.setCursor(0,0); lcd.print(maxvol); //lcd.setCursor(0,1); //lcd.print(voltage); } Теперь все работает как надо, по крайней мере та задача, которая стояла первоначально, решена. Спасибо за помощь! Ваш код получилось идентичен коду выше, но действительно этот код нагляднее и проще для восприятия. По крайней мере мне стало понятно как можно использовать другие функции. По поводу двойного объявления переменных - согласен, допустил ошибку. Тоже прояснилось как их объявлять, если они используются в разных частях функций, циклов. Благодарю за советы! Теперь буду дорабатывать код для отображения сигналов в линейном виде, а не в численном, но это уже я думаю не составит труда.