littlem 发表于 2015-4-5 22:16:07

用STC15W4K做了个同步整流电调,可以转起来了。

MOS管用6个IRF7832,MOS驱动用的NCP5181,STC15W4K 有6个可控制死区的PWM,实现同步整流比较简单。目前采用ADC在PWM OFF过0检测,当然现在就是学习一下,接下来打算借鉴别人的做法,在PWM大于50%时在PWM ON过0检测,稍后拍个视频和大家分享一下,等弄的差不多了和大家分享代码。


littlem 发表于 2015-4-5 22:32:41

没有启动算法,10%占空比直接启动,目前没发现启动失败,同步整流响应很快,特别是减速,瞬间速度就下来了

foxpro2005 发表于 2015-4-5 23:06:08

用stm32比较容易 方便在PWM ON 50%处检测,stc不知道有没有这样的功能

littlem 发表于 2015-4-6 09:21:28

http://v.youku.com/v_show/id_XOTI3NzQ0NDE2.html
目前只是串口控制

littlem 发表于 2015-4-6 09:48:48

foxpro2005 发表于 2015-4-5 23:06
用stm32比较容易 方便在PWM ON 50%处检测,stc不知道有没有这样的功能

我目前在PWM OFF快结束启动ADC检测,当然转换必须在下一个PWM上升沿来到之前转换完毕,用定时器控制ADC,即在PWM上升中断来到时启动定时器,定时35us后启动ADC(PWM22.8kHz,43us每周期,ADC只占用5us就够了),我想PWM ON检测方法是一样的,但是需要控制好过0检测电阻网路的切换,我就是用了ST 的那个过0检测方法,启动即可拉到闭环(如果25ms内无换向要强制换向),很意外,我想这主要归功于同步整流,不需要经过二极管续流产生压降,过0可以很精确。

foxpro2005 发表于 2015-4-6 10:56:45

嗯,刚看了一下视频效果,还不错,给你点个赞

STCunio 发表于 2015-4-7 13:24:31

本帖最后由 STCunio 于 2015-4-7 13:26 编辑

littlem 发表于 2015-4-6 09:48
我目前在PWM OFF快结束启动ADC检测,当然转换必须在下一个PWM上升沿来到之前转换完毕,用定时器控制ADC, ...

请问用PWM ON 50%实现电阻网络的过零检测的方法?有没有资料?ST 的那个过0检测方法能具体说一下吗?

littlem 发表于 2015-4-7 13:41:05

STCunio 发表于 2015-4-7 13:24
请问用PWM ON 50%实现电阻网络的过零检测的方法?有没有资料?ST 的那个过0检测方法能具体说一下吗? ...

http://wenku.baidu.com/link?url=suZ_aoP15jj_Tg1lTLufefMdcfaAB708uvfUltv-XtvoRqLi-lk0jWxQ4hy3Fuj8Wng_JQ2Gsdr9NKRyGChwkTcC15riFG2gsAnvR7N6-se
我就是看了这个做的,用15W4K做电调 感觉还是可以的,PWM中断也可当定时器用,比如堵转时长检测,甚至PPM信号检测

半导体 发表于 2015-4-7 14:16:02

对楼主的方式很感兴趣,期待分享原理图和源码,谢谢!{:3_59:}

superplaim 发表于 2015-4-28 20:14:15

顶一下 最近就想做电调 对15系列也比较感兴趣 希望楼主早日完工 开源

littlem 发表于 2015-5-5 17:01:12

这段时间工作忙,闲了继续,完善好了一定会和大家分享,谢谢支持!

小李非刀 发表于 2015-5-5 21:03:13

LZ也可以先贴代码,大家来完善,不需要一个人战斗。

littlem 发表于 2015-5-6 10:01:22


//*****************************************说明************************************************
//本程序为STC15W4K系列单片机驱动无感BLDC电调程序,作者:河南有色地质七队 马腾飞
//晶振频率35MHz,驱动MOS的PWM频率位27.45kHz
//采用全NMOS IRF7832(20A/30V)半桥驱动芯片FAN5109,PWM单输入,带输出使能控制
//本电调为采用同步驱动方式,续流不经二极管,启动即可检测到反电动势,进入闭环运行,在占空比小于50%时,使用ADC在PWM OFF进行过零检测
//占空比大于50%时在PWM ON进行过零检测,切换自如不会发生抖动,低速、高速性能都比较好
//在调试时发现同步续流方式利于启动,而且响应极快,特别是减速性能,速度可以瞬间下降(程序中设置占空比变化延时,经实测占空比如果突变
//会MOS半桥驱动一起烧)
//电调输入电压不得超过15V,否则会烧,这是由FAN5109决定的,但其性能比较优越,驱动电流大,死区只有几十ns。
//PPM信号 1.1ms高电平启动,2ms最大油门,油门信号丢失或不合法1s后开始降低输出功率直至到停止,该过程持续2s.
//有过零换向延时,但没有过零检测,因为刚开始设计时没有设计此部分电路,不过可方便添加
//**********************************************************************************************

