用PWM實現儀表步進電機的微步細分驅動

用PWM實現儀表步進電機的微步細分驅動

儀表步進電機應用較早的是瑞士SWITEC公司的XC5系列步進電機,隨着國產同類產品的成熟,小型儀表步進電機應用更加廣泛。目前,國內外常用的幾種儀表步進電機電氣參數、驅動原理基本相同:MCU發脈衝給驅動芯片,電機會根據脈衝數轉動相應的步數。也有將步進電機驅動模塊集成在MCU中的,如飛思卡爾的HY64系列。這種應用中,軟件編程較爲簡單,但需要專用的硬件模塊。在某些應用場景中,我們可能會因爲PCB佈線空間、成本等因素,需要儘量減少電機驅動的硬件、簡化驅動軟件。這裏就以偉力VID2905儀表步進電機爲例,分析在分步和微步細分兩種模式下的驅動原理及用單片機PWM直接驅動電機的實現方法。

VID2905儀表步進電機原理簡述

如下圖,電機有兩組定子繞組線圈,1,2腳爲一組,3,4腳爲一組。和大多數電機基本原理一樣,給定子繞組加上特定的交變信號而產生旋轉磁場,轉子在磁場中受力轉動。繞組驅動電流電最大20mA。內置180:1齒輪減速器,即轉子轉180度時,輸出軸轉動1度。
在這裏插入圖片描述

分步驅動原理及實現方法

在這裏插入圖片描述
上圖是產品規格書中,分步驅動的圖解說明。從圖中可以看出,每個分步轉子轉動60度;一個全步是3個分步,轉子轉動180度;一個週期是6個分步,轉子轉動360度;經180:1減速,輸出軸轉動角度分別是1/3度、1度、2度。(至於爲什麼每分步是60度,不必去深究,這是由電機的結構決定的)根據上圖的驅動脈衝時序,在每個分步給兩組線圈加上相應極性的驅動電壓,就可以實現電機的轉動。改變脈衝序列的遞增或遞減順序,就可以控制電機的轉動方向。兩組線圈共佔用4個IO口。下表是4個IO的電平變化時序。H表示高電平,L表示低電平。在這裏插入圖片描述

以下是分步驅動函數(相關的宏定義、初始化部分省略了),已在STM32F407上調試過,讀者可以移植到自己的開發平臺上直接使用。入口參數是轉動方向和分步數,分步數乘以1/3就是電機軸的轉動角度。實際應用中,只需要根據採集到的模擬量值,按量程關係換算爲電機軸的角度,就可以現模擬量值的變化指示。分步模式驅動完全可以滿足如簡單的車載儀表等一些要求不高的應用。


/*---------------------------------------------------------------------
電機分步式運行
入口參數:
   M_Dir:轉動方向,1--正轉  0--反轉
   M_Step:轉動的分步數
----------------------------------------------------------------------*/

void Motor_RunStep(u8 M_Dir,u16 M_Step)
{
  static u8 M_Phase=1;
	u16 i;
	for(i=0;i<M_Step;i++)
	{
		if(M_Dir==0) //反轉
			{
				M_Phase--;			
				if(M_Phase==0)
				M_Phase=6;
			}
		else
			{
				M_Phase++;			
				if(M_Phase==7)
				M_Phase=1;
			}  
				switch (M_Phase)	
					{
						case 1:
							M_1=1;M_2=0;M_3=0;M_4=1;
							break;
						case 2:
							M_1=0;M_2=0;M_3=0;M_4=1;
							break;
						case 3:
							M_1=0;M_2=1;M_3=0;M_4=0;
							break;
						case 4:
							M_1=0;M_2=1;M_3=1;M_4=0;
							break;
						case 5:
							M_1=0;M_2=0;M_3=1;M_4=0;
							break;
						case 6:
							M_1=1;M_2=0;M_3=0;M_4=0;
							break;
						default:
							break;		
					}
			delay_ms(1);
		}
}	

分步驅動的優點是驅動代碼簡單,缺點是轉動抖動噪聲大。要減小電機的抖動降低轉動噪聲,首先需要分析抖動及噪聲的產生原理(如下圖,時間有限,只能順手畫個草圖):電機轉子是由線圈產生的磁場變化帶動旋轉的。前面已經描述,每個分步轉子轉動60度,即每個分步下,由兩組線圈所產生產磁場方向跳變角度是60度。(而且我們會注意到,在這種跳變會導致磁場的方向、大小都變化)假設我們需要轉子在兩組線圈磁場的合力下轉動到目標位置A,給兩組線圈按上表中的時序加上相應電壓,磁場方向跳變到位置A,則轉子隨着磁場跳變開始轉動,當轉子轉到A時,會因爲機械慣性超過A而繼續轉動到B,此時轉子轉動方向和磁場力的方向相反了,那麼在磁場力的作用下,它又反向朝着A轉動;到A時,又會因機械慣性而繼續轉動到C,此時轉動方向又與磁場力方向相反,則又會反向轉動,…,直到在摩擦力在作用下最終停止在位置A。這種反覆來回“盪鞦韆”,便形成了電機的抖動,併產生較大噪聲。
在這裏插入圖片描述

