Пультоскоп на Arduino 27МГц!!!

Andry Smart
Offline
Зарегистрирован: 06.09.2016

Electronik83 пишет:

 

Мне тут один деятель обещал выслать - и чет я не дождался. Заказал с китая два варианта. Жду.....

 

а вы случайно не из Украины?

Andry Smart
Offline
Зарегистрирован: 06.09.2016

удалил дубль

Andry Smart
Offline
Зарегистрирован: 06.09.2016

удалил дубль

seri0shka
seri0shka аватар
Offline
Зарегистрирован: 19.11.2018

Нет. Electronik83 из Северодвинска вроде.

Не отправляйте сообщения по несколько раз. Нажмите кнопочку один раз и подождите- такая специфика сайта )  У меня от нажатия кнопки "сохранить" до результата больше 15 секунд.

Electronik83
Offline
Зарегистрирован: 06.12.2015

seri0shka пишет:

Нет. Electronik83 из Северодвинска вроде.

Не отправляйте сообщения по несколько раз. Нажмите кнопочку один раз и подождите- такая специфика сайта )  У меня от нажатия кнопки "сохранить" до результата больше 15 секунд.

Да, Северодвинск..... близ Архангельска.... Откуда инфа?

seri0shka
seri0shka аватар
Offline
Зарегистрирован: 19.11.2018

Electronik83 пишет:
Да, Северодвинск..... близ Архангельска.... Откуда инфа?

Хотелось бы хоть минимум информации о пользователях на сайте видеть, а не по интернетам копать. Я б даже денег заплатил, чтоб узнать, что случилось с автором темы. Ни имени, ни  города...

Electronik83
Offline
Зарегистрирован: 06.12.2015

Херассе - ввел в гугле свой ник, и нашел даже единственный видос на ютубе, где я сверкаю рожей.... капец....

https://yadi.sk/d/jea5Km5J8tIZzg не знаю для чего я это сделал..... скучаю видно по дисплейчику....

Andry Smart
Offline
Зарегистрирован: 06.09.2016

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

есть подоюные но что то работают через ..опу

Electronik83
Offline
Зарегистрирован: 06.12.2015

Таких приложений полно, но если надо - сделаю своё:)

Господа программеры, а чем отличаются вот эти две строчки, где x - это char, определенный ранее:

x |= 1<<(y);
x |= _BV(y);
 
И что такое _BV?
seri0shka
seri0shka аватар
Offline
Зарегистрирован: 19.11.2018

Цитата:

Например, команда

PORTD |= 1<<3;

установит "1" (сигнал высокого уровня) на выводе PD3.

Команда

PORTD &= ~(1<<4);

установит "0" (сигнал низкого уровня) на выводе PD4.

В AVR GCC сдвиг можно осуществлять и с помощью функции _BV(), которая выполняет поразрядный сдвиг и вставляет результат в компилируемый код.

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

PORTD |= _BV(PD3);      // установить "1" на линии 3 порта D

PORTD &= ~_BV(PD4);      // установить "0" на линии 4 порта D

Одно и то же, если я правильно понимаю. Но в Ардуино проверить нужно.

arthur_1993
Offline
Зарегистрирован: 06.06.2019

Electronic83 когда будет новая версия?

arthur_1993
Offline
Зарегистрирован: 06.06.2019

Electronic83 когда будет новая версия?

seri0shka
seri0shka аватар
Offline
Зарегистрирован: 19.11.2018

Electronik83 пишет:
Я никуда не пропал, просто жду китайского дисплея с алика, т.к. не вижу смысла перелапачивать либу адафрут на оригинале от нокия....  дисплея нет до сих пор....

Я тоже жду уже полгода, причём меня и предыдущая версия от Electronik83 вполне устраивает, только вот анализатор не годится для практического применения. Из-за анализатора жду.

Ещё drauger три недели назад обещал свою версию: "В выходные дотестирую на разных режимах и сигналах, потом выложу"

Ждём-с. Требовать мы не в праве.

Хотя после того, как попробовал дисплей 1202, прежний мне уже неинтересен. Вот пару дней уже переписываю последнюю версию от Электроника под дисплей 1202, пока даже режим осциллографа не добил. Но уже вырисовывается. Жаль, после выхода новой версии всё с начала нужно будет начинать.

tig-rrr
Offline
Зарегистрирован: 26.08.2018

Andry Smart пишет:

вот если бы добавить возожность рисовать картинки и сохранять в виде массива (текст) что бы вставлять в скетч и выводить на дисплей

Electronik83 пишет:

Таких приложений полно, но если надо - сделаю своё:)

Andry Smart пишет:

есть подоюные но что то работают через ..опу

Здравствуйте Всем! По этой причине пол года назад и созрела идея попытать свои силы в создании приложения по своим интересам, т.к. не программист (совсем не программист, в коде 0)  попытка не совсем увенчалась успехом (Иссяк потенциал, многие задачи так и не смог решить). Выкладываю то ,что получилось , сильно не пинайте . Проверить на реальном железе пока нет возможности (возмоны косяки) , пишите если чего не так... попытаюсь исправить.

LCD NOKIA 5510 Pixel Paint ver. 001a   https://yadi.sk/d/Jt2wcDUtz6RTTw

kostyamat
Offline
Зарегистрирован: 16.11.2017

Ребята, а может перевести ваш пультоскоп на новую железку http://arduino.ru/forum/apparatnye-voprosy/obzor-klona-megi328-lgt8f328p вот эту. Та же НАНО, почти, но все в два раза выше,  быстрее :) 32Мгц, АЦП 12бит и т.п.

seri0shka
seri0shka аватар
Offline
Зарегистрирован: 19.11.2018

АЦП 12бит не нужен в данном случае (да и получить 12 бит почти нереально). Кроме того, практически все используемые библиотеки придётся переписывать. Если вдруг найдутся желающие, нужно открывать отдельную тему.

Я всё пытаюсь впихнуть в атмегу 16, 32, или 128 (больше памяти). Не хочет компилировать, ошибки выдаёт.

diksen
Offline
Зарегистрирован: 11.10.2013

kostyamat пишет:

Ребята, а может перевести ваш пультоскоп на новую железку http://arduino.ru/forum/apparatnye-voprosy/obzor-klona-megi328-lgt8f328p вот эту. Та же НАНО, почти, но все в два раза выше,  быстрее :) 32Мгц, АЦП 12бит и т.п.

если откинете десяток-другой страниц этой темы - там я постил скрины пультоскапа на этом чипе. Насколько я помню в коде самого пультоскопа менять ничего не надо т.к. полная совместимость. 

Другой вопрос "портировать" на lgt с ипользованием его всех возможностей...

fly245
fly245 аватар
Offline
Зарегистрирован: 25.08.2013

kostyamat пишет:

Ребята, а может перевести ваш пультоскоп на новую железку http://arduino.ru/forum/apparatnye-voprosy/obzor-klona-megi328-lgt8f328p вот эту. Та же НАНО, почти, но все в два раза выше,  быстрее :) 32Мгц, АЦП 12бит и т.п.

Тогда уж если портировать,то под ESP8266 или ESP32.Память и скорости гораздо больше.

fly245
fly245 аватар
Offline
Зарегистрирован: 25.08.2013

И питание 3,3 вольта, которое поддерживают экраны  tft и OLED

serhiy58
Offline
Зарегистрирован: 19.06.2019

Попалось такое, а компилироваться не хочет, выдает - недопустимая директива предварительной обработки #i несколько раз... Я н силен в программировании поэтому у меня ступор.... Хэлп ми пожалуйста... А штучка интересная ...

https://blog.circuits4you.com/2015/04/nokia-3530i-lcd-interface-with-atmega8.html

serhiy58
Offline
Зарегистрирован: 19.06.2019

[quote=AS31979 Если есть вопросы задавайте пока сам все не позабывал.[/quote]

- А куда выход формирователя импульсов синхронизации подавать? На схаме не увидел... 

- Какой код взят за основу?

- Есть реализация програмного выбора опорнрго напряжения?

...Можно немного рекламы Вашего проекта...

Ёлыпалы
Offline
Зарегистрирован: 20.06.2019
Кто-нибудь пробовал собрать пультоскоп с кварцем 27МГц на ATmega328P-MU? Суффикс MU обозначают тип корпуса микроконтроллера и соответствуют пластиковому QFN. Такой вот например
Интересует стабильность работы на частоте 27МГц. 
NalisniyRoman
Offline
Зарегистрирован: 17.06.2019

Хоть тут похвастаюсь, а то одни женщины вокруг меня )) не понимают радости ))

генер555

 

 

 

 

 

 

 

генератор на 555-м

пультоскоп

 

 

 

 

 

 

 

 

сам пробник, кварц не менял. полосу пропускания соответственно не знаю,соберу какой генер поприличней-посмотрю

видео длинное, скорость лучше х2 ставить

https://www.youtube.com/watch?v=X3brNUTnKGY&t=157s

почему то картинок нет

Joiner
Offline
Зарегистрирован: 04.09.2014

NalisniyRoman пишет:

Хоть тут похвастаюсь....................

Работает! Поздравляю!  ....А теперь осталось самое сложное - довести до конца, т.е. заключить в корпус и сделать законченное устройство. Я на макетках собрал, наверное, больше сотни устройств, а до конца довел только 3-4 :)

NalisniyRoman
Offline
Зарегистрирован: 17.06.2019

Спасибо :) Та же история, но этот как ни странно собрал. у 72-го осцилла уже переключатель не работает-пришлось хоть кое как

https://www.pinterest.ru/pin/759349187153428296

 

 

volodya198024
Offline
Зарегистрирован: 26.12.2016

Да действительно, вся проблема  в коробочке. 

Evg-Chugunov
Evg-Chugunov аватар
Offline
Зарегистрирован: 22.08.2015

Ну так то не зря приборчик пультоскопом обозвали))). Я сперва подходящий пульт для корпуса подобрал, потом в нем и собирал))

Дим
Offline
Зарегистрирован: 05.05.2017

Здравствуйте, собрал Nukia 5110 Pultoscop v2  по данной статье. А он не хочет работать должным образом. При включении пункты меню перлистываются сами собой. В чём может быть причина?

Evg-Chugunov
Evg-Chugunov аватар
Offline
Зарегистрирован: 22.08.2015

Подтягивающие резисторы не припаял?

Дим
Offline
Зарегистрирован: 05.05.2017

Evg-Chugunov пишет:

Подтягивающие резисторы не припаял?

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

mobistrike
mobistrike аватар
Offline
Зарегистрирован: 19.08.2016

Дим пишет:

Evg-Chugunov пишет:

Подтягивающие резисторы не припаял?

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

А смысл ? Выше человек верно подметил - копать в сторону подтягивающих резисторов .

Dushev
Offline
Зарегистрирован: 29.04.2018

Дим, проверьте дорожки  плату! Замена кнопки.

Dushev
Offline
Зарегистрирован: 29.04.2018

 

 

Дим
Offline
Зарегистрирован: 05.05.2017

mobistrike пишет:

Дим пишет:

Evg-Chugunov пишет:

Подтягивающие резисторы не припаял?

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

А смысл ? Выше человек верно подметил - копать в сторону подтягивающих резисторов .

Я же инаписал что резисторы присутствуют.

seri0shka
seri0shka аватар
Offline
Зарегистрирован: 19.11.2018

Чудес не бывает. Один из вариантов:

1. Резисторы притянуты не туда, куда должны (к минусу вместо плюса или наоборот) .

2. Один из резисторов или пайка оборван, в результате пин находится "в воздухе".

3. Резистор не на том пине, который прописан в скетче.

seri0shka
seri0shka аватар
Offline
Зарегистрирован: 19.11.2018

