51单片机步进电机角度控制详解(免费提供代码+仿真)

news/2024/11/22 16:01:10/

51单片机步进电机控制详解

目录

  • 51单片机步进电机控制详解
    • 一、步进电机基本介绍
      • 1. 步进电机结构
      • 2. 步进电机驱动
    • 二、硬件&仿真设计
      • 0.设计要求
      • 1. 硬件设计
        • 显示模块
        • 输入模块
        • 步进电机模块
      • 2. 仿真全图一览
      • 3.PCB设计
    • 三、软件设计
      • 1. 显示模块
        • LCD1602.h
        • LCD1602.c
      • 2. 输入模块
        • Key.h
        • Key.c
      • 3. 步进电机模块
        • Motor.h
        • Motor.c
      • 4. 数据整合
        • Includes.h
        • Communal.h
        • Communal.c
      • 5.主函数
        • main.c
        • 主界面
        • 最近角度设置界面
        • 标定角度设置界面
        • 最近角度调整界面
        • 标定角度调整界面
        • 设置界面
    • 四、工程下载

一、步进电机基本介绍

我个人认为,步进电机的基本原理和介绍看看其他博主的介绍就好了。我比较希望讲一下我对步进电机的关于自己一种理解方式,可能与真正步进电机的原理差的有点大。下面还是给一下我推荐的一些博主对步进电机的介绍文章。
百度步进电机链接
步进电机驱动及原理—star-air

步进电机,把名字扩展一下就是按“步”前进的电机,这里的“步”,我认为既可以解释为“脚步”,也可以解释为”步骤“。脚步就指像人一样,无论速度多快,每次只能跨一步,步进电机也是如此,无论你通电时间多长,只要脉冲不发生变化,步进电机也只走一步。而”步骤“,就当行走方式,人的行走方式行走方式是交替向前的,步进电机也一样,它的脉冲方式按照一定规律运行的。
对了,还有一个很重要的一点,这里的脉冲方式和PWM(脉宽调制)是不一样的,这里的脉冲我认为其实是对51单片机的IO口电平规律变换的频率。

1. 步进电机结构

我们这次使用的是28BYJ-48 5V DC这个型号的步进电机(实物图如下方图),所以我们的机构介绍主要针对此步进电机,若未来有更多扩展再加入更多介绍。
28BYJ-48 5V DC步进电机实物图

28BYJ-48 5V DC步进电机是五线四相直流驱动步进电机,运转过程中电流在0.3A~0.4A(个人测量,数据未必准确),它的一些驱动参数如下:

数据名参数
直径28mm
电压5V
步进角度5.625 × 1 / 64
减速比1 / 64
单个重0.04kg

在此处,我们需要注意的主要为电压与步进角度,它是由5V电压驱动,步进角度为5.625×1/64,这里给的5.625×1/64代表它每次脉冲转动的角度是5.625÷64=0.087890625°,而不是单纯的5.625度,这一点比较容易理解错,一个不注意写出来的程序就是错误的。下面,是28BYJ-48 5V DC的接线示意图和设计图。
28BYJ-48 5V DC步进电机接线示意图和设计图

这是我购买的步进电机的结构示意图,可能和各位的有所区别,请各位以实物为准,如果各位要设计PCB板且要把步进电机装进去,就需要对步进电机的主要结构有了解,不然必要性就不是很大了。

关于接线示意图,也就是步进电机的内部接线图,是我们针对仿真时和具体电路设计需要的,所以还是比较重要的。这里的接线介绍我推荐和仿真一起看(主要是仿真的运行),很容易就理解了,具体的运行会在后面介绍。下面大概说一下接线。

名称接线
蓝1控制线1
粉2控制线2
黄3控制线3
橙4控制线4
红55V VCC或GND(本次使用时VCC)

2. 步进电机驱动

ULN2003驱动文章推荐:【常用芯片】ULN2003工作原理及中文资料(实例:STM32驱动28BYJ48步进电机)

前面说到,步进电机运转过程中电路在0.3A~0.4A之间,而我们的51单片机拉电流1mA,灌电流10mA,所以对我们51单片而言,直接驱动步进电机是不现实的,所以我们需要加一个能承受大电流的中介。根据我们学习时用的开发板关于驱动步进电机所使用模块的是ULN2003芯片,这个芯片能为我们承受大电流,为了便于测试,我也使用了此模块。下面是这个模块的逻辑图和实物图。
ULN2003逻辑图
ULN2003实物图

ULN2003其实相当于7个开关,每个开关的控制端(1~7B)由单片机控制,控制端为高电平(>2.5V)开关接地,低电平时接高电平,就是接了一个取反的电路。介于这种情况,为了方便我们控制步进电机的时候,51单片机IO口高电平时即为通电,所以我们步进电机的红色5号线接VCC(比如在端口1B为高电平,输出端口1C就为低电平,而红色5号线为VCC,1C与步进电机控制线相连,相连后形成电势差,电流导通)。下面时ULN2003的接线。

名称接线
1B~7B控制端口1~7
1C~7C输出端口1~7(输入输出口相对应)
E接地
COM接5V高电平

二、硬件&仿真设计

0.设计要求

本次步进电机设计要求为能显示和控制步进电机具体转动角度,能显示和控制电机正转和反转

1. 硬件设计

针对此设计要求,我们需要显示模块控制输入模块步进电机模块

显示模块