微步細分驅動原理及實現方法

爲了減小電機轉動時產生的抖動和噪聲,就得想辦法使定子線圈的磁場跳變幅度變小,也就是把一個分步變成若干個更小的步,即微步來完成,這種方式就是微步細分驅動。
在這裏插入圖片描述
在這裏插入圖片描述
上圖是電機廠家提供的微步細分時序圖,每個分步分成了4個微步。我們不難看出,它非常近似於正弦波。可以設想,如果“拖動”轉子轉動的磁場,其方向、大小的變化是按照上圖中的圓的方程變化的,而且恰好每週等分爲24個位置,那麼轉子也就會按着圓的方程去轉動。如果用X軸表示第一組線圈所產生的磁場,Y軸表示第二組線圈所產生的磁場,那麼當X,Y軸分別按照正、餘弦規律變化時,二者合成的磁場則符合圓的方程,即x2+y2=r^2,x=rcos(α),y=rsin(α)。這時,轉子在磁場力F,也按圓的方程變化,就可實現轉子的平穩轉動。
因此,現在問題就轉化爲,單片機只能輸出高低電平(邏輯0和1),如何在兩組線圈中產生按正、餘弦規律變化的磁場。要生產按正、餘弦變化的磁場,就需要在兩組線圈中通過按正、餘弦變化的電流。這樣就使問題進一步簡化了,儘管單片機只有01兩種邏輯,但是通常都會有帶有PWM功能的定時器,而PWM佔空比的改變是非常容易的。在兩組線圈上加PWM信號,當我們不停地修改兩組線圈PWM波的佔空比,分別按正弦、餘弦規律變化,由於線圈的電感作用,就會在線圈中產生正、弦變化的電流,這樣就產生了正、餘弦變化的磁場。如果需要非常精確的正弦、餘弦電流,需要加檢流電阻、ADC模塊,引入反饋,形成對電流的精確調節。在普通儀表指示的實際應用中,完全可以按開環方式運行。
通過以上描述,我們可以總結出微步細分驅動的幾個要點。1:使定時器的兩個PWM通道,初始佔空比設爲0,同時配置兩個通用IO口作爲線圈的負端;2:轉子轉動一圈爲1個週期,分爲24個微步,那麼就需要在正弦(餘弦)信號的一個週期內選取24個點,作爲佔空比,可以提前算好,放在常變量中,直接查表使用。3:每走一個微步,修改一次佔空比,在佔空比的過0點,通過改變線圈負端的極性和PWM的輸出比較極性改變線圈中的電流方向。下面的代碼是在STM32F407上實現的微步驅動函數。代碼已調試過,可以直接使用。

    
/*------------------------------------------------------------------------------
線圈1的CCR值,餘弦
-------------------------------------------------------------------------------*/
static const u8 CosTable[] =
{
255, 246, 221, 180, 127, 65, 0, 65, 127, 180, 221, 246,
255, 246, 221, 180, 127, 65, 0, 65, 127, 180, 221, 246
};
/*------------------------------------------------------------------------------
線圈2的CCR值,正弦
-------------------------------------------------------------------------------*/
static const u8 SinTable[] =
{
127, 180, 221, 246, 255, 246, 221, 180, 127, 65, 0, 65,
127, 180, 221, 246, 255, 246, 221, 180, 127, 65, 0, 65
};

