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

news/2024/11/22 21:51:52/

STC单片机4*4按键控制步进电机旋转

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

28BYJ-48型步进电机说明

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

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

四相永磁式的含义

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;                                      //将改变值返还给P1i++;                                           //索引递增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口数据赋给tmptmp = 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口数据赋给tmptmp = 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进为0beats--;}if(beats < 0){index--;index = index & 0x07;      //满-1进为7beats++;}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函数}
}

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

相关文章

步步高的笔试题

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 步进电…

面试流程:小天才步步高

春招笔试&#xff1a; 单选&#xff1a;html内联 css选择器 vue的绑定事件 vue生命周期 vue的父子传值 多选&#xff1a;数据结构 的队和栈特点&#xff0c;很简单。 简答&#xff1a; osi七层模型和tcp/ip四层模型 浏览器url发生的过程 前端优化的常用方法 编程题&#xff…

步步高笔试

步步高 若有说明&#xff1a;int n2,*p&n,*qp;则以下非法的赋值语句是&#xff1a; A pq; B *p*q; C n*q; D pn; 答案&#xff1a; D 在下列说法中&#xff0c;哪个是错误的&#xff08; &#xff09; A若进程A和进程B在临界段上互斥&#xff0c;那么当进程A处于该临界段…