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

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

Можно, но я там кажысь void OscilSlow сломал))). Меню отличается от автора. Вот вам под 5110.

//Страница проэкта  http://srukami.inf.ua/pultoscop_v25110.html
// === От меня [Electronik83] === mailTo: gsmkillersevsk@mail.ru
// 1. Оптимизировал, причесал код
// 2. Сделал красивей главное меню, добавил новый пункт
// 3. Отображение частатоы и напряжения в осцилле при 1Кгц и 2,5в (был баг автора)
// 4. Новый режим - медленный осцилл - доделал...
// 5. Оптимизация.....
// 6. Убрал програмную растяжку сигнала из осцилла (развертка 7 и 8)
// 7. Изменил отрисовку сигнала в осцилле (больше нету этих раздвоений сигнала)
// 8. Убрал нахрен кнопку питания, и все что с ней сязано - проще тумблер (переключатель) поставить...
// 9. Анимировал главное меню. Пофиксил мерцание.
//10. Вставил свое меню генератора, DDS-генератора, терминала.
//11. Добавил немного новой анимации.
//12. Провел эксперимент с DDS-генератором и регулировкой частоты - не сделать.
// === ver19 ==
//13. В осциле - Сделал автосинхронизацию по серединке амплитуды сигнала <= подумать над внешней синхрой !!!
//14. В осциле - показываем масимальное и минимальное значение амплитуды сигнала справа
//15. Оптимизация некоторых участков кода
//16. Атмега теперь сама меряет напряжение своего питания, не надо ничего мультиметром мерять
//17. В осцилле - сделал более удобный вывод верхней строки в осцилле, "устатичил" вывод частоты.
//      - теперь нет этого дерганья из Гц в кГц (и обратно) - просто выводим в Мгц.
//18. Добавил цифровой анализатор (входы A1, A2, A3, A4, A5) - что то мне он не понравился (и мне тоже).
/////////////// smokok ///////////////  
//19. Добавил Авто-опорное 5.0,1.1,0.2 
//20. Добавил Меню экрана (регулировка контрастности и включение подсвеки экрана).
//21. Добавил температуру. Пин термистора (10к) в А0 и +5, доп резистор (10к) в А0 и -5
//22. Разогнал PULTOSCOPE благодаря progrik
//23. Пока только вырезал  UART приемник
//    В планах внедрить Коэффициент заполнения
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
#include <FreqCount.h>
#include <EEPROM.h>
#include <math.h>
#include <PWM.h>

// Настройки пользователя - можно менять
#define Ekran     8  //Экран///Pin///Подсветka
#define OVERCLOCK 16  // частота на которой работает Ардуино
float VCC = 5.0;      // напряжение питания (меряем мультиметром - СТАЛО НЕ НАДО)
Adafruit_PCD8544 display = Adafruit_PCD8544(7, 6, 4, 3, 2); // пины к которым у вас подключен дисплей
byte contrast; // контрастность дисплея
byte Set = 0; // Настройка экрана
bool BL; // Подсветка

#define KEY_L  11 // кнопка ЛЕВО  (можно любой пин)
#define KEY_OK 12 // кнопка ОК    (можно любой пин)
#define KEY_R  13 // кнопка ПРАВО (можно любой пин)
#define VBAT   A5 // любой свободный аналоговый пин для измерения напряжения АКБ 
#define KEY_DELAY 200

// Переменные, дефайны и прочяя хрень - менять со смыслом
#define GEN_PIN  9  // пин для генератора сигналов (не менять)
#define DDS_PIN  10 // пин для генератора dds (не менять)
byte mode = 0; // пункт главного меню
byte menu = 0; // пункт меню

#define BUFSIZE_SHORT 256 //progrik: буфер для рабочего режима, чем короче - тем быстрее отображение. чем длиннее - тем медленнее, но синхра сможет ловить более "растянутый" сигнал
#define BUFSIZE_LONG  512 //progrik: буфер для паузы. старый буфер был 800
#define PAUSE_OFFSET 2    //progrik: смещение графика за один кадр в режиме паузы. если для паузы указан достаточно длинный буфер, то можно увеличить до 2-3-4.., чтоб быстрее мотало
#define APP 2.7           //         2.7) это переход на аппаратный замер
#define NAPR 255          //         это напряжение сигнала
#define LCDX 80

byte adcBuf[BUFSIZE_LONG];
byte vSync = 30;   // уровень синхронизации
bool vRef = 1; // флаг опорного напряжения
bool pause = 0; // флаг режима паузы
byte razv = 4;
int grOffset = 0; // смещение графика в рабочем режиме
byte vMax, vMin;// максимальное и минимальное напряжение сигнала
int kdel = 5;         //bulat  автопредел 1,1/0,2 вольта
bool avtorazv = 1;    // Автоматический выбор развертки
bool flag = 0;
bool kzap = 0; //флаг коэффициента заполнения
unsigned long ok_press_time;

// Переменные для генератора
int PWM = 50;//стартовое значение ШИМ от 0 до 100 для генератора
unsigned long freq = 500; // стартовое значение частоты в Гц для генератора
unsigned long stepFreq = 0;

//int d=0; // можно подменить чем нибудь. count например....
unsigned long count = 0;
//long countX=0;
//uint32_t tic1;
//uint32_t tic2;
//int duty;
//long speedTTL=9600; //скорость терминала

void ShowMode(char y) { // выводим режим работы. y - строка, где рисовать
const char* const modeStr[] PROGMEM= { " Oscilloscop ",    "     PWM     ", "     DDS     ", "     АЧХ     ", " AHAJlи3ATOP ", "    ЭKPAH    "};
  display.setCursor(3, y);      // задаем позицию на экране
  display.print(modeStr[mode]); // выводим режим работы
}
void ShowModeInv(char y) { // выводим режим работы инверсно. y - строка, где рисовать
  display.drawLine(0, y, 83, y, BLACK);
  display.setTextColor(WHITE, BLACK);
  display.setCursor(0, y+1); display.println(" "); display.setCursor(78, y+1); display.println(" ");  // для красоты только
  ShowMode(y+1);  display.setTextColor(BLACK);
}
double Thermister(int RawADC) {
  double Temp;
  Temp = log(((10240000 / RawADC) - 10000)); // 10000=10кОм Для подстройки правим.
  Temp = 1 / (0.001129148 + (0.000234125 * Temp) + (0.0000000876741 * Temp * Temp * Temp));
  Temp = Temp - 273.15;   // Kelvin to Celcius
  return Temp;
}
void printTemp(void) {
  display.setTextColor(BLACK);
  display.setCursor(15, 37);
  display.print("Temp ");
  display.print(float(Thermister(analogRead(0))), 1); // (0) это аналог выход А0
}
// ==== Считывание напряжения питания ардуинки (Vcc) ====
#define Vref11 1.095 // для точной подстройки результата измерений
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));        // результат преобразования в вольтах
}

void setup()  {

  ////////////////////////////Экран//////////////////////////////
  //  Примечание. Экран становится темным,  решение проблемы.///////////////////////////////////////
  //  EEPROM.write(0, 50);  //  Записать закрытую строку,  Вгрузить,  и начать использовать.
  contrast = EEPROM.read(0);
  BL       = EEPROM.read(1);
  pinMode(Ekran, OUTPUT);
  digitalWrite(Ekran, BL);
  //////////////////////////////////////////////////////
  pinMode(KEY_OK, INPUT); pinMode(KEY_L, INPUT); pinMode(KEY_R, INPUT); // настраиваем кнопки на вход
  display.begin();
  display.setContrast(contrast);
  VCC = ReadVcc();
  while (digitalRead(KEY_OK)) { // цикл, пока не нажали кнопку ОК
    // опрос кнопок
    if (!digitalRead(KEY_R)) {
      mode++;
      if (mode > 5  ) mode = 0;
    }
    if (!digitalRead(KEY_L)) {
      mode--;
      if (mode == NAPR) mode = 5;
    }
    display.clearDisplay();  // чистим буфер экранчика
    display.setCursor(24, 19);  // выводим напряжение батареи
    display.setTextColor(WHITE, BLACK);
    display.print(" ");
    display.print(analogRead(VBAT)*VCC / 1024, 1);
    display.print(" V");

    // динамическая графика в скролле
#define COUNT 84 // ширина дисплея, в пикселях
    for (char a = 3; a < 14; a += 2) {
      display.drawLine((count + a   ) % COUNT, a + 4, (count + a   ) % COUNT, 40 - a, BLACK); // левые  полосочки по бокам
      display.drawLine((count - a + 83) % COUNT, a + 4, (count - a + 83) % COUNT, 40 - a, BLACK); // правые полосочки по бокам
    }
    if (count % 84 == 0) { // если больше не надо скроллить
      ShowModeInv(0);
      display.drawRect(21, 18, 42, 11, BLACK); // рамка вокруг картинки
      display.drawRect(19, 16, 46, 15, BLACK); // рамка вокруг картинки
      //        display.drawRect(16, 14, 52, 19, BLACK); // рамка вокруг картинки
    }
    printTemp(); //       Терпература
    display.display();
    delay(150); // задержка отображения уровня заряда, температуры и кнопок заодно
    //    display.display();
  } // цикл нажатия ОК
  // нажали кнопку ОК из меню, инициализируем и выходим из меню

  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) razv = 0;
  if (mode == 5) {
    DDRC = 0x00;  // весь порт С (это A0...A5) на вход и без подтяжки
    PORTC = 0x00;
  }
  // антидребезг кнопки ОК
  for (char a = 37; a > 0; a -= 2) {
    display.clearDisplay();  // - это вместо delay(200);
    ShowModeInv(a);
    display.display();
  }
  ok_press_time = millis(); //progrik: запоминаем время последнего нажатия
}
// безконечный цикл - по сути прыгаем в подпрограммы
void loop() {
  switch (mode) { // Прыгаем в выбранный режим из главного меню
    case 0 : Oscil();        break; // "выпадаем" в осцилл
    case 1 : Generator();    break; // "выпадаем" в генератор
    case 2 : DdsGenerator(); break; // "выпадаем" в DDS генератор
    //    case 3 : Terminal();     break; // "выпадаем" в USART приемник
    case 3 : OscilSlow();    break; // "выпадаем" в медленный осцилл
    case 4 : LogAnalyzer();  break; // "выпадаем" в анализатор
    case 5 : Menu_ekrana();  break; // "выпадаем" в меню экрана
  }
}
//  === Читаем с АЦП данные и помещаем их в буфер === //
void ReadAdc() {
  uint16_t len;
  len = (pause) ? BUFSIZE_LONG : BUFSIZE_SHORT;
  if (razv % 10) { // (razv>0)        // если развертка без задержек всяких (с 1 по 7)
    ADCSRA = 0b11100000 | (8 - (razv % 10)); // установили делитель (/2 - не работает, так что начинаем с /4)
    for (uint16_t i = 0; i < len; i++) { // цикл для чтения
      while (!(ADCSRA & 0x10));     // ждем готовность АЦП
      ADCSRA |= 0x10;               // запускаем следующее преобразование
      adcBuf[i] = ADCH;             // записываем данные в массив
    }
    //    delay(0.3 * BUFSIZE);           // компенсация задержки по сравнению с разверткой 0...
  } else {                          // развертка с задержками (delay)
    ADCSRA = 0b11100111;            // делитель на /128
    for (uint16_t i = 0; i < len; i++) { // цикл для чтения
      while (!(ADCSRA & 0x10));     // ждем готовность АЦП
      ADCSRA |= 0x10;               // запускаем следующее преобразование
      delayMicroseconds(500);       // делаем задержку
      adcBuf[i] = ADCH;             // записываем данные в массив
    }
  }
}

//progrik: для расчета напряжений ---------------
void calc_val()
{
  vMax = 0; vMin = 0xFF;
  uint16_t l = (pause) ? BUFSIZE_LONG : BUFSIZE_SHORT;
  for (uint16_t x = 0; x < l; x++) {
    if (vMax < adcBuf[x]) vMax = adcBuf[x];  // пока 255, но надо экспериментировать
    if (vMin > adcBuf[x]) vMin = adcBuf[x];
  }

  // == Вычисляем максимальное и минимальное значение сигнала == //
  vSync = (vMax - vMin) / 2 + vMin; // уровень синхронизации по середине уровня сигнала
  // == Определение точки синхронизации == //
  bool flagZero = 0; grOffset = 0; // сброс флага и точки синхронизации
  // Ищем перепад от меньшего уровня к большему
  for (uint8_t x = 1; x < BUFSIZE_SHORT - LCDX; x++) { // смотрим весь массив данных АЦП
    if (adcBuf[x] < vSync) flagZero = 1; // нашли меньше, чем синхра (перепад сигнала в минус) - ставим флаг
    if (flagZero && adcBuf[x] > vSync) {
      grOffset = x;  // нашли больше, чем синхра (перепад сигнала в плюс) - запомнили и выходим из цикла
      break;
    }
  }
  // === Считаем частоту сигнала === //
  if (vRef && vMax * VCC / NAPR > APP) { // если можем замерить аппаратно - меряем
    if (FreqCount.available()) count = FreqCount.read(); // вывод частоты по готовности счетчика частоты сигнала
  } else { // === Меряем частоту сигнала программно === //
    flagZero = 0; count = 0; // сброс флага и счетчика
    for (uint8_t x = grOffset; x < BUFSIZE_SHORT - LCDX; x++)  { // смотрим массив от точки синхронизации до конца
      if (adcBuf[x] < vSync) flagZero = 1; // нашли меньше, чем синхра (перепад сигнала в минус) - выставляем флаг
      if (flagZero && adcBuf[x] > vSync) { // нашли больше, чем синхра (перепад сигнала в плюс) - отловили полный период
        switch (razv) { // считем частоту периода
          case 6: count = 1000000 / ((x - grOffset - 1) * 3.25);    break; // делитель 4
          case 5: count = 1000000 / ((x - grOffset)  * 3.25) / 2;  break; // делитель 8
          case 4: count = 1000000 / ((x - grOffset)  * 3.25) / 4;  break; // делитель 16
          case 3: count = 1000000 / ((x - grOffset)  * 3.25) / 8;  break; // делитель 32
          case 2: count = 1000000 / ((x - grOffset)  * 3.25) / 16; break; // делитель 64
          case 1: count = 1000000 / ((x - grOffset)  * 3.25) / 32; break; // делитель 128
          case 0: count = 1000000 / ((x - grOffset)  * 510);     break; // делитель 128 тоже
        }
        break;
      }
    }
  }
  count = count * OVERCLOCK / 16.0; // пересчет частоты на разные кварцы
}
// === Осциллоскоп === //
void Oscil() {
  // установка опорного напряжения АЦП и настройка входа АЦП
  ADMUX = vRef ? 0b01100011 : 0b11100011;
label_ReadAdc:
  // === Обработка кнопок === //
  if (!digitalRead(KEY_L))   switch (menu) { // кнопка лево:)
      case 0 : razv--; if (razv == NAPR) razv = 6;                                  break; // меняем развертку
      case 1 : grOffset -= PAUSE_OFFSET; if (grOffset < 0) grOffset = 0;                      break; // листаем график в паузе
    }
  if (!digitalRead(KEY_R))  switch (menu) { // кнопка право:)
      case 0 : razv++; if (razv == 7) razv = 0;                                   break; // меняем развертку
      case 1 : grOffset += PAUSE_OFFSET; if (grOffset > BUFSIZE_LONG - LCDX) grOffset = BUFSIZE_LONG - LCDX; break; // листаем график в паузе
    }
  if (!digitalRead(KEY_OK))
  {
    if ((millis() - ok_press_time) > 200) // подавление дребезга - ждем 200 мс со времени последнего нажатия
    {
      switch (++menu)
      {
        case 1: grOffset = 0; pause = 1; ReadAdc(); calc_val();    break; // вход в паузу - антидребезг типа
        case 2: menu = 0;  pause = 0;                              break; // перебор меню
      }
    }
    ok_press_time = millis(); //progrik: запоминаем время последнего нажатия кнопки OK
  }
  // === Ведём рассчеты === //
  if (!pause) { // если нет паузы
    ReadAdc();  // то снимаем осциллограмму
    calc_val();
  } // закончили вести рассчеты

  display.clearDisplay(); // чистим экран...
  /*      if (count < 10) kzap=true;
    if (kzap==true && count > 10){
    FreqCount.end();
    tic1 = asm_func(1);
    tic2 = asm_func(0);
    duty=(1/((float)tic1/tic2))*100;
    kzap=false;
    FreqCount.begin(1000);
    }
    if (kzap==false && count > 10 && duty < 100){
    display.setCursor(18,11);
    display.setTextColor(BLACK);
    display.print(duty);
    display.print("%");
    }*/
  // === Отрисовка меню осцилла ===
  if (vRef) display.print(VCC, 1); else {
    if (kdel == 5)display.print("1.1"); else display.print("0.2");
  }
  if (menu == 0) display.setTextColor(BLACK, WHITE);
  if (vRef && vMax * VCC / NAPR > APP) {
    display.print("a");  // если замер аппаратный - "ужирняем" точку
  }
  else  {
    display.print("п");  // иначе "П"рограмный
  }
  display.print(razv);
  if (menu == 1)
    if (!pause)  display.setTextColor(BLACK);
    else { // рисуем прокрутку в режиме паузы
      display.print("s");  display.drawLine(grOffset / 5.7, 8, grOffset / 5.7 + 7, 8, BLACK); display.drawLine(grOffset / 5.7, 9, grOffset / 5.7 + 7, 9, BLACK);
    } // шкала прокрутки
  display.setCursor(36, 0); display.print(count / 1000000.0, 6);  //покажем частоту
  display.setCursor(60, 11); display.print(vMax * (vRef ? VCC : 1.1) / NAPR, 2); // рисуем максималоное напряжение сигнала
  display.setCursor(65, 40); display.print(vMin * (vRef ? VCC : 1.1) / NAPR, 1); // рисуем минимальное  напряжение сигнала
  if (vMax == 0xFF) for (uint8_t x = 0; x < 5; x++) display.drawLine(x, 9, x, 47, BLACK); // указатель привышения напряжения на входе осцила

  // == Отрисовка сетки == //
  for (byte i = 47; i > 5; i = i - 7) {
    display.drawLine( 0, i, 2, i, BLACK);  // черточки слева
  }
  for (byte i = 47; i > 5; i = i - 3) {
    display.drawPixel(21, i, BLACK);  // вертикальный пунктир
    display.drawPixel(42, i, BLACK);
    display.drawPixel(63, i, BLACK);
  }
  for (byte i = 3; i < 84; i = i + 3) {
    display.drawPixel(i, 33, BLACK);
    display.drawPixel(i, 19, BLACK);
  }

  ///////////////авто razv//////////////
  if (avtorazv)
#define PER 1.3
  if (count > 4323.3 * PER) razv = 6;
  else if (count > 2434.5 * PER) razv = 5;
  else if (count > 0969.3 * PER) razv = 4;
  else if (count > 0486.8 * PER) razv = 3;
  else if (count > 0245.8 * PER) razv = 2;
  else if (count > 0120.1 * PER) razv = 1;
  else razv = 0;
  //bulat если зашкаливает включаем предел 5 в
  if (vMax == NAPR) {
    if (vRef == 0)
    {
      vRef = 1;
      ADMUX = 0b01100011;// выбор внутреннего опорного 5.0 В
      goto   label_ReadAdc;
    }
  }
  //bulat если  5 в  и уровень менее 1,1 в то вкл предел 1,1 в
  if (vMax <= 55) {
    if (vRef == 1)
    {
      vRef = 0;
      ADMUX = 0b11100011;// выбор внутреннего опорного 1,1В
      goto   label_ReadAdc;
    }
  }
  //bulat здесь автопредел 1,1/0,22 в,программный
  kdel = 5;
  if (vMax <= 55) {
    if (vRef == 0)
    {
      kdel = 1;
    }
  }
  // == Отрисовка графика == //
  for (uint8_t x = 0; x < 80; x++)
    display.drawLine(x + 4, 47 - adcBuf[x + grOffset] / kdel / 1.35, x + 4, 47 - adcBuf[x + grOffset + 1] / kdel / 1.35, BLACK);
  display.display(); // вываливаем буфер экрана на экран
}
////////////////////////////////////////////////////////////////////////////////////////////
// РЕЖИМ ГЕНЕРАТОРА
void Generator() {
  // обработка кнопок и отрисовка одновременно
  display.clearDisplay();
  ShowModeInv(0);
  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); // задали ШИМ
    display.setTextColor(WHITE, BLACK); // если меняем шим, то рисуем его инверсным
    display.drawLine(15, 39, 68 + (PWM == 100 ? 6 : 0) + (PWM < 10 ? -6 : 0), 39, BLACK); // сверху полоска для большей инверсности
  } 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); // задержка для кнопок
    }
    display.setTextColor(BLACK); // если ШИМ не меняем, то выбираем обычный текст
#define XFREQ 14 // ======== // и, выделяем декаду частоты полосочками
    stepFreq = pow(10, (byte)menu); if (menu > 1) stepFreq++; // устраняем глючность pow, почему 10 в степени 2 = 99?
    byte menu_t = menu; if (menu > 2) menu_t++; if (menu == 6) menu_t++; // делаем табуляцию частоты
    menu_t = 54 - menu_t * 6; // считаем положение полосочек
    display.drawLine(menu_t, XFREQ - 2, menu_t + 4, XFREQ - 2, BLACK); // рисуем полоски
    display.drawLine(menu_t, XFREQ - 3, menu_t + 4, XFREQ - 3, BLACK);
    display.drawLine(menu_t, XFREQ + 8, menu_t + 4, XFREQ + 8, BLACK);
    display.drawLine(menu_t, XFREQ + 9, menu_t + 4, XFREQ + 9, BLACK);
  }
  // рисуем уровень ШИМ (инверсия текста задана ранее)
  display.setCursor(15, 40); // ставим курсор))
  display.print(" PWM=");  display.print(PWM);  display.print("% "); // выводим уровень ШИМ
  display.setTextColor(BLACK); // убираем инверсию при выводе частоты
  // рисуем частоту с табуляцией
  display.setCursor(6, XFREQ);
  for (unsigned long freq_t = 1000000; freq_t>0; freq_t/=10) {
  if (freq>=freq_t) {
    display.print((freq/freq_t)%10); if (freq_t==1000000 || freq_t==1000) display.print("'"); }  else { 
    display.print("_");              if (freq_t==1000000 || freq_t==1000) display.print(" "); }
  }
  display.print(" Hz");
  // отрисовка графика PWM
  for (char x = 17; x < 67; ) {
    if (PWM != 0)             display.drawLine(x, 26, x + PWM / 4, 26, BLACK); x += PWM / 4; // верх
    if (PWM != 0 && PWM != 100) display.drawLine(x, 26, x, 36, BLACK);                   // спад
    if (PWM != 100)           display.drawLine(x, 36, x + 25 - PWM / 4, 36, BLACK); x += 25 - PWM / 4; // низ
    if (PWM != 0 && PWM != 100 && x < 43) display.drawLine( x, 36, x, 26, BLACK);        // подъем
  }
  display.display(); // вываливаем буфер на дисплей
}

// === DDS генератор ===
void DdsGenerator() {
  static const byte ddsWave[][32] PROGMEM = {        
    1,6,15,29,48,69,92,117,143,168,191,212,229,243,251,255,254,248,237,222,203,181,156,131,106,81,59,39,22,10,3,1,   // sinNew    
    1,18,35,52,69,86,103,120,137,154,171,188,205,222,239,255,239,223,207,191,175,159,143,127,111,95,79,63,47,31,15,1, // treugNew    
    1,9,17,25,33,41,49,57,65,73,81,89,97,105,113,121,129,137,145,153,161,169,177,185,193,201,209,217,225,235,245,255,  // pilaNew
    250,246,238,230,222,214,206,198,190,182,174,166,158,150,142,134,126,118,110,102,94,86,78,70,62,54,41,33,25,17,9,1 };// pilaObrNew
  const char* const ddsStr[] PROGMEM = { "          ", "           ", "       ", "             "};
  byte ddsCount=0;
  // Рисуем DDS-генератор
  display.clearDisplay(); ShowModeInv(0); // режим работы, заголовок
  for (byte i = 0; i < 84;) display.drawLine(i, 36 - pgm_read_byte(&ddsWave[menu][i % 32]) / 10, i, 36 - pgm_read_byte(&ddsWave[menu][(i++) % 32]) / 10, BLACK);
  display.setCursor(3, 40);  display.print(ddsStr[menu]);  display.display(); // отрисовали все
  while (digitalRead(KEY_R) && digitalRead(KEY_L) && digitalRead(KEY_OK)) { // выводим выбранный сигнал, пока не нажали кнопку
    pwmWrite(DDS_PIN, pgm_read_byte(&ddsWave[menu][(ddsCount++) & 0x1F]));
    //ResetAvrCheck();
  }
  if (++menu == 4) menu = 0; // нажали кнопку - переключаем режим
  delay(KEY_DELAY); // чтоб кнопки нормально нажимались
}

/*
  // UART приемник
  void Terminal() {
  const long speedUart[] = { 1200,2400,4800,9600,19200,38400,57600,115200 };
  display.clearDisplay();
  ShowModeInv(0);
  display.setCursor(15,15); display.println("Скорость:");
  display.setTextColor(WHITE, BLACK);
  display.setCursor(66, 25); display.println("+");
  display.setCursor(13, 25); display.print("-");
  display.setTextColor(BLACK);
  display.println(" ");
  display.print(speedUart[menu]);
  display.println(" ");
  display.setCursor(9,40); display.println("ОК - запуск");
  if(!digitalRead(KEY_L))  { menu--; if(menu==255) menu=7;   delay(KEY_DELAY);  }
  if(!digitalRead(KEY_R))  { menu++; if(menu==8)   menu=0;   delay(KEY_DELAY);  }
  if(!digitalRead(KEY_OK)) {
    Serial.begin(speedUart[menu]*(16/OVERCLOCK));
    display.clearDisplay();
    display.print(">");
    delay(KEY_DELAY);
    display.display();
    int x=83;
    while (1) {
      //ResetAvrCheck();
      if (Serial.available()) { // Если в буфере есть данные
        if(++x==84) { // проверяем, не пора ли экран очистить
          x=0;
          display.clearDisplay();
        }
        display.print((char)(Serial.read())); // печать байта в дисплей
        display.display(); // выводим символ
      }
    }
  }
  display.display();
  }
*/
void SetTextColor(char c) {
  if (c) display.setTextColor(WHITE, BLACK); else display.setTextColor(BLACK);
}

void OscilSlow() {
  // надо найти главного тормазоглу!!!!!!!!!

  ADMUX = vRef ? 0b01100011 : 0b11100011; // установка опорного напряжения АЦП
  //ADCSRA = 0b11100110; // делитель частоты АЦП - 64
  //ADCSRA = 0b11100010; //delitel 4 // кондер не успевает разряжаться
  ADCSRA = 0b11100011; //delitel 8
  ADCSRA |= 0x10; // запуск преобразования АЦП

  // === Обработка кнопок ===
  if (!digitalRead(KEY_L )) {
    switch (menu) { // тут идея такая - один раз опрашиваем кнопку, т.к. это медленно делается, потом через кейсы прыгаем.
      case 0 : vRef = !vRef;                      break;   // выбор опорного напряжения
      case 1 : razv--; if (razv == NAPR)  razv = 2;    break; // выбор развертки : 1000мс 500мс 100мс на клетку
      case 2 : grOffset -= 15; if (grOffset < 0)  grOffset = BUFSIZE_LONG; break; // режим паузы - прокручиваем осциллограмму
      case 3 : for (uint8_t x = 0; x < 26; x++) adcBuf[x] = 0; count = 0; grOffset = 0;
        display.clearDisplay(); display.setCursor(3, 20); display.print("RESET!");
        display.display(); delay(1000);    break;
    } delay(200);
  } // антидребезг кнопки
  if (!digitalRead(KEY_R )) {
    switch (menu) {
      case 0 : vRef = !vRef;                      break;   // выбор опорного напряжения
      case 1 : razv++; if (razv == 3)    razv = 0;    break; // выбор развертки : 1000мс 500мс 100мс на клетку
      case 2 : grOffset += 15; if (grOffset > BUFSIZE_LONG) grOffset = 0;  break; // режим паузы - прокручиваем осциллограмму
    } delay(200);
  } // антидребезг кнопки
  if (!digitalRead(KEY_OK)) {
    menu++; if (menu == 4) {
      menu = 0;  // перебор меню
      pause = 0;
    }
    if (menu == 2) {
      pause = 1;
      adcBuf[(count++) % BUFSIZE_SHORT] = NAPR; adcBuf[(count++) % BUFSIZE_SHORT] = 0; adcBuf[(count++) % BUFSIZE_SHORT] = NAPR; if (count > 79) grOffset = count - 80;
    } // отметка паузы на графике
    delay(200);
  } // антидребезг кнопки
  // === Отрисовка менюшки ===
  display.clearDisplay();  display.setCursor(0, 0);
  SetTextColor(menu == 0); if (vRef) display.print(VCC, 1); else display.print("1.1");
  SetTextColor(menu == 1); display.print(" ");   display.print(razv);
  SetTextColor(menu == 2); display.print(" S");  display.print(" ");
  display.print((grOffset % BUFSIZE_SHORT) / 2.02, 0);  display.print("%");
  SetTextColor(menu == 3); display.print(" R");
  display.setTextColor( BLACK);

  // == Отрисовка сетки == Новая //
  for (byte i = 47; i > 5; i = i - 7) {
    display.drawLine( 0, i, 2, i, BLACK);  // черточки слева
  }
  /* for(byte i=47;i>5;i=i-7) { display.drawPixel(17,i, BLACK); display.drawPixel(33,i, BLACK); display.drawPixel(49,i, BLACK); display.drawPixel(65,i, BLACK); display.drawPixel(81,i, BLACK); } // вертикальный пунктир
    for(byte i=1;i<84;i=i+8) { display.drawPixel(i,40, BLACK); display.drawPixel(i,26, BLACK); display.drawPixel(i,12, BLACK); }    // горизонтальный пунктир

    // === Отрисовка сетки Старая ===
    for(byte i=47;i>5;i=i-7) { display.drawLine( 0, i, 2, i, BLACK); } // черточки слева
    for(byte i=47;i>5;i=i-3) { display.drawPixel(21,i, BLACK);display.drawPixel(42,i, BLACK);display.drawPixel(63,i, BLACK); } // вертикальный пунктир
    for(byte i=3;i<84;i=i+3) { display.drawPixel(i,33, BLACK);display.drawPixel(i,19, BLACK);}                                 // горизонтальный пунктир
  */
  // === Отрисовка графика ===
  for (uint8_t x = 0; x < 80; x++) display.drawLine(x + 4, 47, x + 4, 47 - adcBuf[(x + grOffset) % BUFSIZE_SHORT] / 7, BLACK);
  if (pause) { // если пауза - рисуем шкалу прокрутки
    display.drawLine((grOffset % BUFSIZE_SHORT) / 2.3, 8, (grOffset % BUFSIZE_SHORT) / 2.3 + 5, 8, BLACK); // шкала прокрутки
    display.drawLine((grOffset % BUFSIZE_SHORT) / 2.3, 9, (grOffset % BUFSIZE_SHORT) / 2.3 + 5, 9, BLACK); // шкала прокрутки
  } else { // если не пауза, то снимаем показания
    while (!(ADCSRA & 0x10)); // ждем, пока АЦП считает
    adcBuf[count % BUFSIZE_SHORT] = ADCH; // сохраняем число из АЦП в массив
    if (count > 79) grOffset = count - 80;
    count++;
    if (count > BUFSIZE_LONG) count = BUFSIZE_SHORT; // зацикливаем счетчик
    //45мс - программа выполняется
    switch (razv) {
      case 0 : delay(55 * (OVERCLOCK / 16.0)); break; // секунда на клетку
      case 1 : delay(05 * (OVERCLOCK / 16.0)); break; // пол секунды на клетку
      case 2 : break; // без задержки.......
    }
  }
  display.display();
}

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

