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

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

 Пришёл маленький экран SSD1306 и вот что получилось, собрал из двух прошивок одну. Почти всё работает на автомате. Скетч нашел в конце 40-вых начало 50-тых страниц. Очень хотелось бы переписать этот скетч под PCD8544, но не умею(((. А 6210 отложу пока До лучших времён или соберу на нём что-нибудь другое. Спасибо bulat943 и Electronik83 за труды и bodriy2014 с остальными тоже.

Удалил только терминал и переделал меню осцила.

#include <FreqCount.h> 
#include <PWM.h>
// ---UserSpace------------------------------------------------------------------------ //
#define ssd1306_sda  A4//2 // пин для подключения шины SDA дисплея (можно любой пин)
#define ssd1306_scl  A5//3 // пин для подключения шины SCL дисплея (можно любой пин) 
#define key_down    11 // кнопка минус (можно любой пин)
#define key_ok      12 // кнопка ОК (можно любой пин)
#define key_up      13 // кнопка плюс (можно любой пин)
#define akb         A3 // любой свободный аналоговый пин для измерения напряжения АКБ 
#define overclock   16 // частота на которой работает Ардуино
float VCC       = 5.0; // напряжение питания, меряем мультиметром
// ---UserSpace_end-------------------------------------------------------------------- //
#define led  9         // пин для генератора сигналов (не менять)
#define dds  10        // пин для генератора dds (не менять)
//        A3    !!     // пин подключиния входа осцилла (для отображения формы сигнала)
//        D5    !!     // пин подключиния входа осцилла (для аппаратного измерения частоты сигнала 
//                                                              через таймер (в FreqCount.h))
// глобальные переменные
bool flag_key; // нужно для разных подключений кнопок
byte mode=0;   // режим работы прибора
byte menu=0;   // подменю
// для осциллоскопа
#define BUFSIZE 356   // размер буфера 256 // 701
byte adcBuf[BUFSIZE]; // забираем память под буфер осцилла
byte syncLevel=30;    // уровень синхронизации 0 до 255 
byte razv=4;          // развертка  
bool pause=0;         // флаг режима паузы
bool opornoe=0;       // флаг опорного напряжения
int  prokr=0;         // прокрутка
int osc=150;          // авто вкл. режима осциллографа
long count=0;         // для подсчета частоты сигнала
int kdel=5;           //bulat  автопредел 1,1/0,22 вольта
bool magnify=0;
// для генератора
unsigned long stepFreq;
int PWM = 50;         // стартовое значение ШИМ от 0 до 100        
unsigned long freq = 500;      // int32_t стартовое значение частоты в Гц
// для DDS-генератора
byte ddsCount=0;
PROGMEM const byte sinM[]     = {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};
PROGMEM const byte pilaM[]    = {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};
PROGMEM const byte RpilaM[]   = {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};
PROGMEM const byte trianglM[] = {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};
// ---------------------------------
void setup() {
  // автоматическое определение подключение кнопок
  byte key_test=0;
  // подтянули кнопки к питанию
  digitalWrite(key_up, HIGH); digitalWrite(key_down, HIGH); digitalWrite(key_ok, HIGH); 
  // автодетект подключения кнопок при включении..
  if (digitalRead(key_up))   key_test++; // если подтяжка к питанию осталась (не стоят резюки на массу) - плюсуем
  if (digitalRead(key_down)) key_test++; // если подтяжка к питанию осталась (не стоят резюки на массу) - плюсуем
  if (digitalRead(key_ok))   key_test++; // если подтяжка к питанию осталась (не стоят резюки на массу) - плюсуем
  if (key_test>1) { // определили подключение кнопок на массу
    flag_key=0;   
  }
  else { // определили подключение кнопок на питание + резюки на массу
    digitalWrite(key_up, LOW); digitalWrite(key_down, LOW); digitalWrite(key_ok, LOW); // убрали подтяжку 
    flag_key=1;
  }
  // если автодетект работает неверно (менюха скачет постоянно), то нужно расскомментировать одну из следующих строчек:
  // flag_key = 0; // кнопки просто подключены к земле, резюков нету.
  // flag_key = 1; // кнопки подключены к питанию и резюки на землю
  
  // Выводим главное меню...    
  ssd1306_init(); // инит дисплея
  while(flag_key-digitalRead(key_ok))  { // выводим меню, пока не выбран режим работы прибора  
////////////////////// Если не нажата 10 сек кнопка ок вход в режим осцилоографа автоматом
   osc--;
    if (osc==0) break;
//////////////////////
    ssd1306_clear();
    ssd1306_set_inv(mode == 0);
    ssd1306_set_pos(25 , 0); ssd1306_string(" ОСЦИЛЛОГРАФ ");
    ssd1306_set_inv(mode == 1);
    ssd1306_set_pos(19 , 2); ssd1306_string(" PWM-генератор ");
    ssd1306_set_inv(mode == 2);
    ssd1306_set_pos(19 , 4); ssd1306_string(" DDS-генератор ");
    ssd1306_set_inv(mode == 3);
    ssd1306_set_inv(0);
    ssd1306_set_pos(25, 7); ssd1306_string("Батарея ");
    ssd1306_string(analogRead(akb)*VCC/1024, 2);
    ssd1306_char('v');
    // перемещаемся по меню  
    if (flag_key-!digitalRead(key_up)  ) { mode++; delay(100); }
    if (flag_key-!digitalRead(key_down)) { mode--; delay(100); }
    if (mode == 0xFF) mode = 2;
    if (mode>2) mode=0;
    //delay(50);  
    ssd1306_refresh(); // выводим все на экран
  }
  // выбран режим работы...
  if (mode==0) FreqCount.begin(1000);
  if (mode==1) { InitTimersSafe(); SetPinFrequencySafe(led, freq); }
  if (mode==2) { InitTimersSafe(); SetPinFrequencySafe(led,200000);     }
  delay(200);
}
// читаем с АЦП данные и помещаем их в буфер...   
void ReadAdc() {
  if (razv) { // (razv>0)           // если развертка без задержек всяких
    ADCSRA = 0b11100000 | (8-razv); // установили делитель (/2 - не работает, так что начинаем с /4)
    for(int i=0; i<BUFSIZE; i++) {  // цикл для чтения  
      while ((ADCSRA & 0x10)==0);   // ждем готовность АЦП
      ADCSRA|=0x10;                 // запускаем следующее преобразование
      adcBuf[i]=ADCH;               // записываем данные в массив
    }
  } else {                          // развертка с задержками (delay)
    ADCSRA = 0b11100111;            // делитель на /128
    for(int i=0; i<BUFSIZE; i++) {  // цикл для чтения
      while ((ADCSRA & 0x10)==0);   // ждем готовность АЦП
      ADCSRA|=0x10;                 // запускаем следующее преобразование
      delayMicroseconds(500);       // делаем задержку
      adcBuf[i]=ADCH;               // записываем данные в массив
    }
  }
}
// безконечный цикл
void loop() {
  if(mode==0) Oscill();       // "выпадаем" в осциллоскоп  
  if(mode==1) Generator();    // "выпадаем" в генератор 
  if(mode==2) DDSGenerator(); // "выпадаем" в DDS генератор
//  if(mode==3) UARTterminal(); // "выпадаем" в USART приемник
}
///////////// РЕЖИМ ОСИЛЛОСКОПА //////////////
void Oscill() {
  if(opornoe) ADMUX = 0b01100011; // выбор внешнего опорного
         else ADMUX = 0b11100011; // выбор внутреннего опорного 1,1В
 label_ReadAdc:

  delay(5);
  if(!pause) ReadAdc(); // если в режиме паузы, то буфер не обновляем
  // определение точки синхронизации
  bool flagSync=0;    // флаг, когда сигнал ниже уровня синхронизации
  int syncOffset=0;   // смещение для вывода с синхронизацией
  for(int y=0; y<BUFSIZE-122; y++) {
    if(adcBuf[y]<syncLevel) flagSync=1;
    if(flagSync && adcBuf[y]>syncLevel) { syncOffset = y; break; } 
  }
  // считаем максимальное и минимальное значение сигнала (для вывода на экран)
  byte Vmax=0, Vmin=255; // тут будем хранить максимальное и минимальное напряжение 
  for(int y=0; y<BUFSIZE; y++) { if(Vmin>adcBuf[y]) Vmin=adcBuf[y]; if(Vmax<adcBuf[y]) Vmax=adcBuf[y]; }
  syncLevel = (Vmax-Vmin) / 2 + Vmin;
//bulat если зашкаливает включаем предел 5 в  
  if (Vmax==255){
    if (opornoe==0)
       {
        opornoe=1;
       ADMUX = 0b01100011;// выбор внутреннего опорного 5.0 В
           goto   label_ReadAdc;
     }
  }
//bulat если  5 в  и уровень менее 1,1 в то вкл предел 1,1 в
  if (Vmax<=55){
    if (opornoe==1)
       {
        opornoe=0;
        ADMUX = 0b11100011;// выбор внутреннего опорного 1,1В
        goto   label_ReadAdc;
     }
  }
  //bulat здесь автопредел 1,1/0,22 в,программный
  kdel=5;
  if (Vmax<=55){
    if (opornoe==0)
       {
       kdel=1;
     }
  }
  //////// отрисовка графика//////// 
  ssd1306_clear();
  byte x=3; // небольшое смещение формы сигнала вправо
  if(pause) { // если в режиме паузы, то рисуем с прокруткой...
    ssd1306_draw_line(0,8,127,8); // линия шкалы прокрутки
    ssd1306_draw_line(prokr/1.9,9 ,prokr/1.9+6,9 ); //шкала прокрутки
    ssd1306_draw_line(prokr/1.9,10,prokr/1.9+6,10); //шкала прокрутки // я тут долго шаманил, но потом вспомнил про функцию map()! тут её надо использовать!
    for(int y=prokr; y<prokr+122; y++) { // рисуем форму сигнала
    // ssd1306_draw_line(++x, 63-adcBuf[y], x, 63-adcBuf[y+1]);
      ssd1306_draw_line(++x, 63-adcBuf[y]/kdel, x, 63-adcBuf[y+1]/kdel);
    }
  } else {  // если паузы нет, то рисуем немного по другому...
    //// рисуем форму сигнала     
    for(int y=syncOffset; y<syncOffset+122; y++) {  
      if (magnify) ssd1306_draw_line(++x, 63-map(adcBuf[y], Vmin, Vmax, 0, 51), x, 63-map(adcBuf[y+1], Vmin, Vmax, 0, 51));  
    else ssd1306_draw_line(++x, 63-adcBuf[y]/kdel, x, 63-adcBuf[y+1]/kdel);
    }
    syncOffset=0;
  }
  ////////////////// отрисовка сетки
  for(byte i=63;i>7;i-=10) { ssd1306_draw_pixel(0, i); ssd1306_draw_pixel(1, i); ssd1306_draw_pixel(2, i); }
  for(byte i=63;i>7;i-=3)  { ssd1306_draw_pixel(21,i); ssd1306_draw_pixel(42,i);  ssd1306_draw_pixel(63,i);
                             ssd1306_draw_pixel(84,i); ssd1306_draw_pixel(105,i); /*ssd1306_draw_pixel(126,i);*/ }
  for(byte i=3;i<127;i+=3) { ssd1306_draw_pixel(i,23); ssd1306_draw_pixel(i,43); }
  
  ///////////////////// отрисовка menu///////////////////
  ssd1306_set_pos(0,0);
  if(menu==0) {
    if(opornoe) 
       {
        ssd1306_string(VCC,1);
        }
        else 
        { 
        if(kdel==5)ssd1306_string("1.1");else ssd1306_string("0.2");
        }
    ssd1306_char(' ');
    ssd1306_set_inv(1); 
    ssd1306_char(' ');
    ssd1306_string(razv);
//    ssd1306_char(' '); 
    ssd1306_set_inv(0);
    ssd1306_string(" П ");
    ssd1306_char(magnify?'+':'='); 
    ssd1306_char(' ');
    if(flag_key-!digitalRead(key_down)) { razv--; if(razv==255) razv=6; }
    if(flag_key-!digitalRead(key_up)  ) { razv++; if(razv==7)   razv=0; }
  }
  if(menu==1) {
    ssd1306_char(' ');
    if(opornoe) ssd1306_string(VCC,1); else ssd1306_string("1.1");
    ssd1306_string(" "); ssd1306_string(razv);
    ssd1306_set_inv(1);
    ssd1306_string(" п ");
    ssd1306_set_inv(0);
    ssd1306_char(magnify?'+':'='); 
    ssd1306_char(' ');
    pause=1;
    if(flag_key-!digitalRead(key_down)) { prokr-=10; if(prokr<0)   prokr=0;   }
    if(flag_key-!digitalRead(key_up)  ) { prokr+=10; if(prokr>(BUFSIZE-127)) prokr=BUFSIZE-127; }     
  }
  if(menu==2) {
    prokr=0;
    pause=0;
//    ssd1306_char(' ');
    if(opornoe) 
       {
        ssd1306_string(VCC,1);
        }
        else 
        { 
        if(kdel==5)ssd1306_string("1.1");else ssd1306_string("0.2");
        }
    ssd1306_char(' '); ssd1306_string(razv);
    ssd1306_string(" П ");
    ssd1306_set_inv(1);
    ssd1306_char(' ');
    ssd1306_char(magnify?'+':'='); 
//    ssd1306_char(' ');
    ssd1306_set_inv(0);
    ssd1306_string(" ");
    if(flag_key-!digitalRead(key_down) || flag_key-!digitalRead(key_up)) magnify = !magnify;
  }
  if (flag_key-!digitalRead(key_ok)) { menu++; if(menu==3) { menu=0; pause=0; }} //перебор меню
  
  if (FreqCount.available()) count = FreqCount.read(); //вывод частоты по готовности счетчика
  // считаем программно частоту сигнала
  byte Freq1=0;
  long Freq=0;
  bool flagFreq1=0, flagFreq2=0, flagFreq3=0;
  for(int y=1; y<255; y++) {
    if(!flagFreq1 && adcBuf[y]<syncLevel) flagFreq2=1; // тут можно че то оптимизировать, тока не могу понять, что [El83]   
    if(!flagFreq1 && flagFreq2 && adcBuf[y]>syncLevel) { flagFreq1=1; Freq1=y; }    
    if( flagFreq1 && adcBuf[y]<syncLevel) flagFreq3=1;    
    if( flagFreq3 && adcBuf[y]>syncLevel) {      
      switch (razv) {
        case 6: Freq=1000000/((y-Freq1-1)*3.26);  break; // делитель 4
        case 5: Freq=1000000/((y-Freq1)*3.26)/2;  break; // делитель 8
        case 4: Freq=1000000/((y-Freq1)*3.26)/4;  break; // делитель 16
        case 3: Freq=1000000/((y-Freq1)*3.26)/8;  break; // делитель 32
        case 2: Freq=1000000/((y-Freq1)*3.26)/16; break; // делитель 64
        case 1: Freq=1000000/((y-Freq1)*3.26)/32; break; // делитель 128
        case 0: Freq=1000000/((y-Freq1)*512);     break; // делитель 128
      }  
      flagFreq1=0; flagFreq3=0;
    }
  }
  // отрисовка частоты сигнала (выбрал 2.7, т.к. было не правильно на 2.5) EL83
  if(opornoe && ((Vmax*VCC/255)>2.7)) count=count*(overclock/16.0); else count=Freq*(overclock/16.0);
  //if (Vmax*VCC/255<0.29) count = 0; // подумать, нужно ли....
  if(count<1000) { ssd1306_string(count       ); ssd1306_string("Hz"); }
            else { ssd1306_string(count/1000.0,3); ssd1306_string("KHz"); }
  if(opornoe && ((Vmax*VCC/255)>2.7)) ssd1306_char('*'); // если меряем частоту аппаратно, рисуем звездочку
  // отрисовка максимального напряжения сигнала
  ssd1306_set_pos(0, 1); 
  if(opornoe) ssd1306_string(Vmax*VCC/255, 2); else ssd1306_string(Vmax*1.1/255, 2);
  // отрисовка минимального напряжения сигнала
  ssd1306_set_pos(0, 7); 
  if(opornoe) ssd1306_string(Vmin*VCC/255, 2); else ssd1306_string(Vmin*1.1/255, 2);
  //delay(200);  
  ssd1306_refresh(); // вываливаем все на экран
}

// РЕЖИМ ГЕНЕРАТОРА
void Generator() {
  ssd1306_clear();   
  if (menu==6) {  // если меняем ШИМ
    if(flag_key-!digitalRead(key_down)){
      PWM=PWM-1;
      if(PWM<0) PWM=100; 
      delay(100); // задержка от дребезга
    }
    if(flag_key-!digitalRead(key_up)){
      PWM=PWM+1;
      if(PWM>100) PWM=0; 
      delay(100);//защита от дребезга 
    }  
  } else { // если меняем частоту
    if(flag_key-!digitalRead(key_down)) {
      freq-=stepFreq;
      if(freq<0) freq=0; 
      SetPinFrequencySafe(led, freq);
      delay(100); // задержка от дребезга 
    }
    if(flag_key-!digitalRead(key_up)){
      freq+=stepFreq;
      SetPinFrequencySafe(led, freq);
      delay(100); // задержка от дребезга 
    }  
  }       
  if(flag_key-!digitalRead(key_ok)) { // переключение разряда выбора частоты 
    delay(100);//защита от дребезга
    menu++;
    if(menu>=7) menu=0;
  } 
  // выводим уровень ШИМ
//  display.setTextSize(1);
  ssd1306_set_pos(43, 1); ssd1306_string("PWM="); ssd1306_string(PWM); ssd1306_string("%");
  // рисуем полосочки уровня ШИМ
  ssd1306_draw_line(63-(63*PWM/100.0),  0, 64+(63*PWM/100.0),  0);
  ssd1306_draw_line(64-(63*PWM/100.0),  1, 63+(63*PWM/100.0),  1);
  ssd1306_draw_line(65-(63*PWM/100.0),  2, 62+(63*PWM/100.0),  2);
  ssd1306_draw_line(63-(63*PWM/100.0), 23, 64+(63*PWM/100.0), 23);
  ssd1306_draw_line(64-(63*PWM/100.0), 22, 63+(63*PWM/100.0), 22);
  ssd1306_draw_line(65-(63*PWM/100.0), 21, 62+(63*PWM/100.0), 21);
  // готовимся для вывода частоты    
  //  display.setTextSize(2);
  float freqX=freq*(overclock/16.0);
  // рисуем частоту
  /*if(freqX<1000)  { 
    ssd1306_set_pos(46, 4); ssd1306_string(freqX, 0); 
  //  display.setTextSize(1); 
    ssd1306_string("Hz"); 
  } else { 
  ssd1306_set_pos(42, 4);
  if(freqX<100000)  ssd1306_string(freqX/1000.0, 2);
  //  display.setTextSize(1); 
  ssd1306_string("KHz"); }*/
  /*if (freqX>1000000) { } else  
  if (freqX>100000)  { } else
  if (freqX>10000)   { } else
  if (freqX>1000)*/
  // 123.456
  //ssd1306_set_pos((menu<3?42:48)+menu*6, 3); ssd1306_string("#");
  // 543210  6 = pwm
  ssd1306_set_pos(42, 4);  ssd1306_string(freqX, 0);  ssd1306_string(" Hz"); 
    // выводим строку статуса (режима)
  ssd1306_set_pos(0,7);
  //  display.setTextSize(1);
  ssd1306_string(">>");
  ssd1306_set_inv(1);
  ssd1306_string(" ");
  if(menu<7) ssd1306_string("x");    
  if(menu==0) { // выбор множителя частоты        
    ssd1306_string(1*(overclock/16.0),1); 
    stepFreq=1;
  }
  if(menu==1){//выбор множителя частоты
    ssd1306_string(10*(overclock/16.0),0); 
    stepFreq=10;
  }
  if(menu==2) { //выбор множителя частоты
    ssd1306_string(100*(overclock/16.0),0); 
    stepFreq=100;
  }
  if(menu==3) { //выбор множителя частоты
    ssd1306_string(1000*(overclock/16.0),0); 
    stepFreq=1000;
  } 
  if(menu==4) { //выбор множителя частоты
    ssd1306_string(10000*(overclock/16.0),0); 
    stepFreq=10000;
  } 
  if(menu==5) { //выбор множителя частоты
    ssd1306_string(100000*(overclock/16.0),0); 
    stepFreq=100000;
  } 
  if(menu==6) { //выбор  PWM
    ssd1306_string("PWM ");
    ssd1306_string(PWM);
    ssd1306_string("%"); 
  } 
  ssd1306_string(" ");
  ssd1306_set_inv(0);
  ssd1306_string("<<");
  // выставили ШИМ
  pwmWrite(led, PWM*2.55);
  delay(100);
  ssd1306_refresh();
}

// Рисуем меню DDS-генератора
void DDSGeneratorMenu() {
  ssd1306_clear(); 
  if (menu==0) ssd1306_set_inv(1); else ssd1306_set_inv(0);
  ssd1306_set_pos(43, 0); ssd1306_string(" Синус ");
  if (menu==1) ssd1306_set_inv(1); else ssd1306_set_inv(0);
  ssd1306_set_pos(25, 2); ssd1306_string(" Треугольник ");
  if (menu==2) ssd1306_set_inv(1); else ssd1306_set_inv(0);
  ssd1306_set_pos(46, 4); ssd1306_string(" Пила ");
  if (menu==3) ssd1306_set_inv(1); else ssd1306_set_inv(0);
  ssd1306_set_pos(34, 6); ssd1306_string(" АнтиПила ");
  ssd1306_set_inv(0);
  ssd1306_refresh();
}

// DDS генератор
void DDSGenerator() {
  if(menu==0) {
    DDSGeneratorMenu();    
    while(flag_key-digitalRead(key_up) && flag_key-digitalRead(key_down) && flag_key-digitalRead(key_ok)) {
      PWM = pgm_read_byte(&sinM[ddsCount]);
      pwmWrite(dds,PWM);  if(ddsCount++==32) ddsCount=0;
    }
    menu++; delay(200);
  }
  if(menu==1) {
    DDSGeneratorMenu();    
    while(flag_key-digitalRead(key_up) && flag_key-digitalRead(key_down) && flag_key-digitalRead(key_ok)) {
      PWM = pgm_read_byte(&trianglM[ddsCount]);
      pwmWrite(dds,PWM);  if(ddsCount++==32) ddsCount=0;
    }
    menu++; delay(200);
  }
  if(menu==2) {
    DDSGeneratorMenu();    
    while(flag_key-digitalRead(key_up) && flag_key-digitalRead(key_down) && flag_key-digitalRead(key_ok)) {
      PWM=pgm_read_byte(&pilaM[ddsCount]);
      pwmWrite(dds,PWM);  if(ddsCount++==32) ddsCount=0; 
    }
    menu++;  delay(200);
  }
  if(menu==3) {
    DDSGeneratorMenu();    
    while(flag_key-digitalRead(key_up) && flag_key-digitalRead(key_down) && flag_key-digitalRead(key_ok)) {
      PWM = pgm_read_byte(&RpilaM[ddsCount]);
      pwmWrite(dds,PWM);  if(ddsCount++==32) ddsCount=0; 
    }
    menu=0;  delay(200);
  }
}

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

Ха, моя прошивка всплыла)))))
У меня такой экран почему то перестал работать...

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