#include <stc15.h>      //用STC官方头文件
#include <intrins.h>

sbit Phase_A = P2^1;    //PWM模块PWM3
sbit Phase_B = P2^2;    //PWM模块PWM4
sbit Phase_C = P2^3;    //PWM模块PWM5

sbit EN_A_MOS_DRV=P3^7;        //控制A相的半桥驱动使能:1:使能MOS驱动器MOS由PWM控制;0:上下MOS均断开,该相悬浮
sbit EN_B_MOS_DRV=P4^2;        //控制B相的半桥驱动使能:1:使能MOS驱动器MOS由PWM控制;0:上下MOS均断开,该相悬浮
sbit EN_C_MOS_DRV=P4^4;        //控制C相的半桥驱动使能:1:使能MOS驱动器MOS由PWM控制;0:上下MOS均断开,该相悬浮

sbit LED   =P0^4;
sbit INT1=P3^3;        //PPM信号控制

#define uchar unsigned char
#define uintunsigned int

uchar Duty_Current;
uchar Duty_Set;
uchar AD_Sample_Delay;               //ADC采样延时,避开消磁阶段采样干扰
uchar Commutaion_Time_Interval;      //换向时间间隔
uchar PWM_ADC_Sampling;            //1:PWM ON采集反电动势 0:PWM OFF采集反电动势
uchar Step;                        //换向序号
uchar Commutaion_Delay;            //换向延时计数变量
uchar V_Mid;                         //中点电压存放变量
uchar Dly;

uint Base_Timer;               //以PWM7中断为节拍的计数存储变量
uint Elec_Period;            //一个完整电周期计数值,该值×PWM周期(36.43us)即为一个电周期时长
uint Elec_Period_temp;         //中间累加变量
uint PPM_Timeout;            //PPM信号计数器溢出计数

uint PPM_HighLevel;                         //PPM信号高电平计数,值×0.34us=高电平时长
uint PPM_LowLevel;                         //PPM信号低电平计数,值×0.34us=高电平时长

bit You_Can_Start;                   //BLDC启动指令标志,1:启动条件具备需要启动;0:需要停止运行   
bit Already_Start;                   //BLDC状态标志,1:已启动;0:停机状态
bit Falling_Edge;                  //侦测反电动势边沿标志,1:迎接下降沿;0:迎接上升沿
bit Cross_Zero;                      //反电动势过零标志1:检测到过零事件后置1;0:换向完成后才置0
bit Signal_Lost;
bit Signal_Ready;
bit PPM_High_Overflow;
bit PPM_LOW_Overflow;

#define CYCLE                           255               // cycle 8位PWM
#define EN_PHASE_A_PWM_OUTPUT         PWMCR=0x82      // 硬件接PWM3
#define EN_PHASE_B_PWM_OUTPUT         PWMCR=0x84      // 硬件接PWM4
#define EN_PHASE_C_PWM_OUTPUT         PWMCR=0x88      // 硬件接PWM5

#define Startup_Duyt_Cycle            15//启动占空比15/256=6%
#define Location_Duyt_Cycle            4//转子定位占空比
#define Start_Commutaion_Delay          18//启动换向延时(仅用于启动,先填一个数/6=每次换向延时)

#define DISABLE_OUTPUT_ALL            PWMCR=0x00,EN_A_MOS_DRV=0,EN_B_MOS_DRV=0,EN_C_MOS_DRV=0,Phase_A=0,Phase_B=0,Phase_C=0,PWMIF=0 //关闭所有MOS输出清除中断标志

#define ADC_FLAG                        0x10            //ADC转换标志

#define Delay_5_Degrees               Elec_Period/72   //过0延时5° 后换向, 即25°进角
#define Delay_10_Degrees                Elec_Period/36   //过0延时10°后换向, 即20°进角
#define Delay_15_Degrees                Elec_Period/24   //过0延时15°后换向, 即15°进角
#define Delay_20_Degrees                Elec_Period/18   //过0延时20°后换向, 即10°进角
#define Delay_25_Degrees                Elec_Period/14   //过0延时25°后换向, 即5° 进角


void Delay1ms()                //@35MHz
{
unsigned char i, j;

        _nop_();
        i = 34;
        j = 0;
        do
       {
                while (--j);
       } while (--i);
}

void Delay_ms(uint delay)
{ uint i;
for(i=0;i<delay;i++)
   {
       Delay1ms();

   }
}
void IO_Initial()
{
Phase_A=0;
Phase_B=0;
Phase_C=0;

Delay1ms();

P2 |=0x70;//456 输出高电平,由于IO口要设置为开漏模式 ,因此实际是HIGH Z
P2M0 = 0x7e;//PWM 123强上拉,456开漏
P2M1=0x70;
       
P3M0=0x80;
P3M1=0x00;//P3.7推挽,为PWM输出做准备
       
P4M0=0x14;//P4.2 P4.4推挽,为PWM输出做准备
P4M1=0x00;

EN_A_MOS_DRV=1;        //使能所有MOS半桥驱动
EN_B_MOS_DRV=1;
EN_C_MOS_DRV=1;

}

