OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 2925|回复: 4

基于F103的BLDC无感电调设计疑问

[复制链接]

14

主题

65

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
240
金钱
240
注册时间
2016-8-4
在线时间
71 小时
发表于 2018-1-4 16:09:50 | 显示全部楼层 |阅读模式
30金钱
本帖最后由 代号_47k 于 2018-1-4 16:50 编辑

最近在做电调,在拜读了timegate 墨鸢 的 无感无刷直流电机之电调设计全攻略 后,想用STM32F103C8T6做一个无感电调,但是程序上没有什么思路,刚开始学习,纯小白,很混乱,论坛里有大神做个吗,求讲解下程序流程,用F103做的话,各位大神有什么资料吗,或者大神做过给分享下程序借鉴借鉴,网上大部分都是有F103做的有感电调。
顺便再附上自己在网上找的一些资料,大家共同学习。

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

9

主题

507

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3347
金钱
3347
注册时间
2013-4-10
在线时间
333 小时
发表于 2018-1-4 19:14:08 | 显示全部楼层
本帖最后由 likunxue 于 2018-1-4 19:15 编辑

我给你一段起动代码,你可以自已完成其它的工作

/********************************************************************************************
函 数 名: 换相
调    用: YouGan_ComHandler(void)
参    数: 无
返 回 值: 无  
********************************************************************************************/         
void ComHandler(u8 Svev)
     {      
     TIM1->CCER &= 0xfffff000;  
     TIM1->CCER |= MoB.CCER[Svev];          //写入PWM控制寄存器的输出状态   
     BLCD_GENGXINSHINENG;                   //产生更新事件COM,充许更新CCxE,CCxNE,CCxM   
     MoB.Signal = MoB.JianCeYouXiaoDianPing[Svev];//选择反电动势信号,检测电平
     MoB.CommutPin = MoB.JIANCEXIAN[Svev];  //选择反电动势引脚
     if(MoB.Signal != 0) BIT_ADM(TIM4->CCER,1) = 0; //CC1P 不反相输入上升沿捕获
     else BIT_ADM(TIM4->CCER,1) = 1;            //下降沿捕获
     }
/******************************************************************************************
程序名称: 过零信号正确判断
调    用: GuoLingXinHaoPanDuan(u8 atx)
入口参数: 无      
返 回 值: 0正确1 出错      
*******************************************************************************************/  
char GuoLingXinHaoPanDuan(void)
     {
     u8 BianMa,Bia,Cnt,BeforeCnt;              
     BianMa = ((GPIOB->IDR >> 6)&0x07); //得到反电动势代表的电机转子位置信息   
     BeforeCnt = 0;         
     for(Cnt = 10; Cnt > 0; Cnt --)
        {             
        Bia = ((GPIOB->IDR >> 6)&0x07); //读取状态
        if(Bia != BianMa){BianMa = Bia;BeforeCnt /= 2;}     //过零不稳定                  
        else{          
            BeforeCnt++;
            if(BeforeCnt > 5)
              {
              if(BianMa == 5){MoB.OutBianMa = 0;return 5;}  //正确编码5      
              if(BianMa == 4)
                {
                if(MoU.DIR ==0)MoB.OutBianMa = 1;else MoB.OutBianMa = 5;               
                return 4;  //正确编码4
                }                       
              if(BianMa == 6)
                {
                if(MoU.DIR ==0)MoB.OutBianMa = 2;else MoB.OutBianMa = 4;       
                return 6;        //正确编码6
                }                       
              if(BianMa == 2)
                {
                if(MoU.DIR ==0)MoB.OutBianMa = 3;else MoB.OutBianMa = 3;       
                return 2;  //正确编码2
                }
              if(BianMa == 3)
                {
                if(MoU.DIR ==0)MoB.OutBianMa = 4;else MoB.OutBianMa = 2;       
                return 3;  //正确编码3
                }
              if(BianMa == 1)
                {
                if(MoU.DIR ==0)MoB.OutBianMa = 5;else MoB.OutBianMa = 1;       
                return 1;  //正确编码1
                }                       
              return 0;  //编码错   
              }   
            }          
        }
     return 0;        //检测错
     }
