Осциллограф – это инструмент, который должен быть у любого радиоинженера. Он используются для анализа и наблюдения за различными сигналами и позволяет выводить их график в двухмерной системе координат. Осциллографы широко применяются для отладки работы различных электронных устройств, позволяя просматривать и сравнивать формы сигналов, уровни напряжений, частоты, шумы и другие необходимые параметры.
Но проблема в том, что хорошие осциллографы достаточно дорого стоят – диапазон цен на осциллографы среднего уровня колеблется в пределах от 500 до $2,000, а цена на "продвинутые" осциллографы может доходить до нескольких тысяч долларов. Поэтому в данной статье мы рассмотрим создание дешевого осциллографа на основе модуля ESP32, который под силу сделать даже начинающему радиолюбителю. Наш осциллограф будет иметь следующие характеристики и особенности:
- один канал;
- 1 Мвыборок/c (1Msps);
- 16-битный буфер (50ms данных на 1 Мвыборке/c);
- шкала от 10 мкс/деление до 5 мс/деление при 1Msps;
- максимальное анализируемое напряжение 3.3V при масштабе 1X и 33V при масштабе 10X;
- быстрое и удобное управление с помощью кнопок;
- быстрый расчет частоты (минимум 20 Гц в зависимости от размера буфера);
- простой фильтр ON/OFF;
- способность проводить измерения максимального, минимального, среднего и пикового значения напряжений;
- возможность сдвига по времени и по напряжению;
- аналоговый и цифровой режим;
- простой триггер;
- автоматическая шкала (автомасштабирование).
Также на нашем сайте мы рассматривали проекты осциллографов на основе других микроконтроллеров (плат):
Необходимые компоненты
- Модуль ESP32 (купить на AliExpress).
- 1.69” 240x280 TFT дисплей ST7789s с закругленными углами.
- Кнопки.
- SPDT переключатели.
- Резисторы 10 и 100 кОм (купить на AliExpress).
- Конденсатор 100 нФ (купить на AliExpress).
- Медная пластина или перфорированная плата.
- Набор для пайки.
Реклама: ООО "АЛИБАБА.КОМ (РУ)" ИНН: 7703380158
Схема проекта
Схема осциллографа на основе модуля ESP32 представлена на следующем рисунке.
Модуль ESP32 в проекте нашего осциллографа будет использоваться для получения данных и управления всеми процессами в схеме. Мы будем использовать его встроенный I2S буфер для хранения и управления сигналами – в нашем случае это будет контакт 38, но вы можете использовать и другие разновидности модуля ESP32.
Для отображения информации в нашем проекте мы будем использовать модуль TFT дисплея с диагональю экрана 1.69” и контроллером ST7789S. Для взаимодействия с ним мы будем использовать интерфейс SPI.
Модуль данного дисплея также содержит слот для SD карт, но мы в нашем проекте его не используем. Но вы можете доработать проект и использовать его для сохранения, к примеру, форм сигналов или другой информации.
Для создания клавиатуры для нашего проекта мы использовали простую конструкцию, состоящую из кнопок и подтягивающих резисторов. Для обнаружения нажатий кнопок мы будем использовать аппаратные прерывания, более подробно про их использование в модуле ESP32 вы можете прочитать в этой статье. Таким образом, мы получим весьма чувствительную клавиатуру.
Аналоговая часть нашего проекта очень проста. Она состоит из двух переключателей SPDT типа для выбора диапазона и выбора AC/DC. Для выбора диапазона мы также добавили делитель напряжения, который можно использовать для подачи на наш осциллограф сигналов с уровнями более 3.3V. Делитель напряжения будет преобразовывать сигналы в отношении 10:1.
Конструирование осциллографа
Вы можете собрать всю конструкцию осциллографа на перфорированной плате либо же на печатной плате, используя для этого Gerber файлы, ссылка на скачивание которых приведена в конце статьи.
Внешний вид дизайна печатной платы для нашего проекта осциллографа приведен на следующем рисунке.
Вид фронтальной части печатной платы в другом формате.
Вид тыльной стороны печатной платы.
Исходный код программы
Все необходимые файлы для нашего проекта, включая код программы, можно скачать по следующей ссылке.
По данной ссылке в репозитории GitHub вы также можете найти архив с именем TFT_eSPI – он содержит модифицированную библиотеку для управления дисплеем. Ее необходимо извлечь в папку с библиотеками Arduino. Если вы уже установили стандартную библиотеку TFT_eSPI, убедитесь в том, что вы ее удалили прежде чем извлекать архив с нашей библиотекой в папку с библиотеками Arduino. Запитать наш осциллограф можно через порт Micro USB внизу конструкции проекта (он только для подачи питания).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
#include <Arduino.h> #include <driver/i2s.h> #include <driver/adc.h> #include <soc/syscon_reg.h> #include <TFT_eSPI.h> #include <SPI.h> #include "esp_adc_cal.h" #include "filters.h" //#define DEBUG_SERIAL //#define DEBUG_BUFF #define DELAY 1000 // Width and height of sprite #define WIDTH 240 #define HEIGHT 280 #define ADC_CHANNEL ADC1_CHANNEL_5 // GPIO33 #define NUM_SAMPLES 1000 // number of samples #define I2S_NUM (0) #define BUFF_SIZE 50000 #define B_MULT BUFF_SIZE/NUM_SAMPLES #define BUTTON_Ok 32 #define BUTTON_Plus 15 #define BUTTON_Minus 35 #define BUTTON_Back 34 TFT_eSPI tft = TFT_eSPI(); // объект для работы TFT дисплеем TFT_eSprite spr = TFT_eSprite(&tft); // Declare Sprite object "spr" with pointer to "tft" object (объявляем объект "spr" с указателем на объект "tft") esp_adc_cal_characteristics_t adc_chars; TaskHandle_t task_menu; TaskHandle_t task_adc; float v_div = 825; float s_div = 10; float offset = 0; float toffset = 0; uint8_t current_filter = 1; //options handler enum Option { None, Autoscale, Vdiv, Sdiv, Offset, TOffset, Filter, Stop, Mode, Single, Clear, Reset, Probe, UpdateF, Cursor1, Cursor2 }; int8_t volts_index = 0; int8_t tscale_index = 0; uint8_t opt = None; bool menu = false; bool info = true; bool set_value = false; float RATE = 1000; //in ksps --> 1000 = 1Msps bool auto_scale = false; bool full_pix = true; bool stop = false; bool stop_change = false; uint16_t i2s_buff[BUFF_SIZE]; bool single_trigger = false; bool data_trigger = false; bool updating_screen = false; bool new_data = false; bool menu_action = false; uint8_t digital_wave_option = 0; //0-auto | 1-analog | 2-digital data (SERIAL/SPI/I2C/etc) int btnok,btnpl,btnmn,btnbk; void IRAM_ATTR btok() { btnok = 1; } void IRAM_ATTR btplus() { btnpl = 1; } void IRAM_ATTR btminus() { btnmn = 1; } void IRAM_ATTR btback() { btnbk = 1; } void setup() { Serial.begin(115200); configure_i2s(1000000); setup_screen(); pinMode(BUTTON_Ok , INPUT); pinMode(BUTTON_Plus , INPUT); pinMode(BUTTON_Minus , INPUT); pinMode(BUTTON_Back , INPUT); attachInterrupt(BUTTON_Ok, btok, RISING); attachInterrupt(BUTTON_Plus, btplus, RISING); attachInterrupt(BUTTON_Minus, btminus, RISING); attachInterrupt(BUTTON_Back, btback, RISING); characterize_adc(); #ifdef DEBUG_BUF debug_buffer(); #endif xTaskCreatePinnedToCore( core0_task, "menu_handle", 10000, /* Stack size in words (размер стека, в словах */ NULL, /* Task input parameter (входной параметр задачи) */ 0, /* Priority of the task (приоритет задачи)*/ &task_menu, /* Task handle. (обработчик задачи) */ 0); /* Core where the task should run (номер ядра, на котором должна выполняться задача */ xTaskCreatePinnedToCore( core1_task, "adc_handle", 10000, /* Stack size in words */ NULL, /* Task input parameter */ 3, /* Priority of the task */ &task_adc, /* Task handle. */ 1); /* Core where the task should run */ } void core0_task( void * pvParameters ) { (void) pvParameters; for (;;) { menu_handler(); if (new_data || menu_action) { new_data = false; menu_action = false; updating_screen = true; update_screen(i2s_buff, RATE); updating_screen = false; vTaskDelay(pdMS_TO_TICKS(10)); Serial.println("CORE0"); } vTaskDelay(pdMS_TO_TICKS(10)); } } void core1_task( void * pvParameters ) { (void) pvParameters; for (;;) { if (!single_trigger) { while (updating_screen) { vTaskDelay(pdMS_TO_TICKS(1)); } if (!stop) { if (stop_change) { i2s_adc_enable(I2S_NUM_0); stop_change = false; } ADC_Sampling(i2s_buff); new_data = true; } else { if (!stop_change) { i2s_adc_disable(I2S_NUM_0); i2s_zero_dma_buffer(I2S_NUM_0); stop_change = true; } } Serial.println("CORE1"); vTaskDelay(pdMS_TO_TICKS(300)); } else { float old_mean = 0; while (single_trigger) { stop = true; ADC_Sampling(i2s_buff); float mean = 0; float max_v, min_v; peak_mean(i2s_buff, BUFF_SIZE, &max_v, &min_v, &mean); //signal captured (pp > 0.4V || changing mean > 0.2V) -> DATA ANALYSIS if ((old_mean != 0 && fabs(mean - old_mean) > 0.2) || to_voltage(max_v) - to_voltage(min_v) > 0.05) { float freq = 0; float period = 0; uint32_t trigger0 = 0; uint32_t trigger1 = 0; //if analog mode OR auto mode and wave recognized as analog bool digital_data = !false; if (digital_wave_option == 1) { trigger_freq_analog(i2s_buff, RATE, mean, max_v, min_v, &freq, &period, &trigger0, &trigger1); } else if (digital_wave_option == 0) { digital_data = digital_analog(i2s_buff, max_v, min_v); if (!digital_data) { trigger_freq_analog(i2s_buff, RATE, mean, max_v, min_v, &freq, &period, &trigger0, &trigger1); } else { trigger_freq_digital(i2s_buff, RATE, mean, max_v, min_v, &freq, &period, &trigger0); } } else { trigger_freq_digital(i2s_buff, RATE, mean, max_v, min_v, &freq, &period, &trigger0); } single_trigger = false; new_data = true; Serial.println("Single GOT"); //return to normal execution in stop mode (возвращаемся к нормальному функционированию в режиме простоя) } vTaskDelay(pdMS_TO_TICKS(1)); //time for the other task to start (low priorit) (время чтобы начать выполнение другой задачи) } vTaskDelay(pdMS_TO_TICKS(300)); } } } void loop() {} |
good job!
I got compiling error of: 'configure_i2s' was not declared in this scope
can be wrong Library?
what I2S library you used?
Thanks
You can download libraries from here - https://github.com/Circuit-Digest/ESP32-Oscilloscope/tree/main/ESP32_Oscilloscope