ТРЕНД (график) на символьном дисплее 1602

Тема в разделе "Глядите, что я сделал", создана пользователем yul-i-an, 19 ноя 2016.

  1. yul-i-an

    yul-i-an Гик

    Решил реализовать вывод тренда на символьный дисплей путем динамической генерации "символов" в соответствии с поступающими данными для отслеживания динамики входных данных (температуры, мощности и т.п.).
    Выкладываю тестовый код на котором изучаю процесс.
    Код (C++):
    #include <LiquidCrystal.h>///yul-i-an 2016
    LiquidCrystal lcd(10, 9, 5, 4, 3, 2);///
    int data[30]={
      1,2,3,5,8,5,5,7,8,3,1,1,2,3,3,3,5,6,7,8,8,8,6,5,5,6,4,3,2,1};//данные для постройки графика
    byte graph[8];

    void setup(){
      lcd.begin(16, 2);
    }
    void loop(){
      //тут будем искать максимум для масштабирования графика
      //и вычислять цену деления одного пикселя
      for (int s=0; s<=5; s++){
        memset(graph, 0, sizeof(graph));//обнуляем генерируемый символ
        for (int y=6; y >=0; y--){//перебираем строки
          for (int x=0; x <5; x++){//перебираем столбец
            if (data[(s*5)+x]>y){//проверка на необходимость установки пикселя
              //в соответствии с масштабом (пока не реализовано)
              graph[6-y] |= (1 << 5-x-1);//устанавливаем пиксель в столбце
            }
          }
        }
        lcd.createChar(s, graph);
        //lcd.clear();
        lcd.setCursor(s, 0);//выводим сгенерированый символ
        lcd.write(byte(s));
        lcd.setCursor(0,1);//выводим номер выводимого пикселя для отладки
        lcd.print(s);
      }
      //прокручиваем график
      delay(100);
      byte v=data[0];
      for (byte i=0;i<29;i++){
        data[i]=data[i+1];
      }
      data[29]=v;
    }
    Код в железе не проверял, так что может лагать (тестовый варийант).

    Вот что получилось на данный момент
    [​IMG]
    Приведенный код пока работает с целыми числами от 0-8 (масштабирование на подходе)

    Вроде всё работает как задумано, но я планировал использовать одно пользовательское знакоместо в памяти LCD для генерации всего графика, но пока это не получается (работаю над этим), если между выводом каждого символа отчищать экран то всё получаеться, работаю над этим.

    Кому показалось интересным подключайтесь.

    Задачи следующие:

    - организовать вывод путем использования одного пользовательского символа

    (для вывода графика более 8 символов длиной или сразу нескольких)

    - организовать вывод в виде линии

    - автомасштабирование (на стадии реализации)

    Особой пользы кроме как слежение за динамикой в нем нет, больше академический интерес, ну и оживление устройств на символьных LCD (метеостанций, регуляторов и т.д.).
     
    Tomasina нравится это.
  2. Karabas

    Karabas Гик

    В железе проблемы с энерционностью экрана вижу я
     
  3. yul-i-an

    yul-i-an Гик

    Я думаю скорость высокая не нужна, раз в 1 мин сдвиг на пиксель для температуры например.
     
  4. yul-i-an

    yul-i-an Гик

    Код с автомасштабированием и случайными входными данными.

    Код сырой, не обращайте внимания.
    Код (C++):
    #include <LiquidCrystal.h>///
    LiquidCrystal lcd(10, 9, 5, 4, 3, 2);///
    int data[30]={
      1,2,3,5,8,5,5,10,15,24,1,1,2,3,3,3,5,6,7,8,8,8,6,5,4,6,4,3,2,1};
    byte graph[8];
    #define maximum 100
    //#define stp maximum/8
    byte mx=data[0];
    void setup(){
      lcd.begin(16, 2);
    }
    void loop(){
      //тут будем искать максимум для масштабирования графика
      //и вычислять цену деления одного пикселя
      for (int s=0; s<=5; s++){
        memset(graph, 0, sizeof(graph));//обнуляем генерируемый символ
        for (int y=6; y >=0; y--){//перебираем строки
          for (int x=0; x <5; x++){//перебираем столбец
            if (data[(s*5)+x]>y*((mx*1.3)/8)){//проверка на необходимость установки пикселя
              //if (random(0, maximum)>y*stp){
              //в соответствии с масштабом (пока не реализовано)
              graph[6-y] |= (1 << 5-x-1);//устанавливаем пиксель в столбце
            }
          }
        }
        lcd.createChar(s, graph);
        //lcd.clear();
        lcd.setCursor(s, 0);//выводим сгенерированый символ
        lcd.write(byte(s));
        lcd.setCursor(0,1);//выводим максимального
        lcd.print("max");
        lcd.print(mx);
        lcd.print(" ");
            lcd.setCursor(8,1);//выводим максимального
        lcd.print("stp");
        lcd.print((mx*1.3)/8);
        lcd.print(" ");
      }
      //прокручиваем график
      delay(200);
      mx=data[0];
      for (byte i=0;i<29;i++){
        data[i]=data[i+1];
      }
      data[29]=((data[28]*4)+random(0, maximum))/5;
      for (byte i=29;i>0;i--){
        if (data[i]>mx){
          mx=data[i];
        }
      }
     
    }
    [​IMG]
    max - максимум шкалы

    stp - цена деления одного пикселя
     
    Neilon, alp69 и Karabas нравится это.
  5. Tomasina

    Tomasina Сушитель лампочек Модератор

    Если взять OLED, то с инерционностью проблем нет.
     
  6. ostrov

    ostrov Гуру

    Тот же графический дисплей от Нокии 5110 по цене того же 16х2 но с любыми графиками без бубнов чем не устраивает?
     
  7. Karabas

    Karabas Гик

    Проверил в железе, абсолютно рабочая идея, даже на той скорости, что сейчас. Единственное что, пики немного размываются, думаю, что обновляя раз в полсекунды све будет выглядеть замечательно.
     

    Вложения:

    • IMG_5272.JPG
      IMG_5272.JPG
      Размер файла:
      136,9 КБ
      Просмотров:
      684
    yul-i-an нравится это.
  8. yul-i-an

    yul-i-an Гик

    Для Tomasina и ostrov. Дело не в том чтобы взять графический дисплей, а в том чтобы показать что и на таком экране можно не только текст выводить. Без обид.
    Karabs`у респект за тест.
     
  9. yul-i-an

    yul-i-an Гик

    Вообщем код преобразовался в 2 функции
    Код (C++):
    void trend(short int *in_data, byte len, byte x, byte y){//массив с данными, длина графика в символах, координаты вывода на экран
      short int mx=0;
      short int mn;
      short int sc=0;//цена деления
      byte graph[8];//для генерации символа
      memset(graph, 0, sizeof(graph));//обнуляем генерируемый символ
      for (byte i=(len*5)-1;i>0;i--){
        if (in_data[i]>mx){
          mx=in_data[i];
        }
        mn=mx;
        if (in_data[i]<mn){
          mn=in_data[i];
        }
      }
      sc=((mx-mn)/8);//расчет цены деления
      for (int a=0; a<=(len-1); a++){//////
        memset(graph, 0, sizeof(graph));//обнуляем генерируемый символ
        for (int y=8; y > 0; y--){//перебираем строки
          for (int x=0; x <5; x++){//перебираем столбец
            if (((in_data[(a*5)+x])-mn+sc)>=(sc*y)){
              graph[8-y] |= (1 << 5-x-1);//устанавливаем пиксель в столбце
            }
          }
        }
        lcd.createChar(a, graph);
        lcd.setCursor((a+x), y);//выводим сгенерированый символ
        lcd.write(byte(a));
      }
    }

    void addVal(float val, short int *in_data, byte len){   //значение, массив в который добовляем, длина графика в символах <8
      for (byte i=0;i<(len*5)-1;i++){
        in_data[i]=in_data[i+1];//прокручиваем
      }
      data[(len*5)-1]=(val*100);//вносим новое значение
    }
    функция trend выводид тренд на дисплей, а addVal добовляет в массив новое значение.
     
  10. Neilon

    Neilon Нерд

    Идея не подкупает эффективностью, но выглядит прикольно!:rolleyes:
     
  11. yul-i-an

    yul-i-an Гик

    подкорректировал немного
    Код (C++):
    void trend(short int *in_data, byte len, byte x, byte y, byte sts){//массив с данными, длина графика в символах, координаты вывода на экран
      short int mx=0;
      short int mn=10000;
      short int sc=0;//цена деления
      byte graph[8];//для генерации символа
      memset(graph, 0, sizeof(graph));//обнуляем генерируемый символ
      for (byte i=(len*5)-1;i>0;i--){
        if (in_data[i]>mx){
          mx=in_data[i];
        }
        if (in_data[i]<mn){
          mn=in_data[i];
        }
      }
      sc=((mx-mn)/8);//расчет цены деления
      for (int a=0; a<=(len-1); a++){//////
        memset(graph, 0, sizeof(graph));//обнуляем генерируемый символ
        for (int y=8; y > 0; y--){//перебираем строки
          for (int x=0; x <5; x++){//перебираем столбец
            if (((in_data[(a*5)+x])-mn+(sc*2))>=(sc*y)){
              graph[8-y] |= (1 << 5-x-1);//устанавливаем пиксель в столбце
            }
          }
        }
        lcd.createChar(a+sts, graph);
        lcd.setCursor((a+x), y);//выводим сгенерированый символ
        lcd.write(byte(a));
      }
    }

    void addDate(float val, short int *in_data, byte len){   //значение, массив в который добовляем, длина графика в символах <8
      for (byte i=0;i<(len*5)-1;i++){
        in_data[i]=in_data[i+1];//прокручиваем
      }
      data[(len*5)-1]=(val*100);//вносим новое значение
    }
    Из практики авто масштабирование не имеет смысла без вывода на дисплей минимального и максимального значений. Если установить фиксированную шкалу, то не видно незначительных изменений.
    Надо с геометрической шкалой попробовать.

    И для интересующихся заготовка для вывода на символьный дисплей псевдо механического счетчика как на старых магнитофонах. Ну это больше для эксперимента было сделано.
    Код (C++):
    #include <LiquidCrystal.h>///
    LiquidCrystal lcd(10, 9, 5, 4, 3, 2);///
    byte numLent[80] = {
      B01110,//0
      B10001,
      B10001,
      B10001,
      B10001,
      B01110,
      B00000,
      B01100,//1
      B00100,
      B00100,
      B00100,
      B00100,
      B11111,
      B00000,
      B01110,//2
      B10001,
      B00010,
      B00100,
      B01000,
      B11110,
      B00000,
      B01110,//3
      B10001,
      B00110,
      B00001,
      B10001,
      B01110,
      B00000,
      B00011,//4
      B00101,
      B01001,
      B11111,
      B00001,
      B00001,
      B00000,
      B11111,//5
      B10000,
      B11110,
      B00001,
      B10001,
      B01110,
      B00000,
      B01110,//6
      B10000,
      B11110,
      B10001,
      B10001,
      B01110,
      B00000,
      B11111,//7
      B10001,
      B00010,
      B00100,
      B01000,
      B01000,
      B00000,
      B01110,//8
      B10001,
      B01110,
      B10001,
      B10001,
      B01110,
      B00000,
      B01110,//9
      B10001,
      B10001,
      B01111,
      B00001,
      B01110,
      B00000,
    };
    byte graph[8];//для генерации символа
    byte n=0;
    void setup(){
      lcd.begin(16, 2);
      lcd.setCursor(0, 0);//выводим сгенерированый символ
      lcd.print("0");
    }

    void loop(){
      memset(graph, 0, sizeof(graph));//обнуляем генерируемый символ
      for (byte ln=0;ln<6;ln++){
        graph[ln]=numLent[ln+n];
      }
      lcd.createChar(0, graph);
      lcd.setCursor(0, 0);//выводим сгенерированый символ
      lcd.write(byte(0));
      delay (62);//62
      n=n+1;
      if(n>64){
        n=0;
      }
    }