hbuthejt 发表于 2026-3-26 22:14:25

想请教一个问题,贵公司的FOC 软件,是否有自动适应不同电机的能力,自学习的算法。

梁工 发表于 2026-3-27 09:53:23

hbuthejt 发表于 2026-3-26 22:14
想请教一个问题,贵公司的FOC 软件,是否有自动适应不同电机的能力,自学习的算法。 ...

没有自学习能力。

小虫儿 发表于 2026-4-7 13:52:44

不错 不错

陈馀庆 发表于 2026-5-10 12:34:23

梁工 发表于 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 13:07:58

陈馀庆 发表于 2026-5-10 12:34


比如R21与R22分压,信号衰减系数=1.5/(10+1.5)=0.13,等效输出电阻为R21与R22并联后的1.3K。
其余2个分压电路同理。

陈馀庆 发表于 2026-5-10 13:39:39

陈馀庆 发表于 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 电机强拖时间长一些而已。请大师抽空指点一下,示波器测量波具体测量哪个位置(示波器探头和负极分别夹在哪儿),请结合反电动势分压图指导一下,谢谢!
附件有三个,一个图片、一个视频、一个程序源码。

陈馀庆 发表于 2026-5-10 14:29:43

这段时间都在研究学习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 16:10:29

陈馀庆 发表于 2026-5-10 13:39
梁大师:我用STC8H8K64U芯片复刻大师无刷直流电机,调试过程中,因为不会使用串口输出,就简单地用5个LED ...

我的例子就是使用四轴飞行器的2212电机,你直接使用这个电机参数即可。
#define                MOTOR_TYPE        0        //0:2212电机

陈馀庆 发表于 2026-5-18 19:00:53

梁大师:我自己打板的无刷驱动电路中的电阻、电容全部认真测量一遍(用正点原子电桥镊子和三合一万用表),同规格的尽量选择相同值的,特别是分压监测反电动势电路部分,更换焊接,然后继续用您的驱动代码,电机依然是可以强拖且转速比较高,但进不了闭环,我想问题肯定出在反电动势检测部分,是否是电路PCB布线产生较大干扰。所以请大师抽空出一期教学视频,专门讲解PCB电路布线,STC芯片哪些部位抗干扰弱,我指的是专门针对STC芯片来讲,如果用STC芯片控制高频高速信号该怎么布置pcb等。谢谢!

梁工 发表于 2026-5-18 21:46:05

陈馀庆 发表于 2026-5-18 19:00
梁大师:我自己打板的无刷驱动电路中的电阻、电容全部认真测量一遍(用正点原子电桥镊子和三合一万用表), ...

PCB布线原则不是只针对STC的MCU,是所有MCU都要遵守。干扰不会干扰STC本身,而是干扰了信号。建议下面的注意点:
1、供电电源正极、负极在驱动电路的MOSFET部分接入,因为这部分电流最大。
2、其余电路均为弱电(小电流),其地线与上述地线连接于一点,即所谓“星型接地” 或 “一点接地”。反电动势检测的反馈电阻的地一定要尽量接在MCU的地(GND),避免有地线干扰影响。

如果你仍然未能驱动成功,可以联系我,寄一块板和一个电机给我,帮你调。
页: 5 6 7 8 9 10 11 12 13 14 [15]
查看完整版本: 三相无刷电机驱动-STC8H/STC32G系列-无HALL-PID控制-OLED显示-串口绘图 启动改进版本