Вывод нескольких значений переменных на LCD 1602 Ардуино.

egor-kartop
Offline
Зарегистрирован: 30.01.2016

Столкнулся с такой задачей. Чтобы сэкономить место на экране LCD решил выводить 3 значения по очереди друг на друга на 10 секунд, но без использования delay. Пытался использовать функцию millis(), но получается в начале экран обновляет значения каждые 10 секунд и после того, как доходит до последнего значения просто пустой экран, т.е. не показывает обратно первое значение. Кто-нибудь побывал обновлять информацию на экране каждые 10 секунд без использования delay? 

vde69
Offline
Зарегистрирован: 10.01.2016
  // --------------------------------------------------------------------------------------------
  // обновление экрана, это последнее...
  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); }
    }

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

egor-kartop пишет:

 Кто-нибудь побывал обновлять информацию на экране каждые 10 секунд без использования delay? 

Нет. 9 пробовали, 11 пробовали, а 10 - Вы первый :))))

Вы бы хоть скетч выложили, что-ли, а то что обсуждать. Пробовали или нет? Ну, пробовали, дальше что?

Клапауций 232
Offline
Зарегистрирован: 05.04.2016
egor-kartop
Offline
Зарегистрирован: 30.01.2016
Вот так скажу точно не работает) Но выводит вначале каждые 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);}
  }

 

Клапауций 232
Offline
Зарегистрирован: 05.04.2016
egor-kartop
Offline
Зарегистрирован: 30.01.2016

А сама функция где? 

getDelayTime(time_anime, time_loop_new)

 

egor-kartop
Offline
Зарегистрирован: 30.01.2016

Вы не ругайтесь, я ваше сообщение уже увидел и следовательно скоро его прочитаю. 

vde69
Offline
Зарегистрирован: 10.01.2016

сейчас меня опять закидают помидорами, что вторая часть не нужна, но я буду непреклонен :)

//*************************************************************************************************
// процедура сравнивает два времени и возвращает разницу в виде числа, учитывает переход времени через 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;
}

 

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

А вот так:

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;
  });
}

:)

egor-kartop
Offline
Зарегистрирован: 30.01.2016

Что такое everyMillis, можете объяснить? То есть странно, что там после запятая и фигурная скобка. 

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

В общем-то уже давненько типовой макрос, предложенный тут автором 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() с точки зрения МК.

egor-kartop
Offline
Зарегистрирован: 30.01.2016

Спасибо большое! Буду разбираться дальше сам. 

vde69
Offline
Зарегистрирован: 10.01.2016

почему millis() дорогое удовольствие ??? ведь это асамленый однотактовый MOV.... ну плюс на стек и переход 3 такта....   

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

vde69 пишет:

почему millis() дорогое удовольствие ???

Пошёл за попкорном!

Сейчас Вам объяснят, что всё, что поставляется с ардуиновским IDE жутко дорого в то время как arhat.h - почти бесплатно :))))

vde69 пишет:

 ведь это асамленый однотактовый 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;
}
 

 

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Обойдетесь. "дураков учить - только портить". :)

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

Arhat109-2 пишет:

Обойдетесь. "дураков учить - только портить". :)

ок. дураков дураки портили.

egor-kartop
Offline
Зарегистрирован: 30.01.2016

Можете помочь? Для меня макросы в С/C++ это что-то новое. То есть, как я понял, макрос запустит выполнение определенной функции с заданным параметром? А как все это должно работать? 

vde69
Offline
Зарегистрирован: 10.01.2016

ну хорошо, идем по порядку

pop, push - не знаю сколько будет, предположительно от 0 до 2*КоличествоРегистров, скорее всего будет типа 16 по числу регистров

jmp - 1

выделение памяти кучи - 2х2

cli() - не помню, вроде 1

mov - скорее всего пойдут через dword регистр это будет 4 такта * 2

возврат - 1 такт

 

итого примерно от 15 до 30 тактов...

 

 

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Arhat109-2 пишет:

Обойдетесь. "дураков учить - только портить". :)

Хрена-се! А я уже попкорном затарился! Пропадёт же теперь! Компенсация-то хоть будет?

vde69
Offline
Зарегистрирован: 10.01.2016

единственный минус millis() это то, что он на несколько тактов замедляет таймер, а на таймер завязан последовательный порт, и при излишнем использование может нарушится работа порта...

других минусов для систем которые не опреируют временем точнее 0.01 сек я не вижу...

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Открываете "кондуит" и читаете про препроцессор С. Можно тупо погуглить.

Нет. Макрос - это "макроподстановка". То есть его тело подставляется в текст программы по некоторым, достаточно простым правилам и уже после выполнения всех подстановок компилируется то, что получилось в результате. Откройте библиотечные и стандартные *.h файлы и обнаружьте там для себя много нового. Макросы препроцессора там "сплошь и рядом". :)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

vde69 пишет:

ну хорошо, идем по порядку

Экой Вы непонятливый! :)))

Куда идёте, зачем идёте? Вам же объяснили, что кошерно то, что делается через arhat.h, а то, что поставляется с ардуиновским IDE - это кака, бяка и вообще bullshit, как выражатся наши американские партнёры!

Это ещё, что! Вот если Вы, не дай Бог, конечно, вздумаете какой-нибудь класс использовать, то Вы будете "обязаны предоставить г-ну Архату листинг, доказывающий отстуствие удорожания кода" - во как! А Вы тут с глупостЯми ... :))))

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Кстати, у вас там после вывода переменной больше ничего нет. Если количество цифирек сильно меняется, то вы будете наблюдать хвосты от предыдущих выводов. А очистка экрана - "ну совсем дорогое удовольствие". Поэтому рекомендую дополнить код выводом 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 - тип переменных.

egor-kartop
Offline
Зарегистрирован: 30.01.2016

Выдает вот такие ошибки: 

sketch_apr13b.ino:49:4: error: macro "everyMillis" passed 4 arguments, but takes just 2
sketch_apr13b.ino: In function 'void loop()':
sketch_apr13b:34: error: 'everyMillis' was not declared in this scope

Вот сам код:

#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;
  });
} 
}

 

egor-kartop
Offline
Зарегистрирован: 30.01.2016

Вроде работает:

#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;
}

Сейчас попробую в основной программе применить.

Все прекрасно робит :)

vde69
Offline
Зарегистрирован: 10.01.2016

ЕвгенийП пишет:

vde69 пишет:

ну хорошо, идем по порядку

Экой Вы непонятливый! :)))

Куда идёте, зачем идёте? Вам же объяснили, что кошерно то, что делается через arhat.h, а то, что поставляется с ардуиновским IDE - это кака, бяка и вообще bullshit, как выражатся наши американские партнёры!

Это ещё, что! Вот если Вы, не дай Бог, конечно, вздумаете какой-нибудь класс использовать, то Вы будете "обязаны предоставить г-ну Архату листинг, доказывающий отстуствие удорожания кода" - во как! А Вы тут с глупостЯми ... :))))

ну я как-бы не новичек совсем, начнем с диплома по Clarion в далеком 1992м году... и мне глубоко покласть на НЕАГУМЕНТИРОВАНЫЕ мнения идущие в разрез с моим опытом, в то-же время я не против учится и принимаю логичные аргументы.

Да я не системник, я чистейший прикладник в програмировании и в вещах уровня "железа" я слаб (хотя мне принадлежит например одна прога защиты компа от несанкционированого доступа, написана на асемблере в 91м и использовалась некоторое время в Бауманке), но с точки зрения стратегии когда надо применять те или иные методы я точно не новичек и не слабак...

Про вопрос "удорожания" кода, я отвечу так: код (как и картину) можно вылизывать годами и идела все равно не будет, нужно уметь остановится достигнув приемлемых результатов. А вообще люди которые в угоду экономии 3х машинных циклов и 10 байтов начинают переписывать типой код у меня кроме сожаления не вызывают никаких чуств.

 

ps

про некий стеб в Ваших постах - я разумеется понял :)

pps

надеюсь, Вы то-же поймете где тут у меня стеб :)

vde69
Offline
Зарегистрирован: 10.01.2016

кстати исходя из http://arduino.ru/forum/obshchii/vremya-vypolneniya-otdelnykh-komand-arduino

millis() = 0,002 сек, что для 99.99% задач хватает за глаза

на сем спор про это заканчиваю, ибо это вообще бесмыленый спор...

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Arhat109-2, писал: "millis() слишком дорогой". Как и ожидалось верного ответа так и не поступило. Расшифровываю:

Сама по себе, millis() конечно же минимальна и делает только консистентное чтение из волатильного глобала атомарно. Как ни странно, но рекомендованный getOvfCount() .. делает ровно тоже самое. Так может смотреть надо было и вовсе не в millis()? :)

Угу. Чтобы миллис был настолько простым, обработчик переполнения 0 таймера - усложнен и писан на С, а в силу кривости компилированного пересчета long-ов, то и сильно избыточен (примерно втрое). В результате, за простую работу millis(), Wiring платит каждоразовым сложным обработчиком прерывания, и плюсом отъедает лишние 5 байт памяти МК.

Это и имелось ввиду. Отказавшись от подсчета миллисекунд, можно И упростить обработчик И иметь простую функцию чтения времени. Что и сделано в arhat.h

Недостаток такого подхода (за всё надо платить!) - необходимость ручного пересчета констант задержек из миллисекунд в тики таймера, если задержки требуются строго по миллисекундам. На самом деле такое - крайне редко, да и сам миллис не считает "точно": каждые 42 мсек он "перескакивает" из-за накопленного остатка FRACT. То есть "характер" течения времени в обоих случаях - ОДИНАКОВ и определяется свойством переполнения таймера.

Но .. я предпочитаю или пересчитать самоу или заставить это делать препроцессор .. или просто плюнуть и писать задержки в "примерных" миллисекундах, чем занимать код неплодотворной работой. :)