28BYJ-48型步进电机说明

步进电机分为反应式、永磁式和混合式三种
我们在这里只讲解28BYJ-48型步进电机的具体含义:
28–步进电机最大有效外径为28毫米
B–表示是步进电机
Y–表示是永磁式
J–表示是减速型
48–表示是4相8拍

供电电压 相数 相电阻 步进角度 减速比 启动频率P.P.S 转矩 噪声db 绝缘介电强度
5V 4 50±10% 5.65/64 1:64 ≥500 ≥300 ≤35 600VAC

四相永磁式的含义

28BYJ-48步进电机中,里圈含有6个永磁齿的结构叫转子。

外圈与电机外壳固定,共有8个齿,每个齿上缠绕了线圈绕组,正对的两个齿上的绕组串联在一起。
也就是说正对的两个绕组将会同时导通或关闭,因此称为四相。

28BYJ-48工作原理

假定电机的起始状态就如图所示,逆时针方向转动,起始时是 B 相绕组的开关闭合,
B 相绕组导通,那么导通电流就会在正上和正下两个定子齿上产生磁性,这两个定子齿上的
磁性就会对转子上的 0 和 3 号齿产生最强的吸引力,就会如图所示的那样,转子的 0 号齿在
正上、3 号齿在正下而处于平衡状态;此时我们会发现,转子的 1 号齿与右上的定子齿也就
是 C 相的一个绕组呈现一个很小的夹角,2 号齿与右边的定子齿也就是 D 相绕组呈现一个稍
微大一点的夹角,很明显这个夹角是 1 号齿和 C 绕组夹角的 2 倍,同理,左侧的情况也是一
样的。
接下来,我们把 B 相绕组断开,而使 C 相绕组导通,那么很明显,右上的定子齿将对转
子 1 号齿产生最大的吸引力,而左下的定子齿将对转子 4 号齿,产生最大的吸引力,在这个
吸引力的作用下,转子 1、4 号齿将对齐到右上和左下的定子齿上而保持平衡,如此,转子
就转过了起始状态时 1 号齿和 C 相绕组那个夹角的角度。
再接下来,断开 C 相绕组,导通 D 相绕组,过程与上述的情况完全相同,最终将使转子
2、5 号齿与定子 D 相绕组对齐,转子又转过了上述同样的角度。
那么很明显,当 A 相绕组再次导通,即完成一个 B-C-D-A 的四节拍操作后,转子的 0、
3 号齿将由原来的对齐到上下 2 个定子齿,而变为了对齐到左上和右下的两个定子齿上,即
转子转过了一个定子齿的角度。依此类推,再来一个四节拍,转子就将再转过一个齿的角度,
8 个四节拍以后转子将转过完整的一圈,而其中单个节拍使转子转过的角度就很容易计算出
来了,即 360 度/(8*4)=11.25度,这个值被称为步进角度。
还有一种更有性能的工作模式,在单四拍的每两个节拍中插入一个双绕组到通的中间节拍,组成八拍模式。这样会使得电机的整体扭力输出增加,更有劲。

让电机转起来


步进电机一共有5根引线,其中红色为公共端,连接到5V电源,接下来橙黄粉蓝对应ABCD相,如果要导通A相绕组,秩序将橙色线接地即可。以此类推得出八拍模式绕组控制顺序表。

最简单的电机转动程序

该步进电机启动频率为550hz,因此我们只需要控制节拍刷新时间大于1.8ms即可。

#include<reg52.h>
unsigned char code BeatCode ={ //每个节拍对应I/O口的控制代码
0x0E,0x0C,0x0D,0x09,0x0B,0x03,0x07,0x06
};
void delay();
void main()
{
unsigned char tmp; //暂存P1IO口数据
unsigned char i = 0; //节拍输出索引
tmp = P1;
tmp = tmp & 0xF0; //清零P1口低4位
tmp = tmp | BeatCode[i]; //将控制代码赋给低4位
P1 = tmp; //将改变值返还给P1
i++; //索引递增
i = i & 0x07; //每八拍索引值归零
delay();
}
void delay()
{
unsigned int num = 200; //大概2ms延时
while(num--);
}

电机转速缓慢的原因分析

虽然电机成功转动,但是大家可以发现,电机转动速度非常缓慢。其原因则是因为该型号电机为减速电机,其内部普遍用小齿轮带动大齿轮,由参数表可知其减速比为1:64。
因此,步进电机真正旋转一周需要的拍数实际为6464=4096,时间为40962ms=8192ms,则步进角度为360/4096,表中步进角度参数5.625/64也与其吻合。

便于控制转过圈数的改进程序

