【單片機】DIY無刷電機驅動器 2

接着上篇,先解釋一下爲什麼會出現“共振”現象,如下圖:

假如A1的佔空比爲25%,B2的佔空比爲10%,C2的佔空比也爲10%,我一開始想象的A1和B1之間的信號強度應該是:

25% x 10% = 2.5%

但是實際遠不是這麼回事,假如B2的高電平時間範圍正好都落在了A1的高電平時間範圍中,那麼A1到B2的信號的實際佔空比就是B2信號的佔空比10%。但是如果 B2的高電平時間範圍沒有完全都落在了A1的高電平時間範圍中或者完全沒有落在A1的高電平時間範圍中的話,A1到B2的有效信號寬度是A1和B2的信號中高電平重疊部分的寬度。比如C2的信號,高電平信號完全落在了A1信號高電平的外部,也就是A1到C2之間根本沒有重疊的部分,不會有電流產生,即使A1和C2的佔空都比大於0。

爲了解決這個問題,我的做法是將所有PWM通道初始化之後先不要使能定時器,而是在初始化完所有定時器之後再統一進行定時器的使能,這樣幾個定時器的使能時間差在幾個us的級別,甚至更小(CPU爲48MHz主頻,執行這幾條指令耗時很低,而且將全局中斷關閉,防止有中斷打斷該過程),而PWM信號的頻率一般是20KHz,週期爲50us,這樣可以讓PWM信號的初相儘可能一樣,這樣就能讓PWM信號的重疊區域最大,示例程序如下:

    ......

    TIM1_PWM_Init(255 - 1,10);    //48MHz / 10 / 255 = 18.8KHz
    TIM3_PWM_Init(255 - 1,10);
    TIM14_PWM_Init(255 - 1,10);
    
    ......

    
    __disable_irq();	//關閉全局中斷

    int tmp1,tmp2,tmp3;
    tmp1 = TIM1->CR1  | TIM_CR1_CEN;
    tmp2 = TIM3->CR1  | TIM_CR1_CEN;
    tmp3 = TIM14->CR1 | TIM_CR1_CEN;
	
    TIM1->CR1  = tmp1;
    TIM3->CR1  = tmp2;
    TIM14->CR1 = tmp3;

    __enable_irq();    //打開全局中斷


    ......

測試一下A1、B1、C1的波形相位問題:

可以看出來A1和B1的相位差0.25u(因爲我的邏輯分析儀的採樣率是4MHz,最小分辨率0.25us,所以實際的大小可能是小於0.25us的),已經很小了,但是B1和C1沒有相位差,因爲B1和C1都是TIM3輸出的PWM,肯定沒有誤差。可以猜測B1和C2差不多也誤差0.25us以下,屬於可接受範圍了,可以使用PWM信號來控制電流大小。實在是缺示波器

下面爲了使得PWM信號派上用場,這裏對三相波形的細分。三相驅動信號原圖爲:

我們將電流的瞬變改成漸變,如下圖的紫色、藍色、綠色三根:

爲了提高驅動電流,將t0到t5這6個階段拆分成t0_1、t0_2、t1_1、t1_2、t2_1、t2_2、t3_1、t3_2、t4_1、t4_2、t5_1、t5_2這12個階段,每一個單獨的階段中只有一相信號處於漸變狀態,其餘兩相處於滿狀態(佔空比100%),這樣就提高了總的電流和扭矩。下圖爲擴展後的波形圖(PS零水平,手繪):

12個階段的A、B、C相的狀態表也在上圖中寫出來了,根據這個狀態表寫每個階段的程序即可:

void t0_1(void)
{
//	printf("A:0->1 B:-1 C:1\r\n");
	*A1 = 0;
	*A2 = 0;
	
	*B1 = 0;
	*B2 = MAX_PWM;
	
	*C1 = MAX_PWM;
	*C2 = 0;
	
	int i;
	for(i=0;i<=MAX_PWM;i++)
	{
		*A1 = i;
		delay_us(INTERVAL);
	}
}

void t0_2(void)
{
//	printf("A:1 B:-1 C:1->0\r\n");
	*A1 = MAX_PWM;
	*A2 = 0;
	
	*B1 = 0;
	*B2 = MAX_PWM;
	
	*C1 = MAX_PWM;
	*C2 = 0;

	int i;
	for(i=0;i<=MAX_PWM;i++)
	{
		*C1 = MAX_PWM - i;
		delay_us(INTERVAL);
	}
}

