eefocus_4087784 发表于 2025-12-15 17:58:13

零知IDE——基于零知ESP32与DRV8833的稳定电机测速系统实现教程

本帖最后由 eefocus_4087784 于 2025-12-15 18:13 编辑

​ ✔零知开源(零知IDE)是一个专为电子初学者/电子兴趣爱好者设计的开源软硬件平台,在硬件上提供超高性价比STM32系列开发板、物联网控制板。取消了Bootloader程序烧录,让开发重心从 “配置环境” 转移到 “创意实现”,极大降低了技术门槛。零知IDE编程软件,内置上千个覆盖多场景的示例代码,支持项目源码一键下载,项目文章在线浏览。零知开源(零知IDE)平台通过软硬件协同创新,让你的创意快速转化为实物,来动手试试吧!✔访问零知实验室,获取更多实战项目和教程资源吧!
www.lingzhilab.com
​
项目概述
      本项目基于零知 ESP32主控,搭配DRV8833 电机驱动模块和红外对射计数传感器,实现了高精度、高稳定性的电机速度测量与控制。核心功能包括:通过串口接收 PWM 指令控制电机转速,利用红外对射传感器采集电机码盘脉冲,结合定时器中断精准采样、EMA 指数加权滤波和平滑阈值处理,最终在 OLED 屏实时显示稳定的速度值(cm/s)、当前 PWM 输出和原始脉冲数
项目难点及解决方案
      问题描述1:实时脉冲数据经常出现大幅跳动

解决方案:采用ESP32的硬件定时器提供精确的1秒采样周期,同时结合指数加权移动平均(EMA)滤波算法和变化阈值检测机制,实现了速度值的稳定输出

一、硬件操作部分硬件清单
组件名称型号规格数量备注
主控板                     零知ESP32-WROOM-32                            1   核心控制器                                    
电机驱动DRV8833双H桥驱动模块1电机速度控制
传感器红外对射计数传感器1脉冲检测
OLED显示屏0.96寸I2C SSD13061数据显示
直流电机微型直流电机1待测对象
码盘20格黑白码盘1安装于电机轴

接线方案表      DRV8833采用5V供电确保电机驱动能力

零知 ESP32 引脚连接器件器件引脚功能说明
12(MOTOR_AIN1)      DRV8833AIN1电机 PWM 控制引脚 1
13(MOTOR_AIN2)DRV8833AIN2电机方向 / 停止控制引脚 2      
14(SENSOR_PIN)红外对射传感器         信号脚(OUT)   脉冲计数信号输入
21(SDA)OLED1306SDAI2C 数据总线
22(SCL)OLED1306SCLI2C 时钟总线
DRV8833 OUT1直流电机电机引脚 1电机动力输出
DRV8833 OUT2直流电机电机引脚 2电机动力输出

具体接线图
连接实物图


二、代码讲解部分      代码核心在于实现了多层级的稳定性算法,下面详细解析关键部分:
硬件定时器中断
// ================= 硬件定时器配置 =================
hw_timer_t *speedSampleTimer = NULL;          // 速度采样定时器
volatile unsigned int timerInterruptCount = 0;// 定时器中断计数(10次=1秒)
portMUX_TYPE timerCriticalMux = portMUX_INITIALIZER_UNLOCKED; // 临界区保护锁

// ================= 中断服务函数 =================
/**
* @brief 红外传感器中断服务函数(上升沿触发+1ms去抖)
* 功能:过滤毛刺信号,统计有效脉冲数
*/
void IRAM_ATTR infraredSensorIsr() {
unsigned long currentIsrTime = micros();
// 1ms去抖:两次中断间隔>1000μs→视为有效脉冲
if (currentIsrTime - lastSensorIsrTime > 1000) {
    portENTER_CRITICAL_ISR(&timerCriticalMux); // 禁止其他中断,保护计数变量
    sensorPulseCount++;
    portEXIT_CRITICAL_ISR(&timerCriticalMux);
    lastSensorIsrTime = currentIsrTime;
}
}

