传送门:STM32控制ULN2003驱动步进电机28BYJ-48最基础方法
上节代码应该是能搜到的控制ULN2003驱动步进28BYJ-48最通用的方法了,但是上节代码的执行会导致整个系统进行阻塞。如果电机运转10圈可能导致41s的阻塞时间,这对于任何系统工程都是致命的。
在这41s中整个cpu都在改变IO口状态和delay_ms中循环,主要流程如下:
A相 --> delay_us(1000) --> AB相 --> delay_us(1000) --> B相 --> delay_us(1000) --> BC相 --> delay_us(1000) --.... --> delay_us(1000) --> A相 --> ....
其他任务得不到及时执行,这对于CPU是一种浪费,同时极大的影响系统及时性。
分析原因主要是由于delay_us(1000)造成的。
本节将仍然采用八拍(天龙八步),即1-2相驱动方式驱动,电机旋转函数中将不再使用delay_us(1000),而是将每步相位的保持时间放在定时器函数中进行,基本可以实现异步控制步进电机,同时执行其他程序进行。本教程采用STM32F103驱动ULN2003控制步进电机28BYJ-48运转。传送门:STM32控制ULN2003步进电机最基础方法
/* 步进电机1参数宏 */
#define LA PAout(1) /* A相 */
#define LB PAout(2) /* B相 */
#define LC PAout(3) /* C相 */
#define LD PAout(4) /* D相 */
/* A相 */
#define LA_GPIO_PORT GPIOA
#define LA_GPIO_PIN GPIO_Pin_1
#define LA_GPIO_CLK RCC_APB2Periph_GPIOA
/* B相 */
#define LB_GPIO_PORT GPIOA
#define LB_GPIO_PIN GPIO_Pin_2
#define LB_GPIO_CLK RCC_APB2Periph_GPIOA
/* C相 */
#define LC_GPIO_PORT GPIOA
#define LC_GPIO_PIN GPIO_Pin_3
#define LC_GPIO_CLK RCC_APB2Periph_GPIOA
/* D相 */
#define LD_GPIO_PORT GPIOA
#define LD_GPIO_PIN GPIO_Pin_4
#define LD_GPIO_CLK RCC_APB2Periph_GPIOA
/**
* @name: Step_Motor_Init
* @description: 步进电机初始化端口
* @param {*}
* @return {*}
*/
void Step_Motor_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(LA_GPIO_CLK | LB_GPIO_CLK | LC_GPIO_CLK | LD_GPIO_CLK, ENABLE);
/* A相端口初始化 */
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = LA_GPIO_PIN;
GPIO_Init(LA_GPIO_PORT, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = LB_GPIO_PIN;
GPIO_Init(LA_GPIO_PORT, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = LC_GPIO_PIN;
GPIO_Init(LA_GPIO_PORT, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = LD_GPIO_PIN;
GPIO_Init(LA_GPIO_PORT, &GPIO_InitStruct);
GPIO_ResetBits(LA_GPIO_PORT, LA_GPIO_PIN);
GPIO_ResetBits(LB_GPIO_PORT, LB_GPIO_PIN);
GPIO_ResetBits(LC_GPIO_PORT, LC_GPIO_PIN);
GPIO_ResetBits(LD_GPIO_PORT, LD_GPIO_PIN);
}
比如:
- A相位定义为bit0,当A相导通的时候状态对应0x01;
- B相位定义为bit1,当B相导通时位0x02;以此类推定义C相和D相;
/* 私有类型定义------------------------------------------------- */
typedef enum _PIN_BIT
{
PLA = 0x01,
PLB = 0x02,
PLC = 0x04,
PLD = 0x08,
} Pin_Bit;
/* 类型定义 ------------------------------------------ */
STRUCT(StepMotor_t)
{
/*
*state: bit0 0 表示电机处于非运行; 1 表示开启运行
* bit1 0 表示电机正转 1 反转
*/
uint8_t state;
/* 每步时间片 */
uint16_t step_slice;
/* 总步数 4096为一圈*/
u32 step_num;
void (*run)(StepMotor_t *motor);
};
/* 变量定义 ---------------------------------------------------- */
StepMotor_t StepMotor = {
0x01, 1200, 8192, Step_Motor_Start_InTimer};
/* 宏定义 ----------------------------------------------- */
#define TIMx TIM3
#define TIMx_APBxClock_Func RCC_APB1PeriphClockCmd
#define TIMx_CLK_EN RCC_APB1Periph_TIM3
#define TIMx_IRQn TIM3_IRQn
#define TIMx_IRQnHandler TIM3_IRQHandler
/* 类型定义 --------------------------------------------- */
STRUCT(Timer_t)
{
/*
* state: 可以复用,通过判断bit0;
* bit0 0表示非运行, 1表示运行
*/
uint8_t state;
uint16_t psc;
uint16_t arr;
/* 中断次数 */
u32 times;
};
/* 变量定义 --------------------------------------------- */
Timer_t Timer = {
0, 72, 0xffff, 8};
/* 函数定义 --------------------------------------------- */
void TIM_Configuration(uint16_t psc, uint16_t arr)
{
TIMx_APBxClock_Func(TIMx_CLK_EN, ENABLE);
/* 时机单元设置 */
TIM_TimeBaseInitTypeDef TIM_TBInitStruct;
TIM_TBInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TBInitStruct.TIM_Period = arr - 1;
TIM_TBInitStruct.TIM_Prescaler = psc - 1;
TIM_TBInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIMx, &TIM_TBInitStruct);
/* 开中断 */
TIM_ITConfig(TIMx, TIM_IT_Update, ENABLE);
NVIC_InitTypeDef NVIC_InitStruct;
/* 中断设置 */
NVIC_InitStruct.NVIC_IRQChannel = TIMx_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
/* 清除中断标志位 */
TIM_ClearITPendingBit(TIMx, TIM_IT_Update);
}
void TIMx_IRQnHandler(void)
{
if(TIM_GetITStatus(TIMx, TIM_IT_Update) == SET)
{
Step_Motor_Handler();
TIM_ClearITPendingBit(TIMx, TIM_IT_Update);
}
}
/**
* @name: Step_Motor_Handler
* @description: 在TIM IRQ中进行处理,设置/改变相位状态值
* @param {*}
* @return {*}
*/
void Step_Motor_Handler(void)
{
if(Timer.times)
{
Timer.times--;
Set_PhaseState(&StepMotor);
}
else /* 任务完成 */
{
printf("Timer timing over \n");
TIM_Cmd(TIMx, DISABLE); /* 关闭硬件定时器 */
Timer.state &= ~0x01; /* 软件定时器置0非运行态,可以其他程序调用 */
StepMotor.state &= ~0x01; /* motor状态为置0,非运行 */
Step_Motor_Stop(&StepMotor); /* 电机各个相位置0 */
}
}
/**
* @name: Set_PhaseState
* @description: 定时器中断中调用,设置电机相位状态
* @param {*}
* @return {*}
*/
void Set_PhaseState(StepMotor_t *motor)
{
/* 保存上次的相位数组下标 */
static uint8_t i = 0;
/* 判断正反转 */
if(!(motor->state & 0x02))
{
LA = (uint8_t)((steps[i]&PLA) >> 0);
LB = (uint8_t)((steps[i]&PLB) >> 1);
LC = (uint8_t)((steps[i]&PLC) >> 2);
LD = (uint8_t)((steps[i]&PLD) >> 3);
}
else
{
LA = (uint8_t)((steps[7-i]&PLA) >> 0);
LB = (uint8_t)((steps[7-i]&PLB) >> 1);
LC = (uint8_t)((steps[7-i]&PLC) >> 2);
LD = (uint8_t)((steps[7-i]&PLD) >> 3);
}
i++;
if(i >= 8)
{
i = 0;
}
}
/**
* @name: Step_Motor_Start_InTimer
* @description: 步进电机开始运行。设置软件定时器和硬件定时器,并开启硬件定时器功能。
* @param {StepMotor_t} *motor
* @return {*}
*/
void Step_Motor_Start_InTimer(StepMotor_t *motor)
{
/* 运行开始,电机开启运行置0 */
motor->state &= ~0x01;
/* 软件定时器设置 */
Timer.state |= 0x01; /* 软件定时器开启 */
Timer.arr = motor->step_slice;
Timer.times = motor->step_num + 1;
/* 硬件定时器设置 */
TIM_SetAutoreload(TIMx, Timer.arr);
printf("step motor start ... \n");
TIM_Cmd(TIMx, ENABLE);
/* 产生中断事件 */
TIM_GenerateEvent(TIMx, TIM_EventSource_Update);
}
int main(void)
{
uint32_t t = 0;
initSysTick();
NVIC_PriorityGroupConfig(2);
Usart1_Init(115200);
LED1_Init();
Step_Motor_Init();
TIM_Configuration(72, 20000);
printf("Hardware init ok.\n");
for(;;)
{
t++;
/* 开启步进电机任务,可以在其他任务中进行 */
if(0 == t % 1500)
StepMotor.state |= 0x01;
if(StepMotor.state & 0x01)
{
StepMotor.run(&StepMotor);
}
/* 其他任务 */
if(t % 100 == 1)
{
LED1_Toggle();
printf("led toggle\n");
}
if(t >= 2000)
t = 0;
delay_ms(10);
}
}
以上代码传送门:STM32驱动ULN控制步进电机天龙八步之定时器异步
喜欢请点个赞哦,谢谢!欢迎指正