void t1_1(void)
{
//	printf("A:1 B:-1 C:0->-1\r\n");
	*A1 = MAX_PWM;
	*A2 = 0;
	
	*B1 = 0;
	*B2 = MAX_PWM;
	
	*C1 = 0;
	*C2 = 0;

	int i;
	for(i=0;i<=MAX_PWM;i++)
	{
		*C2 = i;
		delay_us(INTERVAL);
	}
}

void t1_2(void)
{
//	printf("A:1 B:-1->0 C:-1\r\n");
	*A1 = MAX_PWM;
	*A2 = 0;
	
	*B1 = 0;
	*B2 = MAX_PWM;
	
	*C1 = 0;
	*C2 = MAX_PWM;

	int i;
	for(i=0;i<=MAX_PWM;i++)
	{
		*B2 = MAX_PWM - i;
		delay_us(INTERVAL);
	}
}

void t2_1(void)
{
//	printf("A:1 B:0->1 C:-1\r\n");
	*A1 = MAX_PWM;
	*A2 = 0;
	
	*B1 = 0;
	*B2 = 0;
	
	*C1 = 0;
	*C2 = MAX_PWM;

	int i;
	for(i=0;i<=MAX_PWM;i++)
	{
		*B1 = i;
		delay_us(INTERVAL);
	}
}

void t2_2(void)
{
//	printf("A:1->0 B:1 C:-1\r\n");
	*A1 = MAX_PWM;
	*A2 = 0;
	
	*B1 = MAX_PWM;
	*B2 = 0;
	
	*C1 = 0;
	*C2 = MAX_PWM;

	int i;
	for(i=0;i<=MAX_PWM;i++)
	{
		*A1 = MAX_PWM - i;
		delay_us(INTERVAL);
	}
}

void t3_1(void)
{
//	printf("A:0->-1 B:1 C:-1\r\n");
	*A1 = 0;
	*A2 = 0;
	
	*B1 = MAX_PWM;
	*B2 = 0;
	
	*C1 = 0;
	*C2 = MAX_PWM;

	int i;
	for(i=0;i<=MAX_PWM;i++)
	{
		*A2 = i;
		delay_us(INTERVAL);
	}
}

void t3_2(void)
{
//	printf("A:-1 B:1 C:-1->0\r\n");
	*A1 = 0;
	*A2 = MAX_PWM;
	
	*B1 = MAX_PWM;
	*B2 = 0;
	
	*C1 = 0;
	*C2 = MAX_PWM;

	int i;
	for(i=0;i<=MAX_PWM;i++)
	{
		*C2 = MAX_PWM - i;
		delay_us(INTERVAL);
	}
}

void t4_1(void)
{
//	printf("A:-1 B:1 C:0->1\r\n");
	*A1 = 0;
	*A2 = MAX_PWM;
	
	*B1 = MAX_PWM;
	*B2 = 0;
	
	*C1 = 0;
	*C2 = 0;

	int i;
	for(i=0;i<=MAX_PWM;i++)
	{
		*C1 = i;
		delay_us(INTERVAL);
	}
}

void t4_2(void)
{
//	printf("A:-1 B:1->0 C:1\r\n");
	*A1 = 0;
	*A2 = MAX_PWM;
	
	*B1 = MAX_PWM;
	*B2 = 0;
	
	*C1 = MAX_PWM;
	*C2 = 0;

	int i;
	for(i=0;i<=MAX_PWM;i++)
	{
		*B1 = MAX_PWM - i;
		delay_us(INTERVAL);
	}
}

void t5_1(void)
{
//	printf("A:-1 B:0->-1 C:1\r\n");
	*A1 = 0;
	*A2 = MAX_PWM;
	
	*B1 = 0;
	*B2 = 0;
	
	*C1 = MAX_PWM;
	*C2 = 0;

	int i;
	for(i=0;i<=MAX_PWM;i++)
	{
		*B2 = i;
		delay_us(INTERVAL);
	}
}

void t5_2(void)
{
//	printf("A:-1->0 B:-1 C:1\r\n");
	*A1 = 0;
	*A2 = MAX_PWM;
	
	*B1 = 0;
	*B2 = MAX_PWM;
	
	*C1 = MAX_PWM;
	*C2 = 0;

	int i;
	for(i=0;i<=MAX_PWM;i++)
	{
		*A2 = MAX_PWM - i;
		delay_us(INTERVAL);
	}
}