Пока работает экран от 5в., но потом запитаю от 3,3, мало ли что. А прошивачка зачёт, посмотри, может я там что накосячил. Последние прошивки не то по сравнению с этой, уж очень хорош этот автоматический режим с минимальным управлением. Жалко что забросили и забыли.

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

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

Sanyaba
Sanyaba аватар
Offline
Зарегистрирован: 27.07.2015

Братцы подскажите пожалуйста как быть, указал в коде #define overclock 25 и прошился, запаял кварц на 25MHz, пультоскоп работает без проблем но есть проблема при выборе генератора: в коде указанна начальная частота генератора 500Hz а по факту сразу установленно 781Hz, далее шаг стал кратным 1.56 вместо 1, то есть к примеру добавляю 1KHz а добавлятеся 1.56KHz

//Страница проэкта  http://srukami.inf.ua/pultoscop_v25110.html
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
#include <FreqCount.h> 
#include <PWM.h>
#include <CyberLib.h>
#define led  9   //пин для генератора сигналов (не менять)
#define dds  10   //пин для генератора dds (не менять)
//#################№№№№№№####пользовательские настройки
#define power 8 //пин который опрашивает кнопку включения
#define OFF 14//пин который управляет ключем питания
#define  timepowerON 50 //время удержания кнопки выключения
#define levo 13  //кнопка ЛЕВО(можно любой пин)
#define ok 12    //кнопка ОК(можно любой пин)
#define pravo 11 //кнопка ПРАВО(можно любой пин)
#define akb A5 //любой своюодный аналоговый пин для измерения напряжения АКБ 
#define overclock 25  //Частота на которой работает Ардуино 
Adafruit_PCD8544 display = Adafruit_PCD8544(7, 6, 4, 3, 2);//пины к которым у вас подключен дисплей         
byte cont=50;//контрастность дисплея
byte SinU=30;   //уровень синхронизации 0 до 255 
int PWM = 128;//стартовое значение ШИМ от 0 до 255        
int32_t frequency = 500; //стартовое значение частоты в Гц
float VCC=5.0;  //напряжение питания, меряем мультиметром
//###########################################################
int d=0;
byte menuDDS=0; 
byte sinM[32]={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};
byte pilaM[32]={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};
byte RpilaM[32]={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};
byte trianglM[32]={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};
int powerON=0;//состояние кнопки питания
byte hag=0;
int mnog=0;
boolean flag=0;
byte mass[701];
byte x=0; 
byte menu=0;//переменная выбора меню 
bool opornoe=1; //флаг опорного напряжения
bool paus=0; //флаг режима паузы
byte pultoskop=0; //флаг выбора генератора или осциллографа
byte razv=6;  
unsigned long count =0;
byte sinX=30; 
byte meaX=83;
int Vmax=0;// максимальное напряжение 
byte sinhMASS=0;
long countX=0;
long speedTTL=9600; //скорость терминала 
int prokr=0;
void setup(){
pinMode(A4,INPUT);
digitalWrite(OFF,HIGH);//включем питание 
//Serial.begin(9600);
display.begin();
display.setContrast(cont);  
while(digitalRead(ok)==LOW){  
/////////////////////////////////////////удержание кнопки отключения
if(digitalRead(power)==HIGH){powerON++;delay(10);}  
if(powerON>=timepowerON){digitalWrite(OFF,LOW);}///отключаем питание
/////////////////////////////////////////удержание кнопки отключения 
  if(pultoskop==0){
    display.clearDisplay();
    display.setCursor(10,0);
    display.setTextColor(WHITE, BLACK); // 'inverted' text
    display.println("Пультоскоп");
    display.setCursor(10,10);
    display.setTextColor(BLACK); 
    display.println("Генератор");
    display.setCursor(10,20);
    display.println("DDSгенератор");
    display.setCursor(10,30);
    display.println("Терминал");
    display.setCursor(0,40);
    display.print("Батарея=");
    display.print(analogRead(akb)*5.0/1024);
    display.print("В");    
  }
    if(pultoskop==1){
    display.clearDisplay();
    display.setCursor(10,0);
    display.setTextColor(BLACK); // 'inverted' text
    display.println("Пультоскоп");
    display.setCursor(10,10);
    display.setTextColor(WHITE, BLACK); // 'inverted' text
    display.println("Генератор");
     display.setTextColor(BLACK); // 'inverted' text;
    display.setCursor(10,20);
    display.println("DDSгенератор");
    display.setCursor(10,30);
    display.println("Терминал");
    display.setCursor(0,40);
    display.setTextColor(BLACK); 
    display.print("Батарея=");
    display.print(analogRead(akb)*5.0/1024);
    display.print("В");   
  } 
    if(pultoskop==2){
    display.clearDisplay();
    display.setCursor(10,00);
    display.setTextColor(BLACK); // 'inverted' text
    display.println("Пультоскоп");
    display.setCursor(10,10);
    display.println("Генератор");
    display.setTextColor(WHITE, BLACK); // 'inverted' text;
    display.setCursor(10,20);
    display.println("DDSгенератор");
    display.setTextColor(BLACK);
    display.setCursor(10,30);
    display.println("Терминал");
    display.setCursor(0,40);
    display.setTextColor(BLACK); 
    display.print("Батарея=");
    display.print(analogRead(akb)*5.0/1024);
    display.print("В");   
    }
        if(pultoskop==3){
    display.clearDisplay();
    display.setCursor(10,00);
    display.setTextColor(BLACK); // 'inverted' text
    display.println("Пультоскоп");
    display.setCursor(10,10);
    display.println("Генератор");
    display.setTextColor(BLACK);
    display.setCursor(10,20);
    display.println("DDSгенератор");
    display.setTextColor(WHITE, BLACK);
    display.setCursor(10,30);
    display.println("Терминал");
    display.setCursor(0,40);
    display.setTextColor(BLACK); 
    display.print("Батарея=");
    display.print(analogRead(akb)*5.0/1024);
    display.print("В");   
    }
if(digitalRead(levo)==HIGH){delay(300);pultoskop=pultoskop+1;}
if(digitalRead(pravo)==HIGH){delay(300);pultoskop=pultoskop-1;}
if(pultoskop>3){pultoskop=0;}
delay(50);  
display.display(); }
if(pultoskop==2){InitTimersSafe(); bool success = SetPinFrequencySafe(led,200000);}
if(pultoskop==0){FreqCount.begin(1000);}
if(pultoskop==1){InitTimersSafe(); bool success = SetPinFrequencySafe(led, frequency);}
display.setTextColor(BLACK);
delay(500); }   
void Zamer(){
  if (razv>=6){ADCSRA = 0b11100010;}//delitel 4
  if (razv==5){ADCSRA = 0b11100011;}//delitel 8
  if (razv==4){ADCSRA = 0b11100100;}//delitel 16
  if (razv==3){ADCSRA = 0b11100101;}//delitel 32
  if (razv==2){ADCSRA = 0b11100110;}//delitel 64
  if (razv<2){ADCSRA = 0b11100111;}//delitel 128
  if (razv==0){
      for(int i=0;i<700;i++){ 
          while ((ADCSRA & 0x10)==0);
          ADCSRA|=0x10;
          delayMicroseconds(500);
          mass[i]=ADCH;
      }
  }
  if (razv>0){
      for(int i=0;i<700;i++){ 
          while ((ADCSRA & 0x10)==0);
          ADCSRA|=0x10;
          mass[i]=ADCH;
      }
  }
  
}
void loop() {
/////////////////////////////////////////удержание кнопки отключения
if(digitalRead(power)==HIGH){powerON++;delay(10);}  
if(powerON>=timepowerON){digitalWrite(OFF,LOW);}///отключаем питание
/////////////////////////////////////////удержание кнопки отключения    
if(pultoskop==0){  
if(opornoe==0){ADMUX = 0b11100011;}//выбор внутреннего опорного 1,1В
if(opornoe==1){ADMUX = 0b01100011;}//Выбор внешнего опорного
delay(5);
if(paus==0){Zamer();}
//#######################################определение точки синхронизации
bool flagSINHRO=0;
bool flagSINHRnull=0;
for(int y=1;y<255;y++){
  if(flagSINHRO==0){if(mass[y]<SinU){flagSINHRnull=1;}}
  if(flagSINHRO==0){if(flagSINHRnull==1){if(mass[y]>SinU){flagSINHRO=1;sinhMASS=y;}}}}
//#######################################определение точки синхронизации
//максимальное значение сигнала##########################
Vmax=0; 
for(int y=1;y<255;y++){if(Vmax<mass[y]){Vmax=mass[y];} }
//максимальное значение сигнала##########################
//#######################################определение точки синхронизации
//#######################################отрисовка графика 
if(paus==0){
display.clearDisplay();
display.fillCircle(80,47-SinU/7, 2, BLACK);//рисуем уровень синхронизации    
x=3;
for(int y=sinhMASS;y<sinhMASS+80;y++){
      if(razv<7){x++;}
      if(razv==7){x=x+2;}
      if(razv==8){x=x+3;} 
      display.drawLine(x, 47-mass[y]/7, x+1, 47-mass[y+1]/7, BLACK);
      display.drawLine(x+1, 47-mass[y]/7+1, x+2, 47-mass[y+1]/7+1, BLACK);        
}
sinhMASS=0;}
if(paus==1){
display.clearDisplay();
display.drawLine(prokr/8,8,prokr/8+6,8, BLACK);//шкала прокрутки
display.drawLine(prokr/8,9,prokr/8+6,9, BLACK);//шкала прокрутки
          x=3;
          for(int y=prokr;y<prokr+80;y++){
                if(razv<7){x++;}
                if(razv==7){x=x+2;}
                if(razv==8){x=x+3;} 
                display.drawLine(x, 47-mass[y]/7, x+1, 47-mass[y+1]/7, BLACK);
                display.drawLine(x+1, 47-mass[y]/7+1, x+2, 47-mass[y+1]/7+1, BLACK); 
          }}
//#######################################отрисовка графика
for(byte i=47;i>5;i=i-7){display.drawPixel(0,i, BLACK);display.drawPixel(1,i, BLACK);display.drawPixel(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);}
//////////////////////////////////////////////////сетка
//#######################################отрисовка menu
if(menu==0){
    display.setCursor(0,0);
    display.setTextColor(WHITE,BLACK);
    if(opornoe==0){display.print("1.1");}
    if(opornoe==1){display.print(VCC,1);}
    display.setTextColor(BLACK); 
    display.print(" ");
    display.print(razv);
    display.print(" P");
    if(digitalRead(levo)==HIGH){opornoe=!opornoe;}
    if(digitalRead(pravo)==HIGH){opornoe=!opornoe;}    
}
if(menu==1){
    display.setCursor(0,0);
    display.setTextColor( BLACK);
    if(opornoe==0){display.print("1.1");}
    if(opornoe==1){display.print(VCC,1);}
    display.setTextColor(WHITE, BLACK); // 'inverted' text 
    display.print(" ");
    display.print(razv);
    display.setTextColor( BLACK); // 'inverted' text
    display.print(" P");
    if(digitalRead(levo)==HIGH){razv=razv-1;if(razv==255){razv=0;}}
    if(digitalRead(pravo)==HIGH){razv=razv+1;if(razv==9){razv=8;}}
}
if(menu==2){
    display.setCursor(0,0);
    display.setTextColor( BLACK);
    if(opornoe==0){display.print("1.1");}
    if(opornoe==1){display.print(VCC,1);}
    display.print(" ");
    display.print(razv);
    display.setTextColor(WHITE, BLACK); // 'inverted' text 
    display.print(" P");
    paus=1;
    if(digitalRead(levo)==HIGH){prokr=prokr-10;if(prokr<0){prokr=0;}}
    if(digitalRead(pravo)==HIGH){prokr=prokr+10;if(prokr>620){prokr=620;}}    
}
if(menu==3){
    prokr=0;
    paus=0;
    display.setCursor(0,0);
    display.setTextColor( BLACK);
    if(opornoe==0){display.print("1.1");}
    if(opornoe==1){display.print(VCC,1);}
    display.print(" ");
    display.print(razv);
    display.setTextColor(BLACK);
    display.print(" P");
    if(digitalRead(levo)==HIGH){SinU=SinU-20;if(SinU<20){SinU=20;}}
    if(digitalRead(pravo)==HIGH){SinU=SinU+20;if(SinU>230){SinU=230;}}   
    display.fillCircle(80,47-SinU/7, 5, BLACK);
    display.fillCircle(80,47-SinU/7, 2, WHITE); 
}
if(digitalRead(ok)==HIGH){menu++;if(menu==4){menu=0;paus=0;}}//перебор меню
if (FreqCount.available()) { count = FreqCount.read();}//вывод частоты по готовности счетчика
//#######################################частоты сигнала
byte Frec1=0;
long Frec=0;
bool flagFrec1=0;
bool flagFrec2=0;
bool flagFrec3=0;
for(int y=1;y<255;y++){
  if(flagFrec1==0){if(mass[y]<SinU){flagFrec2=1;}}
  if(flagFrec1==0){if(flagFrec2==1){if(mass[y]>SinU){flagFrec1=1;Frec1=y;}}}
  if(flagFrec1==1){if(mass[y]<SinU){flagFrec3=1;}}
  if(flagFrec3==1){if(mass[y]>SinU){
  if (razv>=6){Frec=1000000/((y-Frec1-1)*3.27);}//delitel 4
  if (razv==5){Frec=1000000/((y-Frec1)*3.27)/2;}//delitel 8
  if (razv==4){Frec=1000000/((y-Frec1)*3.27)/4;}//delitel 16
  if (razv==3){Frec=1000000/((y-Frec1)*3.27)/8;}//delitel 32
  if (razv==2){Frec=1000000/((y-Frec1)*3.27)/16;}//delitel 64
  if (razv==2){Frec=1000000/((y-Frec1)*3.27)/32;}//delitel 128
  if (razv==1){Frec=1000000/((y-Frec1)*3.27)/32;}//delitel 128
  if (razv==0){Frec=1000000/((y-Frec1)*500);}//delitel 128
  flagFrec1=0;flagFrec3=0;}}}
//#######################################частоты сигнала
display.setTextColor( BLACK);
if(opornoe==1){
if((Vmax*VCC/255)>2.5){countX=count*(overclock/16.0);}
if((Vmax*VCC/255)<2.5){countX=Frec*(overclock/16.0);}}
if(opornoe==0){countX=Frec*(overclock/16.0);}
if(countX<1000){display.print(" ");display.print(countX);display.print("Hz");}
if(countX>=1000){float countXK=countX/1000.0;display.print(countXK,1);display.print("KHz");}
if(opornoe==1){display.setCursor(0,39);display.setTextColor(BLACK);
display.print(" ");
display.print(Vmax*VCC/255,2);}
if(opornoe==0){display.setCursor(0,39);display.setTextColor(BLACK);
display.print(" ");
display.print(Vmax*1.1/255,2);}
display.print("V");
//#######################################отрисовка menu
delay(200);  
display.display();
}
if(pultoskop==1){Generator();}
if(pultoskop==2){DDSGenerator();}
if(pultoskop==3){TTL();}
}
//#######################################режим ренератора
void Generator(){
display.clearDisplay();  
if (flag==0){//флаг выборов режима настройки ШИМ или Частоты
            if(digitalRead(levo)==HIGH){
              frequency=frequency-mnog;
              if(frequency<0){frequency=0;}
              bool success = SetPinFrequencySafe(led, frequency);
              delay(3);//защита от дребезга 
            }
            if(digitalRead(pravo)==HIGH){
              frequency=frequency+mnog;
              bool success = SetPinFrequencySafe(led, frequency);
              delay(3);//защита от дребезга 
            }  
}
if (flag==1){//флаг выборов режима настройки ШИМ или Частоты
            if(digitalRead(levo)==HIGH){
              PWM=PWM-1;
              if(PWM<0){PWM=255;} 
              delay(3);//защита от дребезга
            
            }
            if(digitalRead(pravo)==HIGH){
              PWM=PWM+1;
              if(PWM>255){PWM=0;} 
              delay(3);//защита от дребезга 
            }  
}      
if(digitalRead(ok)==HIGH){//переключение разряда выбора частоты 
  delay(3);//защита от дребезга
  hag++;
  if(hag>=6){hag=0;}
} 
////////////
display.setTextSize(1);
display.setCursor(0,5);
display.print("PWM=");
display.print(PWM*100.0/255);
display.print(" %");
display.drawLine(0,0,83*PWM/255.0,0, BLACK);
display.drawLine(0,1,83*PWM/255.0,1, BLACK);
display.drawLine(0,2,83*PWM/255.0,2, BLACK);
display.drawLine(0,15,83*PWM/255.0,15, BLACK);
display.drawLine(0,16,83*PWM/255.0,16, BLACK);
display.drawLine(0,17,83*PWM/255.0,17, BLACK);
///////////    
display.setCursor(5,20);
 display.setTextSize(2);
long frequencyX=frequency*(overclock/16.0);
if(frequencyX<1000){display.print(frequencyX);display.setTextSize(1);display.println("Hz");}
if(frequencyX>=1000){if(frequencyX<10000){display.print((frequencyX/1000.0),2);display.setTextSize(1);display.println("KHz");}}
if(frequencyX>=10000){if(frequencyX<100000){display.print((frequencyX/1000.0),1);display.setTextSize(1);display.println("KHz");}}
if(frequencyX>=100000){display.print((frequencyX/1000.0),0);display.setTextSize(1);display.println("KHz");}
display.setCursor(0,40);
display.setTextSize(1);
display.print(">>X ");    
      if(hag==0){//выбор множителя частоты
        
          //display.print(1,1); 
          mnog=1;
          display.print("1 Hz");
          flag=0;
      }
      if(hag==1){//выбор множителя частоты
          //display.print(10,0); 
          mnog=10;
          display.print("10 Hz");
      }
      if(hag==2){//выбор множителя частоты
          //display.print(100,0); 
          mnog=100;
          display.print("100 Hz");
      }
      if(hag==3){//выбор множителя частоты
          //display.print(1,0);  //display.print(1000*(overclock/16.0),0); 
          mnog=1000;
          display.print("1 KHz");
      } 
      if(hag==4){//выбор множителя частоты
          //display.print(10,0); //display.print(10000*(overclock/16.0),0)
          mnog=10000;
          display.print("10 KHz");
      } 
      if(hag==5){//выбор  PWM
          display.print("PWM ");
          display.print(PWM*100.0/255);
          display.print("%"); 
          flag=1;
      } 
display.print("<<");        
pwmWrite(led, PWM);
delay(300);  
display.display();
}
/////////////////////DDS
void DDSGenerator(){
int fr=10;
if(menuDDS==0){
    display.clearDisplay(); 
    display.setTextColor(WHITE, BLACK); // 'inverted' text
    display.setCursor(10,0);
    display.println("Синус");
    display.setTextColor(BLACK);
    display.setCursor(10,10); 
    display.println("Треугольник");
    display.setCursor(10,20);
    display.println("Пила");
    display.setCursor(10,30);
    display.println("Пила Обр");
    display.setTextColor(BLACK);
    display.setCursor(0,40);
    //display.print("Частота=");
    //display.print(57);
    //display.print("Гц"); 
    delay(100);  
    display.display();
    while(D11_Read==LOW){
      PWM=sinM[d];
      pwmWrite(dds,PWM);
      //delayMicroseconds(fr);
      d++;
      if(d==32){d=0;}}
    menuDDS++;
    delay(200);}
if(menuDDS==1){
    display.clearDisplay(); 
    display.setTextColor(BLACK); // 'inverted' text
    display.setCursor(10,0);
    display.println("Синус");
    display.setTextColor(WHITE, BLACK);
    display.setCursor(10,10); 
    display.println("Треугольник");
    display.setTextColor(BLACK);
    display.setCursor(10,20);
    display.println("Пила");
    display.setCursor(10,30);
    display.println("Пила Обр");
    display.setTextColor(BLACK);
    //display.setCursor(0,40);
    //display.print("Частота=");
   // display.print(57);
    //display.print("Гц");
    delay(100);  
    display.display();
    while(D11_Read==LOW){
      PWM=trianglM[d];
      pwmWrite(dds,PWM);
      //delayMicroseconds(fr);
      d++;
      if(d==32){d=0;}}
    menuDDS++;
    delay(200);}
if(menuDDS==2){
    display.clearDisplay(); 
    display.setTextColor(BLACK); // 'inverted' text
    display.setCursor(10,0);
    display.println("Синус");
    display.setTextColor(BLACK);
    display.setCursor(10,10); 
    display.println("Треугольник");
    display.setTextColor(WHITE, BLACK);
    display.setCursor(10,20);
    display.println("Пила");
    display.setTextColor(BLACK);
    display.setCursor(10,30);
    display.println("Пила Обр");
    display.setTextColor(BLACK);
    //display.setCursor(0,40);
    //display.print("Частота=");
   // display.print(57);
    //display.print("Гц");
    delay(100);  
    display.display();
    while(D11_Read==LOW){
      PWM=pilaM[d];
      pwmWrite(dds,PWM);
     // delayMicroseconds(fr);
      d++;
      if(d==32){d=0;}}
      menuDDS++;
    delay(200);}
if(menuDDS==3){
    display.clearDisplay(); 
    display.setTextColor(BLACK); // 'inverted' text
    display.setCursor(10,0);
    display.println("Синус");
    display.setTextColor(BLACK);
    display.setCursor(10,10); 
    display.println("Треугольник");
    display.setTextColor(BLACK);
    display.setCursor(10,20);
    display.println("Пила");
    display.setTextColor(WHITE, BLACK);
    display.setCursor(10,30);
    display.println("Пила Обр");
    display.setTextColor(BLACK);
    //display.setCursor(0,40);
    //display.print("Частота=");
   // display.print(57);
   // display.print("Гц");
    delay(100);  
    display.display();
    while(D11_Read==LOW){
      PWM=RpilaM[d];
      pwmWrite(dds,PWM);
      //delayMicroseconds(fr);
      d++;
      if(d==32){d=0;}}
      menuDDS++;
    delay(200);}
if(menuDDS==4){menuDDS=0;}

}
/////////////////////DDS
/////////////////////TTL
void TTL(){
display.clearDisplay(); 
display.setTextColor(BLACK); 
display.setCursor(10,0);
display.println("Терминал");
display.setCursor(10,10);
display.println("Скорость");
display.setCursor(10,20);
display.print("-");
display.print(speedTTL);
display.println("+");
display.setCursor(0,30);
display.println("Нажми ОК-старт");
if(digitalRead(pravo)==HIGH){speedTTL=speedTTL+100;}
if(digitalRead(levo)==HIGH){speedTTL=speedTTL-100;}
if(speedTTL<0){speedTTL=250000;}
if(speedTTL>250000){speedTTL=0;}
if(digitalRead(ok)==HIGH){Serial.begin(speedTTL*(16/overclock));
      display.clearDisplay(); 
      delay(100);  
      display.display();
      int x=0;
      int y=0; 
      while(1){ 
            char incomingBytes;  
            if (Serial.available() > 0) { // Если в буфере есть данные
                incomingBytes=Serial.read(); // Считывание байта в переменную incomeByte
                display.setCursor(x,y);
                display.print(incomingBytes); // Печать строки в буффер дисплея
                display.display(); x=x+6;
                if(x==84){x=0;y=y+8;}
                if(y==48){x=0;y=0;
                  display.clearDisplay(); 
                  delay(100);  
                  display.display();}}         
      }} 
delay(100);  
display.display();  
}
/////////////////////TTL

 

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