/*******************************************************************************************
函 数 名: 定时器4中断函数(无感驱动捕获方式获取过零信号)
调    用: 无
参    数: 无
反 回 值: 无      
*******************************************************************************************/         
void TIM4_IRQHandler(void)
     {              
     if(BIT_ADM(TIM4->SR,1) == 1)               //CC1捕获中断标记   
       {                
       u16 Cnt = TIM4->CCR1;                    //读取捕获值,其中包含速度信息   
       BIT_ADM(TIM4->SR,1) = 0;                 //清中断标志                    
       if(TIM4->CNT > MoB.Commut10)   
         {
         u8 BianMa = GuoLingXinHaoPanDuan();    //读取反电动势的编码信息  
         if(MoU.FsmStat == STATFORWARDRUN)
           {
           if((BianMa == 0)&&(TIM4->CNT >MoB.Period4))
             { //编码出错了
             BLCD_SHIJIAN_OFF;                  //关闭电机        
             MoU.FsmStat = STATSTALL;           //电机诸转了                       
             }
           else{
               if(BianMa != MoB.BianMa)
                 { //计算换相时间         
                 MoB.Period4 -= MoB.Period4/2;  //240度
                 MoB.Period4 += Cnt * 4;        //240度
                 MoB.Period   = MoB.Period4/4;  //60度的时间
                 MoB.Commut20 = MoB.Period/3;   //20度的时间
                 MoB.Commut15 = MoB.Period/4;   //15度的时间  
                 MoB.Commut10 = MoB.Period/12;  //10度的时间      
                 Cnt =  MoB.Period - MoB.Commut20;   
                 TIM4->CCR2 = Cnt;              //更新换相时间  
                 QFilter_lst(&Speed_LvBo,Cnt);  //步进角度一阶滤波函数
                 MoB.BeforeFlag = 0xe5;          
                 }  
               MoB.BianMa = BianMa;             //当前编码值存副本      
               }                                  
           }
         else{if(BianMa != 0)MoB.BianMa = BianMa;}//当前编码值存副本  
         }
       }
     if(BIT_ADM(TIM4->SR,2) == 1)               //CC2IF(比较溢出中断)
       { //触发换相   
       BIT_ADM(TIM4->SR,2) = 0;  
       if((MoU.FsmStat == STATFORWARDRUN)&&(MoB.BeforeFlag == 0xe5))
         {
         MoB.OvFlag = 0;                        //清定时器溢出标志位
         TIM4->CNT = 0;                         //清零计数器的值      
         TIM4->CCR2 = MoB.Period4;              //下次换相时间设为60度
         TIM1->CCER &= 0xfffff000;  
         TIM1->CCER |= MoB.CCER[MoB.OutBianMa]; //写入PWM控制寄存器的输出状态   
         BLCD_GENGXINSHINENG;                   //产生更新事件COM,充许更新CCxE,CCxNE,CCxM   
         MoB.Signal = MoB.JianCeYouXiaoDianPing[MoB.OutBianMa];//选择反电动势信号,检测电平
         MoB.CommutPin = MoB.JIANCEXIAN[MoB.OutBianMa];  //选择反电动势引脚
         if(MoB.Signal != 0) BIT_ADM(TIM4->CCER,1) = 0; //CC1P 不反相输入上升沿捕获
         else BIT_ADM(TIM4->CCER,1) = 1;        //下降沿捕获
         MoB.count++;                           //计数器累加
         MoB.BuJuSum += Speed_LvBo.Old_Dat;     //时步角度累加           
         if(MoB.count >= 6)                     //2ms 事件(50*40 = 2000us)
           {                   
           MoU.ActualSpeed = SPEEDMULT/(MoB.BuJuSum /6); //计算当前电机速度
           PI_MACRO(MoU.ActualSpeed);                  
           MoB.BuJuSum = 0;                     //清零步距角累加器
           MoB.count = 0;                        //清零步进角度累加计数器
           }
         MoB.BeforeFlag = 0;                    //清标志         
         }
       MoB.ucCommutOkCn = 0;                    //清零编码出错统计次数  
       }
     if(BIT_ADM(TIM4->SR,4) == 1)               //CC4IF(比较溢出中断)
       {     
       BIT_ADM(TIM4->SR,4) = 0;  
       MoB.OvFlag = 1;                          //溢出标志累加
       if(MoU.FsmStat == STATFORWARDRUN)
         {
         BLCD_SHIJIAN_OFF;                      //关闭电机        
         MoU.FsmStat = STATSTALL;               //电机诸转了                
         }             
       }  
     }      