#include <reg52.h>
void TurnMortor(unsigned long angle); //步进电机转动函数
void main()
{
TurnMortor(360*25); //将转动角度送入函数
while(1);
}
void delay()
{
unsigned int i = 200;
while(i--);
}
void TurnMortor(unsigned long angle)
{
unsigned char tmp; //暂存P1IO口数据
unsigned char index = 0; //节拍输出索引
unsigned char beats = 0; //总节拍数
unsigned char code BeatCode ={ //每个节拍对应I/O口的控制代码
0x0E,0x0C,0x0D,0x09,0x0B,0x03,0x07,0x06
};
beats = (angle/360)*4096; //将角度转化为拍数
while(beats--)
{
tmp = P1; //将P1口数据赋给tmp
tmp = tmp & 0xF0; //清空tmp低四位数据
tmp = tmp | BeatCode[index];
P1 = tmp;
index++; //索引随节拍递增
index = index & 0x07; //索引逢8清零
delay();
}
P1 = P1 | 0x0F; //关闭电机所有相
}

利用中断编写实用性程序

在上述两个程序中,我们都利用了delay()延时函数。例如第二个程序中,大约有200s时间都仅用于延时。在实际的控制系统中,是一定需要避免的。
那么我们可以通过利用中断来使其变成实际控制中可以使用的程序。

#include<reg52.h>
void StartMotor(unsigned long angle);
unsigned long beats = 0;
void main()
{
EA = 1;
ET0 = 1;
TMOD = 0x01;
TH0 = 0xF8;
TL0 = 0x30;
TR0 = 1;
StartMortor(360*25); //旋转25圈
while(1);
}
void StartMortor(unsigned long angle)
{
EA = 0; //关闭中断开关,防止beats运算过程中被打断
beats = (angle/360)*4096;
EA = 1; //重新开启中断
}
void InterruptTimer0() interrupt 1
{
unsigned char tmp;
static unsigned char index = 0;
unsigned char code BeatCode ={ //每个节拍对应I/O口的控制代码
0x0E,0x0C,0x0D,0x09,0x0B,0x03,0x07,0x06
};
TH0 = 0xF8;
TL0 = 0x30;
if(beats != 0)
{
tmp = P1; //将P1口数据赋给tmp
tmp = tmp & 0xF0; //清空tmp低四位数据
tmp = tmp | BeatCode[index];
P1 = tmp;
index++; //索引随节拍递增
index = index & 0x07; //索引逢8清零
beats--;
}
else
{
P1 = P1 | 0x0F; //关闭电机所有相
}
}

说明:
我们所使用的STC89C52 单片机是 8 位单片机,这个 8 位的概念就是说单片机操作数据时都是按 8 位即按1 个字节进行的,那么要操作多个字节(不论是读还是写)就必须分多次进行了。而我们程序中定义的 beats 这个变量是 unsigned long 型,它要占用 4 个字节,那么对它的赋值最少也要分 4 次才能完成了。我们想象一下,假如在完成了其中第一个字节的赋值后,恰好中断发生了,InterruptTimer0 函数得到执行,而这个函数内可能会对 beats 进行减 1 的操作,减法就有可能发生借位,借位就会改变其它的字节,但因为此时其它的字节还没有被赋入新值,于是错误就会发生了,减 1 所得到的结果就不是预期的值了!所以要避免这种错误的发生就得先暂时关闭中断,等赋值完成后再打开中断。而如果我们使用的是 char 或 bit 型变量的话,因为它们都是在 CPU 的一次操作中就完成的,所以即使不关中断,也不会发生错误。

用4*4矩阵按键控制28BYJ-48步进电机

#include<reg52.h>