Electronik83 пишет:

Автоматический выбор развертки (посидел немного в экселе). Прописал перед выводом частоты на экран и после подсчета этой же частоты:

#define PER 1.3                               
  if (count > 3823.3*PER) razv = 6; else
  if (count > 1934.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;
 
Как часы работает:)
Никто не задавался вопросом, почему седьмая разертка не работает? Когда делитель АЦП ставим в 2? И если не работает, то зачем он нужен вообще?

Отлично сделано, работает клас при опорном 5в, но если 1.1 то начинается пляска, возможно из за слабого сигнала (пока отказался оставлять, закоментил) Как бы сделать ВЫКЛЮЧАТЕЛЬ этой функции!?. Посидел немножко и для 5510 в сетч внедрил авто опорное 5.0, 1.1, 0.2  +изначально была авто синхронизация, если меряем частоту аппаратно, рисуем звездочку *, пауза убрана, отображается после нажатия "ок" и так по кругу с разверткой, в меню экрана регулировка контрастности и включение подсветки экрана с сохранением в епром.  Без UART приемника.                                                                  До 20.000KHz аппаратно выводит частоту идеально, что не скажешь о прошивке для ssd1306. Я не могу понять почему так??? Как бы всё вродибы в скетче одинаково, 5510 аппаратно = 19.000KHz,  ssd1306 аппаратно = 18.967KHz. Куда делось 33 Hz??? Может ты про этот косяк и расказывал.

