stm32标准库步进电机非阻塞控制 ULN2003驱动28BYJ-48(中断法)


前言

STM32CubeMX Hal库 步进电机驱动 [ULN2003驱动器] 阻塞模式和非阻塞模式[中断模式]
上次写的式cubemx的现在用标准库的写一下。

目前只有全局角度控制没写,没时间了。

一、定时器配置

由于是使用非阻塞模式,所以需要配置一个定时器,定时器的周期为250us,每250us进一次中断。在中断中进行换向。

void TIM2_Int_Init(u16 arr,u16 psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //时钟使能
	//定时器TIM2初始化
	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE ); //使能指定的TIM2中断,允许更新中断
	//中断优先级NVIC设置
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;  //TIM2中断    
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器
	TIM_Cmd(TIM2, ENABLE);  //使能TIMx					 
}

二、步进电机运行库

1.c

#include "bujin.h"




//步进电机结构体变量声明处
STEP_MOTOR step1;

void moter_init(void)//gpio初始化
{
	GPIO_InitTypeDef a1;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG,ENABLE);
	a1.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7 |GPIO_Pin_8 |GPIO_Pin_9;
	a1.GPIO_Mode=GPIO_Mode_Out_PP;
	a1.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOG,&a1);
	GPIO_ResetBits(GPIOG,GPIO_Pin_6|GPIO_Pin_7 |GPIO_Pin_8 |GPIO_Pin_9);
}
void stepper_struct_init(		// 初始化 stepper结构体
    STEP_MOTOR*   STEP,
	GPIO_TypeDef* IN1_GPIO_PORT,
    uint16_t IN1_GPIO_PIN,
    GPIO_TypeDef* IN2_GPIO_PORT,
    uint16_t IN2_GPIO_PIN,
    GPIO_TypeDef* IN3_GPIO_PORT,
    uint16_t IN3_GPIO_PIN,
    GPIO_TypeDef* IN4_GPIO_PORT,
    uint16_t IN4_GPIO_PIN
	
		)
{
	STEP->IN1_GPIO_PORT = IN1_GPIO_PORT;
	STEP->IN1_GPIO_PIN  = IN1_GPIO_PIN;
	STEP->IN2_GPIO_PORT = IN2_GPIO_PORT;
	STEP->IN2_GPIO_PIN  = IN2_GPIO_PIN;	
	STEP->IN3_GPIO_PORT = IN3_GPIO_PORT;
	STEP->IN3_GPIO_PIN  = IN3_GPIO_PIN;
	STEP->IN4_GPIO_PORT = IN4_GPIO_PORT;
	STEP->IN4_GPIO_PIN  = IN4_GPIO_PIN;	
	
	STEP->Step_Count = 0;//计数值初始值为0,运行过程中不用修改
	STEP->MoterSpeed = 10;//速度值 越小越快。最小为10
    STEP->Rotation_Dir= pos;  // 新增旋转方向变量,0为正转,1为反转
	STEP-> Run_State= 0; // 0:固定速度 1:位置控制	
	STEP->Target_Steps= 0 ; // 新增目标步数变量 ,运行过程中不用手动修改	
	
	STEP->Global_Angle = 0;
	STEP-> Step_Angle_Ratio = 0;
	
}
/*
	换向控制函数
*/
/* 直接操作寄存器的方法控制IO */
#define	digitalHi(p,i)		 {p->BSRR=i;}	 //输出为高电平		
#define digitalLo(p,i)		 {p->BRR=i;}	 //输出低电平
static void StepMotor_Switch(STEP_MOTOR *stp)
{
    switch(stp->Step_Count % 4)
    {
        case 0:

            digitalHi(stp->IN1_GPIO_PORT, stp->IN1_GPIO_PIN);
            digitalLo(stp->IN2_GPIO_PORT, stp->IN2_GPIO_PIN);
            digitalLo(stp->IN3_GPIO_PORT, stp->IN3_GPIO_PIN);
            digitalLo(stp->IN4_GPIO_PORT, stp->IN4_GPIO_PIN);
            break;
        case 1:
            digitalLo(stp->IN1_GPIO_PORT, stp->IN1_GPIO_PIN);
            digitalHi(stp->IN2_GPIO_PORT, stp->IN2_GPIO_PIN);
            digitalLo(stp->IN3_GPIO_PORT, stp->IN3_GPIO_PIN);
            digitalLo(stp->IN4_GPIO_PORT, stp->IN4_GPIO_PIN);
            break;
        case 2:

            digitalLo(stp->IN1_GPIO_PORT, stp->IN1_GPIO_PIN);
            digitalLo(stp->IN2_GPIO_PORT, stp->IN2_GPIO_PIN);
            digitalHi(stp->IN3_GPIO_PORT, stp->IN3_GPIO_PIN);
            digitalLo(stp->IN4_GPIO_PORT, stp->IN4_GPIO_PIN);
            break;
        case 3:
            digitalLo(stp->IN1_GPIO_PORT, stp->IN1_GPIO_PIN);
            digitalLo(stp->IN2_GPIO_PORT, stp->IN2_GPIO_PIN);
            digitalLo(stp->IN3_GPIO_PORT, stp->IN3_GPIO_PIN);
            digitalHi(stp->IN4_GPIO_PORT, stp->IN4_GPIO_PIN);
            break;
		default:break;
    }
}
/*
	运行函数
*/
void StepFour_Func(STEP_MOTOR *stp)//这个函数要在定时器里面跑
{
	
    if(stp->Run_State == 0) // 固定速度模式
    {
        if(stp->Rotation_Dir == pos) //正转
            stp->Step_Count++;
        else//反转
            stp->Step_Count--;
    }
    else // 位置控制模式
    {
        if(stp->Rotation_Dir == pos)//正转
        {
            if(stp->Step_Count < stp->Target_Steps)//设设定值大于当前值
                stp->Step_Count++;
        }
        else//反转
        {

            if(stp->Step_Count > (65535 - stp->Target_Steps)) 
			{
                stp->Step_Count--;
			}
        }
    }	
	StepMotor_Switch(stp);
/*
	直接转动模式
*/
void SetConstantSpeed(STEP_MOTOR *stp, Dir dir,uint32_t MoterSpeed)
{
    stp->Run_State = 0;
    stp->Rotation_Dir = dir;
	if(MoterSpeed<10)
	{
		MoterSpeed = 10;
	}
	stp->MoterSpeed = MoterSpeed;
}
/*
	脉冲模式
*/
void SetTargetPosition(STEP_MOTOR *stp, uint8_t dir, uint16_t steps)//设定转动步数
{
    stp->Run_State = 1;
    stp->Rotation_Dir = pos;
    stp->Target_Steps = steps;
	if(dir == 0)
	{
	    stp->Step_Count = 0;
	}
	else
	{
		stp->Step_Count = 65535;
	}
}
/*
	相对角度模式
*/
void SetRelativeAngularPosition(STEP_MOTOR *stp, float target_angle)
{
    stp->Run_State = 1;
	if(target_angle>=0)
	{
		stp->Rotation_Dir = pos;
		stp->Step_Count = 0;
	}
	else
	{
		stp->Rotation_Dir = neg;
		stp->Step_Count = 65535;
	}
    // 计算目标步数
    uint16_t target_steps = (uint16_t)(target_angle / 360.0 * 2048.0);
    stp->Target_Steps = target_steps;
}
/*
	绝对角度模式
*/
void SetAbsolutelyAngularPosition(STEP_MOTOR *stp, float target_angle)
{
    stp->Run_State = 2;
	if(target_angle>=0)
	{
		stp->Rotation_Dir = pos;
		stp->Step_Count = 0;
	}
	
	else
	{
		stp->Rotation_Dir = neg;
		stp->Step_Count = 65535;
	}
    // 计算目标步数
    uint16_t target_steps = (uint16_t)(target_angle / 360.0 * 2048.0);
    stp->Target_Steps = target_steps;
    
}
u16 TimerCount = 0;
void TIM2_IRQHandler(void)   //TIM2中断  //250us进一次中断 换向一次
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)  //检查TIM2更新中断发生与否
	{
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update  );  //清除TIMx更新中断标志 
		
		TimerCount ++ ; 	

		if(TimerCount >step1.MoterSpeed)   //定时超过  MoterSpeed为步进电机速度控制
		{
			TimerCount = 0;			
			StepFour_Func(&step1);

		}	
		
	}
}

