Вывод нескольких значений переменных на LCD 1602 Ардуино.
- Войдите на сайт для отправки комментариев
Ср, 13/04/2016 - 14:29
Столкнулся с такой задачей. Чтобы сэкономить место на экране LCD решил выводить 3 значения по очереди друг на друга на 10 секунд, но без использования delay. Пытался использовать функцию millis(), но получается в начале экран обновляет значения каждые 10 секунд и после того, как доходит до последнего значения просто пустой экран, т.е. не показывает обратно первое значение. Кто-нибудь побывал обновлять информацию на экране каждые 10 секунд без использования delay?
// -------------------------------------------------------------------------------------------- // обновление экрана, это последнее... time_pause = getDelayTime(time_anime, time_loop_new); if (time_pause > TIME_ANIME) { time_anime = time_loop_new; screen_out(true, false, false); } else { screen_out(false, false, false); } ---------------------------------------------------------------------------------------------------------------- void screen_out(boolean anime, boolean face, boolean force) { byte ii; // анимацию выводим в первую строку if (anime == true) { // делаем шаг анимации if (step_anime < 3) { step_anime = step_anime + 1; } else { step_anime = 0; } // выводим текущий шак на экран if (step_anime == 0) { PrintLcd("|", 0, 0); } else if (step_anime == 1) { PrintLcd("/", 0, 0); } else if (step_anime == 2) { PrintLcd("-", 0, 0); } else { PrintLcd("-", 0, 0); } }Кто-нибудь побывал обновлять информацию на экране каждые 10 секунд без использования delay?
Нет. 9 пробовали, 11 пробовали, а 10 - Вы первый :))))
Вы бы хоть скетч выложили, что-ли, а то что обсуждать. Пробовали или нет? Ну, пробовали, дальше что?
класс титановый велосипед для delay без delay().
Вот так скажу точно не работает) Но выводит вначале каждые 10 сек. if (m==0) //переменная m=0 { //отображаем lcd.setCursor(0, 1); //*P1=p1 P2=p2 LED=0* if (CurrentMil - previousMil > 30000){ lcd.print("P1="); //******************* lcd.print(p1); previousMil=CurrentMil;} else if (CurrentMil - previousMil > 20000){ lcd.print(" P2="); lcd.print(p2);} if (CurrentMil - previousMil > 10){ lcd.print(" LED="); lcd.print(p3);} }алё! коматоз! класс титановый велосипед для delay без delay().
А сама функция где?
Вы не ругайтесь, я ваше сообщение уже увидел и следовательно скоро его прочитаю.
сейчас меня опять закидают помидорами, что вторая часть не нужна, но я буду непреклонен :)
//************************************************************************************************* // процедура сравнивает два времени и возвращает разницу в виде числа, учитывает переход времени через 0 // start_time - начальное время // end_time - конечное время // // !!!! процедура чуствительна к разрядности исполняемого кода !!!! // !!!! процедура может работать неправильно при двойном переходе времени через 0 !!!! //************************************************************************************************* unsigned long getDelayTime(unsigned long start_time, unsigned long end_time) { unsigned long result; if (start_time <= end_time) { result = end_time - start_time; } else { result = 4294967295 - end_time + start_time; } return result; }А вот так:
if (m==0) { lcd.setCursor(0, 1); //*P1=p1 P2=p2 LED=0* everyMillis(30000, { static uint8_t varNum = 0; switch( varNum){ case 0: lcd.print("P1="); lcd.print(p1); break; case 1: lcd.print(" P2="); lcd.print(p2); break; case 2: lcd.print(" LED="); lcd.print(p3);} break; } if( ++varNum >= 3 ) varNum = 0; }); }:)
Что такое everyMillis, можете объяснить? То есть странно, что там после запятая и фигурная скобка.
В общем-то уже давненько типовой макрос, предложенный тут автором Leshak. Выглядит у меня примерно так:
#define everyMillis(interval, action) \ { \ static unsigned long t = 0UL; \ if( millis() - t > (interval) ) \ { \ { action; } \ t = millis(); \ } \ }Поскольку action подставляется "как есть", то вполне допустимо писать блок кода "как-бы" в параметре макроса, что я вам и показал. Это наглядно и удобно. Можно офромить в функцию, но тогда в макрос надо подставлять не её имя, а вызов, типа: everyMillis(wait, myFunc() );
А, в целом, вызов millis() - дорогое удовольствие. Можно напрямую пользоваться счетчиком переполнения таймера для тех же самых целей, только помнить о том, что он считает не по 1000микросекунд (1миллисек.) а по 1024мксек .. и задержка в 1 сек. будет равна 976 "тиков", а не 1000. :)
На гитхабе есть arhat.h и там есть и макрос everyOVF(), который "дешевле", чем everyMillis() с точки зрения МК.
Спасибо большое! Буду разбираться дальше сам.
почему millis() дорогое удовольствие ??? ведь это асамленый однотактовый MOV.... ну плюс на стек и переход 3 такта....
почему millis() дорогое удовольствие ???
Пошёл за попкорном!
Сейчас Вам объяснят, что всё, что поставляется с ардуиновским IDE жутко дорого в то время как arhat.h - почти бесплатно :))))
ведь это асамленый однотактовый MOV.... ну плюс на стек и переход 3 такта....
Ну, не то, чтобы совсем уж так ... и регистр сохранеят/восстанавливает, да и запись long'а не совсем однотактовая :)
unsigned long millis() { unsigned long m; uint8_t oldSREG = SREG; // disable interrupts while we read timer0_millis or we might get an // inconsistent value (e.g. in the middle of a write to timer0_millis) cli(); m = timer0_millis; SREG = oldSREG; return m; }Обойдетесь. "дураков учить - только портить". :)
Обойдетесь. "дураков учить - только портить". :)
ок. дураков дураки портили.
Можете помочь? Для меня макросы в С/C++ это что-то новое. То есть, как я понял, макрос запустит выполнение определенной функции с заданным параметром? А как все это должно работать?
ну хорошо, идем по порядку
pop, push - не знаю сколько будет, предположительно от 0 до 2*КоличествоРегистров, скорее всего будет типа 16 по числу регистров
jmp - 1
выделение памяти кучи - 2х2
cli() - не помню, вроде 1
mov - скорее всего пойдут через dword регистр это будет 4 такта * 2
возврат - 1 такт
итого примерно от 15 до 30 тактов...
Обойдетесь. "дураков учить - только портить". :)
Хрена-се! А я уже попкорном затарился! Пропадёт же теперь! Компенсация-то хоть будет?
единственный минус millis() это то, что он на несколько тактов замедляет таймер, а на таймер завязан последовательный порт, и при излишнем использование может нарушится работа порта...
других минусов для систем которые не опреируют временем точнее 0.01 сек я не вижу...
Открываете "кондуит" и читаете про препроцессор С. Можно тупо погуглить.
Нет. Макрос - это "макроподстановка". То есть его тело подставляется в текст программы по некоторым, достаточно простым правилам и уже после выполнения всех подстановок компилируется то, что получилось в результате. Откройте библиотечные и стандартные *.h файлы и обнаружьте там для себя много нового. Макросы препроцессора там "сплошь и рядом". :)
ну хорошо, идем по порядку
Экой Вы непонятливый! :)))
Куда идёте, зачем идёте? Вам же объяснили, что кошерно то, что делается через arhat.h, а то, что поставляется с ардуиновским IDE - это кака, бяка и вообще bullshit, как выражатся наши американские партнёры!
Это ещё, что! Вот если Вы, не дай Бог, конечно, вздумаете какой-нибудь класс использовать, то Вы будете "обязаны предоставить г-ну Архату листинг, доказывающий отстуствие удорожания кода" - во как! А Вы тут с глупостЯми ... :))))
Кстати, у вас там после вывода переменной больше ничего нет. Если количество цифирек сильно меняется, то вы будете наблюдать хвосты от предыдущих выводов. А очистка экрана - "ну совсем дорогое удовольствие". Поэтому рекомендую дополнить код выводом 1-2 пробелов после вывода переменной, типа так:
if (m==0) { lcd.setCursor(0, 1); //*P1=p1 P2=p2 LED=0* everyMillis(30000, { static uint8_t varNum = 0; static char * names[] = {"P1=", "P2=", "LED="}; char * tmpName; VarType tmpVar; tmpName = names[varNum]; switch( varNum ){ case 0: tmpVar = p1; break; case 1: tmpVar = p2; break; case 2: tmpVar = p3; break; } lcd.print(tmpName); lcd.print(tmpVar); lcd.print(" "); if( ++varNum >= 3 ) varNum = 0; }); }Да, и заодно сократил код за избыточностью, если все переменные одного типа. VarType - тип переменных.
Выдает вот такие ошибки:
Вот сам код:
#define everyMillis(interval, action) \ { \ static unsigned long t = 0UL; \ if( millis() - t > (interval) ) \ { \ { action; } \ t = millis(); \ } \ } #include <Wire.h> #include <LiquidCrystal_I2C.h> //Библиотека LCD // инициализация LCD LiquidCrystal_I2C lcd(0x3F, 16, 2); int m = 0; void setup() { Serial.begin(9600); lcd.begin(); // Вывод приветствия при включении питания если нужно lcd.setCursor(3, 0); lcd.print("DEMO MENU"); lcd.clear(); delay (300);//Задержка приветствия } void loop() { if (m==0) { lcd.setCursor(0, 1); //*P1=p1 P2=p2 LED=0* everyMillis(30000, { static uint8_t varNum = 0; static char * names[] = {"P1=", "P2=", "LED="}; char * tmpName; VarType tmpVar; tmpName = names[varNum]; switch( varNum ){ case 0: tmpVar = p1; break; case 1: tmpVar = p2; break; case 2: tmpVar = p3; break; } lcd.print(tmpName); lcd.print(tmpVar); lcd.print(" "); if( ++varNum >= 3 ) varNum = 0; }); } }Вроде работает:
#include <Wire.h> #include <LiquidCrystal_I2C.h> //Библиотека LCD #define everyMillis(interval, action) \ { \ static unsigned long t = 0UL; \ if( millis() - t > (interval) ) \ { \ { action; } \ t = millis(); \ } \ } // инициализация LCD LiquidCrystal_I2C lcd(0x3F, 16, 2); int m = 0; int p1=0; int p2=0; int p3=0; int varNum = 0; void setup() { Serial.begin(9600); lcd.begin(); // Вывод приветствия при включении питания если нужно lcd.setCursor(3, 0); lcd.print("DEMO MENU"); lcd.clear(); delay (300);//Задержка приветствия } void funk(int varNumber) { static char * names[] = {"P1=", "P2=", "LED="}; char * tmpName; int tmpVar; tmpName = names[varNumber]; switch( varNumber ){ case 0: tmpVar = p1; break; case 1: tmpVar = p2; break; case 2: tmpVar = p3; break; } lcd.print(tmpName); lcd.print(tmpVar); lcd.print(" "); } void loop() { lcd.setCursor(0, 1); //*P1=p1 P2=p2 LED=0* everyMillis(300,funk(varNum)); if( ++varNum >= 3 ) varNum = 0; }Сейчас попробую в основной программе применить.
Все прекрасно робит :)
ну хорошо, идем по порядку
Экой Вы непонятливый! :)))
Куда идёте, зачем идёте? Вам же объяснили, что кошерно то, что делается через arhat.h, а то, что поставляется с ардуиновским IDE - это кака, бяка и вообще bullshit, как выражатся наши американские партнёры!
Это ещё, что! Вот если Вы, не дай Бог, конечно, вздумаете какой-нибудь класс использовать, то Вы будете "обязаны предоставить г-ну Архату листинг, доказывающий отстуствие удорожания кода" - во как! А Вы тут с глупостЯми ... :))))
ну я как-бы не новичек совсем, начнем с диплома по Clarion в далеком 1992м году... и мне глубоко покласть на НЕАГУМЕНТИРОВАНЫЕ мнения идущие в разрез с моим опытом, в то-же время я не против учится и принимаю логичные аргументы.
Да я не системник, я чистейший прикладник в програмировании и в вещах уровня "железа" я слаб (хотя мне принадлежит например одна прога защиты компа от несанкционированого доступа, написана на асемблере в 91м и использовалась некоторое время в Бауманке), но с точки зрения стратегии когда надо применять те или иные методы я точно не новичек и не слабак...
Про вопрос "удорожания" кода, я отвечу так: код (как и картину) можно вылизывать годами и идела все равно не будет, нужно уметь остановится достигнув приемлемых результатов. А вообще люди которые в угоду экономии 3х машинных циклов и 10 байтов начинают переписывать типой код у меня кроме сожаления не вызывают никаких чуств.
ps
про некий стеб в Ваших постах - я разумеется понял :)
pps
надеюсь, Вы то-же поймете где тут у меня стеб :)
кстати исходя из http://arduino.ru/forum/obshchii/vremya-vypolneniya-otdelnykh-komand-arduino
millis() = 0,002 сек, что для 99.99% задач хватает за глаза
на сем спор про это заканчиваю, ибо это вообще бесмыленый спор...
Arhat109-2, писал: "millis() слишком дорогой". Как и ожидалось верного ответа так и не поступило. Расшифровываю:
Сама по себе, millis() конечно же минимальна и делает только консистентное чтение из волатильного глобала атомарно. Как ни странно, но рекомендованный getOvfCount() .. делает ровно тоже самое. Так может смотреть надо было и вовсе не в millis()? :)
Угу. Чтобы миллис был настолько простым, обработчик переполнения 0 таймера - усложнен и писан на С, а в силу кривости компилированного пересчета long-ов, то и сильно избыточен (примерно втрое). В результате, за простую работу millis(), Wiring платит каждоразовым сложным обработчиком прерывания, и плюсом отъедает лишние 5 байт памяти МК.
Это и имелось ввиду. Отказавшись от подсчета миллисекунд, можно И упростить обработчик И иметь простую функцию чтения времени. Что и сделано в arhat.h
Недостаток такого подхода (за всё надо платить!) - необходимость ручного пересчета констант задержек из миллисекунд в тики таймера, если задержки требуются строго по миллисекундам. На самом деле такое - крайне редко, да и сам миллис не считает "точно": каждые 42 мсек он "перескакивает" из-за накопленного остатка FRACT. То есть "характер" течения времени в обоих случаях - ОДИНАКОВ и определяется свойством переполнения таймера.
Но .. я предпочитаю или пересчитать самоу или заставить это делать препроцессор .. или просто плюнуть и писать задержки в "примерных" миллисекундах, чем занимать код неплодотворной работой. :)