/**
* @brief 定时器中断服务函数(100ms触发一次)
* 功能:累计中断次数,触发1秒一次的速度计算
*/
void IRAM_ATTR speedSampleTimerIsr() {
portENTER_CRITICAL_ISR(&timerCriticalMux);
timerInterruptCount++;
portEXIT_CRITICAL_ISR(&timerCriticalMux);
}
IRAM_ATTR确保中断函数存储在IRAM中,提高执行速度
硬件定时器配置
void setup() {
// 串口初始化(115200波特率,用于控制和波形输出)
Serial.begin(115200);
while (!Serial); // 等待串口就绪(兼容部分开发板)

// OLED初始化
if (!oledDisplay.begin(SSD1306_SWITCHCAPVCC, OLED_I2C_ADDR)) {
    Serial.println("OLED Init Failed!");
    while (1); // 初始化失败则卡死,提示错误
}
oledDisplay.setTextColor(SSD1306_WHITE); // 设置字体颜色为白色

// PWM初始化(适配DRV8833)
ledcSetup(PWM_CHANNEL_MOTOR, PWM_FREQUENCY, PWM_RESOLUTION);
ledcSetup(PWM_CHANNEL_STANDBY, PWM_FREQUENCY, PWM_RESOLUTION);
ledcAttachPin(MOTOR_DRV_AIN1, PWM_CHANNEL_MOTOR);
ledcAttachPin(MOTOR_DRV_AIN2, PWM_CHANNEL_STANDBY);

// 红外传感器初始化(下拉输入+上升沿中断)
pinMode(INFRARED_SENSOR_PIN, INPUT_PULLDOWN);
attachInterrupt(digitalPinToInterrupt(INFRARED_SENSOR_PIN),
                  infraredSensorIsr, RISING);

// 速度采样定时器初始化(100ms中断一次)
speedSampleTimer = timerBegin(0, 80, true); // 定时器0,80分频(1MHz计数),向上计数
timerAttachInterrupt(speedSampleTimer, &speedSampleTimerIsr, true); // 绑定中断函数
timerAlarmWrite(speedSampleTimer, 100000ul, true); // 100000×1μs=100ms,自动重载
timerAlarmEnable(speedSampleTimer); // 使能定时器中断

// 系统就绪提示
Serial.println("=== System Ready ===");
Serial.println("Usage: Send PWM value (0~255) via Serial to control motor");
Serial.println("Serial Output Format: RawPulse | FilteredPulse | Speed(cm/s)");
Serial.println("====================");
}
timerBegin 使用定时器0,预分频80(80MHz/80=1MHz),向上计数;timerAlarmWrite设置报警值为100,000微秒,自动重载;每10次中断构成1秒的完整采样周期


指数加权移动平均滤波
void loop() {
// 1. 串口接收PWM指令,控制电机速度
if (Serial.available() > 0) {
    int inputPwm = Serial.parseInt(); // 读取串口发送的PWM值
    // 验证输入有效性(仅接收0~255的数值)
    if ((Serial.read() == '\n' || Serial.read() == '\r') && inputPwm >= 0 && inputPwm <= 255) {
      setMotorPwmSpeed(inputPwm);
      Serial.printf("Motor PWM Set to: %d\n", currentMotorPwm);
    } else {
      Serial.println("Invalid Input! Please send number between 0~255");
    }
}

// 2. 每1秒采样一次,计算稳定速度(定时器中断累计10次=1秒)
if (timerInterruptCount >= 10) {
    portENTER_CRITICAL(&timerCriticalMux); // 临界区保护,避免数据冲突
    unsigned long rawPulse = sensorPulseCount; // 读取原始脉冲数
    sensorPulseCount = 0;                      // 重置脉冲计数
    timerInterruptCount = 0;                   // 重置定时器计数
    portEXIT_CRITICAL(&timerCriticalMux);

    // --- 核心算法:数据平滑+稳定速度计算 ---
    // A. 静态噪声过滤:低速截止
    if (rawPulse < minValidPulse) {
      rawPulse = 0;
      filteredPulseCount = 0.0; // 强制归零,避免噪声导致的虚假速度
    }

    // B. EMA指数加权滤波:平滑原始脉冲数据
    // 公式:滤波后值 = α×当前测量值 + (1-α)×上一次滤波值
    filteredPulseCount = (emaFilterAlpha * rawPulse) + ((1.0 - emaFilterAlpha) * filteredPulseCount);

    // C. 阈值判断:仅当脉冲变化足够大时更新显示(抑制微小波动)
    if (abs(filteredPulseCount - lastDisplayedPulse) > speedUpdateThreshold || rawPulse == 0) {
      lastDisplayedPulse = filteredPulseCount; // 更新显示用脉冲数
    }

    // D. 计算最终稳定速度(cm/s)
    currentSpeedCmS = lastDisplayedPulse * cmPerPulse;

    // --- 串口输出(格式固定,适配波形工具) ---
    // 输出格式:Raw脉冲数 | 滤波后脉冲数 | 速度(cm/s),保留2位小数
    Serial.print(rawPulse);
    Serial.print(" | ");
    Serial.print(filteredPulseCount, 1); // 滤波后脉冲数保留1位小数
    Serial.print(" | ");
    Serial.println(currentSpeedCmS, 2); // 速度保留2位小数,波形更细腻

    // --- 更新OLED显示 ---
    updateOledDisplay(lastDisplayedPulse, currentSpeedCmS);
}
}

