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

smokok
smokok аватар
Offline
Зарегистрирован: 08.06.2018
//void drawPixel (byte x, byte y, boolean color) {
inline __attribute__((always_inline)) void drawPixel (byte x, byte y, boolean color) {
  if ((x < 0) || (x >= 96) || (y < 0) || (y >= 68)) return;
  if (color) LCD_RAM[x + (y / 8) * 96] |= _BV(y % 8);
  else       LCD_RAM[x + (y / 8) * 96] &= ~_BV(y % 8);
}

Так всё ок! И в правду на паузе шустрее стала прокрутка и вообще.

И на посушок такой вопрос, кусокчек кода аналогичен как в 5110 и там в сельсиях, а в 1202 только в фарингейтах! Как так может быть???

double Thermister(int RawADC) {
  double Temp;
  Temp = log(((10240000/RawADC) - 10000)); // 10000=10кОм Для подстройки правим 10000.
  Temp = 1 / (0.001129148 + (0.000234125 * Temp) + (0.0000000876741 * Temp * Temp * Temp));
  Temp = Temp - 273.15;   // Kelvin to Celcius
  return Temp;
}

 

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

smokok, искусственный интеллект в деле: в коде пересчёт Кельвинов в Цельсии, а на выходе Фаренгейты... :)

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

smokok пишет:
Так всё ок! И в правду на паузе шустрее стала прокрутка и вообще.
убери из этой функции первую строку. по идее фпс должен прилично увеличиться. я ж вроде дал функцию без этой строки... закомментируй, а лучше удали нафик первую строку if ((x < 0) || (x >= 96) || (y < 0) || (y >= 68)) return; четыре сравнения, логические операции - это все не нужно тут. это проверка, не вылазит ли точка за пределы экрана. нужно избавиться от нее. просто проверь, не будет ли снова вылетать NAN. если будет - я найду его и убью))

inline __attribute__((always_inline)) void drawPixel (byte x, byte y, boolean color) {
  if (color) LCD_RAM[x + (y / 8) * 96] |= _BV(y % 8);
  else       LCD_RAM[x + (y / 8) * 96] &= ~_BV(y % 8);
}

дальше добавь где-то, например, под функцией DrawLine такую функцию:

void drawLineUltraFast(byte x, byte y0, byte y1, boolean color) {
  if(y0 < y1)
  {
    byte tmp = y0;
    y0 = y1;
    y1 = tmp;
  }
  while(y0 <= y1);
  {
    drawPixel(x, y0, color);
    y0++;
  }
}

а в самом низу 3-я строка снизу, сразу под // == Отрисовка графика == // меняй строку на 

for(uint8_t x = 0; x < 92; x++)  drawLineUltraFast(x + 4, 67 - adcBuf[x + grOffset]/kdel / 0.9, 67 - adcBuf[x + grOffset + 1]/kdel / 0.9, 1);

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

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

progrik пишет:
а в самом низу 3-я строка снизу, сразу под // == Отрисовка графика == // меняй строку на
из за неё виснет после нажатия кнопки входа в оцил и всё.                   строку if ((x < 0) || (x >= 96) || (y < 0) || (y >= 68)) return; удалил и NAN НЕ возвращается в опорном 5.0. :) (((( Можно на месенджер перейти чтобы топик не засыпать, я из Белки (могу номер сбросить). Остальные коды проглотила и летает ещё быстрее!!!

Последний изменил так и всё заработало! СПАСИБО!!!  ВИДЕО

for (uint8_t x = 0; x < 92; x++)  drawLine(x + 4, 67 - adcBuf[x + grOffset]/kdel / 0.9, x + 4, 67 - adcBuf[x + grOffset + 1]/kdel / 0.9, 1);

progrik пишет:
NAN. если будет - я найду его и убью))
Убивай))) хз где оно...  появляется спантанно иногда при включении с кнопки.  Добавил код авто-вход в осцил через 15 секунд/ и ок.


#define BUFSIZE 800
#define history 50

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

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

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

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

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

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

static const char font[] PROGMEM = {
                                  //HEX B DEC переводим
  0x00, 0x00, 0x00, 0x00, 0x00 ,  // 0x20   space
  0x00, 0x00, 0x5f, 0x00, 0x00 ,  // 0x21   !
  0x00, 0x07, 0x00, 0x07, 0x00 ,  // 0x22   "
  0x14, 0x7f, 0x14, 0x7f, 0x14 ,  // 0x23   #
  0x24, 0x2a, 0x7f, 0x2a, 0x12 ,  // 0x24   $
  0x23, 0x13, 0x08, 0x64, 0x62 ,  // 0x25   %
  0x36, 0x49, 0x55, 0x22, 0x50 ,  // 0x26   &
  0x00, 0x05, 0x03, 0x00, 0x00 ,  // 0x27   '
  0x00, 0x1c, 0x22, 0x41, 0x00 ,  // 0x28   (
  0x00, 0x41, 0x22, 0x1c, 0x00 ,  // 0x29   )
  0x14, 0x08, 0x3e, 0x08, 0x14 ,  // 0x2a   *
  0x08, 0x08, 0x3e, 0x08, 0x08 ,  // 0x2b   +
  0x00, 0x50, 0x30, 0x00, 0x00 ,  // 0x2c   ,
  0x08, 0x08, 0x08, 0x08, 0x08 ,  // 0x2d   -
  0x00, 0x60, 0x60, 0x00, 0x00 ,  // 0x2e   .
  0x20, 0x10, 0x08, 0x04, 0x02 ,  // 0x2f   /
  0x3e, 0x51, 0x49, 0x45, 0x3e ,  // 0x30   0
  0x00, 0x42, 0x7f, 0x40, 0x00 ,  // 0x31   1
  0x42, 0x61, 0x51, 0x49, 0x46 ,  // 0x32   2
  0x21, 0x41, 0x45, 0x4b, 0x31 ,  // 0x33   3
  0x18, 0x14, 0x12, 0x7f, 0x10 ,  // 0x34   4
  0x27, 0x45, 0x45, 0x45, 0x39 ,  // 0x35   5
  0x3c, 0x4a, 0x49, 0x49, 0x30 ,  // 0x36   6
  0x01, 0x71, 0x09, 0x05, 0x03 ,  // 0x37   7
  0x36, 0x49, 0x49, 0x49, 0x36 ,  // 0x38   8
  0x06, 0x49, 0x49, 0x29, 0x1e ,  // 0x39   9
  0x00, 0x36, 0x36, 0x00, 0x00 ,  // 0x3a   :
  0x00, 0x56, 0x36, 0x00, 0x00 ,  // 0x3b   ;
  0x08, 0x14, 0x22, 0x41, 0x00 ,  // 0x3c   <
  0x14, 0x14, 0x14, 0x14, 0x14 ,  // 0x3d   =
  0x00, 0x41, 0x22, 0x14, 0x08 ,  // 0x3e   >
  0x02, 0x01, 0x51, 0x09, 0x06 ,  // 0x3f   ?
  0x32, 0x49, 0x79, 0x41, 0x3e ,  // 0x40   @
  0x7e, 0x11, 0x11, 0x11, 0x7e ,  // 0x41   A
  0x7f, 0x49, 0x49, 0x49, 0x36 ,  // 0x42   B
  0x3e, 0x41, 0x41, 0x41, 0x22 ,  // 0x43   C
  0x7f, 0x41, 0x41, 0x22, 0x1c ,  // 0x44   D
  0x7f, 0x49, 0x49, 0x49, 0x41 ,  // 0x45   E
  0x7f, 0x09, 0x09, 0x09, 0x01 ,  // 0x46   F
  0x3e, 0x41, 0x49, 0x49, 0x7a ,  // 0x47   G
  0x7f, 0x08, 0x08, 0x08, 0x7f ,  // 0x48   H
  0x00, 0x41, 0x7f, 0x41, 0x00 ,  // 0x49   I
  0x20, 0x40, 0x41, 0x3f, 0x01 ,  // 0x4a   J
  0x7f, 0x08, 0x14, 0x22, 0x41 ,  // 0x4b   K
  0x7f, 0x40, 0x40, 0x40, 0x40 ,  // 0x4c   L
  0x7f, 0x02, 0x0c, 0x02, 0x7f ,  // 0x4d   M
  0x7f, 0x04, 0x08, 0x10, 0x7f ,  // 0x4e   N
  0x3e, 0x41, 0x41, 0x41, 0x3e ,  // 0x4f   O
  0x7f, 0x09, 0x09, 0x09, 0x06 ,  // 0x50   P
  0x3e, 0x41, 0x51, 0x21, 0x5e ,  // 0x51   Q
  0x7f, 0x09, 0x19, 0x29, 0x46 ,  // 0x52   R
  0x46, 0x49, 0x49, 0x49, 0x31 ,  // 0x53   S
  0x01, 0x01, 0x7f, 0x01, 0x01 ,  // 0x54   T
  0x3f, 0x40, 0x40, 0x40, 0x3f ,  // 0x55   U
  0x1f, 0x20, 0x40, 0x20, 0x1f ,  // 0x56   V
  0x3f, 0x40, 0x38, 0x40, 0x3f ,  // 0x57   W
  0x63, 0x14, 0x08, 0x14, 0x63 ,  // 0x58   X
  0x07, 0x08, 0x70, 0x08, 0x07 ,  // 0x59   Y
  0x61, 0x51, 0x49, 0x45, 0x43 ,  // 0x5a   Z
  0x00, 0x7f, 0x41, 0x41, 0x00 ,  // 0x5b   [
  0x02, 0x04, 0x08, 0x10, 0x20 ,  // 0x5c   backslash
  0x00, 0x41, 0x41, 0x7f, 0x00 ,  // 0x5d   ]
  0x04, 0x02, 0x01, 0x02, 0x04 ,  // 0x5e   ^
  0x40, 0x40, 0x40, 0x40, 0x40 ,  // 0x5f   _
  0x00, 0x01, 0x02, 0x04, 0x00 ,  // 0x60   `
  0x20, 0x54, 0x54, 0x54, 0x78 ,  // 0x61   a
  0x7f, 0x48, 0x44, 0x44, 0x38 ,  // 0x62   b
  0x38, 0x44, 0x44, 0x44, 0x20 ,  // 0x63   c
  0x38, 0x44, 0x44, 0x48, 0x7f ,  // 0x64   d
  0x38, 0x54, 0x54, 0x54, 0x18 ,  // 0x65   e
  0x08, 0x7e, 0x09, 0x01, 0x02 ,  // 0x66   f
  0x0c, 0x52, 0x52, 0x52, 0x3e ,  // 0x67   g
  0x7f, 0x08, 0x04, 0x04, 0x78 ,  // 0x68   h
  0x00, 0x44, 0x7d, 0x40, 0x00 ,  // 0x69   i
  0x20, 0x40, 0x44, 0x3d, 0x00 ,  // 0x6a   j
  0x7f, 0x10, 0x28, 0x44, 0x00 ,  // 0x6b   k
  0x00, 0x41, 0x7f, 0x40, 0x00 ,  // 0x6c   l
  0x7c, 0x04, 0x18, 0x04, 0x78 ,  // 0x6d   m
  0x7c, 0x08, 0x04, 0x04, 0x78 ,  // 0x6e   n
  0x38, 0x44, 0x44, 0x44, 0x38 ,  // 0x6f   o
  0x7c, 0x14, 0x14, 0x14, 0x08 ,  // 0x70   p
  0x08, 0x14, 0x14, 0x18, 0x7c ,  // 0x71   q
  0x7c, 0x08, 0x04, 0x04, 0x08 ,  // 0x72   r
  0x48, 0x54, 0x54, 0x54, 0x20 ,  // 0x73   s
  0x04, 0x3f, 0x44, 0x40, 0x20 ,  // 0x74   t
  0x3c, 0x40, 0x40, 0x20, 0x7c ,  // 0x75   u
  0x1c, 0x20, 0x40, 0x20, 0x1c ,  // 0x76   v
  0x3c, 0x40, 0x30, 0x40, 0x3c ,  // 0x77   w
  0x44, 0x28, 0x10, 0x28, 0x44 ,  // 0x78   x
  0x0c, 0x50, 0x50, 0x50, 0x3c ,  // 0x79   y
  0x44, 0x64, 0x54, 0x4c, 0x44 ,  // 0x7a   z
  0x00, 0x08, 0x36, 0x41, 0x00 ,  // 0x7b   {
  0x00, 0x00, 0x7f, 0x00, 0x00 ,  // 0x7c   |
  0x00, 0x41, 0x36, 0x08, 0x00 ,  // 0x7d   }
  0x10, 0x08, 0x08, 0x10, 0x08 ,  // 0x7e   ~
  0x00, 0x00, 0x00, 0x00, 0x00 ,  // 0x7f
  0x7e, 0x11, 0x11, 0x11, 0x7e ,  // 0x80   A  // Русские символы
  0x7f, 0x49, 0x49, 0x49, 0x33 ,  // 0x81   Б
  0x7f, 0x49, 0x49, 0x49, 0x36 ,  // 0x82   В
  0x7f, 0x01, 0x01, 0x01, 0x03 ,  // 0x83   Г
  0xe0, 0x51, 0x4f, 0x41, 0xff ,  // 0x84   Д
  0x7f, 0x49, 0x49, 0x49, 0x41 ,  // 0x85   E
  0x77, 0x08, 0x7f, 0x08, 0x77 ,  // 0x86   Ж
  0x41, 0x49, 0x49, 0x49, 0x36 ,  // 0x87   З
  0x7f, 0x10, 0x08, 0x04, 0x7f ,  // 0x88   И
  0x7c, 0x21, 0x12, 0x09, 0x7c ,  // 0x89   Й
  0x7f, 0x08, 0x14, 0x22, 0x41 ,  // 0x8A   K
  0x20, 0x41, 0x3f, 0x01, 0x7f ,  // 0x8B   Л
  0x7f, 0x02, 0x0c, 0x02, 0x7f ,  // 0x8C   M
  0x7f, 0x08, 0x08, 0x08, 0x7f ,  // 0x8D   H
  0x3e, 0x41, 0x41, 0x41, 0x3e ,  // 0x8E   O
  0x7f, 0x01, 0x01, 0x01, 0x7f ,  // 0x8F   П
  0x7f, 0x09, 0x09, 0x09, 0x06 ,  // 0x90   P
  0x3e, 0x41, 0x41, 0x41, 0x22 ,  // 0x91   C
  0x01, 0x01, 0x7f, 0x01, 0x01 ,  // 0x92   T
  0x47, 0x28, 0x10, 0x08, 0x07 ,  // 0x93   У
  0x1c, 0x22, 0x7f, 0x22, 0x1c ,  // 0x94   Ф
  0x63, 0x14, 0x08, 0x14, 0x63 ,  // 0x95   X
  0x7f, 0x40, 0x40, 0x40, 0xff ,  // 0x96   Ц
  0x07, 0x08, 0x08, 0x08, 0x7f ,  // 0x97   Ч
  0x7f, 0x40, 0x7f, 0x40, 0x7f ,  // 0x98   Ш
  0x7f, 0x40, 0x7f, 0x40, 0xff ,  // 0x99   Щ
  0x01, 0x7f, 0x48, 0x48, 0x30 ,  // 0x9A   Ъ
  0x7f, 0x48, 0x30, 0x00, 0x7f ,  // 0x9B   Ы
  0x00, 0x7f, 0x48, 0x48, 0x30 ,  // 0x9C   Э
  0x22, 0x41, 0x49, 0x49, 0x3e ,  // 0x9D   Ь
  0x7f, 0x08, 0x3e, 0x41, 0x3e ,  // 0x9E   Ю
  0x46, 0x29, 0x19, 0x09, 0x7f ,  // 0x9F   Я
  0x20, 0x54, 0x54, 0x54, 0x78 ,  // 0xA0   a
  0x3c, 0x4a, 0x4a, 0x49, 0x31 ,  // 0xA1   б
  0x7c, 0x54, 0x54, 0x54, 0x28 ,  // 0xA2   в
  0x7c, 0x04, 0x04, 0x04, 0x0c ,  // 0xA3   г
  0xe0, 0x54, 0x4c, 0x44, 0xfc ,  // 0xA4   д
  0x38, 0x54, 0x54, 0x54, 0x18 ,  // 0xA5   e
  0x6c, 0x10, 0x7c, 0x10, 0x6c ,  // 0xA6   ж
  0x44, 0x44, 0x54, 0x54, 0x28 ,  // 0xA7   з
  0x7c, 0x20, 0x10, 0x08, 0x7c ,  // 0xA8   и
  0x7c, 0x41, 0x22, 0x11, 0x7c ,  // 0xA9   й
  0x7c, 0x10, 0x10, 0x28, 0x44 ,  // 0xAA   к
  0x20, 0x44, 0x3c, 0x04, 0x7c ,  // 0xAB   л
  0x7c, 0x08, 0x10, 0x08, 0x7c ,  // 0xAC   м
  0x7c, 0x10, 0x10, 0x10, 0x7c ,  // 0xAD   н
  0x38, 0x44, 0x44, 0x44, 0x38 ,  // 0xAE   o
  0x7c, 0x04, 0x04, 0x04, 0x7c ,  // 0xAF   п
  0x7C, 0x14, 0x14, 0x14, 0x08 ,  // 0xB0   p
  0x38, 0x44, 0x44, 0x44, 0x20 ,  // 0xB1   c
  0x04, 0x04, 0x7c, 0x04, 0x04 ,  // 0xB2   т
  0x0C, 0x50, 0x50, 0x50, 0x3C ,  // 0xB3   у
  0x30, 0x48, 0xfc, 0x48, 0x30 ,  // 0xB4   ф
  0x44, 0x28, 0x10, 0x28, 0x44 ,  // 0xB5   x
  0x7c, 0x40, 0x40, 0x40, 0xfc ,  // 0xB6   ц
  0x0c, 0x10, 0x10, 0x10, 0x7c ,  // 0xB7   ч
  0x7c, 0x40, 0x7c, 0x40, 0x7c ,  // 0xB8   ш
  0x7c, 0x40, 0x7c, 0x40, 0xfc ,  // 0xB9   щ
  0x04, 0x7c, 0x50, 0x50, 0x20 ,  // 0xBA   ъ
  0x7c, 0x50, 0x50, 0x20, 0x7c ,  // 0xBB   ы
  0x7c, 0x50, 0x50, 0x20, 0x00 ,  // 0xBC   ь
  0x28, 0x44, 0x54, 0x54, 0x38 ,  // 0xBD   э
  0x7c, 0x10, 0x38, 0x44, 0x38 ,  // 0xBE   ю
  0x08, 0x54, 0x34, 0x14, 0x7c ,  // 0xBF   я
};
//********************************************************
// ==== Считывание напряжения питания ардуинки (Vcc) ====
#define Vref11 1.095 // для точной подстройки результата измерений
float ReadVcc() {
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); // устанавливаем вход АЦП на VCC !!!
  delay(1);  // задержка для устаканивания АЦП. Можно её избежать, если ADMUX как в строке выше (не менялся)
  ADCSRA |= _BV(ADSC);                                    // запуск АЦП преобразования
  while (bit_is_set(ADCSRA, ADSC));                       // ждем, пока АЦП отработает (выставит бит)
  return (Vref11 * 1023.0) / (ADCL | (ADCH << 8));        // результат преобразования в вольтах
}
/////// ==== Температура ==== ////////
double Thermister(int RawADC) {
  double Temp;
  Temp = log(((10240000/RawADC) - 10000)); // 10000=10кОм Для подстройки правим 10000.
  Temp = 1 / (0.001129148 + (0.000234125 * Temp) + (0.0000000876741 * Temp * Temp * Temp));
  Temp = Temp - 273.15;   // Kelvin to Celcius
  return Temp;
}
void setup()  {
  pinMode(KEY_OK, INPUT); pinMode(KEY_L, INPUT); pinMode(KEY_R, INPUT); // настраиваем кнопки на вход
  digitalWrite(KEY_OK, HIGH); digitalWrite(KEY_L, HIGH); digitalWrite(KEY_R, HIGH); // подтяжка к плюсу питания
  VCC = ReadVcc();
  Inicialize();  //Инициализация дисплея
  Clear_LCD();
  for (byte i = 0; i < 3; i++)
  {
    strcpy_P(buffer, (char*)pgm_read_word(&(string_table[i]))); // необходимые функции и расшифровки, просто скопируйте
    print(10, i * 10, 1, (buffer));
    //delay( 500 );
  }
  Update();
//  delay(KEY_DELAY);
  while (digitalRead(KEY_OK)) { // цикл, пока не нажали кнопку ОК
  Update();
  // Если не нажата 10 сек кнопка ок вход в режим осцилоографа автоматом
  osc--;
    if (osc==0) break;
  //////////  Выводим показание /////////
    float_print(60, 20, 1, analogRead(VBAT)*VCC/1024,1);  //Вольтаж батареи
    float_print(55, 40, 1, analogRead(TempC), 1);  // выводим на экран показание Температуры
  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[3]))); // пишем слово "Температура"
  print(10, 40, 1, (buffer));
  delay( 300 );
} // цикл нажатия ОК
  // нажали кнопку ОК из меню, инициализируем и выходим из меню
  count = 0; //countX = 0; // восстанавливаем на всякий случай переменные
  if (mode == 0) FreqCount.begin(1000);
  if (mode == 1) {
    InitTimersSafe();
  }
  if (mode == 2) {
    InitTimersSafe();
  }
  if (mode == 4 || mode == 5) DDRC = 0x00; PORTC = 0x00;// Serial.begin(115200); // razv = 0;// весь порт С (это A0...A5) на вход и без подтяжки
  for (char a=37; a>0; a-=2);
}
// беcконечный цикл - по сути прыгаем в подпрограммы
void loop() {
  switch (mode) {
    // Прыгаем в выбранный режим из главного меню
    case 0 : Oscil();        break; // "выпадаем" в осцилл
  }
}
//  === Читаем с АЦП данные и помещаем их в буфер === //
void ReadAdc() {
  if (razv % 10) { // (razv>0)        // если развертка без задержек всяких (с 1 по 7)
    ADCSRA = 0b11100000 | (8 - (razv % 10)); // установили делитель (/2 - не работает, так что начинаем с /4)
    for (int i = 0; i < BUFSIZE; i++) { // цикл для чтения
      while (!(ADCSRA & 0x10));     // ждем готовность АЦП
      ADCSRA |= 0x10;               // запускаем следующее преобразование
      adcBuf[i] = ADCH;             // записываем данные в массив
    }
    delay(0.3 * BUFSIZE);           // компенсация задержки по сравнению с разверткой 0...
  } else {                          // развертка с задержками (delay)
    ADCSRA = 0b11100111;            // делитель на /128
    for (int i = 0; i < BUFSIZE; i++) { // цикл для чтения
      while (!(ADCSRA & 0x10));     // ждем готовность АЦП
      ADCSRA |= 0x10;               // запускаем следующее преобразование
      delayMicroseconds(500);       // делаем задержку
      adcBuf[i] = ADCH;             // записываем данные в массив
    }
  }
}

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

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



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

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

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

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

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

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

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

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

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

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

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

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

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