/******************************************************************************************
程序名称: 电机启动
调    用: BLDC_Start(void)
入口参数: 无      
返 回 值: 无   BLCD_SHIJIAN_ON;                       //开启电机
*******************************************************************************************/      
void BLDC_Start(void)
     {  
     u8 BeforeCnt,ErrCnt,BeforeFlag,AfterFlag;                     
     u16 ucCommutOkCnt  = 0;   
     u16 BulTim;
     BLCD_SHIJIAN_OFF;                            //关闭电机  
     LOOG_DISUMOSHI;                              //反电动势检测置低起动模式         
     delay_ms(100);                               //延时           
     PID_int();                                   //PID参数初始化      
     MoU.ActualSpeed = 0;                         //清零当前电机工作速度   
     MoB.OvFlag = 0;                              //置时器溢出标志位
     MoB.count = 0;                               //速度初始化指针  
     MoB.BuJuSum = 0;                             //清零步距角累加和
     MoB.BeforeFlag = 0;                          //清标志                      
     DianJiFangXiang_Int(MoU.DIR);                //初始化电机方向  
     LvBoShuJu_Int(&Speed_LvBo,8);                //初始电机转速滤波系数      
     MoB.Uret = MoB.MinDianLiu;                   //写入最低电流     
     MoU.FsmStat = STATFORWARDSTART;              //置电机为起动任力标志      
     ComHandler(0);                               //第一次定位AB导通,A+ B-  C相关闭
     BLCD_SHIJIAN_ON;                             //开启电机  
     while(1)
          {
          if(MoB.Uret >= MoB.QiDongDianLiu)break;
          else{          
              MoB.Uret ++;                        //增加起动电流
              delay_ms(1);                        //延时
              }
          }
     delay_ms(30);                                //延时  
     //第二次定位   
     TIM4->CNT = 0;                               //清零计数器的值
     TIM1->SR = 0;                                //清定时器4的所有中断标志              
     MoB.OvFlag = 0;                              //清定时器溢出标志位
     MoB.BuJuSum = 0;                       //清零步距角累加器
     MoB.count = 0;                             //清零步进角度累加计数器         
     MoB.BeforeFlag = 0;                    //清标志  

          
     MoB.Period4  = 0x1fff * 8;                   //初始化换相时间240度的时间           
     MoB.Period   = MoB.Period4 / 4;              //60度的时间
     MoB.Commut20 = MoB.Period / 3;               //20度的时间
     MoB.Commut15 = MoB.Period / 4;               //15度的时间  
     MoB.Commut10 = MoB.Period / 6;               //10度的时间   
     ComHandler(1);                               //AC导通,A+ C- B相关闭  
     BIT_ADM(TIM4->DIER, 1) = 1;                  //定时器CC1E中断使能   
     BIT_ADM(TIM4->DIER, 2) = 1;                  //定时器CC2E中断使能   
     BIT_ADM(TIM4->DIER, 4) = 1;                  //定时器CC4E中断使能  
     BIT_ADM(TIM4->CR1,0) = 1;                    //开定时器TIM4      
     ErrCnt = 0;  
     while(1)
          {          
          BeforeFlag = 0;                         //清过零稳定标志位
          if(TIM4->CNT < MoB.Commut20 )
            {  //如果当前电机位置没有超过20度电度角,需要进行过零前的电平信号检测
            BeforeCnt = 0;  //计数器清零
            while(1)
                 {//读取过零信号(含去抖), 过零前                  
                 if(MEM_ADDR(MoB.CommutPin) == MoB.Signal)         
                   {
                   BeforeCnt++;  //计数器加1
                   if(BeforeCnt >= 16){BeforeFlag = 1; break;}//过零前信号稳定,置标志位,退出检测
                   }
                 else{
                     BeforeCnt /= 2;
                     if(TIM4->CNT > MoB.Commut15 )break;//过零信号错过,跳过
                     }
                 }
            while(TIM4->CNT < MoB.Commut20);       //延时到电机超过20度角时才进行过零后检测
            }        
          BeforeCnt = 0;                           //计数器清零
          AfterFlag = 0;                           //清过零电平稳定标志寄存器
          BulTim = 0x1FFF;  
          while((MoB.OvFlag == 0)&&(TIM4->CNT < MoB.Period4))
               {//读取过零信号, 过零后
               if(MEM_ADDR(MoB.CommutPin) == MoB.Signal){if(BeforeCnt != 0)BeforeCnt--;} //过零前
               else{//过零后,稳定
                   BeforeCnt++;                     //过零后电平信号稳定次数累加
                   if(BeforeCnt >= 16)
                     {
                     AfterFlag = 1;//置标过零后信号稳定标志,并退出
                     BulTim = TIM4->CNT;
                     break;
                     }
                   }
                }  
          MoB.Period4  = BulTim * 8;                //240度的时间      
          MoB.Period   = MoB.Period4 / 4;           //60度的时间
          MoB.Commut20 = MoB.Period / 3;            //20度的时间
          MoB.Commut15 = MoB.Period / 4;            //15度的时间  
          MoB.Commut10 = MoB.Period / 6;            //10度的时间        
          BulTim = MoB.Period - MoB.Commut20;       //得到当前次的换相时间
          QFilter_lst(&Speed_LvBo,BulTim);          //一阶滤波函数(动态调整滤波系数)  
          while(TIM4->CNT < BulTim);                //过零后等待, 再换向(15度延迟)         
          MoB.OvFlag = 0;                           //清定时器溢出标志位
          TIM4->CNT = 0;                            //清零计数器的值      
          TIM4->CCR2 = MoB.Period4;                 //下次换相时间设为60度
          MoB.CurPhase++;
          if(MoB.CurPhase >= 6)MoB.CurPhase = 0;                 
          ComHandler(MoB.CurPhase);                 //6步无感换向   
          if(BeforeFlag && AfterFlag )              //换向稳定判断
            {    //过零前后的检测电平稳定
            if(++ ucCommutOkCnt >= 20 )
              {  
              MoU.FsmStat = STATFORWARDRUN;          //启动成功,切换到闭环置闭环控制标志   
              LOOG_GAOSUMOSHI;    //高速模式      
              }        
            ErrCnt /= 2;                     
            }
          else{   //不稳定
              ErrCnt++;                             //累加过零检测信号不稳定的次数
              ucCommutOkCnt /= 2;                     
              }
          MoB.CntStart++;                           //总换相次数累加器加1
          if((ErrCnt >= 120)||(MoB.CntStart > 800))
            {
            BLCD_SHIJIAN_OFF;                 //关闭电机        
            MoU.FsmStat = STATSTARTFAIL;      //启动失败       
            }                       
          if(MoU.FsmStat != STATFORWARDSTART)return;   
          }
      }   