void PWM_Initial()
{
PWMCFG=0x0e;//PWM3/4/5初始电平为高
       
P_SW2|= 0x80;
PWMC=CYCLE;    //设定PWM Cycle
PWMCKS=0x04;   //35M主频,设定PWM频率为27.45k
       
PWM3T2L=255;   
PWM4T2L=255;
PWM5T2L=255;
PWM7T2L=255;//PWM7仅用于提供中断
       
P_SW2&=0x7f;   
}

void PWM_Duty_Cycle_Control(uchar PWM_Value)
{ P_SW2=0x80;
   
PWM3T1L=PWM_Value; //第一次反转
PWM4T1L=PWM_Value; //第一次反转
PWM5T1L=PWM_Value; //第一次反转
          
P_SW2=0x00;   
}

void ADC_Init()
{
P1ASF = 0X3c; // 开通P1.2-P1.5 AD输入口
ADC_RES=0;
ADC_CONTR =0xE0;//打开ADC电源,设置90个时钟转换一次
}
            
void Commutation()
{
if(Step<5)
          Step++;
else
          Step=0;
switch(Step)
    {
               case 0: //AB
                             EN_C_MOS_DRV=0;
                         Phase_C=1;      //断开C相
               
                         _nop_();
                         _nop_();
                             
                         EN_PHASE_A_PWM_OUTPUT;
                               Phase_B=0;//打开B低端
                        
                         EN_A_MOS_DRV=1;            
                         EN_B_MOS_DRV=1;
                                                      
                               ADC_CONTR = 0XE2;        // 选择P1.2作为ADC输入即C相电压,未开始转换
                         Falling_Edge=1;
                         break;
               
               case 1: //AC
                             EN_B_MOS_DRV=0;
                         Phase_B=1;      //断开B相
               
                         _nop_();
                         _nop_();
                             
                         EN_PHASE_A_PWM_OUTPUT;
                         Phase_C=0;//打开C低端
                                               
                         EN_A_MOS_DRV=1;            
                         EN_C_MOS_DRV=1;
                                                                                
                               ADC_CONTR = 0XE3;        // 选择P1.3作为ADC输入即C相电压,未开始转换
                         Falling_Edge=0;
                               break;
               
               case 2: //BC
                             EN_A_MOS_DRV=0;
                         Phase_A=1;      //断开A相
                             
                         _nop_();
                         _nop_();
                        
                         EN_PHASE_B_PWM_OUTPUT;
                         Phase_C=0;
                        
                               EN_B_MOS_DRV=1;
                         EN_C_MOS_DRV=1;
               
                         ADC_CONTR = 0XE4;        // 选择P1.4作为ADC输入即C相电压,未开始转换
                             Falling_Edge=1;
                               break;
               
               case 3: //BA
                             EN_C_MOS_DRV=0;
                         Phase_C=1;
               
                         _nop_();
                         _nop_();
                             
                         EN_PHASE_B_PWM_OUTPUT;
                         Phase_A=0;//打开A低端
                        
                               EN_A_MOS_DRV=1;            
                         EN_B_MOS_DRV=1;
                                                                               
                               ADC_CONTR = 0XE2;        // 选择P1.2作为ADC输入即C相电压,未开始转换
                               Falling_Edge=0;
                               break;
                                               
               case 4: //CA
                             EN_B_MOS_DRV=0;
                         Phase_B=1;      //断开B相
                             
                         _nop_();
                         _nop_();
                        
                         EN_PHASE_C_PWM_OUTPUT;
                         Phase_A=0;//打开A低端
                                               
                         EN_C_MOS_DRV=1;            
                         EN_A_MOS_DRV=1;
                                               
                               ADC_CONTR = 0XE3;        // 选择P1.3作为ADC输入即C相电压,未开始转换
                         Falling_Edge=1;
                               break;
               
               case 5: //CB
                             EN_A_MOS_DRV=0;
                         Phase_A=1;      //断开A相
               
                         _nop_();
                         _nop_();
               
                               EN_PHASE_C_PWM_OUTPUT;
                         Phase_B=0;//打开A低端
                        
                         EN_C_MOS_DRV=1;            
                         EN_B_MOS_DRV=1;
                                                         
                               ADC_CONTR = 0XE4;        // 选择P1.4作为ADC输入即C相电压,未开始转换
                               Falling_Edge=0;
                               break;
               
                default:break;
   }
AD_Sample_Delay=0;
Base_Timer=0;
}