//====================================================Рисование линии
void drawLine(byte x0, byte y0, byte x1, byte y1, boolean color) {
  int steep = abs(y1 - y0) > abs(x1 - x0);
  if (steep) {
    swap(x0, y0);
    swap(x1, y1);
  }
  if (x0 > x1) {
    swap(x0, x1);
    swap(y0, y1);
  }
  int dx, dy;
  dx = x1 - x0;
  dy = abs(y1 - y0);
  int err = dx / 2;
  int ystep;
  (y0 < y1) ?  ystep = 1 : ystep = -1;
  for (; x0 <= x1; x0++) {
//    (steep) ? drawPixel(y0, x0, color) : drawPixel(x0, y0, color);
if(steep)
{
  drawPixel(y0, x0, color);
}
else
{
  drawPixel(x0, y0, color);
}
    err -= dy;
    if (err < 0) {
      y0 += ystep;
      err += dx;
    }
  }
}
/////////////////добавь где-то, например, под функцией DrawLine такую функцию:////////////////////////////
void drawLineUltraFast(byte x, byte y0, byte y1, boolean color) {
  if(y0 < y1)
  {
    byte tmp = y0;
    y0 = y1;
    y1 = tmp;
  }
  while(y0 <= y1);
  {
    drawPixel(x, y0, color);
    y0++;
  }
}
//========================================Рисование вертикальной линии
void drawFastVLine(byte x, byte y, byte h, boolean color) {
  drawLine(x, y, x, y + h - 1, color);
}

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

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

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

// === Осциллоскоп === //
void Oscil() {
  // установка опорного напряжения АЦП и настройка входа АЦП
  ADMUX = vRef ? 0b01100011 : 0b11100011;
  label_ReadAdc:
  // === Обработка кнопок === //
  if (!digitalRead(KEY_L))   switch (menu) { // кнопка лево:)
      case 0 : razv--; if (razv == 255) razv = 6;                                        break; // меняем развертку
      case 1 : grOffset -= 20; if (grOffset < 0) grOffset = 0;                           break; // листаем график в паузе
    }
  if (!digitalRead(KEY_R))  switch (menu) { // кнопка право:)
      case 0 : razv++; if (razv == 7) razv = 0;                                          break; // меняем развертку
      case 1 : grOffset += 20; if (grOffset > BUFSIZE - LCDX) grOffset = BUFSIZE - LCDX; break; // листаем график в паузе
    }
  if (!digitalRead(KEY_OK))  switch (++menu) {
      case 1: grOffset = 0; pause = 1;            break; // вход в паузу - антидребезг типа
      case 2: menu = 0;  pause = 0;                break; // перебор меню
    }
     //////////////// 
  // === Ведём рассчеты === //
  if (!pause) { // если нет паузы
    ReadAdc();  // то снимаем осциллограмму
    // == Вычисляем максимальное и минимальное значение сигнала == //
    vMax = 0; vMin = 0xFF;
    for (int y = 1; y < 255; y++) {
      if (vMax < adcBuf[y]) vMax = adcBuf[y];  // пока 255, но надо экспериментировать
      if (vMin > adcBuf[y]) vMin = adcBuf[y];
    }
    vSync = (vMax - vMin) / 2 + vMin; // уровень синхронизации по середине уровня сигнала
    // == Определение точки синхронизации == //
    bool flagZero = 0; grOffset = 0; // сброс флага и точки синхронизации
    // Ищем перепад от меньшего уровня к большему
    for (int y = 1; y < BUFSIZE - LCDX; y++) { // смотрим весь массив данных АЦП
      if (adcBuf[y] < vSync) flagZero = 1; // нашли меньше, чем синхра (перепад сигнала в минус) - ставим флаг
      if (flagZero && adcBuf[y] > vSync) {
        grOffset = y;  // нашли больше, чем синхра (перепад сигнала в плюс) - запомнили и выходим из цикла
        break;
      }
    }
    // === Считаем частоту сигнала === //
    if (vRef && vMax * VCC / 255 > 2.7) { // если можем замерить аппаратно - меряем
      if (FreqCount.available()) count = FreqCount.read(); // вывод частоты по готовности счетчика частоты сигнала
    } else { // === Меряем частоту сигнала программно === //
      flagZero = 0; count = 0; // сброс флага и счетчика
      for (int y = grOffset; y < BUFSIZE - LCDX; y++)  { // смотрим массив от точки синхронизации до конца
        if (adcBuf[y] < vSync) flagZero = 1; // нашли меньше, чем синхра (перепад сигнала в минус) - выставляем флаг
        if (flagZero && adcBuf[y] > vSync) { // нашли больше, чем синхра (перепад сигнала в плюс) - отловили полный период
          switch (razv) { // считем частоту периода
            case 6: count = 1000000 / ((y - grOffset - 1) * 3.25);   break; // делитель 4
            case 5: count = 1000000 / ((y - grOffset)  * 3.25) / 2;  break; // делитель 8
            case 4: count = 1000000 / ((y - grOffset)  * 3.25) / 4;  break; // делитель 16
            case 3: count = 1000000 / ((y - grOffset)  * 3.25) / 8;  break; // делитель 32
            case 2: count = 1000000 / ((y - grOffset)  * 3.25) / 16; break; // делитель 64
            case 1: count = 1000000 / ((y - grOffset)  * 3.25) / 32; break; // делитель 128
            case 0: count = 1000000 / ((y - grOffset)  * 510);       break; // делитель 128 тоже
          }
          break;
        }
      }
    }
    count = count * OVERCLOCK / 16.0; // пересчет частоты на разные кварцы
  } // закончили вести рассчеты
  Clear_LCD(); // чистим экран...
  // === Отрисовка меню осцилла ===
//  if (vRef) TextColor = 0; else TextColor = 1;
  if (vRef) float_print(0, 0, 1, VCC, 1); else {
  if(kdel==5)print(0, 0, 1, "1.1"); else print(0, 0, 1, "0.2");
  }
  if (menu == 0) TextColor = 0; else TextColor = 1;  drawChar(21, 0,  1, 'р'); print(27, 0,  1, razv);
  if (menu == 1) { // тут задержка для компенсации с остальными задержками
    delay(100);
//    print(48, 0, 0, " Pause ");
    strcpy_P(buffer, (char*)pgm_read_word(&(string_table[4]))); // необходимые функции и расшифровки, просто скопируйте
    print(48, 0, 0, (buffer));
  } else TextColor = 1;
  if (!pause) float_print (48, 0, 1, count / 1000000.0, 6); // вывод частоты
  else { // рисуем прокрутку в режиме паузы
    temp = grOffset / 8;
    drawFastHLine(temp, 10, 6, 1);  drawFastHLine(temp, 9, 6, 1);
  } // шкала прокрутки
  if (vRef && vMax * VCC / 255 > 2.7)  drawChar(37, 0, 1, 'А'); // если замер  - "а"ппаратный
  else  drawChar(37, 0, 1, 'П'); // иначе "п"програмный
  float_print(73, 12, 1, vMax * (vRef ? VCC : 1.1) / 255, 2); // рисуем максимальное напряжение сигнала
  float_print(78, 54, 1, vMin * (vRef ? VCC : 1.1) / 255, 1); // рисуем минимальное  напряжение сигнала
  if (vMax==0xFF) for(int y=10; y<68; y++) drawFastHLine( 0, y, 5, 5); // превышение максимума АЦП
  // == Отрисовка сетки == //
  for (byte i = 11; i < 68; i = i + 14) {
    drawFastHLine( 0, i, 2, 1);  // черточки слева
  }
  for (byte i = 67; i > 10; i = i - 4) {
    drawPixel(24, i, 1);
    drawPixel(48, i, 1);
    drawPixel(72, i, 1);
  }
  for (byte i = 8; i < 96; i = i + 4) {
    drawPixel(i, 39, 1);
  }
             ///////////////авто razv//////////////
  if (avtorazv)
  #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;
      //bulat если зашкаливает включаем предел 5 в  
  if (vMax==255){
    if (vRef==0)
       {
        vRef=1;
       ADMUX = 0b01100011;// выбор внутреннего опорного 5.0 В
           goto   label_ReadAdc;
     }
  }
//bulat если  5 в  и уровень менее 1,1 в то вкл предел 1,1 в
  if (vMax<=55){
    if (vRef==1)
       {
        vRef=0;
        ADMUX = 0b11100011;// выбор внутреннего опорного 1,1В
        goto   label_ReadAdc;
     }
  }
  //bulat здесь автопредел 1,1/0,22 в,программный
  kdel=5;
  if (vMax<=55){
    if (vRef==0)
       {
       kdel=1;
     }
  }
  // == Отрисовка графика == //
//  for (uint8_t x = 0; x < 92; x++)  drawLineUltraFast(x + 4, 67 - adcBuf[x + grOffset]/kdel / 0.9, 67 - adcBuf[x + grOffset + 1]/kdel / 0.9, 1);
  for (uint8_t x = 0; x < 92; x++)  drawLine(x + 4, 67 - adcBuf[x + grOffset]/kdel / 0.9, x + 4, 67 - adcBuf[x + grOffset + 1]/kdel / 0.9, 1);
  Update();
}
progrik
Offline
Зарегистрирован: 30.12.2018