LCD1602文章推荐: 快速掌握——LCD1602液晶显示(多组实验,附带源程序)

鉴于要显示正转和反转,如果使用数码管作为显示器,其显示效果是不行的,所以显示模块我使用的是LCD1602LCD1602中16指16每行支持显示16个字符,02指有两行。在仿真中为LM016L。接下来说一下介绍LCD1602的具体引脚功能,具体仿真图与实物图如下:
LCD1602仿真图
LCD1602实物图

  1. VSS:这里的VSS可以直接理解为给LCD1602的GND。没有什么可以介绍的。
  2. VDD:就是我们说的VCC高电平,这里的高电平接5V即可。
  3. V0:每个字符显示位的对比度调整,电压越高对比度越低。我们一般会在此处接一个可调电阻,用于调节对比度。
  4. RS:指令、数据选择,低电平时系统判定D0~D7输入为指令,高电平时判断输入为数据。
  5. RW:R/W为读/写信号线,高电平时进行读操作,低电平时进行写操作。
  • 当RS和R/W共同为低电平时可以写入指令或显示地址;
  • 当RS为低电平,R/W为高电平时,可以读忙信号;
  • 当 RS为高电平,R/W为低电平时,可以写入数据。
  1. E:使能端,当端口E出现下降沿时,LCD1602执行指令。
  2. D0~D7:D0~D7为8位双向数据线。
  3. A:背光源正极。
  4. K:背光源负极。

LCD1602的实物与仿真相比,仿真缺少A,K两个端口。除此之外,其他是完全一样的,在实际接线中,我们只需要记得给A,K接上5V和GND就行了。通过对上面LCD1602的了解,我们现在需要对LCD1602正式接线了。具体接线方式我打算如此接线:

端口接线
VSS接GND
VDD接5V VCC
V0接10kΩ滑动电阻(最后因为10k的没找着,将就接了一个1k的)
RS接控制线P2.0
RE接控制线P2.1
E接控制线P2.2
D0~D7接数据传输线P0
A接5V VCC
K接GND

仿真(不知道为啥,LCD1602仿真显示成这样)如下图所示:
LCD1602接线仿真

输入模块

控制输入模块采用16(4×4)键键盘。这种键盘其实是用的很多的,没啥可以介绍的,我比较推荐的是相关输入模块使用自己购买的输入模块,相关代码可以直接替换。我当时做的时候犯了一点傻,我先设计的仿真,再买的模块,相关模块差点没找到,不过最后又设计了PCB这些就没有影响了。下面是模块的具体样式和仿真图:
16键键盘具体图样
16键键盘仿真图
唯一比较注意的是我们虽然实际使用的是微动开关,但在做模拟的时候还是采用的普通的按钮(仿真名称button)具体的功能就不多说了,直接上实物接线表。

端口接线
C4P1.0
C3P1.1
C2P1.2
C1P1.3
R1P1.4
R2P1.5
R3P1.6
R4P1.7

步进电机模块

步进电机模块就型号为28BYJ-48 5V DC的步进电机和ULN2003组合。这里的使用也没有什么可以多说的。直接上仿真图和接线(实物图在上面):
步进电机模块实物图
ULN2003接线

此处需要注意ULN2003芯片的每个端口的具体含义,可以参考前面的ULN2003逻辑框图,将实物的带缺角口与逻辑图带缺角口对起,芯片有字面对准自己,此时实物端口与对照逻辑框图一样。
其次购买的步进电机不同,可能颜色标注不同,以购买实物为准。
还有一点,因为步进电机在仿真中响应速度太慢,仿真中其实无法完全模拟步进电机。在仿真中跑出来的程序有问题。不过我已经在软件中为各位弥补了这一问题,具体的修改方式看后文的Includes.h中关于对宏_PROTEUS_的设定。

ULN2003端口接线
COM5V VCC
EGND
1B51单片机P3.0
2B51单片机P3.1
3B51单片机P3.2
4B51单片机P3.3
1C步进电机C1(蓝1)
2C步进电机C2(粉2)
3C步进电机C3(黄3)
4C步进电机C4(橙4)

2. 仿真全图一览

仿真全图一览

这里没有加51单片机的最小系统板的电路,使用的晶振频率为12MHz。

3.PCB设计

PCB设计网站:嘉立创EDA(可以白嫖PCB电路板)

这个PCB设计是我一时兴起做的,用的是嘉立创EDA(可以白嫖PCB板)。又因为我们做课程设计是由我们老师提供最小系统板,我们只需要在最小系统板上加外围电路。所以设计中我也是直接针对外围电路做的设计。下面是设计图、3D图和成品图:
PCB设计图
PCB3D图正面
PCB3D图背面
实物图

PS:忘买XH插座了,所以就用排针代替了。

三、软件设计

下面,根据每个模块做软件。

1. 显示模块

因为显示模块已经介绍了,为了方便各位更改端口,直接修改LCD1602.h中的RS、RW、E、LCDMsg的参数即可。还有需要注意的是,在LCD1602.c中有一个SendX的宏定义,里面的bitFlip在线没接错的情况下需要删除掉。

LCD1602.h