sbit KEY_IN_1 = P2^4;
sbit KEY_IN_2 = P2^5;
sbit KEY_IN_3 = P2^6;
sbit KEY_IN_4 = P2^7;
sbit KEY_OUT_1 = P2^3;
sbit KEY_OUT_2 = P2^2;
sbit KEY_OUT_3 = P2^1;
sbit KEY_OUT_4 = P2^0; unsigned char code KeyCodeMap[4][4] = { //矩阵按键编号到标准键盘键码的映射表
{ 0x31, 0x32, 0x33, 0x26 }, //数字键 1、数字键 2、数字键 3、向上键
{ 0x34, 0x35, 0x36, 0x25 }, //数字键 4、数字键 5、数字键 6、向左键
{ 0x37, 0x38, 0x39, 0x28 }, //数字键 7、数字键 8、数字键 9、向下键
{ 0x30, 0x1B, 0x0D, 0x27 } //数字键 0、ESC 键、 回车键、 向右键
}; unsigned char KeySta[4][4] ={ //记录按键当前状态
{1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1}
}; static long beats = 0; void StartMortor(signed long angle); //利用angle计算beats节拍数
void EndMortor(); //将beats赋值为0;
void KeyAction(unsigned char keycode); //接收KeyDriver传来的keycode值,将其转化为圈数
void KeyDriver(); //判断哪个按键被按下
void KeyScan(); //按键消抖,控制IO口电平输出
void TurnMortor(); //控制步进电机正转、反转 void main()
{
EA = 1;
ET0 = 1;
TMOD = 0x01;
TH0 = 0xFC;
TL0 = 0x18;
TR = 1;
while(1)
{
KeyDriver(); //调用按键驱动程序
}
}
void StartMortor(signed long angle)
{
EA = 0; //关闭中断开关,防止beats运算过程中被打断
beats = (angle/360)*4096;
EA = 1; //重新开启中断
}
void EndMortor()
{
EA = 0;
beats = 0;
EA = 1;
}
void KeyAction(unsigned char Keycode)
{
static bit DirMortor = 0;
if((Keycode >= 0x31) && (Keycode <= 0x39))
{
if(DirMortor == 0)
{
StartMortor((Keycode-0x30)*360); //将圈数化为角度传入StartMortor函数
}
else
{
StartMortor(-(Keycode-0x30)*360);
}
}
else if(Keycode == 0x26)
{
DirMortor = 0;
}
else if(Keycode == 0x28)
{
DirMortor = 1;
}
else if(Keycode == 0x25)
{
StartMortor(90);
}
else if(Keycode == 0x27)
{
StartMortor(-90);
}
else if(Keycode == 0x1B)
{
EndMortor(); //beats变为0
}
}
void KeyDiver()
{
unsigned char i,j;
static unsigned char backup[4][4] ={
{1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1}
};
for(i = 0;i < 4;i++)
{
for(j = 0;j < 4;j++)
{
if(Keysta[i][j] != backup[i][j])
{
if(backup[i][j] != 0)
{
KeyAction(KeyCodeMap[i][j]);//判断按下按键,传对应值
}
backup[i][j] = Keysta[i][j];
}
}
}
}
void KeyScan();
{
unsigned char i;
static unsigned char keyout = 0;
static unsigned char Keybuf[4][4] ={
{0xFF,0xFF,0xFF,0xFF},{0xFF,0xFF,0xFF,0xFF},
{0xFF,0xFF,0xFF,0xFF},{0xFF,0xFF,0xFF,0xFF}
};
Keybuf[keyout][0] = (Keybuf[keyout][0] << 1) | KEY_IN_1;
Keybuf[keyout][1] = (Keybuf[keyout][1] << 1) | KEY_IN_2;
Keybuf[keyout][2] = (Keybuf[keyout][2] << 1) | KEY_IN_3;
Keybuf[keyout][3] = (Keybuf[keyout][3] << 1) | KEY_IN_4;
for(i = 0;i < 4;i++)
{
if((Keybuf[keyout][i] & 0x0F) == 0x00)
{
Keysta[keyout][i] = 0;
}
if((Keybuf[keyout][i] & 0x0F) == 0x0F)
{
Keysta[keyout][i] = 1;
}
}
keyout++;
keyout = keyout & 0x03;
switch(keyout)
{
case 0: KEY_OUT_4 = 1; KEY_OUT_1 = 0; break;
case 1: KEY_OUT_1 = 1; KEY_OUT_2 = 0; break;
case 2: KEY_OUT_2 = 1; KEY_OUT_3 = 0; break;
case 3: KEY_OUT_3 = 1; KEY_OUT_4 = 0; break;
default:break;
}
}
void TurnMortor();
{
unsigned char tmp;
static signed index = 0;
unsigned char code BeatCode ={ //每个节拍对应I/O口的控制代码
0x0E,0x0C,0x0D,0x09,0x0B,0x03,0x07,0x06
};
if(beats != 0)
{
if(beats > 0)
{
index++;
index = index & 0x07; //满8进为0
beats--;
}
if(beats < 0)
{
index--;
index = index & 0x07; //满-1进为7
beats++;
}
tmp = P1;
tmp = tmp & 0xF0;
tmp = tmp | BeatCode[index];
P1 = tmp;
}
else
{
P1 = P1 | 0x0F;
}
}
void InterruptTimer0() interrupt 1
{
static bit div = 0;
TH0 = 0xFC;
TL0 = 0X18;
KeyScan(); //1ms扫描一次按键
div = ~div;
if(div == 1)
{
TurnMortor(); //2ms执行一次TurnMortor函数
}
}

