STM32筆記(27):控制ULN2003驅動28BYJ-48步進馬達

本篇有兩個主題:一是使用不同的驅動方式控制步進馬達轉動的方向,另一個是控制其轉動的角度。過去曾使用樹梅派做過類似的控制,有些原理可以參考: Raspberry Pi 筆記(12):控制步進馬達。本篇將使用一個按鍵來切換 28BYJ-48 步進馬達驅動的方式,並可讓馬達轉動 180 度。28BYJ-48 是一個常見的步進馬達,幾乎所有學習開發板的人,都會認識這個型號,而型號命名是有意義的:
  • 28:步進馬達的有效最大外徑是 28 毫米
  • B:表示是步進馬達
  • Y:表示是永磁式
  • J:表示是減速型(減速比1:64)
  • 48:表示四相八拍

[28BYJ-48規格]

28BYJ-48 步進馬達的規格如下:
  • 單極兩種規格:DC 5V 及 12V
  • 直徑:28mm
  • 齒輪減速比(變速箱):1/64
  • 定子極數:上(8+8)、下(8+8)
  • 轉子極數:16
  • 步距角度:360 / 64 x 1/64 = 0.087890625(半步模式)
  • 中心抽頭(紅線)與輸入引腳之間的直流電阻:21Ω
  • 輸出軸步數/轉 = (8x4x2) x 64 = 4096(半步模式)
  • 最大速度 = 15~20 rpm

線圈的接線如下圖:

28BYJ-48 步進馬達有 4 相(表示 4 組線圈) 5 根線,紅色是公共端,接 5V 電源,4橙(A),3黃(B),2粉(C),1藍(D)。半步的減速比為 64:1 表示內部馬達轉動 64 圈,外部的軸才轉動 1 圈,可以算出內部馬達轉動一圈,外部軸心轉動角度為 360º / 64 = 5.625º。

若將 5.625º 乘以減速比得到輸出的解析度為:每步旋轉 5.625 x 1/64 = 0.087890625 度。由此算出若要讓外軸轉動一圈,內部馬達需要行走 360 / 0.087890625 = 4096 步。

這是在行走半步的模式,若為全步,則外軸轉動一圈,內部點機需轉動 64 / 2 = 32 步。這表示內部馬達必須移動 32×64 = 2048 步,外部的軸才能轉一圈。每一步將轉動 360º/ 2048 = 0.18º 。

[ULN2003驅動板]

ULN2003 是高壓大電流達林頓晶體管陣列系列產品,具有電流增益高、工作電壓高、溫度範圍寬、帶負載能力強等特點,適合各類要求高速大功率驅動的系統。在實際使用中,只需要使用 STM32 的 I/O 接 ULN2003 驅動板的 IN1 ~ IN4 即可,單片機和此驅動板需要共接地線。

旋轉步進馬達通常稱為驅動,可以是波浪驅動、全步驅動、半步驅動驅動三種之一:

1.全步波浪驅動(Wave Drive)時序為:D->C->B->A ,轉一圈需要四步,一次僅驅動一個線圈工作,較少的消耗。
時序
A0111
B1011
C1101
D1110

2.全步驅動(Full-Step Drive)時序為:AB->BC ->CD ->DA ,轉一圈需要四步,一次激勵兩個線圈,在最大扭矩下,更高的電流換來的是更高的扭矩。
時序
A0011
B1001
C1100
D0110

3.半步驅動(Half-Step Drive)時序為:A->AB->B ->BC ->C ->CD ->D ->DA ,轉一圈需要八步,可使用半步驅動以獲得更精確的運動。
時序
A0111
AB0011
B1011
BC1001
C1101
CD1100
D1110
DA0110



[材料]

  • STM32F103C8T6 主板 x 1
  • STLINK V2 模擬下載器 x 1
  • UNL2003 控制版 x 1
  • 28BYJ-48 步進馬達 x 1
  • 按鍵 x 1
  • 麵包板 x 1
  • 連接線 x N 條

[接線與電路圖]

