STC单片机控制28BYJ-48步进电机
STC单片机4*4按键控制步进电机旋转
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步进电机的更多相关文章
- 单片机成长之路(51基础篇) - 002 STC单片机冷启动和复位有什么区别
STC单片机简介 STC单片机是一款增强型51单片机,完全兼容MCS-51,还增加了新的功能,比如新增两级中断优先级,多一个外中断,内置EEPROM,硬件看门狗,具有掉电模式,512B内存等.还支持I ...
- STC 单片机ADC实现原理
模数转换器原理 数模转换器( analog to digitI converter,ADC),简称为A/D,ADC是链接模拟世界和数字世界的桥梁.它用于将连续的模拟信号转换为数字形式离散信号.典型的, ...
- STM32F103控制两个步进电机按照一定转速比运动
这个暑假没有回家,在学校准备九月份的电子设计竞赛.今天想给大家分享一下STM32定时器控制两个步进电机按照一定速度比转动的问题. 这次做的05年的电子设计竞赛题目,运动悬挂系统..本实验是控制两个步进 ...
- STC单片机 IAP(EEPROM)的使用
STC89C51.52内部都自带有2K字节的EEPROM,54.55和58都自带有16K字节的EEPROM,STC单片机是利用IAP技术实现的EEPROM,内部Flash擦写次数可达100,000 次 ...
- STC单片机串口输出ADXL335角度值
STC单片机串口输出ADXL335角度值: //***************************************************** //名称:单片机串口输出ADXL335角度值 ...
- STC单片机Flash做EEPROM的代码
STC官方给出的建议: /***************************************************************Author:Liming*** * @brie ...
- 5-51单片机ESP8266学习-AT指令(8266TCP服务器--用手机TCP调试助手发信息给单片机控制小灯的亮灭)
http://www.cnblogs.com/yangfengwu/p/8759294.html 源码链接:https://pan.baidu.com/s/1wT8KAOIzvkOXXNpkDI7E8 ...
- Linux(Ubuntu12.04)上玩儿STC单片机(转)
操作系统:Ubuntu16.04 TLS 单片机:STC的STC89C52RC系列及 Atmel的AT89S52... 所需工具: 1.编辑器- Vim(不钟情于IDE,个人喜好,高手勿喷) 2.编译 ...
- 宏晶STC单片机使用STC-ISP串口烧录失败的原因与解决方法汇总
官方网址: http://www.stcisp.com/q_and_a_stcisp.html 个人小结 芯片:STC12C5A60S2 封装:LQFP-48 晶振大小:SD22.1184M 最小系统 ...
随机推荐
- cgroup配置
待续... https://docs.hortonworks.com/HDPDocuments/HDP3/HDP-3.1.0/data-operating-system/content/enablin ...
- whistle抓包-数据包分析
额,这篇忘了是来自哪位作者的了. whistle:1.14.6 这里以抓取浏览器数据包为例,分析抓取的数据. Method:Connect,对应Host:Tunnel to意思是因为网络环境受限,客户 ...
- SAP Shared Object 01 (共享对象)
介绍 共享对象是在共享内存中的一个对象.共享内存是应用服务器中的一个内存区域,可以被应用服务器中的所有程序访问. 在共享对象出现之前,ABAP使用EXPORT 和 IMPORT语句实现内存区域中内容的 ...
- P2150-[NOI2015]寿司晚宴【dp】
正题 题目链接:https://www.luogu.com.cn/problem/P2150 题目大意 将\(2\sim n\)选出一些分成两个集合,要求这两个集合中没有一对数不是互质的.求方案数对\ ...
- Phalcon如何创建多模块并能进行访问 《Phalcon入坑指南系列 四》
本系列目录 一.Phalcon在Windows上安装 <Phalcon入坑指南系列 一> 二.Phalcon入坑必须知道的功能<Phalcon入坑指南系列 二> 三.Phalc ...
- 使用 Vue 脚手架,为什么要学 webpack?
先问大家一个很简单的问题: vue init webpack prjectName 与 vue create projectName 有什么区别呢? 它们是 Vue-cli 2 和 Vue-cli3 ...
- 前端快闪三:多环境灵活配置react
你已经使用Create React App脚手架搭建了React应用,现在该部署了. 一般会使用npm run build或者yarn build构建出静态资源, 由web服务器承载. 您会体验到 多 ...
- Unity 刚体问题 解决相互作用力
在进行开发过程中,当两个都具有碰撞体和刚体的 游戏物体进行接触之后,或多或少都会出现相互作用力,对于体验有一定的影响. 需要在FixedUpdate(间隔固定的时间调用,不受游戏帧率的影响) 当中 ...
- 如何发现 Kubernetes 中服务和工作负载的异常
大家好,我是来自阿里云的李煌东,今天由我为大家分享 Kubernetes 监控公开课的第二节内容:如何发现 Kubernetes 中服务和工作负载的异常. 本次分享由三个部分组成: 一.Kuberne ...
- pycharm上的python虚拟环境移到离线机器上
Pycharm的Terminal 中执行: 查看现有的包到requirements.txt中 pip freeze > requirements.txt 生成依赖包 D:\machangwei\ ...