想请教一个问题,贵公司的FOC 软件,是否有自动适应不同电机的能力,自学习的算法。
hbuthejt 发表于 2026-3-26 22:14
想请教一个问题,贵公司的FOC 软件,是否有自动适应不同电机的能力,自学习的算法。 ...
没有自学习能力。
不错 不错
梁工 发表于 2024-5-22 15:45
这个电容与电阻组成RC滤波,根据要求的响应时间来确定,用于滤除尖峰干扰,但又不会导致过零检测延时太多 ...
这个电容与电阻组成RC滤波,根据要求的响应时间来确定,用于滤除尖峰干扰,但又不会导致过零检测延时太多,一般我控制在40us之内。
我的STC8电路中分压电阻10K与1.5K并联后电阻为1.3K,电容为103,则RC值为13us。
再STC32丁路中,电容我修改为223,则RC值为28.6us,抗尖峰更好,增加的延时不多。
一般来说,如果最高切换周期比较长,则可以使用较大的RC值,如果切换时间比较短,则使用较小RC值,而RC值越大抗尖峰干扰越好,但复杂的延时越长,要做一个平衡。
对于5万转以上的转速,建议使用103(10nF)梁大师:请赐教一下RC滤波问题,究竟是哪些电阻参与并联,请明确指出哪个10k电阻和1.5k电阻并联(基础差)
陈馀庆 发表于 2026-5-10 12:34
比如R21与R22分压,信号衰减系数=1.5/(10+1.5)=0.13,等效输出电阻为R21与R22并联后的1.3K。
其余2个分压电路同理。
陈馀庆 发表于 2026-5-10 12:34
梁大师:我用STC8H8K64U芯片复刻大师无刷直流电机,调试过程中,因为不会使用串口输出,就简单地用5个LED来监视程序是否执行特定位置,驱动电压12伏,测试电机是大疆精灵四航测无人机电机。我在程序void PWMA_ISR(void) interrupt PWMA_VECTOR函数里,放置五个LED控制代码(P37\P10\P11\P26\P27),程序运行后,电机可以强拖起来,但就转转停停反反复复,发现只有P11端口LED间歇性闪灭,其他灯都不工作,由此可判断程序压根没检测到电机反电动势过零情况,上面问了RC滤波问题,比较器反电动势检测电路我用电阻2k,瓷片电容是103,其他都用10k。pwm中断函数:
void PWMA_ISR(void) interrupt PWMA_VECTOR // 23.4375KHz(42.67us)中断率,用于处理启动电机,查询读取比较器结果
{ u8 i;
B_pre_cmpo = B_cmpo; //保存上一个输出电平
cmpo_state = (cmpo_state << 1) | (CMPCR1 & 1); // 读取比较器状态,放在一个字节中, 连续8次检测到相同电平才认为有效
if(cmpo_state == 0xff) B_cmpo= 1; //连续8个采样为1, 则比较器输出1
if(cmpo_state == 0x00) B_cmpo= 0; //连续8个采样为0, 则比较器输出0
//============================ 转动检测 =======================
if(run_mode == ROLL_CHECK) //转动检测
{
if(++StartTime >= ROLL_MaxTime) //超时未检测到转动,ROLL_MaxTime 最大值为2849ms
{
P37 = ! P37;
StartTime = 0;
SartCnt = 0;
P_PWM2N = 0;
P_PWM3N = 0;
RollCheckIndex = 0;
run_mode = PRE_POSITION;
}
else if(RollCheckIndex == 0) //设置A相下降沿
{
ADC_CONTR = 0x80+BMF_A; // 比较器CMP+选择A相
StartTime = 0;
PWMA_ENO= 0; P_PWM1P = 0; P_PWM1N = 0; P_PWM2P = 0; P_PWM2N = 0; P_PWM3P = 0; P_PWM3N = 0; //关闭PWM输出, 自由转动
cmpo_state = 0x00; B_cmpo= 0; //连续8个采样为0, 则比较器输出0
RollCheckIndex = 1;
}
else if(RollCheckIndex == 1) //等待A相下降沿
{
P11 = ! P11;
if(B_pre_cmpo && !B_cmpo) //下降沿出现
{
ADC_CONTR = 0x80+BMF_B; // 比较器CMP+选择B相
StartTime = 0;
cmpo_state = 0xFF;
B_cmpo= 1; //连续8个采样为1, 则比较器输出1
RollCheckIndex = 2;
P10 = ! P10;
}
}
else if(RollCheckIndex == 2) //等待B相上升沿
{
P26 = ! P26;
if(!B_pre_cmpo && B_cmpo) //上升沿出现
{
RollPahseTimeAB = StartTime; //A相下降沿与B相上升沿时间
StartTime = 0;
ADC_CONTR = 0x80+BMF_A; // 比较器CMP+选择A相
cmpo_state = 0x00;
B_cmpo= 0; //连续8个采样为0, 则比较器输出0
RollCheckIndex = 3;
P27 = ! P27;
}
}
else if(RollCheckIndex == 3) //等待A相下降沿
{
if(B_pre_cmpo && !B_cmpo) //下降沿出现
{
StartTime = (StartTime + RollPahseTimeAB)/2; //半个周期(3个换相时间)
if(RollPahseTimeAB < StartTime) //顺时针
{
P_PWM2N = 1;
P_PWM3N = 1; //刹车
StartTime = 0;
RollCheckIndex = 4;
}
else //逆时针
{
T4T3M &= ~(1<<3); // Timer3停止运行
T3H = 0; T3L = 0;
B_Timer3_OverFlow = 0;
T4T3M |=(1<<3); //Timer3开始运行
XiaoCiCnt = 0; //消磁
PhaseTime = StartTime *14; //半个周期(3个换相时间)换相时间折算成us,PhaseTime = (StartTime/3) *42.667;
for(i=0; i<4; i++) PhaseTimeTmp = PhaseTime; //初始换相时间
delay_N_10us(PhaseTime/20-34); //30度角延时=PhaseTime/2/10, -34为修正8次采样时间340us
step = 2;
StepMotor(); //换相(对应A相反电动势下降沿)
TimeOut = 200; //堵转超时
PWM_Value = START_PWM_DUTY;
PWMA_IER = 0; //如果电机已启动运行,则禁止PWMA中断
run_mode = MOTOR_RUN;
delay_us(50);
CMPCR1 &= ~(0x40+0x30); // 软件清除中断标志位, 禁止比较器上升沿、下降沿中断中断
if(step & 1) CMPCR1 |= (1<<5); //1: 允许上升沿中断, 0: 禁止
else CMPCR1 |= (1<<4); //1: 允许下降沿中断, 0: 禁止
}
}
}
else if(RollCheckIndex == 4) //等待A相下降沿, 直到检测不到则停止?
{
if(B_pre_cmpo && !B_cmpo) //下降沿出现
{
StartTime = 0;
}
}
}
//============================预定位模式=========================================
else if(run_mode == PRE_POSITION) //预定位模式
{
if(RollCheckIndex == 0)
{
CMPCR1 &= ~(0x40+0x30); // 软件清除中断标志位, 禁止比较器上升沿、下降沿中断中断
PWMA_ENO= 0x3f; // 全部打开PWM输出
PWM_Value = PRE_PWM_DUTY; // 设置预定位占空比
SartCnt = 0;
angle = 4096-680; //angle = 3416
LoadPwm();
RollCheckIndex = 1;
}
else if(RollCheckIndex == 1)
{
if(++SartCnt == (PRE_STATE_TIME1*23)) //预定位1时间到, PRE_STATE_TIME1最大值为2849ms
{
SartCnt = 0;
angle = 4096-340;
LoadPwm();
RollCheckIndex = 2;
}
}
else if(RollCheckIndex == 2)
{
if(++SartCnt == (PRE_STATE_TIME2*23)) //预定位2时间到, PRE_STATE_TIME2最大值为2849ms
{
SartCnt = 0;
angle = 0;
LoadPwm();
RollCheckIndex = 3;
}
}
else if(RollCheckIndex == 3)
{
if(++SartCnt == (PRE_STATE_TIME3*23)) //预定位3时间到, PRE_STATE_TIME3最大值为2849ms
{
StartTime = D_StartTime*6; //StartTime=t*1000/(1024*4/24)=t*375/64=t*6步
roll_sum= 0;
roll_N = 0;
SartCnt= 0;
PWM_Value = START_PWM_DUTY; // 设置启动占空比
run_mode= STARTING;
}
}
AdcConvert();
}
//============================ 启动模式 =========================================
else if(run_mode == STARTING) //启动模式,使用查表S曲线加速启动
{
if(++fs_cnt == 4) // 使用256点的S曲线表,初速V1(转/分),终速V2(转/分),加速时间t(ms),fs=24000000/1024/4 = 46875 /8 = 5859Hz,则加速总步数StartTime = t/1000*fs = t*1000/(1024*4/24) = t*375/64=t*6步
{
// 查表步进 = 65536/StartTime, 步数SartCnt每次+1,查表得到速度系数 du=S_Curve = S_Curve, 当前速度 V = V1 + (V2-V1)*du/1024 (转/秒)
fs_cnt = 0; // 采样频率fs,16位累加器roll_sum,磁极对MPP,则电机转速 V=60*fs*roll_N/roll_sum/MPP, 转速步进 roll_N = V*MPP*sum/60/fs = V*MPP*65536/60/5859 = 0.1864*V*MPP = V*MPP*191/1024
SartCnt++;
if(SartCnt < StartTime)
{ roll_N = BeginSpeed + (u16)(((u32)S_Curve[(u8)((u32)SartCnt*256/StartTime)] *(FinishSpeed - BeginSpeed))/1024); //当前速度 V = V1 + (V2-V1)*du/1024 = V1 + (V2-V1)*S_Curve/1024(转/秒)
roll_sum += (u16)(((u32)roll_N*MPP*191) >>10); // 当前速度 = V*MPP*191/1024
angle = (roll_sum >> 4); //roll_sum是16位的,SVPWM表长度为12位
LoadPwm();
}
else //强拖结束
{
PWMA_ENO = 0;
P_PWM1P=0;
P_PWM2P=0;
P_PWM3P=0;
P_PWM1N=0;
P_PWM2N=0;
P_PWM3N=0;
StartTime = 0;
RollCheckIndex = 0;
run_mode = ROLL_CHECK; //进入转动检测模式
}
}
AdcConvert();
}
PWMA_SR1 = 0; //清除标志
}
程序源码基本没修改;
另外把加速时间改大D_StartTime 改大一些,比如5000 电机强拖时间长一些而已。请大师抽空指点一下,示波器测量波具体测量哪个位置(示波器探头和负极分别夹在哪儿),请结合反电动势分压图指导一下,谢谢!
附件有三个,一个图片、一个视频、一个程序源码。
这段时间都在研究学习STC公司开源无刷无感驱动直流电机,源代码先前已经上传请教deepseek,虽然deepseek讲解很细,但有些细节还没弄懂,于是尝试串口输出查看代码运行情况失败了,最后采取多个LED来监视程序是否执行特定位置,驱动电压12伏,测试电机是大疆精灵四航测无人机电机。我在程序void PWMA_ISR(void) interrupt PWMA_VECTOR函数里,放置五个LED控制代码(P37\P10\P11\P26\P27),程序运行后,电机强拖转转停停反反复复,发现只有P11端口LED间歇性闪灭,其他灯都不工作,由此可判断程序压根没检测到电机反电动势过零情况,现在请教deepseek三个问题:一是STC公司梁工在论坛答复网友提问说:STC8电路中分压电阻10K与1.5K并联后电阻为1.3K,电容为103,则RC值为13us,究竟是那些电阻并联(附件上传分压电路图);二是程序转动检测疑问。代码是先设置A相下降沿,打开比较器CMP+选择A相,等待A相下降沿出现,接着是选择设置比较器CMP+选择B相,等待B相上升沿,但我对比程序中注解拍序 逆时针 顺时针,我的理解是拍序应该是0-5执行,按逆时针转分析,A相下降沿出现是第二拍序,后面应该是第三拍序C相上升沿,若按顺时针分析,A相下降沿出现,后面是B相上升沿,但程序后面计算出是顺时针转却刹车了,请问究竟是怎么一样时序。三是梁工测量反电动势是测量哪里,请明确在分压电路中指出示波器测量点
deepseek给出答复:问题一:梁工所说“分压电阻10K与1.5K并联后电阻为1.3K”是指哪些电阻?请看你的分压电路图(无霍尔传感器时使用):
[*]每一相(例如 EMFA)都有一路分压网络:
[*]上拉电阻(串联在相线与检测点之间):R37 = 10kΩ
[*]下拉电阻(检测点对地):R40 = 1.5kΩ
[*]滤波电容:C18 = 223(22nF,图中标注223,但梁工举例STC8用103)
[*]同样的结构用于另外两相:EMFB对应R36(10k)、R39(1.5k)、C17;EMFC对应R35(10k)、R38(1.5k)、C16。
并联电阻的由来:
从分压中点(即R37与R40的连接点、同时也是电容C18的正极)向源端看进去,等效电阻 ≈ R37 // R40 = 10kΩ // 1.5kΩ ≈ 1.3kΩ。这个等效电阻与电容C组成一阶RC低通滤波器,其时间常数 τ = R_eq × C。
[*]若C=103(10nF)→ τ = 1.3k × 10nF = 13μs
[*]若C=223(22nF)→ τ = 1.3k × 22nF = 28.6μs
小结:并联的两个电阻就是每相串联在信号通路上的10kΩ电阻和对地的1.5kΩ电阻,它们的并联值决定了RC滤波的等效电阻。问题二:转动检测的时序疑问(A相下降沿之后,到底该等哪个沿?)1. 梁工给的拍序表(逆时针 vs 顺时针)
拍序逆时针(换相通电相,反电动势过零)顺时针(换相通电相,反电动势过零)
0AB, C↓AC, B↓
1AC, B↑AB, C↑
2BC, A↓CB, A↓
3BA, C↑CA, B↑
4CA, B↓BA, C↓
5CB, A↑BC, A↑
表中“A↓”表示A相反电动势下降沿,“B↑”表示B相反电动势上升沿,依此类推。2. 你程序中转动检测的边沿顺序代码逻辑(run_mode == ROLL_CHECK 状态机):
[*]Index0 → 准备检测A相下降沿
[*]Index1 → 等待A相下降沿(P11闪烁) → 检测到后切至B相,准备检测B相上升沿
[*]Index2 → 等待B相上升沿 → 检测到后记录时间,切回A相,准备检测A相下降沿
[*]Index3 → 等待A相下降沿 → 检测到后判断转向:
if(RollPahseTimeAB < StartTime) → 顺时针 → 刹车
else → 逆时针 → 启动电机
3. 这个顺序对应哪个转向?
[*]顺时针:从拍序表看,顺时针时A相下降沿出现在状态2(CB, A↓),之后下一个反电动势过零是状态3的B相上升沿(CA, B↑)。
→ 所以顺序 A↓ → B↑ 正是顺时针的特征。
[*]逆时针:逆时针时A相下降沿出现在状态2(BC, A↓),之后下一个过零是状态3的C相上升沿(BA, C↑)。
→ 顺序应为 A↓ → C↑,而不是B↑。
结论:你的代码中检测 A下降沿 → B上升沿 实际上是在识别顺时针转动。但代码在判断出顺时针后却执行了刹车(P_PWM2N=1, P_PWM3N=1),而没有启动电机。这说明设计者期望的转动方向是逆时针,如果检测到顺时针则刹车并停止(可能为了重新尝试或报错)。4. 你的困惑点“按逆时针分析,A相下降沿后应该是C相上升沿;按顺时针分析,A相下降沿后是B相上升沿。程序后面算出顺时针却刹车了。”正解:你的理解完全正确。代码的逻辑就是:
[*]如果检测到 A↓ → B↑(顺时针),则刹车;
[*]如果检测到 A↓ → C↑(逆时针),才启动。
但代码中并没有等待C上升沿,而是通过时间比较来区分转向:
[*]实际物理转动中,若为逆时针,A↓ 到下一个 A↓ 之间的三个边沿间隔基本相等(每60°电角度)。
[*]代码里用了一个技巧:先测 A↓ → B↑ 的时间(T1),再测 B↑ → 下一个A↓ 的时间(T2)。
[*]顺时针时,T1 ≈ T2 ≈ 60°电角度时间。
[*]逆时针时,由于边沿顺序不同,测到的 A↓ → B↑ 实际上并不是相邻的过零(中间隔了C相上升沿),因此T1 ≈ 2×T2?代码中用 StartTime = (StartTime + RollPahseTimeAB)/2 然后比较 RollPahseTimeAB < StartTime 来判别。
但无论如何,最终分支是:顺时针刹车,逆时针启动。
5. 为什么你实测电机只闪P11(等待A相下降沿)?
[*]电机“强拖转转停停”说明进入了启动强拖阶段(STARTING模式),强拖结束后进入转动检测(ROLL_CHECK)。
[*]转动检测时,电机处于自由转动状态(PWM全部关闭)。你的电机可能因为惯性不足、负载过大或强拖结束时机不对,导致实际没有产生有效的反电动势。
[*]程序一直在 RollCheckIndex == 1 里等待 A相下降沿,P11 不断翻转(LED闪烁),但永远等不到边沿,因为电机已停转或反电动势太小。
[*]其他LED(P10、P26、P27)分别在检测到A下降沿、B上升沿、再次A下降沿时才翻转,所以它们不亮,印证了第一个边沿都没检测到。
问题三:梁工测量反电动势的示波器测量点在分压电路图中,请测量RC滤波后的信号点,即每个10kΩ电阻与1.5kΩ电阻的连接处,同时也是电容的正极。具体位置:
[*]A相测量点:R37与R40的连接点(电容C18正极)
[*]B相测量点:R36与R39的连接点(电容C17正极)
[*]C相测量点:R35与R38的连接点(电容C16正极)
测量注意事项:
[*]用示波器探头(x10档)测量这些点对地,可以看到经过分压衰减(约1.5/11.5 ≈ 0.13倍)和RC滤波后的反电动势波形。
[*]原始电机相线电压可能高达几十伏(你的12V供电下,相线尖峰可能更高),直接测量有风险,测量分压中点更安全。
[*]调试时建议同时测两相(用双通道),观察过零点的顺序和相位差。
如果一直检测不到过零,可能原因:
[*]强拖力度不够,电机未真正旋转到能产生足够反电动势的速度。
[*]RC滤波电容(223 = 22nF)过大,使信号严重滞后且幅度衰减过大,尤其是在低速时。可以临时换成103(10nF)试试。
[*]比较器输入引脚可能被其他外设复用,或分压电阻值计算有误(确保1.5kΩ对地分压后电压在MCU允许范围0~5V内)。
建议:
[*]用示波器确认反电动势波形:在强拖(STARTING模式)期间,测量分压中点(如上所述),看是否有明显的梯形波或正弦波,过零点是否清晰。
[*]减小滤波电容:将C16~C18从223(22nF)换成103(10nF),甚至472(4.7nF),以减小RC延时(13μs→6μs),更容易捕捉高速边沿。
[*]提高强拖最终速度:增大启动结束时的PWM占空比(START_PWM_DUTY),让强拖结束时电机有足够惯性自由旋转几圈。
[*]检查比较器配置:确认 CMPCR1 寄存器设置正确,比较器正负输入端选择无误(正端接分压中点,负端为虚拟中性点或由PWM合成的中点参考)。
希望以上解析能帮你理清思路。如果还有疑问,可以继续提供示波器截图或更详细的代码片段(如比较器初始化部分)。
陈馀庆 发表于 2026-5-10 13:39
梁大师:我用STC8H8K64U芯片复刻大师无刷直流电机,调试过程中,因为不会使用串口输出,就简单地用5个LED ...
我的例子就是使用四轴飞行器的2212电机,你直接使用这个电机参数即可。
#define MOTOR_TYPE 0 //0:2212电机
梁大师:我自己打板的无刷驱动电路中的电阻、电容全部认真测量一遍(用正点原子电桥镊子和三合一万用表),同规格的尽量选择相同值的,特别是分压监测反电动势电路部分,更换焊接,然后继续用您的驱动代码,电机依然是可以强拖且转速比较高,但进不了闭环,我想问题肯定出在反电动势检测部分,是否是电路PCB布线产生较大干扰。所以请大师抽空出一期教学视频,专门讲解PCB电路布线,STC芯片哪些部位抗干扰弱,我指的是专门针对STC芯片来讲,如果用STC芯片控制高频高速信号该怎么布置pcb等。谢谢!
陈馀庆 发表于 2026-5-18 19:00
梁大师:我自己打板的无刷驱动电路中的电阻、电容全部认真测量一遍(用正点原子电桥镊子和三合一万用表), ...
PCB布线原则不是只针对STC的MCU,是所有MCU都要遵守。干扰不会干扰STC本身,而是干扰了信号。建议下面的注意点:
1、供电电源正极、负极在驱动电路的MOSFET部分接入,因为这部分电流最大。
2、其余电路均为弱电(小电流),其地线与上述地线连接于一点,即所谓“星型接地” 或 “一点接地”。反电动势检测的反馈电阻的地一定要尽量接在MCU的地(GND),避免有地线干扰影响。
如果你仍然未能驱动成功,可以联系我,寄一块板和一个电机给我,帮你调。