// ==== Логический анализатор ==== //
void LogAnalyzer() {
  // обрабатываем кнопки
  if (!digitalRead(KEY_L))   switch (menu) {
      case 0 : razv--; if (razv == NAPR) razv = 9; delay(400);                        break;
      case 1 : grOffset -= 20; if (grOffset < 0) grOffset = 0;                        break;
    }
  if (!digitalRead(KEY_R))  switch (menu) {
      case 0 : razv++; if (razv == 10) razv = 0; delay(400);                        break;
      case 1 : grOffset += PAUSE_OFFSET; if (grOffset > BUFSIZE_LONG - LCDX) grOffset = BUFSIZE_LONG - LCDX; break;   break;
    }
  if (!digitalRead(KEY_OK))  switch (++menu) {
      case 1: grOffset = 0; pause = 1; break; // вход в паузу
      case 2: menu = 0;     pause = 0; break; // перебор меню
    }

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

  display.clearDisplay(); // чистим дисплей
  display.print(razv);    // выводим развертку
  display.print(" ");
  if (pause) {
    display.print(" Пауза ");
    // рисуем прокрутку в режиме паузы
    display.drawLine(grOffset / 8, 8, grOffset / 8 + 6, 8, BLACK); // шкала прокрутки
    display.drawLine(grOffset / 8, 9, grOffset / 8 + 6, 9, BLACK); // шкала прокрутки
    delay(200);
  }
  for (byte i = 47; i > 12; i = i - 7) {
    display.drawLine( 0, i - 5, 2, i - 5, BLACK);  // черточки слева
    display.drawPixel(0, i, BLACK);
    display.drawPixel(2, i, BLACK);
  }
  // горизонтальный пунктир
  for (byte i = 5; i < 84; i = i + 3) {
    display.drawPixel(i, 47, BLACK);
    display.drawPixel(i, 40, BLACK);
    display.drawPixel(i, 33, BLACK);
    display.drawPixel(i, 26, BLACK);
    display.drawPixel(i, 19, BLACK);
  }
  for (int y = 0; y < 80; y++) {
    for (int x = 0; x < 5; x++) {
      display.drawLine(y + 4, 47 - (x * 7) - (bit_is_set(adcBuf[y + grOffset], 5 - x) ? 5 : 0), y + 4, 47 - (x * 7) - (bit_is_set(adcBuf[y + 1 + grOffset], 5 - x) ? 5 : 0), BLACK);
    }
  }
  display.display();
}
//////////////////////////////Экран/////////////////////////////////
void Menu_ekrana() {
  Set = 0; delay(200);
  while (digitalRead(KEY_OK)) {
    display.clearDisplay();
    if (Set == 0) display.setTextColor(WHITE, BLACK); else display.setTextColor(BLACK);
    display.setCursor(0 , 0); display.println("Сontr:"); display.setTextColor(BLACK);
    display.setCursor(60 , 0); display.println(contrast);
    display.setCursor(0 , 10); display.println("Light:"); display.setTextColor(BLACK);
    if (BL == 1) { display.setCursor(60 , 10); display.println("ON");} else
                 { display.setCursor(60 , 10); display.println("OFF");} display.display();
    if (!digitalRead(KEY_R))  contrast++;
    if (!digitalRead(KEY_L))  contrast--;
    if (contrast < 30) contrast = 30;
    if (contrast > 80) contrast = 80;
    display.setContrast(contrast);
    printTemp();//       Терпература
    display.display();
    delay(150);
  }
  Set = 1; delay(200);
  while (digitalRead(KEY_OK)) {
    display.clearDisplay();
    display.setCursor(0 , 0); display.println("Сontr:");
    display.setCursor(60 , 0); display.println(contrast);
    if (Set == 1) display.setTextColor(WHITE, BLACK); else display.setTextColor(BLACK);
    display.setCursor(0 , 10); display.println("Light:"); display.setTextColor(BLACK);
    if (BL == 1) { display.setCursor(60 , 10); display.println("ON"); digitalWrite(Ekran, BL); } else
                 { display.setCursor(60 , 10); display.println("OFF"); digitalWrite(Ekran, BL);  }
    if (!digitalRead(KEY_R))  BL=1;
    if (!digitalRead(KEY_L))  BL=0;
    printTemp();//       Терпература
    display.display();
    delay(150);
  }
  display.display();
  display.clearDisplay();  // чистим буфер экранчика
  EEPROM.write(0, contrast);
  EEPROM.write(1, BL);
  void (* reboot)(void) = 0;
  reboot();
}
/////////////////////Коэффициент заполнения
uint32_t asm_func(uint8_t flag) {
  asm volatile (
    "cli"                    "\n\t"
    "mov r21,%0"    "\n\t" // флаг что считывать
    //сохранение конфигурация таймеров
    "in  r30,0x23"            "\n\t" // r30 <- GTCCR
    "ldi r18,0x83"            "\n\t" // GTCCR=0x83
    "out 0x23,r18"            "\n\t" // GTCCR=0x83
    "lds r26,0x80"            "\n\t" // r26 <- TCCR1A
    "sts 0x80,__zero_reg__"   "\n\t" // TCCR1A=0
    "lds r27,0x81"            "\n\t" // r27 <- TCCR1B
    "ldi r19,0x4"             "\n\t"
    "sts 0x81,r19"            "\n\t" // TCCR1B=1<<CS12
    "sts 0x85, __zero_reg__"  "\n\t" //TCNT1L=0
    "sts 0x84, __zero_reg__"  "\n\t" //TCNT1H=0
    "lds r28,0xB0"            "\n\t" // r28 <- TCCR2A
    "sts 0xB0,__zero_reg__"   "\n\t" // TCCR2A=0
    "lds r29,0xB1"            "\n\t" // r29 <- TCCR2B
    "ldi r20,0x1"             "\n\t"
    "sts 0xB1,r20"            "\n\t" // TCCR2B=1<<CS20
    "sts 0xB2,__zero_reg__"   "\n\t" // TCNT2=0
    "lds r31,0x70"            "\n\t" // r31 <- TIMSK2
    "sts 0x70,__zero_reg__"   "\n\t" // TIMSK2=0
    ////*******************************************
    // пройти если ноль
    "wait_begin:"             "\n\t"
    "sbic 0x09,5"             "\n\t" // Порт D5
    "rjmp wait_begin"         "\n\t"
    // пройти если единица -> первый импульс
    "wait_start:"             "\n\t"
    "sbis 0x9,5"              "\n\t"
    "rjmp wait_start"         "\n\t"
    "sts 0x43,__zero_reg__"   "\n\t" // GTCCR=0 старт синхра
    // импульс пошёл, ждём ноль
    "wait_pause:"             "\n\t"
    "sbic 0x09,5"             "\n\t"
    "rjmp wait_pause"         "\n\t"
    "cpi r21,__zero_reg__"    "\n\t" //
    "brne duty"  "\n\t"
    // импульс кончился, ждём  конца периода (единицу)
    "wait_end:"               "\n\t"
    "sbis 0x9,5"              "\n\t"
    "rjmp wait_end"           "\n\t"
    "duty:"                   "\n\t"
    ////*******************************************
    //восстановление таймеров
    "sts 0x80,r26"            "\n\t" // TCCR1B
    "sts 0x81,r27"            "\n\t" // TCCR1B
    "sts 0xB0,r28"            "\n\t" // TCCR2A
    "sts 0xB1,r29"            "\n\t" // TCCR2B
    "sts 0x70,r31"            "\n\t" // TIMSK2
    "out 0x23,r30"            "\n\t" // GTCCR  старт таймеры
    //вывод 3х байт
    "lds r22, 0xB2"           "\n\t" //  tctn2  0 байт
    "lds r23, 0x84"           "\n\t"  // TCNT1L 1 байт
    "lds r24, 0x85"           "\n\t"  // TCNT1H 2 байт
    "lds r25, __zero_reg__"   "\n\t" // пустой  3 байт
    "sei"                     "\n\t"
    ::"r" (flag): );
}
/////////////////////Коэффициент заполнения

 

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

Спасибо, буду пробовать! :)

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

smokok, можно скетч под 1203 сюда, без Яндексдиска?

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

seri0shka, Да конечно! Пожалуйста, сюда КЛАЦ

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

День добрый. Чего-то у меня кнопка ОК буд-то сама нажимается. Схему проверил. А когда на неё нажмёшь и   держишь, то всё нормально.  Наоборот работает. Это происходит во всех режимах. 

Alex-Bee
Offline
Зарегистрирован: 13.03.2020

volodya198024, вы хоть бы намекнули о чём речь. Не все фломастеры одинаково сладкие...
Например, в PULTOSCOPE_SSD1306_final2_DDS_Z_EL83_EDITION.ino, есть строка 80:
 

 //если автодетект работает неверно (менюха скачет постоянно), то нужно расскомментировать одну из следующих строчек:
  // flag_key = 0; // кнопки просто подключены к земле, резюков нету.
  flag_key = 1; // кнопки подключены к питанию и резюки на землю

Может, это вариант вашей проблемы?

progrik
Offline
Зарегистрирован: 30.12.2018

Alex-Bee пишет:
...Может, это вариант вашей проблемы?
а чего "может"? 100 пудов нужно инвертировать подключение кнопок, или в коде править.

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

Прошу прощения за мою не компетентность. Да действительно. Попробуй угадай. В последней версии для 5110 в сообщение  #4401 при подачи питания сразу включается осциллограф. Если зажать кнопку ОК и включить, то включится меню которое можно листать (при зажатой кнопке ОК). Включение выбранного режима при кратковременном отпускание кнопки. Например при включенном PWM-генераторе постоянное перемещение курсора. Я понимаю что произошла инверсия состояния кнопки или входа. Но где? Я без помощи не разберусь. 

Остальные кнопки ведут себя правильно.

progrik
Offline
Зарегистрирован: 30.12.2018

volodya198024 пишет:
Остальные кнопки ведут себя правильно.
ну да, остальные кроме всех работают)) ОК жмакается сам, пункты в меню тоже. а какие тогда кнопки ведут себя "правильно"?)) RESET?)))

приклеил состояние нажатой кнопки, в коде #define KEY_PRESSED 1 //progrik: логическое состояние нажатой кнопки 0 или 1

..ну и в коде не if (!digitalRead(KEY_OK)) а if(digitalRead(KEY_OK) == KEY_PRESSED) везде везде где кнопки....

короче, инвертировал все кнопки. проверить не на чем, так что извольте сами) если я чего напутал - жаль))

//Страница проэкта  <a href="http://srukami.inf.ua/pultoscop_v25110.html" title="http://srukami.inf.ua/pultoscop_v25110.html" rel="nofollow">http://srukami.inf.ua/pultoscop_v25110.html</a>
// === От меня [Electronik83] === mailTo: <a href="mailto:gsmkillersevsk@mail.ru">gsmkillersevsk@mail.ru</a>
// 1. Оптимизировал, причесал код
// 2. Сделал красивей главное меню, добавил новый пункт
// 3. Отображение частатоы и напряжения в осцилле при 1Кгц и 2,5в (был баг автора)
// 4. Новый режим - медленный осцилл - доделал...
// 5. Оптимизация.....
// 6. Убрал програмную растяжку сигнала из осцилла (развертка 7 и 8)
// 7. Изменил отрисовку сигнала в осцилле (больше нету этих раздвоений сигнала)
// 8. Убрал нахрен кнопку питания, и все что с ней сязано - проще тумблер (переключатель) поставить...
// 9. Анимировал главное меню. Пофиксил мерцание.
//10. Вставил свое меню генератора, DDS-генератора, терминала.
//11. Добавил немного новой анимации.
//12. Провел эксперимент с DDS-генератором и регулировкой частоты - не сделать.
// === ver19 ==
//13. В осциле - Сделал автосинхронизацию по серединке амплитуды сигнала <= подумать над внешней синхрой !!!
//14. В осциле - показываем масимальное и минимальное значение амплитуды сигнала справа
//15. Оптимизация некоторых участков кода
//16. Атмега теперь сама меряет напряжение своего питания, не надо ничего мультиметром мерять
//17. В осцилле - сделал более удобный вывод верхней строки в осцилле, "устатичил" вывод частоты.
//      - теперь нет этого дерганья из Гц в кГц (и обратно) - просто выводим в Мгц.
//18. Добавил цифровой анализатор (входы A1, A2, A3, A4, A5) - что то мне он не понравился (и мне тоже).
/////////////// smokok ///////////////  
//19. Добавил Авто-опорное 5.0,1.1,0.2 
//20. Добавил Меню экрана (регулировка контрастности и включение подсвеки экрана).
//21. Добавил температуру. Пин термистора (10к) в А0 и +5, доп резистор (10к) в А0 и -5
//22. Разогнал PULTOSCOPE благодаря progrik
//23. Пока только вырезал  UART приемник
//    В планах внедрить Коэффициент заполнения
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
#include <FreqCount.h>
#include <EEPROM.h>
#include <math.h>
#include <PWM.h>

// Настройки пользователя - можно менять
#define Ekran     8  //Экран///Pin///Подсветka
#define OVERCLOCK 16  // частота на которой работает Ардуино
float VCC = 5.0;      // напряжение питания (меряем мультиметром - СТАЛО НЕ НАДО)
Adafruit_PCD8544 display = Adafruit_PCD8544(7, 6, 4, 3, 2); // пины к которым у вас подключен дисплей
byte contrast; // контрастность дисплея
byte Set = 0; // Настройка экрана
bool BL; // Подсветка

#define KEY_L  11 // кнопка ЛЕВО  (можно любой пин)
#define KEY_OK 12 // кнопка ОК    (можно любой пин)
#define KEY_R  13 // кнопка ПРАВО (можно любой пин)
#define VBAT   A5 // любой свободный аналоговый пин для измерения напряжения АКБ 
#define KEY_DELAY 200
#define KEY_PRESSED 1 //progrik: логическое состояние нажатой кнопки 0 или 1

// Переменные, дефайны и прочяя хрень - менять со смыслом
#define GEN_PIN  9  // пин для генератора сигналов (не менять)
#define DDS_PIN  10 // пин для генератора dds (не менять)
byte mode = 0; // пункт главного меню
byte menu = 0; // пункт меню

#define BUFSIZE_SHORT 256 //progrik: буфер для рабочего режима, чем короче - тем быстрее отображение. чем длиннее - тем медленнее, но синхра сможет ловить более "растянутый" сигнал
#define BUFSIZE_LONG  512 //progrik: буфер для паузы. старый буфер был 800
#define PAUSE_OFFSET 2    //progrik: смещение графика за один кадр в режиме паузы. если для паузы указан достаточно длинный буфер, то можно увеличить до 2-3-4.., чтоб быстрее мотало
#define APP 2.7           //         2.7) это переход на аппаратный замер
#define NAPR 255          //         это напряжение сигнала
#define LCDX 80

byte adcBuf[BUFSIZE_LONG];
byte vSync = 30;   // уровень синхронизации
bool vRef = 1; // флаг опорного напряжения
bool pause = 0; // флаг режима паузы
byte razv = 4;
int grOffset = 0; // смещение графика в рабочем режиме
byte vMax, vMin;// максимальное и минимальное напряжение сигнала
int kdel = 5;         //bulat  автопредел 1,1/0,2 вольта
bool avtorazv = 1;    // Автоматический выбор развертки
bool flag = 0;
bool kzap = 0; //флаг коэффициента заполнения
unsigned long ok_press_time;

// Переменные для генератора
int PWM = 50;//стартовое значение ШИМ от 0 до 100 для генератора
unsigned long freq = 500; // стартовое значение частоты в Гц для генератора
unsigned long stepFreq = 0;

//int d=0; // можно подменить чем нибудь. count например....
unsigned long count = 0;
//long countX=0;
//uint32_t tic1;
//uint32_t tic2;
//int duty;
//long speedTTL=9600; //скорость терминала

void ShowMode(char y) { // выводим режим работы. y - строка, где рисовать
const char* const modeStr[] PROGMEM= { " Oscilloscop ",    "     PWM     ", "     DDS     ", "     АЧХ     ", " AHAJlи3ATOP ", "    ЭKPAH    "};
  display.setCursor(3, y);      // задаем позицию на экране
  display.print(modeStr[mode]); // выводим режим работы
}
void ShowModeInv(char y) { // выводим режим работы инверсно. y - строка, где рисовать
  display.drawLine(0, y, 83, y, BLACK);
  display.setTextColor(WHITE, BLACK);
  display.setCursor(0, y+1); display.println(" "); display.setCursor(78, y+1); display.println(" ");  // для красоты только
  ShowMode(y+1);  display.setTextColor(BLACK);
}
double Thermister(int RawADC) {
  double Temp;
  Temp = log(((10240000 / RawADC) - 10000)); // 10000=10кОм Для подстройки правим.
  Temp = 1 / (0.001129148 + (0.000234125 * Temp) + (0.0000000876741 * Temp * Temp * Temp));
  Temp = Temp - 273.15;   // Kelvin to Celcius
  return Temp;
}
void printTemp(void) {
  display.setTextColor(BLACK);
  display.setCursor(15, 37);
  display.print("Temp ");
  display.print(float(Thermister(analogRead(0))), 1); // (0) это аналог выход А0
}
// ==== Считывание напряжения питания ардуинки (Vcc) ====
#define Vref11 1.095 // для точной подстройки результата измерений
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));        // результат преобразования в вольтах
}

void setup()  {

  ////////////////////////////Экран//////////////////////////////
  //  Примечание. Экран становится темным,  решение проблемы.///////////////////////////////////////
  //  EEPROM.write(0, 50);  //  Записать закрытую строку,  Вгрузить,  и начать использовать.
  contrast = EEPROM.read(0);
  BL       = EEPROM.read(1);
  pinMode(Ekran, OUTPUT);
  digitalWrite(Ekran, BL);
  //////////////////////////////////////////////////////
  pinMode(KEY_OK, INPUT); pinMode(KEY_L, INPUT); pinMode(KEY_R, INPUT); // настраиваем кнопки на вход
  display.begin();
  display.setContrast(contrast);
  VCC = ReadVcc();
  while (digitalRead(KEY_OK) != KEY_PRESSED) { // цикл, пока не нажали кнопку ОК
    // опрос кнопок
    if (digitalRead(KEY_R) == KEY_PRESSED) {
      mode++;
      if (mode > 5  ) mode = 0;
    }
    if (digitalRead(KEY_L) == KEY_PRESSED) {
      mode--;
      if (mode == NAPR) mode = 5;
    }
    display.clearDisplay();  // чистим буфер экранчика
    display.setCursor(24, 19);  // выводим напряжение батареи
    display.setTextColor(WHITE, BLACK);
    display.print(" ");
    display.print(analogRead(VBAT)*VCC / 1024, 1);
    display.print(" V");

    // динамическая графика в скролле
#define COUNT 84 // ширина дисплея, в пикселях
    for (char a = 3; a < 14; a += 2) {
      display.drawLine((count + a   ) % COUNT, a + 4, (count + a   ) % COUNT, 40 - a, BLACK); // левые  полосочки по бокам
      display.drawLine((count - a + 83) % COUNT, a + 4, (count - a + 83) % COUNT, 40 - a, BLACK); // правые полосочки по бокам
    }
    if (count % 84 == 0) { // если больше не надо скроллить
      ShowModeInv(0);
      display.drawRect(21, 18, 42, 11, BLACK); // рамка вокруг картинки
      display.drawRect(19, 16, 46, 15, BLACK); // рамка вокруг картинки
      //        display.drawRect(16, 14, 52, 19, BLACK); // рамка вокруг картинки
    }
    printTemp(); //       Терпература
    display.display();
    delay(150); // задержка отображения уровня заряда, температуры и кнопок заодно
    //    display.display();
  } // цикл нажатия ОК
  // нажали кнопку ОК из меню, инициализируем и выходим из меню

  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) razv = 0;
  if (mode == 5) {
    DDRC = 0x00;  // весь порт С (это A0...A5) на вход и без подтяжки
    PORTC = 0x00;
  }
  // антидребезг кнопки ОК
  for (char a = 37; a > 0; a -= 2) {
    display.clearDisplay();  // - это вместо delay(200);
    ShowModeInv(a);
    display.display();
  }
  ok_press_time = millis(); //progrik: запоминаем время последнего нажатия
}
// безконечный цикл - по сути прыгаем в подпрограммы
void loop() {
  switch (mode) { // Прыгаем в выбранный режим из главного меню
    case 0 : Oscil();        break; // "выпадаем" в осцилл
    case 1 : Generator();    break; // "выпадаем" в генератор
    case 2 : DdsGenerator(); break; // "выпадаем" в DDS генератор
    //    case 3 : Terminal();     break; // "выпадаем" в USART приемник
    case 3 : OscilSlow();    break; // "выпадаем" в медленный осцилл
    case 4 : LogAnalyzer();  break; // "выпадаем" в анализатор
    case 5 : Menu_ekrana();  break; // "выпадаем" в меню экрана
  }
}
//  === Читаем с АЦП данные и помещаем их в буфер === //
void ReadAdc() {
  uint16_t len;
  len = (pause) ? BUFSIZE_LONG : BUFSIZE_SHORT;
  if (razv % 10) { // (razv>0)        // если развертка без задержек всяких (с 1 по 7)
    ADCSRA = 0b11100000 | (8 - (razv % 10)); // установили делитель (/2 - не работает, так что начинаем с /4)
    for (uint16_t i = 0; i < len; i++) { // цикл для чтения
      while (!(ADCSRA & 0x10));     // ждем готовность АЦП
      ADCSRA |= 0x10;               // запускаем следующее преобразование
      adcBuf[i] = ADCH;             // записываем данные в массив
    }
    //    delay(0.3 * BUFSIZE);           // компенсация задержки по сравнению с разверткой 0...
  } else {                          // развертка с задержками (delay)
    ADCSRA = 0b11100111;            // делитель на /128
    for (uint16_t i = 0; i < len; i++) { // цикл для чтения
      while (!(ADCSRA & 0x10));     // ждем готовность АЦП
      ADCSRA |= 0x10;               // запускаем следующее преобразование
      delayMicroseconds(500);       // делаем задержку
      adcBuf[i] = ADCH;             // записываем данные в массив
    }
  }
}

//progrik: для расчета напряжений ---------------
void calc_val()
{
  vMax = 0; vMin = 0xFF;
  uint16_t l = (pause) ? BUFSIZE_LONG : BUFSIZE_SHORT;
  for (uint16_t x = 0; x < l; x++) {
    if (vMax < adcBuf[x]) vMax = adcBuf[x];  // пока 255, но надо экспериментировать
    if (vMin > adcBuf[x]) vMin = adcBuf[x];
  }

  // == Вычисляем максимальное и минимальное значение сигнала == //
  vSync = (vMax - vMin) / 2 + vMin; // уровень синхронизации по середине уровня сигнала
  // == Определение точки синхронизации == //
  bool flagZero = 0; grOffset = 0; // сброс флага и точки синхронизации
  // Ищем перепад от меньшего уровня к большему
  for (uint8_t x = 1; x < BUFSIZE_SHORT - LCDX; x++) { // смотрим весь массив данных АЦП
    if (adcBuf[x] < vSync) flagZero = 1; // нашли меньше, чем синхра (перепад сигнала в минус) - ставим флаг
    if (flagZero && adcBuf[x] > vSync) {
      grOffset = x;  // нашли больше, чем синхра (перепад сигнала в плюс) - запомнили и выходим из цикла
      break;
    }
  }
  // === Считаем частоту сигнала === //
  if (vRef && vMax * VCC / NAPR > APP) { // если можем замерить аппаратно - меряем
    if (FreqCount.available()) count = FreqCount.read(); // вывод частоты по готовности счетчика частоты сигнала
  } else { // === Меряем частоту сигнала программно === //
    flagZero = 0; count = 0; // сброс флага и счетчика
    for (uint8_t x = grOffset; x < BUFSIZE_SHORT - LCDX; x++)  { // смотрим массив от точки синхронизации до конца
      if (adcBuf[x] < vSync) flagZero = 1; // нашли меньше, чем синхра (перепад сигнала в минус) - выставляем флаг
      if (flagZero && adcBuf[x] > vSync) { // нашли больше, чем синхра (перепад сигнала в плюс) - отловили полный период
        switch (razv) { // считем частоту периода
          case 6: count = 1000000 / ((x - grOffset - 1) * 3.25);    break; // делитель 4
          case 5: count = 1000000 / ((x - grOffset)  * 3.25) / 2;  break; // делитель 8
          case 4: count = 1000000 / ((x - grOffset)  * 3.25) / 4;  break; // делитель 16
          case 3: count = 1000000 / ((x - grOffset)  * 3.25) / 8;  break; // делитель 32
          case 2: count = 1000000 / ((x - grOffset)  * 3.25) / 16; break; // делитель 64
          case 1: count = 1000000 / ((x - grOffset)  * 3.25) / 32; break; // делитель 128
          case 0: count = 1000000 / ((x - grOffset)  * 510);     break; // делитель 128 тоже
        }
        break;
      }
    }
  }
  count = count * OVERCLOCK / 16.0; // пересчет частоты на разные кварцы
}
// === Осциллоскоп === //
void Oscil() {
  // установка опорного напряжения АЦП и настройка входа АЦП
  ADMUX = vRef ? 0b01100011 : 0b11100011;
label_ReadAdc:
  // === Обработка кнопок === //
  if (digitalRead(KEY_L) == KEY_PRESSED)   switch (menu) { // кнопка лево:)
      case 0 : razv--; if (razv == NAPR) razv = 6;                                  break; // меняем развертку
      case 1 : grOffset -= PAUSE_OFFSET; if (grOffset < 0) grOffset = 0;                      break; // листаем график в паузе
    }
  if (digitalRead(KEY_R) == KEY_PRESSED)  switch (menu) { // кнопка право:)
      case 0 : razv++; if (razv == 7) razv = 0;                                   break; // меняем развертку
      case 1 : grOffset += PAUSE_OFFSET; if (grOffset > BUFSIZE_LONG - LCDX) grOffset = BUFSIZE_LONG - LCDX; break; // листаем график в паузе
    }
  if (digitalRead(KEY_OK) == KEY_PRESSED)
  {
    if ((millis() - ok_press_time) > 200) // подавление дребезга - ждем 200 мс со времени последнего нажатия
    {
      switch (++menu)
      {
        case 1: grOffset = 0; pause = 1; ReadAdc(); calc_val();    break; // вход в паузу - антидребезг типа
        case 2: menu = 0;  pause = 0;                              break; // перебор меню
      }
    }
    ok_press_time = millis(); //progrik: запоминаем время последнего нажатия кнопки OK
  }
  // === Ведём рассчеты === //
  if (!pause) { // если нет паузы
    ReadAdc();  // то снимаем осциллограмму
    calc_val();
  } // закончили вести рассчеты

  display.clearDisplay(); // чистим экран...
  /*      if (count < 10) kzap=true;
    if (kzap==true && count > 10){
    FreqCount.end();
    tic1 = asm_func(1);
    tic2 = asm_func(0);
    duty=(1/((float)tic1/tic2))*100;
    kzap=false;
    FreqCount.begin(1000);
    }
    if (kzap==false && count > 10 && duty < 100){
    display.setCursor(18,11);
    display.setTextColor(BLACK);
    display.print(duty);
    display.print("%");
    }*/
  // === Отрисовка меню осцилла ===
  if (vRef) display.print(VCC, 1); else {
    if (kdel == 5)display.print("1.1"); else display.print("0.2");
  }
  if (menu == 0) display.setTextColor(BLACK, WHITE);
  if (vRef && vMax * VCC / NAPR > APP) {
    display.print("a");  // если замер аппаратный - "ужирняем" точку
  }
  else  {
    display.print("п");  // иначе "П"рограмный
  }
  display.print(razv);
  if (menu == 1)
    if (!pause)  display.setTextColor(BLACK);
    else { // рисуем прокрутку в режиме паузы
      display.print("s");  display.drawLine(grOffset / 5.7, 8, grOffset / 5.7 + 7, 8, BLACK); display.drawLine(grOffset / 5.7, 9, grOffset / 5.7 + 7, 9, BLACK);
    } // шкала прокрутки
  display.setCursor(36, 0); display.print(count / 1000000.0, 6);  //покажем частоту
  display.setCursor(60, 11); display.print(vMax * (vRef ? VCC : 1.1) / NAPR, 2); // рисуем максималоное напряжение сигнала
  display.setCursor(65, 40); display.print(vMin * (vRef ? VCC : 1.1) / NAPR, 1); // рисуем минимальное  напряжение сигнала
  if (vMax == 0xFF) for (uint8_t x = 0; x < 5; x++) display.drawLine(x, 9, x, 47, BLACK); // указатель привышения напряжения на входе осцила

  // == Отрисовка сетки == //
  for (byte i = 47; i > 5; i = i - 7) {
    display.drawLine( 0, i, 2, i, BLACK);  // черточки слева
  }
  for (byte i = 47; i > 5; i = i - 3) {
    display.drawPixel(21, i, BLACK);  // вертикальный пунктир
    display.drawPixel(42, i, BLACK);
    display.drawPixel(63, i, BLACK);
  }
  for (byte i = 3; i < 84; i = i + 3) {
    display.drawPixel(i, 33, BLACK);
    display.drawPixel(i, 19, BLACK);
  }

  ///////////////авто razv//////////////
  if (avtorazv)
#define PER 1.3
  if (count > 4323.3 * PER) razv = 6;
  else if (count > 2434.5 * PER) razv = 5;
  else if (count > 0969.3 * PER) razv = 4;
  else if (count > 0486.8 * PER) razv = 3;
  else if (count > 0245.8 * PER) razv = 2;
  else if (count > 0120.1 * PER) razv = 1;
  else razv = 0;
  //bulat если зашкаливает включаем предел 5 в
  if (vMax == NAPR) {
    if (vRef == 0)
    {
      vRef = 1;
      ADMUX = 0b01100011;// выбор внутреннего опорного 5.0 В
      goto   label_ReadAdc;
    }
  }
  //bulat если  5 в  и уровень менее 1,1 в то вкл предел 1,1 в
  if (vMax <= 55) {
    if (vRef == 1)
    {
      vRef = 0;
      ADMUX = 0b11100011;// выбор внутреннего опорного 1,1В
      goto   label_ReadAdc;
    }
  }
  //bulat здесь автопредел 1,1/0,22 в,программный
  kdel = 5;
  if (vMax <= 55) {
    if (vRef == 0)
    {
      kdel = 1;
    }
  }
  // == Отрисовка графика == //
  for (uint8_t x = 0; x < 80; x++)
    display.drawLine(x + 4, 47 - adcBuf[x + grOffset] / kdel / 1.35, x + 4, 47 - adcBuf[x + grOffset + 1] / kdel / 1.35, BLACK);
  display.display(); // вываливаем буфер экрана на экран
}
////////////////////////////////////////////////////////////////////////////////////////////
// РЕЖИМ ГЕНЕРАТОРА
void Generator() {
  // обработка кнопок и отрисовка одновременно
  display.clearDisplay();
  ShowModeInv(0);
  if (digitalRead(KEY_OK) == KEY_PRESSED) {
    if (menu++ == 7) menu = 0;  // переходим по разрядам / меняем ШИМ
    delay(KEY_DELAY);
  }
  if (menu == 7) { // меняем ШИМ
    if (digitalRead(KEY_L) == KEY_PRESSED) {
      PWM--;
      if (PWM < 0)   PWM = 100;
      delay(KEY_DELAY);
    }
    if (digitalRead(KEY_R) == KEY_PRESSED) {
      PWM++;
      if (PWM > 100) PWM = 0;
      delay(KEY_DELAY);
    }
    pwmWrite(GEN_PIN, PWM * 2.55); // задали ШИМ
    display.setTextColor(WHITE, BLACK); // если меняем шим, то рисуем его инверсным
    display.drawLine(15, 39, 68 + (PWM == 100 ? 6 : 0) + (PWM < 10 ? -6 : 0), 39, BLACK); // сверху полоска для большей инверсности
  } else { // меняем частоту
    if (digitalRead(KEY_L) == KEY_PRESSED) {
      if (freq > stepFreq)  freq -= stepFreq;          // не даем выйти за допустимый диаппазон
      SetPinFrequencySafe(GEN_PIN, freq / (OVERCLOCK / 16.0)); // задали частоту
      delay(KEY_DELAY); // задержка для кнопок
    }
    if (digitalRead(KEY_R) == KEY_PRESSED) {
      if ((freq + stepFreq) < 10000000) freq += stepFreq; // не даем выйти за допустимый диаппазон
      SetPinFrequencySafe(GEN_PIN, freq / (OVERCLOCK / 16.0)); // задали частоту
      delay(KEY_DELAY); // задержка для кнопок
    }
    display.setTextColor(BLACK); // если ШИМ не меняем, то выбираем обычный текст
#define XFREQ 14 // ======== // и, выделяем декаду частоты полосочками
    stepFreq = pow(10, (byte)menu); if (menu > 1) stepFreq++; // устраняем глючность pow, почему 10 в степени 2 = 99?
    byte menu_t = menu; if (menu > 2) menu_t++; if (menu == 6) menu_t++; // делаем табуляцию частоты
    menu_t = 54 - menu_t * 6; // считаем положение полосочек
    display.drawLine(menu_t, XFREQ - 2, menu_t + 4, XFREQ - 2, BLACK); // рисуем полоски
    display.drawLine(menu_t, XFREQ - 3, menu_t + 4, XFREQ - 3, BLACK);
    display.drawLine(menu_t, XFREQ + 8, menu_t + 4, XFREQ + 8, BLACK);
    display.drawLine(menu_t, XFREQ + 9, menu_t + 4, XFREQ + 9, BLACK);
  }
  // рисуем уровень ШИМ (инверсия текста задана ранее)
  display.setCursor(15, 40); // ставим курсор))
  display.print(" PWM=");  display.print(PWM);  display.print("% "); // выводим уровень ШИМ
  display.setTextColor(BLACK); // убираем инверсию при выводе частоты
  // рисуем частоту с табуляцией
  display.setCursor(6, XFREQ);
  for (unsigned long freq_t = 1000000; freq_t>0; freq_t/=10) {
  if (freq>=freq_t) {
    display.print((freq/freq_t)%10); if (freq_t==1000000 || freq_t==1000) display.print("'"); }  else { 
    display.print("_");              if (freq_t==1000000 || freq_t==1000) display.print(" "); }
  }
  display.print(" Hz");
  // отрисовка графика PWM
  for (char x = 17; x < 67; ) {
    if (PWM != 0)             display.drawLine(x, 26, x + PWM / 4, 26, BLACK); x += PWM / 4; // верх
    if (PWM != 0 && PWM != 100) display.drawLine(x, 26, x, 36, BLACK);                   // спад
    if (PWM != 100)           display.drawLine(x, 36, x + 25 - PWM / 4, 36, BLACK); x += 25 - PWM / 4; // низ
    if (PWM != 0 && PWM != 100 && x < 43) display.drawLine( x, 36, x, 26, BLACK);        // подъем
  }
  display.display(); // вываливаем буфер на дисплей
}