drauger, поделитесь реализацией логического анализатора, хоть черновиками, мне всё равно под другой дисплей переписывать. Обещали больше двух месяцев назад. У меня не получается триггеры прицепить.

Дим
Offline
Зарегистрирован: 05.05.2017

seri0shka пишет:

Чудес не бывает. Один из вариантов:

1. Резисторы притянуты не туда, куда должны (к минусу вместо плюса или наоборот) .

2. Один из резисторов или пайка оборван, в результате пин находится "в воздухе".

3. Резистор не на том пине, который прописан в скетче.

Хорошо, согласен. Но ежели схема собрана на макетке вообще без кнопок и резисторов (только контроллер и дисплей)в этом случае куда копать?

tig-rrr
Offline
Зарегистрирован: 26.08.2018

Дим пишет:

вообще без кнопок и резисторов (только контроллер и дисплей)

Вот менно по этой причине это и происходит, пока не соберёте схему полностью...;)

Как минимум резисторы обязательно нужны в данном случае.

Дим
Offline
Зарегистрирован: 05.05.2017

tig-rrr пишет:

Дим пишет:

вообще без кнопок и резисторов (только контроллер и дисплей)

Вот менно по этой причине это и происходит, пока не соберёте схему полностью...;)

Как минимум резисторы обязательно нужны в данном случае.

Без результата. Заколдованный круг какой то

Дим
Offline
Зарегистрирован: 05.05.2017

Всё получилось (была ошибка в схеме). Теперь другойвопрос - можно ли шуп повесить на другой пин (не на 5), если да, то как это изменить в коде?

pinMode(A4,INPUT);

 и это для чего?

drauger
Offline
Зарегистрирован: 20.02.2018

seri0shka пишет:

drauger, поделитесь реализацией логического анализатора, хоть черновиками, мне всё равно под другой дисплей переписывать. Обещали больше двух месяцев назад. У меня не получается триггеры прицепить.

Ловите. Все работает, но на разных сигналах не тестировал и в реальных измерениях не применял.

 

void Logic() {
  pinMode(A0, INPUT); // указываем пины на ВХОД
  pinMode(A1, INPUT);
  pinMode(A2, INPUT);

start:

  bool measure = 0, pattern_set = 0;
  uint8_t menu = 0;
  uint8_t trigger = 0;
  uint8_t pattern_mask; // маска для события PATTERN
  uint8_t pattern_value; // ЗНАЧЕНИЕ состояния для события PATTERN
  uint8_t mask[] = {1, 2, 2, 3};

  while (!measure) { // опрос кнопок

    if (!digitalRead(KEY_R)) {
      ++menu;  // перебор меню
      menu %= 3;
    }

    if (!digitalRead(KEY_OK)) switch (menu) {
        case 0 :
          ++trigger; trigger %= 4;
          if (trigger == 0) pattern_set = 0;
          break;
        case 1 :
          measure = 1;
          break;
        case 2 :
          setup();
      }
      
    lcd.clearDisplay();
    lcd.setTextColor(BLACK);
    ShowMode(0, 5);

    if (menu == 0) lcd.setTextColor(WHITE, BLACK);
    else lcd.setTextColor(BLACK);
    lcd.setCursor(2, 12);
    switch (trigger) {
      case 0: lcd_pgm_string(logicStr0); break;
      case 1: lcd_pgm_string(logicStr1); break;
      case 2: lcd_pgm_string(logicStr2); break;
      case 3: lcd_pgm_string(logicStr3); break;
    }

    if (menu == 1) lcd.setTextColor(WHITE, BLACK);
    else lcd.setTextColor(BLACK);
    lcd.setCursor(2, 30);
    lcd.print("START");

    if (menu == 2) lcd.setTextColor(WHITE, BLACK);
    else lcd.setTextColor(BLACK);
    lcd.setCursor(2, 40);
    lcd.print("Exit");

    lcd.display();

    if (trigger == 3) {
      lcd.setCursor(5, 21);
      lcd.setTextColor(BLACK);
      for (uint8_t j = 0; j < 3; j++)
        if(mask[j] == 2) lcd.print("x");
          else lcd.print(mask[j]);
      lcd.display();
      if(!pattern_set) {
        pattern_set = read_pattern(pattern_mask, pattern_value, mask);
        menu = 1;
        }
      }
      
    delay(KEY_DELAY);
  }

  lcd.clearDisplay();
  ShowMode(0, 5);
  lcd.setCursor(2, 20);
  lcd_pgm_string(logicStr4);
  lcd.display();

  // инициализация перед замерами
  uint16_t size_buffer = 700;
  uint8_t mass[700]; // так как планируется компрессия данных, размер массива будет равен половине количества замеров
  uint8_t history;
  uint8_t cur_data, prev_data; // текущее и предыдущее состояние шины
  uint8_t count = 0; // счетчик одинаковых замеров
  bool trigger_event = false; //признак сработки триггера

  prev_data = (PINC & B00000111); //сформируем пред. замер состояние пинов порта

  while (!trigger_event) { // Процедура сканирования шины и отлов сработки триггеров
    cur_data = (PINC & B00000111); //сразу же считываем состояние пинов порта

    switch (trigger) { //ветвимся в зависимости от триггера
      // BURST Mode Любое изменение на любом канале
      case (2):
        trigger_event = (cur_data != prev_data);
        break;
      // master channel RISE (переход из 0 в 1)
      case (1):
        trigger_event = (~prev_data & cur_data & 1);
        break;
      // master chanel FALL (переход из 1 в 0)
      case (0):
        trigger_event = (prev_data & ~cur_data & 1);
        break;
      // PATTERN (определенное состояние шины)
      case (3):
        trigger_event = ((cur_data & pattern_mask) == pattern_value); // если проверка на совпадение условий прошла
        break;
    } //End switch

    // Процедура записи данных данных в буфер
    if (cur_data == prev_data && count < 31 && !trigger_event) count++;
    else {
      if (trigger_event) {
        mass[0] = count << 3 | prev_data;   // запоминаем количество и значение одинаковых замеров
        history = count + 1;                // запоминаем начало отсчета
      }
      prev_data = cur_data;                 // запоминаем текущее состояние шины
      count = 0;                            // сбрасываем счетчик одинаковых замеров
    }

    if (!digitalRead(KEY_R)) break; //единственный способ прервать - нажать на кнопку
  }

  if (trigger_event) { //если триггер сработал
    // пишем историю в буфер
    
//        lcd.setCursor(2, 28);
//        lcd.print("Measuring...");
//        lcd.display();
        
    uint16_t count_total = history;
    unsigned long prevMillis = millis(); //запоминаем время начала записи истории

    for (uint16_t i = 1; i < size_buffer; i++) {
      cur_data = (PINC & B00000111); //считываем состояние пинов порта
      if (cur_data == prev_data && count < 31) count++;
      else {
        mass[i] = count << 3 | prev_data;   // запоминаем количество и значение одинаковых замеров
        count_total += count + 1;           // запоминаем общее количество замеров
        prev_data = cur_data;               // запоминаем текущее состояние шины
        count = 0;                          // сбрасываем счетчик одинаковых замеров
      }
    }

    uint16_t time_disc = ((millis() - prevMillis) * 10000) / (count_total - history); //время выборки 10 замеров
    uint8_t zoom = 1;
    uint16_t offset = 0;
    menu = 0;
    
//        lcd.setCursor(2, 36);
//        lcd.print("Drawing...");
//        lcd.display();

    while (1) {
      // обрабатываем кнопки
      if (!digitalRead(KEY_OK))  switch (menu) {
          case 0 : zoom *= 2; if (zoom > 8) zoom = 1; offset %= size_buffer - 84 * zoom; break;
          case 1 : offset++; offset %= size_buffer - 84 * zoom; break;
          case 2 : goto start;
        }
      if (!digitalRead(KEY_R)) { ++menu; menu %= 3; } // перебор меню

      lcd.clearDisplay();
      lcd.setTextColor(BLACK);
      lcd.setCursor(0, 0);
      if (menu == 0) lcd.setTextColor(WHITE, BLACK);
      lcd.write(0x78);
      lcd.print(zoom);
      lcd.setTextColor(BLACK);
      lcd_space();

      if (menu == 1) lcd.setTextColor(WHITE, BLACK);
      lcd.print("<>");
      lcd.setTextColor(BLACK);
      lcd_space();

      lcd.print(zoom * time_disc); lcd_space(); lcd.write(26); lcd.print("s");
      lcd_space();

      if (menu == 2) lcd.setTextColor(WHITE, BLACK);
      lcd.write(0x7F);

      lcd.setCursor(2, 40);
      lcd.setTextColor(BLACK);
      switch (trigger) {
        case 0: lcd_pgm_string(logicStr0); break;
        case 1: lcd_pgm_string(logicStr1); break;
        case 2: lcd_pgm_string(logicStr2); break;
        case 3: lcd_pgm_string(logicStr3); break;
      }

//      lcd.display();

      uint8_t shift = offset / (size_buffer / 84 - zoom);
      lcd.drawFastHLine(shift, 8, 6, BLACK);                                        // шкала прокрутки
      lcd.drawFastHLine(shift, 9, 6, BLACK);                                        // шкала прокрутки
      for (uint8_t i = 10; i < 84; i += 10) lcd.drawFastVLine(i, 12, 3, BLACK);  // шкала разметки
      if (offset == 0) lcd.drawFastVLine(history / zoom, 11, 6, BLACK);             // начало отсчета

//      lcd.display();

      uint8_t y = 0;
      for (uint8_t i = offset; y < 84; i++) {
        uint8_t data = mass[i];
        count = y + ((data >> 3) + 1) / zoom;
        if (count == y) count++;
        for (; y < count && y < 84; y++)
          for (uint8_t x = 0; x < 3; x++) lcd.drawPixel(y, 37 - (x * 7) - ((data & (1 << x)) ? 5 : 0), BLACK);
        if (y < 84) for (uint8_t x = 0; x < 3; x++) lcd.drawLine(y, 37 - (x * 7) - ((data & (1 << x)) ? 5 : 0), y, 37 - (x * 7) - ((mass[i + 1] & (1 << x)) ? 5 : 0), BLACK);
      }

      lcd.display();
      delay(KEY_DELAY);
    }
  }
  else goto start;
}

 

drauger
Offline
Зарегистрирован: 20.02.2018

Дим пишет:

Вможно ли шуп повесить на другой пин (не на 5), если да, то как это изменить в коде?

5 пин - это вход частотомера. Вход осциллографа определяется вот тут:

//ADMUX = vRef ? 0b01100011 : 0b11100011; // установка опорного напряжения АЦП - A3
//ADMUX = vRef ? 0b01100100 : 0b11100100; // установка опорного напряжения АЦП - A4
//ADMUX = vRef ? 0b01100010 : 0b11100010; // установка опорного напряжения АЦП - A2

 

fly245
fly245 аватар
Offline
Зарегистрирован: 25.08.2013

Подскажите,а частоту включительно 8 мгц-сможет замерить частотомер?Просто подумал использовать как цифровую шкалу для КВ  радиоприемника.

seri0shka
seri0shka аватар
Offline
Зарегистрирован: 19.11.2018

drauger пишет:

Ловите. Все работает, но на разных сигналах не тестировал и в реальных измерениях не применял

Спасибо большое! Эх, буквально бы на сутки раньше! Я уже свой анализатор написал почти. Глаза боятся, руки делают. Практически полностью удалось применить "Toy Logic Analyzer" из http://robocraft.ru/blog/3637.html , добавил вывод на дисплей, вывод в UART оставил, пригодится. Теперь довожу получившееся до желаемого.

drauger
Offline
Зарегистрирован: 20.02.2018

fly245 пишет:

Подскажите,а частоту включительно 8 мгц-сможет замерить частотомер?Просто подумал использовать как цифровую шкалу для КВ  радиоприемника.