void Locate_Rotator()//定位函数第一步C+ A+ B- 第二步C+ A-
{
DISABLE_OUTPUT_ALL;//首先关闭所有MOS输出和PWM中断
       
PWMCR=0x8A;//C+ A+
Phase_B=0; //B-
_nop_();
_nop_();       
EN_A_MOS_DRV=1;            
EN_B_MOS_DRV=1;
EN_C_MOS_DRV=1;             

Delay_ms(60);
       
EN_B_MOS_DRV=0;
EN_PHASE_C_PWM_OUTPUT;//C+
_nop_();
_nop_();
Phase_A=0;         //A-
       
Delay_ms(50);
               
_nop_();
_nop_();
}
uchar ADC()                   //ADC,采样可重入函数
{ ADC_CONTR|=0x08;
_nop_();                        
_nop_();
_nop_();
_nop_();
while (!(ADC_CONTR & ADC_FLAG));//等待ADC转换完成
ADC_CONTR&=0xE7;
return ADC_RES;
}
void Zero_Crossing_Detection_PWM_OFF()
{ADC();
   if(Falling_Edge==1&&ADC_RES<0x01)
   {   LED=~LED;   
               Elec_Period_temp+=Commutaion_Time_Interval;
               Commutaion_Time_Interval=0;//每次过0都清0,记录每次过0时间间隔       
               Cross_Zero=1;
               if(Step==5)//每个换向间隔相加得到整个电周期时间,电周期除以一定数值可以得到延时角度,实际是对时间滤波,这样稳定性好
                   { Elec_Period=Elec_Period_temp;
                       Elec_Period_temp=0;
                   }       
               }
   if(Falling_Edge==0&&ADC_RES>0x01)
       {
                Elec_Period_temp+=Commutaion_Time_Interval;
                Commutaion_Time_Interval=0;
                Cross_Zero=1;
                if(Step==5)//每个换向间隔相加得到整个电周期时间,电周期除以一定数值可以得到延时角度,实际是对时间滤波,这样稳定性好
              { Elec_Period=Elec_Period_temp;
                  Elec_Period_temp=0;
              }       
          LED=~LED;
       }
}
void Zero_Crossing_Detection_PWM_ON()
{ADC();
   if(Falling_Edge==1&&ADC_RES<V_Mid)
       {LED=~LED;   
                Elec_Period_temp+=Commutaion_Time_Interval;
                Commutaion_Time_Interval=0;       
      Cross_Zero=1;
                if(Step==5)//每个换向间隔相加得到整个电周期时间,电周期除以一定数值可以得到延时角度,实际是对时间滤波,这样稳定性好
                  { Elec_Period=Elec_Period_temp;
                        Elec_Period_temp=0;
                  }       
       }
   if(Falling_Edge==0&&ADC_RES>V_Mid)
       {
                Elec_Period_temp+=Commutaion_Time_Interval;
                Commutaion_Time_Interval=0;
                Cross_Zero=1;
                if(Step==5)//每个换向间隔相加得到整个电周期时间,电周期除以一定数值可以得到延时角度,实际是对时间滤波,这样稳定性好
                  { Elec_Period=Elec_Period_temp;
                        Elec_Period_temp=0;
                  }       
                LED=~LED;
       }
}

void T1_Init()               
{
   AUXR &= 0xBF;       //12T
   TMOD &= 0x0F;       
               
   TL1 = 0;                //2.1ms
   TH1 = 0;                //
       
   TF1 = 0;//中断标志清0               
   ET1=1;//开T1中断
}

void INT1_Init()
{
   IE1 = 0;        // 清除外中断1标志位
   EX1 = 1;        // INT1 Enable
   IT1 = 0;        //1:下降沿中断;0:上升沿下降沿均中断
}

void Startup_Motor()
{
   Duty_Current=Duty_Set=Startup_Duyt_Cycle;
       
   PWM_Duty_Cycle_Control(Duty_Current);
   PWM_ADC_Sampling=0;
   Elec_Period_temp=0;

   DISABLE_OUTPUT_ALL; //启动前关闭MOS输出清除中断标志,完成启动电桥初始化

   Step=5;//进行入换向函数会执行Step=1的接通顺序,这是根据转子定位函数确定的启动顺序不能更改
   Base_Timer=0;//已PWM中断时间间隔及次数为基准的变量,起到定时或延时作用
   Elec_Period=Start_Commutaion_Delay; //初始启动换向延时,提高启动一次成功率

   P_SW2=0x80;
   PWM7CR=0x05;//开启PWM7T1反转匹配中断
   PWM7T1L=Duty_Current/2+80;
   P_SW2=0x00;   

   Commutation();//启动和PWM中断中都调用此函数,虽然为不可重入函数,但只在启动时调用一次,运行时全部由中断调用,应该不会发生不可预知错误
   Already_Start=1;//已启动标志,如果启动失败,由于有强制换向环节最终马达会正常运行
}
void Stop_Motor()
{
   while(1)//占空比逐渐减少至0,实现迅速减速,占空比瞬间大幅变化,MOS和半桥驱动都会挂
                       {Duty_Current--;
                                PWM_Duty_Cycle_Control(Duty_Current);      
                                Delay_ms(1);
                                if(Duty_Current==1)
                                   break;
                       }
   P_SW2=0x80;
   PWM7CR=0x00;//关闭PWM7中断
   P_SW2=0x00;                                                                
                                              
   Duty_Set=Duty_Current=Location_Duyt_Cycle;//恢复到转子定位PWM duty cycle
   PWM_Duty_Cycle_Control(Duty_Current);                                                                                       
   PWM_ADC_Sampling=0;
   Locate_Rotator();               
   Elec_Period_temp=0;
   Already_Start=0;//已经停止

}
void Signal_Check()// 信号异常检测函数
{
   if(PPM_HighLevel<2625||PPM_HighLevel>6125||PPM_LowLevel>58333)//H0.9ms/H2.1ms/L20ms
       {   
                PPM_Timeout++;//如果进入溢出了,一次溢出约22ms
          if(PPM_Timeout==990)//约1S后
              {
             Signal_Lost=1;
                       Dly=2000/Duty_Set;
          }
       }
if(PPM_HighLevel>=2625&&PPM_HighLevel<=6125&&PPM_LowLevel<=58333)//高电平时长小于2.1ms低电平时长小于20ms认为信号恢复正常
       {
                PPM_Timeout=0;
                Signal_Lost=0;
   }
}

