STM32F103步进电机驱动简单控制

stm32f103步进电机驱动控制

一、硬件接线方法

本文采用混合式二相步进电机-J8HB2401-野火42步进电机,驱动器为野火EBF-MSD4805,下面是常用接线方式:
如上图所示通常采用共阴接线方式,具体接线按照自己需求进行完成。
另外二相电机步距角1.8°,步进驱动器侧面SW1-SW8分别为细分设定、电流设定和驱动模式选择开关。

二、程序

代码如下(示例):

1.main.c

#include "stm32f10x.h"
#include "tim.h"
#include "bsp_usart.h"
#include "stm32f10x_it.h"
#include "stm32f10x_conf.h"
/* 共阴 */
/* 
PUL+->PA.1  PUL-接GND
DIR+->PA.8  DIR-接GND
*/
extern int motor_dir2;
extern int motor_speed;

int main(void)
{	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 	
	USART_Config();	     	
	Driver_Init();/* GPIO_Init */			
	TIM2_Init(999,72-1);       
	while(1) 
	{	
		Locate_Rle2(motor_speed,motor_dir2);/* 变速以及方向 */	
	}
}

2.tim.c

#include "tim.h"
#include "bsp_usart.h"
 
long current_pos[2] = {0,0}; 
int motor_dir2 = 0;
u8 count[2] = {0,0};
u32 motor_speed = 0;
u8 rcr_remainder;     //重复计数余数部分
u8 is_rcr_finish = 1;   //重复计数器是否设置完成
long rcr_integer;	  //重复计数整数部分
long target_pos = 0;    //有符号方向

/* 电机转速与方向函数 */
void Locate_Rle2(u32 frequency,DIR_Type dir) 
{
	motor_dir2 = dir;
	
	DRIVER_DIR = motor_dir2;
	if(motor_dir2 == CW){motor_speed = 3000;}/* motor_dir2高电平时转速3000Hz */
	else if(motor_dir2 == CCW){motor_speed = 10000;}/* motor_dir2低电平时转速10000Hz */
	frequency = motor_speed;
	
	if(TIM2->CR1&0x01)
	{
		printf("\r\nThe last time pulses is not send finished,wait please!\r\n");
		return;
	}
	if((frequency<20)||(frequency>100000))
	{
		printf("\r\nThe frequency is out of range! please reset it!!(range:20Hz~100KHz)\r\n");
		return;
	}
	TIM2_Startup(frequency);			
}

/* PA8 - DIR+*/
void Driver_Init(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	
	GPIO_Init(GPIOA, &GPIO_InitStructure);					
	GPIO_SetBits(GPIOA,GPIO_Pin_8);						
}
 
/* PA1- PUL+ TIM2_CH2 */
void TIM2_Init(u16 arr,u16 psc)
{		 					 
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
 
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); 	
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);                                                                      	
 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;				
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;			
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
	
	TIM_TimeBaseStructure.TIM_Period = arr;					 
	TIM_TimeBaseStructure.TIM_Prescaler =psc;				
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; 			
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); 		
	TIM_ClearITPendingBit(TIM2,TIM_IT_Update);				
 
	TIM_UpdateRequestConfig(TIM2,TIM_UpdateSource_Regular); 
	TIM_SelectOnePulseMode(TIM2,TIM_OPMode_Single);			
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;		
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; 
	TIM_OCInitStructure.TIM_Pulse = arr>>1; 				
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; 
	TIM_OC2Init(TIM2, &TIM_OCInitStructure); 				
 
	TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);  		
	TIM_ARRPreloadConfig(TIM2, ENABLE); 					
	
	TIM_ITConfig(TIM2, TIM_IT_Update ,ENABLE);  			
 
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;  		
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; 		
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 	
	NVIC_Init(&NVIC_InitStructure);  						
	
	TIM_ClearITPendingBit(TIM2, TIM_IT_Update);  			
	TIM_Cmd(TIM2, DISABLE);  															  
}

 
void TIM2_Startup(u32 frequency)   
{
	u16 temp_arr=1000000/frequency-1; 
	TIM_SetAutoreload(TIM2,temp_arr);
	TIM_SetCompare2(TIM2,temp_arr>>1); 
	
	TIM_SetCounter(TIM2,0);
	TIM_Cmd(TIM2, ENABLE);  
}

3.tim.h

#include "stm32f10x.h"
#include "stdlib.h"

#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x02000000+((addr & 0x00FFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 
#define PAout(n)          BIT_ADDR(GPIOA_ODR_Addr,n) 
#define GPIOA_ODR_Addr    (GPIOA_BASE+12) 
#define DRIVER_DIR        PAout(8) 

typedef enum
{
	CW = 1,//高电平顺时针
	CCW = 0,//低电平逆时针
}DIR_Type;

void Driver_Init(void);
void TIM2_Init(u16 arr,u16 psc);
void TIM2_Startup(u32 frequency);
void Locate_Rle2(u32 frequency,DIR_Type dir);

4.stm32f10x_it.c

/* Includes ------------------------------------------------------------------*/
#include "stm32f10x_it.h"
#include "tim.h"
#include "bsp_usart.h"
#include "systick.h"

extern u8 count[2];
extern long current_pos[2]; //有符号方向
extern int motor_dir2;
int counter = 0;

/******* TIM2 - 1ms*********/
void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2,TIM_FLAG_Update)!=RESET)
	{
		TIM_ClearITPendingBit(TIM2,TIM_FLAG_Update);	
		count[0]++;
		counter ++;
		TIM_GenerateEvent(TIM2,TIM_EventSource_Update);
	
		TIM_Cmd(TIM2, ENABLE);  					  
 
		if(count[0]==200)
		{
			if(motor_dir2==CW) 						  
				current_pos[0]+=count[0];
			else          							  
				current_pos[0]-=count[0];
			TIM_Cmd(TIM2, DISABLE);  				  		
			printf("\r\n motor2=%ld\r\n",current_pos[0]);
			count[0]=0;
		}
		
		if(counter <= 5000){motor_dir2=CW;}/* 定时器前5s拉高motor_dir2电平 */
		else if((counter > 5000) && (counter <= 10000)){motor_dir2=CCW;}/* 拉低电平 */
		else if(counter > 10000){counter=0;}/* 复位 */
	}
}

a.中断函数可以从保准库中进行拷贝,具体采用哪一个串口自己需要对照原理图;
b.PAout(n)函数采用GPIO-位带操作, 把“位带地址+ 位序号”转换成别名地址宏统一公式如下,详见野火指南:
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x02000000+((addr & 0x00FFFFFF)<<5)+(bitnum<<2))


总结

很多网友例程都对PAout(n)函数为加讲解,初学者不太容易get;另外在很多博主主函数或者定时器函数中都采用delay()函数进行延时操作是不对的,因为MCU在delay时间内无法进行其他动作。

展望

后续会更新升降电机过程中的梯形、S型加减速,相对,绝对,回原点,PID调节。

物联沃分享整理
物联沃-IOTWORD物联网 » STM32F103步进电机驱动简单控制

发表评论