В принципе, может, но надо делитель поменять.

smokok
smokok аватар
Offline
Зарегистрирован: 08.06.2018

seri0shka пишет:

Посмотрел цены на 1202. Дайте два!

Пытался портировать пультоскоп под 1202, но с моим слабым опытом ничего не вышло. Может проще в библе что то заменить от 5110?

Вот вам для теста экрана скетч от nokia 1100 вместе с библой. (2 в одном).

СКАЧАТЬ

seri0shka
seri0shka аватар
Offline
Зарегистрирован: 19.11.2018

Мой пультоскоп с дисплеем 1202. Пока много недоделано.

Выбор режима:

Режим терминала:

Режим логического анализатора:

Тот же сигнал параллельно отображается на компьютере:

Ещё один замер анализатором:

То же место на компе:

В режиме осциллографа:

В режиме генератора:

Самое сложное- пайка. Шаг 0,6 мм.

 

seri0shka
seri0shka аватар
Offline
Зарегистрирован: 19.11.2018

И сам код. Никаких библиотек не надо- включил в код.

Для того, чтобы русские буквы корректно отображались, в некоторых версиях Arduino IDE надо перед прошивкой сохранить скетч.

// Страница проекта  http://srukami.inf.ua/pultoscop_v25110.html
// === От [Electronik83] === mailTo: gsmkillersevsk@mail.ru
// Оптимизировал код
// Оптимизировал отображение частoты и напряжения в осцилле при 1Кгц и 2,5в (был баг автора)
// Убрал программную растяжку сигнала из осцилла (развертка 7 и 8)
// Изменил отрисовку сигнала в осцилле (больше нет раздвоений сигнала)
// В осцилле cделал автосинхронизацию по серединке амплитуды сигнала
// В осцилле показываем масимальное и минимальное значение амплитуды сигнала справа
// Атмега теперь сама меряет напряжение своего питания, не надо ничего мультиметром мерять
// В осцилле - сделал более удобный вывод верхней строки в осцилле, "устатичил" вывод частоты.
// теперь нет дерганья из Гц в кГц и обратно - просто выводим в МГц.
// Добавил цифровой анализатор (входы A1, A2, A3, A4, A5)


#define BUFSIZE 600
#define history 50

volatile int cur_index = 0; //переменная для отсчета замеров
volatile int StartIndex = 0; // номер замера с которого нужно выводить результат (считается так: (замер сработки триггера-предистория))
volatile byte prev_data; // предыдущее состояние шины
volatile byte pattern_mask; // маска для события PATTERN
volatile byte pattern_value; // ЗНАЧЕНИЕ состояния для события PATTERN
volatile boolean trigger_event = false; //признак сработки триггера
volatile int time_disc = 0; // время выборки 1-го значения
unsigned long prevMillis, curMillis;
enum triggerEvents {BURST, MASTER_RISE, MASTER_FALL, PATTERN}; //именнованный список триггеров-событий
bool flag = 0;

//#include <EEPROM.h>
#define RES 12
#define CS 11
#define Data 10
#define Clock 9
#define swap(a, b) { int t = a; a = b; b = t; }

#include <FreqCount.h>
#include <PWM.h>
// Настройки пользователя - можно менять
#define OVERCLOCK 16  // частота на которой работает Ардуино
float VCC = 5.0;      // напряжение питания
#define KEY_L  4 // кнопка ЛЕВО  (можно любой пин)
#define KEY_OK 3 // кнопка ОК    (можно любой пин)
#define KEY_R  2 // кнопка ПРАВО (можно любой пин)
#define KEY_P  6 // кнопка PAUSE (можно любой пин)
#define KEY_DELAY 200
#define LCDX 96
// Переменные, дефайны и прочяя хрень - менять со смыслом
#define GEN_PIN  13  // 9 пин для генератора сигналов (не менять)
#define DDS_PIN  6 // 10 пин для генератора dds (не менять)

#include <avr/pgmspace.h>
const char string_0[] PROGMEM = "Осциллограф";   // "String 0" и т.д. - это содержимое строк; если необходимо меняйте его
const char string_1[] PROGMEM = "Генератор";
const char string_2[] PROGMEM = "DDSгенератор";
const char string_3[] PROGMEM = "Терминал";
const char string_4[] PROGMEM = "Анализатор";
const char string_5[] PROGMEM = " ";
const char string_6[] PROGMEM = "V bat=";
const char string_7[] PROGMEM = " PWM=";
const char string_8[] PROGMEM = " Pause ";
const char string_9[] PROGMEM = "Частота";
const char string_10[] PROGMEM = "    Синус    ";
const char string_11[] PROGMEM = " Треугольник ";
const char string_12[] PROGMEM = "    Пила     ";
const char string_13[] PROGMEM = "Обратная пила";
const char string_14[] PROGMEM = "Скорость:";
// Теперь создаем таблицу с отсылками к этим строкам:
const char* const string_table[] PROGMEM = {string_0, string_1, string_2, string_3, string_4, string_5, string_6, string_7, string_8, string_9, string_10, string_11, string_12, string_13, string_14};
char buffer[13];    // массив должен быть достаточно велик, чтобы вместить даже самую большую строку

byte symbol1 = 0;
byte symbol2 = 0;
byte symbol3 = 0;
byte mode = 0; // пункт главного меню
byte menu = 0; // пункт меню
byte adcBuf[BUFSIZE];
byte vSync = 30; // уровень синхронизации
bool vRef = 1; // флаг опорного напряжения
bool pause = 0; // флаг режима паузы
byte TextColor = 1;
byte razv = 0;
byte trig = 0;
int grOffset = 0; // смещение графика в рабочем режиме
byte vMax, vMin; // максимальное и минимальное напряжение сигнала
// Переменные для генератора
int PWM = 50; // стартовое значение ШИМ от 0 до 100 для генератора
int temp = 0; // временная переменная***
unsigned long freq = 1000; // стартовое значение частоты в Гц для генератора
unsigned long stepFreq = 0;
unsigned long count = 1;
byte LCD_RAM[96 * 9]; // 96 * 9