UNL2003模組的正負分別接 3.3v 及 GND,IN1~IN4 接腳分別接在 STM32F103x 的 PA1 ~ PA4,按鍵的一腳接 PB14,另一端接地,連接的方式如下:
STM32F103xUNL2003
5v+
GND-
PA1IN1
PA2IN2
PA3IN3
PA4IN4


[程式]

按鍵控制四種驅動方式:按一下使用波浪驅動方式轉動,按兩下使用全步驅動方式轉動,按三下使用半步驅動方式轉動,按四下轉動 180度後暫停,在繼續轉動180,持續進行到下一個按鍵按下。為了跟前一個動作有明顯的不同,每次變換動作,旋轉的方向就相反。主程式 main.c 如下:
#include "delay.h"
#include "sys.h"
#include "motor.h"
#include "button.h"
	
int main(void)
{
	uint16_t btn_count;
	
	delay_init(); 				//延時函數初始化
	Button_Init();				//按鍵初始化
	Step_Motor_GPIO_Init();		//步進馬達初始化
	
	while (1)
	{
		btn_count = Button_Get();
		if (btn_count%5 == 1){
			Motor_Start(1, 4, 1); 	// 順時鐘轉, 延時需大於4ms, 使用波浪驅動 	
		} 
		if (btn_count%5 == 2){
			Motor_Start(0, 4, 2); 	// 逆時鐘轉, 延時需大於4ms, 使用全步驅動
		}
		if (btn_count%5 == 3){ 
			Motor_Start(1, 2, 3); 	// 順時鐘轉, 延時2ms, 使用半步驅動
		}	
		if (btn_count%5 == 4){	
			Motor_Start_Angle(0, 4, 1, 180);	 //	轉動 180度
			delay_ms(50000);    	// 暫停再繼續
		} 
	}	
}
Motor.c 的程式如下:
#include "delay.h"
#include "motor.h"

//引腳初始化
void Step_Motor_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Pin = IN1|IN2|IN3|IN4;
    GPIO_Init(MOTOR_PORT, &GPIO_InitStructure);
}

// 波浪驅動
void Phase4_Single(u8 step, u8 delay)
{
    switch(step){
		case 1:
		IN1_HIGH; IN2_LOW; IN3_LOW; IN4_LOW;
		break;
	case 2:
		IN1_LOW; IN2_HIGH; IN3_LOW; IN4_LOW;
		break;
	case 3:
		IN1_LOW; IN2_LOW; IN3_HIGH; IN4_LOW;		
		break;
	case 4:
		IN1_LOW; IN2_LOW; IN3_LOW; IN4_HIGH;			
		break;
	}
	delay_ms(delay);
}

// 全步驅動
void Phase4_Double(u8 step, u8 delay)
{
    switch(step){
		case 1:
		IN1_LOW; IN2_HIGH; IN3_HIGH; IN4_LOW;
		break;
	case 2:
		IN1_LOW; IN2_LOW; IN3_HIGH; IN4_HIGH;
		break;
	case 3:
		IN1_HIGH; IN2_LOW; IN3_LOW; IN4_HIGH;		
		break;
	case 4:
		IN1_HIGH; IN2_HIGH; IN3_LOW; IN4_LOW;			
		break;
	}
	delay_ms(delay);
}

// 半步驅動
void Phase8_Single(u8 step, u8 delay)
{
    switch(step){
		case 1:
		IN1_LOW; IN2_HIGH; IN3_HIGH; IN4_HIGH;
		break;
	case 2:
		IN1_LOW; IN2_LOW; IN3_HIGH; IN4_HIGH;
		break;
	case 3:
		IN1_HIGH; IN2_LOW; IN3_HIGH; IN4_HIGH;		
		break;
	case 4:
		IN1_HIGH; IN2_LOW; IN3_LOW; IN4_HIGH;			
		break;
	case 5:
		IN1_HIGH; IN2_HIGH; IN3_LOW; IN4_HIGH;
		break;
	case 6:
		IN1_HIGH; IN2_HIGH; IN3_LOW; IN4_LOW;
		break;
	case 7:
		IN1_HIGH; IN2_HIGH; IN3_HIGH; IN4_LOW;		
		break;
	case 8:
		IN1_LOW; IN2_HIGH; IN3_HIGH; IN4_LOW;			
		break;	
	}
	delay_ms(delay);
}