主程序按順序調用這12個函數:

	//設置所有橋臂爲無輸出。注意IO_X1和IO_X2信號不可以同時爲高,
	//否則會導致橋臂短路,容易燒壞MOS管,同時在程序中最好先將爲0的
	//信號先設置爲0,然後再設置不爲0的信號,這樣可以避免短路。
	*A1 = 0;
	*A2 = 0;
	*B1 = 0;
	*B2 = 0;
	*C1 = 0;
	*C2 = 0;
	
	#define INTERVAL	10		//PWM上升下降間隔時間
	#define MAX_PWM		255		
	
	//處於t5狀態,並維持一段時間,保證可靠處於t5狀態
	printf("Ready\r\n");
	{	//t5:CB
		printf("t5:CB\r\n");
		*A1 = 0;
		*A2 = 0;
		
		*C1 = MAX_PWM;
		*C2 = 0;
		
		*B1 = 0;
		*B2 = MAX_PWM;
	}
	delay_ms(200);
	
	while(1)
	{
		t0_1();//		printf("0_1\r\n");		delay_ms(2000);
		t0_2();//		printf("0_2\r\n");		delay_ms(2000);
		t1_1();//		printf("1_1\r\n");		delay_ms(2000);
		t1_2();//		printf("1_2\r\n");		delay_ms(2000);
		t2_1();//		printf("2_1\r\n");		delay_ms(2000);
		t2_2();//		printf("2_2\r\n");		delay_ms(2000);
		t3_1();//		printf("3_1\r\n");		delay_ms(2000);
		t3_2();//		printf("3_2\r\n");		delay_ms(2000);
		t4_1();//		printf("4_1\r\n");		delay_ms(2000);
		t4_2();//		printf("4_2\r\n");		delay_ms(2000);
		t5_1();//		printf("5_1\r\n");		delay_ms(2000);
		t5_2();//		printf("5_2\r\n");		delay_ms(2000);
	}

這裏的INTERVAL是上升延時,和上篇文章中的不是一個含義。調整INTERVAL的大小可以改變轉速,程序運行的不錯,電機比之前運行絲滑很多,和某巧克力一樣。

程序可以停在12個階段中的任意一個階段中的任意一個狀態,所以可以實現和步進電機一樣的步進控制,調大INTERVAL到1000以以上就能看出電機慢慢的轉動的現象了,但是不如步進電機,老是會有“突變”的現象,可能就是無刷電機結構問題導致的,因爲無刷電機一開始就沒這麼用的......

再來改一下程序,如下:

void go_stage(int stage)
{
	if(stage == 0)
	{
		*A1 = 0;
		*A2 = 0;
		
		*B1 = 0;
		*B2 = MAX_PWM;
		
		*C1 = MAX_PWM;
		*C2 = 0;
	}
	else if(stage == 1)
	{
		*A1 = MAX_PWM;
		*A2 = 0;
		
		*B1 = 0;
		*B2 = MAX_PWM;
		
		*C1 = MAX_PWM;
		*C2 = 0;
	}
	else if(stage == 2)
	{
		*A1 = MAX_PWM;
		*A2 = 0;
		
		*B1 = 0;
		*B2 = MAX_PWM;
		
		*C1 = 0;
		*C2 = 0;
	}
	else if(stage == 3)
	{
		*A1 = MAX_PWM;
		*A2 = 0;
		
		*B1 = 0;
		*B2 = MAX_PWM;
		
		*C1 = 0;
		*C2 = MAX_PWM;
	}
	else if(stage == 4)
	{
		*A1 = MAX_PWM;
		*A2 = 0;
		
		*B1 = 0;
		*B2 = 0;
		
		*C1 = 0;
		*C2 = MAX_PWM;
	}
	else if(stage == 5)
	{
		*A1 = MAX_PWM;
		*A2 = 0;
		
		*B1 = MAX_PWM;
		*B2 = 0;
		
		*C1 = 0;
		*C2 = MAX_PWM;
	}
	else if(stage == 6)
	{
		*A1 = 0;
		*A2 = 0;
		
		*B1 = MAX_PWM;
		*B2 = 0;
		
		*C1 = 0;
		*C2 = MAX_PWM;
	}
	else if(stage == 7)
	{
		*A1 = 0;
		*A2 = MAX_PWM;
		
		*B1 = MAX_PWM;
		*B2 = 0;
		
		*C1 = 0;
		*C2 = MAX_PWM;
	}
	else if(stage == 8)
	{
		*A1 = 0;
		*A2 = MAX_PWM;
		
		*B1 = MAX_PWM;
		*B2 = 0;
		
		*C1 = 0;
		*C2 = 0;
	}
	else if(stage == 9)
	{
		*A1 = 0;
		*A2 = MAX_PWM;
		
		*B1 = MAX_PWM;
		*B2 = 0;
		
		*C1 = MAX_PWM;
		*C2 = 0;
	}
	else if(stage == 10)
	{
		*A1 = 0;
		*A2 = MAX_PWM;
		
		*B1 = 0;
		*B2 = 0;
		
		*C1 = MAX_PWM;
		*C2 = 0;
	}
	else if(stage == 11)
	{
		*A1 = 0;
		*A2 = MAX_PWM;
		
		*B1 = 0;
		*B2 = MAX_PWM;
		
		*C1 = MAX_PWM;
		*C2 = 0;
	}
}