static const char font[] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00 ,  // 0x20   space
  0x00, 0x00, 0x5f, 0x00, 0x00 ,  // 0x21   !
  0x00, 0x07, 0x00, 0x07, 0x00 ,  // 0x22   "
  0x14, 0x7f, 0x14, 0x7f, 0x14 ,  // 0x23   #
  0x24, 0x2a, 0x7f, 0x2a, 0x12 ,  // 0x24   $
  0x23, 0x13, 0x08, 0x64, 0x62 ,  // 0x25   %
  0x36, 0x49, 0x55, 0x22, 0x50 ,  // 0x26   &
  0x00, 0x05, 0x03, 0x00, 0x00 ,  // 0x27   '
  0x00, 0x1c, 0x22, 0x41, 0x00 ,  // 0x28   (
  0x00, 0x41, 0x22, 0x1c, 0x00 ,  // 0x29   )
  0x14, 0x08, 0x3e, 0x08, 0x14 ,  // 0x2a   *
  0x08, 0x08, 0x3e, 0x08, 0x08 ,  // 0x2b   +
  0x00, 0x50, 0x30, 0x00, 0x00 ,  // 0x2c   ,
  0x08, 0x08, 0x08, 0x08, 0x08 ,  // 0x2d   -
  0x00, 0x60, 0x60, 0x00, 0x00 ,  // 0x2e   .
  0x20, 0x10, 0x08, 0x04, 0x02 ,  // 0x2f   /
  0x3e, 0x51, 0x49, 0x45, 0x3e ,  // 0x30   0
  0x00, 0x42, 0x7f, 0x40, 0x00 ,  // 0x31   1
  0x42, 0x61, 0x51, 0x49, 0x46 ,  // 0x32   2
  0x21, 0x41, 0x45, 0x4b, 0x31 ,  // 0x33   3
  0x18, 0x14, 0x12, 0x7f, 0x10 ,  // 0x34   4
  0x27, 0x45, 0x45, 0x45, 0x39 ,  // 0x35   5
  0x3c, 0x4a, 0x49, 0x49, 0x30 ,  // 0x36   6
  0x01, 0x71, 0x09, 0x05, 0x03 ,  // 0x37   7
  0x36, 0x49, 0x49, 0x49, 0x36 ,  // 0x38   8
  0x06, 0x49, 0x49, 0x29, 0x1e ,  // 0x39   9
  0x00, 0x36, 0x36, 0x00, 0x00 ,  // 0x3a   :
  0x00, 0x56, 0x36, 0x00, 0x00 ,  // 0x3b   ;
  0x08, 0x14, 0x22, 0x41, 0x00 ,  // 0x3c   <
  0x14, 0x14, 0x14, 0x14, 0x14 ,  // 0x3d   =
  0x00, 0x41, 0x22, 0x14, 0x08 ,  // 0x3e   >
  0x02, 0x01, 0x51, 0x09, 0x06 ,  // 0x3f   ?
  0x32, 0x49, 0x79, 0x41, 0x3e ,  // 0x40   @
  0x7e, 0x11, 0x11, 0x11, 0x7e ,  // 0x41   A
  0x7f, 0x49, 0x49, 0x49, 0x36 ,  // 0x42   B
  0x3e, 0x41, 0x41, 0x41, 0x22 ,  // 0x43   C
  0x7f, 0x41, 0x41, 0x22, 0x1c ,  // 0x44   D
  0x7f, 0x49, 0x49, 0x49, 0x41 ,  // 0x45   E
  0x7f, 0x09, 0x09, 0x09, 0x01 ,  // 0x46   F
  0x3e, 0x41, 0x49, 0x49, 0x7a ,  // 0x47   G
  0x7f, 0x08, 0x08, 0x08, 0x7f ,  // 0x48   H
  0x00, 0x41, 0x7f, 0x41, 0x00 ,  // 0x49   I
  0x20, 0x40, 0x41, 0x3f, 0x01 ,  // 0x4a   J
  0x7f, 0x08, 0x14, 0x22, 0x41 ,  // 0x4b   K
  0x7f, 0x40, 0x40, 0x40, 0x40 ,  // 0x4c   L
  0x7f, 0x02, 0x0c, 0x02, 0x7f ,  // 0x4d   M
  0x7f, 0x04, 0x08, 0x10, 0x7f ,  // 0x4e   N
  0x3e, 0x41, 0x41, 0x41, 0x3e ,  // 0x4f   O
  0x7f, 0x09, 0x09, 0x09, 0x06 ,  // 0x50   P
  0x3e, 0x41, 0x51, 0x21, 0x5e ,  // 0x51   Q
  0x7f, 0x09, 0x19, 0x29, 0x46 ,  // 0x52   R
  0x46, 0x49, 0x49, 0x49, 0x31 ,  // 0x53   S
  0x01, 0x01, 0x7f, 0x01, 0x01 ,  // 0x54   T
  0x3f, 0x40, 0x40, 0x40, 0x3f ,  // 0x55   U
  0x1f, 0x20, 0x40, 0x20, 0x1f ,  // 0x56   V
  0x3f, 0x40, 0x38, 0x40, 0x3f ,  // 0x57   W
  0x63, 0x14, 0x08, 0x14, 0x63 ,  // 0x58   X
  0x07, 0x08, 0x70, 0x08, 0x07 ,  // 0x59   Y
  0x61, 0x51, 0x49, 0x45, 0x43 ,  // 0x5a   Z
  0x00, 0x7f, 0x41, 0x41, 0x00 ,  // 0x5b   [
  0x02, 0x04, 0x08, 0x10, 0x20 ,  // 0x5c   backslash
  0x00, 0x41, 0x41, 0x7f, 0x00 ,  // 0x5d   ]
  0x04, 0x02, 0x01, 0x02, 0x04 ,  // 0x5e   ^
  0x40, 0x40, 0x40, 0x40, 0x40 ,  // 0x5f   _
  0x00, 0x01, 0x02, 0x04, 0x00 ,  // 0x60   `
  0x20, 0x54, 0x54, 0x54, 0x78 ,  // 0x61   a
  0x7f, 0x48, 0x44, 0x44, 0x38 ,  // 0x62   b
  0x38, 0x44, 0x44, 0x44, 0x20 ,  // 0x63   c
  0x38, 0x44, 0x44, 0x48, 0x7f ,  // 0x64   d
  0x38, 0x54, 0x54, 0x54, 0x18 ,  // 0x65   e
  0x08, 0x7e, 0x09, 0x01, 0x02 ,  // 0x66   f
  0x0c, 0x52, 0x52, 0x52, 0x3e ,  // 0x67   g
  0x7f, 0x08, 0x04, 0x04, 0x78 ,  // 0x68   h
  0x00, 0x44, 0x7d, 0x40, 0x00 ,  // 0x69   i
  0x20, 0x40, 0x44, 0x3d, 0x00 ,  // 0x6a   j
  0x7f, 0x10, 0x28, 0x44, 0x00 ,  // 0x6b   k
  0x00, 0x41, 0x7f, 0x40, 0x00 ,  // 0x6c   l
  0x7c, 0x04, 0x18, 0x04, 0x78 ,  // 0x6d   m
  0x7c, 0x08, 0x04, 0x04, 0x78 ,  // 0x6e   n
  0x38, 0x44, 0x44, 0x44, 0x38 ,  // 0x6f   o
  0x7c, 0x14, 0x14, 0x14, 0x08 ,  // 0x70   p
  0x08, 0x14, 0x14, 0x18, 0x7c ,  // 0x71   q
  0x7c, 0x08, 0x04, 0x04, 0x08 ,  // 0x72   r
  0x48, 0x54, 0x54, 0x54, 0x20 ,  // 0x73   s
  0x04, 0x3f, 0x44, 0x40, 0x20 ,  // 0x74   t
  0x3c, 0x40, 0x40, 0x20, 0x7c ,  // 0x75   u
  0x1c, 0x20, 0x40, 0x20, 0x1c ,  // 0x76   v
  0x3c, 0x40, 0x30, 0x40, 0x3c ,  // 0x77   w
  0x44, 0x28, 0x10, 0x28, 0x44 ,  // 0x78   x
  0x0c, 0x50, 0x50, 0x50, 0x3c ,  // 0x79   y
  0x44, 0x64, 0x54, 0x4c, 0x44 ,  // 0x7a   z
  0x00, 0x08, 0x36, 0x41, 0x00 ,  // 0x7b   {
  0x00, 0x00, 0x7f, 0x00, 0x00 ,  // 0x7c   |
  0x00, 0x41, 0x36, 0x08, 0x00 ,  // 0x7d   }
  0x10, 0x08, 0x08, 0x10, 0x08 ,  // 0x7e   ~
  0x00, 0x00, 0x00, 0x00, 0x00 ,  // 0x7f
  0x7e, 0x11, 0x11, 0x11, 0x7e ,  // 0x80   A  // Русские символы
  0x7f, 0x49, 0x49, 0x49, 0x33 ,  // 0x81   Б
  0x7f, 0x49, 0x49, 0x49, 0x36 ,  // 0x82   В
  0x7f, 0x01, 0x01, 0x01, 0x03 ,  // 0x83   Г
  0xe0, 0x51, 0x4f, 0x41, 0xff ,  // 0x84   Д
  0x7f, 0x49, 0x49, 0x49, 0x41 ,  // 0x85   E
  0x77, 0x08, 0x7f, 0x08, 0x77 ,  // 0x86   Ж
  0x41, 0x49, 0x49, 0x49, 0x36 ,  // 0x87   З
  0x7f, 0x10, 0x08, 0x04, 0x7f ,  // 0x88   И
  0x7c, 0x21, 0x12, 0x09, 0x7c ,  // 0x89   Й
  0x7f, 0x08, 0x14, 0x22, 0x41 ,  // 0x8A   K
  0x20, 0x41, 0x3f, 0x01, 0x7f ,  // 0x8B   Л
  0x7f, 0x02, 0x0c, 0x02, 0x7f ,  // 0x8C   M
  0x7f, 0x08, 0x08, 0x08, 0x7f ,  // 0x8D   H
  0x3e, 0x41, 0x41, 0x41, 0x3e ,  // 0x8E   O
  0x7f, 0x01, 0x01, 0x01, 0x7f ,  // 0x8F   П
  0x7f, 0x09, 0x09, 0x09, 0x06 ,  // 0x90   P
  0x3e, 0x41, 0x41, 0x41, 0x22 ,  // 0x91   C
  0x01, 0x01, 0x7f, 0x01, 0x01 ,  // 0x92   T
  0x47, 0x28, 0x10, 0x08, 0x07 ,  // 0x93   У
  0x1c, 0x22, 0x7f, 0x22, 0x1c ,  // 0x94   Ф
  0x63, 0x14, 0x08, 0x14, 0x63 ,  // 0x95   X
  0x7f, 0x40, 0x40, 0x40, 0xff ,  // 0x96   Ц
  0x07, 0x08, 0x08, 0x08, 0x7f ,  // 0x97   Ч
  0x7f, 0x40, 0x7f, 0x40, 0x7f ,  // 0x98   Ш
  0x7f, 0x40, 0x7f, 0x40, 0xff ,  // 0x99   Щ
  0x01, 0x7f, 0x48, 0x48, 0x30 ,  // 0x9A   Ъ
  0x7f, 0x48, 0x30, 0x00, 0x7f ,  // 0x9B   Ы
  0x00, 0x7f, 0x48, 0x48, 0x30 ,  // 0x9C   Э
  0x22, 0x41, 0x49, 0x49, 0x3e ,  // 0x9D   Ь
  0x7f, 0x08, 0x3e, 0x41, 0x3e ,  // 0x9E   Ю
  0x46, 0x29, 0x19, 0x09, 0x7f ,  // 0x9F   Я
  0x20, 0x54, 0x54, 0x54, 0x78 ,  // 0xA0   a
  0x3c, 0x4a, 0x4a, 0x49, 0x31 ,  // 0xA1   б
  0x7c, 0x54, 0x54, 0x54, 0x28 ,  // 0xA2   в
  0x7c, 0x04, 0x04, 0x04, 0x0c ,  // 0xA3   г
  0xe0, 0x54, 0x4c, 0x44, 0xfc ,  // 0xA4   д
  0x38, 0x54, 0x54, 0x54, 0x18 ,  // 0xA5   e
  0x6c, 0x10, 0x7c, 0x10, 0x6c ,  // 0xA6   ж
  0x44, 0x44, 0x54, 0x54, 0x28 ,  // 0xA7   з
  0x7c, 0x20, 0x10, 0x08, 0x7c ,  // 0xA8   и
  0x7c, 0x41, 0x22, 0x11, 0x7c ,  // 0xA9   й
  0x7c, 0x10, 0x10, 0x28, 0x44 ,  // 0xAA   к
  0x20, 0x44, 0x3c, 0x04, 0x7c ,  // 0xAB   л
  0x7c, 0x08, 0x10, 0x08, 0x7c ,  // 0xAC   м
  0x7c, 0x10, 0x10, 0x10, 0x7c ,  // 0xAD   н
  0x38, 0x44, 0x44, 0x44, 0x38 ,  // 0xAE   o
  0x7c, 0x04, 0x04, 0x04, 0x7c ,  // 0xAF   п
  0x7C, 0x14, 0x14, 0x14, 0x08 ,  // 0xB0   p
  0x38, 0x44, 0x44, 0x44, 0x20 ,  // 0xB1   c
  0x04, 0x04, 0x7c, 0x04, 0x04 ,  // 0xB2   т
  0x0C, 0x50, 0x50, 0x50, 0x3C ,  // 0xB3   у
  0x30, 0x48, 0xfc, 0x48, 0x30 ,  // 0xB4   ф
  0x44, 0x28, 0x10, 0x28, 0x44 ,  // 0xB5   x
  0x7c, 0x40, 0x40, 0x40, 0xfc ,  // 0xB6   ц
  0x0c, 0x10, 0x10, 0x10, 0x7c ,  // 0xB7   ч
  0x7c, 0x40, 0x7c, 0x40, 0x7c ,  // 0xB8   ш
  0x7c, 0x40, 0x7c, 0x40, 0xfc ,  // 0xB9   щ
  0x04, 0x7c, 0x50, 0x50, 0x20 ,  // 0xBA   ъ
  0x7c, 0x50, 0x50, 0x20, 0x7c ,  // 0xBB   ы
  0x7c, 0x50, 0x50, 0x20, 0x00 ,  // 0xBC   ь
  0x28, 0x44, 0x54, 0x54, 0x38 ,  // 0xBD   э
  0x7c, 0x10, 0x38, 0x44, 0x38 ,  // 0xBE   ю
  0x08, 0x54, 0x34, 0x14, 0x7c ,  // 0xBF   я
};

//********************************************************
void setup()  {
  pinMode(8, OUTPUT); // 7, 8- питание на дисплей
  pinMode(7, OUTPUT);
  digitalWrite(8, 1);
  digitalWrite(7, 0);
  pinMode(KEY_OK, INPUT); pinMode(KEY_L, INPUT); pinMode(KEY_R, INPUT); pinMode(KEY_P, INPUT); // настраиваем кнопки на вход
  digitalWrite(KEY_OK, HIGH); digitalWrite(KEY_L, HIGH); digitalWrite(KEY_R, HIGH); digitalWrite(KEY_P, HIGH); // подтяжка к плюсу питания
  VCC = ReadVcc();
  Inicialize();  //Инициализация дисплея
  Clear_LCD();
  ukazatel();//fillTriangle(1, mode * 10); // указатель меню
  // drawChar(1, mode * 10, 1, ">");
  //char Str2[8] = {'a', 'r', 'd', 'u', 'i', 'n', 'o'};
  /*   print(10, 0, 1, "Oscillograf");
   print(10, 10, 1, "Generator");
   print(10, 20, 1, "DDSgenerator");
   print(10, 30, 1, "Terminal");
   print(10, 40, 1, "Analizator");*/
  for (byte i = 0; i < 7; i++)
  {
    strcpy_P(buffer, (char*)pgm_read_word(&(string_table[i]))); // необходимые функции и расшифровки, просто скопируйте
    print(10, i * 10, 1, (buffer));
    //delay( 500 );
  }
  /*print(10, 0, 1, "Осциллограф");
  print(10, 10, 1, "Генератор");
  print(10, 20, 1, "DDSгенератор");
  print(10, 30, 1, "Терминал");
  print(10, 40, 1, "Анализатор");
  print(10, 60, 1, "V bat="); // *************/
  float_print(48, 60, 1, VCC, 2);
  Update();
  delay(KEY_DELAY);

  while (digitalRead(KEY_OK)) { // цикл, пока не нажали кнопку ОК
    // опрос кнопок
    if (!digitalRead(KEY_R)) {
      mode++;
      if (mode == 6  ) mode = 0;
      ukazatel();
    }
    if (!digitalRead(KEY_L)) {
      mode--;
      if (mode == 255) mode = 5;
      ukazatel();
    }
    Update();
  } // цикл нажатия ОК
  // нажали кнопку ОК из меню, инициализируем и выходим из меню
  count = 0; //countX = 0; // восстанавливаем на всякий случай переменные
  if (mode == 0) FreqCount.begin(1000);
  if (mode == 1) {
    InitTimersSafe();
    bool success = SetPinFrequencySafe(GEN_PIN, freq);
    pwmWrite(GEN_PIN, PWM * 2.55);
  }
  if (mode == 2) {
    InitTimersSafe();
    bool success = SetPinFrequencySafe(GEN_PIN, 200000);
  }
  if (mode == 4 || mode == 5) DDRC = 0x00; PORTC = 0x00; Serial.begin(115200); // razv = 0;// весь порт С (это A0...A5) на вход и без подтяжки
}
// беcконечный цикл - по сути прыгаем в подпрограммы
void loop() {
  switch (mode) {
    // Прыгаем в выбранный режим из главного меню
    case 0 : Oscil();        break; // "выпадаем" в осцилл
    case 1 : Generator();    break; // "выпадаем" в генератор
    case 2 : DdsGenerator(); break; // "выпадаем" в DDS генератор
    case 3 : Terminal();     break; // "выпадаем" в USART приемник
    case 4 : LogAnalyzer();  break; // "выпадаем" в Анализатор
    case 5 : LogAnalyzer();  break; // "выпадаем" в Анализатор
  }
}
//  === Читаем с АЦП данные и помещаем их в буфер === //
void ReadAdc() {
  if (razv % 10) { // (razv>0)        // если развертка без задержек всяких (с 1 по 7)
    ADCSRA = 0b11100000 | (8 - (razv % 10)); // установили делитель (/2 - не работает, так что начинаем с /4)
    for (int i = 0; i < BUFSIZE; i++) { // цикл для чтения
      while (!(ADCSRA & 0x10));     // ждем готовность АЦП
      ADCSRA |= 0x10;               // запускаем следующее преобразование
      adcBuf[i] = ADCH;             // записываем данные в массив
    }
    delay(0.3 * BUFSIZE);           // компенсация задержки по сравнению с разверткой 0...
  } else {                          // развертка с задержками (delay)
    ADCSRA = 0b11100111;            // делитель на /128
    for (int i = 0; i < BUFSIZE; i++) { // цикл для чтения
      while (!(ADCSRA & 0x10));     // ждем готовность АЦП
      ADCSRA |= 0x10;               // запускаем следующее преобразование
      delayMicroseconds(500);       // делаем задержку
      adcBuf[i] = ADCH;             // записываем данные в массив
    }
  }
}