void Main()
{
        IO_Initial();
        PWM_Initial();
        ADC_Init(); // 初始化ADC
       
        T1_Init();
       
        Duty_Set=Duty_Current=Location_Duyt_Cycle;
        PWM_Duty_Cycle_Control(Duty_Current);
        Locate_Rotator();
       
        IP2=0x04; //PWM为优先级1
        EA =1;
       
        PPM_HighLevel=3209;
        Signal_Ready=0;
        Delay_ms(500);
        INT1_Init();
       
        while(1)
       { while(1) //信号检测
                {
                        if(PPM_HighLevel<2625||PPM_HighLevel>3208||PPM_LowLevel>58333)
                          {
                               LED=~LED;//LED以0.1S频率闪烁
                               Delay_ms(100);
                          }
                        if(PPM_HighLevel>=2625&&PPM_HighLevel<=3208&&PPM_LowLevel<=58333)//高电平时长小于2.1ms低电平时长小于20ms认为信号恢复正常
                          {                                                              
                               LED=~LED;//LED以0.4S频率闪烁
                               Delay_ms(400);
                               Signal_Ready=1;
                                                                       
                           }
                        if(PPM_HighLevel>3208&&Signal_Ready==1)break;                                                               
                }

          while(1)//主循环
           {
                       Delay_ms(1);
                       if(Duty_Set<Duty_Current)Duty_Current--;
                       if(Duty_Set>Duty_Current)Duty_Current++;
                       if(Duty_Set==Duty_Current)_nop_();
                               
                       PWM_Duty_Cycle_Control(Duty_Current);
                       Signal_Check();
                       if(Signal_Lost)//信号丢失,
                           {
                                  Delay_ms(Dly);
                                  Duty_Set--;
                                  if(Duty_Set<3)
                                        {
                                               Stop_Motor();//执行停机程序
                                               LED=1;
                                               PPM_HighLevel=3209;
                                               Signal_Ready=0;
                                               PPM_Timeout=0;
                                               Signal_Lost=0;
                                               break;
                                        }       
                           }
                       if(!Signal_Lost&&!PPM_Timeout)//如果信号未丢失未超时
                           {
                                  if(PPM_HighLevel>3208)//1.100-2.100ms
                                        {
                                                You_Can_Start=1; //可以启动了
                                                if(Already_Start)
                                                  {Duty_Set=(PPM_HighLevel-3208)/12;
                                                       if(Duty_Set>=252)
                                                                Duty_Set=252;//限制最大占空比不得超过252/256=98.4%,其实最大也无所谓,只是为了安全起见       
                                                       if(Duty_Set<=5)       
                                                                Duty_Set=5;        //限制最小占空比               
                                                       
                                                  }
                                        }
                                  else
                                  {
                                                You_Can_Start=0;//要停止了       
                                        }
                                                       
                             if(You_Can_Start==1&&Already_Start==0){Startup_Motor();LED=0;}
                             if(You_Can_Start==0&&Already_Start==1){Stop_Motor();LED=1;}
                          }                                
               }
   }
}

void INT1_ISR() interrupt 2 using 2       
{ TR1 = 0;
if(INT1)//上升沿记录低电平计数
        {PPM_High_Overflow=0;
           if(!PPM_LOW_Overflow)PPM_LowLevel = ((uint)TH1 << 8) + TL1;//上升沿来临如果之前无溢出才记录
           else PPM_LowLevel=65535;//溢出则赋最大值
           TL1 = 0;               
           TH1 = 0;               
       TR1 = 1;//T1在INT1高电平时可开始计数
                       
   }
   else//下降沿记录高电平计数
        {PPM_LOW_Overflow=0;
           if(!PPM_High_Overflow)PPM_HighLevel = ((uint)TH1 << 8) + TL1;//下降沿来临如果之前无溢出才记录
           else PPM_HighLevel=65535; //溢出则赋最大值
           TL1 = 0;               
           TH1 = 0;               
       TR1 = 1;//T1在INT1高电平时可开始计数
    }
}