Скетч для 5510 // автор Electronik83     

//Страница проэкта  <a href="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>): ver 17032017
// 1. В режиме генератора PWM в процентах в целых числах (без дробей), нарисовал график сигнала PWM
// 2. В генераторе пофиксил/оптимизировал вывод частоты (1000 герц не показывало)
// 3. Автоматическое определение схемы подключения кнопок (при включении лучше ничего не нажимать:) :
//      a). Как у автора - кнопки к питанию и резюки на массу
//      b). Как я хочу - кнопки к земле и без всяких резюков
// 4. Отправил вэйвы для DDS генератора во флэш - оперативы больше стало свободной
// 5. Реализовал перезапуск при зажатии кнопок "+" и "-" (из осцилла не работает (не знаю, как исправить!))
// 6. Убрал развертки 7 и 8 (программное растягивание) 
// 7. На DDS - генераторе видно форму выводимого сигнала, оптимизировал, сделал свои формы -  ваще клевый стал... 
// 8. Переделал меню генератора - вроде удобней стало.
// 9. В терминале можно выбирать стандартные скорости
// 10. Координально переделал некоторые куски кода, например - вывод меню....
// 11. От smokok, заменил терминал на меню контрастности и вкл. откл. подсветки с сохранением в EEPROM
// 12. Сломал перезагрузку кнопками + и - из за добавления EEPROM, но прошива очень шустрая однако!!!
// 1х. И т.д. и т.п.)))
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
#include <FreqCount.h> 
#include <PWM.h>
#include <EEPROM.h>
// ---- UserSpace ---- //
// Подключение дисплея 
// (clk(scl, sck), din(sda, mosi), dc, ce (cs), rst)  
#define Ekran     8  //Экран/////////////////////////////////Подсвет
Adafruit_PCD8544 display = Adafruit_PCD8544(7, 6, 4, 3, 2); // пины к которым подключен дисплей        
//  #define contrast 52  // контрастность дисплея////////////////////////////////////////
#define key_down 11  // кнопка минус (можно любой пин)
#define key_ok   12  // кнопка ОК (можно любой пин)
#define key_up   13  // кнопка плюс (можно любой пин)
#define akb      A5  // любой свободный аналоговый пин для измерения напряжения АКБ 
#define overclock 16 // частота на которой работает Ардуино
float VCC=5.10;  // напряжение питания, меряем мультиметром
// ---- UserSpace_End ---- //
//        A3    !!   // пин подключиния входа осцилла (для отображения формы сигнала)
//        D5    !!   // пин подключиния входа осцилла (для аппаратного измерения частоты через таймер (в FreqCount.h))
#define led  9       // пин для генератора сигналов (не менять)
#define dds  10      // пин для генератора dds (не менять)

// ---- Глобальные переменные ---- //
byte contrast;
byte Set=0;       ///////////////////////Экран/////////////////////////////////
bool BL;          /////////////////////////Экран///////////////////////////////
bool flag_key;       // нужно для разных подключений кнопок
byte mode=0;         // режим работы прибора
byte menu=0;         // подменю
const char* const modeStr[] PROGMEM= { " Осциллоскоп ", "PWM-генератор", "DDS-генератор", "* * Экран * *"};

// ---- Переменые для осциллоскопа ---- //
#define BUFSIZE 700
byte adcBuf[BUFSIZE]; // буфер проебразований АЦП
int kdel=5;           //bulat  автопредел 1,1/0,22 вольта
byte syncLevel=50;    // уровень синхронизации 0 до 255
byte razv=4;          // развертка
bool pause=0;         // флаг режима паузы
bool opornoe=1;       // флаг опорного напряжения
int  pauseOffset=0;   // смещение графика в режиме паузы (прокрутка)
long count=0;         // для подсчета частоты сигнала
//         bool magnify=0; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// ---- Переменные для генератора ---- //
unsigned long stepFreq=0;
int PWM = 50;    // стартовое значение ШИМ от 0 до 100 для генератора        
unsigned long freq = 500; // стартовое значение частоты в Гц для генератора

void ShowMode(char m, char y) {
   display.setCursor(3, y);
   switch (m) {
      case 0: display.print(modeStr[0]); break;
      case 1: display.print(modeStr[1]); break;
      case 2: display.print(modeStr[2]); break;
      case 3: display.print(modeStr[3]); break;
    }
}
void ShowModeInv(char y) {
  display.setTextColor(WHITE, BLACK);
  display.setCursor(0, y); display.println(" "); display.setCursor(77, y); display.println(" ");  // для красоты только
  ShowMode(mode, y);
  display.setTextColor(BLACK);
}