// === DDS генератор ===
void DdsGenerator() {
  static const byte ddsWave[][32] PROGMEM = {        
    1,6,15,29,48,69,92,117,143,168,191,212,229,243,251,255,254,248,237,222,203,181,156,131,106,81,59,39,22,10,3,1,   // sinNew    
    1,18,35,52,69,86,103,120,137,154,171,188,205,222,239,255,239,223,207,191,175,159,143,127,111,95,79,63,47,31,15,1, // treugNew    
    1,9,17,25,33,41,49,57,65,73,81,89,97,105,113,121,129,137,145,153,161,169,177,185,193,201,209,217,225,235,245,255,  // pilaNew
    250,246,238,230,222,214,206,198,190,182,174,166,158,150,142,134,126,118,110,102,94,86,78,70,62,54,41,33,25,17,9,1 };// pilaObrNew
  const char* const ddsStr[] PROGMEM = { "          ", "           ", "       ", "             "};
  byte ddsCount=0;
  // Рисуем DDS-генератор
  display.clearDisplay(); ShowModeInv(0); // режим работы, заголовок
  for (byte i = 0; i < 84;) display.drawLine(i, 36 - pgm_read_byte(&ddsWave[menu][i % 32]) / 10, i, 36 - pgm_read_byte(&ddsWave[menu][(i++) % 32]) / 10, BLACK);
  display.setCursor(3, 40);  display.print(ddsStr[menu]);  display.display(); // отрисовали все
  while (digitalRead(KEY_R)!=KEY_PRESSED && digitalRead(KEY_L)!=KEY_PRESSED && digitalRead(KEY_OK)!=KEY_PRESSED) { // выводим выбранный сигнал, пока не нажали кнопку
    pwmWrite(DDS_PIN, pgm_read_byte(&ddsWave[menu][(ddsCount++) & 0x1F]));
    //ResetAvrCheck();
  }
  if (++menu == 4) menu = 0; // нажали кнопку - переключаем режим
  delay(KEY_DELAY); // чтоб кнопки нормально нажимались
}

/*
  // UART приемник
  void Terminal() {
  const long speedUart[] = { 1200,2400,4800,9600,19200,38400,57600,115200 };
  display.clearDisplay();
  ShowModeInv(0);
  display.setCursor(15,15); display.println("Скорость:");
  display.setTextColor(WHITE, BLACK);
  display.setCursor(66, 25); display.println("+");
  display.setCursor(13, 25); display.print("-");
  display.setTextColor(BLACK);
  display.println(" ");
  display.print(speedUart[menu]);
  display.println(" ");
  display.setCursor(9,40); display.println("ОК - запуск");
  if(digitalRead(KEY_L) == KEY_PRESSED)  { menu--; if(menu==255) menu=7;   delay(KEY_DELAY);  }
  if(digitalRead(KEY_R) == KEY_PRESSED)  { menu++; if(menu==8)   menu=0;   delay(KEY_DELAY);  }
  if(digitalRead(KEY_OK) == KEY_PRESSED) {
    Serial.begin(speedUart[menu]*(16/OVERCLOCK));
    display.clearDisplay();
    display.print(">");
    delay(KEY_DELAY);
    display.display();
    int x=83;
    while (1) {
      //ResetAvrCheck();
      if (Serial.available()) { // Если в буфере есть данные
        if(++x==84) { // проверяем, не пора ли экран очистить
          x=0;
          display.clearDisplay();
        }
        display.print((char)(Serial.read())); // печать байта в дисплей
        display.display(); // выводим символ
      }
    }
  }
  display.display();
  }
*/
void SetTextColor(char c) {
  if (c) display.setTextColor(WHITE, BLACK); else display.setTextColor(BLACK);
}

void OscilSlow() {
  // надо найти главного тормазоглу!!!!!!!!!

  ADMUX = vRef ? 0b01100011 : 0b11100011; // установка опорного напряжения АЦП
  //ADCSRA = 0b11100110; // делитель частоты АЦП - 64
  //ADCSRA = 0b11100010; //delitel 4 // кондер не успевает разряжаться
  ADCSRA = 0b11100011; //delitel 8
  ADCSRA |= 0x10; // запуск преобразования АЦП

  // === Обработка кнопок ===
  if (digitalRead(KEY_L) == KEY_PRESSED) {
    switch (menu) { // тут идея такая - один раз опрашиваем кнопку, т.к. это медленно делается, потом через кейсы прыгаем.
      case 0 : vRef = !vRef;                      break;   // выбор опорного напряжения
      case 1 : razv--; if (razv == NAPR)  razv = 2;    break; // выбор развертки : 1000мс 500мс 100мс на клетку
      case 2 : grOffset -= 15; if (grOffset < 0)  grOffset = BUFSIZE_LONG; break; // режим паузы - прокручиваем осциллограмму
      case 3 : for (uint8_t x = 0; x < 26; x++) adcBuf[x] = 0; count = 0; grOffset = 0;
        display.clearDisplay(); display.setCursor(3, 20); display.print("RESET!");
        display.display(); delay(1000);    break;
    } delay(200);
  } // антидребезг кнопки
  if (digitalRead(KEY_R) == KEY_PRESSED) {
    switch (menu) {
      case 0 : vRef = !vRef;                      break;   // выбор опорного напряжения
      case 1 : razv++; if (razv == 3)    razv = 0;    break; // выбор развертки : 1000мс 500мс 100мс на клетку
      case 2 : grOffset += 15; if (grOffset > BUFSIZE_LONG) grOffset = 0;  break; // режим паузы - прокручиваем осциллограмму
    } delay(200);
  } // антидребезг кнопки
  if (digitalRead(KEY_OK) == KEY_PRESSED) {
    menu++; if (menu == 4) {
      menu = 0;  // перебор меню
      pause = 0;
    }
    if (menu == 2) {
      pause = 1;
      adcBuf[(count++) % BUFSIZE_SHORT] = NAPR; adcBuf[(count++) % BUFSIZE_SHORT] = 0; adcBuf[(count++) % BUFSIZE_SHORT] = NAPR; if (count > 79) grOffset = count - 80;
    } // отметка паузы на графике
    delay(200);
  } // антидребезг кнопки
  // === Отрисовка менюшки ===
  display.clearDisplay();  display.setCursor(0, 0);
  SetTextColor(menu == 0); if (vRef) display.print(VCC, 1); else display.print("1.1");
  SetTextColor(menu == 1); display.print(" ");   display.print(razv);
  SetTextColor(menu == 2); display.print(" S");  display.print(" ");
  display.print((grOffset % BUFSIZE_SHORT) / 2.02, 0);  display.print("%");
  SetTextColor(menu == 3); display.print(" R");
  display.setTextColor( BLACK);

  // == Отрисовка сетки == Новая //
  for (byte i = 47; i > 5; i = i - 7) {
    display.drawLine( 0, i, 2, i, BLACK);  // черточки слева
  }
  /* for(byte i=47;i>5;i=i-7) { display.drawPixel(17,i, BLACK); display.drawPixel(33,i, BLACK); display.drawPixel(49,i, BLACK); display.drawPixel(65,i, BLACK); display.drawPixel(81,i, BLACK); } // вертикальный пунктир
    for(byte i=1;i<84;i=i+8) { display.drawPixel(i,40, BLACK); display.drawPixel(i,26, BLACK); display.drawPixel(i,12, BLACK); }    // горизонтальный пунктир

    // === Отрисовка сетки Старая ===
    for(byte i=47;i>5;i=i-7) { display.drawLine( 0, i, 2, i, BLACK); } // черточки слева
    for(byte i=47;i>5;i=i-3) { display.drawPixel(21,i, BLACK);display.drawPixel(42,i, BLACK);display.drawPixel(63,i, BLACK); } // вертикальный пунктир
    for(byte i=3;i<84;i=i+3) { display.drawPixel(i,33, BLACK);display.drawPixel(i,19, BLACK);}                                 // горизонтальный пунктир
  */
  // === Отрисовка графика ===
  for (uint8_t x = 0; x < 80; x++) display.drawLine(x + 4, 47, x + 4, 47 - adcBuf[(x + grOffset) % BUFSIZE_SHORT] / 7, BLACK);
  if (pause) { // если пауза - рисуем шкалу прокрутки
    display.drawLine((grOffset % BUFSIZE_SHORT) / 2.3, 8, (grOffset % BUFSIZE_SHORT) / 2.3 + 5, 8, BLACK); // шкала прокрутки
    display.drawLine((grOffset % BUFSIZE_SHORT) / 2.3, 9, (grOffset % BUFSIZE_SHORT) / 2.3 + 5, 9, BLACK); // шкала прокрутки
  } else { // если не пауза, то снимаем показания
    while (!(ADCSRA & 0x10)); // ждем, пока АЦП считает
    adcBuf[count % BUFSIZE_SHORT] = ADCH; // сохраняем число из АЦП в массив
    if (count > 79) grOffset = count - 80;
    count++;
    if (count > BUFSIZE_LONG) count = BUFSIZE_SHORT; // зацикливаем счетчик
    //45мс - программа выполняется
    switch (razv) {
      case 0 : delay(55 * (OVERCLOCK / 16.0)); break; // секунда на клетку
      case 1 : delay(05 * (OVERCLOCK / 16.0)); break; // пол секунды на клетку
      case 2 : break; // без задержки.......
    }
  }
  display.display();
}

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

// ==== Логический анализатор ==== //
void LogAnalyzer() {
  // обрабатываем кнопки
  if (digitalRead(KEY_L) == KEY_PRESSED)   switch (menu) {
      case 0 : razv--; if (razv == NAPR) razv = 9; delay(400);                        break;
      case 1 : grOffset -= 20; if (grOffset < 0) grOffset = 0;                        break;
    }
  if (digitalRead(KEY_R) == KEY_PRESSED)  switch (menu) {
      case 0 : razv++; if (razv == 10) razv = 0; delay(400);                        break;
      case 1 : grOffset += PAUSE_OFFSET; if (grOffset > BUFSIZE_LONG - LCDX) grOffset = BUFSIZE_LONG - LCDX; break;   break;
    }
  if (digitalRead(KEY_OK) == KEY_PRESSED)  switch (++menu) {
      case 1: grOffset = 0; pause = 1; break; // вход в паузу
      case 2: menu = 0;     pause = 0; break; // перебор меню
    }

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

  display.clearDisplay(); // чистим дисплей
  display.print(razv);    // выводим развертку
  display.print(" ");
  if (pause) {
    display.print(" Пауза ");
    // рисуем прокрутку в режиме паузы
    display.drawLine(grOffset / 8, 8, grOffset / 8 + 6, 8, BLACK); // шкала прокрутки
    display.drawLine(grOffset / 8, 9, grOffset / 8 + 6, 9, BLACK); // шкала прокрутки
    delay(200);
  }
  for (byte i = 47; i > 12; i = i - 7) {
    display.drawLine( 0, i - 5, 2, i - 5, BLACK);  // черточки слева
    display.drawPixel(0, i, BLACK);
    display.drawPixel(2, i, BLACK);
  }
  // горизонтальный пунктир
  for (byte i = 5; i < 84; i = i + 3) {
    display.drawPixel(i, 47, BLACK);
    display.drawPixel(i, 40, BLACK);
    display.drawPixel(i, 33, BLACK);
    display.drawPixel(i, 26, BLACK);
    display.drawPixel(i, 19, BLACK);
  }
  for (int y = 0; y < 80; y++) {
    for (int x = 0; x < 5; x++) {
      display.drawLine(y + 4, 47 - (x * 7) - (bit_is_set(adcBuf[y + grOffset], 5 - x) ? 5 : 0), y + 4, 47 - (x * 7) - (bit_is_set(adcBuf[y + 1 + grOffset], 5 - x) ? 5 : 0), BLACK);
    }
  }
  display.display();
}
//////////////////////////////Экран/////////////////////////////////
void Menu_ekrana() {
  Set = 0; delay(200);
  while (digitalRead(KEY_OK) != KEY_PRESSED) {
    display.clearDisplay();
    if (Set == 0) display.setTextColor(WHITE, BLACK); else display.setTextColor(BLACK);
    display.setCursor(0 , 0); display.println("Сontr:"); display.setTextColor(BLACK);
    display.setCursor(60 , 0); display.println(contrast);
    display.setCursor(0 , 10); display.println("Light:"); display.setTextColor(BLACK);
    if (BL == 1) { display.setCursor(60 , 10); display.println("ON");} else
                 { display.setCursor(60 , 10); display.println("OFF");} display.display();
    if (digitalRead(KEY_R) == KEY_PRESSED)  contrast++;
    if (digitalRead(KEY_L) == KEY_PRESSED)  contrast--;
    if (contrast < 30) contrast = 30;
    if (contrast > 80) contrast = 80;
    display.setContrast(contrast);
    printTemp();//       Терпература
    display.display();
    delay(150);
  }
  Set = 1; delay(200);
  while (digitalRead(KEY_OK) != KEY_PRESSED) {
    display.clearDisplay();
    display.setCursor(0 , 0); display.println("Сontr:");
    display.setCursor(60 , 0); display.println(contrast);
    if (Set == 1) display.setTextColor(WHITE, BLACK); else display.setTextColor(BLACK);
    display.setCursor(0 , 10); display.println("Light:"); display.setTextColor(BLACK);
    if (BL == 1) { display.setCursor(60 , 10); display.println("ON"); digitalWrite(Ekran, BL); } else
                 { display.setCursor(60 , 10); display.println("OFF"); digitalWrite(Ekran, BL);  }
    if (digitalRead(KEY_R) == KEY_PRESSED)  BL=1;
    if (digitalRead(KEY_L) == KEY_PRESSED)  BL=0;
    printTemp();//       Терпература
    display.display();
    delay(150);
  }
  display.display();
  display.clearDisplay();  // чистим буфер экранчика
  EEPROM.write(0, contrast);
  EEPROM.write(1, BL);
  void (* reboot)(void) = 0;
  reboot();
}
/////////////////////Коэффициент заполнения
uint32_t asm_func(uint8_t flag) {
  asm volatile (
    "cli"                    "\n\t"
    "mov r21,%0"    "\n\t" // флаг что считывать
    //сохранение конфигурация таймеров
    "in  r30,0x23"            "\n\t" // r30 <- GTCCR
    "ldi r18,0x83"            "\n\t" // GTCCR=0x83
    "out 0x23,r18"            "\n\t" // GTCCR=0x83
    "lds r26,0x80"            "\n\t" // r26 <- TCCR1A
    "sts 0x80,__zero_reg__"   "\n\t" // TCCR1A=0
    "lds r27,0x81"            "\n\t" // r27 <- TCCR1B
    "ldi r19,0x4"             "\n\t"
    "sts 0x81,r19"            "\n\t" // TCCR1B=1<<CS12
    "sts 0x85, __zero_reg__"  "\n\t" //TCNT1L=0
    "sts 0x84, __zero_reg__"  "\n\t" //TCNT1H=0
    "lds r28,0xB0"            "\n\t" // r28 <- TCCR2A
    "sts 0xB0,__zero_reg__"   "\n\t" // TCCR2A=0
    "lds r29,0xB1"            "\n\t" // r29 <- TCCR2B
    "ldi r20,0x1"             "\n\t"
    "sts 0xB1,r20"            "\n\t" // TCCR2B=1<<CS20
    "sts 0xB2,__zero_reg__"   "\n\t" // TCNT2=0
    "lds r31,0x70"            "\n\t" // r31 <- TIMSK2
    "sts 0x70,__zero_reg__"   "\n\t" // TIMSK2=0
    ////*******************************************
    // пройти если ноль
    "wait_begin:"             "\n\t"
    "sbic 0x09,5"             "\n\t" // Порт D5
    "rjmp wait_begin"         "\n\t"
    // пройти если единица -> первый импульс
    "wait_start:"             "\n\t"
    "sbis 0x9,5"              "\n\t"
    "rjmp wait_start"         "\n\t"
    "sts 0x43,__zero_reg__"   "\n\t" // GTCCR=0 старт синхра
    // импульс пошёл, ждём ноль
    "wait_pause:"             "\n\t"
    "sbic 0x09,5"             "\n\t"
    "rjmp wait_pause"         "\n\t"
    "cpi r21,__zero_reg__"    "\n\t" //
    "brne duty"  "\n\t"
    // импульс кончился, ждём  конца периода (единицу)
    "wait_end:"               "\n\t"
    "sbis 0x9,5"              "\n\t"
    "rjmp wait_end"           "\n\t"
    "duty:"                   "\n\t"
    ////*******************************************
    //восстановление таймеров
    "sts 0x80,r26"            "\n\t" // TCCR1B
    "sts 0x81,r27"            "\n\t" // TCCR1B
    "sts 0xB0,r28"            "\n\t" // TCCR2A
    "sts 0xB1,r29"            "\n\t" // TCCR2B
    "sts 0x70,r31"            "\n\t" // TIMSK2
    "out 0x23,r30"            "\n\t" // GTCCR  старт таймеры
    //вывод 3х байт
    "lds r22, 0xB2"           "\n\t" //  tctn2  0 байт
    "lds r23, 0x84"           "\n\t"  // TCNT1L 1 байт
    "lds r24, 0x85"           "\n\t"  // TCNT1H 2 байт
    "lds r25, __zero_reg__"   "\n\t" // пустой  3 байт
    "sei"                     "\n\t"
    ::"r" (flag): );
}
/////////////////////Коэффициент заполнения

но попробовать стоит))

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

Ура! Заработала. Все кнопки фунциклируют правильно. Я в восторге от вас.

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

progrik пишет:

volodya198024 пишет:
Остальные кнопки ведут себя правильно.
ну да, остальные кроме всех работают)) ОК жмакается сам, пункты в меню тоже. а какие тогда кнопки ведут себя "правильно"?)) RESET?)))

приклеил состояние нажатой кнопки, в коде #define KEY_PRESSED 1 //progrik: логическое состояние нажатой кнопки 0 или 1

..ну и в коде не if (!digitalRead(KEY_OK)) а if(digitalRead(KEY_OK) == KEY_PRESSED) везде везде где кнопки....

короче, инвертировал все кнопки. проверить не на чем, так что извольте сами) если я чего напутал - жаль))

//Страница проэкта  <a href="http://srukami.inf.ua/pultoscop_v25110.html" title="http://srukami.inf.ua/pultoscop_v25110.html" rel="nofollow">http://srukami.inf.ua/pultoscop_v25110.html</a>
// === От меня [Electronik83] === mailTo: <a href="mailto:gsmkillersevsk@mail.ru">gsmkillersevsk@mail.ru</a>
// 1. Оптимизировал, причесал код
// 2. Сделал красивей главное меню, добавил новый пункт
// 3. Отображение частатоы и напряжения в осцилле при 1Кгц и 2,5в (был баг автора)
// 4. Новый режим - медленный осцилл - доделал...
// 5. Оптимизация.....
// 6. Убрал програмную растяжку сигнала из осцилла (развертка 7 и 8)
// 7. Изменил отрисовку сигнала в осцилле (больше нету этих раздвоений сигнала)
// 8. Убрал нахрен кнопку питания, и все что с ней сязано - проще тумблер (переключатель) поставить...
// 9. Анимировал главное меню. Пофиксил мерцание.
//10. Вставил свое меню генератора, DDS-генератора, терминала.
//11. Добавил немного новой анимации.
//12. Провел эксперимент с DDS-генератором и регулировкой частоты - не сделать.
// === ver19 ==
//13. В осциле - Сделал автосинхронизацию по серединке амплитуды сигнала <= подумать над внешней синхрой !!!
//14. В осциле - показываем масимальное и минимальное значение амплитуды сигнала справа
//15. Оптимизация некоторых участков кода
//16. Атмега теперь сама меряет напряжение своего питания, не надо ничего мультиметром мерять
//17. В осцилле - сделал более удобный вывод верхней строки в осцилле, "устатичил" вывод частоты.
//      - теперь нет этого дерганья из Гц в кГц (и обратно) - просто выводим в Мгц.
//18. Добавил цифровой анализатор (входы A1, A2, A3, A4, A5) - что то мне он не понравился (и мне тоже).
/////////////// smokok ///////////////  
//19. Добавил Авто-опорное 5.0,1.1,0.2 
//20. Добавил Меню экрана (регулировка контрастности и включение подсвеки экрана).
//21. Добавил температуру. Пин термистора (10к) в А0 и +5, доп резистор (10к) в А0 и -5
//22. Разогнал PULTOSCOPE благодаря progrik
//23. Пока только вырезал  UART приемник
//    В планах внедрить Коэффициент заполнения

Уже наверное надо схему рисовать ,ведь видоизменилась..

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

progrik пишет:

volodya198024 пишет:
Остальные кнопки ведут себя правильно.
ну да, остальные кроме всех работают)) ОК жмакается сам, пункты в меню тоже. а какие тогда кнопки ведут себя "правильно"?)) RESET?)))

приклеил состояние нажатой кнопки, в коде #define KEY_PRESSED 1 //progrik: логическое состояние нажатой кнопки 0 или 1

..ну и в коде не if (!digitalRead(KEY_OK)) а if(digitalRead(KEY_OK) == KEY_PRESSED) везде везде где кнопки....

короче, инвертировал все кнопки. проверить не на чем, так что извольте сами) если я чего напутал - жаль))

//Страница проэкта  <a href="http://srukami.inf.ua/pultoscop_v25110.html" title="http://srukami.inf.ua/pultoscop_v25110.html" rel="nofollow">http://srukami.inf.ua/pultoscop_v25110.html</a>
// === От меня [Electronik83] === mailTo: <a href="mailto:gsmkillersevsk@mail.ru">gsmkillersevsk@mail.ru</a>
// 1. Оптимизировал, причесал код
// 2. Сделал красивей главное меню, добавил новый пункт
// 3. Отображение частатоы и напряжения в осцилле при 1Кгц и 2,5в (был баг автора)
// 4. Новый режим - медленный осцилл - доделал...
// 5. Оптимизация.....
// 6. Убрал програмную растяжку сигнала из осцилла (развертка 7 и 8)
// 7. Изменил отрисовку сигнала в осцилле (больше нету этих раздвоений сигнала)
// 8. Убрал нахрен кнопку питания, и все что с ней сязано - проще тумблер (переключатель) поставить...
// 9. Анимировал главное меню. Пофиксил мерцание.
//10. Вставил свое меню генератора, DDS-генератора, терминала.
//11. Добавил немного новой анимации.
//12. Провел эксперимент с DDS-генератором и регулировкой частоты - не сделать.
// === ver19 ==
//13. В осциле - Сделал автосинхронизацию по серединке амплитуды сигнала <= подумать над внешней синхрой !!!
//14. В осциле - показываем масимальное и минимальное значение амплитуды сигнала справа
//15. Оптимизация некоторых участков кода
//16. Атмега теперь сама меряет напряжение своего питания, не надо ничего мультиметром мерять
//17. В осцилле - сделал более удобный вывод верхней строки в осцилле, "устатичил" вывод частоты.
//      - теперь нет этого дерганья из Гц в кГц (и обратно) - просто выводим в Мгц.
//18. Добавил цифровой анализатор (входы A1, A2, A3, A4, A5) - что то мне он не понравился (и мне тоже).
/////////////// smokok ///////////////  
//19. Добавил Авто-опорное 5.0,1.1,0.2 
//20. Добавил Меню экрана (регулировка контрастности и включение подсвеки экрана).
//21. Добавил температуру. Пин термистора (10к) в А0 и +5, доп резистор (10к) в А0 и -5
//22. Разогнал PULTOSCOPE благодаря progrik
//23. Пока только вырезал  UART приемник
//    В планах внедрить Коэффициент заполнения

Уже наверное надо схему рисовать ,ведь видоизменилась..

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

progrik пишет:

volodya198024 пишет:
Остальные кнопки ведут себя правильно.
ну да, остальные кроме всех работают)) ОК жмакается сам, пункты в меню тоже. а какие тогда кнопки ведут себя "правильно"?)) RESET?)))

приклеил состояние нажатой кнопки, в коде #define KEY_PRESSED 1 //progrik: логическое состояние нажатой кнопки 0 или 1

..ну и в коде не if (!digitalRead(KEY_OK)) а if(digitalRead(KEY_OK) == KEY_PRESSED) везде везде где кнопки....

короче, инвертировал все кнопки. проверить не на чем, так что извольте сами) если я чего напутал - жаль))