// === Чтение цифровых данных со всего порта C (A0...A5) === //
/*void ReadDig() {
  if (razv == 9) {
    for (int i = 0; i < BUFSIZE; i++) adcBuf[i] = PINC; // быстро читаем данные
  } else {
    for (int i = 0; i < BUFSIZE; i++) {
      adcBuf[i] = PINC;  // читаем данные
      delayMicroseconds((9 - razv) * 100);
    }
  }
}*/

// ==== функция вывода float ====
void float_print(byte x, byte y, boolean color, float num, byte zn) { // последняя переменная- кол знаков после запятой
  char c[20];
  long d = num;
  byte m = 1;
  while (d > 9) {
    d = d / 10;
    m++;
  }
  print(x, y, color, dtostrf(num, m, zn, c));
}

// ==== Считывание напряжения питания ардуинки (Vcc) ====
#define Vref11 1.212 // для точной подстройки результата измерений
float ReadVcc() {
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); // устанавливаем вход АЦП на VCC !!!
  delay(1);  // задержка для устаканивания АЦП. Можно её избежать, если ADMUX как в строке выше (не менялся)
  ADCSRA |= _BV(ADSC);                                    // запуск АЦП преобразования
  while (bit_is_set(ADCSRA, ADSC));                       // ждем, пока АЦП отработает (выставит бит)
  return (Vref11 * 1023.0) / (ADCL | (ADCH << 8));        // результат преобразования в вольтах
}

/*int GetCountsOfDigits(int n) //найти количество цифр в числе https://habr.com/ru/post/269237/
{
  return n < 10 ? 1 :
         n < 100 ? 2 :
         n < 1000 ? 3 :
         n < 10000 ? 4 :
         n < 100000 ? 5 :
         n < 1000000 ? 6 :
         n < 10000000 ? 7 :
         n < 100000000 ? 8 :
         n < 1000000000 ? 9 : 10;
}*/

// ==== найти количество цифр в числе   https://habr.com/ru/post/269237/
/*static int stringSize(long x) {
  long p = 10;
  for (int i = 1; i < 19; i++) {
    if (x < p)
      return i;
    p = 10 * p;
  }
  return 19;
}*/

void vivod(byte x, byte y, byte n, byte k) { // //Печать цифры размером 24х48 пикселя (x, y, цифра, размер (1-24х48, 2-12х24))
  if ((n != 1) && (n != 4)) {
    fillRect(x + 3 / k, y, 15 / k, 5 / k, 1  ); // сегмент A
  }
  if ((n != 5) && (n != 6)) {
    fillRect(x + 16 / k, y + 3 / k, 5 / k, 20 / k, 1  ); // сегмент B
  }
  if (n != 2) {
    fillRect(x + 16 / k, y + 24 / k, 5 / k, 20 / k, 1  ); // сегмент C
  }
  if ((n != 1) && (n != 4) && (n != 7)) {
    fillRect(x + 3 / k, y + 42 / k,  15 / k, 5 / k, 1  ); // сегмент D
  }
  if ((n == 0) || (n == 2) || (n == 6) || (n == 8)) {
    fillRect(x, y + 24 / k, 5 / k, 20 / k, 1  ); // сегмент E
  }
  if ((n != 1) && (n != 2) && (n != 3) && (n != 7)) {
    fillRect(x, y + 3 / k, 5 / k, 20 / k, 1  ); // сегмент F
  }
  if ((n != 0) && (n != 1) && (n != 7)) {
    fillRect(x + 3 / k, y + 21 / k,  15 / k, 5 / k, 1  ); // сегмент G
  }
}

//====================================================указатель меню
void ukazatel() {
  fillRect(0, 0, 10, 60, 0);
  for (byte i = 0; i < 4; i++) {                                  // указатель меню
    drawLine(1 + i, mode * 10 + i, 1 + i, mode * 10 + 6 - i, 1);
  }
  Update();
  delay(KEY_DELAY);
}


//===================================================Инициализация дисплея
void Inicialize() {
  pinMode(RES,   OUTPUT);
  pinMode(CS,    OUTPUT);
  pinMode(Data,  OUTPUT);
  pinMode(Clock, OUTPUT);
  // Инициализация дисплея
  dWrite(RES, 1);
  dWrite(Clock, 0);
  dWrite(Data, 0);
  dWrite(CS, 0);
  delay(20);
  dWrite(CS, 1);
  SendByte(0, 0x2F);           // Power control set(charge pump on/off)
  SendByte(0, 0xA4);
  SendByte(0, 0xAF);           // экран вкл/выкл
  //Clear_LCD();
  //Update();
}

//=======================================================Управление пинами
void dWrite(byte pin, byte val) {
  byte bit = digitalPinToBitMask(pin);
  volatile byte *out;
  out = portOutputRegister(digitalPinToPort(pin));
  (val) ? *out |= bit : *out &= ~bit;
}

//=========================================================Отправка 9 байт
void SendByte(byte mode, byte c) {
  dWrite(CS, 0);
  (mode) ? dWrite(Data, 1) : dWrite(Data, 0);
  dWrite(Clock, 1);
  for (byte i = 0; i < 8; i++) {
    dWrite(Clock, 0);
    (c & 0x80) ? dWrite(Data, 1) : dWrite(Data, 0);
    dWrite(Clock, 1);
    c <<= 1;
  }
  dWrite(Clock, 0);
}

//======================================================Очистка дисплея
void Clear_LCD() {
  for (int index = 0; index < 864 ; index++) {
    LCD_RAM[index] = (0x00);
  }
}

//=====================================================Обновить дисплей
void Update() {
  for (byte p = 0; p < 9; p++) {
    SendByte(0, 0xB0 | p);
    SendByte(0, 0x00);
    SendByte(0, 0x10);
    for (byte col = 0; col < 96; col++) {
      SendByte(1, LCD_RAM[(96 * p) + col]);
    }
  }
}

//===================================================Нарисовать пиксель
void drawPixel (byte x, byte y, boolean color) {
  if ((x < 0) || (x >= 96) || (y < 0) || (y >= 68)) return;
  if (color) LCD_RAM[x + (y / 8) * 96] |= _BV(y % 8);
  else       LCD_RAM[x + (y / 8) * 96] &= ~_BV(y % 8);
}

//=====================================================Нарисовать букву
void drawChar(byte x, byte y, boolean color, unsigned char c) {
  if ((x >= 96) || (y >= 68) || ((x + 4) < 0) || ((y + 7) < 0)) return;
  if (c < 128)            c = c - 32;
  if (c >= 144 && c <= 175) c = c - 48;
  if (c >= 128 && c <= 143) c = c + 16;
  if (c >= 176 && c <= 191) c = c - 48;
  if (c > 191)  return;
  for (byte i = 0; i < 6; i++ ) {
    byte line;
    (i == 5) ? line = 0x0 : line = pgm_read_byte(font + (c * 5) + i); // line = EEPROM.read((c * 5) + i);
    for (byte j = 0; j < 8; j++) {
      (line & 0x1) ? drawPixel(x + i, y + j, color) : drawPixel(x + i, y + j, !color);
      line >>= 1;
    }
  }
}

//========================================================Вывод строки
void print(byte x, byte y, boolean color, char *str) {
  unsigned char type = *str;
  if (type >= 128) x = x - 3;
  while (*str) {
    drawChar(x, y, color, *str++);
    unsigned char type = *str;
    (type >= 128) ? x = x + 3 : x = x + 6;
  }
}

//========================================================Вывод числовых значений
void print(byte x, byte y, boolean color, long num) {
  char c[20];
  print(x, y, color, ltoa(num, c, 10));
}

//====================================================Рисование линии
void drawLine(byte x0, byte y0, byte x1, byte y1, boolean color) {
  int steep = abs(y1 - y0) > abs(x1 - x0);
  if (steep) {
    swap(x0, y0);
    swap(x1, y1);
  }
  if (x0 > x1) {
    swap(x0, x1);
    swap(y0, y1);
  }
  int dx, dy;
  dx = x1 - x0;
  dy = abs(y1 - y0);
  int err = dx / 2;
  int ystep;
  (y0 < y1) ?  ystep = 1 : ystep = -1;
  for (; x0 <= x1; x0++) {
    (steep) ? drawPixel(y0, x0, color) : drawPixel(x0, y0, color);
    err -= dy;
    if (err < 0) {
      y0 += ystep;
      err += dx;
    }
  }
}

//========================================Рисование вертикальной линии
void drawFastVLine(byte x, byte y, byte h, boolean color) {
  drawLine(x, y, x, y + h - 1, color);
}

//======================================Рисование горизонтальной линии
void drawFastHLine(byte x, byte y, byte w, boolean color) {
  drawLine(x, y, x + w - 1, y, color);
}

//=====================================Рисование залитый прямоугольник
void fillRect(byte x, byte y, byte w, byte h, boolean color) {
  for (byte i = x; i < x + w; i++) {
    drawFastVLine(i, y, h, color);
  }
}

//=====================================Рисование битового нолика
void printnull(byte a, byte i) {
  drawPixel(29 + a - i * 4, 64, 1);
  drawFastVLine(28 + a - i * 4, 65, 2, 1); // и вывести биты на дисплей
  drawFastVLine(30 + a - i * 4, 65, 2, 1);
  drawPixel(29 + a - i * 4, 67, 1);
}