void T1_ISR() interrupt 3 using 2//低电平或高电平时间溢出都进入中断
{
        if(!INT1)
          {PPM_LOW_Overflow=1;
             PPM_LowLevel=65535;
      }
        if(INT1)
          {PPM_High_Overflow=1;
             PPM_HighLevel=65535;
      }       
}

void PWM_ISR() interrupt 22 using 1 //在中断中完成ADC过零检测和沿时换向
{
Base_Timer++;
Commutaion_Time_Interval++;
AD_Sample_Delay++;
       
if(Cross_Zero)
          {AD_Sample_Delay=0;//过0检测到换向完成前值都为0,期间不进行过0检测
               Commutaion_Delay++;
               if(Commutaion_Delay==Delay_20_Degrees)
                   {   
                       Commutaion_Delay=0;
                       Commutation();
                       Cross_Zero=0;
                   }
          }                       
       
if(AD_Sample_Delay>=2)//换向后PWM第二次中断时开始过0采样,避免换向瞬间消磁干扰
       {                       
               switch(PWM_ADC_Sampling)
               {
                             case 0: Zero_Crossing_Detection_PWM_OFF();//在PWM OFF过零采样
                                             break;
                             case 1: Zero_Crossing_Detection_PWM_ON(); //在PWM ON 过零采样
                                             break;
                             default:break;
                           }
   }       
       
if(Step==2&&!Commutaion_Delay)//每个完整换向周期 只执行一次当STEP=2时,
        {if(Duty_Current<0x80)//占空比小于0.5再PWM OFF采样
             {P2 |=0x70;        //456 输出高电平 由于开漏模式无法输出高电平实际为Hi-Z,相当于断开
                        PWM_ADC_Sampling=0;
                        P_SW2=0x80;
                  PWM7T1L=Duty_Current/2+80; //第一次翻转
                        P_SW2=0x00;
               }
       else //占空比大于0.5改为PWM ON 采样
                 {        P2 &=0x8f;         //456 输出低电平 三相分压电阻接地 初始化ADC分压采样
                        PWM_ADC_Sampling=1;
                        P_SW2=0x80;
                  PWM7T1L=Duty_Current/4; //第一次翻转
                        P_SW2=0x00;
                        ADC_CONTR = 0XE5;//切换到中点采样通道
                        V_Mid= ADC()/2;    //采集中点电压,需要除以2
                        ADC_CONTR = 0XE4;//采样完毕后切换到原通道
         }
        }
       
if(Base_Timer>=1000)//约36ms,无换向堵转了,强制换相
        {
           Elec_Period=Start_Commutaion_Delay;
           Commutaion_Delay=0;
           Duty_Set=Duty_Current=Startup_Duyt_Cycle; //以启动低占空比换相
           PWM_Duty_Cycle_Control(Duty_Current);
           Commutation();
           Cross_Zero=0;
                       
    }
PWMIF=0;//清除PWM中断标志,
       
}

littlem 发表于 2015-5-6 10:10:38

在keil里是对齐的,不知贴上来怎么乱了,原理图晚上回家发吧。本人不是电子专业,也不从事电子专业,只因爱好业余时间学习一下,所以程序编写肯定有很多不规范地方和不好的习惯,还请大家指教。目前来看性能是可以的也比较稳定,可以小于10%的较低占空比带负载启动(用手捏住也能稳定启动),运行时响应极快,可瞬间加速或减速;PPM油门信号由一片STC404as产生,没有试验过通用航模遥控器信号,准备买个遥控器和接收机试验一下,那个信号丢失保护功能,可能不符合实际情况,只求模仿,大家可以完善。

littlem 发表于 2015-5-6 10:15:46

说明一下,原来MOS驱动用的是 NCP5181 说实话性能不错,但不知为什么 单品机中互补PWM明明设置了死区(是100%肯定足够的死区),可某些情况还是会导致上下MOS直通,郁闷了,无奈换了单PWM输入的 FAN5109 这个死区是固定的 只需要输入一路PWM即可,带输出使能可用于同步续流也就是其中一相是可以悬浮的。

littlem 发表于 2015-5-6 10:20:57

再次强调 同步续流有很大优势,这是我没想到的,根部不需要启动算法,这是我试验的结果。以前做的数控电源也是用同步整流BUCK,效率奇高。

littlem 发表于 2015-5-6 10:28:41

PPM 信号处理没有用PCA捕获, 而是用了 外部中断 加定时器 ,感觉系统占用时间反而少,如果用PCA需要操作 long型数数据,中断占用时间可能会更多(绝对不能影响过0采样和换向的及时性),只是猜测还没有验证。