smokok верни как я говорил, ниже // == Отрисовка графика == // строку c drawLineUltraFast раскомментируй, с drawLine закомментируй. а в самой функции drawLineUltraFast в строке drawPixel(x, y0, color); поменяй в вызове функции x и y0 местами. вот так: drawPixel(y0, x, color); и пробуй.

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

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

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

ЗЫ: а зачем тебе это? у тебя же на фотке дсо138 с паршивкой от GFX вроде. как гласит реклама, "зачем вам этот геморрой?"))

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

А можно выложить версию чисто под 5110 и oled ,чтоб только один ослик в меню?

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

progrik пишет:
не запустится функция drawLineUltraFast - верни все взад)) без дисплея совсем грустно такой фигней заниматься.
Вернул, а то виснет  даже во время авто-входа. Был бы я рядом, дал бы дисплей погонять, за одно и отдохнул бы))).
progrik пишет:
ЗЫ: а зачем тебе это? у тебя же на фотке дсо138 с паршивкой от GFX вроде. как гласит реклама, "зачем вам этот геморрой?"))
Зачем... Первое это интересно и так же Под этот экран думал тахометр для мотопеда собрать, но так бедно скетча нет и чтоб не пропало осцил вгрузил. Соберу чтоб не валялось, подганю кому нибудь экслюзивчик.  Да и многим пригодятся Ваши советы и работа других людей с этого форума для самоделки. GFX конечно далеко впереди (туда бы ещё в паршивку  авторазв. добавить), но с ProMini 328P не нужно парится с ремонтом для кого то, если сломалась подменил  на новый модуль и всё, не жалко))).  Как то так))). Ещё раз Спасибо за советы!!!

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

а где делось объявление inline __attribute__((always_inline)) void drawPixel(...??? у тебя просто void drawPixel()... ДОПИШИ чтоб стало inline __attribute__((always_inline)) void drawPixel(....

и я нашел... перепутал... вместо "<" нужно ">" ... одна ошибка, и точки не выводятся... поменяй функцию drawLineUltraFast

void drawLineUltraFast(byte x, byte y0, byte y1, boolean color) {
  if(y0 > y1)  //здесь была ошибка, стоял оператор "<"
  {
    byte tmp = y0;
    y0 = y1;
    y1 = tmp;
  }
  while(y0 <= y1);
  {
    drawPixel(x, y0, color);
    y0++;
  }
}

а снизу раскоментируй строку с вызовом drawLineUltraFast, а другую с drawLine закомментируй. скажешь, чё как/

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

fly245 пишет:
А можно выложить версию чисто под 5110 и oled ,чтоб только один ослик в меню?
Можно, только здесь много версий и какая вам нужна, вам решать.

progrik пишет:
а снизу раскоментируй строку с вызовом drawLineUltraFast, а другую с drawLine закомментируй. скажешь, чё как/
Выше сделал, зашло. А снизу тупик при запуске. Только пока так.  y и х менял местами в drawLineUltraFast, не компилирует.

//  for (uint8_t y = 0; y < 92; y++)  drawLineUltraFast(y + 4, 67 - adcBuf[y + grOffset]/kdel / 0.9, 67 - adcBuf[y + grOffset + 1]/kdel / 0.9, 1);
    for (uint8_t x = 0; x < 92; x++)  drawLine(x + 4, 67 - adcBuf[x + grOffset]/kdel / 0.9, x + 4, 67 - adcBuf[x + grOffset + 1]/kdel / 0.9, 1);

 

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

smokok блин. я после while(y0 <= y1) случайно запилил точку с запятой, а вместо "больше" поставил "меньше". в 3-х строках две ошибки. учитесь, пока я жив))

а не компилится - та ладно)... сейчас дам полный код, у меня компилируется

#define BUFSIZE 800
#define history 50

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

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

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

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

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

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

static const char font[] PROGMEM = {
                                  //HEX B DEC переводим
  0x00, 0x00, 0x00, 0x00, 0x00 ,  // 0x20   space
  0x00, 0x00, 0x5f, 0x00, 0x00 ,  // 0x21   !
  0x00, 0x07, 0x00, 0x07, 0x00 ,  // 0x22   "
  0x14, 0x7f, 0x14, 0x7f, 0x14 ,  // 0x23   #
  0x24, 0x2a, 0x7f, 0x2a, 0x12 ,  // 0x24   $
  0x23, 0x13, 0x08, 0x64, 0x62 ,  // 0x25   %
  0x36, 0x49, 0x55, 0x22, 0x50 ,  // 0x26   &
  0x00, 0x05, 0x03, 0x00, 0x00 ,  // 0x27   '
  0x00, 0x1c, 0x22, 0x41, 0x00 ,  // 0x28   (
  0x00, 0x41, 0x22, 0x1c, 0x00 ,  // 0x29   )
  0x14, 0x08, 0x3e, 0x08, 0x14 ,  // 0x2a   *
  0x08, 0x08, 0x3e, 0x08, 0x08 ,  // 0x2b   +
  0x00, 0x50, 0x30, 0x00, 0x00 ,  // 0x2c   ,
  0x08, 0x08, 0x08, 0x08, 0x08 ,  // 0x2d   -
  0x00, 0x60, 0x60, 0x00, 0x00 ,  // 0x2e   .
  0x20, 0x10, 0x08, 0x04, 0x02 ,  // 0x2f   /
  0x3e, 0x51, 0x49, 0x45, 0x3e ,  // 0x30   0
  0x00, 0x42, 0x7f, 0x40, 0x00 ,  // 0x31   1
  0x42, 0x61, 0x51, 0x49, 0x46 ,  // 0x32   2
  0x21, 0x41, 0x45, 0x4b, 0x31 ,  // 0x33   3
  0x18, 0x14, 0x12, 0x7f, 0x10 ,  // 0x34   4
  0x27, 0x45, 0x45, 0x45, 0x39 ,  // 0x35   5
  0x3c, 0x4a, 0x49, 0x49, 0x30 ,  // 0x36   6
  0x01, 0x71, 0x09, 0x05, 0x03 ,  // 0x37   7
  0x36, 0x49, 0x49, 0x49, 0x36 ,  // 0x38   8
  0x06, 0x49, 0x49, 0x29, 0x1e ,  // 0x39   9
  0x00, 0x36, 0x36, 0x00, 0x00 ,  // 0x3a   :
  0x00, 0x56, 0x36, 0x00, 0x00 ,  // 0x3b   ;
  0x08, 0x14, 0x22, 0x41, 0x00 ,  // 0x3c   <
  0x14, 0x14, 0x14, 0x14, 0x14 ,  // 0x3d   =
  0x00, 0x41, 0x22, 0x14, 0x08 ,  // 0x3e   >
  0x02, 0x01, 0x51, 0x09, 0x06 ,  // 0x3f   ?
  0x32, 0x49, 0x79, 0x41, 0x3e ,  // 0x40   @
  0x7e, 0x11, 0x11, 0x11, 0x7e ,  // 0x41   A
  0x7f, 0x49, 0x49, 0x49, 0x36 ,  // 0x42   B
  0x3e, 0x41, 0x41, 0x41, 0x22 ,  // 0x43   C
  0x7f, 0x41, 0x41, 0x22, 0x1c ,  // 0x44   D
  0x7f, 0x49, 0x49, 0x49, 0x41 ,  // 0x45   E
  0x7f, 0x09, 0x09, 0x09, 0x01 ,  // 0x46   F
  0x3e, 0x41, 0x49, 0x49, 0x7a ,  // 0x47   G
  0x7f, 0x08, 0x08, 0x08, 0x7f ,  // 0x48   H
  0x00, 0x41, 0x7f, 0x41, 0x00 ,  // 0x49   I
  0x20, 0x40, 0x41, 0x3f, 0x01 ,  // 0x4a   J
  0x7f, 0x08, 0x14, 0x22, 0x41 ,  // 0x4b   K
  0x7f, 0x40, 0x40, 0x40, 0x40 ,  // 0x4c   L
  0x7f, 0x02, 0x0c, 0x02, 0x7f ,  // 0x4d   M
  0x7f, 0x04, 0x08, 0x10, 0x7f ,  // 0x4e   N
  0x3e, 0x41, 0x41, 0x41, 0x3e ,  // 0x4f   O
  0x7f, 0x09, 0x09, 0x09, 0x06 ,  // 0x50   P
  0x3e, 0x41, 0x51, 0x21, 0x5e ,  // 0x51   Q
  0x7f, 0x09, 0x19, 0x29, 0x46 ,  // 0x52   R
  0x46, 0x49, 0x49, 0x49, 0x31 ,  // 0x53   S
  0x01, 0x01, 0x7f, 0x01, 0x01 ,  // 0x54   T
  0x3f, 0x40, 0x40, 0x40, 0x3f ,  // 0x55   U
  0x1f, 0x20, 0x40, 0x20, 0x1f ,  // 0x56   V
  0x3f, 0x40, 0x38, 0x40, 0x3f ,  // 0x57   W
  0x63, 0x14, 0x08, 0x14, 0x63 ,  // 0x58   X
  0x07, 0x08, 0x70, 0x08, 0x07 ,  // 0x59   Y
  0x61, 0x51, 0x49, 0x45, 0x43 ,  // 0x5a   Z
  0x00, 0x7f, 0x41, 0x41, 0x00 ,  // 0x5b   [
  0x02, 0x04, 0x08, 0x10, 0x20 ,  // 0x5c   backslash
  0x00, 0x41, 0x41, 0x7f, 0x00 ,  // 0x5d   ]
  0x04, 0x02, 0x01, 0x02, 0x04 ,  // 0x5e   ^
  0x40, 0x40, 0x40, 0x40, 0x40 ,  // 0x5f   _
  0x00, 0x01, 0x02, 0x04, 0x00 ,  // 0x60   `
  0x20, 0x54, 0x54, 0x54, 0x78 ,  // 0x61   a
  0x7f, 0x48, 0x44, 0x44, 0x38 ,  // 0x62   b
  0x38, 0x44, 0x44, 0x44, 0x20 ,  // 0x63   c
  0x38, 0x44, 0x44, 0x48, 0x7f ,  // 0x64   d
  0x38, 0x54, 0x54, 0x54, 0x18 ,  // 0x65   e
  0x08, 0x7e, 0x09, 0x01, 0x02 ,  // 0x66   f
  0x0c, 0x52, 0x52, 0x52, 0x3e ,  // 0x67   g
  0x7f, 0x08, 0x04, 0x04, 0x78 ,  // 0x68   h
  0x00, 0x44, 0x7d, 0x40, 0x00 ,  // 0x69   i
  0x20, 0x40, 0x44, 0x3d, 0x00 ,  // 0x6a   j
  0x7f, 0x10, 0x28, 0x44, 0x00 ,  // 0x6b   k
  0x00, 0x41, 0x7f, 0x40, 0x00 ,  // 0x6c   l
  0x7c, 0x04, 0x18, 0x04, 0x78 ,  // 0x6d   m
  0x7c, 0x08, 0x04, 0x04, 0x78 ,  // 0x6e   n
  0x38, 0x44, 0x44, 0x44, 0x38 ,  // 0x6f   o
  0x7c, 0x14, 0x14, 0x14, 0x08 ,  // 0x70   p
  0x08, 0x14, 0x14, 0x18, 0x7c ,  // 0x71   q
  0x7c, 0x08, 0x04, 0x04, 0x08 ,  // 0x72   r
  0x48, 0x54, 0x54, 0x54, 0x20 ,  // 0x73   s
  0x04, 0x3f, 0x44, 0x40, 0x20 ,  // 0x74   t
  0x3c, 0x40, 0x40, 0x20, 0x7c ,  // 0x75   u
  0x1c, 0x20, 0x40, 0x20, 0x1c ,  // 0x76   v
  0x3c, 0x40, 0x30, 0x40, 0x3c ,  // 0x77   w
  0x44, 0x28, 0x10, 0x28, 0x44 ,  // 0x78   x
  0x0c, 0x50, 0x50, 0x50, 0x3c ,  // 0x79   y
  0x44, 0x64, 0x54, 0x4c, 0x44 ,  // 0x7a   z
  0x00, 0x08, 0x36, 0x41, 0x00 ,  // 0x7b   {
  0x00, 0x00, 0x7f, 0x00, 0x00 ,  // 0x7c   |
  0x00, 0x41, 0x36, 0x08, 0x00 ,  // 0x7d   }
  0x10, 0x08, 0x08, 0x10, 0x08 ,  // 0x7e   ~
  0x00, 0x00, 0x00, 0x00, 0x00 ,  // 0x7f
  0x7e, 0x11, 0x11, 0x11, 0x7e ,  // 0x80   A  // Русские символы
  0x7f, 0x49, 0x49, 0x49, 0x33 ,  // 0x81   Б
  0x7f, 0x49, 0x49, 0x49, 0x36 ,  // 0x82   В
  0x7f, 0x01, 0x01, 0x01, 0x03 ,  // 0x83   Г
  0xe0, 0x51, 0x4f, 0x41, 0xff ,  // 0x84   Д
  0x7f, 0x49, 0x49, 0x49, 0x41 ,  // 0x85   E
  0x77, 0x08, 0x7f, 0x08, 0x77 ,  // 0x86   Ж
  0x41, 0x49, 0x49, 0x49, 0x36 ,  // 0x87   З
  0x7f, 0x10, 0x08, 0x04, 0x7f ,  // 0x88   И
  0x7c, 0x21, 0x12, 0x09, 0x7c ,  // 0x89   Й
  0x7f, 0x08, 0x14, 0x22, 0x41 ,  // 0x8A   K
  0x20, 0x41, 0x3f, 0x01, 0x7f ,  // 0x8B   Л
  0x7f, 0x02, 0x0c, 0x02, 0x7f ,  // 0x8C   M
  0x7f, 0x08, 0x08, 0x08, 0x7f ,  // 0x8D   H
  0x3e, 0x41, 0x41, 0x41, 0x3e ,  // 0x8E   O
  0x7f, 0x01, 0x01, 0x01, 0x7f ,  // 0x8F   П
  0x7f, 0x09, 0x09, 0x09, 0x06 ,  // 0x90   P
  0x3e, 0x41, 0x41, 0x41, 0x22 ,  // 0x91   C
  0x01, 0x01, 0x7f, 0x01, 0x01 ,  // 0x92   T
  0x47, 0x28, 0x10, 0x08, 0x07 ,  // 0x93   У
  0x1c, 0x22, 0x7f, 0x22, 0x1c ,  // 0x94   Ф
  0x63, 0x14, 0x08, 0x14, 0x63 ,  // 0x95   X
  0x7f, 0x40, 0x40, 0x40, 0xff ,  // 0x96   Ц
  0x07, 0x08, 0x08, 0x08, 0x7f ,  // 0x97   Ч
  0x7f, 0x40, 0x7f, 0x40, 0x7f ,  // 0x98   Ш
  0x7f, 0x40, 0x7f, 0x40, 0xff ,  // 0x99   Щ
  0x01, 0x7f, 0x48, 0x48, 0x30 ,  // 0x9A   Ъ
  0x7f, 0x48, 0x30, 0x00, 0x7f ,  // 0x9B   Ы
  0x00, 0x7f, 0x48, 0x48, 0x30 ,  // 0x9C   Э
  0x22, 0x41, 0x49, 0x49, 0x3e ,  // 0x9D   Ь
  0x7f, 0x08, 0x3e, 0x41, 0x3e ,  // 0x9E   Ю
  0x46, 0x29, 0x19, 0x09, 0x7f ,  // 0x9F   Я
  0x20, 0x54, 0x54, 0x54, 0x78 ,  // 0xA0   a
  0x3c, 0x4a, 0x4a, 0x49, 0x31 ,  // 0xA1   б
  0x7c, 0x54, 0x54, 0x54, 0x28 ,  // 0xA2   в
  0x7c, 0x04, 0x04, 0x04, 0x0c ,  // 0xA3   г
  0xe0, 0x54, 0x4c, 0x44, 0xfc ,  // 0xA4   д
  0x38, 0x54, 0x54, 0x54, 0x18 ,  // 0xA5   e
  0x6c, 0x10, 0x7c, 0x10, 0x6c ,  // 0xA6   ж
  0x44, 0x44, 0x54, 0x54, 0x28 ,  // 0xA7   з
  0x7c, 0x20, 0x10, 0x08, 0x7c ,  // 0xA8   и
  0x7c, 0x41, 0x22, 0x11, 0x7c ,  // 0xA9   й
  0x7c, 0x10, 0x10, 0x28, 0x44 ,  // 0xAA   к
  0x20, 0x44, 0x3c, 0x04, 0x7c ,  // 0xAB   л
  0x7c, 0x08, 0x10, 0x08, 0x7c ,  // 0xAC   м
  0x7c, 0x10, 0x10, 0x10, 0x7c ,  // 0xAD   н
  0x38, 0x44, 0x44, 0x44, 0x38 ,  // 0xAE   o
  0x7c, 0x04, 0x04, 0x04, 0x7c ,  // 0xAF   п
  0x7C, 0x14, 0x14, 0x14, 0x08 ,  // 0xB0   p
  0x38, 0x44, 0x44, 0x44, 0x20 ,  // 0xB1   c
  0x04, 0x04, 0x7c, 0x04, 0x04 ,  // 0xB2   т
  0x0C, 0x50, 0x50, 0x50, 0x3C ,  // 0xB3   у
  0x30, 0x48, 0xfc, 0x48, 0x30 ,  // 0xB4   ф
  0x44, 0x28, 0x10, 0x28, 0x44 ,  // 0xB5   x
  0x7c, 0x40, 0x40, 0x40, 0xfc ,  // 0xB6   ц
  0x0c, 0x10, 0x10, 0x10, 0x7c ,  // 0xB7   ч
  0x7c, 0x40, 0x7c, 0x40, 0x7c ,  // 0xB8   ш
  0x7c, 0x40, 0x7c, 0x40, 0xfc ,  // 0xB9   щ
  0x04, 0x7c, 0x50, 0x50, 0x20 ,  // 0xBA   ъ
  0x7c, 0x50, 0x50, 0x20, 0x7c ,  // 0xBB   ы
  0x7c, 0x50, 0x50, 0x20, 0x00 ,  // 0xBC   ь
  0x28, 0x44, 0x54, 0x54, 0x38 ,  // 0xBD   э
  0x7c, 0x10, 0x38, 0x44, 0x38 ,  // 0xBE   ю
  0x08, 0x54, 0x34, 0x14, 0x7c ,  // 0xBF   я
};
//********************************************************
// ==== Считывание напряжения питания ардуинки (Vcc) ====
#define Vref11 1.095 // для точной подстройки результата измерений
float ReadVcc() {
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); // устанавливаем вход АЦП на VCC !!!
  delay(1);  // задержка для устаканивания АЦП. Можно её избежать, если ADMUX как в строке выше (не менялся)
  ADCSRA |= _BV(ADSC);                                    // запуск АЦП преобразования
  while (bit_is_set(ADCSRA, ADSC));                       // ждем, пока АЦП отработает (выставит бит)
  return (Vref11 * 1023.0) / (ADCL | (ADCH << 8));        // результат преобразования в вольтах
}
/////// ==== Температура ==== ////////
double Thermister(int RawADC) {
  double Temp;
  Temp = log(((10240000/RawADC) - 10000)); // 10000=10кОм Для подстройки правим 10000.
  Temp = 1 / (0.001129148 + (0.000234125 * Temp) + (0.0000000876741 * Temp * Temp * Temp));
  Temp = Temp - 273.15;   // Kelvin to Celcius
  return Temp;
}
void setup()  {
  pinMode(KEY_OK, INPUT); pinMode(KEY_L, INPUT); pinMode(KEY_R, INPUT); // настраиваем кнопки на вход
  digitalWrite(KEY_OK, HIGH); digitalWrite(KEY_L, HIGH); digitalWrite(KEY_R, HIGH); // подтяжка к плюсу питания
  VCC = ReadVcc();
  Inicialize();  //Инициализация дисплея
  Clear_LCD();
  for (byte i = 0; i < 3; i++)
  {
    strcpy_P(buffer, (char*)pgm_read_word(&(string_table[i]))); // необходимые функции и расшифровки, просто скопируйте
    print(10, i * 10, 1, (buffer));
    //delay( 500 );
  }
  Update();
//  delay(KEY_DELAY);
  while (digitalRead(KEY_OK)) { // цикл, пока не нажали кнопку ОК
  Update();
  // Если не нажата 10 сек кнопка ок вход в режим осцилоографа автоматом
  osc--;
    if (osc==0) break;
  //////////  Выводим показание /////////
    float_print(60, 20, 1, analogRead(VBAT)*VCC/1024,1);  //Вольтаж батареи
    float_print(55, 40, 1, analogRead(TempC), 1);  // выводим на экран показание Температуры
  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[3]))); // пишем слово "Температура"
  print(10, 40, 1, (buffer));
  delay( 300 );
} // цикл нажатия ОК
  // нажали кнопку ОК из меню, инициализируем и выходим из меню
  count = 0; //countX = 0; // восстанавливаем на всякий случай переменные
  if (mode == 0) FreqCount.begin(1000);
  if (mode == 1) {
    InitTimersSafe();
  }
  if (mode == 2) {
    InitTimersSafe();
  }
  if (mode == 4 || mode == 5) DDRC = 0x00; PORTC = 0x00;// Serial.begin(115200); // razv = 0;// весь порт С (это A0...A5) на вход и без подтяжки
  for (char a=37; a>0; a-=2);
}
// беcконечный цикл - по сути прыгаем в подпрограммы
void loop() {
  switch (mode) {
    // Прыгаем в выбранный режим из главного меню
    case 0 : Oscil();        break; // "выпадаем" в осцилл
  }
}
//  === Читаем с АЦП данные и помещаем их в буфер === //
void ReadAdc() {
  if (razv % 10) { // (razv>0)        // если развертка без задержек всяких (с 1 по 7)
    ADCSRA = 0b11100000 | (8 - (razv % 10)); // установили делитель (/2 - не работает, так что начинаем с /4)
    for (int i = 0; i < BUFSIZE; i++) { // цикл для чтения
      while (!(ADCSRA & 0x10));     // ждем готовность АЦП
      ADCSRA |= 0x10;               // запускаем следующее преобразование
      adcBuf[i] = ADCH;             // записываем данные в массив
    }
    delay(0.3 * BUFSIZE);           // компенсация задержки по сравнению с разверткой 0...
  } else {                          // развертка с задержками (delay)
    ADCSRA = 0b11100111;            // делитель на /128
    for (int i = 0; i < BUFSIZE; i++) { // цикл для чтения
      while (!(ADCSRA & 0x10));     // ждем готовность АЦП
      ADCSRA |= 0x10;               // запускаем следующее преобразование
      delayMicroseconds(500);       // делаем задержку
      adcBuf[i] = ADCH;             // записываем данные в массив
    }
  }
}

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

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



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

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

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

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

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

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

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

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

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

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

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

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

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