//Страница проэкта  <a href="http://srukami.inf.ua/pultoscop_v25110.html" title="http://srukami.inf.ua/pultoscop_v25110.html" rel="nofollow">http://srukami.inf.ua/pultoscop_v25110.html</a>
// === От меня [Electronik83] === mailTo: <a href="mailto:gsmkillersevsk@mail.ru">gsmkillersevsk@mail.ru</a>
// 1. Оптимизировал, причесал код
// 2. Сделал красивей главное меню, добавил новый пункт
// 3. Отображение частатоы и напряжения в осцилле при 1Кгц и 2,5в (был баг автора)
// 4. Новый режим - медленный осцилл - доделал...
// 5. Оптимизация.....
// 6. Убрал програмную растяжку сигнала из осцилла (развертка 7 и 8)
// 7. Изменил отрисовку сигнала в осцилле (больше нету этих раздвоений сигнала)
// 8. Убрал нахрен кнопку питания, и все что с ней сязано - проще тумблер (переключатель) поставить...
// 9. Анимировал главное меню. Пофиксил мерцание.
//10. Вставил свое меню генератора, DDS-генератора, терминала.
//11. Добавил немного новой анимации.
//12. Провел эксперимент с DDS-генератором и регулировкой частоты - не сделать.
// === ver19 ==
//13. В осциле - Сделал автосинхронизацию по серединке амплитуды сигнала <= подумать над внешней синхрой !!!
//14. В осциле - показываем масимальное и минимальное значение амплитуды сигнала справа
//15. Оптимизация некоторых участков кода
//16. Атмега теперь сама меряет напряжение своего питания, не надо ничего мультиметром мерять
//17. В осцилле - сделал более удобный вывод верхней строки в осцилле, "устатичил" вывод частоты.
//      - теперь нет этого дерганья из Гц в кГц (и обратно) - просто выводим в Мгц.
//18. Добавил цифровой анализатор (входы A1, A2, A3, A4, A5) - что то мне он не понравился (и мне тоже).
/////////////// smokok ///////////////  
//19. Добавил Авто-опорное 5.0,1.1,0.2 
//20. Добавил Меню экрана (регулировка контрастности и включение подсвеки экрана).
//21. Добавил температуру. Пин термистора (10к) в А0 и +5, доп резистор (10к) в А0 и -5
//22. Разогнал PULTOSCOPE благодаря progrik
//23. Пока только вырезал  UART приемник
//    В планах внедрить Коэффициент заполнения

Уже наверное надо схему рисовать ,ведь видоизменилась..

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

progrik пишет:

volodya198024 пишет:
Остальные кнопки ведут себя правильно.
ну да, остальные кроме всех работают)) ОК жмакается сам, пункты в меню тоже. а какие тогда кнопки ведут себя "правильно"?)) RESET?)))

приклеил состояние нажатой кнопки, в коде #define KEY_PRESSED 1 //progrik: логическое состояние нажатой кнопки 0 или 1

..ну и в коде не if (!digitalRead(KEY_OK)) а if(digitalRead(KEY_OK) == KEY_PRESSED) везде везде где кнопки....

короче, инвертировал все кнопки. проверить не на чем, так что извольте сами) если я чего напутал - жаль))

//Страница проэкта  <a href="http://srukami.inf.ua/pultoscop_v25110.html" title="http://srukami.inf.ua/pultoscop_v25110.html" rel="nofollow">http://srukami.inf.ua/pultoscop_v25110.html</a>
// === От меня [Electronik83] === mailTo: <a href="mailto:gsmkillersevsk@mail.ru">gsmkillersevsk@mail.ru</a>
// 1. Оптимизировал, причесал код
// 2. Сделал красивей главное меню, добавил новый пункт
// 3. Отображение частатоы и напряжения в осцилле при 1Кгц и 2,5в (был баг автора)
// 4. Новый режим - медленный осцилл - доделал...
// 5. Оптимизация.....
// 6. Убрал програмную растяжку сигнала из осцилла (развертка 7 и 8)
// 7. Изменил отрисовку сигнала в осцилле (больше нету этих раздвоений сигнала)
// 8. Убрал нахрен кнопку питания, и все что с ней сязано - проще тумблер (переключатель) поставить...
// 9. Анимировал главное меню. Пофиксил мерцание.
//10. Вставил свое меню генератора, DDS-генератора, терминала.
//11. Добавил немного новой анимации.
//12. Провел эксперимент с DDS-генератором и регулировкой частоты - не сделать.
// === ver19 ==
//13. В осциле - Сделал автосинхронизацию по серединке амплитуды сигнала <= подумать над внешней синхрой !!!
//14. В осциле - показываем масимальное и минимальное значение амплитуды сигнала справа
//15. Оптимизация некоторых участков кода
//16. Атмега теперь сама меряет напряжение своего питания, не надо ничего мультиметром мерять
//17. В осцилле - сделал более удобный вывод верхней строки в осцилле, "устатичил" вывод частоты.
//      - теперь нет этого дерганья из Гц в кГц (и обратно) - просто выводим в Мгц.
//18. Добавил цифровой анализатор (входы A1, A2, A3, A4, A5) - что то мне он не понравился (и мне тоже).
/////////////// smokok ///////////////  
//19. Добавил Авто-опорное 5.0,1.1,0.2 
//20. Добавил Меню экрана (регулировка контрастности и включение подсвеки экрана).
//21. Добавил температуру. Пин термистора (10к) в А0 и +5, доп резистор (10к) в А0 и -5
//22. Разогнал PULTOSCOPE благодаря progrik
//23. Пока только вырезал  UART приемник
//    В планах внедрить Коэффициент заполнения

Уже наверное надо схему рисовать ,ведь видоизменилась..

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

Виноват,сглючил инет,можно лишнее убрать

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

По просьбе progrik снова мне помог добавить в пультоскоп очередную фишку!!! Теперь при длительном нажатии "ОК" отключается АВТОРАЗВЁРТКА и переходит на ручной режим кнопок "лево - право". Включается так же удержанием. Присутствуют индикаторы состояния "а" и "р".  При коротком нажатии режим "Пауза".  Спасибо большое  progrik за помощьСкетчи залил с кодом #define KEY_PRESSED 0 //progrik: логическое состояние нажатой кнопки 0 или 1

ВИДЕО

Скетч 1230



#define BUFSIZE_SHORT 256 //progrik: буфер для рабочего режима, чем короче - тем быстрее отображение. чем длиннее - тем медленнее, но синхра сможет ловить более "растянутый" сигнал
#define BUFSIZE_LONG  512 //progrik: буфер для паузы. старый буфер был 800
#define PAUSE_OFFSET 1    //progrik: смещение графика за один кадр в режиме паузы. если для паузы указан достаточно длинный буфер, то можно увеличить до 2-3-4.., чтоб быстрее мотало
#define APP 2.7           //         2.7) это переход на аппаратный замер
#define NAPR 255          //         это напряжение сигнала

// Пины экранчика
#define RES 3
#define CS 4
#define Data 6
#define Clock 7
#define swap(a, b) { byte t = a; a = b; b = t; }

//progrik: определяем макросы для действий с управляющими пинами
#define CLOCK_HIGH  bitWrite(PORTD,7,1) // pin7=PORTD,7   pin8=PORTB,0   pin9=PORTB,1  и так далее
#define CLOCK_LOW    bitWrite(PORTD,7,0)
#define DATA_HIGH   bitWrite(PORTD,6,1) // pin6=PORTD,6
#define DATA_LOW     bitWrite(PORTD,6,0)
#define RESET_HIGH  bitWrite(PORTD,3,1) // pin3
#define RESET_LOW    bitWrite(PORTD,3,0)
#define CS_HIGH     bitWrite(PORTD,4,1) // pin4
#define CS_LOW       bitWrite(PORTD,4,0)

#include <FreqCount.h> 
#include <math.h>
#include <PWM.h>

// Настройки пользователя - можно менять
#define OVERCLOCK 16  // частота на которой работает Ардуино
float VCC = 5.0;      // напряжение питания
#define KEY_L  11 // кнопка ЛЕВО  (можно любой пин)
#define KEY_OK 12 // кнопка ОК    (можно любой пин)
#define KEY_R  13 // кнопка ПРАВО (можно любой пин)
#define VBAT   A1 // любой свободный аналоговый пин для измерения напряжения АКБ 
#define TempC  A0 // любой свободный аналоговый пин для измерения температуры
#define KEY_DELAY 200
#define KEY_PRESSED 0 //progrik: логическое состояние нажатой кнопки 0 или 1
#define LCDX 96

#include <avr/pgmspace.h>
const char string_0[] PROGMEM = "ОСЦИЛЛОГРАФ";   // "String 0" и т.д. - это содержимое строк; если необходимо меняйте его
const char string_1[] PROGMEM = " ";
const char string_2[] PROGMEM = "VБат.= ";
const char string_3[] PROGMEM = "Temп.= ";
const char string_4[] PROGMEM = "Pause";

// Теперь создаем таблицу с отсылками к этим строкам:
const char* const string_table[] PROGMEM = {string_0, string_1, string_2, string_3, string_4};
char buffer[11];    // массив должен быть достаточно велик, чтобы вместить даже самую большую строку

byte mode = 0; // пункт главного меню
byte menu = 0; // пункт меню
byte adcBuf[BUFSIZE_LONG];
byte vSync = 30; // уровень синхронизации
bool vRef = 1; // флаг опорного напряжения
bool pause = 0; // флаг режима паузы
bool avtorazv=1;      // Автоматический выбор развертки
byte TextColor = 1;
byte razv = 0;
byte trig = 0;
int kdel=5;       //bulat  автопредел 1,1/0,22 вольта
int osc=15;          // авто вкл. режима осциллографа (секунды)
int grOffset = 0; // смещение графика в рабочем режиме
byte vMax, vMin; // максимальное и минимальное напряжение сигнала
int temp = 0; // временная переменная***
bool flag = 0;
unsigned long ok_last_time; //была ok_press_time
unsigned long ok_start_time;
bool ok_short_press = false;
unsigned long count = 0;
byte LCD_RAM[96 * 9]; // 96 * 9

static const char font[] PROGMEM = {
                                  //HEX B DEC переводим
  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   я
};
//********************************************************
// ==== Считывание напряжения питания ардуинки (Vcc) ====
#define Vref11 1.095 // для точной подстройки результата измерений
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));        // результат преобразования в вольтах
}
/////// ==== Температура ==== ////////
double Thermister(int RawADC) {
  double Temp;
  Temp = log(((10240000/RawADC) - 10000)); // 10000=10кОм Для подстройки правим.
  Temp = 1 / (0.001129148 + (0.000234125 * Temp) + (0.0000000876741 * Temp * Temp * Temp));
  Temp = Temp - 273.15;   // Kelvin to Celcius
  return Temp;
}
void setup()  {
  pinMode(KEY_OK, INPUT); pinMode(KEY_L, INPUT); pinMode(KEY_R, INPUT); // настраиваем кнопки на вход
//  digitalWrite(KEY_OK, HIGH); digitalWrite(KEY_L, HIGH); digitalWrite(KEY_R, HIGH); // подтяжка к плюсу питания
  VCC = ReadVcc();
  Initialize();  //Инициализация дисплея
  Clear_LCD();
  for (byte i = 0; i < 3; i++)
  {
    strcpy_P(buffer, (char*)pgm_read_word(&(string_table[i]))); // необходимые функции и расшифровки, просто скопируйте
    print(10, i * 10, 1, (buffer));
    //delay( 500 );
  }
  Update();
//  delay(KEY_DELAY);
  while (digitalRead(KEY_OK) != KEY_PRESSED) { // цикл, пока не нажали кнопку ОК
  Update();
  // Если не нажата 10 сек кнопка ок вход в режим осцилоографа автоматом
  osc--;
    if (osc==0) break;
  //////////  Выводим показание /////////
    float_print(60, 20, 1, analogRead(VBAT)*VCC/1024,1);  //Вольтаж батареи
    float_print(55, 40, 1, analogRead(TempC), 1);  // выводим на экран показание Температуры
  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[3]))); // пишем слово "Температура"
  print(10, 40, 1, (buffer));
  delay( 300 );
} // цикл нажатия ОК
  // нажали кнопку ОК из меню, инициализируем и выходим из меню
  count = 0; //countX = 0; // восстанавливаем на всякий случай переменные
  if (mode == 0) FreqCount.begin(1000);
  if (mode == 1) {
    InitTimersSafe();
  }
//  if (mode == 2) {
//    InitTimersSafe();
//  }
  if (mode == 3 || mode == 4) DDRC = 0x00; PORTC = 0x00;// Serial.begin(115200); // razv = 0;// весь порт С (это A0...A5) на вход и без подтяжки
  for (char a=37; a>0; a-=2);
  ok_last_time = millis(); //progrik: запоминаем время последнего нажатия
}
// беcконечный цикл - по сути прыгаем в подпрограммы
void loop() {
  switch (mode) {
    // Прыгаем в выбранный режим из главного меню
    case 0 : Oscil();        break; // "выпадаем" в осцилл
  }
}
//  === Читаем с АЦП данные и помещаем их в буфер === // 
void ReadAdc() {
uint16_t len;
len = (pause) ? BUFSIZE_LONG : BUFSIZE_SHORT;
  if (razv % 10) { // (razv>0)        // если развертка без задержек всяких (с 1 по 7)
    ADCSRA = 0b11100000 | (8 - (razv % 10)); // установили делитель (/2 - не работает, так что начинаем с /4)
    for (uint16_t i = 0; i < len; i++) { // цикл для чтения
      while (!(ADCSRA & 0x10));     // ждем готовность АЦП
      ADCSRA |= 0x10;               // запускаем следующее преобразование
      adcBuf[i] = ADCH;             // записываем данные в массив
    }
   // delay(0.3 * BUFSIZE_LONG);  //progrik: не нужно тормозить то, что и так еле работает))  // компенсация задержки по сравнению с разверткой 0...
  } else {                          // развертка с задержками (delay)
    ADCSRA = 0b11100111;            // делитель на /128
    for (uint16_t i = 0; i < len; i++) { // цикл для чтения
      while (!(ADCSRA & 0x10));     // ждем готовность АЦП
      ADCSRA |= 0x10;               // запускаем следующее преобразование
      delayMicroseconds(500);       // делаем задержку
      adcBuf[i] = ADCH;             // записываем данные в массив
    }
  }
}

// ==== функция вывода 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));
}



/*int GetCountsOfDigits(int n) //найти количество цифр в числе <a href="<a href="<a href="<a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a>" rel="nofollow"><a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a></a>" rel="nofollow"><a href="<a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a>" rel="nofollow"><a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a></a></a>" rel="nofollow"><a href="<a href="<a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a>" rel="nofollow"><a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a></a>" rel="nofollow"><a href="<a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a>" rel="nofollow"><a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a></a></a></a>
{
  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;
}*/