// === Осциллоскоп === //
void Oscil() {
  // установка опорного напряжения АЦП и настройка входа АЦП
  ADMUX = vRef ? 0b01100011 : 0b11100011;
  // === Обработка кнопок === //
  if (!digitalRead(KEY_L))   switch (menu) { // кнопка лево:)
      case 0 : vRef = !vRef;                                                             break; // меняем опорное напряжение
      case 1 : razv--; if (razv == 255) razv = 6;                                        break; // меняем развертку
      case 2 : grOffset -= 20; if (grOffset < 0) grOffset = 0;                           break; // листаем график в паузе
    }
  if (!digitalRead(KEY_R))  switch (menu) { // кнопка право:)
      case 0 : vRef = !vRef;                                                             break; // меняем опорное напряжение
      case 1 : razv++; if (razv == 7) razv = 0;                                          break; // меняем развертку
      case 2 : grOffset += 20; if (grOffset > BUFSIZE - LCDX) grOffset = BUFSIZE - LCDX; break; // листаем график в паузе
    }
  if (!digitalRead(KEY_OK))  switch (++menu) {
      case 2: grOffset = 0; pause = 1;                                                   break; // вход в паузу - антидребезг типа
      case 3: menu = 0; pause = 0;                                                       break; // перебор меню
    }
  if (!digitalRead(KEY_P)) {
    menu = 2; pause = 1;
  }
  // === Ведём рассчеты === //
  if (!pause) { // если нет паузы
    ReadAdc();  // то снимаем осциллограмму
    // == Вычисляем максимальное и минимальное значение сигнала == //
    vMax = 0; vMin = 0xFF;
    for (int y = 1; y < 255; y++) {
      if (vMax < adcBuf[y]) vMax = adcBuf[y];  // пока 255, но надо экспериментировать
      if (vMin > adcBuf[y]) vMin = adcBuf[y];
    }
    vSync = (vMax - vMin) / 2 + vMin; // уровень синхронизации по середине уровня сигнала
    // == Определение точки синхронизации == //
    bool flagZero = 0; grOffset = 0; // сброс флага и точки синхронизации
    // Ищем перепад от меньшего уровня к большему
    for (int y = 1; y < BUFSIZE - LCDX; y++) { // смотрим весь массив данных АЦП
      if (adcBuf[y] < vSync) flagZero = 1; // нашли меньше, чем синхра (перепад сигнала в минус) - ставим флаг
      if (flagZero && adcBuf[y] > vSync) {
        grOffset = y;  // нашли больше, чем синхра (перепад сигнала в плюс) - запомнили и выходим из цикла
        break;
      }
    }
    // === Считаем частоту сигнала === //
    if (vRef && vMax * VCC / 255 > 2.7) { // если можем замерить аппаратно - меряем
      if (FreqCount.available()) count = FreqCount.read(); // вывод частоты по готовности счетчика частоты сигнала
    } else { // === Меряем частоту сигнала программно === //
      flagZero = 0; count = 0; // сброс флага и счетчика
      for (int y = grOffset; y < BUFSIZE - LCDX; y++)  { // смотрим массив от точки синхронизации до конца
        if (adcBuf[y] < vSync) flagZero = 1; // нашли меньше, чем синхра (перепад сигнала в минус) - выставляем флаг
        if (flagZero && adcBuf[y] > vSync) { // нашли больше, чем синхра (перепад сигнала в плюс) - отловили полный период
          switch (razv) { // считем частоту периода
            case 6: count = 1000000 / ((y - grOffset - 1) * 3.25);   break; // делитель 4
            case 5: count = 1000000 / ((y - grOffset)  * 3.25) / 2;  break; // делитель 8
            case 4: count = 1000000 / ((y - grOffset)  * 3.25) / 4;  break; // делитель 16
            case 3: count = 1000000 / ((y - grOffset)  * 3.25) / 8;  break; // делитель 32
            case 2: count = 1000000 / ((y - grOffset)  * 3.25) / 16; break; // делитель 64
            case 1: count = 1000000 / ((y - grOffset)  * 3.25) / 32; break; // делитель 128
            case 0: count = 1000000 / ((y - grOffset)  * 510);       break; // делитель 128 тоже
          }
          break;
        }
      }
    }
    count = count * OVERCLOCK / 16.0; // пересчет частоты на разные кварцы
  } // закончили вести рассчеты
  Clear_LCD(); // чистим экран...
  // === Отрисовка меню осцилла ===
  if (menu == 0) TextColor = 0; else TextColor = 1;
  if (vRef) float_print(0, 0, TextColor, VCC, 1); else  print(0, 0, TextColor, "1.1");
  if (menu == 1) TextColor = 0; else TextColor = 1;
  print(24, 0, TextColor, razv);
  if (menu == 2) { // тут задержка для компенсации с остальными задержками
    delay(200);
    //print(48, 0, 0, " Pause ");
    strcpy_P(buffer, (char*)pgm_read_word(&(string_table[8]))); // необходимые функции и расшифровки, просто скопируйте
    print(48, 0, 0, (buffer));
  } else TextColor = 1;
  if (!pause) float_print (48, 0, 1, count / 1000000.0, 6); // вывод частоты
  else { // рисуем прокрутку в режиме паузы
    temp = grOffset / 8;
    drawFastHLine(temp, 10, 6, 1);  drawFastHLine(temp, 9, 6, 1);
  } // шкала прокрутки
  if (vRef && vMax * VCC / 255 > 2.7)  drawChar(42, 0, 1, 97); // если замер аппаратный - "a"
  else  drawChar(42, 0, 1, 112); // иначе "p"
  float_print(78, 14, 1, vMax * (vRef ? VCC : 1.1) / 255, 1); // рисуем максимальное напряжение сигнала
  float_print(78, 54, 1, vMin * (vRef ? VCC : 1.1) / 255, 1); // рисуем минимальное  напряжение сигнала
  // if (vMax==0xFF) for(int y=0; y<4; y++) display.drawLine(y,9,y,47, BLACK); // превышение максимума АЦП
  // == Отрисовка сетки == //
  for (byte i = 11; i < 68; i = i + 14) {
    drawFastHLine( 0, i, 2, 1);  // черточки слева
  }
  for (byte i = 67; i > 10; i = i - 4) {
    drawPixel(24, i, 1);
    drawPixel(48, i, 1);
    drawPixel(72, i, 1);
  }
  for (byte i = 8; i < 96; i = i + 4) {
    drawPixel(i, 39, 1);
  }
  // == Отрисовка графика == //
  for (int y = 0; y < 92; y++)  drawLine(y + 4, 67 - adcBuf[y + grOffset] / 7, y + 4, 67 - adcBuf[y + grOffset + 1] / 7, 1);
  Update();
}

//**********************************************************************************
void Generator() {
  // обработка кнопок и отрисовка одновременно
  Clear_LCD();
  //print(0, 0, 1, "Частота");
  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[9]))); // необходимые функции и расшифровки, просто скопируйте
  print(0, 0, 1, (buffer));
  if (!digitalRead(KEY_OK)) {
    if (menu++ == 7) menu = 0;  // переходим по разрядам / меняем ШИМ
    delay(KEY_DELAY);
  }
  if (menu == 7) { // меняем ШИМ
    if (!digitalRead(KEY_L)) {
      PWM--;
      if (PWM < 0)   PWM = 100;
      delay(KEY_DELAY);
    }
    if (!digitalRead(KEY_R)) {
      PWM++;
      if (PWM > 100) PWM = 0;
      delay(KEY_DELAY);
    }
    pwmWrite(GEN_PIN, PWM * 2.55); // задали ШИМ
    TextColor = 0; // если меняем шим, то рисуем его инверсным
  } else { // меняем частоту
    if (!digitalRead(KEY_L)) {
      if (freq > stepFreq)  freq -= stepFreq;          // не даем выйти за допустимый диаппазон
      SetPinFrequencySafe(GEN_PIN, freq / (OVERCLOCK / 16.0)); // задали частоту
      delay(KEY_DELAY); // задержка для кнопок
    }
    if (!digitalRead(KEY_R)) {
      if ((freq + stepFreq) < 10000000) freq += stepFreq; // не даем выйти за допустимый диаппазон
      SetPinFrequencySafe(GEN_PIN, freq / (OVERCLOCK / 16.0)); // задали частоту
      delay(KEY_DELAY); // задержка для кнопок
    }
    TextColor = 1; // если ШИМ не меняем, то выбираем обычный текст
#define XFREQ 10 // ======== // и, выделяем декаду частоты полосочками
    stepFreq = pow(10, (byte)menu); if (menu > 1) stepFreq++; // устраняем глючность pow, почему 10 в степени 2 = 99?
    temp = 42 - menu * 6;
    if (temp < 10) temp = 0;
    drawFastHLine(temp, XFREQ + 10, 5, 1); // выделяем декаду частоты полосочками
    drawFastHLine(temp, XFREQ + 9, 5, 1);
  }
  // рисуем уровень ШИМ (инверсия текста задана ранее)
  drawFastHLine(15, 39 , 54, !TextColor); // сверху полоска для большей инверсности
  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[7]))); // необходимые функции и расшифровки, просто скопируйте
  print(15, 40, TextColor, (buffer));   print(45, 40, TextColor, PWM);
  print(57, 40, TextColor, "% "); // выводим уровень ШИМ
  if (PWM < 10) {
    drawChar(45, 40, TextColor, 32); // " "
    print(51, 40, TextColor, PWM);
  }
  drawChar(63, 40, TextColor, 32); // " "
  if (PWM == 100)  {
    print(45, 40, TextColor, 100);
    print(63, 40, TextColor, "%");
  }
  TextColor = 1; // убираем инверсию при выводе частоты
  // print(42 - stringSize(freq) * 6, XFREQ, 1, freq); // вывод частоты
  float_print (0, XFREQ, 1, freq / 1000000.0, 6); // вывод частоты
  print(50, XFREQ, 1, "MHz");
  // отрисовка графика PWM
  for (char x = 17; x < 67; ) {
    if (PWM != 0)  drawFastHLine(x, 26, PWM / 4, 1); x += PWM / 4;             // верх
    if (PWM != 0 && PWM != 100)  drawFastVLine(x, 26, 10, 1);                  // спад
    if (PWM != 100)  drawFastHLine(x, 36, 25 - PWM / 4, 1); x += 25 - PWM / 4; // низ
    if (PWM != 0 && PWM != 100 && x < 43)  drawFastVLine( x, 26, 11, 1);       // подъем
  }
  Update(); // вываливаем буфер на дисплей
}

//**********************************************************************************
void DdsGenerator() {
  static const byte ddsWave[][32] PROGMEM = {
    2, 10, 21, 37, 57, 79, 103, 127, 152, 176, 198, 218, 234, 245, 253, 255, 253, 245, 233, 218, 198, 176, 152, 128, 103, 79, 57, 37, 21, 10, 2, 0, // El83_sinNew
    16, 32, 48, 64, 80, 96, 112, 128, 143, 159, 175, 191, 207, 223, 239, 255, 239, 223, 207, 191, 175, 159, 143, 128, 112, 96, 80, 64, 48, 32, 16, 0, // El83_treugNew
    8, 16, 25, 33, 41, 49, 58, 66, 74, 82, 90, 99, 107, 115, 123, 132, 140, 148, 156, 165, 173, 181, 189, 197, 206, 214, 222, 230, 239, 247, 255, 0, // El83_pilaNew
    255, 247, 239, 230, 222, 214, 206, 197, 189, 181, 173, 165, 156, 148, 140, 132, 123, 115, 107, 99, 90, 82, 74, 66, 58, 49, 41, 33, 25, 16, 8, 0 // El83_pilaObrNew
  };
  byte ddsCount = 0;
  // Рисуем DDS-генератор
  Clear_LCD(); // режим работы, заголовок
  for (byte i = 0; i < 96;)  drawLine(i, 36 - pgm_read_byte(&ddsWave[menu][i % 32]) / 10, i, 36 - pgm_read_byte(&ddsWave[menu][(i++) % 32]) / 10, 1);
  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[10 + menu]))); // необходимые функции и расшифровки, просто скопируйте
  print(5, 40, 1, (buffer));
  /*switch (menu) { //..........
    case 0:    strcpy_P(buffer, (char*)pgm_read_word(&(string_table[10]))); // необходимые функции и расшифровки, просто скопируйте
      print(3, 40, 1, (buffer));  break;
    case 1:    strcpy_P(buffer, (char*)pgm_read_word(&(string_table[11]))); // необходимые функции и расшифровки, просто скопируйте
      print(3, 40, 1, (buffer));  break;
    case 2:    strcpy_P(buffer, (char*)pgm_read_word(&(string_table[12]))); // необходимые функции и расшифровки, просто скопируйте
      print(3, 40, 1, (buffer));  break;
    case 3:    strcpy_P(buffer, (char*)pgm_read_word(&(string_table[13]))); // необходимые функции и расшифровки, просто скопируйте
      print(3, 40, 1, (buffer));  break;
  }*/
  Update();
  while (digitalRead(KEY_R)) { // выводим выбранный сигнал, пока не нажали кнопку
    pwmWrite(DDS_PIN, pgm_read_byte(&ddsWave[menu][(ddsCount++) & 0x1F]));
  }
  if (++menu == 4) menu = 0; // нажали кнопку - переключаем режим
  delay(KEY_DELAY); // чтоб кнопки нормально нажимались
}

