示波器是任何電子工程師必備的測試儀器。它用于可視化和觀察各種信號,通常作為一個二維圖,其中一個或多個信號隨時間繪制。它們用于電子設備的設計和調試,以查看和比較波形,并確定施加在其輸入端的信號隨時間變化的電壓電平、頻率、噪聲和其他參數。這使得示波器成為電子工程師或制造商辦公桌上非常重要的工具。然而,示波器相當昂貴。入門級型號的價格從 500 美元到 2,000 美元不等。而先進的示波器則要花費數千美元,這使得它們超出了基本用戶的承受能力。但是如果我們能創造出一種更便宜、更緊湊、并且容易制作?這就是導致今天教程的問題。
ESP32 示波器功能
單通道
1Msps
50000 @ 16bits 緩沖區(50ms 數據,1Msps)
在 1Msps 時從 10us/div 擴展到 5ms/div
1X 模式下的最大 VPP 3.3V 和 10X 模式下的 33V
使用觸覺開關進行快速響應控制。
頻率計算(20hz min 由于緩沖區大小)
簡單均值濾波器開/關
最大、最小、平均和峰峰值電壓
時間和電壓偏移
模擬、數字/數據模式
單次觸發
自動縮放
構建基于 ESP32 的示波器所需的組件
ESP32 開發套件
1.69” 240x280 圓角 TFT 顯示器(ST7789s)
觸覺開關
單刀雙擲開關
100K電阻
10K電阻
100nF電容
覆銅板或穿孔板
焊接工具
ESP32示波器電路圖
下面給出了基于 ESP32 的示波器的完整電路圖。
ESP32 用作數據采集的控制器。我們將利用內置的 I2S 緩沖區來存儲和操作信號。這里使用 38 Pin 變體,但您也可以使用其他開發模塊。
對于顯示,我們使用的是 1.69” TFT 顯示模塊。它的分辨率為 240x280 像素。顯示控制器是 ST7789S,為了驅動它,我們將使用 SPI 通信。
該模塊還包含一個我們尚未使用的 SD 卡插槽。我們可以在未來的更新中將其用于波形捕獲或類似應用。
鍵盤非常簡單。帶有上拉電阻的觸覺開關用于此目的。我們正在使用硬件中斷來檢測每個按鍵。這將為我們提供一個非常靈敏的鍵盤。您可以了解我們之前介紹的ESP32 中斷。
模擬輸入部分相當簡單。它由兩個 SPDT 開關組成,用于范圍選擇和 AC/DC 耦合選擇。對于范圍選擇,我們添加了一個分壓器,可用于饋送峰值電壓高于 3.3V 的信號。分壓器將信號轉換為 10:1 的比率。
構建和測試電路
您可以在 perfboard 中構建此項目,也可以使用頁面底部鏈接中的文件制作 PCB。包括用于墨粉轉移方法的 PDF 文件和用于制造的 Gerber 文件。這是示波器的 PCB 布局。
這是相同的PCB視圖。
底部 PCB 視圖。
用于示波器的 Arduino 代碼
從本文底部給出的 Circuit Digest GitHub 存儲庫鏈接下載整個代碼。在 GitHub 存儲庫中,您還可以找到一個名為TFT_eSPI的存檔。這個修改后的庫是驅動顯示器所必需的。將其解壓縮到 Arduino 庫文件夾。如果您已經安裝了TFT_eSPI 庫,請確保在提取修改后的庫之前將其刪除。完成后,在板管理器中選擇 esp32。然后編譯代碼并上傳。就是這樣,我們的 DIY 示波器就可以使用了。您可以使用底部的 Micro USB 端口為示波器供電。此端口僅用于供電。
代碼
#include
?
#include <驅動程序/i2s.h> #include <驅動程序/adc.h> #include#include #include #include “esp_adc_cal.h” #include “過濾器.h” //#define DEBUG_SERIAL //#define DEBUG_BUFF #define 延遲 1000 //精靈的寬度和高度 #定義寬度 240 #定義高度 280 #define ADC_CHANNEL ADC1_CHANNEL_5 // GPIO33 #define NUM_SAMPLES 1000 // 樣本數 #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); // 使用指向“tft”對象的指針聲明 Sprite 對象“spr” esp_adc_cal_characteristics_t adc_chars; TaskHandle_t 任務菜單; TaskHandle_t task_adc; 浮動 v_div = 825; 浮動 s_div = 10; 浮動偏移量 = 0; 浮動toffset = 0; uint8_t current_filter = 1; //選項處理程序 枚舉選項 { 沒有任何, 自動縮放, 分壓, 斯迪夫, 抵消, 偏移量, 篩選, 停止, 模式, 單身的, 清除, 重置, 探測, 更新F, 光標1, 光標2 }; int8_t volts_index = 0; int8_t tscale_index = 0; uint8_t opt = 無; 布爾菜單=假; 布爾信息=真; 布爾 set_value = false; 浮動率 = 1000;//以 ksps --> 1000 = 1Msps bool auto_scale = false; bool full_pix = true; 布爾停止=假; bool stop_change = false; uint16_t i2s_buff[BUFF_SIZE]; bool single_trigger = false; 布爾數據觸發器 = 假; 布爾更新屏幕=假; 布爾新數據=假; 布爾菜單操作 = 假; uint8_t digital_wave_option = 0;//0-自動 | 1-模擬 | 2 位數據 (SERIAL/SPI/I2C/etc) int btnok,btnpl,btnmn,btnbk; 無效 IRAM_ATTR btok() { btnok = 1; } 無效 IRAM_ATTR btplus() { btnpl = 1; } 無效 IRAM_ATTR btminus() { btnmn = 1; } 無效 IRAM_ATTR btback() { btnbk = 1; } 無效設置(){ 序列號.開始(115200); configure_i2s(1000000); 設置屏幕(); pinMode(BUTTON_Ok,輸入); pinMode(BUTTON_Plus,輸入); pinMode(BUTTON_Minus,輸入); pinMode(BUTTON_Back,輸入); attachInterrupt(BUTTON_Ok, btok, RISING); attachInterrupt(BUTTON_Plus, btplus, RISING); attachInterrupt(BUTTON_Minus, btminus, RISING); attachInterrupt(BUTTON_Back, btback, RISING); 特征ADC(); #ifdef DEBUG_BUF 調試緩沖區(); #萬一 xTaskCreatePinnedToCore( core0_task, "menu_handle", 10000, /* 以字為單位的堆棧大小 */ NULL, /* 任務輸入參數 */ 0, /* 任務的優先級 */ &task_menu, /* 任務句柄。*/ 0); /* 任務應該運行的核心 */ xTaskCreatePinnedToCore( core1_task, "adc_handle", 10000, /* 以字為單位的堆棧大小 */ NULL, /* 任務輸入參數 */ 3、/*任務的優先級*/ &task_adc, /* 任務句柄。*/ 1); /* 任務應該運行的核心 */ } 無效的core0_task(無效* pvParameters){ (void) pvParameters; 為了 (;;) { 菜單處理程序(); 如果(新數據 || 菜單操作){ 新數據=假; 菜單操作 = 假; 更新屏幕=真; 更新屏幕(i2s_buff,速率); 更新屏幕=假; vTaskDelay(pdMS_TO_TICKS(10)); Serial.println("CORE0"); } vTaskDelay(pdMS_TO_TICKS(10)); } } 無效core1_task(無效* pvParameters){ (void) pvParameters; 為了 (;;) { 如果(!single_trigger){ 而(更新屏幕){ vTaskDelay(pdMS_TO_TICKS(1)); } 如果(!停止){ 如果(停止更改){ i2s_adc_enable(I2S_NUM_0); stop_change = 假; } ADC_Sampling(i2s_buff); 新數據=真; } 別的 { 如果(!stop_change){ i2s_adc_disable(I2S_NUM_0); i2s_zero_dma_buffer(I2S_NUM_0); stop_change = 真; } } Serial.println("CORE1"); vTaskDelay(pdMS_TO_TICKS(300)); } 別的 { 浮動 old_mean = 0; 而(single_trigger){ 停止=真; ADC_Sampling(i2s_buff); 浮動平均值 = 0; 浮動 max_v, min_v; peak_mean(i2s_buff, BUFF_SIZE, &max_v, &min_v, &mean); //信號捕獲(pp > 0.4V || 變化平均值 > 0.2V)-> 數據分析 if ((old_mean != 0 && fabs(mean - old_mean) > 0.2) || to_voltage(max_v) - to_voltage(min_v) > 0.05) { 浮動頻率 = 0; 浮動周期 = 0; uint32_t 觸發器0 = 0; uint32_t 觸發器1 = 0; //如果模擬模式或自動模式和波形識別為模擬 布爾數字數據=!假; if (digital_wave_option == 1) { trigger_freq_analog(i2s_buff, RATE, mean, max_v, min_v, &freq, &period, &trigger0, &trigger1); } 否則如果(digital_wave_option == 0){ 數字數據=數字模擬(i2s_buff,max_v,min_v); 如果(!數字數據){ trigger_freq_analog(i2s_buff, RATE, mean, max_v, min_v, &freq, &period, &trigger0, &trigger1); } 別的 { trigger_freq_digital(i2s_buff, RATE, mean, max_v, min_v, &freq, &period, &trigger0); } } 別的 { trigger_freq_digital(i2s_buff, RATE, mean, max_v, min_v, &freq, &period, &trigger0); } 單觸發器 = 假; 新數據=真; Serial.println("單機"); //在停止模式下返回正常執行 } vTaskDelay(pdMS_TO_TICKS(1)); //其他任務開始的時間(低優先級) } vTaskDelay(pdMS_TO_TICKS(300)); } } } 無效循環(){}
?
評論