// ==== Инициализация всего :) ==== //
void setup() {
  delay(500); // ждем, пока питание устаканится (меньше глюков чтоб потом ловить)
  
////////////////////////////Экран//////////////////////////////
//  Примечание. Экран становится темным,  решение проблемы.///////////////////////////////////////
//  EEPROM.write(0, 50);  //  Записать закрытую строку,  Обновить  и начать использовать.
  contrast = EEPROM.read(0);
  BL       = EEPROM.read(1);
  pinMode(Ekran, OUTPUT);
  digitalWrite(Ekran, BL);
  byte key_test=0;
  digitalWrite(key_up, HIGH); digitalWrite(key_down, HIGH); digitalWrite(key_ok, HIGH); 
  if (digitalRead(key_up))   key_test++; 
  if (digitalRead(key_down)) key_test++; 
  if (digitalRead(key_ok))   key_test++; 
  if (key_test>1) { 
    flag_key=0;   
  }
  else {
    digitalWrite(key_up, LOW); digitalWrite(key_down, LOW); digitalWrite(key_ok, LOW); 
    flag_key=1;
  }   
  display.begin();
  display.setContrast(contrast);  
  while(flag_key-digitalRead(key_ok))  {
    display.clearDisplay();
//////////////////////////////////////////////////////

 // автодетект кнопок
  byte key_test=0;
  //pinMode(key_up, INPUT); pinMode(key_down, INPUT); pinMode(key_ok, INPUT); 
  // подтянули кнопки к питанию
  digitalWrite(key_up, HIGH); digitalWrite(key_down, HIGH); digitalWrite(key_ok, HIGH); 
  // автодетект подключения кнопок при включении..
  if (digitalRead(key_up))   key_test++; // если подтяжка к питанию осталась (не стоят резюки на массу) - плюсуем
  if (digitalRead(key_down)) key_test++; // если подтяжка к питанию осталась (не стоят резюки на массу) - плюсуем
  if (digitalRead(key_ok))   key_test++; // если подтяжка к питанию осталась (не стоят резюки на массу) - плюсуем
  if (key_test>1) { // определили подключение кнопок на массу
    flag_key=0;   
  }
  else { // определили подключение кнопок на питание + резюки на массу
    digitalWrite(key_up, LOW); digitalWrite(key_down, LOW); digitalWrite(key_ok, LOW); // убрали подтяжку 
    flag_key=1;
  }
  // если автодетект работает неверно (менюха скачет постоянно), то нужно расскомментировать одну из следующих строчек:
  // flag_key = 0; // кнопки просто подключены к земле, резюков нету.
  // flag_key = 1; // кнопки подключены к питанию и резюки на землю
  
  while(flag_key-digitalRead(key_ok))  { // выводим меню, пока не выбран режим работы прибора  
    display.clearDisplay();              // чистим дисплей
    // выводим главное меню
    for (char i=0; i<4; i++) {
      if (i==mode) ShowModeInv(i*10); else ShowMode(i, i*10);      
    }
    delay(50);
    display.setTextColor(WHITE, BLACK);
    display.setCursor(12,40); display.print(" Бат=");  // выводим напряжение батареи
    display.print(analogRead(akb)*VCC/1024);
    display.print("v");
    display.setTextColor(BLACK);
    // перемещаемся по меню  
    if (flag_key-!digitalRead(key_up)  ) { mode++; if (mode>3)       mode=0; delay(100); }
    if (flag_key-!digitalRead(key_down)) { mode--; if (mode == 0xFF) mode=3; delay(100); }
    display.display(); // выводим все на экран
  }
  // выбран режим работы...
  if (mode==0) FreqCount.begin(1000);
  if (mode==1) { InitTimersSafe(); SetPinFrequencySafe(led, freq);   }
  if (mode==2) { InitTimersSafe(); SetPinFrequencySafe(led, 200000); }
  delay(150);
}
  } 
// безконечный цикл - по сути прыгаем в подпрограммы
void loop() {
  if (mode==0) Oscill();       // "выпадаем" в осцилл
  if (mode==1) Generator();    // "выпадаем" в генератор 
  if (mode==2) DdsGenerator(); // "выпадаем" в DDS генератор
  if (mode==3) Menu_ekrana();  // "выпадаем" в Menu Экрана
}
// читаем с АЦП данные и помещаем их в буфер...   
void ReadAdc() {
  if (razv) { // (razv>0)           // если развертка без задержек всяких
    ADCSRA = 0b11100000 | (8-razv); // установили делитель (/2 - не работает, так что начинаем с /4)
    for(int i=0; i<BUFSIZE; i++) {  // цикл для чтения  
      while ((ADCSRA & 0x10)==0);   // ждем готовность АЦП
      ADCSRA|=0x10;                 // запускаем следующее преобразование
      adcBuf[i]=ADCH;               // записываем данные в массив
    }
  } else {                          // развертка с задержками (delay)
    ADCSRA = 0b11100111;            // делитель на /128
    for(int i=0; i<BUFSIZE; i++) {  // цикл для чтения
      while ((ADCSRA & 0x10)==0);   // ждем готовность АЦП
      ADCSRA|=0x10;                 // запускаем следующее преобразование
      delayMicroseconds(500);       // делаем задержку 
      adcBuf[i]=ADCH;               // записываем данные в массив
    }
  }
}
//////////////////////////////// РЕЖИМ ОСЦИЛЛОСКОПА//////////////////////////////
void Oscill() {  
  if(opornoe) ADMUX = 0b01100011; // выбор внешнего опорного
         else ADMUX = 0b11100011; // выбор внутреннего опорного 1,1В
 label_ReadAdc:
         
  delay(100);
  if(!pause) ReadAdc(); // если паузы нет, то считываем сигнал в буфер
  // определение точки синхронизации
  bool flagSync=0;    // флаг, когда сигнал ниже уровня синхронизации
  int syncOffset=0;   // смещение для вывода с синхронизацией
  // считаем максимальное и минимальное значение сигнала (для вывода на экран)
  byte Vmax=0, Vmin=255; // тут будем хранить максимальное и минимальное напряжение 
  for(int y=0; y<BUFSIZE; y++) { if(Vmin>adcBuf[y]) Vmin=adcBuf[y]; if(Vmax<adcBuf[y]) Vmax=adcBuf[y]; }
  syncLevel = (Vmax-Vmin) / 2 + Vmin; // /////////////// Авто синхронизация 
  // считаем смещение графика по уровню синхронизации
  for(int y=0; y<BUFSIZE-80; y++) {
    if(adcBuf[y]<syncLevel) flagSync=1;
    if(flagSync && adcBuf[y]>syncLevel) { syncOffset = y; break; } 
  }
  //bulat если зашкаливает включаем предел 5 в  
  if (Vmax==255){
    if (opornoe==0)
       {
        opornoe=1;
       ADMUX = 0b01100011;// выбор внутреннего опорного 5.0 В
           goto   label_ReadAdc;
     }
  }
//bulat если  5 в  и уровень менее 1,1 в то вкл предел 1,1 в
  if (Vmax<=55){
    if (opornoe==1)
       {
        opornoe=0;
        ADMUX = 0b11100011;// выбор внутреннего опорного 1,1В
        goto   label_ReadAdc;
     }
  }
  //bulat здесь автопредел 1,1/0,22 в,программный
  kdel=5;
  if (Vmax<=55){
    if (opornoe==0)
       {
       kdel=1;
     }
  }
  // отрисовка графика 
  display.clearDisplay(); // чистим дисплей
  byte x=1; // задаем смещение графика немного вправо
  if(pause) { // если в режиме паузы, то рисуем с прокруткой...
    display.drawLine(pauseOffset/8,8,pauseOffset/8+6,8, BLACK); //шкала прокрутки
    display.drawLine(pauseOffset/8,9,pauseOffset/8+6,9, BLACK); //шкала прокрутки
    for(int y=pauseOffset; y<pauseOffset+80; y++) { // рисуем форму сигнала
      display.drawLine(++x, 46-adcBuf[y]/kdel, x+1, 46-adcBuf[y+1]/kdel, BLACK);  
      //линия сигнала/////////
      //display.drawLine(x+1, 47-adcBuf[y]/7+1, x+2, 47-adcBuf[y+1]/7+1, BLACK); //тоньше линия сигнала/////////
    }
  } else {  // если паузы нет, то рисуем немного по другому))..
    display.fillCircle(80,47-syncLevel/7, 2, BLACK); //рисуем уровень синхронизации    
    for(int y=syncOffset; y<syncOffset+80; y++) {  // рисуем форму сигнала
      display.drawLine(++x, 46-adcBuf[y]/kdel, x+1, 46-adcBuf[y+1]/kdel, BLACK);   
      //линия сигнала/////////
      //display.drawLine(x+1, 47-adcBuf[y]/7+2, x+1, 47-adcBuf[y+1]/7+1, BLACK);  //тоньше линия сигнала/////////
    }
  }
  // отрисовка сетки
  for(byte i=47;i>5;i-=7) { display.drawPixel(0, i, BLACK); display.drawPixel(1, i, BLACK); display.drawPixel(2, i, BLACK); }
  for(byte i=47;i>5;i-=3) { display.drawPixel(21,i, BLACK); display.drawPixel(42,i, BLACK); display.drawPixel(63,i, BLACK); }
  for(byte i=3;i<84;i+=3) { display.drawPixel(i,33, BLACK); display.drawPixel(i,19, BLACK); }
 
  // отрисовка menu
  display.setCursor(0,0);
  if (menu==0) {
    if(opornoe) 
       {
        display.print(VCC,1);
        }
        else 
        { 
        if(kdel==5)display.print("1.1");else display.print("0.2");
        }
   
    if(flag_key-!digitalRead(key_down)) { razv--; if(razv==255) razv=6; }
    if(flag_key-!digitalRead(key_up)  ) { razv++; if(razv==7)   razv=0; }
    else display.setTextColor(WHITE, BLACK);
    display.print(" "); display.print(razv); display.print(" ");
    display.setTextColor(BLACK);
    if(flag_key-!digitalRead(key_down) || flag_key-!digitalRead(key_up));
  }  
/*     ///////////////авто razv
  #define PER 1.3                               
  if (count > 3823.3*PER) razv = 6; else
  if (count > 1934.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;
*/     //////////////// 
  if (menu==1) { 
    pause=1;

    if(flag_key-!digitalRead(key_down)) { pauseOffset-=27; if(pauseOffset<0)   pauseOffset=0;   }
    if(flag_key-!digitalRead(key_up)  ) { pauseOffset+=27; if(pauseOffset>619) pauseOffset=619; }    
    else display.setTextColor(WHITE, BLACK);
    display.print(" ~ ");
    display.setTextColor(BLACK);
  }
/*  if(menu==2) {
    pauseOffset=0;
    pause=0;
    if(flag_key-!digitalRead(key_down)) { syncLevel-=20; if(syncLevel<20)  syncLevel=20;  }
    if(flag_key-!digitalRead(key_up)  ) { syncLevel+=20; if(syncLevel>230) syncLevel=230; }   
    display.fillCircle(80,47-syncLevel/7, 5, BLACK);
    display.fillCircle(80,47-syncLevel/7, 2, WHITE); 
  }*/
  if (flag_key-!digitalRead(key_ok)) { menu++; if(menu==2) { menu=0; pause=0; }}

  // забираем частоту сигнала, полученную аппаратно (через таймер)
  if (FreqCount.available()) count = FreqCount.read(); // забираем по готовности счетчика
    
  // считаем программно частоту сигнала
  byte Freq1=0;
  long Freq=0;
  bool flagFreq1=0, flagFreq2=0, flagFreq3=0;
  for(int y=1; y<255; y++) {
    if(!flagFreq1 && adcBuf[y]<syncLevel) flagFreq2=1; // тут можно че то оптимизировать, тока не могу понять, что [El83]   
    if(!flagFreq1 && flagFreq2 && adcBuf[y]>syncLevel) { flagFreq1=1; Freq1=y; }    
    if( flagFreq1 && adcBuf[y]<syncLevel) flagFreq3=1;    
    if( flagFreq3 && adcBuf[y]>syncLevel) {      
      switch (razv) { // тут позже оптимизирую!                           ////////!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!11
        case 6: Freq=1000000/((y-Freq1-1)*3.27);  break; // делитель 4
        case 5: Freq=1000000/((y-Freq1)*3.27)/2;  break; // делитель 8
        case 4: Freq=1000000/((y-Freq1)*3.27)/4;  break; // делитель 16
        case 3: Freq=1000000/((y-Freq1)*3.27)/8;  break; // делитель 32
        case 2: Freq=1000000/((y-Freq1)*3.27)/16; break; // делитель 64
        case 1: Freq=1000000/((y-Freq1)*3.27)/32; break; // делитель 128
        case 0: Freq=1000000/((y-Freq1)*500);     break; // делитель 128
      }  
      flagFreq1=0; flagFreq3=0;
    }
  }
  // отрисовка частоты сигнала
  if (opornoe && ((Vmax*VCC/255)>2.5)) count=count*(overclock/16.0); 
                                  else count=Freq *(overclock/16.0);
  if (count<1000) { display.print(" "); display.print(count); display.print("Hz"); }
             else { display.print(count/1000.0,3); display.print("KHz"); }
  display.setTextColor(WHITE, BLACK);           
  if(opornoe && ((Vmax*VCC/255)>2.5)) display.print('*'); // если меряем частоту аппаратно, рисуем звездочку
  display.setTextColor(BLACK);
  // отрисовка максимального напряжения сигнала
  display.setCursor(0,40); 
  if (opornoe) display.print(Vmax*VCC/255,2); else display.print(Vmax*1.1/255,2);
//  display.print("v");
//  delay(150);  
  display.display(); // вываливаем все на экран
}