//馬達停止
void Motor_Stop(void)
{
	IN1_LOW; IN2_LOW; IN3_LOW; IN4_LOW;	
}
	
/*
功能:轉動1/64圈
direction: 轉動方向 1:正轉 非1:反轉
delay: 延時時長,需 >= 2
phase: 1:四相單步, 2:四相雙步, 3:八相單步
*/

void Motor_Start(u8 direction, u8 delay, u8 phase)
{
	if (direction){  // 順時針轉			
		switch(phase){
			case 1:	
				for(u8 i=1;i<5;i++){
					Phase4_Single(i, delay);	}
				break;
			case 2:
				for(u8 i=1;i<5;i++){
					Phase4_Double(i, delay); }
				break;
			case 3:
				for(u8 i=1;i<9;i++){
					Phase8_Single(i, delay); }
				break;
			default:
				break;
		}	
	} else // 逆時針轉
	{
		switch(phase){
			case 1:	
				for(u8 i=4;i>0;i--){
					Phase4_Single(i, delay); }
				break;
			case 2:
				for(u8 i=4;i>0;i--){
					Phase4_Double(i, delay); }
				break;
			case 3:
				for(u8 i=8;i>0;i--){
					Phase8_Single(i, delay);	}
				break;
			default:
				break;
		}	
	}	
}

/*
功能:旋轉至特定角度
direction: 轉動方向 1:正轉 非1:反轉
delay: 延時時長,需 >= 2
phase: 1:四相單步, 2:四相雙步, 3:八相單步
angle: 轉動角度
*/
void Motor_Start_Angle(u8 direction, u8 delay, u8 phase, u16 angle)
{
	for(u16 j=0;j<(64*angle/45);j++){
		Motor_Start(direction, delay, phase);
	}
}

Motor.h 程式如下:
#ifndef __MOTOR_H__
#define __MOTOR_H__

#define MOTOR_PORT GPIOA
#define IN1 GPIO_Pin_1
#define IN2 GPIO_Pin_2
#define IN3 GPIO_Pin_3
#define IN4 GPIO_Pin_4

#define IN1_HIGH GPIO_SetBits(MOTOR_PORT,IN1);
#define IN1_LOW  GPIO_ResetBits(MOTOR_PORT,IN1);

#define IN2_HIGH GPIO_SetBits(MOTOR_PORT,IN2);
#define IN2_LOW  GPIO_ResetBits(MOTOR_PORT,IN2);

#define IN3_HIGH GPIO_SetBits(MOTOR_PORT,IN3);
#define IN3_LOW  GPIO_ResetBits(MOTOR_PORT,IN3);

#define IN4_HIGH GPIO_SetBits(MOTOR_PORT,IN4);
#define IN4_LOW  GPIO_ResetBits(MOTOR_PORT,IN4);

void Step_Motor_GPIO_Init(void);
void motor_circle(int n, int direction, int delay);
void Phase4_Single(u8 step, u8 delay);
void Phase8_Single(u8 step, u8 delay);
void Phase4_Double(u8 step, u8 delay);
void Motor_Start(u8 direction, u8 freq, u8 phase);
void Motor_Start_Angle(u8 direction, u8 freq, u8 phase, u16 angle);
void Motor_Stop(void);

#endif

完整的程式請參考 Github:12.ULN2003 Control 28BYJ-48 Stepper Motor

[結果]

按下三次按鍵使用半步驅動時,發現有時會停頓不會轉動,原因可能是我使用 STM32 開發板上的 5V 電源,加上這個驅動強調精度,扭力不足,如果改用外接電源,應該可以改變這個狀況。


[參考資料]


Post a Comment

較新的 較舊