环节输入输出核心作用
低速截止          原始脉冲数0 或原始值过滤静态噪声(无电机转动时的误计数)   
EMA 滤波原始脉冲数 / 上次平滑值平滑后脉冲数         抑制大幅波动,保留趋势
变化阈值平滑后脉冲数 / 上次显示值       稳定显示值过滤 ±1~2 的微小波动,保持显示稳定


项目完整代码
/**************************************************************************************
文件: /DRV8833_Motor_Stable_Speed/DRV8833_Motor_Stable_Speed.ino
作者:零知实验室(深圳市在芯间科技有限公司)
-^^- 零知开源,让电子制作变得更简单! -^^-
时间: 2025-12-2 16:36:27
说明: 基于ESP32的电机稳速测速系统。通过红外传感器检测脉冲,结合EMA滤波和阈值算法计算稳定速度,并在OLED上实时显示。可通过串口调节电机PWM速度。
***************************************************************************************/

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// ================= 用户可调参数 (校准区) =================
// 1. 脉冲-距离转换系数 (cm/脉冲):根据实际硬件校准
// 理论计算: 轮径25.75mm × π ÷ 码盘20格 ≈ 0.4045 cm/脉冲
// 校准规则:测量距离偏短→增大;偏长→减小
float cmPerPulse = 0.4045;

// 2. EMA滤波平滑系数 (0.01~1.0)
// 小值→更平滑但响应慢;大值→响应快但抖动大
const float emaFilterAlpha = 0.3;

// 3. 速度更新阈值 (死区):抑制微小波动
// 仅当脉冲变化超过此值时更新显示,避免数值频繁跳动
const float speedUpdateThreshold = 2.0;

// 4. 最小有效脉冲数:过滤静态噪声
// 1秒内脉冲数小于此值→视为电机停止
const int minValidPulse = 2;

// ================= 硬件引脚定义 =================
#define OLED_SCREEN_WIDTH 128    // OLED屏幕宽度
#define OLED_SCREEN_HEIGHT 64    // OLED屏幕高度
#define OLED_I2C_ADDR 0x3C       // OLED默认I2C地址
Adafruit_SSD1306 oledDisplay(OLED_SCREEN_WIDTH, OLED_SCREEN_HEIGHT, &Wire, -1);

// DRV8833电机驱动引脚
#define MOTOR_DRV_AIN1 12      // 电机PWM控制引脚1
#define MOTOR_DRV_AIN2 13      // 电机方向/停止控制引脚2
#define INFRARED_SENSOR_PIN 14   // 红外对射传感器信号引脚

// ESP32 PWM配置参数
#define PWM_CHANNEL_MOTOR 0      // 电机PWM通道1
#define PWM_CHANNEL_STANDBY 1    // 电机备用通道2
#define PWM_RESOLUTION 8         // PWM分辨率(8位→0~255)
#define PWM_FREQUENCY 1000       // PWM频率(1kHz,适配DRV8833)

// ================= 全局变量 =================
volatile unsigned long sensorPulseCount = 0;   // 传感器原始脉冲计数
volatile unsigned long lastSensorIsrTime = 0;// 传感器中断上次触发时间(去抖用)

// 滤波后数据存储
float filteredPulseCount = 0.0;    // EMA滤波后的脉冲数
float currentSpeedCmS = 0.0;       // 当前稳定速度(cm/s)
float lastDisplayedPulse = 0.0;    // 上一次显示的脉冲数