//====================================================Рисование линии
void drawLine(byte x0, byte y0, byte x1, byte y1, boolean color) {
  int steep = abs(y1 - y0) > abs(x1 - x0);
  if (steep) {
    swap(x0, y0);
    swap(x1, y1);
  }
  if (x0 > x1) {
    swap(x0, x1);
    swap(y0, y1);
  }
  int dx, dy;
  dx = x1 - x0;
  dy = abs(y1 - y0);
  int err = dx / 2;
  int ystep;
  (y0 < y1) ?  ystep = 1 : ystep = -1;
  for (; x0 <= x1; x0++) {
//    (steep) ? drawPixel(y0, x0, color) : drawPixel(x0, y0, color);
if(steep)
{
  drawPixel(y0, x0, color);
}
else
{
  drawPixel(x0, y0, color);
}
    err -= dy;
    if (err < 0) {
      y0 += ystep;
      err += dx;
    }
  }
}
/////////////////добавь где-то, например, под функцией DrawLine такую функцию:////////////////////////////
void drawLineUltraFast(byte x, byte y0, byte y1, boolean color) {
  if(y0 > y1)
  {
    byte tmp = y0;
    y0 = y1;
    y1 = tmp;
  }
  while(y0 <= y1)
  {
    drawPixel(x, y0, color);
    y0++;
  }
}
//========================================Рисование вертикальной линии
void drawFastVLine(byte x, byte y, byte h, boolean color) {
  drawLine(x, y, x, y + h - 1, color);
}

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

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

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

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

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

только что изменил, забыл добавить inline __attribute__((always_inline)) перед void drawPixel... проверяй

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

Учусь - учусь))). Всё норм, запускается! Теперь другое дело, повеселее! Спасибо! Можно считать проект допилен....))). Я скоро на работу до позна ухожу, буду не на связи. Видос делать?

Мой комп при загрузке скетча в синий экран смерти уходит на 10-ке (крыша едет), как я ещё выдерживаю?)))

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

да, давай видос) пожалуйста) поздравляю с допилом))

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

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

nervol
Offline
Зарегистрирован: 04.03.2020

del

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

Подскажите ,почему повторяются строчки?

Serial.begin(9600);
  #ifdef  DEBUG 
 Serial.begin(9600);
 #endif

Не компилиться с ошибкой что нет PWM.h: No such file or directory .Добавил в проект  PWM.h -все равно такая же ошибка ..версия IDE 1.8.11

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

Ничего себе, неделю на форум не заглядывал, а здесь такой прогресс!
progrik, я все функции повытаскивал из чужой библы, ещё и повыбрасывал лишнее, насколько хватает познаний. Просто когда мы пользуемся чужими библиотеками, то многого не замечаем, а здесь всё на виду. Отказ от проверки в drawPixel чреват последствиями, когда контроллер виснет и не реагирует ни на что до перезагрузки, я сталкивался.
Насчёт аппаратного SPI хотелось бы поподробней, как его применить для 9-битной передачи данных на STE2007 (LCD NOKIA 1202/1230)?
И ещё, можно все рекомендации за последнюю неделю форума свести пошагово в одном посте? А то информации много, сложно разобраться.
 

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

seri0shka пишет:
Отказ от проверки в drawPixel чреват последствиями, когда контроллер виснет и не реагирует ни на что до перезагрузки, я сталкивался.
еще раз: проверка нужна только для библиотеки, которая будет работать даже у дурачка. а если ты не будешь пытаться выводить пиксели за пределы дисплея - она абсолютно не нужна. там в сравнении проверяется byte на "меньше ноля", это БРЕД. _подумай_, какой будет результат byte x=255; while(--x >= 0); сколько проработает цикл, когда байт станет меньше ноля? (цикл вечный, байт не может принимать отрицательные значения). хватит болтать про последствия, если не понимаешь, что в коде! а там трындец полный))

_кодер_ ОБЯЗАН знать, что делает _каждая_ строка, знать каждую переменную в своем коде. в этом коде шлак засыпан песком и пылью... кода здесь нету.

прикинем, сколько времени шлется 1 байт через функцию SendByte. в ней dWrite вызывается _28_ раз на байт!!! постоянно прижимается чипселект, хотя нужно всего один раз... если она была бы пустая - все равно по 8-12 тактов за вызов, итого 28*8 = минимум 224(!) такта на один байт. и dWrite еще тормознет патамушта и там бред, из 4 строк и кучи ереси нужно всего применить маску к порту. два такта... зачем каждый раз вычислять пин, ты не помнишь куда прицепил проводок? или если не вычислять, то зависнет и перестанет реагировать?... итого получаем 300-500-700-... тактов на байт... это фиаско. это пи*дец _полный_.

аппаратный SPI - есть подозрения, что можно дослать 1 бит руками. но это не точно. я потом узнал, что он 9 бит и авр не держит его.

вот это (mode) ? dWrite(Data, 1) : dWrite(Data, 0); а надо dWrite(Data, mode); в mode уже передается 1 или 0... еще два-три такта наши. там _везде такое_!! долго расписывать... так что даже не юзая аппаратный SPI можно еще раз в 10 ускорить отрисовку. проблема в том, что это уже не поможет. разве что на паузе. остальной код тормозит. сравни видео smokok где без ускорения (на прошлой странице, в посте где он видео с кодом выложил) и с ускорением(чуть выше). при работе почти незаметно. на паузе на перемотке раза в 2,5 быстрее.. в отрисовке код уже менее кривой. но еще кривой. остальной код мне не интересен в силу его слабости. я уже писал, что это все нужно переписать так, что б ты мог себе дать ответ на вопрос про каждую строку, переменную, вызов функции, указатель, время выполнения и возможные способы ускорения. научишься так кодить и задавать себе такие вопросы и знать/находить ответы - код полетит. до этого - только боль)))

вот смотри, в моей функции drawLineUltraFast есть код

  while(y0 <= y1)
  {
    drawPixel(x, y0, color);
    y0++;
  }

вроде все норм. НО. раз кто-то вызвал функцию рисовать линию, то по крайней мере один пиксель будет нарисован. а это значит, что _перед_ первым вызовом drawPixel _не_нужно_ ничего проверять. и получаем ускорение еще на 2 такта. мелочь, а приятно)) получаем:

  do
  {
    drawPixel(x, y0, color);
   }
  while(++y0 <= y1);

вот так это работает... почти всегда есть где ускорить)

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

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

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

Подскажите, пожалуйста, на какой странице последняя версия схемы и программы, 88 страниц тяжко смотреть(

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

uldin@mail.ru пишет:

Подскажите, пожалуйста, на какой странице последняя версия схемы и программы, 88 страниц тяжко смотреть(

Смотря под какой дисплей..А так все у Автора на страничке ,остальное уже кто как хотел,тот под себя и перекраивал..

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

progrik, спасибо за ведро  ̶п̶о̶м̶о̶е̶в̶  критики, вот из-за этого начинающие ардуинщики и бросают начатое. Я не брошу, в моём возрасте в макдональдс поздно )
По поводу LCD1202 я код недавно полностью перелопатил (другое применение, там скорость нужна была), в скетче  почти годовой давности этого нет. Да в пультоскопе и не особо важна скорость вывода на экран. Сейчас мне интересен другой проект, ваш опыт сильно бы помог, возможно, стОит в личке пообщаться.

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

seri0shka пишет:

progrik, спасибо за ведро  ̶п̶о̶м̶о̶е̶в̶  критики, вот из-за этого начинающие ардуинщики и бросают начатое. Я не брошу, в моём возрасте в макдональдс поздно )
По поводу LCD1202 я код недавно полностью перелопатил (другое применение, там скорость нужна была), в скетче  почти годовой давности этого нет. Да в пультоскопе и не особо важна скорость вывода на экран. Сейчас мне интересен другой проект, ваш опыт сильно бы помог, возможно, стОит в личке пообщаться.

Не ошибается тот,кто ничего не делает.Сделать выводы и добивать дальше..Не отступать и не сдаваться..

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

seri0shka пишет:
progrik, спасибо за ведро  ̶п̶о̶м̶о̶е̶в̶  критики, вот из-за этого начинающие ардуинщики и бросают начатое. Я не брошу, в моём возрасте в макдональдс поздно )
ну вообще-то по ходу наполнения ведра я, в отличии от многих, разжевывал каждый свой шаг, чужие ошибки, и пытался максимально донести, что как, зачем и почему, и приводил код... так что, в одной руке ведро из под помоев (помои уже известно где), а в другой намыленная мочалка... а если кто не хочет отмыться, то в макдональдс никогда не поздно))

скорость важна и нужна. не неси ересь. с таким кодом даже кнопки хер во время нажимаются, ведь так? а быстрые процессы/всплески, да мало ли чего посмотреть? слайд-шоу в 3 к/с - это херня даже для часов. неужели ты не видел Okmor по моему сделал на внешнем ацп - он тоже разогнал код вывода, так приятно смотреть, хоть и унылое ч/б...

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

короче мне все ясно. одному говорю: -а чего у тебя часы моргают полтора раза в секунду, как попало, вообще не во время и с разными интервалами, в лупе идет постоянный опрос RTC, ведь на модуле есть нога, которая изменяет состояние два раза в секунду(меандр 1 Гц), просто подключи ее на прерывание и рисуй точки ровно прировно раз в пол сек - вкл/выкл и там уже раз в минуту/час/сутки дергай модуль RTC. его ответ был ох*енен: -я это сделал для примера, каждый сам сможет подправить и сделать что захочет.

ВЫ СЕРЬЕЗНО? одному скорость отрисовки в осциллографе не нужна, другой кидает на стол провода, ардуино нано, дисплей, модули, смешал в кучу и говорит - вот что я сделал для вас. это просто демка. вы теперь сможете сделать все сами...

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

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

Да я не обижаюсь, всё-таки когда говорят "ты дурак тут, тут и вот тут" и объясняют причину, то это всё же лучше, чем просто "ты дурак". За подсказки спасибо, приму к сведению, хотя к проекту пультоскопа пока не вернусь, сейчас другие приоритеты. А чтобы показать мой суперскоростной вывод,  его нужно привести в соответствие с этим проектом, а на это руки не доходят, потому и не показываю, уж извините. Да и забросают помидорами за "трындец полный".

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

 

вот вот вот! Поэтому мечта - через wifi 8266 вывести на Андроид (универсальный экран!) и было бы полегче))

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

smokok пишет:
...на паузе шустрее стала прокрутка и вообще.
я заморочился, глянул время исполнения - не так как хотелось. поэтому еще допилил вывод, теперь работает еще приблизительно раз в 5 быстрее. сто кадров выводятся за 2+- сек (было около 9-10сек), зависит от заполнения. 100 кадров с ровным лучем за 1,3 сек. то есть, +-50 FPS получается. теперь общая скорость больше будет зависеть от остального кода.

я думал раз в 10 скорость поднимется. но благодаря оптимизатору компилятора этот код хоть как-то работал. браво оптимизаторы компилятора!!)) я убрал ну просто нереальное кол-во вызовов функций - но ускорилось не в 10, а только в 5 раз. ну 5 так 5)) я бы и на аппаратный SPI всю эту хрень повесил, но не имеет смысла, пока есть остальной код...))

чтобы поднять fps "живого" отображения на низких скоростях adc, синхронизацию нужно ловить прямо в функции чтения ацп, и читать буфер до размера дисплея по Х, или чуть больше. а на всю длину буфера читать только по нажатии на паузу. не нужно постоянно считывать килобайт, если ты видишь только 90 байт... разве что немного упадет точность определения частоты...

UPD: вот код. seri0shka вместо простыни все изменения я пометил в комментариях как "progrik". вывод теперь должен херачить как паровоз! если что не понятно - спрашивай

#define BUFSIZE 800
#define history 50

volatile int cur_index = 0; //переменная для отсчета замеров
volatile int StartIndex = 0; // номер замера с которого нужно выводить результат (считается так: (замер сработки триггера-предистория))
volatile byte prev_data; // предыдущее состояние шины
volatile byte pattern_mask; // маска для события PATTERN
volatile byte pattern_value; // ЗНАЧЕНИЕ состояния для события PATTERN
volatile boolean trigger_event = false; //признак сработки триггера
volatile int time_disc = 0; // время выборки 1-го значения
unsigned long prevMillis, curMillis;
enum triggerEvents {BURST, MASTER_RISE, MASTER_FALL, PATTERN}; //именнованный список триггеров-событий
bool flag = 0;
// Пины экранчика
#define RES 3
#define CS 4
#define Data 6
#define Clock 7
#define swap(a, b) { byte t = a; a = b; b = t; } //progrik: было int t... работа идет только с byte. так быстрее.

//progrik: порт(ы) для clock, data, reset, chipSelect (CS) согласно распиновке Arduino Nano пины 3,4,6,7 все на порту D
#define CLOCK_PORT PORTD
#define DATA_PORT  PORTD
#define RESET_PORT PORTD
#define CS_PORT    PORTD

//progrik: маски для порта вывода. для Clock указан пин 7
//progrik: в распиновке Arduino NANO это D7 - порт D пин 7, маска - B10000000. для D0 была бы B00000001 (счет с ноля справа налево)
#define CLOCK_MASK B10000000
#define DATA_MASK  B01000000
#define RESET_MASK B00001000
#define CS_MASK    B00010000
//progrik: определяем макросы для действий с управляющими пинами
#define CLOCK_ACTIVE  CLOCK_PORT &= ~CLOCK_MASK
#define CLOCK_IDLE    CLOCK_PORT |=  CLOCK_MASK
#define DATA_ACTIVE   DATA_PORT  &= ~DATA_MASK
#define DATA_IDLE     DATA_PORT  |=  DATA_MASK
#define RESET_ACTIVE  RESET_PORT &= ~RESET_MASK
#define RESET_IDLE    RESET_PORT |=  RESET_MASK
#define CS_ACTIVE     CS_PORT    &= ~CS_MASK
#define CS_IDLE       CS_PORT    |=  CS_MASK

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

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

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

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

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

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

void setup()  {
  pinMode(KEY_OK, INPUT); pinMode(KEY_L, INPUT); pinMode(KEY_R, INPUT); // настраиваем кнопки на вход
  digitalWrite(KEY_OK, HIGH); digitalWrite(KEY_L, HIGH); digitalWrite(KEY_R, HIGH); // подтяжка к плюсу питания
  VCC = ReadVcc();
  Inicialize();  //Инициализация дисплея
  Clear_LCD();
  for (byte i = 0; i < 3; i++)
  {
    strcpy_P(buffer, (char*)pgm_read_word(&(string_table[i]))); // необходимые функции и расшифровки, просто скопируйте
    print(10, i * 10, 1, (buffer));
    //delay( 500 );
  }
  Update();
//  delay(KEY_DELAY);
  while (digitalRead(KEY_OK)) { // цикл, пока не нажали кнопку ОК
  Update();
  // Если не нажата 10 сек кнопка ок вход в режим осцилоографа автоматом
  osc--;
    if (osc==0) break;
  //////////  Выводим показание /////////
    float_print(60, 20, 1, analogRead(VBAT)*VCC/1024,1);  //Вольтаж батареи
    float_print(55, 40, 1, analogRead(TempC), 1);  // выводим на экран показание Температуры
  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[3]))); // пишем слово "Температура"
  print(10, 40, 1, (buffer));
  delay( 300 );
} // цикл нажатия ОК
  // нажали кнопку ОК из меню, инициализируем и выходим из меню
  count = 0; //countX = 0; // восстанавливаем на всякий случай переменные
  if (mode == 0) FreqCount.begin(1000);
  if (mode == 1) {
    InitTimersSafe();
  }
  if (mode == 2) {
    InitTimersSafe();
  }
  if (mode == 4 || mode == 5) DDRC = 0x00; PORTC = 0x00;// Serial.begin(115200); // razv = 0;// весь порт С (это A0...A5) на вход и без подтяжки
  for (char a=37; a>0; a-=2);
}
// беcконечный цикл - по сути прыгаем в подпрограммы
void loop() {
  switch (mode) {
    // Прыгаем в выбранный режим из главного меню
    case 0 : Oscil();        break; // "выпадаем" в осцилл
  }
}
//  === Читаем с АЦП данные и помещаем их в буфер === //
void ReadAdc() {
  if (razv % 10) { // (razv>0)        // если развертка без задержек всяких (с 1 по 7)
    ADCSRA = 0b11100000 | (8 - (razv % 10)); // установили делитель (/2 - не работает, так что начинаем с /4)
    for (int i = 0; i < BUFSIZE; i++) { // цикл для чтения
      while (!(ADCSRA & 0x10));     // ждем готовность АЦП
      ADCSRA |= 0x10;               // запускаем следующее преобразование
      adcBuf[i] = ADCH;             // записываем данные в массив
    }
    delay(0.3 * BUFSIZE);           // компенсация задержки по сравнению с разверткой 0...
  } else {                          // развертка с задержками (delay)
    ADCSRA = 0b11100111;            // делитель на /128
    for (int i = 0; i < BUFSIZE; i++) { // цикл для чтения
      while (!(ADCSRA & 0x10));     // ждем готовность АЦП
      ADCSRA |= 0x10;               // запускаем следующее преобразование
      delayMicroseconds(500);       // делаем задержку
      adcBuf[i] = ADCH;             // записываем данные в массив
    }
  }
}

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

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



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

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

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

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

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

/*
//=======================================================Управление пинами
//progrik: добалено определение inline. функция встраивается в код и не расходуется воемя на ее вызов.
//progrik: UPD: функция не испольуется, удалить.
inline __attribute__((always_inline)) void dWrite(byte pin, byte val) {
  byte bit = digitalPinToBitMask(pin);
  volatile byte *out;
  out = portOutputRegister(digitalPinToPort(pin));
  (val) ? *out |= bit : *out &= ~bit;
}
*/

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

//======================================================Очистка дисплея
void Clear_LCD() {
  memset(LCD_RAM, 0, 864); //progrik: велосипед не нужен. memset даже чутка быстрее. это из Си.
/*
  for (int index = 0; index < 864 ; index++) {
    LCD_RAM[index] = (0x00);
  }*/
}

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

//===================================================Нарисовать пиксель
//progrik: добалено определение inline. функция встраивается в код и не расходуется время на ее вызов.
//убрана ненужная проверка byte на "меньше ноля" и на вылет за пределы дисплея. не нужно этого допускать в коде.
inline __attribute__((always_inline)) void drawPixel(byte x, byte y, bool color) {
  if(color) LCD_RAM[x + (y / 8) * 96] |= _BV(y % 8);
  else      LCD_RAM[x + (y / 8) * 96] &= ~_BV(y % 8);
}

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

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

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

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

//progrik: добавил функцию для быстрой отрисовки вертикальных линий луча
inline __attribute__((always_inline)) void drawVLineUltraFast(byte x, byte y0, byte y1, boolean color) {
  if(y0 > y1)
    swap(y0, y1);
  do
  {
    drawPixel(x, y0, color);
  }
  while(++y0 <= y1);
}
//========================================Рисование вертикальной линии
void drawFastVLine(byte x, byte y, byte h, boolean color) {
  byte y1 = y + h - 1;
  do
  {
    drawPixel(x, y, color);
  }
  while(++y <= y1);
}

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

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

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

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

// == Отрисовка графика == //
  //for(int y = 0; y < 92; y++)  drawLine(y + 4, 67 - adcBuf[y + grOffset]/kdel / 0.9, y + 4, 67 - adcBuf[y + grOffset + 1]/kdel / 0.9, 1);
  //progrik: заменил вызов функции рисования _наклонных_линий_ на рисующую простые вертикальные прямые..
  for (uint8_t x = 0; x < 92; x++)  drawVLineUltraFast(x + 4, 67 - adcBuf[x + grOffset]/kdel / 0.9, 67 - adcBuf[x + grOffset + 1]/kdel / 0.9, 1);
  Update();
}

smokok залей плиз проверить. вот теперь вывод ускорен) могу еще, но это уже уровень "ниндзя", новичкам нельзя) глянь теперь на скорость (на паузе особенно). и если вдохновишься результатом - сделай плз видос.)) так же потом можешь в первой строке указать #define BUFSIZE 200 (изменить 800 на 200-300, живьем смотреть намного быстрее станет) а если залагает или не заработает - скажи что и как, гляну. все таки без дисплея легко не заметить баг какой...

newwoodoo
Offline
Зарегистрирован: 02.05.2020

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

 

 

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

progrik пишет:
smokok залей плиз проверить. вот теперь вывод ускорен) могу еще, но это уже уровень "ниндзя", новичкам нельзя) глянь теперь на скорость (на паузе особенно). и если вдохновишься результатом - сделай плз видос.)) так же потом можешь в первой строке указать #define BUFSIZE 200 (изменить 800 на 200-300, живьем смотреть намного быстрее станет) а если залагает или не заработает - скажи что и как, гляну. все таки без дисплея легко не заметить баг какой...
Привет! Извини за задержку с ответом. Попробовал, но не запускается. Просто моргает экранчик и нет никакой индикации. Что там подправить я хз.

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