2.h

#ifndef __BUJIN_H
#define __BUJIN_H
#include "stm32f10x.h"                  // Device header

typedef enum {
    pos = 0,//pos为正转
    neg
} Dir;

typedef struct
{
    uint16_t Step_Count;//计数值初始值为0,运行过程中不用修改
	uint32_t MoterSpeed;//速度值 越小越快。最小为10
    Dir Rotation_Dir;  // 新增旋转方向变量,0为正转,1为反转
	uint8_t Run_State; // 0:固定速度 1:位置控制	
	uint16_t Target_Steps; // 新增目标步数变量 ,运行过程中不用手动修改

	
	GPIO_TypeDef* IN1_GPIO_PORT;
    uint16_t IN1_GPIO_PIN;
    GPIO_TypeDef* IN2_GPIO_PORT;
    uint16_t IN2_GPIO_PIN;
    GPIO_TypeDef* IN3_GPIO_PORT;
    uint16_t IN3_GPIO_PIN;
    GPIO_TypeDef* IN4_GPIO_PORT;
    uint16_t IN4_GPIO_PIN;
	
	
    float Global_Angle; // 全局角度变量,单位为度
    float Step_Angle_Ratio; // 步数-角度转换系数	
	
} STEP_MOTOR;

void moter_init(void);//电机初始化  gpio初始化函数