int currentMotorPwm = 0;         // 当前电机PWM输出值

// ================= 硬件定时器配置 =================
hw_timer_t *speedSampleTimer = NULL;          // 速度采样定时器
volatile unsigned int timerInterruptCount = 0;// 定时器中断计数(10次=1秒)
portMUX_TYPE timerCriticalMux = portMUX_INITIALIZER_UNLOCKED; // 临界区保护锁

// ================= 中断服务函数 =================
/**
* @brief 红外传感器中断服务函数(上升沿触发+1ms去抖)
* 功能:过滤毛刺信号,统计有效脉冲数
*/
void IRAM_ATTR infraredSensorIsr() {
unsigned long currentIsrTime = micros();
// 1ms去抖:两次中断间隔>1000μs→视为有效脉冲
if (currentIsrTime - lastSensorIsrTime > 1000) {
    portENTER_CRITICAL_ISR(&timerCriticalMux); // 禁止其他中断,保护计数变量
    sensorPulseCount++;
    portEXIT_CRITICAL_ISR(&timerCriticalMux);
    lastSensorIsrTime = currentIsrTime;
}
}

/**
* @brief 定时器中断服务函数(100ms触发一次)
* 功能:累计中断次数,触发1秒一次的速度计算
*/
void IRAM_ATTR speedSampleTimerIsr() {
portENTER_CRITICAL_ISR(&timerCriticalMux);
timerInterruptCount++;
portEXIT_CRITICAL_ISR(&timerCriticalMux);
}

// ================= 辅助函数 =================
/**
* @brief 设置电机PWM速度
* @param pwmVal: PWM值(0~255),0→停止,越大转速越快
*/
void setMotorPwmSpeed(int pwmVal) {
currentMotorPwm = constrain(pwmVal, 0, 255); // 限制PWM范围(0~255)
if (currentMotorPwm == 0) {
    ledcWrite(PWM_CHANNEL_MOTOR, 0);
    ledcWrite(PWM_CHANNEL_STANDBY, 0); // 两通道均为0→电机停止
} else {
    ledcWrite(PWM_CHANNEL_MOTOR, currentMotorPwm);
    ledcWrite(PWM_CHANNEL_STANDBY, 0); // DRV8833正转模式
}
}

/**
* @brief 更新OLED显示内容
* @param dispPulse: 显示用脉冲数(滤波后)
* @param dispSpeed: 显示用速度(cm/s)
*/
void updateOledDisplay(float dispPulse, float dispSpeed) {
oledDisplay.clearDisplay();

// 标题栏
oledDisplay.setTextSize(1);
oledDisplay.setCursor(0, 0);
oledDisplay.println("STABLE SPEED METER");
oledDisplay.drawLine(0, 10, OLED_SCREEN_WIDTH, 10, SSD1306_WHITE);

// 速度显示(大号字体)
oledDisplay.setCursor(0, 20);
oledDisplay.setTextSize(2);
oledDisplay.print(dispSpeed, 2); // 保留2位小数,提高精度
oledDisplay.setTextSize(1);
oledDisplay.println(" cm/s");

// 辅助信息(PWM+脉冲数)
oledDisplay.setCursor(0, 45);
oledDisplay.print("PWM: "); oledDisplay.print(currentMotorPwm);

oledDisplay.setCursor(64, 45);
oledDisplay.print("Pulse: "); oledDisplay.print((int)dispPulse);

oledDisplay.display();
}