// ==== найти количество цифр в числе   <a href="<a href="<a href="<a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a>" rel="nofollow"><a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a></a>" rel="nofollow"><a href="<a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a>" rel="nofollow"><a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a></a></a>" rel="nofollow"><a href="<a href="<a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a>" rel="nofollow"><a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a></a>" rel="nofollow"><a href="<a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a>" rel="nofollow"><a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a></a></a></a>
/*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 Initialize() {
  pinMode(RES,   OUTPUT);
  pinMode(CS,    OUTPUT);
  pinMode(Data,  OUTPUT);
  pinMode(Clock, OUTPUT);
  // Инициализация дисплея
  //  dWrite(RES, 1);
  RESET_HIGH;
//  dWrite(Clock, 0);
  CLOCK_LOW;
//  dWrite(Data, 0);
  DATA_LOW;
//  dWrite(CS, 0);
//  CS_LOW;
  delay(20);
//  dWrite(CS, 1);
  CS_HIGH;
  SendByte(0, 0x2F);           // Power control set(charge pump on/off)
  SendByte(0, 0xA4);
  SendByte(0, 0xAF);           // экран вкл/выкл
  //Clear_LCD();
  //Update();
//  delay(500); //progrik: задержка после включения
//  CS_LOW; //progrik: вернул для тестов
//  delay(500); //progrik: задержка после включения
}

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

//=========================================================Отправка 9 байт
//progrik: добалено определение inline. функция встраивается в код и не расходуется время на ее вызов.
//progrik: вместо многократных вызовов функций дергаем ногами при помощи написанных вверху кода макросов
inline __attribute__((always_inline)) void SendByte(byte mode, byte c) {
  //dWrite(CS, 0); //progrik: чип селект нужно включить один раз в конце инициализации дисплея, и больше не трогать. перенес в инициализацию
  CS_LOW; //progrik: вернул для тестов
  //(mode) ? dWrite(Data, 1) : dWrite(Data, 0);
  (mode) ? DATA_HIGH : DATA_LOW;
  //dWrite(Clock, 1);
  CLOCK_HIGH;
  for (byte i = 0; i < 8; i++)
  {
    //dWrite(Clock, 0);
    CLOCK_LOW;
    //(c & 0x80) ? dWrite(Data, 1) : dWrite(Data, 0);
    (c & 0x80) ? DATA_HIGH : DATA_LOW;
    //dWrite(Clock, 1);
    CLOCK_HIGH;
    c <<= 1;
  }
  //dWrite(Clock, 0);
  CLOCK_LOW;
}

//======================================================Очистка дисплея
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]);
    }
  }
}

//===================================================Нарисовать пиксель
inline __attribute__((always_inline)) void drawPixel (byte x, byte y, boolean color) {
//  if ((x < 0) || (x >= 96) || (y < 0) || (y >= 68)) return; //  NAN херня
  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);
if(line & 0x1)
{
  drawPixel(x + i, y + j, color);
}
else
{
  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++) {
if(steep)
{
  drawPixel(y0, x0, color);
}
else
{
  drawPixel(x0, y0, color);
}
    err -= dy;
    if (err < 0) {
      y0 += ystep;
      err += dx;
    }
  }
}
/////////////////добавь где-то, например, под функцией DrawLine такую функцию:////////////////////////////
void drawVLineUltraFast(byte x, byte y0, byte y1, boolean color) {
  if(y0 > y1)
  {
    swap(y0, y1);
  }
  do
  {
    drawPixel(x, y0, color);
  }
  while(++y0 <= y1);
}
//========================================Рисование вертикальной линии
void drawFastVLine(byte x, byte y, byte h, boolean color) {
  //drawLine(x, y, x, y + h - 1, color);
  byte y1 = y + h - 1;
  do
  {
    drawPixel(x, y, color);
  }
  while(++y <= y1);
}

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

//=====================================Рисование залитый прямоугольник
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);
}
//progrik: для расчета напряжений ---------------
void calc_val()
{
  vMax = 0; vMin = 0xFF;
  uint16_t l = (pause) ? BUFSIZE_LONG : BUFSIZE_SHORT;
  for (uint16_t x = 0; x < l; x++) {
    if (vMax < adcBuf[x]) vMax = adcBuf[x];  // пока 255, но надо экспериментировать
    if (vMin > adcBuf[x]) vMin = adcBuf[x];
  }
  }
// === Осциллоскоп === //
void Oscil() {
  // установка опорного напряжения АЦП и настройка входа АЦП
  ADMUX = vRef ? 0b01100011 : 0b11100011;
  label_ReadAdc:
  // === Обработка кнопок === //
  if (digitalRead(KEY_L) == KEY_PRESSED)   switch (menu) { // кнопка лево:)
      case 0 : razv--; if (razv == NAPR) razv = 6; delay(200);                                 break; // меняем развертку
      case 1 : grOffset -= PAUSE_OFFSET; if (grOffset < 0) grOffset = 0;                      break; // листаем график в паузе
    }
  if (digitalRead(KEY_R) == KEY_PRESSED)  switch (menu) { // кнопка право:)
      case 0 : razv++; if (razv == 7) razv = 0; delay(200);                                  break; // меняем развертку
      case 1 : grOffset += PAUSE_OFFSET; if (grOffset > BUFSIZE_LONG - LCDX) grOffset = BUFSIZE_LONG - LCDX; break; // листаем график в паузе
    }
  if (digitalRead(KEY_OK) == KEY_PRESSED)
   {
     if(ok_short_press && (millis() - ok_start_time) > 600) //progrik: задержка для длинного нажатия
     {
       avtorazv = !avtorazv;   //progrik: переключит авторазвертку после длительного нажатия ^^^ 600ms
       ok_short_press = false;
     }
     if((millis() - ok_last_time) > 200) //progrik: подавление дребезга - ждем 200 мс со времени последнего нажатия
     {
       ok_start_time = millis(); //progrik: запоминаем время первого нажатия кнопки OK
       ok_short_press = true;   //progrik: отмечаем флаг короткого нажатия
     }
     ok_last_time = millis(); //progrik: запоминаем время последнего нажатия кнопки OK
   }
   else
   {
     if(ok_short_press) //progrik: если нужно отработать короткое нажатие (флаг установлен)
     {
       ok_short_press = false; //progrik: снимаем флаг короткого нажатия
       switch (++menu)
       {
         case 1: grOffset = 0; pause = 1; ReadAdc(); calc_val(); break; 
// вход в паузу - антидребезг типа
         case 2: menu = 0;  pause = 0; break; // перебор меню
       }
     }
   }
  // === Ведём рассчеты === //
  if (!pause) { // если нет паузы 
    ReadAdc();  // то снимаем осциллограмму
    calc_val();
    // == Вычисляем максимальное и минимальное значение сигнала == //
    vSync = (vMax - vMin) / 2 + vMin; // уровень синхронизации по середине уровня сигнала
    // == Определение точки синхронизации == //
    bool flagZero=0; grOffset = 0; // сброс флага и точки синхронизации    
    // Ищем перепад от меньшего уровня к большему
    for (uint8_t x=1; x < BUFSIZE_SHORT - LCDX; x++) { // смотрим весь массив данных АЦП
      if (adcBuf[x]<vSync) flagZero=1;   // нашли меньше, чем синхра (перепад сигнала в минус) - ставим флаг
      if (flagZero && adcBuf[x]>vSync) { grOffset=x; break; } // нашли больше, чем синхра (перепад сигнала в плюс) - запомнили и выходим из цикла
    }
    // === Считаем частоту сигнала === //
    if (vRef && vMax * VCC / NAPR > APP) { // если можем замерить аппаратно - меряем. > 2.7) это переход на аппаратный замер
      if (FreqCount.available()) count = FreqCount.read(); // вывод частоты по готовности счетчика частоты сигнала
    } else { // === Меряем частоту сигнала программно === //
      flagZero = 0; count = 0; // сброс флага и счетчика
      for(uint8_t x=grOffset; x < BUFSIZE_SHORT - LCDX; x++)  { // смотрим массив от точки синхронизации до конца
        if (adcBuf[x] < vSync) flagZero = 1; // нашли меньше, чем синхра (перепад сигнала в минус) - выставляем флаг
        if (flagZero && adcBuf[x] > vSync) { // нашли больше, чем синхра (перепад сигнала в плюс) - отловили полный период
          switch (razv) { // считем частоту периода
            case 6: count = 1000000 / ((x - grOffset - 1) * 3.25);   break; // делитель 4
            case 5: count = 1000000 / ((x - grOffset)  * 3.25) / 2;  break; // делитель 8
            case 4: count = 1000000 / ((x - grOffset)  * 3.25) / 4;  break; // делитель 16
            case 3: count = 1000000 / ((x - grOffset)  * 3.25) / 8;  break; // делитель 32
            case 2: count = 1000000 / ((x - grOffset)  * 3.25) / 16; break; // делитель 64
            case 1: count = 1000000 / ((x - grOffset)  * 3.25) / 32; break; // делитель 128
            case 0: count = 1000000 / ((x - grOffset)  * 510);       break; // делитель 128 тоже
          }
          break;
        }
      }
    }
    count = count * OVERCLOCK / 16.0; // пересчет частоты на разные кварцы
  } // закончили вести рассчеты
  Clear_LCD(); // чистим экран...
  // === Отрисовка меню осцилла ===
  if (vRef) float_print(0, 0, 1, VCC, 1); else {
  if(kdel==5)print(0, 0, 1, "1.1"); else print(0, 0, 1, "*22");  // отображение опорного
  }
  if (menu == 0) TextColor = 0; else TextColor = 1; print(27, 0,  1, razv);  // отображение развёртки
  if (vRef && vMax * VCC / NAPR > APP)  drawChar(19, 0, 1, 'а'); // если замер  - "А"ппаратный
  else  drawChar(19, 0, 1, 'п'); // иначе "П"рограмный
  if (menu == 1) { // тут всё для паузы
//    delay(20);
  } else TextColor = 1;
  if (!pause) TextColor = 0;
  else { // рисуем прокрутку в режиме паузы
    strcpy_P(buffer, (char*)pgm_read_word(&(string_table[4])));
    print(6, 12, 1, (buffer));
    temp = grOffset / 4.7;  // место остоновки чёрточки при прокрутке в паузе в конце экрана
    drawFastHLine(temp, 10, 7, 1);  drawFastHLine(temp, 9, 7, 1);  //чёрточка прокрутки в паузе
  } // шкала прокрутки закончилась
  (pause) ? float_print (48, 0, 0, count / 1000000.0, 6) : float_print (48, 0, 1, count / 1000000.0, 6); // вывод частоты
  float_print(73, 12, 1, vMax * (vRef ? VCC : 1.1) / NAPR, 2); // рисуем максимальное напряжение сигнала
  float_print(78, 58, 1, vMin * (vRef ? VCC : 1.1) / NAPR, 1); // рисуем минимальное  напряжение сигнала
  if (vMax==0xFF) for(uint8_t x=10; x<68; x++) drawFastHLine( 0, x, 5, 5); // превышение максимума АЦП
  if (avtorazv) drawChar(34, 0, 1, 'а');   // указатель состояния авторазвёртки
  else  drawChar(34, 0, 1, 'p');
  // == Отрисовка сетки == //
  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, 19, 1);  // это сеточка # горизонтальные линии
    drawPixel(i, 39, 1);
    drawPixel(i, 59, 1);
  }
             ///////////////авто razv//////////////
  if (avtorazv)
  #define PER 1.3
  if (count > 4323.3 * PER) razv = 6;
  else if (count > 2434.5 * PER) razv = 5;
  else if (count > 0969.3 * PER) razv = 4;
  else if (count > 0486.8 * PER) razv = 3;
  else if (count > 0245.8 * PER) razv = 2;
  else if (count > 0120.1 * PER) razv = 1;
  else razv = 0;
      //bulat если зашкаливает включаем предел 5 в  
  if (vMax==NAPR){
    if (vRef==0)
       {
        vRef=1;
       ADMUX = 0b01100011;// выбор внутреннего опорного 5.0 В
           goto   label_ReadAdc;
     }
  }
//bulat если  5 в  и уровень менее 1,1 в то вкл предел 1,1 в
  if (vMax<=55){
    if (vRef==1)
       {
        vRef=0;
        ADMUX = 0b11100011;// выбор внутреннего опорного 1,1В
        goto   label_ReadAdc;
     }
  }
  //bulat здесь автопредел 1,1/0,22 в,программный
  kdel=5;
  if (vMax<=55){
    if (vRef==0)
       {
       kdel=1;
     }
  }
  // == Отрисовка графика == //
  for (uint8_t x = 0; x < 92; x++)
    drawVLineUltraFast(x + 4, 67 - adcBuf[x + grOffset]/kdel / 0.9, 67 - adcBuf[x + grOffset + 1]/kdel / 0.9, 1); //progrik: drawVLineUltraFast вместо медленной drawLine
  Update();
}

Скетч 5110

//Страница проэкта  <a href="<a href="http://srukami.inf.ua/pultoscop_v25110.html" rel="nofollow">http://srukami.inf.ua/pultoscop_v25110.html</a>" title="<a href="http://srukami.inf.ua/pultoscop_v25110.html" rel="nofollow">http://srukami.inf.ua/pultoscop_v25110.html</a>" rel="nofollow"><a href="http://srukami.inf.ua/pultoscop_v25110.html" rel="nofollow">http://srukami.inf.ua/pultoscop_v25110.html</a></a>
// === От меня [Electronik83] === mailTo: <a href="mailto:gsmkillersevsk@mail.ru">gsmkillersevsk@mail.ru</a>
// 1. Оптимизировал, причесал код
// 2. Сделал красивей главное меню, добавил новый пункт
// 3. Отображение частатоы и напряжения в осцилле при 1Кгц и 2,5в (был баг автора)
// 4. Новый режим - медленный осцилл - доделал...
// 5. Оптимизация.....
// 6. Убрал програмную растяжку сигнала из осцилла (развертка 7 и 8)
// 7. Изменил отрисовку сигнала в осцилле (больше нету этих раздвоений сигнала)
// 8. Убрал нахрен кнопку питания, и все что с ней сязано - проще тумблер (переключатель) поставить...
// 9. Анимировал главное меню. Пофиксил мерцание.
//10. Вставил свое меню генератора, DDS-генератора, терминала.
//11. Добавил немного новой анимации.
//12. Провел эксперимент с DDS-генератором и регулировкой частоты - не сделать.
// === ver19 ==
//13. В осциле - Сделал автосинхронизацию по серединке амплитуды сигнала <= подумать над внешней синхрой !!!
//14. В осциле - показываем масимальное и минимальное значение амплитуды сигнала справа
//15. Оптимизация некоторых участков кода
//16. Атмега теперь сама меряет напряжение своего питания, не надо ничего мультиметром мерять
//17. В осцилле - сделал более удобный вывод верхней строки в осцилле, "устатичил" вывод частоты.
//      - теперь нет этого дерганья из Гц в кГц (и обратно) - просто выводим в Мгц.
//18. Добавил цифровой анализатор (входы A1, A2, A3, A4, A5) - что то мне он не понравился (и мне тоже).
/////////////// smokok ///////////////  
//19. Добавил Авто-опорное 5.0,1.1,0.2 
//20. Добавил Меню экрана (регулировка контрастности и включение подсвеки экрана).
//21. Добавил температуру. Пин термистора (10к) в А0 и +5, доп резистор (10к) в А0 и -5
//22. Разогнал PULTOSCOPE благодаря progrik
//23. Пока только вырезал  UART приемник
//24. И ещё вырезал цифровой анализатор
//25. Благодаря progrik добавил оттключение АвтоРазвёртки на длительное нажатие "ОК"
//    В планах внедрить Коэффициент заполнения
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
#include <FreqCount.h>
#include <EEPROM.h>
#include <math.h>
#include <PWM.h>

// Настройки пользователя - можно менять
#define Ekran     8  //Экран///Pin///Подсветka
#define OVERCLOCK 16  // частота на которой работает Ардуино
float VCC = 5.0;      // напряжение питания (меряем мультиметром - СТАЛО НЕ НАДО)
Adafruit_PCD8544 display = Adafruit_PCD8544(7, 6, 4, 3, 2); // пины к которым у вас подключен дисплей
byte contrast; // контрастность дисплея
byte Set = 0; // Настройка экрана
bool BL; // Подсветка

#define KEY_L  11 // кнопка ЛЕВО  (можно любой пин)
#define KEY_OK 12 // кнопка ОК    (можно любой пин)
#define KEY_R  13 // кнопка ПРАВО (можно любой пин)
#define VBAT   A5 // любой свободный аналоговый пин для измерения напряжения АКБ 
#define KEY_DELAY 200
#define KEY_PRESSED 0 //progrik: логическое состояние нажатой кнопки 0 или 1

// Переменные, дефайны и прочяя хрень - менять со смыслом
#define GEN_PIN  9  // пин для генератора сигналов (не менять)
#define DDS_PIN  10 // пин для генератора dds (не менять)
byte mode = 0; // пункт главного меню
byte menu = 0; // пункт меню

#define BUFSIZE_SHORT 256 //progrik: буфер для рабочего режима, чем короче - тем быстрее отображение. чем длиннее - тем медленнее, но синхра сможет ловить более "растянутый" сигнал
#define BUFSIZE_LONG  512 //progrik: буфер для паузы. старый буфер был 800
#define PAUSE_OFFSET 2    //progrik: смещение графика за один кадр в режиме паузы. если для паузы указан достаточно длинный буфер, то можно увеличить до 2-3-4.., чтоб быстрее мотало
#define APP 2.7           //         2.7) это переход на аппаратный замер
#define NAPR 255          //         это напряжение сигнала
#define LCDX 80

byte adcBuf[BUFSIZE_LONG];
byte vSync = 30;   // уровень синхронизации
bool vRef = 1; // флаг опорного напряжения
bool pause = 0; // флаг режима паузы
byte razv = 4;
int grOffset = 0; // смещение графика в рабочем режиме
byte vMax, vMin;// максимальное и минимальное напряжение сигнала
int kdel = 5;         //bulat  автопредел 1,1/0,2 вольта
bool avtorazv = 1;    // Автоматический выбор развертки
bool flag = 0;
bool kzap = 0; //флаг коэффициента заполнения
unsigned long ok_last_time; //была ok_press_time
unsigned long ok_start_time;
bool ok_short_press = false;

// Переменные для генератора
int PWM = 50;//стартовое значение ШИМ от 0 до 100 для генератора
unsigned long freq = 500; // стартовое значение частоты в Гц для генератора
unsigned long stepFreq = 0;

//int d=0; // можно подменить чем нибудь. count например....
unsigned long count = 0;
//long countX=0;
//uint32_t tic1;
//uint32_t tic2;
//int duty;
//long speedTTL=9600; //скорость терминала

void ShowMode(char y) { // выводим режим работы. y - строка, где рисовать
const char* const modeStr[] PROGMEM= { " Oscilloscop ",    "     PWM     ", "     DDS     ", "     АЧХ     ", "    ЭKPAH    "};
  display.setCursor(3, y);      // задаем позицию на экране
  display.print(modeStr[mode]); // выводим режим работы
}
void ShowModeInv(char y) { // выводим режим работы инверсно. y - строка, где рисовать
  display.drawLine(0, y, 83, y, BLACK);
  display.setTextColor(WHITE, BLACK);
  display.setCursor(0, y+1); display.println(" "); display.setCursor(78, y+1); display.println(" ");  // для красоты только
  ShowMode(y+1);  display.setTextColor(BLACK);
}
double Thermister(int RawADC) {
  double Temp;
  Temp = log(((10240000 / RawADC) - 10000)); // 10000=10кОм Для подстройки правим.
  Temp = 1 / (0.001129148 + (0.000234125 * Temp) + (0.0000000876741 * Temp * Temp * Temp));
  Temp = Temp - 273.15;   // Kelvin to Celcius
  return Temp;
}
void printTemp(void) {
  display.setTextColor(BLACK);
  display.setCursor(15, 37);
  display.print("Temp ");
  display.print(float(Thermister(analogRead(0))), 1); // (0) это аналог выход А0
}
// ==== Считывание напряжения питания ардуинки (Vcc) ====
#define Vref11 1.095 // для точной подстройки результата измерений
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));        // результат преобразования в вольтах
}

void setup()  {

  ////////////////////////////Экран//////////////////////////////
  //  Примечание. Экран становится темным,  решение проблемы.///////////////////////////////////////
  //  EEPROM.write(0, 50);  //  Записать закрытую строку,  Вгрузить,  и начать использовать.
  contrast = EEPROM.read(0);
  BL       = EEPROM.read(1);
  pinMode(Ekran, OUTPUT);
  digitalWrite(Ekran, BL);
  //////////////////////////////////////////////////////
  pinMode(KEY_OK, INPUT); pinMode(KEY_L, INPUT); pinMode(KEY_R, INPUT); // настраиваем кнопки на вход
  display.begin();
  display.setContrast(contrast);
  VCC = ReadVcc();
  while (digitalRead(KEY_OK) != KEY_PRESSED) { // цикл, пока не нажали кнопку ОК
    // опрос кнопок
    if (digitalRead(KEY_R) == KEY_PRESSED) {
      mode++;
      if (mode >4  ) mode = 0;
    }
    if (digitalRead(KEY_L) == KEY_PRESSED) {
      mode--;
      if (mode == NAPR) mode = 4;
    }
    display.clearDisplay();  // чистим буфер экранчика
    display.setCursor(24, 19);  // выводим напряжение батареи
    display.setTextColor(WHITE, BLACK);
    display.print(" ");
    display.print(analogRead(VBAT)*VCC / 1024, 1);
    display.print(" V");

    // динамическая графика в скролле
#define COUNT 84 // ширина дисплея, в пикселях
    for (char a = 3; a < 14; a += 2) {
      display.drawLine((count + a   ) % COUNT, a + 4, (count + a   ) % COUNT, 40 - a, BLACK); // левые  полосочки по бокам
      display.drawLine((count - a + 83) % COUNT, a + 4, (count - a + 83) % COUNT, 40 - a, BLACK); // правые полосочки по бокам
    }
    if (count % 84 == 0) { // если больше не надо скроллить
      ShowModeInv(0);
      display.drawRect(21, 18, 42, 11, BLACK); // рамка вокруг картинки
      display.drawRect(19, 16, 46, 15, BLACK); // рамка вокруг картинки
      //        display.drawRect(16, 14, 52, 19, BLACK); // рамка вокруг картинки
    }
    printTemp(); //       Терпература
    display.display();
    delay(150); // задержка отображения уровня заряда, температуры и кнопок заодно
    //    display.display();
  } // цикл нажатия ОК
  // нажали кнопку ОК из меню, инициализируем и выходим из меню

  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) razv = 0;
  if (mode == 5) {
    DDRC = 0x00;  // весь порт С (это A0...A5) на вход и без подтяжки
    PORTC = 0x00;
  }
  // антидребезг кнопки ОК
  for (char a = 37; a > 0; a -= 2) {
    display.clearDisplay();  // - это вместо delay(200);
    ShowModeInv(a);
    display.display();
  }
  ok_last_time = millis(); //progrik: запоминаем время последнего нажатия
}
// безконечный цикл - по сути прыгаем в подпрограммы
void loop() {
  switch (mode) { // Прыгаем в выбранный режим из главного меню
    case 0 : Oscil();        break; // "выпадаем" в осцилл
    case 1 : Generator();    break; // "выпадаем" в генератор
    case 2 : DdsGenerator(); break; // "выпадаем" в DDS генератор
    //    case 3 : Terminal();     break; // "выпадаем" в USART приемник
    case 3 : OscilSlow();    break; // "выпадаем" в медленный осцилл
//    case 4 : LogAnalyzer();  break; // "выпадаем" в анализатор
    case 4 : Menu_ekrana();  break; // "выпадаем" в меню экрана
  }
}
//  === Читаем с АЦП данные и помещаем их в буфер === //
void ReadAdc() {
  uint16_t len;
  len = (pause) ? BUFSIZE_LONG : BUFSIZE_SHORT;
  if (razv % 10) { // (razv>0)        // если развертка без задержек всяких (с 1 по 7)
    ADCSRA = 0b11100000 | (8 - (razv % 10)); // установили делитель (/2 - не работает, так что начинаем с /4)
    for (uint16_t i = 0; i < len; i++) { // цикл для чтения
      while (!(ADCSRA & 0x10));     // ждем готовность АЦП
      ADCSRA |= 0x10;               // запускаем следующее преобразование
      adcBuf[i] = ADCH;             // записываем данные в массив
    }
    //    delay(0.3 * BUFSIZE);           // компенсация задержки по сравнению с разверткой 0...
  } else {                          // развертка с задержками (delay)
    ADCSRA = 0b11100111;            // делитель на /128
    for (uint16_t i = 0; i < len; i++) { // цикл для чтения
      while (!(ADCSRA & 0x10));     // ждем готовность АЦП
      ADCSRA |= 0x10;               // запускаем следующее преобразование
      delayMicroseconds(500);       // делаем задержку
      adcBuf[i] = ADCH;             // записываем данные в массив
    }
  }
}

//progrik: для расчета напряжений ---------------
void calc_val()
{
  vMax = 0; vMin = 0xFF;
  uint16_t l = (pause) ? BUFSIZE_LONG : BUFSIZE_SHORT;
  for (uint16_t x = 0; x < l; x++) {
    if (vMax < adcBuf[x]) vMax = adcBuf[x];  // пока 255, но надо экспериментировать
    if (vMin > adcBuf[x]) vMin = adcBuf[x];
  }

  // == Вычисляем максимальное и минимальное значение сигнала == //
  vSync = (vMax - vMin) / 2 + vMin; // уровень синхронизации по середине уровня сигнала
  // == Определение точки синхронизации == //
  bool flagZero = 0; grOffset = 0; // сброс флага и точки синхронизации
  // Ищем перепад от меньшего уровня к большему
  for (uint8_t x = 1; x < BUFSIZE_SHORT - LCDX; x++) { // смотрим весь массив данных АЦП
    if (adcBuf[x] < vSync) flagZero = 1; // нашли меньше, чем синхра (перепад сигнала в минус) - ставим флаг
    if (flagZero && adcBuf[x] > vSync) {
      grOffset = x;  // нашли больше, чем синхра (перепад сигнала в плюс) - запомнили и выходим из цикла
      break;
    }
  }
  // === Считаем частоту сигнала === //
  if (vRef && vMax * VCC / NAPR > APP) { // если можем замерить аппаратно - меряем
    if (FreqCount.available()) count = FreqCount.read(); // вывод частоты по готовности счетчика частоты сигнала
  } else { // === Меряем частоту сигнала программно === //
    flagZero = 0; count = 0; // сброс флага и счетчика
    for (uint8_t x = grOffset; x < BUFSIZE_SHORT - LCDX; x++)  { // смотрим массив от точки синхронизации до конца
      if (adcBuf[x] < vSync) flagZero = 1; // нашли меньше, чем синхра (перепад сигнала в минус) - выставляем флаг
      if (flagZero && adcBuf[x] > vSync) { // нашли больше, чем синхра (перепад сигнала в плюс) - отловили полный период
        switch (razv) { // считем частоту периода
          case 6: count = 1000000 / ((x - grOffset - 1) * 3.25);    break; // делитель 4
          case 5: count = 1000000 / ((x - grOffset)  * 3.25) / 2;  break; // делитель 8
          case 4: count = 1000000 / ((x - grOffset)  * 3.25) / 4;  break; // делитель 16
          case 3: count = 1000000 / ((x - grOffset)  * 3.25) / 8;  break; // делитель 32
          case 2: count = 1000000 / ((x - grOffset)  * 3.25) / 16; break; // делитель 64
          case 1: count = 1000000 / ((x - grOffset)  * 3.25) / 32; break; // делитель 128
          case 0: count = 1000000 / ((x - grOffset)  * 510);     break; // делитель 128 тоже
        }
        break;
      }
    }
  }
  count = count * OVERCLOCK / 16.0; // пересчет частоты на разные кварцы
}
// === Осциллоскоп === //
void Oscil() {
  // установка опорного напряжения АЦП и настройка входа АЦП
  ADMUX = vRef ? 0b01100011 : 0b11100011;
label_ReadAdc:
  // === Обработка кнопок === //
  if (digitalRead(KEY_L) == KEY_PRESSED)   switch (menu) { // кнопка лево:)
      case 0 : razv--; if (razv == NAPR) razv = 6; delay(200);                                 break; // меняем развертку
      case 1 : grOffset -= PAUSE_OFFSET; if (grOffset < 0) grOffset = 0;                      break; // листаем график в паузе
    }
  if (digitalRead(KEY_R) == KEY_PRESSED)  switch (menu) { // кнопка право:)
      case 0 : razv++; if (razv == 7) razv = 0; delay(200);                                  break; // меняем развертку
      case 1 : grOffset += PAUSE_OFFSET; if (grOffset > BUFSIZE_LONG - LCDX) grOffset = BUFSIZE_LONG - LCDX; break; // листаем график в паузе
    }
  if (digitalRead(KEY_OK) == KEY_PRESSED)
   {
     if(ok_short_press && (millis() - ok_start_time) > 600) //progrik: задержка для длинного нажатия
     {
       avtorazv = !avtorazv;   //progrik: переключит авторазвертку после длительного нажатия ^^^ 600ms
       ok_short_press = false;
     }
     if((millis() - ok_last_time) > 200) //progrik: подавление дребезга - ждем 200 мс со времени последнего нажатия
     {
       ok_start_time = millis(); //progrik: запоминаем время первого нажатия кнопки OK
       ok_short_press = true;   //progrik: отмечаем флаг короткого нажатия
     }
     ok_last_time = millis(); //progrik: запоминаем время последнего нажатия кнопки OK
   }
   else
   {
     if(ok_short_press) //progrik: если нужно отработать короткое нажатие (флаг установлен)
     {
       ok_short_press = false; //progrik: снимаем флаг короткого нажатия
       switch (++menu)
       {
         case 1: grOffset = 0; pause = 1; ReadAdc(); calc_val(); break; 
// вход в паузу - антидребезг типа
         case 2: menu = 0;  pause = 0; break; // перебор меню
       }
     }
   }
  // === Ведём рассчеты === //
  if (!pause) { // если нет паузы
    ReadAdc();  // то снимаем осциллограмму
    calc_val();
  } // закончили вести рассчеты

  display.clearDisplay(); // чистим экран...
  /*      if (count < 10) kzap=true;
    if (kzap==true && count > 10){
    FreqCount.end();
    tic1 = asm_func(1);
    tic2 = asm_func(0);
    duty=(1/((float)tic1/tic2))*100;
    kzap=false;
    FreqCount.begin(1000);
    }
    if (kzap==false && count > 10 && duty < 100){
    display.setCursor(18,11);
    display.setTextColor(BLACK);
    display.print(duty);
    display.print("%");
    }*/
  // === Отрисовка меню осцилла ===
  if (vRef) display.print(VCC, 1); else {
    if (kdel == 5)display.print("1.1"); else display.print("*22");
  }
  if (menu == 0) display.setTextColor(BLACK, WHITE);
  if (vRef && vMax * VCC / NAPR > APP) {
    display.print("a");  // если замер аппаратный - "ужирняем" точку
  }
  else  {
    display.print("п");  // иначе "П"рограмный
  }
  display.print(razv); 
  display.print(avtorazv?'a':'p');   // указатель состояния авторазвёртки
  if (menu == 1)
    if (!pause)  display.setTextColor(BLACK);
  else {   // рисуем прокрутку в режиме паузы
  display.setCursor(5, 11); display.print("Pause");  display.drawLine(grOffset / 5.7, 8, grOffset / 5.7 + 7, 8, BLACK); display.drawLine(grOffset / 5.7, 9, grOffset / 5.7 + 7, 9, BLACK);
   }  // шкала прокрутки
  display.setCursor(36, 0); display.print(count / 1000000.0, 6);  //покажем частоту
  display.setCursor(60, 11); display.print(vMax * (vRef ? VCC : 1.1) / NAPR, 2); // рисуем максималоное напряжение сигнала
  display.setCursor(65, 40); display.print(vMin * (vRef ? VCC : 1.1) / NAPR, 1); // рисуем минимальное  напряжение сигнала
  if (vMax == 0xFF) for (uint8_t x = 0; x < 5; x++) display.drawLine(x, 9, x, 47, BLACK); // указатель привышения напряжения на входе осцила

  // == Отрисовка сетки == //
  for (byte i = 47; i > 5; i = i - 7) {
    display.drawLine( 0, i, 2, i, BLACK);  // черточки слева
  }
  for (byte i = 47; i > 5; i = i - 3) {
    display.drawPixel(21, i, BLACK);  // вертикальный пунктир
    display.drawPixel(42, i, BLACK);
    display.drawPixel(63, i, BLACK);
  }
  for (byte i = 3; i < 84; i = i + 3) {
    display.drawPixel(i, 33, BLACK);
    display.drawPixel(i, 19, BLACK);
  }

  ///////////////авто razv//////////////
  if (avtorazv)
#define PER 1.3
  if (count > 4323.3 * PER) razv = 6;
  else if (count > 2434.5 * PER) razv = 5;
  else if (count > 0969.3 * PER) razv = 4;
  else if (count > 0486.8 * PER) razv = 3;
  else if (count > 0245.8 * PER) razv = 2;
  else if (count > 0120.1 * PER) razv = 1;
  else razv = 0;
  //bulat если зашкаливает включаем предел 5 в
  if (vMax == NAPR) {
    if (vRef == 0)
    {
      vRef = 1;
      ADMUX = 0b01100011;// выбор внутреннего опорного 5.0 В
      goto   label_ReadAdc;
    }
  }
  //bulat если  5 в  и уровень менее 1,1 в то вкл предел 1,1 в
  if (vMax <= 55) {
    if (vRef == 1)
    {
      vRef = 0;
      ADMUX = 0b11100011;// выбор внутреннего опорного 1,1В
      goto   label_ReadAdc;
    }
  }
  //bulat здесь автопредел 1,1/0,22 в,программный
  kdel = 5;
  if (vMax <= 55) {
    if (vRef == 0)
    {
      kdel = 1;
    }
  }
  // == Отрисовка графика == //
  for (uint8_t x = 0; x < 80; x++)
    display.drawLine(x + 4, 47 - adcBuf[x + grOffset] / kdel / 1.35, x + 4, 47 - adcBuf[x + grOffset + 1] / kdel / 1.35, BLACK);
  display.display(); // вываливаем буфер экрана на экран
}
////////////////////////////////////////////////////////////////////////////////////////////
// РЕЖИМ ГЕНЕРАТОРА
void Generator() {
  // обработка кнопок и отрисовка одновременно
  display.clearDisplay();
  ShowModeInv(0);
  if (digitalRead(KEY_OK) == KEY_PRESSED) {
    if (menu++ == 7) menu = 0;  // переходим по разрядам / меняем ШИМ
    delay(KEY_DELAY);
  }
  if (menu == 7) { // меняем ШИМ
    if (digitalRead(KEY_L) == KEY_PRESSED) {
      PWM--;
      if (PWM < 0)   PWM = 100;
      delay(KEY_DELAY);
    }
    if (digitalRead(KEY_R) == KEY_PRESSED) {
      PWM++;
      if (PWM > 100) PWM = 0;
      delay(KEY_DELAY);
    }
    pwmWrite(GEN_PIN, PWM * 2.55); // задали ШИМ
    display.setTextColor(WHITE, BLACK); // если меняем шим, то рисуем его инверсным
    display.drawLine(15, 39, 68 + (PWM == 100 ? 6 : 0) + (PWM < 10 ? -6 : 0), 39, BLACK); // сверху полоска для большей инверсности
  } else { // меняем частоту
    if (digitalRead(KEY_L) == KEY_PRESSED) {
      if (freq > stepFreq)  freq -= stepFreq;          // не даем выйти за допустимый диаппазон
      SetPinFrequencySafe(GEN_PIN, freq / (OVERCLOCK / 16.0)); // задали частоту
      delay(KEY_DELAY); // задержка для кнопок
    }
    if (digitalRead(KEY_R) == KEY_PRESSED) {
      if ((freq + stepFreq) < 10000000) freq += stepFreq; // не даем выйти за допустимый диаппазон
      SetPinFrequencySafe(GEN_PIN, freq / (OVERCLOCK / 16.0)); // задали частоту
      delay(KEY_DELAY); // задержка для кнопок
    }
    display.setTextColor(BLACK); // если ШИМ не меняем, то выбираем обычный текст
#define XFREQ 14 // ======== // и, выделяем декаду частоты полосочками
    stepFreq = pow(10, (byte)menu); if (menu > 1) stepFreq++; // устраняем глючность pow, почему 10 в степени 2 = 99?
    byte menu_t = menu; if (menu > 2) menu_t++; if (menu == 6) menu_t++; // делаем табуляцию частоты
    menu_t = 54 - menu_t * 6; // считаем положение полосочек
    display.drawLine(menu_t, XFREQ - 2, menu_t + 4, XFREQ - 2, BLACK); // рисуем полоски
    display.drawLine(menu_t, XFREQ - 3, menu_t + 4, XFREQ - 3, BLACK);
    display.drawLine(menu_t, XFREQ + 8, menu_t + 4, XFREQ + 8, BLACK);
    display.drawLine(menu_t, XFREQ + 9, menu_t + 4, XFREQ + 9, BLACK);
  }
  // рисуем уровень ШИМ (инверсия текста задана ранее)
  display.setCursor(15, 40); // ставим курсор))
  display.print(" PWM=");  display.print(PWM);  display.print("% "); // выводим уровень ШИМ
  display.setTextColor(BLACK); // убираем инверсию при выводе частоты
  // рисуем частоту с табуляцией
  display.setCursor(6, XFREQ);
  for (unsigned long freq_t = 1000000; freq_t>0; freq_t/=10) {
  if (freq>=freq_t) {
    display.print((freq/freq_t)%10); if (freq_t==1000000 || freq_t==1000) display.print("'"); }  else { 
    display.print("_");              if (freq_t==1000000 || freq_t==1000) display.print(" "); }
  }
  display.print(" Hz");
  // отрисовка графика PWM
  for (char x = 17; x < 67; ) {
    if (PWM != 0)             display.drawLine(x, 26, x + PWM / 4, 26, BLACK); x += PWM / 4; // верх
    if (PWM != 0 && PWM != 100) display.drawLine(x, 26, x, 36, BLACK);                   // спад
    if (PWM != 100)           display.drawLine(x, 36, x + 25 - PWM / 4, 36, BLACK); x += 25 - PWM / 4; // низ
    if (PWM != 0 && PWM != 100 && x < 43) display.drawLine( x, 36, x, 26, BLACK);        // подъем
  }
  display.display(); // вываливаем буфер на дисплей
}

// === DDS генератор ===
void DdsGenerator() {
  static const byte ddsWave[][32] PROGMEM = {        
    1,6,15,29,48,69,92,117,143,168,191,212,229,243,251,255,254,248,237,222,203,181,156,131,106,81,59,39,22,10,3,1,   // sinNew    
    1,18,35,52,69,86,103,120,137,154,171,188,205,222,239,255,239,223,207,191,175,159,143,127,111,95,79,63,47,31,15,1, // treugNew    
    1,9,17,25,33,41,49,57,65,73,81,89,97,105,113,121,129,137,145,153,161,169,177,185,193,201,209,217,225,235,245,255,  // pilaNew
    250,246,238,230,222,214,206,198,190,182,174,166,158,150,142,134,126,118,110,102,94,86,78,70,62,54,41,33,25,17,9,1 };// pilaObrNew
  const char* const ddsStr[] PROGMEM = { "          ", "           ", "       ", "             "};
  byte ddsCount=0;
  // Рисуем DDS-генератор
  display.clearDisplay(); ShowModeInv(0); // режим работы, заголовок
  for (byte i = 0; i < 84;) display.drawLine(i, 36 - pgm_read_byte(&ddsWave[menu][i % 32]) / 10, i, 36 - pgm_read_byte(&ddsWave[menu][(i++) % 32]) / 10, BLACK);
  display.setCursor(3, 40);  display.print(ddsStr[menu]);  display.display(); // отрисовали все
  while (digitalRead(KEY_R)!=KEY_PRESSED && digitalRead(KEY_L)!=KEY_PRESSED && digitalRead(KEY_OK)!=KEY_PRESSED) { // выводим выбранный сигнал, пока не нажали кнопку
    pwmWrite(DDS_PIN, pgm_read_byte(&ddsWave[menu][(ddsCount++) & 0x1F]));
    //ResetAvrCheck();
  }
  if (++menu == 4) menu = 0; // нажали кнопку - переключаем режим
  delay(KEY_DELAY); // чтоб кнопки нормально нажимались
}

/*
  // UART приемник
  void Terminal() {
  const long speedUart[] = { 1200,2400,4800,9600,19200,38400,57600,115200 };
  display.clearDisplay();
  ShowModeInv(0);
  display.setCursor(15,15); display.println("Скорость:");
  display.setTextColor(WHITE, BLACK);
  display.setCursor(66, 25); display.println("+");
  display.setCursor(13, 25); display.print("-");
  display.setTextColor(BLACK);
  display.println(" ");
  display.print(speedUart[menu]);
  display.println(" ");
  display.setCursor(9,40); display.println("ОК - запуск");
  if(digitalRead(KEY_L) == KEY_PRESSED)  { menu--; if(menu==255) menu=7;   delay(KEY_DELAY);  }
  if(digitalRead(KEY_R) == KEY_PRESSED)  { menu++; if(menu==8)   menu=0;   delay(KEY_DELAY);  }
  if(digitalRead(KEY_OK) == KEY_PRESSED) {
    Serial.begin(speedUart[menu]*(16/OVERCLOCK));
    display.clearDisplay();
    display.print(">");
    delay(KEY_DELAY);
    display.display();
    int x=83;
    while (1) {
      //ResetAvrCheck();
      if (Serial.available()) { // Если в буфере есть данные
        if(++x==84) { // проверяем, не пора ли экран очистить
          x=0;
          display.clearDisplay();
        }
        display.print((char)(Serial.read())); // печать байта в дисплей
        display.display(); // выводим символ
      }
    }
  }
  display.display();
  }
*/
void SetTextColor(char c) {
  if (c) display.setTextColor(WHITE, BLACK); else display.setTextColor(BLACK);
}

void OscilSlow() {
  // надо найти главного тормазоглу!!!!!!!!!

  ADMUX = vRef ? 0b01100011 : 0b11100011; // установка опорного напряжения АЦП
  //ADCSRA = 0b11100110; // делитель частоты АЦП - 64
  //ADCSRA = 0b11100010; //delitel 4 // кондер не успевает разряжаться
  ADCSRA = 0b11100011; //delitel 8
  ADCSRA |= 0x10; // запуск преобразования АЦП

  // === Обработка кнопок ===
  if (digitalRead(KEY_L) == KEY_PRESSED) {
    switch (menu) { // тут идея такая - один раз опрашиваем кнопку, т.к. это медленно делается, потом через кейсы прыгаем.
      case 0 : vRef = !vRef;                      break;   // выбор опорного напряжения
      case 1 : razv--; if (razv == NAPR)  razv = 2;    break; // выбор развертки : 1000мс 500мс 100мс на клетку
      case 2 : grOffset -= 15; if (grOffset < 0)  grOffset = BUFSIZE_LONG; break; // режим паузы - прокручиваем осциллограмму
      case 3 : for (uint8_t x = 0; x < 26; x++) adcBuf[x] = 0; count = 0; grOffset = 0;
        display.clearDisplay(); display.setCursor(3, 20); display.print("RESET!");
        display.display(); delay(1000);    break;
    } delay(200);
  } // антидребезг кнопки
  if (digitalRead(KEY_R) == KEY_PRESSED) {
    switch (menu) {
      case 0 : vRef = !vRef;                      break;   // выбор опорного напряжения
      case 1 : razv++; if (razv == 3)    razv = 0;    break; // выбор развертки : 1000мс 500мс 100мс на клетку
      case 2 : grOffset += 15; if (grOffset > BUFSIZE_LONG) grOffset = 0;  break; // режим паузы - прокручиваем осциллограмму
    } delay(200);
  } // антидребезг кнопки
  if (digitalRead(KEY_OK) == KEY_PRESSED) {
    menu++; if (menu == 4) {
      menu = 0;  // перебор меню
      pause = 0;
    }
    if (menu == 2) {
      pause = 1;
      adcBuf[(count++) % BUFSIZE_SHORT] = NAPR; adcBuf[(count++) % BUFSIZE_SHORT] = 0; adcBuf[(count++) % BUFSIZE_SHORT] = NAPR; if (count > 79) grOffset = count - 80;
    } // отметка паузы на графике
    delay(200);
  } // антидребезг кнопки
  // === Отрисовка менюшки ===
  display.clearDisplay();  display.setCursor(0, 0);
  SetTextColor(menu == 0); if (vRef) display.print(VCC, 1); else display.print("1.1");
  SetTextColor(menu == 1); display.print(" ");   display.print(razv);
  SetTextColor(menu == 2); display.print(" S");  display.print(" ");
  display.print((grOffset % BUFSIZE_SHORT) / 2.02, 0);  display.print("%");
  SetTextColor(menu == 3); display.print(" R");
  display.setTextColor( BLACK);

  // == Отрисовка сетки == Новая //
  for (byte i = 47; i > 5; i = i - 7) {
    display.drawLine( 0, i, 2, i, BLACK);  // черточки слева
  }
  /* for(byte i=47;i>5;i=i-7) { display.drawPixel(17,i, BLACK); display.drawPixel(33,i, BLACK); display.drawPixel(49,i, BLACK); display.drawPixel(65,i, BLACK); display.drawPixel(81,i, BLACK); } // вертикальный пунктир
    for(byte i=1;i<84;i=i+8) { display.drawPixel(i,40, BLACK); display.drawPixel(i,26, BLACK); display.drawPixel(i,12, BLACK); }    // горизонтальный пунктир

    // === Отрисовка сетки Старая ===
    for(byte i=47;i>5;i=i-7) { display.drawLine( 0, i, 2, i, BLACK); } // черточки слева
    for(byte i=47;i>5;i=i-3) { display.drawPixel(21,i, BLACK);display.drawPixel(42,i, BLACK);display.drawPixel(63,i, BLACK); } // вертикальный пунктир
    for(byte i=3;i<84;i=i+3) { display.drawPixel(i,33, BLACK);display.drawPixel(i,19, BLACK);}                                 // горизонтальный пунктир
  */
  // === Отрисовка графика ===
  for (uint8_t x = 0; x < 80; x++) display.drawLine(x + 4, 47, x + 4, 47 - adcBuf[(x + grOffset) % BUFSIZE_SHORT] / 7, BLACK);
  if (pause) { // если пауза - рисуем шкалу прокрутки
    display.drawLine((grOffset % BUFSIZE_SHORT) / 2.3, 8, (grOffset % BUFSIZE_SHORT) / 2.3 + 5, 8, BLACK); // шкала прокрутки
    display.drawLine((grOffset % BUFSIZE_SHORT) / 2.3, 9, (grOffset % BUFSIZE_SHORT) / 2.3 + 5, 9, BLACK); // шкала прокрутки
  } else { // если не пауза, то снимаем показания
    while (!(ADCSRA & 0x10)); // ждем, пока АЦП считает
    adcBuf[count % BUFSIZE_SHORT] = ADCH; // сохраняем число из АЦП в массив
    if (count > 79) grOffset = count - 80;
    count++;
    if (count > BUFSIZE_LONG) count = BUFSIZE_SHORT; // зацикливаем счетчик
    //45мс - программа выполняется
    switch (razv) {
      case 0 : delay(55 * (OVERCLOCK / 16.0)); break; // секунда на клетку
      case 1 : delay(05 * (OVERCLOCK / 16.0)); break; // пол секунды на клетку
      case 2 : break; // без задержки.......
    }
  }
  display.display();
}