// LCD1602.h
#ifndef _LCD1602_H_
#define _LCD1602_H_
// 此文件所需头文件
#include <stdio.h>
#include <reg52.h>
// 关键字替换
#ifndef u8
#define u8 unsigned char
#endif
#ifndef u16
#define u16 unsigned int
#endif
/* 1602显示器 */
// P0做数据传输 P0.0~7 <-----> D0~D7
// P2做控制端口 P2.0~2 <-----> RS RW Esbit RS = P2^0; // 此处修改RS端口
sbit RW = P2^1; // 此处修改RW端口
sbit E = P2^2; // 此处修改E端口
#define LCDMsg P0 // 定义数据输出口#define WriteCo {RS = 0; RW = 0;} // 写入指令 / 显示地址
#define WriteDa {RS = 1; RW = 0;} // 写入数据
// 发送数据,注意如果线没接错就把下面的bitFlip(Msg)直接替换为Msg.这是我画PCB的时候出错设计的软件修补。
#define SendX(X, Msg) {Write##X; LCDMsg = bitFlip(Msg); E = 1; Delay3ms(); E = 0;}
// 1602命令
#define CL 0x01 // clear 清屏
#define RC 0x02// Rest Cursor 光标复位
#define SC(ID, Word) (0x04 | ID << 1 | Word) // Set Cursor光标设置,ID:光标移动0左1右,Word置1使文字移动
#define SW(D, C, B) (0x08 | D << 2 | C << 1 | B) // 显示设置(置1有效)D:屏幕显示 C:光标显示 B:光标闪烁
#define MC(SC, RL) (0x10 | SC << 3 | RL << 2) // SC:1动文字0动光标 RL:光标移动0左1右
#define SF(DL, N, F) (0x20 | DL << 4 | N << 3 | F << 2) // Set Function 功能设置 DL:1为4位总线,0为8位总线 N:0为单行显示,1为双行显示,F:0显示5X7的点阵字符,1显示5X10的显示字符
#define ST(T) (0x40 | (T & 0x3F)) // 设置字符表地址
#define SS(S) (0x80 | (S & 0x7F)) // 设置存储地址// LCD初始化
void LCD_Init();// 显示字符串
void LCD_ShowString(bit, u8, u8*);// 显示数字
void LCD_ShowNum(bit, u8, u8*, u16);// 显示浮点数
void LCD_ShowFloat(bit, u8, u8*, float);#endif

LCD1602.c

// LCD1602.c
#ifdef _INCLUDES_#include "Includes.h"#ifndef _LCD1602_H_#error "未加装LCD1602.h文件。"#endif
#else#include "LCD1602.h"
#endif// LCD初始化
void LCD_Init(){SendX(Co, SF(1, 1, 0)); // 4总线,双行显示,5X7SendX(Co, SW(1, 0, 0)); // 4总线,双行显示,5X7SendX(Co, SC(1, 0)); // 数据读写操作后,光标自动加一,画面不动SendX(Co, CL); // 清屏
}// 显示字符串
// 传参:行(0为第一行,1为第2行), 列,字符串。
void LCD_ShowString(bit Line, u8 Col, u8* Str){if (Line){SendX(Co, SS(Col | 0x40));} else {SendX(Co, SS(Col));}while(*Str != '\0'){SendX(Da, *(Str++));}
}// 显示整数
// 传参:行(0为第一行,1为第2行), 显示格式(和C语言printf中的相同),数字。
void LCD_ShowNum(bit Line, u8 Col, u8* Sta, u16 Num){u8 Mes[10];sprintf(Mes, Sta, Num);LCD_ShowString(Line, Col, Mes);
}// 显示浮点数
// 传参:行(0为第一行,1为第2行), 显示格式(和C语言printf中的相同),小数。
void LCD_ShowFloat(bit Line, u8 Col, u8* Sta, float Num){u8 Mes[10];sprintf(Mes, Sta, Num);LCD_ShowString(Line, Col, Mes);
}

2. 输入模块

Key.h

输入模块也比较简单,为了让各位能更好的修改数据,直接修改Key.h中的KEY就能直接按键修改连接位置。

// Key.h
#ifndef _KEY_H_
#define _KEY_H_
// 此文件所需头文件
#include <reg52.h>
// 关键字替换
#ifndef u8
#define u8 unsigned char
#endif
#ifndef u16
#define u16 unsigned int
#endif
/* 键盘设计 */
/*
P1 <---> 16键键盘
16键键盘步进1		2		3		删除4		5		6		确定7		8		9		取消正		0 		反		设置	*/
// 修改这里更改按键连接位置
#define KEY P1// 按键功能定义
#define NUM_1 0xE7
#define NUM_2 0xEB
#define NUM_3 0xED
#define DEL 0xEE
#define NUM_4 0xD7
#define NUM_5 0xDB
#define NUM_6 0xDD
#define ENTER 0xDE
#define NUM_7 0xB7
#define NUM_8 0xBB
#define NUM_9 0xBD
#define CANCEL 0xBE
#define CORRECT 0x77
#define NUM_0 0x7B
#define ANTI 0x7D
#define SET 0x7E#define UP NUM_2
#define RIGHT NUM_6
#define LEFT NUM_4
#define DOWN NUM_8
#define YES NUM_5// 按键读取, 返回参数:键盘按下位置,未检测到为 0
u8 GetKey(bit);#endif

Key.c

