自带减速器,五线四相单极性,直径为28mm。一般有五根线。红色是公共的VCC(5v),A相蓝色,B相粉色,C相黄色,D相橙色。如果采用单双拍如图所示:
第一步:先D相导通接GND,其他接高电平。第二部:D,C相导通接GND其他接高电平。一次走完8步。如果对应MCU那么红色接VCC(5V),其他四相接MCU的普通I/o并且选择推挽输出。如果让电机反转则把步数反过来。注意每一步的切换都要延时一段时间。
首先相电阻就是公共端与每相之间的电阻。 步距角有两种情况。没有加减速比的时候。5.625度。
加减速比就是5.625/ 64。 因为减速比是1:64。启动频率>=500。
一个脉冲电机转5.625度。一圈(360)= 360/ 5.625 = 64个脉冲。那么减速后轴转一圈需要64 *64 = 4096.当然减速比不可能精确1:64.总有误差存在。28BYJ-48步进电机实际上是:减速齿轮+步进电机组成。内部减速齿轮如下:
由于MCU的端口的电流很小对于驱动电流大的外设需要驱动板(放大电流)。
电机需要驱动的电流 VCC/ 相电阻 = 5 / 300 =16.7mA。
ULN2003是大电流达林顿晶体管阵列系列产品,具有电流增益高、工作电压高、温度范围宽、带负载能力强等特点,适应于各类要求高速大功率驱动的系统。ULN2003A由7组达林顿晶体管阵列和相应的电阻网络以及钳位二极管网络构成,具有同时驱动7组负载的能力,为单片双极型大功率高速集成电路。ULN2003可用于小型步进电机驱动。
下面如图接线方法:
ULN2003_IN1, ULN2003_IN2,ULN2003_IN3,ULN2003_IN4左边接MCU的I/o口,经过ULN2003AD后电流就放大了从而去驱动电机。
uln2003.h
#ifndef __BSP_ULN2003_H__
#define __BSP_ULN2003_H__
#include "stm32f4xx_hal.h"
#define ULN2003_RCC_CLK_ENABLE() __HAL_RCC_GPIOF_CLK_ENABLE()
#define ULN2003_GPIO_PIN (GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6)
#define ULN2003_GPIO_PORT GPIOF
#define A_ON HAL_GPIO_WritePin(ULN2003_GPIO_PORT,GPIO_PIN_3,GPIO_PIN_SET)
#define A_OFF HAL_GPIO_WritePin(ULN2003_GPIO_PORT,GPIO_PIN_3,GPIO_PIN_RESET)
#define B_ON HAL_GPIO_WritePin(ULN2003_GPIO_PORT,GPIO_PIN_4,GPIO_PIN_SET)
#define B_OFF HAL_GPIO_WritePin(ULN2003_GPIO_PORT,GPIO_PIN_4,GPIO_PIN_RESET)
#define C_ON HAL_GPIO_WritePin(ULN2003_GPIO_PORT,GPIO_PIN_5,GPIO_PIN_SET)
#define C_OFF HAL_GPIO_WritePin(ULN2003_GPIO_PORT,GPIO_PIN_5,GPIO_PIN_RESET)
#define D_ON HAL_GPIO_WritePin(ULN2003_GPIO_PORT,GPIO_PIN_6,GPIO_PIN_SET)
#define D_OFF HAL_GPIO_WritePin(ULN2003_GPIO_PORT,GPIO_PIN_6,GPIO_PIN_RESET)
void ULN2003_GPIO_Init(void);
#endif // __BSP_ULN2003_H__
uln2003.c
#include "StepMotor/bsp_uln2003.h"
void ULN2003_GPIO_Init(void)
{
/* 定义IO硬件初始化结构体变量 */
GPIO_InitTypeDef GPIO_InitStruct;
/* 使能(开启)端口时钟 */
ULN2003_RCC_CLK_ENABLE();
/* 一开始设置为低电平 */
HAL_GPIO_WritePin(ULN2003_GPIO_PORT,ULN2003_GPIO_PIN,GPIO_PIN_RESET);
/* 设定引脚IO编号 */
GPIO_InitStruct.Pin = ULN2003_GPIO_PIN;
/* 设定引脚IO为输出模式 */
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
/* 设定引脚IO操作速度 */
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
/* 初始化引脚IO */
HAL_GPIO_Init(ULN2003_GPIO_PORT, &GPIO_InitStruct);
}
main.c
#include "stm32f4xx_hal.h"
#include "StepMotor/bsp_uln2003.h"
#define STEPMOTOR_SPEED 10 // 定义步进电机速度,值越小,速度越快
void SystemClock_Config(void);
/**
* 函数功能: 系统时钟配置
* 输入参数: 无
* 返 回 值: 无
* 说 明: 无
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
__HAL_RCC_PWR_CLK_ENABLE(); // 使能PWR时钟
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); // 设置调压器输出电压级别1
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; // 外部晶振,8MHz
RCC_OscInitStruct.HSEState = RCC_HSE_ON; // 打开HSE
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; // 打开PLL
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; // PLL时钟源选择HSE
RCC_OscInitStruct.PLL.PLLM = 8; // 8分频MHz
RCC_OscInitStruct.PLL.PLLN = 336; // 336倍频
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; // 2分频,得到168MHz主时钟
RCC_OscInitStruct.PLL.PLLQ = 7; // USB/SDIO/随机数产生器等的主PLL分频系数
HAL_RCC_OscConfig(&RCC_OscInitStruct);
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // 系统时钟:168MHz
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB时钟: 168MHz
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; // APB1时钟:42MHz
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; // APB2时钟:84MHz
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5);
HAL_RCC_EnableCSS(); // 使能CSS功能,优先使用外部晶振,内部时钟源为备用
// HAL_RCC_GetHCLKFreq()/1000 1ms中断一次
// HAL_RCC_GetHCLKFreq()/100000 10us中断一次
// HAL_RCC_GetHCLKFreq()/1000000 1us中断一次
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000); // 配置并启动系统滴答定时器
/* 系统滴答定时器时钟源 */
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* 系统滴答定时器中断优先级配置 */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
/**
* 函数功能: 主函数.
* 输入参数: 无
* 返 回 值: 无
* 说 明: 无
*/
int main(void)
{
uint8_t i=0;
/* 复位所有外设,初始化Flash接口和系统滴答定时器 */
HAL_Init();
/* 配置系统时钟 */
SystemClock_Config();
ULN2003_GPIO_Init();
/* 无限循环 */
while (1)
{
// 8个节拍控制:A->AB->B->BC->C->CD->D->DA 正转 如果反过来可以反转 2.这个可以控制方向
switch(i)
{
case 0:
A_ON; B_OFF; C_OFF; D_OFF;
break;
case 1:
A_ON; B_ON; C_OFF; D_OFF;
break;
case 2:
A_OFF; B_ON; C_OFF; D_OFF;
break;
case 3:
A_OFF; B_ON; C_ON; D_OFF;
break;
case 4:
A_OFF; B_OFF; C_ON; D_OFF;
break;
case 5:
A_OFF; B_OFF; C_ON; D_ON;
break;
case 6:
A_OFF; B_OFF; C_OFF; D_ON;
break;
case 7:
A_ON; B_OFF; C_OFF; D_ON;
break;
}
i++; // 步进加1
if(i==8) i=0; // 重新开始循环步进
HAL_Delay(STEPMOTOR_SPEED); // 延时一小段时间,时间长短决定电机转速 1.这个延时可以控制速度
}
}
这里公共端是接GDN,所以某相接高低平就接通,并且这里采用单双拍工作方式。每一步切换都有一个延时,这个延时可以控制速度。改变步数的顺序可以控制方向。
uln2003.h uln2003.c跟上面一样,只是main.c不同,并且加了按键控制。
main.c
#include "stm32f4xx_hal.h"
#include "StepMotor/bsp_uln2003.h"
#include "key/bsp_key.h"
#define STEPMOTOR_SPEED 1 // 定义步进电机速度,值越小,速度越快
#define STEPMOTOR_CIRCLE_NUMBER 10 // 转动圈数
#define STEPMOTOR_DIRECTION 1 // 1:顺时针 0:逆时针
uint8_t speed = STEPMOTOR_SPEED; // 用一个变量保存速度
// 转动圈数:28BYJ-48步进电机的步距角度为5.625/64,即每64个脉冲转5.625度
// 要转一圈需要360/5.625*64=4096个脉冲。 // 没有减速:一个脉冲 转 5.625 如果转360 要64个脉冲 减速后:因为减速比是1:64 轴需要 64 *64 =4096个脉冲
uint32_t Circle_number= 0; // 圈数
// 选择方向控制
uint8_t direction = STEPMOTOR_DIRECTION; // 用一个变量保存方向
void SystemClock_Config(void);
/**
* 函数功能: 系统时钟配置
* 输入参数: 无
* 返 回 值: 无
* 说 明: 无
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
__HAL_RCC_PWR_CLK_ENABLE(); // 使能PWR时钟
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); // 设置调压器输出电压级别1
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; // 外部晶振,8MHz
RCC_OscInitStruct.HSEState = RCC_HSE_ON; // 打开HSE
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; // 打开PLL
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; // PLL时钟源选择HSE
RCC_OscInitStruct.PLL.PLLM = 8; // 8分频MHz
RCC_OscInitStruct.PLL.PLLN = 336; // 336倍频
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; // 2分频,得到168MHz主时钟
RCC_OscInitStruct.PLL.PLLQ = 7; // USB/SDIO/随机数产生器等的主PLL分频系数
HAL_RCC_OscConfig(&RCC_OscInitStruct);
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // 系统时钟:168MHz
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB时钟: 168MHz
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; // APB1时钟:42MHz
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; // APB2时钟:84MHz
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5);
HAL_RCC_EnableCSS(); // 使能CSS功能,优先使用外部晶振,内部时钟源为备用
// HAL_RCC_GetHCLKFreq()/1000 1ms中断一次
// HAL_RCC_GetHCLKFreq()/100000 10us中断一次
// HAL_RCC_GetHCLKFreq()/1000000 1us中断一次
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000); // 配置并启动系统滴答定时器
/* 系统滴答定时器时钟源 */
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* 系统滴答定时器中断优先级配置 */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
/**
* 函数功能: 输出一个数据给ULN2003从而实现向步进电机发送一个脉冲
* 输入参数: step:指定步进序号,可选值0~7
* direction:方向选择
* 可选值:1:顺时针
* 0:逆时针
* 返 回 值: 无
* 说 明: 无
*/
static void step_motor_pulse(uint8_t step,uint8_t direction)
{
uint8_t temp=step;
if(direction==0) // 如果为逆时针旋转
{
temp=7-step; // 调换节拍信号
}
switch(temp)
{
// 8个节拍控制:A->AB->B->BC->C->CD->D->DA
case 0:
A_ON; B_OFF; C_OFF; D_OFF;
break;
case 1:
A_ON; B_ON; C_OFF; D_OFF;
break;
case 2:
A_OFF; B_ON; C_OFF; D_OFF;
break;
case 3:
A_OFF; B_ON; C_ON; D_OFF;
break;
case 4:
A_OFF; B_OFF; C_ON; D_OFF;
break;
case 5:
A_OFF; B_OFF; C_ON; D_ON;
break;
case 6:
A_OFF; B_OFF; C_OFF; D_ON;
break;
case 7:
A_ON; B_OFF; C_OFF; D_ON;
break;
}
}
/**
* 函数功能: 主函数.
* 输入参数: 无
* 返 回 值: 无
* 说 明: 无
*/
int main(void)
{
/* 复位所有外设,初始化Flash接口和系统滴答定时器 */
HAL_Init();
/* 配置系统时钟 */
SystemClock_Config();
KEY_GPIO_Init();
ULN2003_GPIO_Init();
/* 无限循环 */
while (1)
{
if(KEY1_StateRead()==KEY_DOWN)
{
direction=1; // 顺时针方向
Circle_number=STEPMOTOR_CIRCLE_NUMBER;//赋值给目标旋转圈数,重新开始旋转
}
if(KEY2_StateRead()==KEY_DOWN)
{
direction=0; // 逆时针方向
Circle_number=STEPMOTOR_CIRCLE_NUMBER;//赋值给目标旋转圈数,重新开始旋转
}
if(KEY3_StateRead() == KEY_DOWN) // 加速
{
speed --;
if(speed <= STEPMOTOR_SPEED)
speed = STEPMOTOR_SPEED;
}
if(KEY4_StateRead() == KEY_DOWN) // 减速
{
speed ++;
}
}
}
/**
* 函数功能: 滴答定时器回调函数.
* 输入参数: 无
* 返 回 值: 无
* 说 明: 每隔一定的时间就输出新节拍信号
*/
void HAL_SYSTICK_Callback(void)
{
static uint8_t count=0; // 用于旋转速度控制
static uint8_t step=0; // 当前步进节拍
static uint16_t pulse_count=0; // 脉冲计数,4096个脉冲电机旋转一圈
if(Circle_number) // 如果等待旋转圈数不为0
{
count++; // 增加时间计数
if(count==speed) // 时间计数与目标速度相对时执行下一节拍输出 // 旋转一圈要 4096ms *speed
{
step_motor_pulse(step,direction); // 输出新节拍信号
pulse_count++; // 脉冲输出数增加
step++; // 节拍数增加
if(step==8) step=0; // 循环开始输出节拍
count=0; // 清零时间计数
}
if(pulse_count==4096) // 如果已经输出了4096个脉冲信号,已经转动了一圈
{
pulse_count=0; // 脉冲计数清零
Circle_number--; // 等待旋转圈数减1
}
}
else // 转完设置的圈数停止旋转
{
A_OFF; B_OFF; C_OFF; D_OFF; // 停机
}
}