// === Чтение цифровых данных со всего порта C (A0...A5) === //
void ReadDig() {
  if (razv == 9) {
    for (int i = 0; i < BUFSIZE_LONG; i++) adcBuf[i] = PINC; // быстро читаем данные
  } else {
    for (int i = 0; i < BUFSIZE_LONG; i++) {
      adcBuf[i] = PINC;  // читаем данные
      delayMicroseconds((9 - razv) * 100);
    }
  }
}
/*
// ==== Логический анализатор ==== //
void LogAnalyzer() {
  // обрабатываем кнопки
  if (digitalRead(KEY_L) == KEY_PRESSED)   switch (menu) {
      case 0 : razv--; if (razv == NAPR) razv = 9; delay(400);                        break;
      case 1 : grOffset -= 20; if (grOffset < 0) grOffset = 0;                        break;
    }
  if (digitalRead(KEY_R) == KEY_PRESSED)  switch (menu) {
      case 0 : razv++; if (razv == 10) razv = 0; delay(400);                        break;
      case 1 : grOffset += PAUSE_OFFSET; if (grOffset > BUFSIZE_LONG - LCDX) grOffset = BUFSIZE_LONG - LCDX; break;   break;
    }
  if (digitalRead(KEY_OK) == KEY_PRESSED)  switch (++menu) {
      case 1: grOffset = 0; pause = 1; break; // вход в паузу
      case 2: menu = 0;     pause = 0; break; // перебор меню
    }

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

  display.clearDisplay(); // чистим дисплей
  display.print(razv);    // выводим развертку
  display.print(" ");
  if (pause) {
    display.print(" Пауза ");
    // рисуем прокрутку в режиме паузы
    display.drawLine(grOffset / 8, 8, grOffset / 8 + 6, 8, BLACK); // шкала прокрутки
    display.drawLine(grOffset / 8, 9, grOffset / 8 + 6, 9, BLACK); // шкала прокрутки
    delay(200);
  }
  for (byte i = 47; i > 12; i = i - 7) {
    display.drawLine( 0, i - 5, 2, i - 5, BLACK);  // черточки слева
    display.drawPixel(0, i, BLACK);
    display.drawPixel(2, i, BLACK);
  }
  // горизонтальный пунктир
  for (byte i = 5; i < 84; i = i + 3) {
    display.drawPixel(i, 47, BLACK);
    display.drawPixel(i, 40, BLACK);
    display.drawPixel(i, 33, BLACK);
    display.drawPixel(i, 26, BLACK);
    display.drawPixel(i, 19, BLACK);
  }
  for (int y = 0; y < 80; y++) {
    for (int x = 0; x < 5; x++) {
      display.drawLine(y + 4, 47 - (x * 7) - (bit_is_set(adcBuf[y + grOffset], 5 - x) ? 5 : 0), y + 4, 47 - (x * 7) - (bit_is_set(adcBuf[y + 1 + grOffset], 5 - x) ? 5 : 0), BLACK);
    }
  }
  display.display();
}
*/
//////////////////////////////Экран/////////////////////////////////
void Menu_ekrana() {
  Set = 0; delay(200);
  while (digitalRead(KEY_OK) != KEY_PRESSED) {
    display.clearDisplay();
    if (Set == 0) display.setTextColor(WHITE, BLACK); else display.setTextColor(BLACK);
    display.setCursor(0 , 0); display.println("Сontr:"); display.setTextColor(BLACK);
    display.setCursor(60 , 0); display.println(contrast);
    display.setCursor(0 , 10); display.println("Light:"); display.setTextColor(BLACK);
    if (BL == 1) { display.setCursor(60 , 10); display.println("ON");} else
                 { display.setCursor(60 , 10); display.println("OFF");} display.display();
    if (digitalRead(KEY_R) == KEY_PRESSED)  contrast++;
    if (digitalRead(KEY_L) == KEY_PRESSED)  contrast--;
    if (contrast < 30) contrast = 30;
    if (contrast > 80) contrast = 80;
    display.setContrast(contrast);
    printTemp();//       Терпература
    display.display();
    delay(150);
  }
  Set = 1; delay(200);
  while (digitalRead(KEY_OK) != KEY_PRESSED) {
    display.clearDisplay();
    display.setCursor(0 , 0); display.println("Сontr:");
    display.setCursor(60 , 0); display.println(contrast);
    if (Set == 1) display.setTextColor(WHITE, BLACK); else display.setTextColor(BLACK);
    display.setCursor(0 , 10); display.println("Light:"); display.setTextColor(BLACK);
    if (BL == 1) { display.setCursor(60 , 10); display.println("ON"); digitalWrite(Ekran, BL); } else
                 { display.setCursor(60 , 10); display.println("OFF"); digitalWrite(Ekran, BL);  }
    if (digitalRead(KEY_R) == KEY_PRESSED)  BL=1;
    if (digitalRead(KEY_L) == KEY_PRESSED)  BL=0;
    printTemp();//       Терпература
    display.display();
    delay(150);
  }
  display.display();
  display.clearDisplay();  // чистим буфер экранчика
  EEPROM.write(0, contrast);
  EEPROM.write(1, BL);
  void (* reboot)(void) = 0;
  reboot();
}
/////////////////////Коэффициент заполнения
uint32_t asm_func(uint8_t flag) {
  asm volatile (
    "cli"                    "\n\t"
    "mov r21,%0"    "\n\t" // флаг что считывать
    //сохранение конфигурация таймеров
    "in  r30,0x23"            "\n\t" // r30 <- GTCCR
    "ldi r18,0x83"            "\n\t" // GTCCR=0x83
    "out 0x23,r18"            "\n\t" // GTCCR=0x83
    "lds r26,0x80"            "\n\t" // r26 <- TCCR1A
    "sts 0x80,__zero_reg__"   "\n\t" // TCCR1A=0
    "lds r27,0x81"            "\n\t" // r27 <- TCCR1B
    "ldi r19,0x4"             "\n\t"
    "sts 0x81,r19"            "\n\t" // TCCR1B=1<<CS12
    "sts 0x85, __zero_reg__"  "\n\t" //TCNT1L=0
    "sts 0x84, __zero_reg__"  "\n\t" //TCNT1H=0
    "lds r28,0xB0"            "\n\t" // r28 <- TCCR2A
    "sts 0xB0,__zero_reg__"   "\n\t" // TCCR2A=0
    "lds r29,0xB1"            "\n\t" // r29 <- TCCR2B
    "ldi r20,0x1"             "\n\t"
    "sts 0xB1,r20"            "\n\t" // TCCR2B=1<<CS20
    "sts 0xB2,__zero_reg__"   "\n\t" // TCNT2=0
    "lds r31,0x70"            "\n\t" // r31 <- TIMSK2
    "sts 0x70,__zero_reg__"   "\n\t" // TIMSK2=0
    ////*******************************************
    // пройти если ноль
    "wait_begin:"             "\n\t"
    "sbic 0x09,5"             "\n\t" // Порт D5
    "rjmp wait_begin"         "\n\t"
    // пройти если единица -> первый импульс
    "wait_start:"             "\n\t"
    "sbis 0x9,5"              "\n\t"
    "rjmp wait_start"         "\n\t"
    "sts 0x43,__zero_reg__"   "\n\t" // GTCCR=0 старт синхра
    // импульс пошёл, ждём ноль
    "wait_pause:"             "\n\t"
    "sbic 0x09,5"             "\n\t"
    "rjmp wait_pause"         "\n\t"
    "cpi r21,__zero_reg__"    "\n\t" //
    "brne duty"  "\n\t"
    // импульс кончился, ждём  конца периода (единицу)
    "wait_end:"               "\n\t"
    "sbis 0x9,5"              "\n\t"
    "rjmp wait_end"           "\n\t"
    "duty:"                   "\n\t"
    ////*******************************************
    //восстановление таймеров
    "sts 0x80,r26"            "\n\t" // TCCR1B
    "sts 0x81,r27"            "\n\t" // TCCR1B
    "sts 0xB0,r28"            "\n\t" // TCCR2A
    "sts 0xB1,r29"            "\n\t" // TCCR2B
    "sts 0x70,r31"            "\n\t" // TIMSK2
    "out 0x23,r30"            "\n\t" // GTCCR  старт таймеры
    //вывод 3х байт
    "lds r22, 0xB2"           "\n\t" //  tctn2  0 байт
    "lds r23, 0x84"           "\n\t"  // TCNT1L 1 байт
    "lds r24, 0x85"           "\n\t"  // TCNT1H 2 байт
    "lds r25, __zero_reg__"   "\n\t" // пустой  3 байт
    "sei"                     "\n\t"
    ::"r" (flag): );
}
/////////////////////Коэффициент заполнения

 

Hy6yk
Offline
Зарегистрирован: 14.06.2020

В первую очередь, хотелось бы сказать большое спасибо автору этого проекта! И всем кто позднее присоединился к его развитию отдельное спасибо!!

Прочитал всю тему)) Читал как какой-то детектив)) было интересно чем же это всё закончится. Ну или может не закончится, а во что это всё выльется. Сам я совсем только начинающий "ардуинщик/электронщик" и поэтому многое было на "птичьем языке", но решимости заказать дисплей и собрать приборчик это не убавило. Дисплей правда пока всё не доедет... но всё остальное для сборки имеется. Сейчас ковыряюсь в коде, пытаюсь разобраться что к чему и подкорректировать под свои хотелки.

Хочется конечно персонально отметить людей, но боюсь кого-то перечислять, дабы никого не пропустить, потому что людей приложивших свою руку (скорее конечно голову) было много! Ещё раз, спасибо всем участникам!! Подобные простые проекты не отпугивают начинающего радиолюбителя обилием незнакомых понятий, а дают возможность приобщиться к этому замечательному делу и заглянуть немного глубже.

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

smokok  можно к скетчу(п.4416 - 5110) пояснения к меню осцилографа: как перейти к ручной развертке, к ручному изменению ОН, 

при длительном нажатии ОК перехода к ручной установке развертки (от progrik )я не увидел. (может не туда смотрел).

У меня есть дисплей от Нокии 3510(монохром) 96х65 как 1230, только там контроллер PCF8814 и он размером побольше,  помогите, пожалуйста адаптировать скетч (п.4416 - 1230) под этот дисплей, на него есть библиотека от Адафруит. 

В последствии хочу оставить осцилограф, терминал и ТТ тестер из (п.3853 от diksen) и энкодер, - не знаю влезет ли..., но это потом, - сначала нужно дисплей "прикрутить"...

uldin@mail.ru
Offline
Зарегистрирован: 15.02.2012

откомпилировал без ошибок "Скетч 5110", где бы ещё схемку найти)

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

void OscilSlow вылечили?

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

void OscilSlow вылечили?

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

Очередное обновление от progrik! Теперь кнопки работают на должном уровне!!! При одном нажатии переключение, чуть передержал - нет перескоков. Убран delay в меню пультоскопа который я добавил. 

Ребята, Я не спец, мне также помогают как вам по мере возможности опытные люди, за что им огромное СПАСИБО!!! progrik реально ускорил наш диафильм на экране до хорошего кино при спящей теме пультоскопа.  Эти обновления как свет в тёмном небе!

 

Схемка 5110 кто просил, ССЫЛКА

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

void OscilSlow пока не вылечил. Может поможет Electronik83 или сам по мере возможности, если не выйдет, то надежда умирает последней))).

1230

#define BUFSIZE_SHORT 256 //progrik: буфер для рабочего режима, чем короче - тем быстрее отображение. чем длиннее - тем медленнее, но синхра сможет ловить более "растянутый" сигнал
#define BUFSIZE_LONG  512 //progrik: буфер для паузы. старый буфер был 800
#define PAUSE_OFFSET 1    //progrik: смещение графика за один кадр в режиме паузы. если для паузы указан достаточно длинный буфер, то можно увеличить до 2-3-4.., чтоб быстрее мотало
#define APP 2.7           //         2.7) это переход на аппаратный замер
#define NAPR 255          //         это напряжение сигнала

// Пины экранчика
#define RES 3
#define CS 4
#define Data 6
#define Clock 7
#define swap(a, b) { byte t = a; a = b; b = t; }

//progrik: определяем макросы для действий с управляющими пинами
#define CLOCK_HIGH  bitWrite(PORTD,7,1) // pin7=PORTD,7   pin8=PORTB,0   pin9=PORTB,1  и так далее
#define CLOCK_LOW    bitWrite(PORTD,7,0)
#define DATA_HIGH   bitWrite(PORTD,6,1) // pin6=PORTD,6
#define DATA_LOW     bitWrite(PORTD,6,0)
#define RESET_HIGH  bitWrite(PORTD,3,1) // pin3
#define RESET_LOW    bitWrite(PORTD,3,0)
#define CS_HIGH     bitWrite(PORTD,4,1) // pin4
#define CS_LOW       bitWrite(PORTD,4,0)

#include <FreqCount.h> 
#include <math.h>
#include <PWM.h>

// Настройки пользователя - можно менять
#define OVERCLOCK 16  // частота на которой работает Ардуино
float VCC = 5.0;      // напряжение питания
#define KEY_L  11 // кнопка ЛЕВО  (можно любой пин)
#define KEY_OK 12 // кнопка ОК    (можно любой пин)
#define KEY_R  13 // кнопка ПРАВО (можно любой пин)
#define VBAT   A1 // любой свободный аналоговый пин для измерения напряжения АКБ 
#define TempC  A0 // любой свободный аналоговый пин для измерения температуры
#define KEY_DELAY 200
#define KEY_PRESSED 0 //progrik: логическое состояние нажатой кнопки 0 или 1
#define LCDX 96

#include <avr/pgmspace.h>
const char string_0[] PROGMEM = "ОСЦИЛЛОГРАФ";   // "String 0" и т.д. - это содержимое строк; если необходимо меняйте его
const char string_1[] PROGMEM = " ";
const char string_2[] PROGMEM = "VБат.= ";
const char string_3[] PROGMEM = "Temп.= ";
const char string_4[] PROGMEM = "Pause";

// Теперь создаем таблицу с отсылками к этим строкам:
const char* const string_table[] PROGMEM = {string_0, string_1, string_2, string_3, string_4};
char buffer[11];    // массив должен быть достаточно велик, чтобы вместить даже самую большую строку

byte mode = 0; // пункт главного меню
byte menu = 0; // пункт меню
byte adcBuf[BUFSIZE_LONG];
byte vSync = 30; // уровень синхронизации
bool vRef = 1; // флаг опорного напряжения
bool pause = 0; // флаг режима паузы
bool avtorazv=1;      // Автоматический выбор развертки
byte TextColor = 1;
byte razv = 0;
byte trig = 0;
int kdel=5;       //bulat  автопредел 1,1/0,22 вольта
int osc=15;          // авто вкл. режима осциллографа (секунды)
int grOffset = 0; // смещение графика в рабочем режиме
byte vMax, vMin; // максимальное и минимальное напряжение сигнала
int temp = 0; // временная переменная***
bool flag = 0;
unsigned long ok_last_time; //была ok_press_time
unsigned long ok_start_time;
bool ok_short_press = false;
unsigned long count = 0;
byte LCD_RAM[96 * 9]; // 96 * 9

static const char font[] PROGMEM = {
                                  //HEX B DEC переводим
  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   я
};
//********************************************************
// ==== Считывание напряжения питания ардуинки (Vcc) ====
#define Vref11 1.095 // для точной подстройки результата измерений
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));        // результат преобразования в вольтах
}
/////// ==== Температура ==== ////////
double Thermister(int RawADC) {
  double Temp;
  Temp = log(((10240000/RawADC) - 10000)); // 10000=10кОм Для подстройки правим.
  Temp = 1 / (0.001129148 + (0.000234125 * Temp) + (0.0000000876741 * Temp * Temp * Temp));
  Temp = Temp - 273.15;   // Kelvin to Celcius
  return Temp;
}
void setup()  {
  pinMode(KEY_OK, INPUT); pinMode(KEY_L, INPUT); pinMode(KEY_R, INPUT); // настраиваем кнопки на вход
//  digitalWrite(KEY_OK, HIGH); digitalWrite(KEY_L, HIGH); digitalWrite(KEY_R, HIGH); // подтяжка к плюсу питания
  VCC = ReadVcc();
  Initialize();  //Инициализация дисплея
  Clear_LCD();
  for (byte i = 0; i < 3; i++)
  {
    strcpy_P(buffer, (char*)pgm_read_word(&(string_table[i]))); // необходимые функции и расшифровки, просто скопируйте
    print(10, i * 10, 1, (buffer));
    //delay( 500 );
  }
  Update();
//  delay(KEY_DELAY);
  while (digitalRead(KEY_OK) != KEY_PRESSED) { // цикл, пока не нажали кнопку ОК
  Update();
  // Если не нажата 10 сек кнопка ок вход в режим осцилоографа автоматом
  osc--;
    if (osc==0) break;
  //////////  Выводим показание /////////
    float_print(60, 20, 1, analogRead(VBAT)*VCC/1024,1);  //Вольтаж батареи
    float_print(55, 40, 1, analogRead(TempC), 1);  // выводим на экран показание Температуры
  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[3]))); // пишем слово "Температура"
  print(10, 40, 1, (buffer));
  delay( 300 );
} // цикл нажатия ОК
  // нажали кнопку ОК из меню, инициализируем и выходим из меню
  count = 0; //countX = 0; // восстанавливаем на всякий случай переменные
  if (mode == 0) FreqCount.begin(1000);
  if (mode == 1) {
    InitTimersSafe();
  }
//  if (mode == 2) {
//    InitTimersSafe();
//  }
  if (mode == 3 || mode == 4) DDRC = 0x00; PORTC = 0x00;// Serial.begin(115200); // razv = 0;// весь порт С (это A0...A5) на вход и без подтяжки
  for (char a=37; a>0; a-=2);
  ok_last_time = millis(); //progrik: запоминаем время последнего нажатия
}
// беcконечный цикл - по сути прыгаем в подпрограммы
void loop() {
  switch (mode) {
    // Прыгаем в выбранный режим из главного меню
    case 0 : Oscil();        break; // "выпадаем" в осцилл
  }
}
//  === Читаем с АЦП данные и помещаем их в буфер === // 
void ReadAdc() {
uint16_t len;
len = (pause) ? BUFSIZE_LONG : BUFSIZE_SHORT;
  if (razv % 10) { // (razv>0)        // если развертка без задержек всяких (с 1 по 7)
    ADCSRA = 0b11100000 | (8 - (razv % 10)); // установили делитель (/2 - не работает, так что начинаем с /4)
    for (uint16_t i = 0; i < len; i++) { // цикл для чтения
      while (!(ADCSRA & 0x10));     // ждем готовность АЦП
      ADCSRA |= 0x10;               // запускаем следующее преобразование
      adcBuf[i] = ADCH;             // записываем данные в массив
    }
   // delay(0.3 * BUFSIZE_LONG);  //progrik: не нужно тормозить то, что и так еле работает))  // компенсация задержки по сравнению с разверткой 0...
  } else {                          // развертка с задержками (delay)
    ADCSRA = 0b11100111;            // делитель на /128
    for (uint16_t i = 0; i < len; i++) { // цикл для чтения
      while (!(ADCSRA & 0x10));     // ждем готовность АЦП
      ADCSRA |= 0x10;               // запускаем следующее преобразование
      delayMicroseconds(500);       // делаем задержку
      adcBuf[i] = ADCH;             // записываем данные в массив
    }
  }
}

// ==== функция вывода 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));
}



/*int GetCountsOfDigits(int n) //найти количество цифр в числе <a href="<a href="<a href="<a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a>" rel="nofollow"><a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a></a>" rel="nofollow"><a href="<a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a>" rel="nofollow"><a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a></a></a>" rel="nofollow"><a href="<a href="<a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a>" rel="nofollow"><a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a></a>" rel="nofollow"><a href="<a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a>" rel="nofollow"><a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a></a></a></a>
{
  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;
}*/