回复

使用道具 举报

14

主题

65

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
240
金钱
240
注册时间
2016-8-4
在线时间
71 小时
 楼主| 发表于 2018-1-5 10:32:42 | 显示全部楼层
大神,您的程序有点不明白,一开始这个GuoLingXinHaoPanDuan函数中确定编码值是干什么用的,无感电调的过零检测一般不是反电势吗,用AD采集来模拟比较然后中断。
回复

使用道具 举报

9

主题

507

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3347
金钱
3347
注册时间
2013-4-10
在线时间
333 小时
发表于 2018-1-8 09:41:40 | 显示全部楼层
代号_47k 发表于 2018-1-5 10:32
大神,您的程序有点不明白,一开始这个GuoLingXinHaoPanDuan函数中确定编码值是干什么用的,无感电调的过零 ...

编码值是3条反电动势的电平值的组合码, 我采用的是过零检测反电动势,用AD采集我试过了,时间不够,F1的芯片没有电压比较器,你要在一个换相周期中采集很多次电才能找到过零点,占用CPU太多时间了
回复

使用道具 举报

9

主题

507

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3347
金钱
3347
注册时间
2013-4-10
在线时间
333 小时
发表于 2018-1-8 09:43:59 | 显示全部楼层
用过零比较很好实现,不用中断也可以采用查询的方法,不过,用中断可以让CPU去做更多的事情,
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-7-22 19:30

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表