smokok пишет:
Попробовал, но не запускается. Просто моргает экранчик и нет никакой индикации. Что там подправить я хз.
привет! а хоть что как моргает? признаки жизни значит есть... сейчас появилось время, погляжу код, что может быть. как чего соображу - сразу дам знать. нужно уже добить раз и навсегда) хоть бери и себе покупай такой дисплей))

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

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

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

progrik пишет:
хоть бери и себе покупай такой дисплей))
progrik, ты в России?

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

smokok пишет:
..подсветка моргает и тускло светит, а на дисплее ничего нет. Как будто пины не правильно обрабатываются.
о, ты тут. давай через мыло допилим, чтоб форум освободить от ненужных постов) пиши мне на мыло moricz (сбк) ukr после точки net, я буду тебе накидывать варианты кода, пока не найдем затык. а потом уже сюда код шпульнем, чтоб все могли порезвиться)) там по идее не долго искать...

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

#define BUFSIZE 800
#define history 50

volatile int cur_index = 0; //переменная для отсчета замеров
volatile int StartIndex = 0; // номер замера с которого нужно выводить результат (считается так: (замер сработки триггера-предистория))
volatile byte prev_data; // предыдущее состояние шины
volatile byte pattern_mask; // маска для события PATTERN
volatile byte pattern_value; // ЗНАЧЕНИЕ состояния для события PATTERN
volatile boolean trigger_event = false; //признак сработки триггера
volatile int time_disc = 0; // время выборки 1-го значения
unsigned long prevMillis, curMillis;
enum triggerEvents {BURST, MASTER_RISE, MASTER_FALL, PATTERN}; //именнованный список триггеров-событий
bool flag = 0;
// Пины экранчика
#define RES 3
#define CS 4
#define Data 6
#define Clock 7
#define swap(a, b) { byte t = a; a = b; b = t; } //progrik: было int t... работа идет только с byte. так быстрее.

//progrik: порт(ы) для clock, data, reset, chipSelect (CS) согласно распиновке Arduino Nano пины 3,4,6,7 все на порту D
#define CLOCK_PORT PORTD
#define DATA_PORT  PORTD
#define RESET_PORT PORTD
#define CS_PORT    PORTD

//progrik: маски для порта вывода. для Clock указан пин 7
//progrik: в распиновке Arduino NANO это D7 - порт D пин 7, маска - B10000000. для D0 была бы B00000001 (счет с ноля справа налево)
#define CLOCK_MASK B10000000
#define DATA_MASK  B01000000
#define RESET_MASK B00001000
#define CS_MASK    B00010000
//progrik: определяем макросы для действий с управляющими пинами
#define CLOCK_IDLE   CLOCK_PORT &= ~CLOCK_MASK
#define CLOCK_ACTIVE CLOCK_PORT |=  CLOCK_MASK
#define DATA_IDLE    DATA_PORT  &= ~DATA_MASK
#define DATA_ACTIVE  DATA_PORT  |=  DATA_MASK
#define RESET_IDLE   RESET_PORT &= ~RESET_MASK
#define RESET_ACTIVE RESET_PORT |=  RESET_MASK
#define CS_IDLE      CS_PORT    &= ~CS_MASK
#define CS_ACTIVE    CS_PORT    |=  CS_MASK

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

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

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

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

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

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

void setup()  {
  pinMode(KEY_OK, INPUT); pinMode(KEY_L, INPUT); pinMode(KEY_R, INPUT); // настраиваем кнопки на вход
  digitalWrite(KEY_OK, HIGH); digitalWrite(KEY_L, HIGH); digitalWrite(KEY_R, HIGH); // подтяжка к плюсу питания
  VCC = ReadVcc();
  Inicialize();  //Инициализация дисплея
  Clear_LCD();
  for (byte i = 0; i < 3; i++)
  {
    strcpy_P(buffer, (char*)pgm_read_word(&(string_table[i]))); // необходимые функции и расшифровки, просто скопируйте
    print(10, i * 10, 1, (buffer));
    //delay( 500 );
  }
  Update();
//  delay(KEY_DELAY);
  while (digitalRead(KEY_OK)) { // цикл, пока не нажали кнопку ОК
  Update();
  // Если не нажата 10 сек кнопка ок вход в режим осцилоографа автоматом
  osc--;
    if (osc==0) break;
  //////////  Выводим показание /////////
    float_print(60, 20, 1, analogRead(VBAT)*VCC/1024,1);  //Вольтаж батареи
    float_print(55, 40, 1, analogRead(TempC), 1);  // выводим на экран показание Температуры
  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[3]))); // пишем слово "Температура"
  print(10, 40, 1, (buffer));
  delay( 300 );
} // цикл нажатия ОК
  // нажали кнопку ОК из меню, инициализируем и выходим из меню
  count = 0; //countX = 0; // восстанавливаем на всякий случай переменные
  if (mode == 0) FreqCount.begin(1000);
  if (mode == 1) {
    InitTimersSafe();
  }
  if (mode == 2) {
    InitTimersSafe();
  }
  if (mode == 4 || mode == 5) DDRC = 0x00; PORTC = 0x00;// Serial.begin(115200); // razv = 0;// весь порт С (это A0...A5) на вход и без подтяжки
  for (char a=37; a>0; a-=2);
}
// беcконечный цикл - по сути прыгаем в подпрограммы
void loop() {
  switch (mode) {
    // Прыгаем в выбранный режим из главного меню
    case 0 : Oscil();        break; // "выпадаем" в осцилл
  }
}
//  === Читаем с АЦП данные и помещаем их в буфер === //
void ReadAdc() {
  if (razv % 10) { // (razv>0)        // если развертка без задержек всяких (с 1 по 7)
    ADCSRA = 0b11100000 | (8 - (razv % 10)); // установили делитель (/2 - не работает, так что начинаем с /4)
    for (int i = 0; i < BUFSIZE; i++) { // цикл для чтения
      while (!(ADCSRA & 0x10));     // ждем готовность АЦП
      ADCSRA |= 0x10;               // запускаем следующее преобразование
      adcBuf[i] = ADCH;             // записываем данные в массив
    }
    delay(0.3 * BUFSIZE);           // компенсация задержки по сравнению с разверткой 0...
  } else {                          // развертка с задержками (delay)
    ADCSRA = 0b11100111;            // делитель на /128
    for (int i = 0; i < BUFSIZE; i++) { // цикл для чтения
      while (!(ADCSRA & 0x10));     // ждем готовность АЦП
      ADCSRA |= 0x10;               // запускаем следующее преобразование
      delayMicroseconds(500);       // делаем задержку
      adcBuf[i] = ADCH;             // записываем данные в массив
    }
  }
}

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

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



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

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

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

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

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

/*
//=======================================================Управление пинами
//progrik: добалено определение inline. функция встраивается в код и не расходуется воемя на ее вызов.
//progrik: UPD: функция не испольуется, удалить.
inline __attribute__((always_inline)) void dWrite(byte pin, byte val) {
  byte bit = digitalPinToBitMask(pin);
  volatile byte *out;
  out = portOutputRegister(digitalPinToPort(pin));
  (val) ? *out |= bit : *out &= ~bit;
}
*/

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

//======================================================Очистка дисплея
void Clear_LCD() {
  memset(LCD_RAM, 0, 864); //progrik: велосипед не нужен. memset даже чутка быстрее. это из Си.
/*
  for (int index = 0; index < 864 ; index++) {
    LCD_RAM[index] = (0x00);
  }*/
}

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

//===================================================Нарисовать пиксель
//progrik: добалено определение inline. функция встраивается в код и не расходуется время на ее вызов.
//убрана ненужная проверка byte на "меньше ноля" и на вылет за пределы дисплея. не нужно этого допускать в коде.
inline __attribute__((always_inline)) void drawPixel(byte x, byte y, bool color) {
  if(color) LCD_RAM[x + (y / 8) * 96] |= _BV(y % 8);
  else      LCD_RAM[x + (y / 8) * 96] &= ~_BV(y % 8);
}

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

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

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

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

//progrik: добавил функцию для быстрой отрисовки вертикальных линий луча
inline __attribute__((always_inline)) void drawVLineUltraFast(byte x, byte y0, byte y1, boolean color) {
  if(y0 > y1)
    swap(y0, y1);
  do
  {
    drawPixel(x, y0, color);
  }
  while(++y0 <= y1);
}
//========================================Рисование вертикальной линии
void drawFastVLine(byte x, byte y, byte h, boolean color) {
  byte y1 = y + h - 1;
  do
  {
    drawPixel(x, y, color);
  }
  while(++y <= y1);
}

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

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

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

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

// == Отрисовка графика == //
  //for(int y = 0; y < 92; y++)  drawLine(y + 4, 67 - adcBuf[y + grOffset]/kdel / 0.9, y + 4, 67 - adcBuf[y + grOffset + 1]/kdel / 0.9, 1);
  //progrik: заменил вызов функции рисования _наклонных_линий_ на рисующую простые вертикальные прямые..

  for (uint8_t x = 0; x < 92; x++)
    drawVLineUltraFast(x + 4, 67 - adcBuf[x + grOffset]/kdel / 0.9, 67 - adcBuf[x + grOffset + 1]/kdel / 0.9, 1);
  Update();
}

seri0shka нет, Украина) хотел дисплей предложить?)) у нас таких есть вроде бакса по полтора, надо глянуть. просто я не уверен, что мне вообще он нужен, а спортивный интерес могу удовлетворить и удаленно без дисплея)) что скоро и сделаю)) и восцарит всеобщая радость)) а позже я покажу демку своего цветного спектроскопа-осциллографа "Spectator" или просто "spectAscope" уровня "ниндзя")) лень допиливать до уровня "БОГ"))) и тогда... потом объясню тебе еще одну мораль..)) кстати, ты говорил о каком-то проекте... если хочешь - тоже маякни на мыло, может чего подскажу, если знаю...

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

Вот эту всю простыню

//progrik: порт(ы) для clock, data, reset, chipSelect (CS) согласно распиновке Arduino Nano пины 3,4,6,7 все на порту D
#define CLOCK_PORT PORTD
#define DATA_PORT  PORTD
#define RESET_PORT PORTD
#define CS_PORT    PORTD
//progrik: маски для порта вывода. для Clock указан пин 7
//progrik: в распиновке Arduino NANO это D7 - порт D пин 7, маска - B10000000. для D0 была бы B00000001 (счет с ноля справа налево)
#define CLOCK_MASK B10000000
#define DATA_MASK  B01000000
#define RESET_MASK B00001000
#define CS_MASK    B00010000
//progrik: определяем макросы для действий с управляющими пинами
#define CLOCK_ACTIVE  CLOCK_PORT &= ~CLOCK_MASK
#define CLOCK_IDLE    CLOCK_PORT |=  CLOCK_MASK
#define DATA_ACTIVE   DATA_PORT  &= ~DATA_MASK
#define DATA_IDLE     DATA_PORT  |=  DATA_MASK
#define RESET_ACTIVE  RESET_PORT &= ~RESET_MASK
#define RESET_IDLE    RESET_PORT |=  RESET_MASK
#define CS_ACTIVE     CS_PORT    &= ~CS_MASK
#define CS_IDLE       CS_PORT    |=  CS_MASK

В ArduinoIDE можно заменить на

#define CLOCK_ACTIVE  bitWrite(PORTD,7,1) // pin7=PORTD,7   pin8=PORTB,0   pin9=PORTB,1  и так далее
#define CLOCK_IDLE    bitWrite(PORTD,7,0)
#define DATA_ACTIVE   bitWrite(PORTD,6,1) // pin6=PORTD,6
#define DATA_IDLE     bitWrite(PORTD,6,0)
#define RESET_ACTIVE  bitWrite(PORTD,3,1) // pin3
#define RESET_IDLE    bitWrite(PORTD,3,0)
#define CS_ACTIVE     bitWrite(PORTD,4,1) // pin4
#define CS_IDLE       bitWrite(PORTD,4,0)

Это не уменьшит ни на байт прошивку, но в коде смотрится гораздо читабельней, особенно для кодеров не уровня "нинзя". Сам нашёл этот секрет совсем недавно, он почему-то практически нигде не упоминается, и очень облегчает жизнь.

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

seri0shka пишет:
В ArduinoIDE можно заменить на ....... Это не уменьшит ни на байт прошивку, но в коде смотрится гораздо читабельней, особенно для кодеров не уровня "нинзя". Сам нашёл этот секрет совсем недавно, он почему-то практически нигде не упоминается, и очень облегчает жизнь.
ниможна)) если нидзя пишет, то он знает, что пишет) ты заглянул в Arduino.h? этот код будет(должен) работать в туеву тучу раз медленнее. я же тут за скорость, а не за простоту написания. я понимаю, в глубокой молодости тоже пытался весь код в одну строку написать. это не правильно) и вот почему (ардуино.х)

#define bitWrite(value, bit, bitvalue) ((bitvalue) ? bitSet(value, bit) : bitClear(value, bit))
#define bitSet(value, bit) ((value) |= (1UL << (bit)))
#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
 
сам мне скажи, что тут происходит? в моем случае к порту применяется маска. все. скажу больше. например, при CLOCK_PORT &= ~CLOCK_MASK порт не "читается->применяется маска->пишется в порт", а благодаря тому, что маски для компилятора как константа и оптимизатор понимает, что изменяется только один бит, то он применяет к порту ассемблерную инструкцию sbi/cbi которая выполняется за ДВА такта.
в твоем случае маска может быть неожиданной, и компилятор _возможно_ не сможет это оптимизировать. НО если оптимизатор совсем умный, просто до безобразия, поймет, что в коде больше нет вызова макроса, то может быть и такой код оптимизирует... надо проверить. просто в коде ниндзя я точно знаю, что происходит) а тут (bitvalue) ? ... уже какая-то проверка. зачем? что проверять, если я знаю, что писать... короче, надо проверить скорость... я проверю) если скорость окажется одинаковой - ниндзя пожмет тебе руку и выпишет звездочку))) но продолжу пользоваться портами с масками. меня удручает вызывать что-то, когда ты не знаешь, как это будет работать...
у тебя такой-же дисплей, ты код случайно не затестил? интересно, заработало ли, и что со скоростью, не гнется ли пространство после разгона)))
smokok
smokok аватар
Offline
Зарегистрирован: 08.06.2018

progrik теперь просто белый пустой экран. Я на связи!

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

seri0shka пишет:
В ArduinoIDE можно заменить на ...
проверил. таки да, можно. компилятор не запутать такими макросами)) жму руку, вручаю звезду)) ведь можешь, когда захочешь))

проверили с smokok основные функции вывода работают. все полетело. рисование луча и вывод на дисплей 100 кадров - 1,3 сек. надо еще глянуть, как там цифры выводятся и расчеты ведутся. они тоже способны угнетать скорость... но наверное это уже особо и не нужно... найду причину, почему не заводилось, и можно считать этот дисплейчик пройденным этапом в блокноте не держа его в руках)) на удалёнке, на удалёночке))

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

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