// Key.c
#ifdef _INCLUDES_#include "Includes.h"#ifndef _KEY_H_#error "未加装Key.h文件。"#endif
#else#include "Key.h"
#endif// 按键读取, 返回参数:键盘按下位置,未检测到为 0
// 传参Keep_Key为是否等待按键抬起1是,0否
u8 GetKey(bit Keep_Key){u8 i, j;KEY = 0xF0;Delay5ms();i = KEY;if(i == 0xF0){return 0;} else {Delay5ms();if(KEY == i){KEY = 0x0F;Delay1ms();j = KEY & 0x0F;if(j == 0x0F){return 0;} else {Delay5ms();if (j == KEY & 0x0F){if(Keep_Key){while(KEY & 0x0F != 0x0F) ;}return i | j;} else {return 0;}}}else{return 0;}}
}

3. 步进电机模块

步进电机模块的数据修改需要根据基础比例来修改,不然代码会出问题。而且因为51单片机无论float还是double类型,位数都只有32位,所以浮点数的精度不会很高,建议基础比例就在这一比例。可以增加,不建议再减少了。同时,当我们修改此基础比例后,我们需要修改后面的Includes.h中的Motor结构体的一部分元素的长度,具体长度后面会做详细介绍。当然,此文件也是支持修改接线的。修改MotorLine.h中的MotorLine即可,若要修改IO口的话需要更改Motor.c中的Motor_Data中的数据。同时还有一个关于_PROTEUS_的宏,此宏用于控制我们的Motor_Revolve函数是使用在仿真中还是实物中,因为一部分原因,这两者不互通,这一点需要注意。

Motor.h

// Motor.h
#ifndef _MOTOR_H_
#define _MOTOR_H_
// 此文件所需头文件
#include <reg52.h>
// 关键字替换
#ifndef u8
#define u8 unsigned char
#endif
#ifndef u16
#define u16 unsigned int
#endif//电机接线 P3.0 -> P3.4
#define MotorLine P3
// 基础数据
// 基础比例:8 数据设置要求:2的整数倍
#define DData 512 // 总转动量 数据设置要求 64 * 基础比例
#define DNum 8 // 旋转最低值 数据设置要求: 64 / 基础比例
#define NFundation 0.703125 // 基础转角 数据设置要求: 5.625 / 基础比例#define MotorNum 8 // 设定转动数据// 电机旋转
void Motor_Revolve(u8, u16, bit, bit);#endif

Motor.c

// Motor.c
#ifdef _INCLUDES_#include "Includes.h"#ifndef _MOTOR_H_#error "未加装Motor.h文件。"#endif
#else#include "Motor.h"#define Delay1ms() Delayms(12, 169)#define Delay5ms() Delayms(59, 90)void Delayms(u8 i, u8 j){do{while (--j);} while (--i);
}
#endif
u8 code Motor_Data[MotorNum] = {0x09, 0x01, 0x03, 0x02, 0x06, 0x04, 0x0C, 0x08};// 电机旋转
// 传参:起始角,旋转角,旋转方向,默认方向
void Motor_Revolve(u8 Start, u16 Num, bit Orientation, bit NOrien){
#ifndef _PROTEUS_Num *= DNum;
#endifif(NOrien && Start) Start = MotorNum - Start;if(Orientation){while(Num-- != 0){Start = (Start == 0) ? MotorNum - 1 : Start - 1;MotorLine = Motor_Data[Start];
#ifndef _PROTEUS_Delay1ms();
#elseDelay5ms();
#endif}} else {while(Num-- != 0){Start = (Start >= MotorNum - 1) ? 0 : Start + 1;MotorLine = Motor_Data[Start];
#ifndef _PROTEUS_Delay1ms();
#elseDelay5ms();
#endif}}
}

4. 数据整合

通过前面的函数,我们不难看出,我们使用了一个Includes.h的自定义头文件,这里的Includes.h除了要加入之外,还要在魔术棒当中进行设置才能完全加入,加入的方式如下:
_INCLUDES_加入方式

Includes.h头文件的内容如下,其中可以设置的内容有默认设置修改(DOrientation, DTurn_Zero, DAngle和DRotation),其中需要我们注意的是,里面有一个仿真设置宏_PROTEUS_,此宏用于管理产生的hex文件是用于仿真还是实物,注释掉此宏,程序将用于实物,不注释就用于仿真。

Includes.h

// Includes.h
#ifndef _INCLUDES_H_
#define _INCLUDES_H_// 系统头文件
#include <reg52.h>
#include <stdio.h>// 仿真设置,定义以下宏编译出的文件将能在仿真中无误运行
// #define _PROTEUS_// 公共部分
#include "Communal.h"
// 按键部分
#include "Key.h"
// LCD部分
#include "LCD1602.h"
// 步进电机部分
#include "Motor.h"// 默认设置
#define DOrientation 1
#define DTurn_Zero 1
#define DAngle 0
#define DRotation 1// 设置信息保存
typedef struct Motor{u8 Orientation : 1;		// 方向设置,正(Correct)1、反(Anti)0u8 Turn_Zero : 1;		// 转向置零,是(Yes)1、否(No)0u8 CH : 1;				// 正负号输入设置 CH和CHH是用于节省内容空间设置的,放弃原bit位u8 CHH : 1;				// 正负号输入返回设置u8 : 4;					// 对齐空位u16 Angle : 9;			// 旋转角度基础值 长度设置要求: log(2, Motor.h中的DData的值)u16 Rotation : 9;		// 单次旋转角度设置 长度设置要求:
} Motor;#define ShowFloat(LINE, COL, NUM) LCD_ShowFloat(LINE, COL, "%7.3f", NUM * NFundation)
#define ShowNum(COL, NUM) LCD_ShowNum(0, COL, "%3d", NUM)
#define ShowString(LINE, COL, STR) LCD_ShowString(LINE, COL, STR)
#define Revolve(Orien, Num) Motor_Revolve(Setting.Angle % MotorNum, Num, Orien, Setting.Orientation)#endif