//////////////////////////////// РЕЖИМ ГЕНЕРАТОРА//////////////////////////////
void Generator() {
  // обработка кнопок и отрисовка одновременно
  display.clearDisplay();
  ShowModeInv(0);  
  if(flag_key-!digitalRead(key_ok)) { if(menu++==7) menu=0; delay(200); } // переходим по разрядам / меняем ШИМ
  if (menu==7) { // меняем ШИМ
    if(flag_key-!digitalRead(key_down)) { PWM--; if(PWM<0)   PWM=100; delay(200); }
    if(flag_key-!digitalRead(key_up))   { PWM++; if(PWM>100) PWM=0;   delay(200); }
    pwmWrite(led, PWM*2.55); // задали ШИМ
    display.setTextColor(WHITE, BLACK); // если меняем шим, то рисуем его инверсным
    display.drawLine(15, 39, 68+(PWM==100?6:0)+(PWM<10?-6:0), 39, BLACK); // сверху полоска для большей инверсности
  } else { // меняем частоту
    if(flag_key-!digitalRead(key_down)) {
      if (freq>stepFreq)  freq-=stepFreq;              // не даем выйти за допустимый диаппазон
      SetPinFrequencySafe(led, freq/(overclock/16.0)); // задали частоту
      delay(150); // задержка для кнопок 
    }
    if(flag_key-!digitalRead(key_up))   {
      if ((freq+stepFreq)<10000000) freq+=stepFreq;    // не даем выйти за допустимый диаппазон
      SetPinFrequencySafe(led, freq/(overclock/16.0)); // задали частоту
      delay(150); // задержка для кнопок 
    }  
    display.setTextColor(BLACK); // если ШИМ не меняем, то выбираем обычный текст
    #define XFREQ 12 // ======== // и, выделяем декаду частоты полосочками
    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, 25, x+PWM/4, 25, BLACK); x+=PWM/4;       // верх 
    if (PWM!=0 && PWM!=100) display.drawLine(x, 25, 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, 25, BLACK);              // подъем
  }
  display.display(); // вываливаем буфер на дисплей
}

//////////////////////////////// DDS генератор//////////////////////////////
void DdsGenerator() {
  static const byte ddsWave[][32] PROGMEM = {
    2,10,21,37,57,79,103,127,152,176,198,218,234,245,253,255,253,245,233,218,198,176,152,128,103,79,57,37,21,10,2,0,    // El83_sinNew    
    16,32,48,64,80,96,112,128,143,159,175,191,207,223,239,255,239,223,207,191,175,159,143,128,112,96,80,64,48,32,16,0,  // El83_treugNew    
    8,16,25,33,41,49,58,66,74,82,90,99,107,115,123,132,140,148,156,165,173,181,189,197,206,214,222,230,239,247,255,0,   // El83_pilaNew
    255,247,239,230,222,214,206,197,189,181,173,165,156,148,140,132,123,115,107,99,90,82,74,66,58,49,41,33,25,16,8,0 }; // El83_pilaObrNew
  const char* const ddsStr[] PROGMEM = { "    Синус", " Треугольник", "    Пила", "Обратная пила"};
  byte ddsCount=0;
  // Рисуем DDS-генератор
  display.clearDisplay();  
  ShowModeInv(0);  
    for (byte i=0; i<84;) display.drawLine(i, 37-pgm_read_byte(&ddsWave[menu][i%32])/9, i, 37-pgm_read_byte(&ddsWave[menu][(i++)%32])/9, BLACK);
  display.setCursor(3, 40);  display.print(ddsStr[menu]);  display.display(); // отрисовали все
  // выводим выбранный сигнал, пока не нажали кнопку
  while(flag_key-digitalRead(key_up) && flag_key-digitalRead(key_down) && flag_key-digitalRead(key_ok)) {
    pwmWrite(dds, pgm_read_byte(&ddsWave[menu][(ddsCount++)&0x1F]));
  }
  if (++menu==4) menu = 0; // нажали кнопку - переключаем режим
  delay(150); // чтоб кнопки нормально нажимались
}

//////////////////////////////Экран/////////////////////////////////
void Menu_ekrana() {
  Set=0;delay(200);
  while (flag_key-digitalRead(key_ok)){
    display.clearDisplay();
    if (Set == 0) display.setTextColor(WHITE, BLACK); else display.setTextColor(BLACK);
    display.setCursor(0 , 0); display.println("Контраст:"); display.setTextColor(BLACK);
    display.setCursor(60 , 0); display.println(contrast);
    display.setCursor(0 , 10); display.println("Подсветк:"); display.setTextColor(BLACK);
    if (BL == 1) { display.setCursor(60 , 10); display.println("Вкл");} else
                 { display.setCursor(60 , 10); display.println("Отк");} display.display();
    if (flag_key-!digitalRead(key_up))   contrast++;
    if (flag_key-!digitalRead(key_down)) contrast--;
    if (contrast<30) contrast=30;
    if (contrast>70) contrast=70;
    display.setContrast(contrast); 
    delay(150);
  }
  Set=1; delay(200);
  while (flag_key-digitalRead(key_ok)){
    display.clearDisplay();
    display.setCursor(0 , 0); display.println("Контраст:");
    display.setCursor(60 , 0); display.println(contrast);
    if (Set == 1) display.setTextColor(WHITE, BLACK); else display.setTextColor(BLACK);
    display.setCursor(0 , 10); display.println("Подсветк:"); display.setTextColor(BLACK);
    if (BL == 1) { display.setCursor(60 , 10); display.println("Вкл"); digitalWrite(Ekran, BL); } else
                 { display.setCursor(60 , 10); display.println("Отк"); digitalWrite(Ekran, BL);  }
    if (flag_key-!digitalRead(key_up))   BL=1;
    if (flag_key-!digitalRead(key_down)) BL=0;
    display.display();
    delay(150);
  }
  display.display();
  display.clearDisplay();
  EEPROM.write(0, contrast);
  EEPROM.write(1, BL);
  setup();
}

//////////////////////////////////////////////////////////////////////////////////////////////

Скетч для ssd1306 // автор Electronik83

Собрал все три кнопки под одну для экономии места (меняется через "ок"), есть режим лупа от автора, отображает минимальное максимальное напряжение, убрал терминал (не пользуюсь), авто вкл. режима осциллографа (Если не нажата кнопка ок 10 сек вход в режим осцилоографа автоматом), авто опорное, авто синхронизация, авто развертку добавил, но закомментировал, если меряем частоту аппаратно, рисуем звездочку *    может и ещё что есть))).   

Прошивка более 1000KHz начинает занижать частоту по мере её увеличения, прошу пожалуйста помочь разобратся.