//**********************************************************************************
void Terminal() {
  //const long speedUart[] = { 9600, 19200, 38400, 57600, 115200, 300, 600, 1200, 2400, 4800 };
  const PROGMEM  uint32_t speedUart[] = { 9600, 19200, 38400, 57600, 115200, 250000, 300, 600, 1200, 2400, 4800 }; // ------
  Clear_LCD();
  //print(10, 0, 1, "Скорость:");
  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[14]))); // необходимые функции и расшифровки, просто скопируйте
  print(10, 0, 1, (buffer));
  print(10, 10, 1, speedUart[menu]);
  Update();
  if (!digitalRead(KEY_L))  {
    menu--;
    if (menu == 255) menu = 10;
    // print(28, 10, 1, "   ");
    print(10, 10, 1, speedUart[menu]);
    Update();
    delay(KEY_DELAY);
  }
  if (!digitalRead(KEY_R))  {
    menu++;
    if (menu == 11)   menu = 0;
    // print(28, 10, 1, "   ");
    print(10, 10, 1, speedUart[menu]);
    Update();
    delay(KEY_DELAY);
  }
  if (!digitalRead(KEY_OK)) {
    Clear_LCD();
    if (!digitalRead(KEY_P)) pause = 1; // включаем флаг, что пауза была нажата для вкл дополн функции
    else  drawFastHLine(10, 66 , 75, 1);
    Serial.begin(speedUart[menu] * (16 / OVERCLOCK));
    drawChar(0, 0, 0, 62);
    Update();
    delay(KEY_DELAY);
    Update();
    byte x = 0;
    byte y = 0;
    while (1) {
      if (Serial.available()) { // Если в буфере есть данные
        if (x > 90) { // проверяем, не заполнен ли экран
          x = 0;
          y = y + 8;
        }
        byte symbol = (Serial.read());
        if ((symbol == 10 || symbol == 13) && x != 0) {
          x = 0;
          y = y + 8;
        }
        if (y > 56) {
          y = 0;
          x = 0;
        }
        if (symbol != 10 && symbol != 13) {
          if (symbol == 127 || symbol == 8) { // Del
            if (x == 0) {
              x = 90;
              if (y != 0) {
                y = y - 8;
              }
              else y = 56;
            }
            else
            {
              x += -6;
            }
            drawChar(x, y, 1, 32); // " "
          }
          else {
            symbol3 = symbol2;
            symbol2 = symbol1;
            symbol1 = symbol;
            if (symbol > 191) { //русские буквы
              if (symbol > 239) {
                drawChar(x, y, 1, symbol - 112); // 
              }
              else drawChar(x, y, 1, symbol - 48); // 
            }
            else drawChar(x, y, 1, symbol);  // печать байта в дисплей
            // print(x + 6, y, 1, "               ");
            //for (byte i = 0; i < 7; i++) drawLine(x + 6, y + i, 95, y + i, 0); // очистить место
            //for (byte i = 0; i < 7; i++) drawFastHLine(x + 6, y + i, 90, 0);// не работает
            x = x + 6;
          }
        }
        for (byte i = 0; i < 7; i++) drawLine(x, y + i, 95, y + i, 0); // очистить место
        if (pause == 1) { // вывод бинарных кодов символов только если были нажаты ДВЕ кнопки
          /*char p[17]; //
          itoa (symbol, p, 16); // перевод в шестнадцатеричное
           print(0, 60, 1, p); // отображение шестнадцатеричного*/
          drawFastHLine(0, 65, 96, 0); // очистить место
          drawFastHLine(0, 66, 96, 0);
          drawPixel(31, 67, 1); // разделение байтов
          drawPixel(63, 67, 1);
          for (byte i = 0; i < 8; i++) { // разложить код символа на биты
            if (bitRead(symbol3, i)) drawFastVLine(29 - i * 4, 64, 4, 1); // биты предпредпоследнего байта
            else printnull(0, i);
            if (bitRead(symbol2, i)) drawFastVLine(61 - i * 4, 64, 4, 1); // биты предпоследнего байта
            else printnull(32, i);
            if (bitRead(symbol1, i)) drawFastVLine(94 - i * 4, 64, 4, 1); // биты последнего байта смещены на пиксель вправо
            else printnull(65, i); // биты последнего байта смещены на пиксель вправо
          }
          /*for (byte i = 0; i < 8; i++) { // разложить код символа на биты
            if (bitRead(symbol2, i) == 1)  drawFastVLine(29 + 32 - i * 4, 64, 4, 1); //  drawChar(i * 6, 56, 1, 49);
            else {//  drawChar(i * 6, 56, 1, 48);
              drawPixel(29 + 32 - i * 4, 64, 1);
              drawFastVLine(28 + 32 - i * 4, 65, 2, 1); // и вывести биты на дисплей
              drawFastVLine(30 + 32 - i * 4, 65, 2, 1);
              drawPixel(29 + 32 - i * 4, 67, 1);
            }
          }
          for (byte i = 0; i < 8; i++) { // разложить код символа на биты
            if (bitRead(symbol1, i) == 1)  drawFastVLine(29 + 64 - i * 4, 64, 4, 1); //  drawChar(i * 6, 56, 1, 49);
            else {//  drawChar(i * 6, 56, 1, 48);
              drawPixel(29 + 64 - i * 4, 64, 1);
              drawFastVLine(28 + 64 - i * 4, 65, 2, 1); // и вывести биты на дисплей
              drawFastVLine(30 + 64 - i * 4, 65, 2, 1);
              drawPixel(29 + 64 - i * 4, 67, 1);
            }
          }*/
        }
        Update();
      }
    }
  }
  Update();
}

//*********************************************************************************
void LogAnalyzer() {
  // обрабатываем кнопки
  if (!digitalRead(KEY_L)) switch (menu) {
      case 0 : razv--; if (razv == 255) razv = 19; delay(KEY_DELAY);                       break; // меняем развертку
      case 1 : trig--; if (trig == 255) trig = 3; delay(KEY_DELAY);                        break; // меняем trigger
      case 2 : grOffset -= 10; if (grOffset < 0) grOffset = 0;                             break; // листаем график в паузе
    }
  if (!digitalRead(KEY_R)) switch (menu) {
      case 0 : razv++; if (razv == 20) razv = 0; delay(KEY_DELAY);                         break; // меняем развертку
      case 1 : trig++; if (trig == 4) trig = 0; delay(KEY_DELAY);                          break; // меняем trigger
      case 2 : grOffset += 5; if (grOffset >= BUFSIZE - 50) grOffset = BUFSIZE - 50;       break; // листаем график в паузе
    }
  if (!digitalRead(KEY_OK)) switch (++menu) {
      //case 2 : grOffset = 0; pause = 1;                                                    break; // вход в паузу
      case 3 : menu = 0;     pause = 0;                                                    break; // перебор меню
        delay(KEY_DELAY);
    }
  if (!digitalRead(KEY_P)) {
    menu = 2; grOffset = 0; pause = 1; flag = 0;// вход в паузу
  }

  //if (!pause) ReadDig(); // если нет паузы - читаем данные

  Clear_LCD(); // чистим дисплей
  if ( menu == 0 ) print(0, 0, 0, razv);  // выводим развертку
  else print(0, 0, 1, razv);              // выводим развертку
  if ( menu == 1 ) print(24, 0, 0, trig); // выводим trigger
  else print(24, 0, 1, trig);             // выводим trigger
  if (pause) {
    if (flag == 0) {
      print(50, 0, 1, "Waiting");
      Update();
      switch (trig) {
        case 0 : scanLoadBus(BURST); flag = 1; menu = 2;           break;
        case 1 : scanLoadBus(MASTER_RISE); flag = 1; menu = 2;     break;
        case 2 : scanLoadBus(MASTER_FALL); flag = 1; menu = 2;     break;
        case 3 : scanLoadBus(PATTERN); flag = 1; menu = 2;         break;
      }
    }
    //print(8, 0, 1, " Pause ");
    strcpy_P(buffer, (char*)pgm_read_word(&(string_table[8]))); // необходимые функции и расшифровки, просто скопируйте
    print(50, 0, 1, (buffer));
    // рисуем прокрутку в режиме паузы
    drawLine( grOffset / 8, 8, grOffset / 8 + 6, 8,  1); // шкала прокрутки
    drawLine( grOffset / 8, 9, grOffset / 8 + 6, 9,  1); // шкала прокрутки
    delay(200);
  }
  for (byte chan = 0; chan < 4; chan++) // последовательный перебор 4-х каналов
  { int show_index = StartIndex >> 1;
    int read_index;
    print(0, 10, 1, (show_index + grOffset));
    drawLine(50, 10, 50, 15, 1);
    print(54, 10, 1, (grOffset + grOffset + 50));
    for (int x = 0; x < 48; x++ ) {
      read_index = show_index + grOffset + x;
      if (read_index >= BUFSIZE) read_index = read_index - BUFSIZE;
      if ((x % 5) == 0) {
        drawLine(x + x, 60, x + x, 64, 1); // если номер отсчета кратен 10 - рисуем черточку
        if (((grOffset + x - 100) / 5 % 10) == 0) print (x + x - 2, 60, 1, (grOffset + x) / 50);
      }
      //Декомпресиия старшей части
      if ((adcBuf[read_index] >> 4) & (1 << chan)) // выделяем соответствующий бит текущего канала
      {
        drawLine(x + x, chan * 10 + 20, x + x, chan * 10 + 24, 1); //если высокий уровень
      }
      else
      {
        drawPixel(x + x, chan * 10 + 24, 1); // если низкий уровень
      }
      //Декомпресиия младшей части
      if (adcBuf[read_index] & (1 << chan)) // выделяем соответствующий бит текущего канала
      {
        drawLine(x + x + 1, chan * 10 + 20, x + x + 1, chan * 10 + 24, 1); //если высокий уровень
      }
      else
      {
        drawPixel(x + x + 1, chan * 10 + 24, 1); // если низкий уровень
      }
    } // end y loop
  }
  Update();
}

//============================================
// Функция расчета номера замера с которого начинается вывод данных включая предисторию
// иными словами, от замера сработки тригера отнимаем (с учетом перекрутки буффера) величину предистории
//============================================
int calc_StartIndex() {
  //=========== расчет точки останова чтобы не затереть начало сработки триггера
  if (cur_index < history) //если значение тек. индекса меньше, чем размер истории
  {
    return ((BUFSIZE + BUFSIZE) + cur_index - history); //считаем таким образом
  }
  else
  {
    return (cur_index - history); // ну и если больше, то просто отнимаем
  }//=========== End расчет точки останова чтобы не затереть начало сработки триггера
}

