你的位置: 首页 > 仪器仪表 > 电工仪器仪表

基于ESP32实现示波器的制作教程

2023-08-09 11:30:16 | 人围观 | 评论:

  示波器是任何电子工程师必备的测试仪器。它用于可视化和观察各种信号,通常作为一个二维图,其中一个或多个信号随时间绘制。它们用于电子设备的设计和调试,以查看和比较波形,并确定施加在其输入端的信号随时间变化的电压电平、频率、噪声和其他参数。这使得示波器成为电子工程师或制造商办公桌上非常重要的工具。然而,示波器相当昂贵。入门级型号的价格从 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 的示波器的完整电路图。

poYBAGLKM_uAND4QAAFh3txgaIQ478.png

  ESP32 用作数据采集的控制器。我们将利用内置的 I2S 缓冲区来存储和操作信号。这里使用 38 Pin 变体,但您也可以使用其他开发模块。

pYYBAGLKM_iALX7kAAEVz0dPU5Y011.png

  对于显示,我们使用的是 1.69” TFT 显示模块。它的分辨率为 240x280 像素。显示控制器是 ST7789S,为了驱动它,我们将使用 SPI 通信

pYYBAGLKM_KAa9TsAAChLANxAWc802.png

  该模块还包含一个我们尚未使用的 SD 卡插槽。我们可以在未来的更新中将其用于波形捕获或类似应用。

poYBAGLKM_WABLhuAAPzJG4qDJ8575.png

  键盘非常简单。带有上拉电阻的触觉开关用于此目的。我们正在使用硬件中断来检测每个按键。这将为我们提供一个非常灵敏的键盘。您可以了解我们之前介绍的ESP32 中断。

pYYBAGLKM-uAM1SFAACG2trtwyk980.png

  模拟输入部分相当简单。它由两个 SPDT 开关组成,用于范围选择和 AC/DC 耦合选择。对于范围选择,我们添加了一个分压器,可用于馈送峰值电压高于 3.3V 的信号。分压器将信号转换为 10:1 的比率。

pYYBAGLKM-mAdw3JAACh89aUV4s072.png

  构建和测试电路

  您可以在 perfboard 中构建此项目,也可以使用页面底部链接中的文件制作 PCB。包括用于墨粉转移方法的 PDF 文件和用于制造的 Gerber 文件。这是示波器的 PCB 布局。

poYBAGLKM-2AWJ1EAANux9LScV4325.png

  这是相同的PCB视图。

poYBAGLKM-GAQZ-aAALeonXbYqQ009.png

  底部 PCB 视图。

poYBAGLKM_KAKZR5AAOTKM8ElG8659.png

  用于示波器的 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));
    }
  }
}
无效循环(){}

 





标签:[db:tags]

相关内容推荐: