示波器是任何电子工程师必备的测试仪器。它用于可视化和观察各种信号,通常作为一个二维图,其中一个或多个信号随时间绘制。它们用于电子设备的设计和调试,以查看和比较波形,并确定施加在其输入端的信号随时间变化的电压电平、频率、噪声和其他参数。这使得示波器成为电子工程师或制造商办公桌上非常重要的工具。然而,示波器相当昂贵。入门级型号的价格从 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)); } } } 无效循环(){}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)