/*---------------------------------------------------------------------
電機驅動端口初始化 (微步模式)
----------------------------------------------------------------------*/
void MOTOR_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStru;
	
	TIM_TimeBaseInitTypeDef  TIM3_TimeBaseStru;
	TIM_OCInitTypeDef        TIM3_OCStru;     
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);  //使能時鐘
	RCC_APB1PeriphClockCmd (RCC_APB1Periph_TIM3,ENABLE);
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC,ENABLE);  //使能時鐘
	
	GPIO_InitStru.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1;        //PE0,PE1
	GPIO_InitStru.GPIO_Mode=GPIO_Mode_OUT;                //輸出模式
	GPIO_InitStru.GPIO_OType=GPIO_OType_PP;               //推輓輸出
	GPIO_InitStru.GPIO_PuPd=GPIO_PuPd_UP;                 //上拉
	GPIO_InitStru.GPIO_Speed=GPIO_Speed_50MHz;            //速度50MHz
	
	GPIO_Init(GPIOE,&GPIO_InitStru);
	GPIO_ResetBits(GPIOE,GPIO_Pin_0|GPIO_Pin_1);      //初始值
	
  GPIO_InitStru.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7; 
	GPIO_InitStru.GPIO_Mode=GPIO_Mode_AF;
	
	GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_TIM3);
	GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_TIM3);
	GPIO_Init(GPIOC,&GPIO_InitStru);
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
	GPIO_PinAFConfig(GPIOA,GPIO_Pin_14,GPIO_AF_SWJ);
	GPIO_PinAFConfig(GPIOA,GPIO_Pin_13,GPIO_AF_SWJ);

	
	TIM3_TimeBaseStru.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM3_TimeBaseStru.TIM_CounterMode=TIM_CounterMode_Up;
	TIM3_TimeBaseStru.TIM_Period=255;
	TIM3_TimeBaseStru.TIM_Prescaler=167;
	TIM_TimeBaseInit(TIM3,&TIM3_TimeBaseStru);

	TIM3_OCStru.TIM_OCIdleState=TIM_OCIdleState_Reset;
	TIM3_OCStru.TIM_OutputState=TIM_OutputState_Enable;
	TIM3_OCStru.TIM_OCMode=TIM_OCMode_PWM1;
	TIM3_OCStru.TIM_Pulse=0;
	TIM_OC1Init(TIM3, &TIM3_OCStru);
	TIM_OC2Init(TIM3, &TIM3_OCStru);
	 
	TIM_Cmd(TIM3, ENABLE);	
}

/*---------------------------------------------------------------------
電機微步式運行
入口參數:
   M_Dir:轉動方向,1--正轉  0--反轉
   M_Step:轉動的微步數,每1微步電機軸轉動1/12度
----------------------------------------------------------------------*/
void MOTOR_RunMicroStep(u8 M_Dir,u16 M_Step)
{
	static u8 M_Phase;
	u16 i;
	for(i=0;i<M_Step;i++)
	{
		 if(M_Dir==1)      //順時針轉
				{
						if(M_Phase==23)//如果相位到最大值23了,則回0,開始下一週期
							M_Phase=0;
						else          //否則相位加1
							M_Phase++;
						switch(M_Phase)//根據相位值來決定電流方向的改變
						{
							case 6:
								M1_2=1;
								TIM_OC1PolarityConfig(TIM3,TIM_OCPolarity_Low);	
								break;
							case 10:
								M1_3=1; 
								TIM_OC2PolarityConfig(TIM3,TIM_OCPolarity_Low);	
								break;
							case 18:
								M1_2=0; 
								TIM_OC1PolarityConfig(TIM3,TIM_OCPolarity_High);
								break;
							case 22:
								M1_3=0; 
								TIM_OC2PolarityConfig(TIM3,TIM_OCPolarity_High);					
								break;
							default:
								break;
						}
				}
				else if(M_Dir==0)            //如果M_Dir=0,反轉。
				{
					if(M_Phase==0)//如果相位到0,則回最大值23,開始下一週期
							M_Phase=23;
						else          
							M_Phase--;//否則相位減1
						switch(M_Phase)//根據相位值來決定電流方向的改變
						{
							case 17:
								M1_2=1;  
								TIM_OC1PolarityConfig(TIM3,TIM_OCPolarity_Low);				
								break;
							case 21:
								M1_3=1; 
								TIM_OC2PolarityConfig(TIM3,TIM_OCPolarity_Low);					
								break;
							case 5:
								M1_2=0; 
								TIM_OC1PolarityConfig(TIM3,TIM_OCPolarity_High);	
								break;
							case 9:
								M1_3=0;
								TIM_OC2PolarityConfig(TIM3,TIM_OCPolarity_High);	
								break;
							default:
								break;
						}
				}
				TIM_SetCompare1(TIM3, CosTable[M_Phase]);
				TIM_SetCompare2(TIM3, SinTable[M_Phase]);
				delay_ms(1);
	} 
}


其它注意事項

1、任何驅動方式中,都要注意每兩步之間的延時不能太短,否則會造成電機“失步”。這個時間需要根據電機規格書中的數據去推算。
2、PWM波的頻率要合適,頻率過高或過低都會使電流更不接近於正、餘弦。
3、提前計算的正餘弦常量表,最大值要和PWM中的ARR值(重裝載值)一樣,這樣才能保證在一個週期中,最大佔空比能達到1。否則可能因爲電流不夠造成電機不動。
4、如果需要在運行過程中進行加減速,則需要增加一個定時器,根據步距的長短,用定時器去及時修改步間延時。步間延時決定了電機的轉動速度。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章