/*控制函数*/
void SetConstantSpeed(STEP_MOTOR *stp, Dir dir,uint32_t MoterSpeed);//直接转动模式
void SetTargetPosition(STEP_MOTOR *stp, uint8_t dir, uint16_t steps);//脉冲模式
void SetRelativeAngularPosition(STEP_MOTOR *stp, float target_angle);//相对角度模式

void stepper_struct_init(		// 初始化 步进电机 结构体初始化
    STEP_MOTOR*   STEP,
	GPIO_TypeDef* IN1_GPIO_PORT,
    uint16_t IN1_GPIO_PIN,
    GPIO_TypeDef* IN2_GPIO_PORT,
    uint16_t IN2_GPIO_PIN,
    GPIO_TypeDef* IN3_GPIO_PORT,
    uint16_t IN3_GPIO_PIN,
    GPIO_TypeDef* IN4_GPIO_PORT,
    uint16_t IN4_GPIO_PIN
		);
//外部变量声明
extern STEP_MOTOR step1;
#endif


三、如何使用

需要几个步进电机就在步进电机.c和.h的地方//步进电机结构体变量声明处添加 STEP_MOTOR step1;extern STEP_MOTOR step1;
然后需要在主函数的地方先对自己的引脚初始化,例如void moter_init(void)//gpio初始化

然后是给步进电机结构体初始化

	stepper_struct_init( &step1,
		GPIOG,GPIO_Pin_6,
		GPIOG,GPIO_Pin_7,
		GPIOG,GPIO_Pin_8,
		GPIOG,GPIO_Pin_9
			)	;//初始化

然后再给定时器初始化,一定要先初始化步进电机,不然会有一定错误。

TIM2_Int_Init(24,719);   //Tout= ((24+1)*( 719+1))/72=250us	

想添加其他的定时器就还得在定时器中断void TIM2_IRQHandler(void) //TIM2中断 //250us进一次中断 换向一次里面再添加额外步进电机的结构体。


  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
步进电机28BYJ-48是一种4相5线式步进电机,可以使用ULN2003芯片来驱动ULN2003是一种集成了7个开关型晶体管的芯片,可以方便地控制步进电机,具有低功耗、高可靠性等优点。 以下是使用stm32f103c8t6控制步进电机ULN2003驱动步进电机28BYJ-48的步骤: 1. 确定步进电机28BYJ-48的相序和控制信号,根据相序表将ULN2003的输出端口与步进电机连接。 2. 在stm32f103c8t6的开发环境中编写C语言程序,使用GPIO控制ULN2003的输出端口,从而控制步进电机的运动。 3. 根据需要设置步进电机的转速、方向等参数,调整程序代码,实现步进电机的精准控制。 下面是一个简单的示例程序,用于控制步进电机28BYJ-48以顺时针方向旋转一个圈: ```c #include "stm32f10x.h" #define IN1 GPIO_Pin_0 #define IN2 GPIO_Pin_1 #define IN3 GPIO_Pin_2 #define IN4 GPIO_Pin_3 #define DELAY 5 void delay_ms(uint16_t ms) { while(ms--) { uint16_t i = 5000; while(i--); } } void step(uint8_t i) { switch(i) { case 0: GPIO_ResetBits(GPIOA, IN1); GPIO_ResetBits(GPIOA, IN2); GPIO_ResetBits(GPIOA, IN3); GPIO_SetBits(GPIOA, IN4); break; case 1: GPIO_ResetBits(GPIOA, IN1); GPIO_ResetBits(GPIOA, IN2); GPIO_SetBits(GPIOA, IN3); GPIO_ResetBits(GPIOA, IN4); break; case 2: GPIO_SetBits(GPIOA, IN1); GPIO_ResetBits(GPIOA, IN2); GPIO_ResetBits(GPIOA, IN3); GPIO_ResetBits(GPIOA, IN4); break; case 3: GPIO_ResetBits(GPIOA, IN1); GPIO_SetBits(GPIOA, IN2); GPIO_ResetBits(GPIOA, IN3); GPIO_ResetBits(GPIOA, IN4); break; } } int main(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = IN1 | IN2 | IN3 | IN4; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); for(int i = 0; i < 512; i++) { step(i % 4); delay_ms(DELAY); } return 0; } ``` 在本程序中,首先定义了步进电机28BYJ-48的4个控制信号IN1、IN2、IN3、IN4,然后使用GPIO初始化这些端口。在主函数中,使用step函数循环控制步进电机旋转一个圈。 step函数根据相序表依次控制ULN2003的输出端口,从而实现步进电机的旋转。为了让步进电机旋转得更平稳,还加入了一个延时函数delay_ms,以等待一定时间后再进行下一步操作。最后,程序返回0,结束执行。 需要注意的是,在实际使用过程中,还需要根据具体的步进电机型号和应用场景进行优化和调整。例如,可以通过增加步进电机的电压和电流来提高转速和扭矩,或者使用更高级别的控制来实现更精准的控制

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值