#include <FreqCount.h> 
#include <PWM.h>
// ---UserSpace------------------------------------------------------------------------ //
#define ssd1306_scl  7//3 // пин для подключения шины SCL дисплея (можно любой пин) 
#define ssd1306_sda  6//2 // пин для подключения шины SDA дисплея (можно любой пин)
#define key_down    13 // кнопка минус (можно любой пин)
#define key_ok      12 // кнопка ОК (можно любой пин)
#define key_up      11 // кнопка плюс (можно любой пин)
#define akb         A5 // любой свободный аналоговый пин для измерения напряжения АКБ 
#define overclock   16 // частота на которой работает Ардуино
float VCC       = 5.0; // напряжение питания, меряем мультиметром
// ---UserSpace_end-------------------------------------------------------------------- //
#define led  9         // пин для генератора сигналов (не менять)
#define dds  10        // пин для генератора dds (не менять)
//        A3    !!     // пин подключиния входа осцилла (для отображения формы сигнала)
//        D5    !!     // пин подключиния входа осцилла (для аппаратного измерения частоты сигнала 
//                                                              через таймер (в FreqCount.h))
// глобальные переменные
bool flag_key; // нужно для разных подключений кнопок
byte mode=0;   // режим работы прибора
byte menu=0;   // подменю
// для осциллоскопа
#define BUFSIZE 356   // размер буфера 256 // 701
byte adcBuf[BUFSIZE]; // забираем память под буфер осцилла
byte syncLevel=0;    // уровень синхронизации 0 до 255 
byte razv=4;          // развертка  
bool pause=0;         // флаг режима паузы
bool opornoe=0;       // флаг опорного напряжения
int  prokr=0;         // прокрутка
int osc=150;          // авто вкл. режима осциллографа
long count=0;         // для подсчета частоты сигнала
int kdel=5;           //bulat  автопредел 1,1/0,22 вольта
bool magnify=0;
// для генератора
unsigned long stepFreq;
int PWM = 50;         // стартовое значение ШИМ от 0 до 100        
unsigned long freq = 500;      // int32_t стартовое значение частоты в Гц
// для DDS-генератора
byte ddsCount=0;
PROGMEM const byte sinM[]     = {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};
PROGMEM const byte pilaM[]    = {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};
PROGMEM const byte RpilaM[]   = {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};
PROGMEM const byte trianglM[] = {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};
// ---------------------------------
void setup() {
  // автоматическое определение подключение кнопок
  byte key_test=0;
  // подтянули кнопки к питанию
  digitalWrite(key_up, HIGH); digitalWrite(key_down, HIGH); digitalWrite(key_ok, HIGH); 
  // автодетект подключения кнопок при включении..
  if (digitalRead(key_up))   key_test++; // если подтяжка к питанию осталась (не стоят резюки на массу) - плюсуем
  if (digitalRead(key_down)) key_test++; // если подтяжка к питанию осталась (не стоят резюки на массу) - плюсуем
  if (digitalRead(key_ok))   key_test++; // если подтяжка к питанию осталась (не стоят резюки на массу) - плюсуем
  if (key_test>1) { // определили подключение кнопок на массу
    flag_key=0;   
  }
  else { // определили подключение кнопок на питание + резюки на массу
    digitalWrite(key_up, LOW); digitalWrite(key_down, LOW); digitalWrite(key_ok, LOW); // убрали подтяжку 
    flag_key=1;
  }
  // если автодетект работает неверно (менюха скачет постоянно), то нужно расскомментировать одну из следующих строчек:
  // flag_key = 0; // кнопки просто подключены к земле, резюков нету.
  // flag_key = 1; // кнопки подключены к питанию и резюки на землю
  
  // Выводим главное меню...    
  ssd1306_init(); // инит дисплея
  while(flag_key-digitalRead(key_ok))  { // выводим меню, пока не выбран режим работы прибора  
////////////////////// Если не нажата 10 сек кнопка ок вход в режим осцилоографа автоматом
   osc--;
    if (osc==0) break;
//////////////////////
    ssd1306_clear();
    ssd1306_set_inv(mode == 0);
    ssd1306_set_pos(25 , 0); ssd1306_string(" ОСЦИЛЛОСКОП ");
    ssd1306_set_inv(mode == 1);
    ssd1306_set_pos(19 , 2); ssd1306_string(" PWM-генератор ");
    ssd1306_set_inv(mode == 2);
    ssd1306_set_pos(19 , 4); ssd1306_string(" DDS-генератор ");
    ssd1306_set_inv(mode == 3);
    ssd1306_set_inv(0);
    ssd1306_set_pos(25, 7); ssd1306_string("Батарея ");
    ssd1306_string(analogRead(akb)*VCC/1024, 2);
    ssd1306_char('v');
    // перемещаемся по меню  
    if (flag_key-!digitalRead(key_up)  ) { mode++; delay(100); }
    if (flag_key-!digitalRead(key_down)) { mode--; delay(100); }
    if (mode == 0xFF) mode = 2;
    if (mode>2) mode=0;
    //delay(50);  
    ssd1306_refresh(); // выводим все на экран
  }
  // выбран режим работы...
  if (mode==0) FreqCount.begin(1000);
  if (mode==1) { InitTimersSafe(); SetPinFrequencySafe(led, freq); }
  if (mode==2) { InitTimersSafe(); SetPinFrequencySafe(led,200000);     }
  delay(200);
}
// читаем с АЦП данные и помещаем их в буфер...   
void ReadAdc() {
  if (razv) { // (razv>0)           // если развертка без задержек всяких
    ADCSRA = 0b11100000 | (8-razv); // установили делитель (/2 - не работает, так что начинаем с /4)
    for(int i=0; i<BUFSIZE; i++) {  // цикл для чтения  
      while ((ADCSRA & 0x10)==0);   // ждем готовность АЦП
      ADCSRA|=0x10;                 // запускаем следующее преобразование
      adcBuf[i]=ADCH;               // записываем данные в массив
    }
  } else {                          // развертка с задержками (delay)
    ADCSRA = 0b11100111;            // делитель на /128
    for(int i=0; i<BUFSIZE; i++) {  // цикл для чтения
      while ((ADCSRA & 0x10)==0);   // ждем готовность АЦП
      ADCSRA|=0x10;                 // запускаем следующее преобразование
      delayMicroseconds(500);       // делаем задержку
      adcBuf[i]=ADCH;               // записываем данные в массив
    }
  }
}
// безконечный цикл
void loop() {
  if(mode==0) Oscill();       // "выпадаем" в осциллоскоп  
  if(mode==1) Generator();    // "выпадаем" в генератор 
  if(mode==2) DDSGenerator(); // "выпадаем" в DDS генератор
//  if(mode==3) UARTterminal(); // "выпадаем" в USART приемник
}
///////////// РЕЖИМ ОСИЛЛОСКОПА //////////////
void Oscill() {
  if(opornoe) ADMUX = 0b01100011; // выбор внешнего опорного
         else ADMUX = 0b11100011; // выбор внутреннего опорного 1,1В
 label_ReadAdc:

  delay(5);
  if(!pause) ReadAdc(); // если в режиме паузы, то буфер не обновляем
  // определение точки синхронизации
  bool flagSync=0;    // флаг, когда сигнал ниже уровня синхронизации
  int syncOffset=0;   // смещение для вывода с синхронизацией
  for(int y=0; y<BUFSIZE-122; y++) {
    if(adcBuf[y]<syncLevel) flagSync=1;
    if(flagSync && adcBuf[y]>syncLevel) { syncOffset = y; break; } 
  }
  // считаем максимальное и минимальное значение сигнала (для вывода на экран)
  byte Vmax=0, Vmin=255; // тут будем хранить максимальное и минимальное напряжение 
  for(int y=0; y<BUFSIZE; y++) { if(Vmin>adcBuf[y]) Vmin=adcBuf[y]; if(Vmax<adcBuf[y]) Vmax=adcBuf[y]; }
  syncLevel = (Vmax-Vmin) / 2 + Vmin;
//bulat если зашкаливает включаем предел 5 в  
  if (Vmax==255){
    if (opornoe==0)
       {
        opornoe=1;
       ADMUX = 0b01100011;// выбор внутреннего опорного 5.0 В
           goto   label_ReadAdc;
     }
  }
//bulat если  5 в  и уровень менее 1,1 в то вкл предел 1,1 в
  if (Vmax<=55){
    if (opornoe==1)
       {
        opornoe=0;
        ADMUX = 0b11100011;// выбор внутреннего опорного 1,1В
        goto   label_ReadAdc;
     }
  }
  //bulat здесь автопредел 1,1/0,22 в,программный
  kdel=5;
  if (Vmax<=55){
    if (opornoe==0)
       {
       kdel=1;
     }
  }
  //////// отрисовка графика//////// 
  ssd1306_clear();
  byte x=3; // небольшое смещение формы сигнала вправо
  if(pause) { // если в режиме паузы, то рисуем с прокруткой...
    ssd1306_draw_line(0,8,127,8); // линия шкалы прокрутки
    ssd1306_draw_line(prokr/1.9,9 ,prokr/1.9+6,9 ); //шкала прокрутки
    ssd1306_draw_line(prokr/1.9,10,prokr/1.9+6,10); //шкала прокрутки // я тут долго шаманил, но потом вспомнил про функцию map()! тут её надо использовать!
    for(int y=prokr; y<prokr+122; y++) { // рисуем форму сигнала
    // ssd1306_draw_line(++x, 63-adcBuf[y], x, 63-adcBuf[y+1]);
      ssd1306_draw_line(++x, 63-adcBuf[y]/kdel, x, 63-adcBuf[y+1]/kdel);
    }
  } else {  // если паузы нет, то рисуем немного по другому...
    //// рисуем форму сигнала     
    for(int y=syncOffset; y<syncOffset+122; y++) {  
    if (magnify) ssd1306_draw_line(++x, 63-map(adcBuf[y], Vmin, Vmax, 0, 51), x, 63-map(adcBuf[y+1], Vmin, Vmax, 0, 51));  
    else ssd1306_draw_line(++x, 63-adcBuf[y]/kdel, x, 63-adcBuf[y+1]/kdel);
    }
    syncOffset=0;
  }
  ////////////////// отрисовка сетки
  for(byte i=63;i>7;i-=10) { ssd1306_draw_pixel(0, i); ssd1306_draw_pixel(1, i); ssd1306_draw_pixel(2, i); }
  for(byte i=63;i>7;i-=3)  { ssd1306_draw_pixel(21,i); ssd1306_draw_pixel(42,i);  ssd1306_draw_pixel(63,i);
                             ssd1306_draw_pixel(84,i); ssd1306_draw_pixel(105,i); /*ssd1306_draw_pixel(126,i);*/ }
  for(byte i=3;i<127;i+=3) { ssd1306_draw_pixel(i,23); ssd1306_draw_pixel(i,43); }
  
  //////////////////// отрисовка menu///////////////////

/*   ///////////////авто razv
  #define PER 1.3                               
  if (count > 3823.3*PER) razv = 6; else
  if (count > 1934.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;
*/  //////////////// 
  ssd1306_set_pos(0,0);
  if(menu==0) {
  prokr=0;
     if(opornoe) 
       {
        ssd1306_string(VCC,1);
        }
        else 
        { 
        if(kdel==5)ssd1306_string("1.1");else ssd1306_string("0.2");
        }
    ssd1306_char(' ');
    ssd1306_set_inv(1); 
    ssd1306_char(' '); ssd1306_string(razv); 
    ssd1306_char(' '); 
    ssd1306_set_inv(0);
    ssd1306_string(" ");
    if(flag_key-!digitalRead(key_down)) { razv--; if(razv==255) razv=6; }
    if(flag_key-!digitalRead(key_up)  ) { razv++; if(razv==7)   razv=0; }
  }
  if(menu==1) {
    if(opornoe) 
       {
        ssd1306_string(VCC,1);
        }
        else 
        { 
        if(kdel==5)ssd1306_string("1.1");else ssd1306_string("0.2");
        }
    ssd1306_string(" ");
    ssd1306_set_inv(1);
    ssd1306_char(' ');
    ssd1306_char(magnify?'+':'='); 
    ssd1306_char(' ');
    ssd1306_set_inv(0);
    ssd1306_string(" ");
    if(flag_key-!digitalRead(key_down) || flag_key-!digitalRead(key_up)) magnify = !magnify;
  }
  if(menu==2) {
    pause=0;
    if(opornoe) 
       {
        ssd1306_string(VCC,1);
        }
        else 
        { 
        if(kdel==5)ssd1306_string("1.1");else ssd1306_string("0.2");
        }
    ssd1306_string(" ");
    ssd1306_set_inv(1);
    ssd1306_string(" п ");
    ssd1306_set_inv(0);
    ssd1306_char(' ');
    pause=1;
    if(flag_key-!digitalRead(key_down)) { prokr-=10; if(prokr<0)   prokr=0;   }
    if(flag_key-!digitalRead(key_up)  ) { prokr+=10; if(prokr>(BUFSIZE-127)) prokr=BUFSIZE-127; }     
  }
  if (flag_key-!digitalRead(key_ok)) { menu++; if(menu==3) { menu=0; pause=0; }} //перебор меню

  if (FreqCount.available()) count = FreqCount.read(); //вывод частоты по готовности счетчика
  // считаем программно частоту сигнала
  byte Freq1=0;
  long Freq=0;
  bool flagFreq1=0, flagFreq2=0, flagFreq3=0;
  for(int y=1; y<255; y++) {
    if(!flagFreq1 && adcBuf[y]<syncLevel) flagFreq2=1; // тут можно че то оптимизировать, тока не могу понять, что [El83]   
    if(!flagFreq1 && flagFreq2 && adcBuf[y]>syncLevel) { flagFreq1=1; Freq1=y; }    
    if( flagFreq1 && adcBuf[y]<syncLevel) flagFreq3=1;    
    if( flagFreq3 && adcBuf[y]>syncLevel) {      
      switch (razv) {
        case 6: Freq=1000000/((y-Freq1-1)*3.27);  break; // делитель 4
        case 5: Freq=1000000/((y-Freq1)*3.27)/2;  break; // делитель 8
        case 4: Freq=1000000/((y-Freq1)*3.27)/4;  break; // делитель 16
        case 3: Freq=1000000/((y-Freq1)*3.27)/8;  break; // делитель 32
        case 2: Freq=1000000/((y-Freq1)*3.27)/16; break; // делитель 64
        case 1: Freq=1000000/((y-Freq1)*3.27)/32; break; // делитель 128
        case 0: Freq=1000000/((y-Freq1)*500);     break; // делитель 128
      }  
      flagFreq1=0; flagFreq3=0;
    }
  }
  // отрисовка частоты сигнала (выбрал 2.7, т.к. было не правильно на 2.5) EL83
  if(opornoe && ((Vmax*VCC/255)>2.5)) count=count*(overclock/16.0);
                                  else count=Freq*(overclock/16.0);
  //if (Vmax*VCC/255<0.29) count = 0; // подумать, нужно ли....
  if(count<1000) { ssd1306_string(count       ); ssd1306_string("Hz"); }
            else { ssd1306_string(count/1000.0,3); ssd1306_string("KHz"); }
  ssd1306_string(" ");
  if(opornoe && ((Vmax*VCC/255)>2.5)) ssd1306_char('*'); // если меряем частоту аппаратно, рисуем звездочку
  // отрисовка максимального напряжения сигнала
  ssd1306_set_pos(0, 1); 
  if(opornoe) ssd1306_string(Vmax*VCC/255, 2); else ssd1306_string(Vmax*1.1/255, 2);
  // отрисовка минимального напряжения сигнала
  ssd1306_set_pos(0, 7); 
  if(opornoe) ssd1306_string(Vmin*VCC/255, 2); else ssd1306_string(Vmin*1.1/255, 2);
  //delay(200);  
  ssd1306_refresh(); // вываливаем все на экран
}

// РЕЖИМ ГЕНЕРАТОРА
void Generator() {
  ssd1306_clear();   
  if (menu==6) {  // если меняем ШИМ
    if(flag_key-!digitalRead(key_down)){
      PWM=PWM-1;
      if(PWM<0) PWM=100; 
      delay(100); // задержка от дребезга
    }
    if(flag_key-!digitalRead(key_up)){
      PWM=PWM+1;
      if(PWM>100) PWM=0; 
      delay(100);//защита от дребезга 
    }  
  } else { // если меняем частоту
    if(flag_key-!digitalRead(key_down)) {
      freq-=stepFreq;
      if(freq<0) freq=0; 
      SetPinFrequencySafe(led, freq);
      delay(100); // задержка от дребезга 
    }
    if(flag_key-!digitalRead(key_up)){
      freq+=stepFreq;
      SetPinFrequencySafe(led, freq);
      delay(100); // задержка от дребезга 
    }  
  }       
  if(flag_key-!digitalRead(key_ok)) { // переключение разряда выбора частоты 
    delay(100);//защита от дребезга
    menu++;
    if(menu>=7) menu=0;
  } 
  // выводим уровень ШИМ
//  display.setTextSize(1);
  ssd1306_set_pos(43, 1); ssd1306_string("PWM="); ssd1306_string(PWM); ssd1306_string("%");
  // рисуем полосочки уровня ШИМ
  ssd1306_draw_line(63-(63*PWM/100.0),  0, 64+(63*PWM/100.0),  0);
  ssd1306_draw_line(64-(63*PWM/100.0),  1, 63+(63*PWM/100.0),  1);
  ssd1306_draw_line(65-(63*PWM/100.0),  2, 62+(63*PWM/100.0),  2);
  ssd1306_draw_line(63-(63*PWM/100.0), 23, 64+(63*PWM/100.0), 23);
  ssd1306_draw_line(64-(63*PWM/100.0), 22, 63+(63*PWM/100.0), 22);
  ssd1306_draw_line(65-(63*PWM/100.0), 21, 62+(63*PWM/100.0), 21);
  // готовимся для вывода частоты    
  //  display.setTextSize(2);
  float freqX=freq*(overclock/16.0);
  // рисуем частоту
  /*if(freqX<1000)  { 
    ssd1306_set_pos(46, 4); ssd1306_string(freqX, 0); 
  //  display.setTextSize(1); 
    ssd1306_string("Hz"); 
  } else { 
  ssd1306_set_pos(42, 4);
  if(freqX<100000)  ssd1306_string(freqX/1000.0, 2);
  //  display.setTextSize(1); 
  ssd1306_string("KHz"); }*/
  /*if (freqX>1000000) { } else  
  if (freqX>100000)  { } else
  if (freqX>10000)   { } else
  if (freqX>1000)*/
  // 123.456
  //ssd1306_set_pos((menu<3?42:48)+menu*6, 3); ssd1306_string("#");
  // 543210  6 = pwm
  ssd1306_set_pos(42, 4);  ssd1306_string(freqX, 0);  ssd1306_string(" Hz"); 
    // выводим строку статуса (режима)
  ssd1306_set_pos(0,7);
  //  display.setTextSize(1);
  ssd1306_string(">>");
  ssd1306_set_inv(1);
  ssd1306_string(" ");
  if(menu<7) ssd1306_string("x");    
  if(menu==0) { // выбор множителя частоты        
    ssd1306_string(1*(overclock/16.0),1); 
    stepFreq=1;
  }
  if(menu==1){//выбор множителя частоты
    ssd1306_string(10*(overclock/16.0),0); 
    stepFreq=10;
  }
  if(menu==2) { //выбор множителя частоты
    ssd1306_string(100*(overclock/16.0),0); 
    stepFreq=100;
  }
  if(menu==3) { //выбор множителя частоты
    ssd1306_string(1000*(overclock/16.0),0); 
    stepFreq=1000;
  } 
  if(menu==4) { //выбор множителя частоты
    ssd1306_string(10000*(overclock/16.0),0); 
    stepFreq=10000;
  } 
  if(menu==5) { //выбор множителя частоты
    ssd1306_string(100000*(overclock/16.0),0); 
    stepFreq=100000;
  } 
  if(menu==6) { //выбор  PWM
    ssd1306_string("PWM ");
    ssd1306_string(PWM);
    ssd1306_string("%"); 
  } 
  ssd1306_string(" ");
  ssd1306_set_inv(0);
  ssd1306_string("<<");
  // выставили ШИМ
  pwmWrite(led, PWM*2.55);
  delay(100);
  ssd1306_refresh();
}

// Рисуем меню DDS-генератора
void DDSGeneratorMenu() {
  ssd1306_clear(); 
  if (menu==0) ssd1306_set_inv(1); else ssd1306_set_inv(0);
  ssd1306_set_pos(43, 0); ssd1306_string(" Синус ");
  if (menu==1) ssd1306_set_inv(1); else ssd1306_set_inv(0);
  ssd1306_set_pos(25, 2); ssd1306_string(" Треугольник ");
  if (menu==2) ssd1306_set_inv(1); else ssd1306_set_inv(0);
  ssd1306_set_pos(46, 4); ssd1306_string(" Пила ");
  if (menu==3) ssd1306_set_inv(1); else ssd1306_set_inv(0);
  ssd1306_set_pos(34, 6); ssd1306_string(" АнтиПила ");
  ssd1306_set_inv(0);
  ssd1306_refresh();
}