小李非刀 发表于 2015-5-6 15:00:10

用PCA捕捉 和 外中断+定时器 来读PPM信号一样的,都是unsigned int变量,并且一般几ms~10ms中断一次,占CPU时间基本可以忽略了。

小李非刀 发表于 2015-5-6 15:01:56

LZ贴了程序,这个给100个赞先!
我会帮忙测试,众人拾柴吧。。。

littlem 发表于 2015-5-6 15:14:44

谢谢! 这就是开源的乐趣,一起努力吧

littlem 发表于 2015-5-6 20:45:37

原理图这是,没有过流检测暂时

littlem 发表于 2015-5-6 20:46:17

zhonghua_li 发表于 2015-11-27 11:16:07

速度大于1w转,崩溃。
采用AD同步采样,过零点分辨率低(最小时间为1个PWM周期)。
速度过快时,换向时间就差不多1个PWM周期。

yanzhiwei 发表于 2015-12-1 15:34:12

也想搞电机控制方面的,收藏了,谢谢

littlem 发表于 2016-2-25 21:01:30

zhonghua_li 发表于 2015-11-27 11:16
速度大于1w转,崩溃。
采用AD同步采样,过零点分辨率低(最小时间为1个PWM周期)。
速度过快时,换向时间就 ...

确实是,速度不可能太快,太快用示波器看,过零很不稳,波形抖动。不过带载后还是很稳定的。

ymyhd 发表于 2016-2-29 11:11:04

真很好 收藏了。。以后能看看。。。

makeflyeasy 发表于 2016-6-8 10:43:13

foxpro2005 发表于 2015-4-5 23:06
用stm32比较容易 方便在PWM ON 50%处检测,stc不知道有没有这样的功能

大哥,我刚接触电调不久,想自己做个电调,也看了ST的那个PWM采样,就是要同步PWM采样的话我用定时器的比较输出中断,在电平翻转的时候产生中断采样,就是ST 的比较输出那里调的不好,不知道大神有没有关于定时器比较输出的相关资料或代码,小弟在此感激不尽

foxpro2005 发表于 2016-6-8 15:31:24

本帖最后由 foxpro2005 于 2016-6-8 15:32 编辑

makeflyeasy 发表于 2016-6-8 10:43
大哥,我刚接触电调不久,想自己做个电调,也看了ST的那个PWM采样,就是要同步PWM采样的话我用定时器的比 ...

