
交流电斩波控制串励式电机(上海标模JB50D)主控ESP32
简介
用于控制上海标模串励式电机JB50D
简介:用于控制上海标模串励式电机JB50D开源协议
:MIT License
描述
1.简介
用于控制上海标模串励式电机JB50D
主要为了学习如果通过单片机进行斩波控制
2.电机如图

3.外观

(output显示0~30为显示bug,已修复)



4.视频演示
【上海标模JB50D串励式电机斩波控制器】 https://www.bilibili.com/video/BV15o5r6jEbm/?share_source=copy_web&vd_source=cc24686df83ec755d5303e9e7f562b6f
5.操作介绍
- 开机后会锁定输出控制,需要短按旋转编码器解锁
- 后续短按旋转编码器让输出归零
- 短按(2个按键的其一)用于直接设定输出(旋转编码器控制很差)
- 长按则设定当前输出到该按键,记忆该值到永久性存储(NVS)
- 同时按下2个按键(并非旋转编码器)0.5秒后进入OTA,需要事先在代码上设定WiFi信息,再次按下2个按键返回正常模式
6.注意事项
- 为了无极控制,选择该旋转编码器,向左正常工作,向右极难控制,软件判断已尽力,如果可以不推荐使用
- 软件支持OTA,需要修改代码的WiFi部分,位于文件
config.h - 输出上使用代码进行了限制,即使设置输出100,也无法提供220V电压(电机会炸)
- 2个LED指示为:右侧为过零检测指示,如果过零检测存在会点亮;左侧为按键指示,按下后点亮,松开后熄灭
- 测速的霍尔传感器使用3144,预留2个霍尔引脚是为了错开90度精确测速,实际上已经满足实验要求就没接第二个,代码部分也没做.另外串励式电机干扰极大,务必接地线!!
- ESP32使用开发板,但买成16P的排母,因此最后2个空着
7.代码
7.1仓库
基本由ai完成
https://gitee.com/ashin10/esp32-swdc-controller.git
7.2过零检测+斩波代码核心思路
首先需要理解双向可控硅的导通规则是:
接收到脉冲信号->打开可控硅->电压变零时(过零点)自动切断电路
并非像pwm一样需要控制单片机拉高后再拉低引脚来控制通断
整个流程按时间线:
-
交流电正半周期(火线->零线)高电平,光耦的原边导通,副边导通,过零检测引脚被拉低
-
进入零点附近,光耦原边断开,副边断开,过零检测引脚被拉高,此时交流电并非真的过零点(可能是几十伏,但光耦已经不导通),因此提前了0.775ms(与限流电阻有关)
-
单片机设定过零检测引脚上升沿中断触发,记录当前微秒时间戳
-
单片机内创建了FreeRTOS任务,读取↑微秒时间戳,先通过
vTaskDelay(pdMS_TO_TICKS(waitMs));等待毫秒,再通过while (micros() < target) { }精确等待微秒,然后发射脉冲信号控制光耦MOC2032->可控硅进行斩波 -
上述为正半周期(0-10ms),负半周期(10-20ms)同样的先计算需要等待的时间(过零时间点+10ms+斩波时间点),再等待毫秒微秒的方式进行精确斩波
-
经过测试,最小启动电机的斩波等待时间7650us,需求最大转速6300us,因此以此时间段一百分等分进行0~100的电机档为控制
-
核心代码部分:
// FreeRTOS任务:根据输出值发射脉冲
void pulseControlTask(void *pvParameters) {
while (1) {
// 关键修复:在循环开始时就清除标志
// 确保必须等待一个新的过零信号,防止被之前的中断影响
zeroCrossDetected = false;
// 等待过零信号
while (!zeroCrossDetected) {
vTaskDelay(pdMS_TO_TICKS(1));
}
// 保存当前过零时间戳(此时标志刚被中断设置)
unsigned long zeroCrossTime = zeroCrossTimestamp;
// 读取当前输出值(0~30)
int currentOutput = outputPulse;
// output=0时不输出脉冲
if (currentOutput == 0) {
continue;
}
// ======================
// 延时逻辑
// ======================
// 输出1 → 延时7650us
// 输出100 → 延时6300us
unsigned long delayUs = map(currentOutput, 1, 100, 7650, 6300);
const unsigned long HALF_CYCLE = 10000; // 半波10ms
// ======================
// 正半周触发
// ======================
unsigned long target1 = zeroCrossTime + 775 + delayUs;
// 等待时机
unsigned long currentMicros = micros();
if (target1 > currentMicros) {
unsigned long waitUs = target1 - currentMicros;
// 先等毫秒级的时间,减少误差
if (waitUs > 0) {
// 等待毫秒
delayMicroseconds(waitUs % 1000);
// 计算需要等待的毫秒数
unsigned long waitMs = waitUs / 1000;
if (waitMs > 0) {
vTaskDelay(pdMS_TO_TICKS(waitMs));
}
}
// 精确等待剩余的微秒
while (micros() < target1) {
// 空循环等待
}
}
// 输出脉冲
digitalWrite(CTRL_PIN, HIGH);
delayMicroseconds(30);
digitalWrite(CTRL_PIN, LOW);
// ======================
// 负半周 = 过零时间 +10ms
// ======================
unsigned long target2 = zeroCrossTime + HALF_CYCLE + 775 + delayUs;
// 等待负半周时机
currentMicros = micros();
if (target2 > currentMicros) {
unsigned long waitUs = target2 - currentMicros;
// 先等毫秒级的时间,减少误差
if (waitUs > 0) {
// 等待毫秒
delayMicroseconds(waitUs % 1000);
// 计算需要等待的毫秒数
unsigned long waitMs = waitUs / 1000;
if (waitMs > 0) {
vTaskDelay(pdMS_TO_TICKS(waitMs));
}
}
// 精确等待剩余的微秒
while (micros() < target2) {
// 空循环等待
}
}
// 负半周脉冲
digitalWrite(CTRL_PIN, HIGH);
delayMicroseconds(30);
digitalWrite(CTRL_PIN, LOW);
// 处理完过零信号后关闭LED2
digitalWrite(LED2_PIN, LOW);
}
}
8.物料清单
ACDC电源
旋转编码器(建议购买塑料头)
共模电感
设计图
未生成预览图,请在编辑器重新保存一次BOM
暂无BOM
克隆工程知识产权声明&复刻说明
本项目为开源硬件项目,其相关的知识产权归创作者所有。创作者在本平台上传该硬件项目仅供平台用户用于学习交流及研究,不包括任何商业性使用,请勿用于商业售卖或其他盈利性的用途;如您认为本项目涉嫌侵犯了您的相关权益,请点击上方“侵权投诉”按钮,我们将按照嘉立创《侵权投诉与申诉规则》进行处理。
请在进行项目复刻时自行验证电路的可行性,并自行辨别该项目是否对您适用。您对复刻项目的任何后果负责,无论何种情况,本平台将不对您在复刻项目时,遇到的任何因开源项目电路设计问题所导致的直接、间接等损害负责。