// ==== найти количество цифр в числе   <a href="<a href="<a href="<a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a>" rel="nofollow"><a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a></a>" rel="nofollow"><a href="<a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a>" rel="nofollow"><a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a></a></a>" rel="nofollow"><a href="<a href="<a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a>" rel="nofollow"><a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a></a>" rel="nofollow"><a href="<a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a>" rel="nofollow"><a href="<a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a>" rel="nofollow"><a href="https://habr.com/ru/post/269237/" rel="nofollow">https://habr.com/ru/post/269237/</a></a></a></a></a>
/*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 Initialize() {
  pinMode(RES,   OUTPUT);
  pinMode(CS,    OUTPUT);
  pinMode(Data,  OUTPUT);
  pinMode(Clock, OUTPUT);
  // Инициализация дисплея
  //  dWrite(RES, 1);
  RESET_HIGH;
//  dWrite(Clock, 0);
  CLOCK_LOW;
//  dWrite(Data, 0);
  DATA_LOW;
//  dWrite(CS, 0);
//  CS_LOW;
  delay(20);
//  dWrite(CS, 1);
  CS_HIGH;
  SendByte(0, 0x2F);           // Power control set(charge pump on/off)
  SendByte(0, 0xA4);
  SendByte(0, 0xAF);           // экран вкл/выкл
  //Clear_LCD();
  //Update();
//  delay(500); //progrik: задержка после включения
//  CS_LOW; //progrik: вернул для тестов
//  delay(500); //progrik: задержка после включения
}

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

//=========================================================Отправка 9 байт
//progrik: добалено определение inline. функция встраивается в код и не расходуется время на ее вызов.
//progrik: вместо многократных вызовов функций дергаем ногами при помощи написанных вверху кода макросов
inline __attribute__((always_inline)) void SendByte(byte mode, byte c) {
  //dWrite(CS, 0); //progrik: чип селект нужно включить один раз в конце инициализации дисплея, и больше не трогать. перенес в инициализацию
  CS_LOW; //progrik: вернул для тестов
  //(mode) ? dWrite(Data, 1) : dWrite(Data, 0);
  (mode) ? DATA_HIGH : DATA_LOW;
  //dWrite(Clock, 1);
  CLOCK_HIGH;
  for (byte i = 0; i < 8; i++)
  {
    //dWrite(Clock, 0);
    CLOCK_LOW;
    //(c & 0x80) ? dWrite(Data, 1) : dWrite(Data, 0);
    (c & 0x80) ? DATA_HIGH : DATA_LOW;
    //dWrite(Clock, 1);
    CLOCK_HIGH;
    c <<= 1;
  }
  //dWrite(Clock, 0);
  CLOCK_LOW;
}

//======================================================Очистка дисплея
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]);
    }
  }
}

//===================================================Нарисовать пиксель
inline __attribute__((always_inline)) void drawPixel (byte x, byte y, boolean color) {
//  if ((x < 0) || (x >= 96) || (y < 0) || (y >= 68)) return; //  NAN херня
  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);
if(line & 0x1)
{
  drawPixel(x + i, y + j, color);
}
else
{
  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++) {
if(steep)
{
  drawPixel(y0, x0, color);
}
else
{
  drawPixel(x0, y0, color);
}
    err -= dy;
    if (err < 0) {
      y0 += ystep;
      err += dx;
    }
  }
}
/////////////////добавь где-то, например, под функцией DrawLine такую функцию:////////////////////////////
void drawVLineUltraFast(byte x, byte y0, byte y1, boolean color) {
  if(y0 > y1)
  {
    swap(y0, y1);
  }
  do
  {
    drawPixel(x, y0, color);
  }
  while(++y0 <= y1);
}
//========================================Рисование вертикальной линии
void drawFastVLine(byte x, byte y, byte h, boolean color) {
  //drawLine(x, y, x, y + h - 1, color);
  byte y1 = y + h - 1;
  do
  {
    drawPixel(x, y, color);
  }
  while(++y <= y1);
}

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

//=====================================Рисование залитый прямоугольник
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);
}
//progrik: для расчета напряжений ---------------
void calc_val()
{
  vMax = 0; vMin = 0xFF;
  uint16_t l = (pause) ? BUFSIZE_LONG : BUFSIZE_SHORT;
  for (uint16_t x = 0; x < l; x++) {
    if (vMax < adcBuf[x]) vMax = adcBuf[x];  // пока 255, но надо экспериментировать
    if (vMin > adcBuf[x]) vMin = adcBuf[x];
  }
  }
// === Осциллоскоп === //
void Oscil() {
  // установка опорного напряжения АЦП и настройка входа АЦП
  ADMUX = vRef ? 0b01100011 : 0b11100011;
  label_ReadAdc:
  // === Обработка кнопок === //
  if(digitalRead(KEY_L) == KEY_PRESSED) // кнопка лево
  {
    switch (menu)
    {              //progrik: подавление дребезга и предотвращение постоянного циклического переключения
      case 0 : if((millis() - ok_last_time) > 100) { razv--; if(razv == NAPR) razv = 6; }     break; // меняем развертку
      case 1 : grOffset -= PAUSE_OFFSET; if (grOffset < 0) grOffset = 0;                      break; // листаем график в паузе
    }
  ok_last_time = millis(); //progrik: запоминаем время последнего нажатия кнопки
  }

  if(digitalRead(KEY_R) == KEY_PRESSED) // кнопка направо
  {
    switch (menu)
    {            //progrik: подавление дребезга и предотвращение постоянного циклического переключения
    case 0 : if((millis() - ok_last_time) > 100) { razv++; if (razv == 7) razv = 0; }                      break; // меняем развертку
    case 1 : grOffset += PAUSE_OFFSET; if (grOffset > BUFSIZE_LONG - LCDX) grOffset = BUFSIZE_LONG - LCDX; break; // листаем график в паузе
    }
  ok_last_time = millis(); //progrik: запоминаем время последнего нажатия кнопки
  }
  if (digitalRead(KEY_OK) == KEY_PRESSED)
   {
     if(ok_short_press && (millis() - ok_start_time) > 600) //progrik: задержка для длинного нажатия
     {
       avtorazv = !avtorazv;   //progrik: переключит авторазвертку после длительного нажатия ^^^ 600ms
       ok_short_press = false;
     }
     if((millis() - ok_last_time) > 200) //progrik: подавление дребезга - ждем 200 мс со времени последнего нажатия
     {
       ok_start_time = millis(); //progrik: запоминаем время первого нажатия кнопки OK
       ok_short_press = true;   //progrik: отмечаем флаг короткого нажатия
     }
     ok_last_time = millis(); //progrik: запоминаем время последнего нажатия кнопки OK
   }
   else
   {
     if(ok_short_press) //progrik: если нужно отработать короткое нажатие (флаг установлен)
     {
       ok_short_press = false; //progrik: снимаем флаг короткого нажатия
       switch (++menu)
       {
         case 1: grOffset = 0; pause = 1; ReadAdc(); calc_val(); break; 
// вход в паузу - антидребезг типа
         case 2: menu = 0;  pause = 0; break; // перебор меню
       }
     }
   }
  // === Ведём рассчеты === //
  if (!pause) { // если нет паузы 
    ReadAdc();  // то снимаем осциллограмму
    calc_val();
    // == Вычисляем максимальное и минимальное значение сигнала == //
    vSync = (vMax - vMin) / 2 + vMin; // уровень синхронизации по середине уровня сигнала
    // == Определение точки синхронизации == //
    bool flagZero=0; grOffset = 0; // сброс флага и точки синхронизации    
    // Ищем перепад от меньшего уровня к большему
    for (uint8_t x=1; x < BUFSIZE_SHORT - LCDX; x++) { // смотрим весь массив данных АЦП
      if (adcBuf[x]<vSync) flagZero=1;   // нашли меньше, чем синхра (перепад сигнала в минус) - ставим флаг
      if (flagZero && adcBuf[x]>vSync) { grOffset=x; break; } // нашли больше, чем синхра (перепад сигнала в плюс) - запомнили и выходим из цикла
    }
    // === Считаем частоту сигнала === //
    if (vRef && vMax * VCC / NAPR > APP) { // если можем замерить аппаратно - меряем. > 2.7) это переход на аппаратный замер
      if (FreqCount.available()) count = FreqCount.read(); // вывод частоты по готовности счетчика частоты сигнала
    } else { // === Меряем частоту сигнала программно === //
      flagZero = 0; count = 0; // сброс флага и счетчика
      for(uint8_t x=grOffset; x < BUFSIZE_SHORT - LCDX; x++)  { // смотрим массив от точки синхронизации до конца
        if (adcBuf[x] < vSync) flagZero = 1; // нашли меньше, чем синхра (перепад сигнала в минус) - выставляем флаг
        if (flagZero && adcBuf[x] > vSync) { // нашли больше, чем синхра (перепад сигнала в плюс) - отловили полный период
          switch (razv) { // считем частоту периода
            case 6: count = 1000000 / ((x - grOffset - 1) * 3.25);   break; // делитель 4
            case 5: count = 1000000 / ((x - grOffset)  * 3.25) / 2;  break; // делитель 8
            case 4: count = 1000000 / ((x - grOffset)  * 3.25) / 4;  break; // делитель 16
            case 3: count = 1000000 / ((x - grOffset)  * 3.25) / 8;  break; // делитель 32
            case 2: count = 1000000 / ((x - grOffset)  * 3.25) / 16; break; // делитель 64
            case 1: count = 1000000 / ((x - grOffset)  * 3.25) / 32; break; // делитель 128
            case 0: count = 1000000 / ((x - grOffset)  * 510);       break; // делитель 128 тоже
          }
          break;
        }
      }
    }
    count = count * OVERCLOCK / 16.0; // пересчет частоты на разные кварцы
  } // закончили вести рассчеты
  Clear_LCD(); // чистим экран...
  // === Отрисовка меню осцилла ===
  if (vRef) float_print(0, 0, 1, VCC, 1); else {
  if(kdel==5)print(0, 0, 1, "1.1"); else print(0, 0, 1, "*22");  // отображение опорного
  }
  if (menu == 0) TextColor = 0; else TextColor = 1; print(27, 0,  1, razv);  // отображение развёртки
  if (vRef && vMax * VCC / NAPR > APP)  drawChar(19, 0, 1, 'а'); // если замер  - "А"ппаратный
  else  drawChar(19, 0, 1, 'п'); // иначе "П"рограмный
  if (menu == 1) { // тут всё для паузы
//    delay(20);
  } else TextColor = 1;
  if (!pause) TextColor = 0;
  else { // рисуем прокрутку в режиме паузы
    strcpy_P(buffer, (char*)pgm_read_word(&(string_table[4])));
    print(6, 12, 1, (buffer));
    temp = grOffset / 4.7;  // место остоновки чёрточки при прокрутке в паузе в конце экрана
    drawFastHLine(temp, 10, 7, 1);  drawFastHLine(temp, 9, 7, 1);  //чёрточка прокрутки в паузе
  } // шкала прокрутки закончилась
  (pause) ? float_print (48, 0, 0, count / 1000000.0, 6) : float_print (48, 0, 1, count / 1000000.0, 6); // вывод частоты
  float_print(73, 12, 1, vMax * (vRef ? VCC : 1.1) / NAPR, 2); // рисуем максимальное напряжение сигнала
  float_print(78, 58, 1, vMin * (vRef ? VCC : 1.1) / NAPR, 1); // рисуем минимальное  напряжение сигнала
  if (vMax==0xFF) for(uint8_t x=10; x<68; x++) drawFastHLine( 0, x, 5, 5); // превышение максимума АЦП
  if (avtorazv) drawChar(34, 0, 1, 'а');   // указатель состояния авторазвёртки
  else  drawChar(34, 0, 1, 'p');
  // == Отрисовка сетки == //
  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, 19, 1);  // это сеточка # горизонтальные линии
    drawPixel(i, 39, 1);
    drawPixel(i, 59, 1);
  }
             ///////////////авто razv//////////////
  if (avtorazv)
  #define PER 1.3
  if (count > 4323.3 * PER) razv = 6;
  else if (count > 2434.5 * PER) razv = 5;
  else if (count > 0969.3 * PER) razv = 4;
  else if (count > 0486.8 * PER) razv = 3;
  else if (count > 0245.8 * PER) razv = 2;
  else if (count > 0120.1 * PER) razv = 1;
  else razv = 0;
      //bulat если зашкаливает включаем предел 5 в  
  if (vMax==NAPR){
    if (vRef==0)
       {
        vRef=1;
       ADMUX = 0b01100011;// выбор внутреннего опорного 5.0 В
           goto   label_ReadAdc;
     }
  }
//bulat если  5 в  и уровень менее 1,1 в то вкл предел 1,1 в
  if (vMax<=55){
    if (vRef==1)
       {
        vRef=0;
        ADMUX = 0b11100011;// выбор внутреннего опорного 1,1В
        goto   label_ReadAdc;
     }
  }
  //bulat здесь автопредел 1,1/0,22 в,программный
  kdel=5;
  if (vMax<=55){
    if (vRef==0)
       {
       kdel=1;
     }
  }
  // == Отрисовка графика == //
  for (uint8_t x = 0; x < 92; x++)
    drawVLineUltraFast(x + 4, 67 - adcBuf[x + grOffset]/kdel / 0.9, 67 - adcBuf[x + grOffset + 1]/kdel / 0.9, 1); //progrik: drawVLineUltraFast вместо медленной drawLine
  Update();
}

5110

//Страница проэкта  <a href="<a href="<a href="http://srukami.inf.ua/pultoscop_v25110.html" rel="nofollow">http://srukami.inf.ua/pultoscop_v25110.html</a>" rel="nofollow"><a href="http://srukami.inf.ua/pultoscop_v25110.html" rel="nofollow">http://srukami.inf.ua/pultoscop_v25110.html</a></a>" title="<a href="<a href="http://srukami.inf.ua/pultoscop_v25110.html" rel="nofollow">http://srukami.inf.ua/pultoscop_v25110.html</a>" rel="nofollow"><a href="http://srukami.inf.ua/pultoscop_v25110.html" rel="nofollow">http://srukami.inf.ua/pultoscop_v25110.html</a></a>" rel="nofollow"><a href="<a href="http://srukami.inf.ua/pultoscop_v25110.html" rel="nofollow">http://srukami.inf.ua/pultoscop_v25110.html</a>" rel="nofollow"><a href="http://srukami.inf.ua/pultoscop_v25110.html" rel="nofollow">http://srukami.inf.ua/pultoscop_v25110.html</a></a></a>
// === От меня [Electronik83] === mailTo: <a href="mailto:gsmkillersevsk@mail.ru">gsmkillersevsk@mail.ru</a>
// 1. Оптимизировал, причесал код
// 2. Сделал красивей главное меню, добавил новый пункт
// 3. Отображение частатоы и напряжения в осцилле при 1Кгц и 2,5в (был баг автора)
// 4. Новый режим - медленный осцилл - доделал...
// 5. Оптимизация.....
// 6. Убрал програмную растяжку сигнала из осцилла (развертка 7 и 8)
// 7. Изменил отрисовку сигнала в осцилле (больше нету этих раздвоений сигнала)
// 8. Убрал нахрен кнопку питания, и все что с ней сязано - проще тумблер (переключатель) поставить...
// 9. Анимировал главное меню. Пофиксил мерцание.
//10. Вставил свое меню генератора, DDS-генератора, терминала.
//11. Добавил немного новой анимации.
//12. Провел эксперимент с DDS-генератором и регулировкой частоты - не сделать.
// === ver19 ==
//13. В осциле - Сделал автосинхронизацию по серединке амплитуды сигнала <= подумать над внешней синхрой !!!
//14. В осциле - показываем масимальное и минимальное значение амплитуды сигнала справа
//15. Оптимизация некоторых участков кода
//16. Атмега теперь сама меряет напряжение своего питания, не надо ничего мультиметром мерять
//17. В осцилле - сделал более удобный вывод верхней строки в осцилле, "устатичил" вывод частоты.
//      - теперь нет этого дерганья из Гц в кГц (и обратно) - просто выводим в Мгц.
//18. Добавил цифровой анализатор (входы A1, A2, A3, A4, A5) - что то мне он не понравился (и мне тоже).
/////////////// smokok ///////////////  
//19. Добавил Авто-опорное 5.0,1.1,0.2 
//20. Добавил Меню экрана (регулировка контрастности и включение подсвеки экрана).
//21. Добавил температуру. Пин термистора (10к) в А0 и +5, доп резистор (10к) в А0 и -5
//22. Разогнал PULTOSCOPE благодаря progrik
//23. Пока только вырезал  UART приемник
//24. И ещё потом вырезал цифровой анализатор (может зря?)))).
//25. Благодаря progrik добавил отключение АвтоРазвёртки на длительное нажатие "ОК"
//26. progrik зделал кнопки лучше, с изменением полярности
//    Немножко сломал void OscilSlow
//    В планах внедрить Коэффициент заполнения (а за чем??)
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
#include <FreqCount.h>
#include <EEPROM.h>
#include <math.h>
#include <PWM.h>

// Настройки пользователя - можно менять
#define Ekran     8  //Экран///Pin///Подсветka
#define OVERCLOCK 16  // частота на которой работает Ардуино
float VCC = 5.0;      // напряжение питания (меряем мультиметром - СТАЛО НЕ НАДО)
Adafruit_PCD8544 display = Adafruit_PCD8544(7, 6, 4, 3, 2); // пины к которым у вас подключен дисплей
byte contrast; // контрастность дисплея
byte Set = 0; // Настройка экрана
bool BL; // Подсветка

#define KEY_L  11 // кнопка ЛЕВО  (можно любой пин)
#define KEY_OK 12 // кнопка ОК    (можно любой пин)
#define KEY_R  13 // кнопка ПРАВО (можно любой пин)
#define VBAT   A5 // любой свободный аналоговый пин для измерения напряжения АКБ 
#define KEY_DELAY 200
#define KEY_PRESSED 0 //progrik: логическое состояние нажатой кнопки 0 или 1

// Переменные, дефайны и прочяя хрень - менять со смыслом
#define GEN_PIN  9  // пин для генератора сигналов (не менять)
#define DDS_PIN  10 // пин для генератора dds (не менять)
byte mode = 0; // пункт главного меню
byte menu = 0; // пункт меню

#define BUFSIZE_SHORT 256 //progrik: буфер для рабочего режима, чем короче - тем быстрее отображение. чем длиннее - тем медленнее, но синхра сможет ловить более "растянутый" сигнал
#define BUFSIZE_LONG  512 //progrik: буфер для паузы. старый буфер был 800
#define PAUSE_OFFSET 2    //progrik: смещение графика за один кадр в режиме паузы. если для паузы указан достаточно длинный буфер, то можно увеличить до 2-3-4.., чтоб быстрее мотало
#define APP 2.7           //         2.7) это переход на аппаратный замер
#define NAPR 255          //         это напряжение сигнала
#define LCDX 80

byte adcBuf[BUFSIZE_LONG];
byte vSync = 30;   // уровень синхронизации
bool vRef = 1; // флаг опорного напряжения
bool pause = 0; // флаг режима паузы
byte razv = 4;
int grOffset = 0; // смещение графика в рабочем режиме
byte vMax, vMin;// максимальное и минимальное напряжение сигнала
int kdel = 5;         //bulat  автопредел 1,1/0,2 вольта
bool avtorazv = 1;    // Автоматический выбор развертки
bool flag = 0;
bool kzap = 0; //флаг коэффициента заполнения
unsigned long ok_last_time; //была ok_press_time
unsigned long ok_start_time;
bool ok_short_press = false;

// Переменные для генератора
int PWM = 50;//стартовое значение ШИМ от 0 до 100 для генератора
unsigned long freq = 500; // стартовое значение частоты в Гц для генератора
unsigned long stepFreq = 0;

//int d=0; // можно подменить чем нибудь. count например....
unsigned long count = 0;
//long countX=0;
//uint32_t tic1;
//uint32_t tic2;
//int duty;
//long speedTTL=9600; //скорость терминала

void ShowMode(char y) { // выводим режим работы. y - строка, где рисовать
const char* const modeStr[] PROGMEM= { " Oscilloscop ",    "     PWM     ", "     DDS     ", "     АЧХ     ", "    ЭKPAH    "};
  display.setCursor(3, y);      // задаем позицию на экране
  display.print(modeStr[mode]); // выводим режим работы
}
void ShowModeInv(char y) { // выводим режим работы инверсно. y - строка, где рисовать
  display.drawLine(0, y, 83, y, BLACK);
  display.setTextColor(WHITE, BLACK);
  display.setCursor(0, y+1); display.println(" "); display.setCursor(78, y+1); display.println(" ");  // для красоты только
  ShowMode(y+1);  display.setTextColor(BLACK);
}
double Thermister(int RawADC) {
  double Temp;
  Temp = log(((10240000 / RawADC) - 10000)); // 10000=10кОм Для подстройки правим.
  Temp = 1 / (0.001129148 + (0.000234125 * Temp) + (0.0000000876741 * Temp * Temp * Temp));
  Temp = Temp - 273.15;   // Kelvin to Celcius
  return Temp;
}
void printTemp(void) {
  display.setTextColor(BLACK);
  display.setCursor(15, 37);
  display.print("Temp ");
  display.print(float(Thermister(analogRead(0))), 1); // (0) это аналог выход А0
}
// ==== Считывание напряжения питания ардуинки (Vcc) ====
#define Vref11 1.095 // для точной подстройки результата измерений
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));        // результат преобразования в вольтах
}

void setup()  {

  ////////////////////////////Экран//////////////////////////////
  //  Примечание. Экран становится темным,  решение проблемы.///////////////////////////////////////
  //  EEPROM.write(0, 50);  //  Записать закрытую строку,  Вгрузить,  и начать использовать.
  contrast = EEPROM.read(0);
  BL       = EEPROM.read(1);
  pinMode(Ekran, OUTPUT);
  digitalWrite(Ekran, BL);
  //////////////////////////////////////////////////////
  pinMode(KEY_OK, INPUT); pinMode(KEY_L, INPUT); pinMode(KEY_R, INPUT); // настраиваем кнопки на вход
  display.begin();
  display.setContrast(contrast);
  VCC = ReadVcc();
  while (digitalRead(KEY_OK) != KEY_PRESSED) { // цикл, пока не нажали кнопку ОК
    // опрос кнопок
    if (digitalRead(KEY_R) == KEY_PRESSED) {
      mode++;
      if (mode >4  ) mode = 0;
    }
    if (digitalRead(KEY_L) == KEY_PRESSED) {
      mode--;
      if (mode == NAPR) mode = 4;
    }
    display.clearDisplay();  // чистим буфер экранчика
    display.setCursor(24, 19);  // выводим напряжение батареи
    display.setTextColor(WHITE, BLACK);
    display.print(" ");
    display.print(analogRead(VBAT)*VCC / 1024, 1);
    display.print(" V");

    // динамическая графика в скролле
#define COUNT 84 // ширина дисплея, в пикселях
    for (char a = 3; a < 14; a += 2) {
      display.drawLine((count + a   ) % COUNT, a + 4, (count + a   ) % COUNT, 40 - a, BLACK); // левые  полосочки по бокам
      display.drawLine((count - a + 83) % COUNT, a + 4, (count - a + 83) % COUNT, 40 - a, BLACK); // правые полосочки по бокам
    }
    if (count % 84 == 0) { // если больше не надо скроллить
      ShowModeInv(0);
      display.drawRect(21, 18, 42, 11, BLACK); // рамка вокруг картинки
      display.drawRect(19, 16, 46, 15, BLACK); // рамка вокруг картинки
      //        display.drawRect(16, 14, 52, 19, BLACK); // рамка вокруг картинки
    }
    printTemp(); //       Терпература
    display.display();
    delay(150); // задержка отображения уровня заряда, температуры и кнопок заодно
    //    display.display();
  } // цикл нажатия ОК
  // нажали кнопку ОК из меню, инициализируем и выходим из меню

  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) razv = 0;
  if (mode == 5) {
    DDRC = 0x00;  // весь порт С (это A0...A5) на вход и без подтяжки
    PORTC = 0x00;
  }
  // антидребезг кнопки ОК
  for (char a = 37; a > 0; a -= 2) {
    display.clearDisplay();  // - это вместо delay(200);
    ShowModeInv(a);
    display.display();
  }
  ok_last_time = millis(); //progrik: запоминаем время последнего нажатия
}
// безконечный цикл - по сути прыгаем в подпрограммы
void loop() {
  switch (mode) { // Прыгаем в выбранный режим из главного меню
    case 0 : Oscil();        break; // "выпадаем" в осцилл
    case 1 : Generator();    break; // "выпадаем" в генератор
    case 2 : DdsGenerator(); break; // "выпадаем" в DDS генератор
    //    case 3 : Terminal();     break; // "выпадаем" в USART приемник
    case 3 : OscilSlow();    break; // "выпадаем" в медленный осцилл
//    case 4 : LogAnalyzer();  break; // "выпадаем" в анализатор
    case 4 : Menu_ekrana();  break; // "выпадаем" в меню экрана
  }
}
//  === Читаем с АЦП данные и помещаем их в буфер === //
void ReadAdc() {
  uint16_t len;
  len = (pause) ? BUFSIZE_LONG : BUFSIZE_SHORT;
  if (razv % 10) { // (razv>0)        // если развертка без задержек всяких (с 1 по 7)
    ADCSRA = 0b11100000 | (8 - (razv % 10)); // установили делитель (/2 - не работает, так что начинаем с /4)
    for (uint16_t i = 0; i < len; i++) { // цикл для чтения
      while (!(ADCSRA & 0x10));     // ждем готовность АЦП
      ADCSRA |= 0x10;               // запускаем следующее преобразование
      adcBuf[i] = ADCH;             // записываем данные в массив
    }
    //    delay(0.3 * BUFSIZE);           // компенсация задержки по сравнению с разверткой 0...
  } else {                          // развертка с задержками (delay)
    ADCSRA = 0b11100111;            // делитель на /128
    for (uint16_t i = 0; i < len; i++) { // цикл для чтения
      while (!(ADCSRA & 0x10));     // ждем готовность АЦП
      ADCSRA |= 0x10;               // запускаем следующее преобразование
      delayMicroseconds(500);       // делаем задержку
      adcBuf[i] = ADCH;             // записываем данные в массив
    }
  }
}

//progrik: для расчета напряжений ---------------
void calc_val()
{
  vMax = 0; vMin = 0xFF;
  uint16_t l = (pause) ? BUFSIZE_LONG : BUFSIZE_SHORT;
  for (uint16_t x = 0; x < l; x++) {
    if (vMax < adcBuf[x]) vMax = adcBuf[x];  // пока 255, но надо экспериментировать
    if (vMin > adcBuf[x]) vMin = adcBuf[x];
  }

  // == Вычисляем максимальное и минимальное значение сигнала == //
  vSync = (vMax - vMin) / 2 + vMin; // уровень синхронизации по середине уровня сигнала
  // == Определение точки синхронизации == //
  bool flagZero = 0; grOffset = 0; // сброс флага и точки синхронизации
  // Ищем перепад от меньшего уровня к большему
  for (uint8_t x = 1; x < BUFSIZE_SHORT - LCDX; x++) { // смотрим весь массив данных АЦП
    if (adcBuf[x] < vSync) flagZero = 1; // нашли меньше, чем синхра (перепад сигнала в минус) - ставим флаг
    if (flagZero && adcBuf[x] > vSync) {
      grOffset = x;  // нашли больше, чем синхра (перепад сигнала в плюс) - запомнили и выходим из цикла
      break;
    }
  }
  // === Считаем частоту сигнала === //
  if (vRef && vMax * VCC / NAPR > APP) { // если можем замерить аппаратно - меряем
    if (FreqCount.available()) count = FreqCount.read(); // вывод частоты по готовности счетчика частоты сигнала
  } else { // === Меряем частоту сигнала программно === //
    flagZero = 0; count = 0; // сброс флага и счетчика
    for (uint8_t x = grOffset; x < BUFSIZE_SHORT - LCDX; x++)  { // смотрим массив от точки синхронизации до конца
      if (adcBuf[x] < vSync) flagZero = 1; // нашли меньше, чем синхра (перепад сигнала в минус) - выставляем флаг
      if (flagZero && adcBuf[x] > vSync) { // нашли больше, чем синхра (перепад сигнала в плюс) - отловили полный период
        switch (razv) { // считем частоту периода
          case 6: count = 1000000 / ((x - grOffset - 1) * 3.25);    break; // делитель 4
          case 5: count = 1000000 / ((x - grOffset)  * 3.25) / 2;  break; // делитель 8
          case 4: count = 1000000 / ((x - grOffset)  * 3.25) / 4;  break; // делитель 16
          case 3: count = 1000000 / ((x - grOffset)  * 3.25) / 8;  break; // делитель 32
          case 2: count = 1000000 / ((x - grOffset)  * 3.25) / 16; break; // делитель 64
          case 1: count = 1000000 / ((x - grOffset)  * 3.25) / 32; break; // делитель 128
          case 0: count = 1000000 / ((x - grOffset)  * 510);     break; // делитель 128 тоже
        }
        break;
      }
    }
  }
  count = count * OVERCLOCK / 16.0; // пересчет частоты на разные кварцы
}
// === Осциллоскоп === //
void Oscil() {
  // установка опорного напряжения АЦП и настройка входа АЦП
  ADMUX = vRef ? 0b01100011 : 0b11100011;
label_ReadAdc:
  // === Обработка кнопок === //
  if(digitalRead(KEY_L) == KEY_PRESSED) // кнопка лево
  {
    switch (menu)
    {              //progrik: подавление дребезга и предотвращение постоянного циклического переключения
      case 0 : if((millis() - ok_last_time) > 100) { razv--; if(razv == NAPR) razv = 6; }     break; // меняем развертку
      case 1 : grOffset -= PAUSE_OFFSET; if (grOffset < 0) grOffset = 0;                      break; // листаем график в паузе
    }
  ok_last_time = millis(); //progrik: запоминаем время последнего нажатия кнопки
  }

  if(digitalRead(KEY_R) == KEY_PRESSED) // кнопка направо
  {
    switch (menu)
    {            //progrik: подавление дребезга и предотвращение постоянного циклического переключения
    case 0 : if((millis() - ok_last_time) > 100) { razv++; if (razv == 7) razv = 0; }                      break; // меняем развертку
    case 1 : grOffset += PAUSE_OFFSET; if (grOffset > BUFSIZE_LONG - LCDX) grOffset = BUFSIZE_LONG - LCDX; break; // листаем график в паузе
    }
  ok_last_time = millis(); //progrik: запоминаем время последнего нажатия кнопки
  }
  if (digitalRead(KEY_OK) == KEY_PRESSED)
   {
     if(ok_short_press && (millis() - ok_start_time) > 600) //progrik: задержка для длинного нажатия
     {
       avtorazv = !avtorazv;   //progrik: переключит авторазвертку после длительного нажатия ^^^ 600ms
       ok_short_press = false;
     }
     if((millis() - ok_last_time) > 200) //progrik: подавление дребезга - ждем 200 мс со времени последнего нажатия
     {
       ok_start_time = millis(); //progrik: запоминаем время первого нажатия кнопки OK
       ok_short_press = true;   //progrik: отмечаем флаг короткого нажатия
     }
     ok_last_time = millis(); //progrik: запоминаем время последнего нажатия кнопки OK
   }
   else
   {
     if(ok_short_press) //progrik: если нужно отработать короткое нажатие (флаг установлен)
     {
       ok_short_press = false; //progrik: снимаем флаг короткого нажатия
       switch (++menu)
       {
         case 1: grOffset = 0; pause = 1; ReadAdc(); calc_val(); break; 
// вход в паузу - антидребезг типа
         case 2: menu = 0;  pause = 0; break; // перебор меню
       }
     }
   }
  // === Ведём рассчеты === //
  if (!pause) { // если нет паузы
    ReadAdc();  // то снимаем осциллограмму
    calc_val();
  } // закончили вести рассчеты

  display.clearDisplay(); // чистим экран...
  /*      if (count < 10) kzap=true;
    if (kzap==true && count > 10){
    FreqCount.end();
    tic1 = asm_func(1);
    tic2 = asm_func(0);
    duty=(1/((float)tic1/tic2))*100;
    kzap=false;
    FreqCount.begin(1000);
    }
    if (kzap==false && count > 10 && duty < 100){
    display.setCursor(18,11);
    display.setTextColor(BLACK);
    display.print(duty);
    display.print("%");
    }*/
  // === Отрисовка меню осцилла ===
  if (vRef) display.print(VCC, 1); else {
    if (kdel == 5)display.print("1.1"); else display.print("*22");
  }
  if (menu == 0) display.setTextColor(BLACK, WHITE);
  if (vRef && vMax * VCC / NAPR > APP) {
    display.print("a");  // если замер аппаратный - "ужирняем" точку
  }
  else  {
    display.print("п");  // иначе "П"рограмный
  }
  display.print(razv); 
  display.print(avtorazv?'a':'p');   // указатель состояния авторазвёртки
  if (menu == 1)
    if (!pause)  display.setTextColor(BLACK);
  else {   // рисуем прокрутку в режиме паузы
  display.setCursor(5, 11); display.print("Pause");  display.drawLine(grOffset / 5.7, 8, grOffset / 5.7 + 7, 8, BLACK); display.drawLine(grOffset / 5.7, 9, grOffset / 5.7 + 7, 9, BLACK);
   }  // шкала прокрутки
  display.setCursor(36, 0); display.print(count / 1000000.0, 6);  //покажем частоту
  display.setCursor(60, 11); display.print(vMax * (vRef ? VCC : 1.1) / NAPR, 2); // рисуем максималоное напряжение сигнала
  display.setCursor(65, 40); display.print(vMin * (vRef ? VCC : 1.1) / NAPR, 1); // рисуем минимальное  напряжение сигнала
  if (vMax == 0xFF) for (uint8_t x = 0; x < 5; x++) display.drawLine(x, 9, x, 47, BLACK); // указатель привышения напряжения на входе осцила

  // == Отрисовка сетки == //
  for (byte i = 47; i > 5; i = i - 7) {
    display.drawLine( 0, i, 2, i, BLACK);  // черточки слева
  }
  for (byte i = 47; i > 5; i = i - 3) {
    display.drawPixel(21, i, BLACK);  // вертикальный пунктир
    display.drawPixel(42, i, BLACK);
    display.drawPixel(63, i, BLACK);
  }
  for (byte i = 3; i < 84; i = i + 3) {
    display.drawPixel(i, 33, BLACK);
    display.drawPixel(i, 19, BLACK);
  }

  ///////////////авто razv//////////////
  if (avtorazv)
#define PER 1.3
  if (count > 4323.3 * PER) razv = 6;
  else if (count > 2434.5 * PER) razv = 5;
  else if (count > 0969.3 * PER) razv = 4;
  else if (count > 0486.8 * PER) razv = 3;
  else if (count > 0245.8 * PER) razv = 2;
  else if (count > 0120.1 * PER) razv = 1;
  else razv = 0;
  //bulat если зашкаливает включаем предел 5 в
  if (vMax == NAPR) {
    if (vRef == 0)
    {
      vRef = 1;
      ADMUX = 0b01100011;// выбор внутреннего опорного 5.0 В
      goto   label_ReadAdc;
    }
  }
  //bulat если  5 в  и уровень менее 1,1 в то вкл предел 1,1 в
  if (vMax <= 55) {
    if (vRef == 1)
    {
      vRef = 0;
      ADMUX = 0b11100011;// выбор внутреннего опорного 1,1В
      goto   label_ReadAdc;
    }
  }
  //bulat здесь автопредел 1,1/0,22 в,программный
  kdel = 5;
  if (vMax <= 55) {
    if (vRef == 0)
    {
      kdel = 1;
    }
  }
  // == Отрисовка графика == //
  for (uint8_t x = 0; x < 80; x++)
    display.drawLine(x + 4, 47 - adcBuf[x + grOffset] / kdel / 1.35, x + 4, 47 - adcBuf[x + grOffset + 1] / kdel / 1.35, BLACK);
  display.display(); // вываливаем буфер экрана на экран
}
////////////////////////////////////////////////////////////////////////////////////////////
// РЕЖИМ ГЕНЕРАТОРА
void Generator() {
  // обработка кнопок и отрисовка одновременно
  display.clearDisplay();
  ShowModeInv(0);
  if (digitalRead(KEY_OK) == KEY_PRESSED) {
    if (menu++ == 7) menu = 0;  // переходим по разрядам / меняем ШИМ
    delay(KEY_DELAY);
  }
  if (menu == 7) { // меняем ШИМ
    if (digitalRead(KEY_L) == KEY_PRESSED) {
      PWM--;
      if (PWM < 0)   PWM = 100;
      delay(KEY_DELAY);
    }
    if (digitalRead(KEY_R) == KEY_PRESSED) {
      PWM++;
      if (PWM > 100) PWM = 0;
      delay(KEY_DELAY);
    }
    pwmWrite(GEN_PIN, PWM * 2.55); // задали ШИМ
    display.setTextColor(WHITE, BLACK); // если меняем шим, то рисуем его инверсным
    display.drawLine(15, 39, 68 + (PWM == 100 ? 6 : 0) + (PWM < 10 ? -6 : 0), 39, BLACK); // сверху полоска для большей инверсности
  } else { // меняем частоту
    if (digitalRead(KEY_L) == KEY_PRESSED) {
      if (freq > stepFreq)  freq -= stepFreq;          // не даем выйти за допустимый диаппазон
      SetPinFrequencySafe(GEN_PIN, freq / (OVERCLOCK / 16.0)); // задали частоту
      delay(KEY_DELAY); // задержка для кнопок
    }
    if (digitalRead(KEY_R) == KEY_PRESSED) {
      if ((freq + stepFreq) < 10000000) freq += stepFreq; // не даем выйти за допустимый диаппазон
      SetPinFrequencySafe(GEN_PIN, freq / (OVERCLOCK / 16.0)); // задали частоту
      delay(KEY_DELAY); // задержка для кнопок
    }
    display.setTextColor(BLACK); // если ШИМ не меняем, то выбираем обычный текст
#define XFREQ 14 // ======== // и, выделяем декаду частоты полосочками
    stepFreq = pow(10, (byte)menu); if (menu > 1) stepFreq++; // устраняем глючность pow, почему 10 в степени 2 = 99?
    byte menu_t = menu; if (menu > 2) menu_t++; if (menu == 6) menu_t++; // делаем табуляцию частоты
    menu_t = 54 - menu_t * 6; // считаем положение полосочек
    display.drawLine(menu_t, XFREQ - 2, menu_t + 4, XFREQ - 2, BLACK); // рисуем полоски
    display.drawLine(menu_t, XFREQ - 3, menu_t + 4, XFREQ - 3, BLACK);
    display.drawLine(menu_t, XFREQ + 8, menu_t + 4, XFREQ + 8, BLACK);
    display.drawLine(menu_t, XFREQ + 9, menu_t + 4, XFREQ + 9, BLACK);
  }
  // рисуем уровень ШИМ (инверсия текста задана ранее)
  display.setCursor(15, 40); // ставим курсор))
  display.print(" PWM=");  display.print(PWM);  display.print("% "); // выводим уровень ШИМ
  display.setTextColor(BLACK); // убираем инверсию при выводе частоты
  // рисуем частоту с табуляцией
  display.setCursor(6, XFREQ);
  for (unsigned long freq_t = 1000000; freq_t>0; freq_t/=10) {
  if (freq>=freq_t) {
    display.print((freq/freq_t)%10); if (freq_t==1000000 || freq_t==1000) display.print("'"); }  else { 
    display.print("_");              if (freq_t==1000000 || freq_t==1000) display.print(" "); }
  }
  display.print(" Hz");
  // отрисовка графика PWM
  for (char x = 17; x < 67; ) {
    if (PWM != 0)             display.drawLine(x, 26, x + PWM / 4, 26, BLACK); x += PWM / 4; // верх
    if (PWM != 0 && PWM != 100) display.drawLine(x, 26, x, 36, BLACK);                   // спад
    if (PWM != 100)           display.drawLine(x, 36, x + 25 - PWM / 4, 36, BLACK); x += 25 - PWM / 4; // низ
    if (PWM != 0 && PWM != 100 && x < 43) display.drawLine( x, 36, x, 26, BLACK);        // подъем
  }
  display.display(); // вываливаем буфер на дисплей
}