Внук помог - написал расчет в массив и загрузка на комп. Осталось быстро-быстро заполнить массив из АЦП в 8266, передать по WiFi на Андроид и получить пультоскоп))Внук помог - написал расчет в массив и загрузка на комп. Осталось быстро-быстро заполнить массив из АЦП в 8266, передать по WiFi на Андроид и получить пультоскоп))

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

uldin@mail.ru пишет:

Внук помог - написал расчет в массив и загрузка на комп. Осталось быстро-быстро заполнить массив из АЦП в 8266, передать по WiFi на Андроид и получить пультоскоп))Внук помог - написал расчет в массив и загрузка на комп. Осталось быстро-быстро заполнить массив из АЦП в 8266, передать по WiFi на Андроид и получить пультоскоп))

Достойно новой темы-с удовольствием бы потестил.

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

uldin@mail.ru пишет:
Внук помог - написал расчет в массив и загрузка на комп. Осталось быстро-быстро заполнить массив из АЦП в 8266, передать по WiFi на Андроид и получить пультоскоп))
в функцию передается тип byte, где он присваивается элементу массива float и выводится как int. это за гранью добра и зла))

проблема не в том, чтобы по вайфай на ведро что-то передать, а в том, _кто_ напишет софт под андроид. а этот код... он ни к чему. он не нужен. это два простейших цикла с кошмарными неправильными и ненужными типами данных, отжирающих память и быстродействие... 

у меня есть поломанный черенок от лопаты, осталось накопать золота и получить бабло)))

ставьте перед собой реальные цели))

weisswp
Offline
Зарегистрирован: 26.05.2018

Доброго времени суток.
Я дико извиняюсь, но можно ли как то это вот все адаптировать под ssd1306?)

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

weisswp, на предыдущей странице, Enjoy даже фото готового прибора выложил с экраном 1306. Ищите скетчи от Electronik83, все адаптировано уже.
 

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

weisswp пишет:
но можно ли как то это вот все адаптировать под ssd1306?)
 SSD1306 от ЭЛЕКТРОНИКА83 + автоматика

weisswp
Offline
Зарегистрирован: 26.05.2018

smokok пишет:

weisswp пишет:
но можно ли как то это вот все адаптировать под ssd1306?)
 SSD1306 от ЭЛЕКТРОНИКА83 + автоматика

Alex-Bee пишет:

weisswp, на предыдущей странице, Enjoy даже фото готового прибора выложил с экраном 1306. Ищите скетчи от Electronik83, все адаптировано уже.
 

Спасибо :)

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

nervol пишет:

Тогда сменил 2.7 на 0.1 в этой строке

    if (vRef && vMax*VCC/255 > 2.7) { // если можем замерить аппаратно - меряем 
сменил на
    if (vRef && vMax*VCC/255 > 0.1) { // если можем замерить аппаратно - меряем  

В итоге, 5v 1000Гц через делитель 1/10 при опорном 5, частоту не меряет.

2.7 это напряжение при котором переходит измерение частоты на аппаратный режим, ниже 2.7 // === Меряем частоту сигнала программно === //. 2.7 и 2.5 это (возможно) нижний придел, по этому 0.1 вольта при опорном 5, частоту не меряет. 

16МГц=3,25микросекунды один замер в частоте периода

27МГц=1,68микросекунды один замер в частоте периода

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

Сделаль чудо девайс!!! У меня вопрось: Какие значение измеряеть на вольтах,т,е Vrms или Vpp или Vp?  

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

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

V=Vrms,Vpp,Vp ???

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

AS31979 пишет:
...В отличии от програмной синхронизации картинка ОЧЕНЬ стабильна ... Если есть вопросы задавайте пока сам все не позабывал.
такая синхронизация всего лишь начинает сбор данных после синхроимпульса, а не запускает АЦП по синхре, как было бы лучше...

она поймает даже самый медленный сигнал на любой, даже самой быстрой развертке. программная же поймает только если синхро уровень попал в (длина_буфера минус ширина экрана)... но "дрожание" сигнала на уже вменяемых частотах, когда парочка периодов уже влазят в буфер - останется. (может не на этих дисплейчиках). сдается мне, нужно именно запускать АЦП в момент синхры... тогда при хорошем генераторе будет ровно как bmp картинка... а вообще, такая синхронизация абсолютно не имеет смысла, ибо 1) геморрой+микруха+детальки 2) прямо в цикле чтения можно проверять значение ацп на переход через синхру на любых доступных напряжениях (пофиг на логические уровни) без потери скорости (ниже напишу, почему и как)... как-то так...

вот это ADCSRA|=0x10; - это не запуск нового измерения, как ты пишешь, это снятие флага ADIF, о чем нужно помнить, а чтобы помнить так нужно и писать: ADCSRA |= (1<<ADIF); -тут понятно, что снимаем флаг. а начать новое преобразование это ADSC - Start Conversion - запуск преобразования - ADCSRA |= (1<<ADSC); нужно привыкать пользоваться предопределенными константами, это понятнее и удобнее и читабельнее и запоминабельнее))

очень много людей боятся что-то делать пока буфер набирают.. ну блин, даже при минимально возможном делителе 2 у тебя есть 26 тактов (13 на оцифровку и * 2(делитель для ацп)). я успею на дисплее несколько точек вывести за это время, или собрать max и min значения, чтоб потом в отдельном цикле не тратить время, или... просто нужно понимать, что туда можно впихать, а что нет... например, ожидание флага + очистка флага - тактов 5-6, сохранение значения - 3-4, if(max_v<current_val) max_v=current_val; else if(min_v>current_val) min_v=current_val; - около 10+-... ну короче, около 20 тактов на все это... остальные 6 тактов будешь висеть в цикле ожидания флага... если все правильно сделать и не превысить лимит, на скорость набивания буфера все это не повлияет никак.

но лучше ловить синхру прямо при чтении ацп. сначала дождаться, когда сигнал уйдет ниже уровня синхры, потом в цикле if(current_val > sync_level) { поймали синхру за хвост, читаем до конца } ... и тогда синхра будет работать на любых рабочих напряжениях с любой амплитудой и без операционника и лишней ноги...

соответственно, в комментариях к действиям над ацп есть заблуждения и фантазии)).. "в принципе можно переписать код так что выполнять операциию & не потребуется" - вот это не имеет под собой никакого смысла вообще. патамушта есть на это время... "Быстродействие и так не очень, а читать вместо одного регистра два - потеря времени" - тоже самое. есть навалом времени, чтобы считать оба байта, и т.д...
          // Место где возможно удастся ускорить программу - запись идет не байта, а с операцией
          // наложения на регистр - можно просто писать байт в регистр
          ADCSRA|=0x10;

здесь тоже особо не выиграешь. оптимизатор компилятора заменит ADCSRA|=0x10; на sbi, который выполняется за 2 такта. про время уже писал...

while(digitalRead(13)==HIGH) {if (digitalRead(8)==LOW){goto waiting_zero;} } 
waiting_zero:

сколько скобок и букоффффф))) касается всех: перестаньте писать код в одну строку!! это неправильно, неудобно, нечитабельно и незапоминабельно!!))) не пиши в одну строку то, что можно написать в несколько. вот правильное исключение, тот-же код:

while(digitalRead(13)==HIGH && digitalRead(8)==HIGH);

ну или хотя бы просто break; вместо goto и метки... и вот это вот digitalRead(13) и digitalRead(8) - что это - не поймешь.. лучше сделать дефайны #define SYNC_SIG 8  #define SYNC_MODE 13 .. и в коде уже юзать digitalRead(SYNC_SIG) и digitalRead(SYNC_MODE) ... хоть будет понятно, что за пины и зачем смотришь... но это тоже лирика)

все, у меня лимит на критику)) уже накопался в чужом коде))
 
Спасибо за схемы и всякие полезные картинки!) как раз собирался впихнуть в свою погремушку lm324) других нет и не надо)
удачи!
smokok
smokok аватар
Offline
Зарегистрирован: 08.06.2018

Прошу извинить с задержкой скетча для НХ1230. Вот ССЫЛКА скачать.

Большое спасибо progrik за проделанную работу и разгон. В скетче только режим осциллографа. Все функции автоматические кроме паузы))). С паузой сильно не заморачивались, но слайдшоу там уже нет. В активном режиме отрисовка и отклик осциллограммы почти мгновенный. С таким же успехом разогнал 5110 от [Electronik83]. Пример можно посмотреть ТУТ.

И так же можно посмотреть как было НХ1230 КЛАЦ
И как стало НХ1230 КЛАЦ

Скетч использует 9486 байт (29%) памяти устройства. Всего доступно 32256 байт.

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

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

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

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

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

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

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

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