// DDS генератор
void DDSGenerator() {
  if(menu==0) {
    DDSGeneratorMenu();    
    while(flag_key-digitalRead(key_up) && flag_key-digitalRead(key_down) && flag_key-digitalRead(key_ok)) {
      PWM = pgm_read_byte(&sinM[ddsCount]);
      pwmWrite(dds,PWM);  if(ddsCount++==32) ddsCount=0;
    }
    menu++; delay(200);
  }
  if(menu==1) {
    DDSGeneratorMenu();    
    while(flag_key-digitalRead(key_up) && flag_key-digitalRead(key_down) && flag_key-digitalRead(key_ok)) {
      PWM = pgm_read_byte(&trianglM[ddsCount]);
      pwmWrite(dds,PWM);  if(ddsCount++==32) ddsCount=0;
    }
    menu++; delay(200);
  }
  if(menu==2) {
    DDSGeneratorMenu();    
    while(flag_key-digitalRead(key_up) && flag_key-digitalRead(key_down) && flag_key-digitalRead(key_ok)) {
      PWM=pgm_read_byte(&pilaM[ddsCount]);
      pwmWrite(dds,PWM);  if(ddsCount++==32) ddsCount=0; 
    }
    menu++;  delay(200);
  }
  if(menu==3) {
    DDSGeneratorMenu();    
    while(flag_key-digitalRead(key_up) && flag_key-digitalRead(key_down) && flag_key-digitalRead(key_ok)) {
      PWM = pgm_read_byte(&RpilaM[ddsCount]);
      pwmWrite(dds,PWM);  if(ddsCount++==32) ddsCount=0; 
    }
    menu=0;  delay(200);
  }
}

 

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

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

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

Electronik83 пишет:
И прерывания нужные пропускаются.
На 5510 стоит кварц в большом корпусе на 16 с кандёрами, а на ssd1306 родной маленький. Может из за кварца? Жалко подпрравить не получается. Перепаять пока не могу чтоб проверить, уже плату спрятал. 

Цитата:
Как бы сделать ВЫКЛЮЧАТЕЛЬ этой функции!?
Добавил в меню оцила ВыКлючатель Автоматической развертки. Теперь если начинается пляска, то мона не выходя из осцила отключить или включить авто режим.  

И стоит применять CyberLib.h?

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

Electronik83,  собрал на макетке, установил прошивку PULTOSCOPE_LCD5110_final2_DDS_Z_El83_18_OK , спасибо! меню очень понравилось, настройка гененратора Шикарна!  только обнаружился БАГ при изменении частоты (очень хорошо видно когда подключен к осцилографу) меняется и PWM ( на пультоскопе значения не изменяются) , когда пробегаешся по меню покругу -всё нормализуется...

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

Electronik83,  собрал на макетке, установил прошивку PULTOSCOPE_LCD5110_final2_DDS_Z_El83_18_OK , спасибо! меню очень понравилось, настройка гененратора Шикарна!  только обнаружился БАГ при изменении частоты (очень хорошо видно когда подключен к осцилографу) меняется и PWM ( на пультоскопе значения не изменяются) , когда пробегаешся по меню покругу -всё нормализуется...

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

Electronik83,  свои наблюдения по поводу индикации батареи (в главном меню полоска сверху на весь экран) - не совсем информативна т.к. она не отображает "заряд" аккума в % , а является "вольтметром" от 0 до 5в . И соответственно оценить заряд аккума можно уж очень приблизительно! Имхо если графический вид индикации осталять то он должен показывать состояние заряда в % (или на крайний случай вольтметр от 3  до 4.2v)          

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

Вот попался сайт с распиновками дисплеев http://serdisplib.sourceforge.net/ может кому пригодится.

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

tig-rrr, более старые версии отображают батарею в цифровом формате

https://yadi.sk/d/A5-4emnx3adobB

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

 

Вчера собирал пультоскоп на макетке и задумался. По схеме входной сигнал подается на выводы А3 для АЦП и D5 для частотомера. Вход D5, как я понимаю, жестко определен в библиотеке FreqCount. При этом тот же вывод D5, судя по строке 24

Adafruit_PCD8544 display = Adafruit_PCD8544(7, 6, 5, 4, 3); // пины к которым у вас подключен дисплей

используется библиотекой Adafruit_PCD8544.h для сигнала Data/Command select (D/C). Одно другому не мешает?

printer
Offline
Зарегистрирован: 23.10.2017

Okmor -зачем код ? твой ослик круче(разве что дисплей) да и автомат бы ему как у 205B Я повторил твой проект мне нравится.И Частоту он мерит верно

LegionerGUR
Offline
Зарегистрирован: 08.09.2018

Всем привет, у меня такой вопрос имеется китайская Arduino Nano. В проекте нужно заменить кварц на более высокой частоты. на у меня на плате стоит керамический резонатор, получается мне надо его выпаять и собрать схему с кварцем и конденсаторами, получится ли так обойти???!!! Или можно оставить родной резонатор, но тогда я пологаю нужно и в прошивке менять частоту, и возможно ещё что то??!

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

подскажите я чтото  не увидел какую библиотеку вы использовали для ssd1306 ?

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

подскажите я чтото  не увидел какую библиотеку вы использовали для ssd1306 ?

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

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

denis64
Offline
Зарегистрирован: 07.10.2013

удалено

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

я о той версии, что  smokok выложил

 

там, где отрисовка буфера на экран ssd1306_refresh(); // выводим все на экран

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

удалил дубль

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

удалил дубль

denis64
Offline
Зарегистрирован: 07.10.2013

MIC22 Спасибо!

 

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

Ок. Забуду.

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

https://yadi.sk/d/1fvwhbao3acrvX
SSD1306_EL83_EDITION_avto
Внутри лежит библиотека. Почти всё работает на автомате.

StingerHT
Offline
Зарегистрирован: 19.02.2018

А можно схему подключения SSD1306

LegionerGUR
Offline
Зарегистрирован: 08.09.2018

Ребята кто подскажет у меня вроде последняя версия прошивки а генератор меандра не работает при выборе этого меню виснет и все.Помогите!

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

StingerHT пишет:

А можно схему подключения SSD1306

#define ssd1306_scl 7 // пин для подключения шины SCL дисплея (можно любой пин) 

#define ssd1306_sda  6  // пин для подключения шины SDA дисплея (можно любой пин)

плюс и минус 

LegionerGUR пишет:

Ребята кто подскажет у меня вроде последняя версия прошивки а генератор меандра не работает при выборе этого меню виснет и все.Помогите!

Так установите другую прошивку. Тут никого не осталось (ясновидищих) кто будет чинить прошивки и их модернизировать. 

StingerHT
Offline
Зарегистрирован: 19.02.2018

Понял спасибо!

LegionerGUR
Offline
Зарегистрирован: 08.09.2018

 

Все исправил, немного покапался в прошивке.

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

LegionerGUR так поделитесь пожалуйста

LegionerGUR
Offline
Зарегистрирован: 08.09.2018

Скачивай на здоровье ))) https://yadi.sk/d/ZCXw4XlztTYjFw убрал из меню терминал, так как мне он не нужен.

skytter
Offline
Зарегистрирован: 18.08.2018

Всем добрый день. Буду признателен за помощь.

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

...\AppData\Local\Temp\cc4LxDZi.ltrans0.ltrans.o: In function `setup':

...\PULTOSCOPE_LCD5110_final2_DDS_Z.ino:140: undefined reference to `InitTimersSafe()'
...\PULTOSCOPE_LCD5110_final2_DDS_Z.ino:140: undefined reference to `SetPinFrequencySafe(signed char, unsigned long)'
...\PULTOSCOPE_LCD5110_final2_DDS_Z.ino:142: undefined reference to `InitTimersSafe()'
...\PULTOSCOPE_LCD5110_final2_DDS_Z.ino:142: undefined reference to `SetPinFrequencySafe(signed char, unsigned long)'

...\AppData\Local\Temp\cc4LxDZi.ltrans0.ltrans.o: In function `Generator':
...\PULTOSCOPE_LCD5110_final2_DDS_Z.ino:327: undefined reference to `SetPinFrequencySafe(signed char, unsigned long)'
...\PULTOSCOPE_LCD5110_final2_DDS_Z.ino:332: undefined reference to `SetPinFrequencySafe(signed char, unsigned long)'
...\PULTOSCOPE_LCD5110_final2_DDS_Z.ino:402: undefined reference to `pwmWrite(unsigned char, unsigned char)'

...\AppData\Local\Temp\cc4LxDZi.ltrans0.ltrans.o: In function `DDSGenerator':
...\PULTOSCOPE_LCD5110_final2_DDS_Z.ino:430: undefined reference to `pwmWrite(unsigned char, unsigned char)'
...\PULTOSCOPE_LCD5110_final2_DDS_Z.ino:458: undefined reference to `pwmWrite(unsigned char, unsigned char)'
...\PULTOSCOPE_LCD5110_final2_DDS_Z.ino:487: undefined reference to `pwmWrite(unsigned char, unsigned char)'
...\PULTOSCOPE_LCD5110_final2_DDS_Z.ino:516: undefined reference to `pwmWrite(unsigned char, unsigned char)'

collect2.exe: error: ld returned 1 exit status

насколько я смог разобраться проблема с библиотекой "PWM", вроде это вызов функций из нее - но как это исправить я не знаю...

буду признателен за любую подсказку (несколько часов поиска на вебе мне не помогли)

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

У меня тоже интересный вопрос. По умолчанию у нас ДДС-генератор и генератор меандра работают на разных выходах. Почему тогда в сетапе мы инициализируем один и тот же GEN_PIN?


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

 

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

В итоге мне все-таки удалось скрестить пультоскоп с тестером радиодеталей из вот этой темы: http://arduino.ru/forum/proekty/transistor-tester-arduino

Еще есть, над чем поработать в плане оптимизации, но в целом все функционирует правильно. Главное, что все влезло в Атмегу328 - скетч занимает меньше 27 кбайт, глобальные переменные - 1136 байт, свободно 912 байт динамической памяти. Получился практически универсальный прибор.

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

Круто!!! У меня идея скрестить  пультоскоп с NoStripGrid на отдельной атмеге. Но застрял на библиотеке экрана NoStripGrid 

_maxus
Offline
Зарегистрирован: 13.05.2017

drauger, а прожектик посмотреть можно (схему и скетч) с описанием?

 
Nikolaj666
Offline
Зарегистрирован: 19.01.2017

поддерживаю просьбу

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

Посмотреть можно. Только схему надо нарисовать, а код - причесать. Сделаю - выложу.

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

ждем с нетерпением. не затягивай в долгий ящик :)

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

В ящик, похоже, сыграл мой экран. Причесанный код даже проверить не на чем.

По схеме ничего особо сложного. Я взял базовую схему транзистор-тестера из указанной выше темы, вход осциллоскопа завел на А2, выход обоих генераторов - на пин 10. Соответственно, экран сидел на 2,3,4,6,7 (SCL, SDA, C/D, CE - не подключен, RST), на 5 - вход частотомера. Кнопки я перевел на подтяжку к питанию и посадил на А3 (ОК, он же Тест) и А4 (+). Еще одну кнопку посадил на вход Reset, чтобы для возврата в меню питание не дергать. На А5 предполагалось замерять напряжение аккумулятора. А6 свободен, была идея сделать на нем подстветку экрана, но не успел. Вроде все.

Главное, все очень удачно входило в оригинальный корпус от Nokia 3330, купленного мной еще в 2001 году! Теперь, если брать экран у китайцев, не знаю, влезет ли он в этот корпус.

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

Ыыыы. Поздравляю. Я помню то чувство, когда мой сломался. Он показал какую то ересь и перестал показывать и откликаться по шине. Жду новых. Будут в начале ноября. Возмусь за дело с новыми силами))) Нельзя их видно пятью вольтами кормить. Я про ssd1306.

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

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

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

а можно выложить код? охота посмотреть. хоть не причесаный

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

https://github.com/drauger/tt-osc

Версия 0.01 точно работает на стандартной схеме пультоскопа, только свои номера пинов нужно подставить. В версии 0.03 особых изменений нет, тут я просто начал код причесывать, но на железе еще не проверял.

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

Проверил версию 0.03, тоже все работает. Правда, вылезла неожиданная проблема. Похоже, что на моей Pro Mini не разведены выходы А6 и А7, на которые у меня кнопки заведены. Что характерно, на Nano, на которой я собирал прототип на макетке, было то же самое. Придется что-то придумывать...

tirexx
Offline
Зарегистрирован: 28.06.2014

Доброго времени суток форумчане!

Интересует сей прибор, но открыв тему понял, что почти за 3 года версия автора менялась и дополнялась неизвестное количество раз. Есть ли где то систематизированное описание версий или модификаций? Менялись ли в зависимости от версии схема прибора?

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

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

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

Electronik83,  подскажите пожалуйста, у Вас нет более новой версии ? Т.К. уже описывал в посте #3759 небольшой баг с которым столкнулся, может есть подправленная версия ? :)

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

Нет. 18-ая пока последняя. Я пока забросил этот проэкт. Жду экранчиков..