void main(void)
{

    ......


	//設置所有橋臂爲無輸出。注意IO_X1和IO_X2信號不可以同時爲高,
	//否則會導致橋臂短路,容易燒壞MOS管,同時在程序中最好先將爲0的
	//信號先設置爲0,然後再設置不爲0的信號,這樣可以避免短路。
	*A1 = 0;
	*A2 = 0;
	*B1 = 0;
	*B2 = 0;
	*C1 = 0;
	*C2 = 0;
	
	#define INTERVAL	10		//PWM上升下降間隔時間
	#define MAX_PWM		255		
	
	printf("Ready\r\n");
	stage = 0;
	go_stage(stage);
	delay_ms(200);
	
	direction = 0;	//控制轉動方向
	stage = 0;		//當前階段
	offset = 0;		//階段中偏移
	while(1)
	{
		if(direction == 0)	//順時針
		{
			offset++;
			if(offset == MAX_PWM + 1)	//換相
			{
				offset= 0;
				stage++;
				if(stage == 12)
					stage = 0;
				go_stage(stage);
			}
		}
		else
		{
			if(offset == 0)	//換相
			{
				offset= 255;
				if(stage == 0)
					stage = 11;
				else
					stage--;
				go_stage(stage);
			}
			else
				offset--;
		}
		
		switch(stage)
		{
			case 0	:*A1 = offset;				break;
			case 1	:*C1 = MAX_PWM - offset;	break;
			case 2	:*C2 = offset;				break;
			case 3	:*B2 = MAX_PWM - offset;	break;
			case 4	:*B1 = offset;				break;
			case 5	:*A1 = MAX_PWM - offset;	break;
			case 6	:*A2 = offset;				break;
			case 7	:*C2 = MAX_PWM - offset;	break;
			case 8	:*C1 = offset;				break;
			case 9	:*B1 = MAX_PWM - offset;	break;
			case 10	:*B2 = offset;				break;
			case 11	:*A2 = MAX_PWM - offset;	break;
		}
		delay_us(INTERVAL);
	}
}

通過調整direction和INTERVAL的值可以修改轉動方向和轉動速度,改變MAX_PWM可以調整電流大小。抓取一下波形看看6個通道的PWM信號的12個階段,其中白色的部分是PWM信號:

以第一個階段也就是程序裏面的 t0_1 階段爲例,6個通道首先處於 t5:CB 的初始化狀態,然後A1通道從0%到100%漸變,A2通道爲0%,B1、B2、C1、C2分別爲0%、100%、100%、0%,和我們設計的波形是一樣的。

程序裏面可以把PWM的細分數降低,我改成(10 - 1)(這裏 -1 是爲了當PWM取值10時佔空比爲100%,與具體單片機內部定時器的PWM實現機制相關),然後將分頻數增加到500,因爲沒必要細分那麼多級,MAX_PWM的取值範圍爲 0 到 10 ,這樣INTERVAL的值也可以放大到 ms 級別,可以放在一個定時時間爲INTERVAL的定時器中斷中來處理電機的驅動,如果INTERVAL很小的話,會導致一直在定時器中斷中出不來,示例程序:

    TIM1_PWM_Init(10 - 1,500);    //48M / 500 / 10 = 9.6KHz
    TIM3_PWM_Init(10 - 1,500);
    TIM14_PWM_Init(10 - 1,500);

    #define INTERVAL    1000		//PWM上升下降間隔時間
    #define MAX_PWM    10		

然後我將電機驅動電壓改成12V的電源,調整MAX_PWM的值分別測量電流大小,我測試使用電機在滿PWM的時候能達到3A的電流,在50%的佔空比的時候電流爲0.4A左右。不通電機和供電電壓需要匹配不同的INTERVAL和MAX_PWM的值才能轉的好。

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