下面是定时器TIM1或TIM8的配置代码:
<code>
/**
* @briefTIMx(高级定时器TIM1或TIM8)模块初始化
* @paramhwPulse - 占空比
* @retval None
*/
static void TIMER_Config(uint16_t hwPulse)
{
    TIM_TimeBaseInitTypeDefTIM_TimeBaseStructure;
    TIM_OCInitTypeDefTIM_OCInitStructure;
    TIM_BDTRInitTypeDef TIM_BDTRInitStructure;

    /* TIMx时基单元配置 ------------------------------------------------------*/
    TIM_TimeBaseStructure.TIM_Prescaler = BLDCM_TIM_PSC - 1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseStructure.TIM_Period = BLDCM_PWM_PERIOD - 1;
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;

    TIM_TimeBaseInit(BLDCM_TIMER, &TIM_TimeBaseStructure);

    /* TIMx比较输出单元配置 --------------------------------------------------*/
    /* 刚开始时互补通道先关闭, 这样在没有产生换相之前互补通道就不会有高电平输出 */
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;               // 正向参考波形
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Disable;// TIM_OutputState_Enable
    TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable;// TIM_OutputNState_Enable
    TIM_OCInitStructure.TIM_Pulse = hwPulse;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
    TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;      // TIM_OCIdleState_Reset
    TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;    // TIM_OCNIdleState_Reset

//TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;               // 反向参考波形
//TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Disable;// TIM_OutputState_Enable
//TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable;// TIM_OutputNState_Enable
//TIM_OCInitStructure.TIM_Pulse = hwPulse;
//TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
//TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low;
//TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
//TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Set;

    TIM_OC1Init(BLDCM_TIMER, &TIM_OCInitStructure);
    TIM_OC1PreloadConfig(BLDCM_TIMER, TIM_OCPreload_Enable);      // TIMx->CCR1预装使能

    TIM_OC2Init(BLDCM_TIMER, &TIM_OCInitStructure);
    TIM_OC2PreloadConfig(BLDCM_TIMER, TIM_OCPreload_Enable);      // TIMx->CCR2预装使能

    TIM_OC3Init(BLDCM_TIMER, &TIM_OCInitStructure);
    TIM_OC3PreloadConfig(BLDCM_TIMER, TIM_OCPreload_Enable);      // TIMx->CCR3预装使能

    /* TIMx->CC4通道用于触发ADC1的注入通道组转换 */
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;               // 反向参考波形
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = hwPulse >> 1;                   // 在PWM脉冲宽度的50%处触发
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OC4Init(BLDCM_TIMER, &TIM_OCInitStructure);
    TIM_OC4PreloadConfig(BLDCM_TIMER, TIM_OCPreload_Enable);      // TIMx->CCR4预装使能

    /* TIMx比较输出控制单元配置 ----------------------------------------------*/
    TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Disable;            // OSSR 当定时器不工作时, 输出0
    TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Disable;            // OSSI 当定时器不工作时, 输出0
    TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_OFF;
    TIM_BDTRInitStructure.TIM_DeadTime = BLDCM_PWM_DEAD_TIME;
    TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable;
    TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_Low;      // 低电平有效
    TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable; // TIM_AutomaticOutput_Disable;

    TIM_BDTRConfig(BLDCM_TIMER, &TIM_BDTRInitStructure);

    /* TIMx模块控制 ----------------------------------------------------------*/
    // 使用COM事件同步更新寄存器,需要CCxE,CCxNE,OCxM预装载
    // 开启捕获/比较使能寄存器TIMx->CCERx的CCxE,CCxNE的位预装功能
    // 开启捕获/比较模式寄存器TIMx->CCMRx的OCxM,的位预装功能
    TIM_CCPreloadControl(BLDCM_TIMER, ENABLE);

    /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
    // 本项目中使用了TIM2的OC1比较匹配来产生TRGI信号来触发COM事件换相更新

    // 捕获/比较控制更新选择, TIMx->CR2.CCUS配置
    TIM_SelectCOM(BLDCM_TIMER, ENABLE);               // 更新选择: COM位或TRGI的上升沿

    // 触发源选择, 为了避免发生错误, TIMx->SMCR.TS的配置, 要在TIMx->SMCR.SMS=000的时候配置
    TIM_SelectInputTrigger(BLDCM_TIMER, TIM_TS_ITR1);   // 选择内部触发源TIM2连接到TIM1, 见STM32参考手册P237
    /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */

    //TIM_SelectSlaveMode(BLDCM_TIMER, TIM_SlaveMode_Reset);
   
    /* 在启动计数前, 产生一次更新事件以初始化有预装载的寄存器 */
    //TIM_GenerateEvent(BLDCM_TIMER, TIM_EventSource_Update);

    /* TIMx中断配置   */
    TIM_ClearITPendingBit(BLDCM_TIMER, TIM_IT_Update |
                        TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4 |
                        TIM_IT_COM | TIM_IT_Trigger | TIM_IT_Break);
    //TIM_ITConfig(BLDCM_TIMER, TIM_IT_COM, ENABLE);    // TIM_IT_Break, 改由需要的时候再来开启

    /* TIMx计数使能   */
    TIM_Cmd(BLDCM_TIMER, ENABLE);

    /* TIMx主输出使能 */
    TIM_CtrlPWMOutputs(BLDCM_TIMER, ENABLE);
}
</code>

makeflyeasy 发表于 2016-6-8 21:27:10

foxpro2005 发表于 2016-6-8 15:31
下面是定时器TIM1或TIM8的配置代码:

/**


谢谢大哥,真是及时雨啊,感激不尽

hdl 发表于 2016-6-9 08:08:44

littlem 发表于 2015-5-5 17:01
这段时间工作忙,闲了继续,完善好了一定会和大家分享,谢谢支持!

顶,楼主威武

sctwp 发表于 2017-12-12 16:57:21

仔细看了你的程序没有看到同步整流在那里实现,难到是通过FAN5109实现的吗?

sctwp 发表于 2017-12-12 21:32:11


仔细看了你的程序没有看到同步整流在那里实现,难到是通过FAN5109实现的吗?

littlem 发表于 2017-12-19 21:10:56

FAN5109是单输入电桥驱动芯片,上下管互补驱动,同时具有使能功能,同步整流正是通过它实现

lcmdw 发表于 2017-12-20 09:39:15

学习      

饭桶 发表于 2017-12-20 09:47:14

好玩               

retome 发表于 2018-3-26 15:41:35

littlem 发表于 2017-12-19 21:10
FAN5109是单输入电桥驱动芯片,上下管互补驱动,同时具有使能功能,同步整流正是通过它实现 ...

非电子专业做得比电子专业还厉害!佩服

bg4mna 发表于 2018-3-27 12:49:54

厉害,兴趣爱好是巨大动力。

No.5 发表于 2018-3-27 18:15:20

牛逼,不得不顶

dory_m 发表于 2018-3-28 08:20:06

学习,谢谢!!!

fuquan_dai 发表于 2020-7-19 13:28:16

谢谢分享!好好补补无刷电调~

fuquan_dai 发表于 2020-7-21 10:38:26

参考lz的程序终于成功了,非常感谢!
页: [1]
查看完整版本: 用STC15W4K做了个同步整流电调,可以转起来了。