本程序编写基于秉火霸道STM32F103ZET6运行环境。
今天来玩一下步进电机模块,电机控制,是一门非常高深的学问,如果想去走工控行业需要玩到电机方面的,那么步进电机一定少不了,不管怎么说,我们还是可以把它驱动起来的,以下是我买的一个步进电机驱动模块。
步进电机选用的是:28BYJ48-H12
这里在软件编程上有一个比较重要参数需要了解一下,就是步距角。
那么什么是步距角呢?度娘给你答案,可以详细看看。
https://baike.baidu.com/item/%E6%AD%A5%E8%B7%9D%E8%A7%92/5946465?fr=aladdin
来看看下面这个换算公式,或许你就明白了。
如上图所示,步距角=5.625°/64
意思就是每64个脉冲步进电机就会转5.625度,因此我们很容易得出以下计算公式:
电机转一圈有360°,那么转一圈的脉冲数 = 360 / 5.625 * 64 = 4096 个脉冲。
进而很容易得到以下角度与脉冲的简单算法:
/*
Rotation_Angle:旋转角度
返回:Motor_Pulse 根据公式计算得出的脉冲个数
*/
int Motor_Angle_Cal(int Rotation_Angle)
{
Motor_Pulse = (int)((double)(Rotation_Angle / 5.625) * 64) ;
return Motor_Pulse ;
}
下面开始在CubeMX上配置管脚,然后编写程序。
PA4、PA5、PA6、PA7默认配置为输出,默认电平为低电平,即各个相默认不工作。
PA0、PC13即为按键,当按下即为高电平,否则为低电平。
motor.h
#ifndef __MOTOR_H
#define __MOTOR_H
#include "main.h"
//4相控制定义
#define MOTOR_A_ON HAL_GPIO_WritePin(GPIOA, MOTOR_A_Pin, GPIO_PIN_SET);
#define MOTOR_A_OFF HAL_GPIO_WritePin(GPIOA, MOTOR_A_Pin, GPIO_PIN_RESET);
#define MOTOR_B_ON HAL_GPIO_WritePin(GPIOA, MOTOR_B_Pin, GPIO_PIN_SET);
#define MOTOR_B_OFF HAL_GPIO_WritePin(GPIOA, MOTOR_B_Pin, GPIO_PIN_RESET);
#define MOTOR_C_ON HAL_GPIO_WritePin(GPIOA, MOTOR_C_Pin, GPIO_PIN_SET);
#define MOTOR_C_OFF HAL_GPIO_WritePin(GPIOA, MOTOR_C_Pin, GPIO_PIN_RESET);
#define MOTOR_D_ON HAL_GPIO_WritePin(GPIOA, MOTOR_D_Pin, GPIO_PIN_SET);
#define MOTOR_D_OFF HAL_GPIO_WritePin(GPIOA, MOTOR_D_Pin, GPIO_PIN_RESET);
extern int direction ;
extern uint16_t Motor_Pulse ;
//电机脉冲计算
int Motor_Angle_Cal(int Rotation_Angle);
//电机8节拍控制
void MOTOR_CONTROLD(uint8_t step, uint8_t direction);
//关闭电机
void CLOSE_MOTOR(void);
#endif //__MOTOR_H
motor.c
#include "Motor.h"
//电机旋转的方向
int direction = 0 ;
//电机旋转的脉冲个数
uint16_t Motor_Pulse = 0 ;
//电机控制,采用8节拍来做
//A->AB->B->BC->C->CD->D->DA
void MOTOR_CONTROLD(uint8_t step, uint8_t direction)
{
uint8_t __step = step ;
//判断电机的旋转方向,如果为1,则逆向旋转
if(1 == direction)
__step = 8 - step ;
switch(__step)
{
//A
case 0:
MOTOR_A_ON;
MOTOR_B_OFF;
MOTOR_C_OFF;
MOTOR_D_OFF;
break ;
//AB
case 1:
MOTOR_A_ON;
MOTOR_B_ON;
MOTOR_C_OFF;
MOTOR_D_OFF;
break ;
//B
case 2:
MOTOR_A_OFF;
MOTOR_B_ON;
MOTOR_C_OFF;
MOTOR_D_OFF;
break ;
//BC
case 3:
MOTOR_A_OFF;
MOTOR_B_ON;
MOTOR_C_ON;
MOTOR_D_OFF;
break ;
//C
case 4:
MOTOR_A_OFF;
MOTOR_B_OFF;
MOTOR_C_ON;
MOTOR_D_OFF;
break ;
//CD
case 5:
MOTOR_A_OFF;
MOTOR_B_OFF;
MOTOR_C_ON;
MOTOR_D_ON;
break ;
//D
case 6:
MOTOR_A_OFF;
MOTOR_B_OFF;
MOTOR_C_OFF;
MOTOR_D_ON;
//DA
case 7:
MOTOR_A_ON;
MOTOR_B_OFF;
MOTOR_C_OFF;
MOTOR_D_ON;
break ;
}
}
//关闭电机
void CLOSE_MOTOR(void)
{
HAL_GPIO_WritePin(GPIOA, MOTOR_A_Pin | MOTOR_B_Pin | MOTOR_C_Pin | MOTOR_D_Pin, GPIO_PIN_RESET);
}
/*
Rotation_Angle:旋转角度
返回:Motor_Pulse 根据公式计算得出的脉冲个数
*/
int Motor_Angle_Cal(int Rotation_Angle)
{
Motor_Pulse = (int)((double)(Rotation_Angle / 5.625) * 64) ;
return Motor_Pulse ;
}
Key_Scan.h
#ifndef __KEY_SCAN_H
#define __KEY_SCAN_H
#include "main.h"
//按键0扫描
void Key0_Scan(uint8_t *pKeyValue);
//按键1扫描
void Key1_Scan(uint8_t *pKeyValue);
#endif //__KEY_SCAN_H
Key_Scan.c
#include "Key_Scan.h"
void Key0_Scan(uint8_t *pKeyValue)
{
static char key_status = 0;
static int key_time = 0 ;
switch(key_status)
{
case 0: //检测.消抖
if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == 1)
key_status++;
break;
case 1: //确定
if((key_time < 100) && HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == 1)
{
key_time++;
HAL_Delay(10);
}
else
{
if(key_time >= 80)
*pKeyValue = 2 ; //长按返回2
else
*pKeyValue = 1 ; //短按返回1
key_time = 0;
key_status++;
}
/***********************************************************/
break;
case 2: //等待松开
if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == 0)
key_status = 0;
break;
default:
break;
}
}
void Key1_Scan(uint8_t *pKeyValue)
{
static char key_status = 0;
static int key_time = 0 ;
switch(key_status)
{
case 0: //检测.消抖
if(HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == 1)
key_status++;
break;
case 1: //确定
if((key_time < 100) && HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == 1)
{
key_time++;
HAL_Delay(10);
}
else
{
if(key_time >= 80)
*pKeyValue = 2 ; //长按返回2
else
*pKeyValue = 1 ; //短按返回1
key_time = 0;
key_status++;
}
/***********************************************************/
break;
case 2: //等待松开
if(HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == 0)
key_status = 0;
break;
default:
break;
}
}
我们来实现一个,当短按KEY0的时候,顺时针转,长按KEY0时,顺时针微调1°,当短按KEY1的时候,逆时针转,长按KEY1时,逆时针微调1°。
在这里,我们定义使用嘀嗒定时器来驱动电机转动。
我们在中断服务函数这个文件这里讲嘀嗒定期器产生的中断处理添加到这里,跳转进去发现这是个弱函数,也就是带__weak关键字修饰的,这并不是标准C的语法,而是拓展的,并没有实现。
加上了__weak 修饰符的函数,用户可以在用户文件中重新定义一个同名函数,最终编译器编译的时候,会选择用户定义的函数,如果用户没有重新定义这个函数,那么编译器就会执行__weak 声明的函数,并且编译器不会报错。所以我们可以在别的地方定义一个相同名字的函数,而不必也尽量不要修改之前的函数。
接下来我们定义个跟这个弱函数一模一样的处理函数,实现它。
main.c
/* USER CODE BEGIN 4 */
void HAL_SYSTICK_Callback(void)
{
static uint8_t step = 0 ;
//如果当前设置的脉冲数不为0
if(Motor_Pulse)
{
//控制步进电机旋转
MOTOR_CONTROLD(step, direction);
step++ ;
if(8 == step)
step = 0 ;
//脉冲计数不断减,当减到0时,说明控制到位,即需要执行else的关闭电机步骤。
Motor_Pulse-- ;
}
else
{
//关闭电机
CLOSE_MOTOR();
}
}
在main.c中,也别忘了定义串口的发送函数噢。
#include
//定义printf的重定向函数fputc,满足串口调试打印
int fputc(int ch, FILE* file)
{
return HAL_UART_Transmit(&huart2, (uint8_t*)&ch, 1, 100);
}
接下来实现主程序逻辑:
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
//角度微调
int Angle_adjustment = 0 ;
int count_angle = 0;
int count_pulse = 0 ;
uint8_t key0 = 0, key1 = 0;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
//设置按键一次步进的角度为45度
count_angle = 45 ;
//角度微调度数设置为1
Angle_adjustment = 1 ;
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
Key0_Scan(&key0);
Key1_Scan(&key1);
//控制正转
if(1 == key0)
{
direction = 0 ;
count_pulse = Motor_Angle_Cal(count_angle);
printf("旋转角度:%d度====>脉冲数:%d\n",count_angle,count_pulse);
key0 = 0 ;
}
//正转微调
else if(2 == key0)
{
direction = 0 ;
count_pulse = Motor_Angle_Cal(Angle_adjustment);
printf("微调角度:%d度====>脉冲数:%d\n",Angle_adjustment,count_pulse);
key0 = 0 ;
}
//控制反转
if(1 == key1)
{
direction = 1 ;
count_pulse = Motor_Angle_Cal(count_angle);
printf("旋转角度:%d度====>脉冲数:%d\n",-count_angle,count_pulse);
key1 = 0 ;
}
//反转微调
else if(2 == key1)
{
direction = 1 ;
count_pulse = Motor_Angle_Cal(Angle_adjustment);
printf("微调角度:%d度====>脉冲数:%d\n",Angle_adjustment,count_pulse);
key1 = 0 ;
}
}
/* USER CODE END 3 */
}
运行结果:
与预期相符合。
源码工程链接:
链接:https://pan.baidu.com/s/11pvYa1HhJKa0-5P9fzIs-g
提取码:9dog
留个小项目给大家来实现:
如图所示,这是电热壶,通过触摸按键可以设置功能,其中加水的转轴可以用步进电机来实现,抽水可以用淘宝买的抽气泵来实现,至于其它的,可以搜索相应的模块来做。