// ================= 初始化函数 =================
void setup() {
// 串口初始化(115200波特率,用于控制和波形输出)
Serial.begin(115200);
while (!Serial); // 等待串口就绪(兼容部分开发板)

// OLED初始化
if (!oledDisplay.begin(SSD1306_SWITCHCAPVCC, OLED_I2C_ADDR)) {
    Serial.println("OLED Init Failed!");
    while (1); // 初始化失败则卡死,提示错误
}
oledDisplay.setTextColor(SSD1306_WHITE); // 设置字体颜色为白色

// PWM初始化(适配DRV8833)
ledcSetup(PWM_CHANNEL_MOTOR, PWM_FREQUENCY, PWM_RESOLUTION);
ledcSetup(PWM_CHANNEL_STANDBY, PWM_FREQUENCY, PWM_RESOLUTION);
ledcAttachPin(MOTOR_DRV_AIN1, PWM_CHANNEL_MOTOR);
ledcAttachPin(MOTOR_DRV_AIN2, PWM_CHANNEL_STANDBY);

// 红外传感器初始化(下拉输入+上升沿中断)
pinMode(INFRARED_SENSOR_PIN, INPUT_PULLDOWN);
attachInterrupt(digitalPinToInterrupt(INFRARED_SENSOR_PIN),
                  infraredSensorIsr, RISING);

// 速度采样定时器初始化(100ms中断一次)
speedSampleTimer = timerBegin(0, 80, true); // 定时器0,80分频(1MHz计数),向上计数
timerAttachInterrupt(speedSampleTimer, &speedSampleTimerIsr, true); // 绑定中断函数
timerAlarmWrite(speedSampleTimer, 100000ul, true); // 100000×1μs=100ms,自动重载
timerAlarmEnable(speedSampleTimer); // 使能定时器中断

// 系统就绪提示
Serial.println("=== System Ready ===");
Serial.println("Usage: Send PWM value (0~255) via Serial to control motor");
Serial.println("Serial Output Format: RawPulse | FilteredPulse | Speed(cm/s)");
Serial.println("====================");
}

// ================= 主循环 =================
void loop() {
// 1. 串口接收PWM指令,控制电机速度
if (Serial.available() > 0) {
    int inputPwm = Serial.parseInt(); // 读取串口发送的PWM值
    // 验证输入有效性(仅接收0~255的数值)
    if ((Serial.read() == '\n' || Serial.read() == '\r') && inputPwm >= 0 && inputPwm <= 255) {
      setMotorPwmSpeed(inputPwm);
      Serial.printf("Motor PWM Set to: %d\n", currentMotorPwm);
    } else {
      Serial.println("Invalid Input! Please send number between 0~255");
    }
}

// 2. 每1秒采样一次,计算稳定速度(定时器中断累计10次=1秒)
if (timerInterruptCount >= 10) {
    portENTER_CRITICAL(&timerCriticalMux); // 临界区保护,避免数据冲突
    unsigned long rawPulse = sensorPulseCount; // 读取原始脉冲数
    sensorPulseCount = 0;                      // 重置脉冲计数
    timerInterruptCount = 0;                   // 重置定时器计数
    portEXIT_CRITICAL(&timerCriticalMux);

    // --- 核心算法:数据平滑+稳定速度计算 ---
    // A. 静态噪声过滤:低速截止
    if (rawPulse < minValidPulse) {
      rawPulse = 0;
      filteredPulseCount = 0.0; // 强制归零,避免噪声导致的虚假速度
    }

    // B. EMA指数加权滤波:平滑原始脉冲数据
    // 公式:滤波后值 = α×当前测量值 + (1-α)×上一次滤波值
    filteredPulseCount = (emaFilterAlpha * rawPulse) + ((1.0 - emaFilterAlpha) * filteredPulseCount);

    // C. 阈值判断:仅当脉冲变化足够大时更新显示(抑制微小波动)
    if (abs(filteredPulseCount - lastDisplayedPulse) > speedUpdateThreshold || rawPulse == 0) {
      lastDisplayedPulse = filteredPulseCount; // 更新显示用脉冲数
    }

    // D. 计算最终稳定速度(cm/s)
    currentSpeedCmS = lastDisplayedPulse * cmPerPulse;

    // --- 串口输出(格式固定,适配波形工具) ---
    // 输出格式:Raw脉冲数 | 滤波后脉冲数 | 速度(cm/s),保留2位小数
    Serial.print(rawPulse);
    Serial.print(" | ");
    Serial.print(filteredPulseCount, 1); // 滤波后脉冲数保留1位小数
    Serial.print(" | ");
    Serial.println(currentSpeedCmS, 2); // 速度保留2位小数,波形更细腻

    // --- 更新OLED显示 ---
    updateOledDisplay(lastDisplayedPulse, currentSpeedCmS);
}
}
系统流程图



三、项目结果演示校准全过程1)硬件安装与接线
      按接线表连接所有器件,确保:
红外对射传感器与码盘间距 5~10mm,对齐齿隙;DRV8833 供电电压匹配电机;OLED I2C 地址为 0x3C
2)传统 PID 方案测试
      烧录原始 PID 代码,打开OLED 显示屏上的局域网地址,观察脉冲波形变化