static const char font[] PROGMEM = {
                                  //HEX B DEC переводим
  0x00, 0x00, 0x00, 0x00, 0x00 ,  // 0x20   space
  0x00, 0x00, 0x5f, 0x00, 0x00 ,  // 0x21   !
  0x00, 0x07, 0x00, 0x07, 0x00 ,  // 0x22   "
  0x14, 0x7f, 0x14, 0x7f, 0x14 ,  // 0x23   #
  0x24, 0x2a, 0x7f, 0x2a, 0x12 ,  // 0x24   $
  0x23, 0x13, 0x08, 0x64, 0x62 ,  // 0x25   %
  0x36, 0x49, 0x55, 0x22, 0x50 ,  // 0x26   &
  0x00, 0x05, 0x03, 0x00, 0x00 ,  // 0x27   '
  0x00, 0x1c, 0x22, 0x41, 0x00 ,  // 0x28   (
  0x00, 0x41, 0x22, 0x1c, 0x00 ,  // 0x29   )
  0x14, 0x08, 0x3e, 0x08, 0x14 ,  // 0x2a   *
  0x08, 0x08, 0x3e, 0x08, 0x08 ,  // 0x2b   +
  0x00, 0x50, 0x30, 0x00, 0x00 ,  // 0x2c   ,
  0x08, 0x08, 0x08, 0x08, 0x08 ,  // 0x2d   -
  0x00, 0x60, 0x60, 0x00, 0x00 ,  // 0x2e   .
  0x20, 0x10, 0x08, 0x04, 0x02 ,  // 0x2f   /
  0x3e, 0x51, 0x49, 0x45, 0x3e ,  // 0x30   0
  0x00, 0x42, 0x7f, 0x40, 0x00 ,  // 0x31   1
  0x42, 0x61, 0x51, 0x49, 0x46 ,  // 0x32   2
  0x21, 0x41, 0x45, 0x4b, 0x31 ,  // 0x33   3
  0x18, 0x14, 0x12, 0x7f, 0x10 ,  // 0x34   4
  0x27, 0x45, 0x45, 0x45, 0x39 ,  // 0x35   5
  0x3c, 0x4a, 0x49, 0x49, 0x30 ,  // 0x36   6
  0x01, 0x71, 0x09, 0x05, 0x03 ,  // 0x37   7
  0x36, 0x49, 0x49, 0x49, 0x36 ,  // 0x38   8
  0x06, 0x49, 0x49, 0x29, 0x1e ,  // 0x39   9
  0x00, 0x36, 0x36, 0x00, 0x00 ,  // 0x3a   :
  0x00, 0x56, 0x36, 0x00, 0x00 ,  // 0x3b   ;
  0x08, 0x14, 0x22, 0x41, 0x00 ,  // 0x3c   <
  0x14, 0x14, 0x14, 0x14, 0x14 ,  // 0x3d   =
  0x00, 0x41, 0x22, 0x14, 0x08 ,  // 0x3e   >
  0x02, 0x01, 0x51, 0x09, 0x06 ,  // 0x3f   ?
  0x32, 0x49, 0x79, 0x41, 0x3e ,  // 0x40   @
  0x7e, 0x11, 0x11, 0x11, 0x7e ,  // 0x41   A
  0x7f, 0x49, 0x49, 0x49, 0x36 ,  // 0x42   B
  0x3e, 0x41, 0x41, 0x41, 0x22 ,  // 0x43   C
  0x7f, 0x41, 0x41, 0x22, 0x1c ,  // 0x44   D
  0x7f, 0x49, 0x49, 0x49, 0x41 ,  // 0x45   E
  0x7f, 0x09, 0x09, 0x09, 0x01 ,  // 0x46   F
  0x3e, 0x41, 0x49, 0x49, 0x7a ,  // 0x47   G
  0x7f, 0x08, 0x08, 0x08, 0x7f ,  // 0x48   H
  0x00, 0x41, 0x7f, 0x41, 0x00 ,  // 0x49   I
  0x20, 0x40, 0x41, 0x3f, 0x01 ,  // 0x4a   J
  0x7f, 0x08, 0x14, 0x22, 0x41 ,  // 0x4b   K
  0x7f, 0x40, 0x40, 0x40, 0x40 ,  // 0x4c   L
  0x7f, 0x02, 0x0c, 0x02, 0x7f ,  // 0x4d   M
  0x7f, 0x04, 0x08, 0x10, 0x7f ,  // 0x4e   N
  0x3e, 0x41, 0x41, 0x41, 0x3e ,  // 0x4f   O
  0x7f, 0x09, 0x09, 0x09, 0x06 ,  // 0x50   P
  0x3e, 0x41, 0x51, 0x21, 0x5e ,  // 0x51   Q
  0x7f, 0x09, 0x19, 0x29, 0x46 ,  // 0x52   R
  0x46, 0x49, 0x49, 0x49, 0x31 ,  // 0x53   S
  0x01, 0x01, 0x7f, 0x01, 0x01 ,  // 0x54   T
  0x3f, 0x40, 0x40, 0x40, 0x3f ,  // 0x55   U
  0x1f, 0x20, 0x40, 0x20, 0x1f ,  // 0x56   V
  0x3f, 0x40, 0x38, 0x40, 0x3f ,  // 0x57   W
  0x63, 0x14, 0x08, 0x14, 0x63 ,  // 0x58   X
  0x07, 0x08, 0x70, 0x08, 0x07 ,  // 0x59   Y
  0x61, 0x51, 0x49, 0x45, 0x43 ,  // 0x5a   Z
  0x00, 0x7f, 0x41, 0x41, 0x00 ,  // 0x5b   [
  0x02, 0x04, 0x08, 0x10, 0x20 ,  // 0x5c   backslash
  0x00, 0x41, 0x41, 0x7f, 0x00 ,  // 0x5d   ]
  0x04, 0x02, 0x01, 0x02, 0x04 ,  // 0x5e   ^
  0x40, 0x40, 0x40, 0x40, 0x40 ,  // 0x5f   _
  0x00, 0x01, 0x02, 0x04, 0x00 ,  // 0x60   `
  0x20, 0x54, 0x54, 0x54, 0x78 ,  // 0x61   a
  0x7f, 0x48, 0x44, 0x44, 0x38 ,  // 0x62   b
  0x38, 0x44, 0x44, 0x44, 0x20 ,  // 0x63   c
  0x38, 0x44, 0x44, 0x48, 0x7f ,  // 0x64   d
  0x38, 0x54, 0x54, 0x54, 0x18 ,  // 0x65   e
  0x08, 0x7e, 0x09, 0x01, 0x02 ,  // 0x66   f
  0x0c, 0x52, 0x52, 0x52, 0x3e ,  // 0x67   g
  0x7f, 0x08, 0x04, 0x04, 0x78 ,  // 0x68   h
  0x00, 0x44, 0x7d, 0x40, 0x00 ,  // 0x69   i
  0x20, 0x40, 0x44, 0x3d, 0x00 ,  // 0x6a   j
  0x7f, 0x10, 0x28, 0x44, 0x00 ,  // 0x6b   k
  0x00, 0x41, 0x7f, 0x40, 0x00 ,  // 0x6c   l
  0x7c, 0x04, 0x18, 0x04, 0x78 ,  // 0x6d   m
  0x7c, 0x08, 0x04, 0x04, 0x78 ,  // 0x6e   n
  0x38, 0x44, 0x44, 0x44, 0x38 ,  // 0x6f   o
  0x7c, 0x14, 0x14, 0x14, 0x08 ,  // 0x70   p
  0x08, 0x14, 0x14, 0x18, 0x7c ,  // 0x71   q
  0x7c, 0x08, 0x04, 0x04, 0x08 ,  // 0x72   r
  0x48, 0x54, 0x54, 0x54, 0x20 ,  // 0x73   s
  0x04, 0x3f, 0x44, 0x40, 0x20 ,  // 0x74   t
  0x3c, 0x40, 0x40, 0x20, 0x7c ,  // 0x75   u
  0x1c, 0x20, 0x40, 0x20, 0x1c ,  // 0x76   v
  0x3c, 0x40, 0x30, 0x40, 0x3c ,  // 0x77   w
  0x44, 0x28, 0x10, 0x28, 0x44 ,  // 0x78   x
  0x0c, 0x50, 0x50, 0x50, 0x3c ,  // 0x79   y
  0x44, 0x64, 0x54, 0x4c, 0x44 ,  // 0x7a   z
  0x00, 0x08, 0x36, 0x41, 0x00 ,  // 0x7b   {
  0x00, 0x00, 0x7f, 0x00, 0x00 ,  // 0x7c   |
  0x00, 0x41, 0x36, 0x08, 0x00 ,  // 0x7d   }
  0x10, 0x08, 0x08, 0x10, 0x08 ,  // 0x7e   ~
  0x00, 0x00, 0x00, 0x00, 0x00 ,  // 0x7f
  0x7e, 0x11, 0x11, 0x11, 0x7e ,  // 0x80   A  // Русские символы
  0x7f, 0x49, 0x49, 0x49, 0x33 ,  // 0x81   Б
  0x7f, 0x49, 0x49, 0x49, 0x36 ,  // 0x82   В
  0x7f, 0x01, 0x01, 0x01, 0x03 ,  // 0x83   Г
  0xe0, 0x51, 0x4f, 0x41, 0xff ,  // 0x84   Д
  0x7f, 0x49, 0x49, 0x49, 0x41 ,  // 0x85   E
  0x77, 0x08, 0x7f, 0x08, 0x77 ,  // 0x86   Ж
  0x41, 0x49, 0x49, 0x49, 0x36 ,  // 0x87   З
  0x7f, 0x10, 0x08, 0x04, 0x7f ,  // 0x88   И
  0x7c, 0x21, 0x12, 0x09, 0x7c ,  // 0x89   Й
  0x7f, 0x08, 0x14, 0x22, 0x41 ,  // 0x8A   K
  0x20, 0x41, 0x3f, 0x01, 0x7f ,  // 0x8B   Л
  0x7f, 0x02, 0x0c, 0x02, 0x7f ,  // 0x8C   M
  0x7f, 0x08, 0x08, 0x08, 0x7f ,  // 0x8D   H
  0x3e, 0x41, 0x41, 0x41, 0x3e ,  // 0x8E   O
  0x7f, 0x01, 0x01, 0x01, 0x7f ,  // 0x8F   П
  0x7f, 0x09, 0x09, 0x09, 0x06 ,  // 0x90   P
  0x3e, 0x41, 0x41, 0x41, 0x22 ,  // 0x91   C
  0x01, 0x01, 0x7f, 0x01, 0x01 ,  // 0x92   T
  0x47, 0x28, 0x10, 0x08, 0x07 ,  // 0x93   У
  0x1c, 0x22, 0x7f, 0x22, 0x1c ,  // 0x94   Ф
  0x63, 0x14, 0x08, 0x14, 0x63 ,  // 0x95   X
  0x7f, 0x40, 0x40, 0x40, 0xff ,  // 0x96   Ц
  0x07, 0x08, 0x08, 0x08, 0x7f ,  // 0x97   Ч
  0x7f, 0x40, 0x7f, 0x40, 0x7f ,  // 0x98   Ш
  0x7f, 0x40, 0x7f, 0x40, 0xff ,  // 0x99   Щ
  0x01, 0x7f, 0x48, 0x48, 0x30 ,  // 0x9A   Ъ
  0x7f, 0x48, 0x30, 0x00, 0x7f ,  // 0x9B   Ы
  0x00, 0x7f, 0x48, 0x48, 0x30 ,  // 0x9C   Э
  0x22, 0x41, 0x49, 0x49, 0x3e ,  // 0x9D   Ь
  0x7f, 0x08, 0x3e, 0x41, 0x3e ,  // 0x9E   Ю
  0x46, 0x29, 0x19, 0x09, 0x7f ,  // 0x9F   Я
  0x20, 0x54, 0x54, 0x54, 0x78 ,  // 0xA0   a
  0x3c, 0x4a, 0x4a, 0x49, 0x31 ,  // 0xA1   б
  0x7c, 0x54, 0x54, 0x54, 0x28 ,  // 0xA2   в
  0x7c, 0x04, 0x04, 0x04, 0x0c ,  // 0xA3   г
  0xe0, 0x54, 0x4c, 0x44, 0xfc ,  // 0xA4   д
  0x38, 0x54, 0x54, 0x54, 0x18 ,  // 0xA5   e
  0x6c, 0x10, 0x7c, 0x10, 0x6c ,  // 0xA6   ж
  0x44, 0x44, 0x54, 0x54, 0x28 ,  // 0xA7   з
  0x7c, 0x20, 0x10, 0x08, 0x7c ,  // 0xA8   и
  0x7c, 0x41, 0x22, 0x11, 0x7c ,  // 0xA9   й
  0x7c, 0x10, 0x10, 0x28, 0x44 ,  // 0xAA   к
  0x20, 0x44, 0x3c, 0x04, 0x7c ,  // 0xAB   л
  0x7c, 0x08, 0x10, 0x08, 0x7c ,  // 0xAC   м
  0x7c, 0x10, 0x10, 0x10, 0x7c ,  // 0xAD   н
  0x38, 0x44, 0x44, 0x44, 0x38 ,  // 0xAE   o
  0x7c, 0x04, 0x04, 0x04, 0x7c ,  // 0xAF   п
  0x7C, 0x14, 0x14, 0x14, 0x08 ,  // 0xB0   p
  0x38, 0x44, 0x44, 0x44, 0x20 ,  // 0xB1   c
  0x04, 0x04, 0x7c, 0x04, 0x04 ,  // 0xB2   т
  0x0C, 0x50, 0x50, 0x50, 0x3C ,  // 0xB3   у
  0x30, 0x48, 0xfc, 0x48, 0x30 ,  // 0xB4   ф
  0x44, 0x28, 0x10, 0x28, 0x44 ,  // 0xB5   x
  0x7c, 0x40, 0x40, 0x40, 0xfc ,  // 0xB6   ц
  0x0c, 0x10, 0x10, 0x10, 0x7c ,  // 0xB7   ч
  0x7c, 0x40, 0x7c, 0x40, 0x7c ,  // 0xB8   ш
  0x7c, 0x40, 0x7c, 0x40, 0xfc ,  // 0xB9   щ
  0x04, 0x7c, 0x50, 0x50, 0x20 ,  // 0xBA   ъ
  0x7c, 0x50, 0x50, 0x20, 0x7c ,  // 0xBB   ы
  0x7c, 0x50, 0x50, 0x20, 0x00 ,  // 0xBC   ь
  0x28, 0x44, 0x54, 0x54, 0x38 ,  // 0xBD   э
  0x7c, 0x10, 0x38, 0x44, 0x38 ,  // 0xBE   ю
  0x08, 0x54, 0x34, 0x14, 0x7c ,  // 0xBF   я
};
//********************************************************
// ==== Считывание напряжения питания ардуинки (Vcc) ====
#define Vref11 1.095 // для точной подстройки результата измерений
float ReadVcc() {
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); // устанавливаем вход АЦП на VCC !!!
  delay(1);  // задержка для устаканивания АЦП. Можно её избежать, если ADMUX как в строке выше (не менялся)
  ADCSRA |= _BV(ADSC);                                    // запуск АЦП преобразования
  while (bit_is_set(ADCSRA, ADSC));                       // ждем, пока АЦП отработает (выставит бит)
  return (Vref11 * 1023.0) / (ADCL | (ADCH << 8));        // результат преобразования в вольтах
}
/////// ==== Температура ==== ////////
double Thermister(int RawADC) {
  double Temp;
  Temp = log(((10240000/RawADC) - 10000)); // 10000=10кОм Для подстройки правим.
  Temp = 1 / (0.001129148 + (0.000234125 * Temp) + (0.0000000876741 * Temp * Temp * Temp));
  Temp = Temp - 273.15;   // Kelvin to Celcius
  return Temp;
}
void setup()  {
  pinMode(KEY_OK, INPUT); pinMode(KEY_L, INPUT); pinMode(KEY_R, INPUT); // настраиваем кнопки на вход
  digitalWrite(KEY_OK, HIGH); digitalWrite(KEY_L, HIGH); digitalWrite(KEY_R, HIGH); // подтяжка к плюсу питания
  VCC = ReadVcc();
  Initialize();  //Инициализация дисплея
  Clear_LCD();
  for (byte i = 0; i < 3; i++)
  {
    strcpy_P(buffer, (char*)pgm_read_word(&(string_table[i]))); // необходимые функции и расшифровки, просто скопируйте
    print(10, i * 10, 1, (buffer));
    //delay( 500 );
  }
  Update();
//  delay(KEY_DELAY);
  while (digitalRead(KEY_OK)) { // цикл, пока не нажали кнопку ОК
  Update();
  // Если не нажата 10 сек кнопка ок вход в режим осцилоографа автоматом
  osc--;
    if (osc==0) break;
  //////////  Выводим показание /////////
    float_print(60, 20, 1, analogRead(VBAT)*VCC/1024,1);  //Вольтаж батареи
    float_print(55, 40, 1, analogRead(TempC), 1);  // выводим на экран показание Температуры
  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[3]))); // пишем слово "Температура"
  print(10, 40, 1, (buffer));
  delay( 300 );
} // цикл нажатия ОК
  // нажали кнопку ОК из меню, инициализируем и выходим из меню
  count = 0; //countX = 0; // восстанавливаем на всякий случай переменные
  if (mode == 0) FreqCount.begin(1000);
  if (mode == 1) {
    InitTimersSafe();
  }
//  if (mode == 2) {
//    InitTimersSafe();
//  }
  if (mode == 3 || mode == 4) DDRC = 0x00; PORTC = 0x00;// Serial.begin(115200); // razv = 0;// весь порт С (это A0...A5) на вход и без подтяжки
  for (char a=37; a>0; a-=2);
  ok_press_time = millis(); //progrik: запоминаем время последнего нажатия
}
// беcконечный цикл - по сути прыгаем в подпрограммы
void loop() {
  switch (mode) {
    // Прыгаем в выбранный режим из главного меню
    case 0 : Oscil();        break; // "выпадаем" в осцилл
  }
}
//  === Читаем с АЦП данные и помещаем их в буфер === // 
void ReadAdc() {
uint16_t len;
len = (pause) ? BUFSIZE_LONG : BUFSIZE_SHORT;
  if (razv % 10) { // (razv>0)        // если развертка без задержек всяких (с 1 по 7)
    ADCSRA = 0b11100000 | (8 - (razv % 10)); // установили делитель (/2 - не работает, так что начинаем с /4)
    for (uint16_t i = 0; i < len; i++) { // цикл для чтения
      while (!(ADCSRA & 0x10));     // ждем готовность АЦП
      ADCSRA |= 0x10;               // запускаем следующее преобразование
      adcBuf[i] = ADCH;             // записываем данные в массив
    }
   // delay(0.3 * BUFSIZE_LONG);  //progrik: не нужно тормозить то, что и так еле работает))  // компенсация задержки по сравнению с разверткой 0...
  } else {                          // развертка с задержками (delay)
    ADCSRA = 0b11100111;            // делитель на /128
    for (uint16_t i = 0; i < len; i++) { // цикл для чтения
      while (!(ADCSRA & 0x10));     // ждем готовность АЦП
      ADCSRA |= 0x10;               // запускаем следующее преобразование
      delayMicroseconds(500);       // делаем задержку
      adcBuf[i] = ADCH;             // записываем данные в массив
    }
  }
}

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



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

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

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

//===================================================Инициализация дисплея
void Initialize() {
  pinMode(RES,   OUTPUT);
  pinMode(CS,    OUTPUT);
  pinMode(Data,  OUTPUT);
  pinMode(Clock, OUTPUT);
  // Инициализация дисплея
  //  dWrite(RES, 1);
  RESET_HIGH;
//  dWrite(Clock, 0);
  CLOCK_LOW;
//  dWrite(Data, 0);
  DATA_LOW;
//  dWrite(CS, 0);
//  CS_LOW;
  delay(20);
//  dWrite(CS, 1);
  CS_HIGH;
  SendByte(0, 0x2F);           // Power control set(charge pump on/off)
  SendByte(0, 0xA4);
  SendByte(0, 0xAF);           // экран вкл/выкл
  //Clear_LCD();
  //Update();
//  delay(500); //progrik: задержка после включения
//  CS_LOW; //progrik: вернул для тестов
//  delay(500); //progrik: задержка после включения
}

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

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

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

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

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

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

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

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

//====================================================Рисование линии
void drawLine(byte x0, byte y0, byte x1, byte y1, boolean color) {
  int steep = abs(y1 - y0) > abs(x1 - x0);
  if (steep) {
    swap(x0, y0);
    swap(x1, y1);
  }
  if (x0 > x1) {
    swap(x0, x1);
    swap(y0, y1);
  }
  int dx, dy;
  dx = x1 - x0;
  dy = abs(y1 - y0);
  int err = dx / 2;
  int ystep;
  (y0 < y1) ?  ystep = 1 : ystep = -1;
  for (; x0 <= x1; x0++) {
if(steep)
{
  drawPixel(y0, x0, color);
}
else
{
  drawPixel(x0, y0, color);
}
    err -= dy;
    if (err < 0) {
      y0 += ystep;
      err += dx;
    }
  }
}
/////////////////добавь где-то, например, под функцией DrawLine такую функцию:////////////////////////////
void drawVLineUltraFast(byte x, byte y0, byte y1, boolean color) {
  if(y0 > y1)
  {
    swap(y0, y1);
  }
  do
  {
    drawPixel(x, y0, color);
  }
  while(++y0 <= y1);
}
//========================================Рисование вертикальной линии
void drawFastVLine(byte x, byte y, byte h, boolean color) {
  //drawLine(x, y, x, y + h - 1, color);
  byte y1 = y + h - 1;
  do
  {
    drawPixel(x, y, color);
  }
  while(++y <= y1);
}

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

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

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

 

 

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

А можно скетч под 5110 ?