STC单片机控制28BYJ-48步进电机的更多相关文章

  1. 单片机成长之路(51基础篇) - 002 STC单片机冷启动和复位有什么区别

    STC单片机简介 STC单片机是一款增强型51单片机,完全兼容MCS-51,还增加了新的功能,比如新增两级中断优先级,多一个外中断,内置EEPROM,硬件看门狗,具有掉电模式,512B内存等.还支持I ...

  2. STC 单片机ADC实现原理

    模数转换器原理 数模转换器( analog to digitI converter,ADC),简称为A/D,ADC是链接模拟世界和数字世界的桥梁.它用于将连续的模拟信号转换为数字形式离散信号.典型的, ...

  3. STM32F103控制两个步进电机按照一定转速比运动

    这个暑假没有回家,在学校准备九月份的电子设计竞赛.今天想给大家分享一下STM32定时器控制两个步进电机按照一定速度比转动的问题. 这次做的05年的电子设计竞赛题目,运动悬挂系统..本实验是控制两个步进 ...

  4. STC单片机 IAP(EEPROM)的使用

    STC89C51.52内部都自带有2K字节的EEPROM,54.55和58都自带有16K字节的EEPROM,STC单片机是利用IAP技术实现的EEPROM,内部Flash擦写次数可达100,000 次 ...

  5. STC单片机串口输出ADXL335角度值

    STC单片机串口输出ADXL335角度值: //***************************************************** //名称:单片机串口输出ADXL335角度值 ...

  6. STC单片机Flash做EEPROM的代码

    STC官方给出的建议: /***************************************************************Author:Liming*** * @brie ...

  7. 5-51单片机ESP8266学习-AT指令(8266TCP服务器--用手机TCP调试助手发信息给单片机控制小灯的亮灭)

    http://www.cnblogs.com/yangfengwu/p/8759294.html 源码链接:https://pan.baidu.com/s/1wT8KAOIzvkOXXNpkDI7E8 ...

  8. Linux(Ubuntu12.04)上玩儿STC单片机(转)

    操作系统:Ubuntu16.04 TLS 单片机:STC的STC89C52RC系列及 Atmel的AT89S52... 所需工具: 1.编辑器- Vim(不钟情于IDE,个人喜好,高手勿喷) 2.编译 ...

  9. 宏晶STC单片机使用STC-ISP串口烧录失败的原因与解决方法汇总

    官方网址: http://www.stcisp.com/q_and_a_stcisp.html 个人小结 芯片:STC12C5A60S2 封装:LQFP-48 晶振大小:SD22.1184M 最小系统 ...

随机推荐

  1. appium日志

    2020-10-02 00:44:10:672 [Appium] Welcome to Appium v1.16.0 2020-10-02 00:44:10:673 [Appium] Non-defa ...

  2. Windows系统中的SVN使用方法

    Windows 下搭建 SVN(3.9版本)服务器 2018年08月11日 12:22:55 Amarao 阅读数 11984   版权声明:本文为博主原创文章,遵循CC 4.0 by-sa版权协议, ...

  3. 【原创】linux mint 17.3 kvm 安装windows7虚拟机

    一.安装windows7虚拟机 linux mint 17.3是一个不错的桌面发行版本,我下载了 linux mint 17.3 for xfce 桌面版本,运行速度没得说,而且安装设置都挺简单,非常 ...

  4. 关于spring cloud项目搭建问题

    spring cloud 是基于spring boot搭建,父项目中引入依赖时候一定要将spring boot和spring cloud 的版本号对应起来,要不然jar包报错,项目也启动不起来!!!下 ...

  5. State Space Model Content

    State Space Model 状态空间模型及其卡尔曼滤波技术 混合正态分布下的状态空间模型及其滤波

  6. 痞子衡嵌入式:我的三个小项目陆续上线恩智浦官方Github

    恍如眨眼间,痞子衡在飞思卡尔/恩智浦已经工作 8 年多了,前 5 年主要是在软件团队,最近 3 年在系统团队.所处团队不同,工作思维也不同,自从转到系统团队,开始跟客户打起交道,对待问题和解决问题的立 ...

  7. 详解package-lock.json的作用

    目录 详解package-lock.json package-lock.json的作用 版本号的定义规则与前缀对安装的影响 改动package.json后依旧能改变项目依赖的版本 当前项目的真实版本号 ...

  8. SQL Server链接服务器信息查询

    exec sp_helpserver --查询链接服务器select * from sys.servers --查询链接服务器链接地址

  9. bzoj2064分裂(dp)

    题目大意: 给定一个初始集合和目标集合,有两种操作:1.合并集合中的两个元素,新元素为两个元素之和 2.分裂集合中的一个元素,得到的两个新元素之和等于原先的元素.要求用最小步数使初始集合变为目标集合, ...

  10. qsort()函数的使用

    函数声明 void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*)) 参数 ...