原始脉冲数波动 52~71,抖动严重,无法稳定显示
3)改进方案校准
      烧录改进后代码,进行以下校准:
距离校准(CM_PER_PULSE):
      ①用尺子量取 1 米(100cm)距离,在串口发送 PWM=150,让电机带动轮子沿直线走 1 米;
      ②记录串口打印的Raw值(假设为 247),则CM_PER_PULSE=100/247≈0.405,修改代码后重新上传
滤波系数调节(FILTER_ALPHA):
      若速度仍抖,减小 α(如 0.2);若响应过慢,增大 α(如 0.4)
阈值调节(CHANGE_THRESHOLD):
      微小波动多则增大至 2.5~3.0;响应迟缓则减小至 1.5~2.0
4)稳定测试
      发送不同 PWM 值(50~255),观察串口和 OLED:
串口打印:Raw 值波动 ±3~5,但 Filtered 值波动≤±1,DispSpeed 波动≤±0.3 cm/s;
OLED 显示:速度值稳定,无频繁跳变

参数调节指南
参数默认值调节范围作用调节建议
CM_PER_PULSE0.4045      0.1-1.0每个脉冲对应的距离         根据实际测量校准
FILTER_ALPHA0.30.01-1.0          滤波平滑系数值越小越平滑但响应慢      
CHANGE_THRESHOLD   2.00.5-10.0变化检测阈值值越大显示越稳定
MIN_PULSE_CUTOFF21-10最小有效脉冲过滤静止噪声

稳定速度输出波形图
打开调试功能/串口绘图仪→设置波特率→观察波形脉冲和速度变化:


速度波形基本呈水平线,波动幅度≤±0.3 cm/s,无大幅跳变

视频演示
https://live.csdn.net/v/504219?spm=1001.2014.3001.5501
串口发送 PWM 指令(255→127→90→0)、实时展示 OLED 屏,速度值稳定更新,PWM 与原始脉冲数同步显示、串口波形区速度波形平稳,无剧烈波动

四、DRV8833 与红外对射传感器技术讲解DRV8833 电机驱动模块工作原理
      DRV8833 内部集成双 H 桥驱动电路,H 桥由 4 个 MOS 管组成,通过控制 MOS 管的导通 / 截止实现电机正转、反转、停止和调速
正转:AIN1=PWM,AIN2=LOW;
反转:AIN1=LOW,AIN2=PWM;
停止:AIN1=LOW,AIN2=LOW;
调速:改变 PWM 占空比(0~255),占空比越大,电机转速越高


红外对射计数传感器工作原理
传感器由红外发射管和接收管组成,呈对射式安装:
      无遮挡时:发射管发出的红外光被接收管接收,接收管导通,输出低电平;有遮挡时(码盘齿经过):红外光被阻断,接收管截止,输出高电平;电机转动时,码盘的齿和间隙交替遮挡红外光,传感器输出连续脉冲,脉冲数 = 码盘齿数 × 转动圈数。
计数逻辑
本项目电机码盘为 20 格(20 齿 + 20 间隙),电机转 1 圈,传感器输出 20 个脉冲;结合轮径计算每脉冲对应的距离:

每脉冲距离(cm)= 轮子周长(cm)/ 码盘齿数 =(π×轮径cm)/20


​ 五、常见问题解答(FAQ)Q1:为什么我的脉冲计数仍然不稳定?      A:请按以下步骤排查:检查传感器安装是否牢固,码盘是否偏心;测量传感器输出信号,增大 FILTER_ALPHA 至 0.4~0.5,或增大 CHANGE_THRESHOLD 至 2.5~3.0,尝试更小的值(如0.2);检查电源稳定性
Q2:电机不转动      A:请检查: DRV8833 供电,确保供电电大于等于电机工作电压; PWM 值大于等于70,电机启动需要最小电压驱动;DRV8833 模块引脚的 OUT1/OUT2 正确连接到电机,,AIN1=12,AIN2=13
项目资源整合
      DRV8833数据手册:    DRV8833 Dual H-Bridge Motor Driver datasheet (Rev. E)
      OLED SSD1306库文件:    Adafruit_SSD1306
页: [1]
查看完整版本: 零知IDE——基于零知ESP32与DRV8833的稳定电机测速系统实现教程

Baidu
map