这个头文件是专门针对我们设计的文件所制作的。里面有一个Motor的struct结构体定义,里面包含了我们所设置的功能,而且为了简化代码且实现循环增加,如果我们要修改步进电机的基础值,还需要修改这里的值,修改后代码才能正常运行。修改要求为Motor.h中DData关于2的对数的值
文件中还加了公共部分的代码。公共部分的代码如下:

Communal.h

// Communal.h
/* 公共部分 */
#ifndef _COMMUNAL_H_
#define _COMMUNAL_H_// 关键字替换
#ifndef u8
#define u8 unsigned char
#endif
#ifndef u16
#define u16 unsigned int
#endif// 常见延时表 --- 12MHz
#define Delay1ms() Delayms(12, 169)
#define Delay3ms() Delayms(36, 1)
#define Delay5ms() Delayms(59, 90)
#define Delay10ms() Delayms(117, 184)
#define Delay20ms() Delayms(234,115)// 基本延时函数
void Delayms(u8, u8);// 二进制数据反向
u8 bitFlip(u8);
#endif

Communal.c

// Communal.c
#ifdef _INCLUDES_#include "Includes.h"#ifndef _COMMUNAL_H_#error "未加装Communal.h文件。"#endif
#else#include "Communal.h"
#endif#ifdef _INCLUDES_#pragma message("已打开_INCLUDES_,此工程包含Includes.h.")
#endif
#ifdef _PROTEUS_#pragma message("已打开_PROTEUS_,编译后hex文件需用于Proteus仿真中.");
#endif// 基本延时函数
void Delayms(u8 i, u8 j){do{while (--j);} while (--i);
}// 二进制数据反向
// 传参:待反转数据
u8 bitFlip(u8 Date){u8 ret;u8 i;for(i = 0; i < 8; i++){ret <<= 1;ret += Date & 0x01;Date >>= 1;}return ret;
}

本来这个文件的作用只是加Delay的相关函数的。然后因为我PCB设计出了失误(手动捂脸),把D0~D7的数据端口画颠倒了,所以加了一个二进制数据翻转bitFlip的代码。

5.主函数

此次设计的主要核心就是main.c,代码其实比较简单,就不解释了,直接上代码然后再做介绍:

main.c