//===========================================================
// Процедура записи данных данных в буфер
// тут происходит запись сжатых данных и увеличение текущего индекса замера
// запись будет происходить при нечетном значении индекса (т.е., считаны уже два значения)
void rec_data(byte cur_data) {
  if ((cur_index & 1)) //если текущее значение индекса нечетное (нечетные числа в двоичном виде всегда заканчиваются на 1)
  {
    adcBuf[cur_index >> 1] = ((prev_data << 4) | cur_data); // пишим обьеденное число так сказать КОМПРЕССИЯ :)
  }
  prev_data = cur_data; //запоминаем текущее состояние шины
  cur_index++; //увеличиваем номер замера
  if (cur_index == (BUFSIZE + BUFSIZE)) cur_index = 0; //если достигли конца буфера - сброс и все сначала
} //end rec_data

//============================================
// Процедура сканирования шины и отлов сработки триггеров
// При сканировании шины будем учитывать тип триггера: typeTriggerEvent
// Чтобы считать значения младших четырех бит (PORTC0, PORTC1, PORTC2, PORTC3)
// нужно делать так: my_var1 = PINC & b00001111; // такой операцией мы обнулим старшие 4 бит и получим ТОЛЬКО младшие 4-ре
//============================================
void scan_bus(byte typeTriggerEvent) {
  while (!trigger_event)
  {
    // ПЕРВЫМ делом нужно считать данные с шины, а уже потом будем разбираться со всякими сверками и расчетами
    byte cur_data = (PINC & B00001111); //сразу же считываем состояние пинов порта
    // ниже отладочная пришлепка для примерной оценки скорости оцифровки
    // по частоте переключения пина будем судить о скорости
    // данные считаны, теперь проверяем есть ли условия сработки триггера и если ДА, запоминаем номер замера (0-1024)
    switch (typeTriggerEvent) //ветвимся в зависимости от режима триггера
    {
      // BURST Mode Любое изменение на любом канале
      case (BURST):
        if (cur_data != prev_data)
        {
          trigger_event = true; //ставим признак сработки триггера
          StartIndex = calc_StartIndex(); // расчет номера замера с которого будем делать вывод
        }
        break;
      //---------------------------------
      // master channel RISE (переход из 0 в 1)
      case (MASTER_RISE):
        if (~prev_data & cur_data & 1)
        {
          trigger_event = true; //ставим признак сработки триггера
          StartIndex = calc_StartIndex(); // расчет номера замера с которого будем делать вывод
        }
        break;
      //---------------------------------
      // master chanel FALL (переход из 1 в 0)
      case (MASTER_FALL):
        if (prev_data & ~cur_data & 1)
        {
          trigger_event = true; //ставим признак сработки триггера
          StartIndex = calc_StartIndex(); // расчет номера замера с которого будем делать вывод
        }
        break;
      //---------------------------------
      // PATTERN (определенное состояние шины)
      case (PATTERN):
        //if ((cur_data & pattern_mask) == pattern_value) // если проверка на совпадение условий прошла
        if ((cur_data & 7) == 5) // если проверка на совпадение условий прошла
        {
          trigger_event = true; //ставим признак сработки триггера
          StartIndex = calc_StartIndex(); // расчет номера замера с которого будем делать вывод
        }
        break;
    } //End switch (typeTriggerEvent)
    rec_data(cur_data); //запись данных в буффер
    if ((cur_index == 0) && Serial.available()) break; //единственный способ прервать - при нулевом индексе что-то появилось в ком-порту
    delayMicroseconds(razv * 4);
  } //end while
}// end load data

//====== read_bus ============
// тупо пишем состояние каналов
void read_bus() {
  prevMillis = millis(); //запоминаем время начала записи истории
  while (cur_index != StartIndex)
  {
    rec_data(PINC & B00001111); //запись данных в буффер
    delayMicroseconds(razv * 4);
  } //end while
  curMillis = millis(); //время конца записи истории
}

//==================
// инициализация перед замерами
//Обнуляем буффер, сбрасываем индексы и запоминаем состояние шины
void init_logic() {
  for (int i = 0; i <= BUFSIZE; i++) //в цикле затиранием рабочий массив
  {
    adcBuf[i] = 0;
  }
  cur_index = 0; // сбрасываем счетчик замеров
  StartIndex = 0; // сбрасываем положение начала данных для вывода
  time_disc = 0; // сбрасываем значения интервала одной выборки
  trigger_event = false; //сбрасываем признак сработки триггера
  prev_data = (PINC & B00001111); //сформируем пред. замер состояние пинов порта
} // end logic init

//========================================================
// процедура отображения полезной (и не очень) информации
void show_info(byte typeTriggerEvent) {
  // немного расчетов "для красоты": для считывания ((BUFSIZE+BUFSIZE)-history) количества замеров требуется
  // сколько-то милисекунд (или, если умножить на 1000 - микросекунд) посчитаем, сколько микросекунд уходит на одну выборку (естественно, ОЧЕНЬ-ОЧЕНЬ ПРИМЕРНО)
  time_disc = ((curMillis - prevMillis) * 1000) / (((BUFSIZE + BUFSIZE) - history)); //время выборки одного замера
  Serial.println(F("---------------------------------------------------------"));
  Serial.print(F("Record size=")); Serial.print((BUFSIZE + BUFSIZE) - history, DEC); Serial.print(F("  Pre-history size=")); Serial.println(history, DEC);
  Serial.print(F("Current Trigger: "));
  switch (typeTriggerEvent) {
    case (MASTER_RISE):
      Serial.println(F("Master (Ch0) RISE (0->1)"));
      break;
    case (MASTER_FALL):
      Serial.println(F("Master (Ch0) FALL (1->0)"));
      break;
    case (BURST):
      Serial.println(F("Change on any channel"));
      break;
    case (PATTERN):
      Serial.print(F("Bus status"));
      Serial.print(F("  MASK / VALUE =")); Serial.print(pattern_mask, HEX); Serial.print(F(" / ")); Serial.println(pattern_value, HEX);
      break;
  } //end switch
  //Serial.print("Start/Stop =");Serial.print(prevMillis);Serial.print(" / "); Serial.print(curMillis);
  //Serial.print("[Ind Start = ");Serial.print(StartIndex);Serial.print("] ");
  Serial.print(F("Time: 1 div = ")); Serial.print(time_disc); Serial.println(F(" (uSec)"));
  Serial.println(F("******************************************"));
}

//==============================
// Процедура орисовки результатов в виде псевдографика
// процедура достаточно медленная, но буфер к этому моменту заполнен, так что скорость тут УЖЕ не важна
//==============================
void show_graph() {
  for (byte chan = 0; chan < 4; chan++) // последовательный перебор 4-х каналов
  {
    Serial.print(F("Ch N")); Serial.print(chan);
    int show_index = StartIndex >> 1;
    Serial.print(F("\t"));
    for (int y = 0; y < BUFSIZE; y++ )
    {
      if (show_index == BUFSIZE) show_index = 0;
      //Декомпресиия старшей части
      if ((adcBuf[show_index] >> 4) & (1 << chan)) // выделяем соответствующий бит текущего канала
      {
        Serial.print(F("-")); //если высокий уровень
      }
      else
      {
        Serial.print(F("_")); // если низкий уровень
      }
      //Декомпресиия младшей части
      if (adcBuf[show_index] & (1 << chan)) // выделяем соответствующий бит текущего канала
      {
        Serial.print(F("-")); //если высокий уровень
      }
      else
      {
        Serial.print(F("_")); // если низкий уровень
      }
      show_index++;
    }// end y loop
    Serial.println();
  } //end loop chan
  // ===========================================
  // для красоты - типа вывод шкалы разметки :)
  // ===========================================
  Serial.print(F("\t")); //код TAB для ровности вывода
  for (int x = 0; x < (BUFSIZE + BUFSIZE); x++)
  {
    if (x == history) Serial.print(F("^")); //обозначим начало отсчета
    if ((x % 10) == 0) Serial.print(F("|")); // если номер отсчета кратен 10 - рисуем черточку
    if ((x % 10 != 0) && (x != history)) Serial.print(F(".")); // иначе рисуем точку
  }
  Serial.println(F(".")); //завершающая точка и перевод строки
  // рисуем "типа" разметку
  Serial.print(F("\t"));//код TAB для ровности вывода
  for (int x = 0; x < (BUFSIZE + BUFSIZE); x++)
  {
    if ((x % 10) == 0)
    {
      Serial.print(x / 10);
      if (x < 100)                  Serial.print(F("         ")); //чтобы не плыли значения относительно шкалы, в зависимости от разрядности тек. замера
      else if ((x >= 100) && (x < 1000))   Serial.print(F("        ")); // для случая ЗАМЕР - два знака
      else if ((x >= 1000) && (x < 10000)) Serial.print(F("       ")); // для случая ЗАМЕР - три знака
    }
  }
  Serial.println();

}//end show_graph

//===================================================
//фантик обертка сканирования шины (scan_bus+read_bus)
//1 фаза - ждем триггер или отмену пользователем
//2 фаза - заполнение буфера данными
void scanLoadBus(byte typeTriggerEvent)
{
  Serial.print(F("Waiting......"));
  //digitalWrite(TriggerPin, LOW); // светрдиод TRG выключаем
  init_logic(); //инициализируем буфер и переменные
  scan_bus(typeTriggerEvent);
  if (trigger_event) //если триггер сработал
  {
    read_bus(); // пишем историю в буфер
    //digitalWrite(TriggerPin,HIGH); // светoдиод TRG включаем
    Serial.println(F("Ok!"));
    show_info(typeTriggerEvent); //выводим информацию
    show_graph(); //и следом выводим график
  }
  else
  {
    Serial.println(F("User break")); //а если мы вернулись без признака сработки триггера - значит ожидание было прервано пользователем
    Serial.flush(); //очищаем буфер
  }
} //end scanLoadBus
//
//

Sketch uses 18 628 bytes (60%) of program storage space. Maximum is 30 720 bytes.
Global variables use 1 844 bytes (90%) of dynamic memory, leaving 204 bytes for local variables. Maximum is 2 048 bytes.

Делал так, чтобы по максимуму освбодить динамическую память. Буфер можно увеличить с 600 до 700, будет свободно 104 байта динамической для безопасной работы.

Какую бы функцию ещё добавить, чтоб задействовать оставшуюся программнную память?

serhiy58
Offline
Зарегистрирован: 19.06.2019
diksen! Не пропадай!
Ты начал шикарный проэкт мультитестероскопа и забросил его. Не делай этого! 
Твоя задумка просто гениальна! Я повторил данный проэкт и он вполне прилично работает, но есть некоторые  но... И порешать эти проблемы мне пока не светит, поскольку в ближайшем будущем мне программистом не стать.
О наболевшем:
- при измерении ТТестером он сразу показывает 82нФ(это конденсатор фильтра Ф/Ген), можна порешать переключателем, но это ... Ведь есть свободные  А5...А7, может их можна пекреназначить, а А3 оставить под кнопки?
- если отказаться от бутика и шиться с помощью БСБ АСП с авердутки, используя компеляцию хексов без бутика из под Ардуино-ИДЕ, освободится место и тогда возможно влезет автоматика от Electronik83 и терминал;
- если использовать идею AS31979 (п.4135), то вообще шоколадка получится из "г... и палок за 4 бакса" 
Огромная просьба не бросать проэкт и помочь с програмным обеспеченим.
Железо готово, - нужна Ваша помощь...
Это фото со снятым дисплеем 
serhiy58
Offline
Зарегистрирован: 19.06.2019

Плиииз!