// === DDS генератор ===
void DdsGenerator() {
  static const byte ddsWave[][32] PROGMEM = {        
    1,6,15,29,48,69,92,117,143,168,191,212,229,243,251,255,254,248,237,222,203,181,156,131,106,81,59,39,22,10,3,1,   // sinNew    
    1,18,35,52,69,86,103,120,137,154,171,188,205,222,239,255,239,223,207,191,175,159,143,127,111,95,79,63,47,31,15,1, // treugNew    
    1,9,17,25,33,41,49,57,65,73,81,89,97,105,113,121,129,137,145,153,161,169,177,185,193,201,209,217,225,235,245,255,  // pilaNew
    250,246,238,230,222,214,206,198,190,182,174,166,158,150,142,134,126,118,110,102,94,86,78,70,62,54,41,33,25,17,9,1 };// pilaObrNew
  const char* const ddsStr[] PROGMEM = { "          ", "           ", "       ", "             "};
  byte ddsCount=0;
  // Рисуем DDS-генератор
  display.clearDisplay(); ShowModeInv(0); // режим работы, заголовок
  for (byte i = 0; i < 84;) display.drawLine(i, 36 - pgm_read_byte(&ddsWave[menu][i % 32]) / 10, i, 36 - pgm_read_byte(&ddsWave[menu][(i++) % 32]) / 10, BLACK);
  display.setCursor(3, 40);  display.print(ddsStr[menu]);  display.display(); // отрисовали все
  while (digitalRead(KEY_R)!=KEY_PRESSED && digitalRead(KEY_L)!=KEY_PRESSED && digitalRead(KEY_OK)!=KEY_PRESSED) { // выводим выбранный сигнал, пока не нажали кнопку
    pwmWrite(DDS_PIN, pgm_read_byte(&ddsWave[menu][(ddsCount++) & 0x1F]));
    //ResetAvrCheck();
  }
  if (++menu == 4) menu = 0; // нажали кнопку - переключаем режим
  delay(KEY_DELAY); // чтоб кнопки нормально нажимались
}

/*
  // UART приемник
  void Terminal() {
  const long speedUart[] = { 1200,2400,4800,9600,19200,38400,57600,115200 };
  display.clearDisplay();
  ShowModeInv(0);
  display.setCursor(15,15); display.println("Скорость:");
  display.setTextColor(WHITE, BLACK);
  display.setCursor(66, 25); display.println("+");
  display.setCursor(13, 25); display.print("-");
  display.setTextColor(BLACK);
  display.println(" ");
  display.print(speedUart[menu]);
  display.println(" ");
  display.setCursor(9,40); display.println("ОК - запуск");
  if(digitalRead(KEY_L) == KEY_PRESSED)  { menu--; if(menu==255) menu=7;   delay(KEY_DELAY);  }
  if(digitalRead(KEY_R) == KEY_PRESSED)  { menu++; if(menu==8)   menu=0;   delay(KEY_DELAY);  }
  if(digitalRead(KEY_OK) == KEY_PRESSED) {
    Serial.begin(speedUart[menu]*(16/OVERCLOCK));
    display.clearDisplay();
    display.print(">");
    delay(KEY_DELAY);
    display.display();
    int x=83;
    while (1) {
      //ResetAvrCheck();
      if (Serial.available()) { // Если в буфере есть данные
        if(++x==84) { // проверяем, не пора ли экран очистить
          x=0;
          display.clearDisplay();
        }
        display.print((char)(Serial.read())); // печать байта в дисплей
        display.display(); // выводим символ
      }
    }
  }
  display.display();
  }
*/
void SetTextColor(char c) {
  if (c) display.setTextColor(WHITE, BLACK); else display.setTextColor(BLACK);
}

void OscilSlow() {
  // надо найти главного тормазоглу!!!!!!!!!

  ADMUX = vRef ? 0b01100011 : 0b11100011; // установка опорного напряжения АЦП
  //ADCSRA = 0b11100110; // делитель частоты АЦП - 64
  //ADCSRA = 0b11100010; //delitel 4 // кондер не успевает разряжаться
  ADCSRA = 0b11100011; //delitel 8
  ADCSRA |= 0x10; // запуск преобразования АЦП

  // === Обработка кнопок ===
  if (digitalRead(KEY_L) == KEY_PRESSED) {
    switch (menu) { // тут идея такая - один раз опрашиваем кнопку, т.к. это медленно делается, потом через кейсы прыгаем.
      case 0 : vRef = !vRef;                      break;   // выбор опорного напряжения
      case 1 : razv--; if (razv == NAPR)  razv = 2;    break; // выбор развертки : 1000мс 500мс 100мс на клетку
      case 2 : grOffset -= 15; if (grOffset < 0)  grOffset = BUFSIZE_LONG; break; // режим паузы - прокручиваем осциллограмму
      case 3 : for (uint8_t x = 0; x < 26; x++) adcBuf[x] = 0; count = 0; grOffset = 0;
        display.clearDisplay(); display.setCursor(3, 20); display.print("RESET!");
        display.display(); delay(1000);    break;
    } delay(200);
  } // антидребезг кнопки
  if (digitalRead(KEY_R) == KEY_PRESSED) {
    switch (menu) {
      case 0 : vRef = !vRef;                      break;   // выбор опорного напряжения
      case 1 : razv++; if (razv == 3)    razv = 0;    break; // выбор развертки : 1000мс 500мс 100мс на клетку
      case 2 : grOffset += 15; if (grOffset > BUFSIZE_LONG) grOffset = 0;  break; // режим паузы - прокручиваем осциллограмму
    } delay(200);
  } // антидребезг кнопки
  if (digitalRead(KEY_OK) == KEY_PRESSED) {
    menu++; if (menu == 4) {
      menu = 0;  // перебор меню
      pause = 0;
    }
    if (menu == 2) {
      pause = 1;
      adcBuf[(count++) % BUFSIZE_SHORT] = NAPR; adcBuf[(count++) % BUFSIZE_SHORT] = 0; adcBuf[(count++) % BUFSIZE_SHORT] = NAPR; if (count > 79) grOffset = count - 80;
    } // отметка паузы на графике
    delay(200);
  } // антидребезг кнопки
  // === Отрисовка менюшки ===
  display.clearDisplay();  display.setCursor(0, 0);
  SetTextColor(menu == 0); if (vRef) display.print(VCC, 1); else display.print("1.1");
  SetTextColor(menu == 1); display.print(" ");   display.print(razv);
  SetTextColor(menu == 2); display.print(" S");  display.print(" ");
  display.print((grOffset % BUFSIZE_SHORT) / 2.02, 0);  display.print("%");
  SetTextColor(menu == 3); display.print(" R");
  display.setTextColor( BLACK);

  // == Отрисовка сетки == Новая //
  for (byte i = 47; i > 5; i = i - 7) {
    display.drawLine( 0, i, 2, i, BLACK);  // черточки слева
  }
  /* for(byte i=47;i>5;i=i-7) { display.drawPixel(17,i, BLACK); display.drawPixel(33,i, BLACK); display.drawPixel(49,i, BLACK); display.drawPixel(65,i, BLACK); display.drawPixel(81,i, BLACK); } // вертикальный пунктир
    for(byte i=1;i<84;i=i+8) { display.drawPixel(i,40, BLACK); display.drawPixel(i,26, BLACK); display.drawPixel(i,12, BLACK); }    // горизонтальный пунктир

    // === Отрисовка сетки Старая ===
    for(byte i=47;i>5;i=i-7) { display.drawLine( 0, i, 2, i, BLACK); } // черточки слева
    for(byte i=47;i>5;i=i-3) { display.drawPixel(21,i, BLACK);display.drawPixel(42,i, BLACK);display.drawPixel(63,i, BLACK); } // вертикальный пунктир
    for(byte i=3;i<84;i=i+3) { display.drawPixel(i,33, BLACK);display.drawPixel(i,19, BLACK);}                                 // горизонтальный пунктир
  */
  // === Отрисовка графика ===
  for (uint8_t x = 0; x < 80; x++) display.drawLine(x + 4, 47, x + 4, 47 - adcBuf[(x + grOffset) % BUFSIZE_SHORT] / 7, BLACK);
  if (pause) { // если пауза - рисуем шкалу прокрутки
    display.drawLine((grOffset % BUFSIZE_SHORT) / 2.3, 8, (grOffset % BUFSIZE_SHORT) / 2.3 + 5, 8, BLACK); // шкала прокрутки
    display.drawLine((grOffset % BUFSIZE_SHORT) / 2.3, 9, (grOffset % BUFSIZE_SHORT) / 2.3 + 5, 9, BLACK); // шкала прокрутки
  } else { // если не пауза, то снимаем показания
    while (!(ADCSRA & 0x10)); // ждем, пока АЦП считает
    adcBuf[count % BUFSIZE_SHORT] = ADCH; // сохраняем число из АЦП в массив
    if (count > 79) grOffset = count - 80;
    count++;
    if (count > BUFSIZE_LONG) count = BUFSIZE_SHORT; // зацикливаем счетчик
    //45мс - программа выполняется
    switch (razv) {
      case 0 : delay(55 * (OVERCLOCK / 16.0)); break; // секунда на клетку
      case 1 : delay(05 * (OVERCLOCK / 16.0)); break; // пол секунды на клетку
      case 2 : break; // без задержки.......
    }
  }
  display.display();
}

// === Чтение цифровых данных со всего порта C (A0...A5) === //
void ReadDig() {
  if (razv == 9) {
    for (int i = 0; i < BUFSIZE_LONG; i++) adcBuf[i] = PINC; // быстро читаем данные
  } else {
    for (int i = 0; i < BUFSIZE_LONG; i++) {
      adcBuf[i] = PINC;  // читаем данные
      delayMicroseconds((9 - razv) * 100);
    }
  }
}
/*
// ==== Логический анализатор ==== //
void LogAnalyzer() {
  // обрабатываем кнопки
  if (digitalRead(KEY_L) == KEY_PRESSED)   switch (menu) {
      case 0 : razv--; if (razv == NAPR) razv = 9; delay(400);                        break;
      case 1 : grOffset -= 20; if (grOffset < 0) grOffset = 0;                        break;
    }
  if (digitalRead(KEY_R) == KEY_PRESSED)  switch (menu) {
      case 0 : razv++; if (razv == 10) razv = 0; delay(400);                        break;
      case 1 : grOffset += PAUSE_OFFSET; if (grOffset > BUFSIZE_LONG - LCDX) grOffset = BUFSIZE_LONG - LCDX; break;   break;
    }
  if (digitalRead(KEY_OK) == KEY_PRESSED)  switch (++menu) {
      case 1: grOffset = 0; pause = 1; break; // вход в паузу
      case 2: menu = 0;     pause = 0; break; // перебор меню
    }

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

  display.clearDisplay(); // чистим дисплей
  display.print(razv);    // выводим развертку
  display.print(" ");
  if (pause) {
    display.print(" Пауза ");
    // рисуем прокрутку в режиме паузы
    display.drawLine(grOffset / 8, 8, grOffset / 8 + 6, 8, BLACK); // шкала прокрутки
    display.drawLine(grOffset / 8, 9, grOffset / 8 + 6, 9, BLACK); // шкала прокрутки
    delay(200);
  }
  for (byte i = 47; i > 12; i = i - 7) {
    display.drawLine( 0, i - 5, 2, i - 5, BLACK);  // черточки слева
    display.drawPixel(0, i, BLACK);
    display.drawPixel(2, i, BLACK);
  }
  // горизонтальный пунктир
  for (byte i = 5; i < 84; i = i + 3) {
    display.drawPixel(i, 47, BLACK);
    display.drawPixel(i, 40, BLACK);
    display.drawPixel(i, 33, BLACK);
    display.drawPixel(i, 26, BLACK);
    display.drawPixel(i, 19, BLACK);
  }
  for (int y = 0; y < 80; y++) {
    for (int x = 0; x < 5; x++) {
      display.drawLine(y + 4, 47 - (x * 7) - (bit_is_set(adcBuf[y + grOffset], 5 - x) ? 5 : 0), y + 4, 47 - (x * 7) - (bit_is_set(adcBuf[y + 1 + grOffset], 5 - x) ? 5 : 0), BLACK);
    }
  }
  display.display();
}
*/
//////////////////////////////Экран/////////////////////////////////
void Menu_ekrana() {
  Set = 0; delay(200);
  while (digitalRead(KEY_OK) != KEY_PRESSED) {
    display.clearDisplay();
    if (Set == 0) display.setTextColor(WHITE, BLACK); else display.setTextColor(BLACK);
    display.setCursor(0 , 0); display.println("Сontr:"); display.setTextColor(BLACK);
    display.setCursor(60 , 0); display.println(contrast);
    display.setCursor(0 , 10); display.println("Light:"); display.setTextColor(BLACK);
    if (BL == 1) { display.setCursor(60 , 10); display.println("ON");} else
                 { display.setCursor(60 , 10); display.println("OFF");} display.display();
    if (digitalRead(KEY_R) == KEY_PRESSED)  contrast++;
    if (digitalRead(KEY_L) == KEY_PRESSED)  contrast--;
    if (contrast < 30) contrast = 30;
    if (contrast > 80) contrast = 80;
    display.setContrast(contrast);
    printTemp();//       Терпература
    display.display();
    delay(150);
  }
  Set = 1; delay(200);
  while (digitalRead(KEY_OK) != KEY_PRESSED) {
    display.clearDisplay();
    display.setCursor(0 , 0); display.println("Сontr:");
    display.setCursor(60 , 0); display.println(contrast);
    if (Set == 1) display.setTextColor(WHITE, BLACK); else display.setTextColor(BLACK);
    display.setCursor(0 , 10); display.println("Light:"); display.setTextColor(BLACK);
    if (BL == 1) { display.setCursor(60 , 10); display.println("ON"); digitalWrite(Ekran, BL); } else
                 { display.setCursor(60 , 10); display.println("OFF"); digitalWrite(Ekran, BL);  }
    if (digitalRead(KEY_R) == KEY_PRESSED)  BL=1;
    if (digitalRead(KEY_L) == KEY_PRESSED)  BL=0;
    printTemp();//       Терпература
    display.display();
    delay(150);
  }
  display.display();
  display.clearDisplay();  // чистим буфер экранчика
  EEPROM.write(0, contrast);
  EEPROM.write(1, BL);
  void (* reboot)(void) = 0;
  reboot();
}
/////////////////////Коэффициент заполнения
uint32_t asm_func(uint8_t flag) {
  asm volatile (
    "cli"                    "\n\t"
    "mov r21,%0"    "\n\t" // флаг что считывать
    //сохранение конфигурация таймеров
    "in  r30,0x23"            "\n\t" // r30 <- GTCCR
    "ldi r18,0x83"            "\n\t" // GTCCR=0x83
    "out 0x23,r18"            "\n\t" // GTCCR=0x83
    "lds r26,0x80"            "\n\t" // r26 <- TCCR1A
    "sts 0x80,__zero_reg__"   "\n\t" // TCCR1A=0
    "lds r27,0x81"            "\n\t" // r27 <- TCCR1B
    "ldi r19,0x4"             "\n\t"
    "sts 0x81,r19"            "\n\t" // TCCR1B=1<<CS12
    "sts 0x85, __zero_reg__"  "\n\t" //TCNT1L=0
    "sts 0x84, __zero_reg__"  "\n\t" //TCNT1H=0
    "lds r28,0xB0"            "\n\t" // r28 <- TCCR2A
    "sts 0xB0,__zero_reg__"   "\n\t" // TCCR2A=0
    "lds r29,0xB1"            "\n\t" // r29 <- TCCR2B
    "ldi r20,0x1"             "\n\t"
    "sts 0xB1,r20"            "\n\t" // TCCR2B=1<<CS20
    "sts 0xB2,__zero_reg__"   "\n\t" // TCNT2=0
    "lds r31,0x70"            "\n\t" // r31 <- TIMSK2
    "sts 0x70,__zero_reg__"   "\n\t" // TIMSK2=0
    ////*******************************************
    // пройти если ноль
    "wait_begin:"             "\n\t"
    "sbic 0x09,5"             "\n\t" // Порт D5
    "rjmp wait_begin"         "\n\t"
    // пройти если единица -> первый импульс
    "wait_start:"             "\n\t"
    "sbis 0x9,5"              "\n\t"
    "rjmp wait_start"         "\n\t"
    "sts 0x43,__zero_reg__"   "\n\t" // GTCCR=0 старт синхра
    // импульс пошёл, ждём ноль
    "wait_pause:"             "\n\t"
    "sbic 0x09,5"             "\n\t"
    "rjmp wait_pause"         "\n\t"
    "cpi r21,__zero_reg__"    "\n\t" //
    "brne duty"  "\n\t"
    // импульс кончился, ждём  конца периода (единицу)
    "wait_end:"               "\n\t"
    "sbis 0x9,5"              "\n\t"
    "rjmp wait_end"           "\n\t"
    "duty:"                   "\n\t"
    ////*******************************************
    //восстановление таймеров
    "sts 0x80,r26"            "\n\t" // TCCR1B
    "sts 0x81,r27"            "\n\t" // TCCR1B
    "sts 0xB0,r28"            "\n\t" // TCCR2A
    "sts 0xB1,r29"            "\n\t" // TCCR2B
    "sts 0x70,r31"            "\n\t" // TIMSK2
    "out 0x23,r30"            "\n\t" // GTCCR  старт таймеры
    //вывод 3х байт
    "lds r22, 0xB2"           "\n\t" //  tctn2  0 байт
    "lds r23, 0x84"           "\n\t"  // TCNT1L 1 байт
    "lds r24, 0x85"           "\n\t"  // TCNT1H 2 байт
    "lds r25, __zero_reg__"   "\n\t" // пустой  3 байт
    "sei"                     "\n\t"
    ::"r" (flag): );
}
/////////////////////Коэффициент заполнения

 

uldin@mail.ru
Offline
Зарегистрирован: 15.02.2012

Понял! Спасибо за схему!

progrik
Offline
Зарегистрирован: 30.12.2018

...

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

progrik, smokok Спасибо за код!    Залил п.4422 все чудесно работает!

Вернул на родину терминал, причесал немного отображение его меню, поставил по умолчанию  9600...

Есть один ньюанс: частоту при входном меньше Вольта показывает вместо 10000  - 10250... т.е. на пределах ОН 1,1В и 0,22В, а на 5,1В  показыывает 10009

 // UART приемник
  void Terminal() {
  const long speedUart[] = { 9600,19200,38400,57600,115200,1200,2400,4800, };
  display.clearDisplay();
  ShowModeInv(0);
  display.setCursor(25,15); display.println("Speed:");
  display.setTextColor(WHITE, BLACK);
  display.setCursor(13, 27); display.print("-");
  display.setTextColor(BLACK);
  display.print(" "); display.print(speedUart[menu]);  
  display.setTextColor(WHITE, BLACK);
  display.setCursor(66, 27); display.println("+");
  display.setTextColor(BLACK); ...

Хотел подключить дисплей Нокии3510(контроллер PCD8544, как в нокии1100 96х65), но "не заводится", - экранчик молчит, хотя с теми же библиотеками циферки - буковки в демо выводятся...

Есть одна просьба: "прикрутите", пожалуйста, энкодер, -  с ним намного удобнее...

Спасибо!                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           

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

Раз пошла такая пьянка (по поводу ускорения отображения на дисплее), то вот ещё подсказки.
Насчёт аппаратного SPI, надеюсь, знают все. Никакой другой вариант быстрей не будет. Но есть одно интересное исключение. Это USART в режиме SPI. На картинке ниже привожу 6 скринов с анализатора, все сняты в разных режимах (внимание, масштаб везде разный, поэтому смотрите на временные параметры).

Плюсы USART в режиме SPI:

1. Скорость выше, чем у аппаратного SPI, процентов на 30, с учётом пауз между передачей байтов. Возможность передавать по 2 байта подряд совсем без паузы (то есть 16 подряд тактирующих импульсов).
2.Заняты, в отличие от аппаратного SPI, только 2 ноги контроллера (1-data, 4-clk). У аппаратного SPI всегда заняты ноги 11, 12, 13, даже если 12 ни к чему не подключена. Здесь же ногу №0 можно использовать в любых своих целях. Само собой, для общения с дисплеем нужны ещё как минимум 2 или 3 ноги (то есть всего 4 или 5 сигнальных плюс 2 питание).

Минусы USART в режиме SPI:
1. Не поддерживает 9-битную передачу, необходимую некоторым дисплеям, хотя USART в обычном режиме умеет. (Это не точно, может кто подскажет как.)
2. Привязка к конкретным ногам контроллера (1 и 4).
3. Выплывает из пункта 2 - невозможность использовать USART для общения (не переживайте, скетчи при этом заливаются нормально).

Обратите внимание, на четвёртой и шестой картинках видна передача 2 байт подряд- расчёска с 16 зубцами.

Информация есть здесь //https://feilipu.me/2015/02/17/avr-atmega-usart-spi-mspim/
 

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

Вот фрагмент кода, должен подойти для 5110, хотя я проверял на совсем другом дисплее. Ещё раз напоминаю: нога 0 - data(SDA,DI и т.п.), нога 4 - clk(SCK и др.), остальные ножки к дисплею тоже прописываются в скетче...

void Update() { //=======================Обновить дисплей
  for (byte p = 0; p < 8; p++) {
    transfer(0xb0 + p, 0);
    transfer(0x10, 0xe0);
    bitWrite(PORTD, 0, 1);// высокий уровень на DC (Data/Comand), можно любую ногу контроллера, кроме 0 и 4
    for (byte col = 0; col < LCD_X ; col = col + 2) {
      //SendByte(LCD_D, LCD_RAM[(LCD_X * p) + col]); //===========так было
      transfer((LCD_RAM[(LCD_X * p) + col]), (LCD_RAM[(LCD_X * p) + col + 1]));//=========так стало, отправляем по 2 байта подряд, чтоб быстрее
    }
    bitWrite(PORTD, 0, 0);// низкий уровень на DC (Data/Comand)
  }
}

void  transfer(byte sp) { //=====================подпрограмма отправки байта
  UCSR0A = _BV (TXC0); // Сбросить флаг завершения передачи, все остальные биты должны быть записаны в 0.
  UDR0 = sp; // Начать передачу первого байта.
  while (! (UCSR0A & _BV (TXC0))); // Проверьте, что мы закончили, ожидая флага завершения передачи.
}

void  transfer(byte sp1, byte sp2) { //=================подпрограмма отправки двух байт
  UCSR0A = _BV (TXC0); // Сбросить флаг завершения передачи, все остальные биты должны быть записаны в 0.
  UDR0 = sp1; // Начать передачу первого байта.
  UDR0 = sp2; // Начать передачу второго байта.
  while (! (UCSR0A & _BV (TXC0))); // Проверьте, что мы закончили, ожидая флага завершения передачи.
}

И напоследок: кто поможет мне это переписать под Atmega64 ? Зачем? А затем, что мне нужны одновременно SPI, UART для терминала, и SPI к дисплею. Да и цена меньше, чем Atmega328. Пока что эксперименты не увенчались успехом.

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

Всем здравствуйте. Я так понимаю что у меня, у одного в этом девайсе с кнопками проблемы. Или с контроллерами, сразу с двумя. Чего у меня кнопочки сами нажимаются?  progrik меня научил что делать. Но всё таки интересно почему у всех работает, а у меня нет.

progrik
Offline
Зарегистрирован: 30.12.2018

раз пошла такая пьянка.... я человек - проверка. у меня SPI работает на 4МГц, и это проверенно опытом. можно быстрее, проводочки плохие... заливанием дисплея.... точки на фпс.... ВСЁ. он работает ровно на своей скорости. это аппаратная фигня, оно работает такт в такт... и только две ноги, я не понимаю, зачем тебе 3-я....

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

progrik
Offline
Зарегистрирован: 30.12.2018

есть 2 способа подключать кнопки. прижимаешь через резистор к земле(минус, 0), или подтягиваешь к плюсу(+, 1) в коде поменяй 0 на 1 или наоборот. как-то так...

progrik
Offline
Зарегистрирован: 30.12.2018

наверное, нужно создать топик "понимаем, что делаем". уверен, польза будет всем.

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

У меня входы контроллера стянуты на землю через 10 КОм и "+" подаётся через кнопки. Как на схеме. Буду стараться, изучать. 

progrik
Offline
Зарегистрирован: 30.12.2018

volodya198024 пишет:

У меня входы контроллера стянуты на землю через 10 КОм и "+" подаётся через кнопки. Как на схеме. Буду стараться, изучать. 

я на землю опускаю, а на плюс подтягиваю)) найди #define  где ЯВНО написано - 0 или 1... просто поменяй. сорри за французкий...

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

Так просто! И всё заработало. Спасибо.

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

progrik пишет:

...у меня SPI работает на 4МГц, и это проверенно опытом...это аппаратная фигня, оно работает такт в такт...
USART в режиме SPI работает на 8МГц, и это проверенно опытом. SPI может работать на 8МГц, но у меня пока не получилось. Кстати, есть нюанс- не все дисплеи поддерживают такую скорость передачи данных.

progrik пишет:

... и только две ноги, я не понимаю, зачем тебе 3-я....
Мне третья не нужна, это дисплею нужны кроме CLK и DATA ещё D/C, SCE, RESET (последний не обязательно). Вопрос в другом: можешь ли ты использовать ногу 12 для каких-либо целей при подключении дисплея через аппаратный SPI?

progrik
Offline
Зарегистрирован: 30.12.2018

seri0shka пишет:
Кстати, есть нюанс- не все дисплеи поддерживают такую скорость передачи данных....Вопрос в другом: можешь ли ты использовать ногу 12 для каких-либо целей при подключении дисплея через аппаратный SPI?
я искренне не понимаю, что значит "могу ли я использовать". у меня тфт подключен на порт целиком. я потерял сериал. для загрузки кода вынимаю 2 провода.... ноги-это ноги. даже при работе SPI ты можешь дослать 9-й бит. ты МОЖЕШЬ делать все, что нужно, дрыгай что и как хочешь. просто понимай, когда нужно остановиться)))
а, дисплеи.... ну наверное. только мой ili9341 320*240 понимает и на стм32, чуть быстрее)) ты учти, что эти дисплеи были для ранних видеокамер, они гребут как надо. а про ч/б - только маты. ну юзайте не 20, а 10-ти летней давности дисплеи... я потом психану и выложу очень быструю погремушку. 50+ ФПС и это со спектром.... просто, чтоб люди поняли, там нечего бояться.... ну, даташыт не помешает понять...

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

а где раздобыть версию с рабочим медленным осциллографом?

AIM2104
Offline
Зарегистрирован: 24.02.2020

Всеь привет!!! Ребята помогите уточнить, Пультоскоп какие значение показываеть на вольта,т. е . Пик-Пик(Vpp) или Пик(Vp) или среднеквадратичное(Vrms) напряжение? Нужно измерить ВЧ сигнал через диодный детектор.

Alex-Bee
Offline
Зарегистрирован: 13.03.2020

Пока тут идет "баловство", некоторые уже продают это чудо :)
Уж больно картинка на экране похожа на ту, что у Electronik83.
Понравилось, как он вместо трёх кнопок колёсико применил, как на китайских Bluetooth-модулях...
http://www.tindie.com/products/phoenixcnc/olediscope-an-oscilloscope-for-your-pocket/


 

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

Alex-Bee пишет:

Пока тут идет "баловство", некоторые уже продают это чудо :)
Уж больно картинка на экране похожа на ту, что у Electronik83.

Тихо сп***дил и ушёл, называется нашёл!!! Пускай покупают те кто паять не могают ))). за 15$ можно два на коленке собрать с удовольствием или дсо138 взять, один фиг там корпуса нет. Ну молодец что платы себе нарисовал.

Alex-Bee
Offline
Зарегистрирован: 13.03.2020

Del

Alex-Bee
Offline
Зарегистрирован: 13.03.2020

Так если бы хоть за 15$. У меня показывает $43.95 (3 039,84 ₽ RUB)
Даже нет ссылки на первоисточник... "Мой проект..."
Сразу мысль мелькнула, что если бы кто-то, типа AlexGyver, сделал разводку подобной платы (там ведь просто Ардуинка перенесена на свою плату с прорезью под SSD1306) и подарил китайцам, то можно было бы его уже купить за 2-3... ну пусть 5$. У него там связи есть. Лампы его очень даже продаются. Думаю, покупали были бы...

AIM2104
Offline
Зарегистрирован: 24.02.2020

Кто может ответить,помогите!!!!
Пультоскоп какие значение показываеть на вольта,т. е . Пик-Пик(Vpp) или Пик(Vp) или среднеквадратичное(Vrms) напряжение?
Нужно измерить ВЧ сигнал через диодный детектор.

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

ну поделитесь плиииз прошивкой в которой работает медленный осциллограф. можно на дисплее от нокия или олед.

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

блин а еще я хочу такой же трехпозиционный джойстик как у этого плагиатора 

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

Andry Smart пишет:

блин а еще я хочу такой же трехпозиционный джойстик как у этого плагиатора 

А чем плох 4х позиционный джойстик от сотовых?Цена копейки и места занимает минимум места .https://aliexpress.ru/item/32969557581.html

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

Del

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

ну не плохая штука конечно но для моих целей нужен именно такой как у плагиатора. с боковым выходом.

 

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

Alex-Bee
Offline
Зарегистрирован: 13.03.2020

Andry Smart, это то, что вам надо? (вариант подешевле). Есть такие же, но дороже.
 

https://a.aliexpress.com/_BORXXB

 

Вот еще дешевле, US $0.64 за 10 шт.
https://aliexpress.ru/item/4000697123862.html

Alex-Bee
Offline
Зарегистрирован: 13.03.2020

Andry Smart, если собираетесь "плагиатить" у плагиатора, то вот ещё "его" программатор к "его" осциллоскопу, за 13$...
Покупаете на Али готовый мини переходник и припаиваете к нему, от туда же (с Али), пружинные контакты. Сэкономите около 10$, как минимум.