// main.c
#include "Includes.h"Motor Setting = {DOrientation, DTurn_Zero, 0, 1, DAngle, DRotation};// 获取数字
u16 GetNum(u16 Data, u8 *showMes){u8 num = 0;SendX(Co, CL);ShowString(0, 0, showMes);Setting.CHH = 1;if(Setting.CH) ShowString(0 ,11, "+");ShowString(1, 0, "*");ShowFloat(1, 1, 1);ShowString(1, 8, "=");ShowFloat(1, 9, Data);SendX(Co, SW(1, 1, 1));ShowNum(12, Data);while(1){switch(GetKey(1)){case NUM_9:num++;case NUM_8:num++;case NUM_7:num++;case NUM_6:num++;case NUM_5:num++;case NUM_4:num++;case NUM_3:num++;case NUM_2:num++;case NUM_1:num++;case NUM_0:Data = Data * 10 + num;num = 0;if(Data > DData){Data = DData;}ShowFloat(1, 9, Data);ShowNum(12, Data);break;case CORRECT:if(Setting.CH){if(Setting.CHH == 1) ShowString(0, 11, "-");else ShowString(0, 11, "+");SendX(Co, SS(15));Setting.CHH = !Setting.CHH;break;}case ANTI:if(Setting.CH){if(Setting.CHH == 1) ShowString(0, 11, "-");else ShowString(0, 11, "+");SendX(Co, SS(15));Setting.CHH = !Setting.CHH;break;}case ENTER:case SET:if(Setting.CH) return Data == 0 ? 0xFFFF : Data;else return Data;case CANCEL:return 0xFFFF;case DEL:Data /= 10;ShowFloat(1, 9, Data);ShowNum(12, Data);}}
}// 输入角度自匹配
u16 GetAngle(){float Data = 0;u8 i, Point = 0;u16 NearNum = 0;float GetNum = 0;SendX(Co, CL);ShowString(0, 0, "Set");Setting.CHH = 1;if(Setting.CH) ShowString(0, 7, "+");ShowString(1, 0, "Angle");ShowFloat(1, 9, NearNum);LCD_ShowFloat(0, 8, "%7g", Data);SendX(Co, SW(1, 1, 1));while(1){switch(GetKey(1)){case NUM_9:GetNum++;case NUM_8:GetNum++;case NUM_7:GetNum++;case NUM_6:GetNum++;case NUM_5:GetNum++;case NUM_4:GetNum++;case NUM_3:GetNum++;case NUM_2:GetNum++;case NUM_1:GetNum++;case NUM_0:if(Point == 0){Data *= 10;Data += GetNum;} else if(Point <= 3) {for(i = 0; i < Point; i ++){GetNum /= 10;}Point += 1;Data += GetNum;} else break;if(Data > 360){Data = 360;}NearNum = (u16)(Data / NFundation + 0.5);ShowFloat(1, 9, NearNum);LCD_ShowFloat(0, 8, "%7g", Data);GetNum = 0;break;case CORRECT:if(Setting.CH){if(Setting.CHH == 1) ShowString(0, 7, "-");else ShowString(0, 7, "+");SendX(Co, SS(15));Setting.CHH = !Setting.CHH;break;}case ANTI:if(Point == 0){ShowString(0, 6, ".");SendX(Co, SS(15));Point = 1;} else if(Point == 1){ShowString(0, 6, " ");SendX(Co, SS(15));Point = 0;}break;case ENTER:case SET:if(Setting.CH) return NearNum == 0 ? 0xFFFF : NearNum;else return NearNum;case CANCEL:return 0xFFFF;case DEL:if(Point == 0){Data = (u16)Data / 10;} else if(Point == 1){ShowString(0, 6, " ");SendX(Co, SS(15));Point = 0;break;} else {for(i = 0; i < Point - 1; i++){Data *= 10;}Data = (u16)(Data/10);for(i = 0; i < Point - 2; i++){Data /= 10.0;}Point -= 1;}NearNum = (u16)(Data / NFundation + 0.5);ShowFloat(1, 9, NearNum);LCD_ShowFloat(0, 8, "%7g", Data);break;}}
}// 设置
void Motor_Set(){extern Motor Setting;// 转向置零,背景灯,单次旋转角度,重置基准旋转角,重置u8 code msg[5][6] = {"Turn ", "Pause", "RBase", "RSet "};u8 Key, ch = 0;SendX(Co, CL);ShowString(0, 0, "Other Setting");ShowString(1, 0, msg[0]);if(Setting.Turn_Zero) ShowString(1, 13, "Yes");else ShowString(1, 13, " NO");while(1){Key = GetKey(1);switch(Key){case UP:case LEFT:if(!ch) ch = 3;else ch--;goto Moto_Set_1;case DOWN:case RIGHT:if(ch == 3) ch = 0;else ch++;
Moto_Set_1:ShowString(1, 0, msg[ch]);switch(ch){case 0:if(Setting.Turn_Zero) ShowString(1, 9, "    YES");else ShowString(1, 9, "     NO");break;case 1:ShowFloat(1, 9, Setting.Rotation);break;case 2:ShowString(1, 9, "       ");break;case 3:ShowString(1, 13, "   ");}break;case YES:case ENTER:case SET:switch(ch){case 0:Setting.Turn_Zero = !Setting.Turn_Zero;if(Setting.Turn_Zero) ShowString(1, 13, "YES");else ShowString(1, 13, " NO");break;break;case 1:Key = GetNum(Setting.Rotation, msg[1]);if(Key == 0xFFFF) Setting.Rotation = 1;else Setting.Rotation = Key;SendX(Co, CL);ShowString(0, 0, "Other Setting");ShowString(1, 0, msg[ch]);ShowFloat(1, 9, Setting.Rotation);break;case 2:Setting.Angle = 0;return;case 3:if(Setting.Angle > DData / 2) Revolve(Setting.Orientation, DData - Setting.Angle);else Revolve(!Setting.Orientation, Setting.Angle);Setting.Orientation = DOrientation;Setting.Angle = DAngle;Setting.Turn_Zero = DTurn_Zero;Setting.Rotation = DRotation;return;}break;case CANCEL:case DEL:case CORRECT:case ANTI:return;}}
}// 主界面显示
void MainShow(){SendX(Co, SW(1, 0, 0));SendX(Co, CL);ShowString(0, 0, "Angle:");ShowFloat(0, 9, Setting.Angle);ShowString(1, 0, "Dirction:");if(Setting.Orientation)ShowString(1, 9, "Correct");elseShowString(1, 9, "   Anti");
}// 主函数
void main(){u16 Key;LCD_Init();MainShow();while(1){switch(GetKey(0)){case UP:case RIGHT: // 上/右Revolve(Setting.Orientation, Setting.Rotation);Setting.Angle += Setting.Rotation;ShowFloat(0, 9, Setting.Angle);break;case LEFT:case DOWN: // 左/下Revolve(!Setting.Orientation, Setting.Rotation);Setting.Angle -= Setting.Rotation;ShowFloat(0, 9, Setting.Angle);break;case CORRECT: // 正方向case ANTI: // 反方向 更改:方向切换if(!Setting.Orientation){ShowString(1, 9, "Correct");if(Setting.Turn_Zero){Setting.Angle = DData - Setting.Angle;ShowFloat(0, 9, Setting.Angle);} else {if(Setting.Angle > DData / 4)Revolve(Setting.Orientation, DData - Setting.Angle * 2);elseRevolve(!Setting.Orientation, Setting.Angle * 2);}Setting.Orientation = 1;} else {ShowString(1, 9, "   Anti");if(Setting.Turn_Zero){Setting.Angle = DData - Setting.Angle;ShowFloat(0, 9, Setting.Angle);} else {if(Setting.Angle > DData / 4)Revolve(Setting.Orientation, DData - Setting.Angle * 2);elseRevolve(!Setting.Orientation, Setting.Angle * 2);}Setting.Orientation = 0;}break;case NUM_0: // 累加Setting.CH = 1;case YES:case ENTER: // 确定/回车Key = GetNum(0, "Angle");goto Adjustment;break;case NUM_7: // 角度输入case NUM_9:Setting.CH = 1;case NUM_1:case NUM_3:Key = GetAngle();
Adjustment:if(Setting.CH){if(Key != 0xFFFF){Revolve(Setting.CHH == 1 ? Setting.Orientation : !Setting.Orientation, Key);if(Setting.CHH){Setting.Angle += Key;}else{Setting.Angle -= Key;}}Setting.CH = 0;} else {if(Key != 0xFFFF){if(Key > Setting.Angle){if (Key - Setting.Angle > DData / 2)Revolve(!Setting.Orientation, DData - Key + Setting.Angle);elseRevolve(Setting.Orientation, Key - Setting.Angle);} else {if(Setting.Angle - Key > DData / 2)Revolve(Setting.Orientation, DData- Setting.Angle + Key);elseRevolve(!Setting.Orientation, Setting.Angle - Key);}Setting.Angle = Key == DData ? 0 : Key;}}MainShow();break;case SET: // 设置Motor_Set();MainShow();break;case CANCEL: // 角度清零case DEL:Setting.Angle = 0;ShowFloat(0, 9, Setting.Angle);break;}}
}

此次设计,主要针对步进电机的转动设置,我设计了几大界面,主界面、标定角度设置界面、标定角度设置调整、最近角度设置、最近角度调整以及其他设置功能,设计根据按键进行介绍,按键的功能将直接以表格的形式呈现,后期详解该功能将以坐标写出,比如第3行第4列的按钮坐标为(3, 4)。

主界面

主界面
主界面下,各按键的功能如下

按键第1列第2列第3列第4列
第1行最近角度设置当前角度+单次旋转角度最近角度设置角度清零
第2行当前角度-单次旋转角度标定角度设置当前角度+单次旋转角度标定角度设置
第3行最近角度调整当前角度-单次旋转角度最近角度调整角度清零
第4行旋转方向反转标定角度调整旋转方向反转设置键

各功能介绍:

  • 最近角度设置:根据用户输入指定角度,系统自动调整到旋转到离此角度最近的角度。
  • 当前角度+单次旋转角度:当按下指定按键后,根据我们所设置的旋转方向旋转标定旋转角度。标定旋转角度值为Setting.Rotation中设置。
  • 角度清零:将Setting.Angle的值清零,即将当前的角度作为默认角度,可用于校正步进电机的位置。
  • 当前角度-单次旋转角度:当按下指定按键后,根据我们所设置的旋转方向的反方向旋转标定旋转角度。标定旋转角度值为Setting.Rotation中设置。
  • 标定角度设置:此模式下输入值将直接与NFundation相乘,即旋转指定量的默认角度值。
  • 最近角度调整:与最近角度设置基本相同,唯一的区别是设置的角度将根据当前角度增加或减少指定角度。
  • 旋转方向反转:修改默认旋转方向,对角度增加或减少的功能有效,其次可以在设置中调整方向反转后是角度调整还是步进电机调整。
  • 标定角度调整:与标定角度设置基本相同,唯一的区别是设置的角度将根据当前角度增加或减少指定角度。
  • 设置键:进入设置功能。

除了功能当前角度±单次旋转角度角度转换/清零没有更多界面外。其他功能都有独立界面。下面一一介绍其界面和功能键。

最近角度设置界面

最近角度设置界面
最近角度设置界面下,各按键的功能如下

按键第1列第2列第3列第4列
第1行输入1输入2输入3退格Del
第2行输入4输入5输入6确定
第3行输入7输入8输入9取消
第4行小数点输入0小数点设置

此界面第一行会显示你设置的角度,第二行会显示具体旋转的角度,在点击小数点后,可输入小数点后的数。最高输入三位小数+三位整数+小数点位。
此界面设置键同确认键。最大数固定为360.000,再大无法增加。

标定角度设置界面

标定角度设置界面
标定角度设置界面下,各按键的功能如下

按键第1列第2列第3列第4列
第1行输入1输入2输入3退格Del
第2行输入4输入5输入6确定
第3行输入7输入8输入9取消
第4行确定输入0确定设置

此界面第一行会显示你设置的基值,第二行会显示乘以Motor.h中宏定义的DFundation后具体设置的角度,只能输入整数。
此界面设置键同确认键。最大数固定为Motor.h中宏定义的DData,带自动调整功能。

最近角度调整界面

最近角度调整界面
最近角度调整界面下,各按键的功能如下

按键第1列第2列第3列第4列
第1行输入1输入2输入3退格Del
第2行输入4输入5输入6确定
第3行输入7输入8输入9取消
第4行正负方向选择输入0小数点设置

此界面和最近角度设置界面类似,唯一多的是前方的+/-号,+号表示沿当前设置方向旋转设置角度,-号表示沿当前设置方向的反方向旋转设置角度。输入上将按钮(4,1)修改为正负方向选择。

标定角度调整界面

标定角度调整界面
标定角度调整界面下,各按键的功能如下

按键第1列第2列第3列第4列
第1行输入1输入2输入3退格Del
第2行输入4输入5输入6确定
第3行输入7输入8输入9取消
第4行正负方向选择输入0正负方向选择设置

此界面和标定角度设置界面类似,唯一多的是前方的+/-号,+号表示沿当前设置方向旋转设置角度,-号表示沿当前设置方向的反方向旋转设置角度。输入上将按钮(4,1)和按钮(4,3)修改为正负方向选择。

设置界面

设置界面下有TurnPauseRBaseRSet几个功能。Pause下有其他界面,其他设置界面差不多,设置界面如下图:
设置界面Turn
设置界面下,各按键的功能如下

按键第1列第2列第3列第4列
第1行无功能上一个无功能退格Del
第2行上一个切换/设置下一个切换/设置
第3行无功能下一个无功能退出
第4行退出无功能退出切换/设置

输入中能进入更多设置界面的进入更多界面,否者为切换模式。

  • Turn设置转向后的操作,设置为Yes时,进行换算角度,不转动电机。设置为No时转动电机,不切换角度。
  • Pause设置单次旋转角度,即Setting.Rotation,可按按钮(2,2)、(4, 2)、(4,4)进入数据设置界面,界面如下:
    Pause设置界面
    此界面按钮与标定角度设置按钮模式相同,当此值设定为0时,功能当前角度±单次旋转角度无效。
  • RBase功能同角度清零。
  • RSet将角度与设置恢复为默认设定值,此处的恢复无法恢复角度清零产生的影响。

四、工程下载

下面,是喜闻乐见的工程代码,提供CSDN下载链接和百度的下载链接。

文件为此文章的附加资源,若无在CSDN下载的意向,可以通过百度网盘下载。
备注:此文件只包含程序和仿真,无PCB制作图,因学校设计要求,设计的PCB只有外围电路,参考价值不大。同时也无模块购买链接,需自行购买或找我要也可。程序报错可私信我共同解决(PS:不经常看CSDN私信)。

CSDN下载链接:51单片机角度控制(包含程序+仿真)
百度下载链接:51单片机角度控制(包含程序+仿真) 提取码yadu


http://www.ppmy.cn/news/189703.html

相关文章

步步高面试真题

步步高面试真题&#xff1a; 假设有一个A界面&#xff0c;通过A在同一个栈里面启动了B界面&#xff0c;在B界面点击返回键&#xff0c;返回到A&#xff0c;此时从点击返回键开始&#xff0c;A和B分别经历了哪一些生命周期&#xff0c;按顺序说出来&#xff1f;&#xff08;下面…

STC单片机控制28BYJ-48步进电机

STC单片机4*4按键控制步进电机旋转 28BYJ-48型步进电机说明四相永磁式的含义28BYJ-48工作原理让电机转起来最简单的电机转动程序电机转速缓慢的原因分析便于控制转过圈数的改进程序利用中断编写实用性程序用4*4矩阵按键控制28BYJ-48步进电机 28BYJ-48型步进电机说明 步进电机分…

步步高的笔试题

JDK 8 的一些特性 UDP传递的包的大小受MIU的限制吗 锁 clone, 和深克隆 Redis支持的数据结构&#xff0c; Redis的命令 服务器攻击 NIO, IO mysql 锁 线程 https 作用 MQ 消息队列放在哪里 HTTP协议 反射 Tomcat的调优参数 对称加密算法 UDP 传输层 Java 8GC 异…

【51毕业设计案例】【009】语音体重电子秤(带上限报警)-基于51单片机

自我介绍&#xff1a; 可接私人定制单片机设计定做(B设、LW)&#xff0c;包答疑&#xff0c;售后无忧 需要请联系WX&#xff1a;code_51_32 QQ&#xff1a;3393617216 功能介绍&#xff1a; 测量体重显示&#xff0c;并语音播报重量 设置重量上限&#xff0c;超过报警 在没…

中兴B863AV3.2-M_专用线刷刷机固件包及教程(线刷后不再需要卡刷)

中兴B863AV3.2-M_专用线刷刷机固件包及教程&#xff08;线刷后不再需要卡刷&#xff09; 适用型号&#xff1a;B863AV3.2-M&#xff08;USB双公线线刷&#xff0c;只需一次线刷&#xff0c;不再需要线刷后再卡刷&#xff09; 系统版本&#xff1a;Android9 蓝牙语音&#xf…

51单片机设计多功能电子秤(实训项目)

一.硬件准备 1.压力传感器&#xff08;HX711称重模块&#xff09; 2.接线说明 如上图接线所示&#xff1a;称重模块HX711模块各个引脚与单片机引脚对应如下&#xff1a; (1).VCC——>VCC (2).GND——>GND (3).SCLK——>SDA (4).DT——>SCLK 注&#xff1a;引脚一定…

智能家居:步进电机驱动TB67H450FNG

直流有刷电机驱动IC&#xff1a;TB67H450FNG电机驱动器 近几年由于科技的进步和网络的普及&#xff0c;智能家居产业得到快速发展&#xff0c;智能家居产品给人们的生活品质带来显著提高&#xff0c;现代人也越来越依赖于只能家居。 在追求产品功能的同时&#xff0c;人们也越…

【STM32】步进电机及其驱动(ULN2003驱动28BYJ-48丨按键控制电机旋转)

本篇文章包含的内容 一、步进电机的结构和工作原理1.1 步进控制系统的组成1.2 步进电机简介1.3 步进电机的分类1.4 步进电机的工作原理1.4.1 单极性步进电机&#xff08;5线4相&#xff09;1.4.2 双极性步进电机&#xff08;4线2相&#xff09;1.4.3 细分器驱动原理 1.5 步进电…