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

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

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个永磁齿的结构叫转子。

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

外圈与电机外壳固定,共有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度,这个值被称为步进角度。
还有一种更有性能的工作模式,在单四拍的每两个节拍中插入一个双绕组到通的中间节拍,组成八拍模式。这样会使得电机的整体扭力输出增加,更有劲。

让电机转起来

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

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

; 最简单的电机转动程序

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

#include
unsigned char code BeatCode ={
    0x0E,0x0C,0x0D,0x09,0x0B,0x03,0x07,0x06
    };
void delay();
void main()
{
    unsigned char tmp;
    unsigned char i = 0;
    tmp = P1;
    tmp = tmp & 0xF0;
    tmp = tmp | BeatCode[i];
    P1 = tmp;
    i++;
    i = i & 0x07;
    delay();
}
void delay()
{
    unsigned int num = 200;
    while(num--);
}

电机转速缓慢的原因分析

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

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

#include
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;
    unsigned char index = 0;
    unsigned char beats = 0;
    unsigned char code BeatCode ={
    0x0E,0x0C,0x0D,0x09,0x0B,0x03,0x07,0x06
    };
    beats = (angle/360)*4096;
    while(beats--)
    {
        tmp = P1;
        tmp = tmp & 0xF0;
        tmp = tmp | BeatCode[index];
        P1 = tmp;
        index++;
        index = index & 0x07;
        delay();
    }
    P1 = P1 | 0x0F;
}

利用中断编写实用性程序

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

#include
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);
    while(1);
}
void StartMortor(unsigned long angle)
{
    EA = 0;
    beats = (angle/360)*4096;
    EA = 1;
}
void InterruptTimer0() interrupt 1
{
    unsigned char tmp;
    static unsigned char index = 0;
    unsigned char code BeatCode ={
    0x0E,0x0C,0x0D,0x09,0x0B,0x03,0x07,0x06
    };
    TH0 = 0xF8;
    TL0 = 0x30;
    if(beats != 0)
    {
        tmp = P1;
        tmp = tmp & 0xF0;
        tmp = tmp | BeatCode[index];
        P1 = tmp;
        index++;
        index = index & 0x07;
        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

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 },
 { 0x34, 0x35, 0x36, 0x25 },
 { 0x37, 0x38, 0x39, 0x28 },
 { 0x30, 0x1B, 0x0D, 0x27 }
 };

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);
void EndMortor();
void KeyAction(unsigned char keycode);
void KeyDriver();
void KeyScan();
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 = (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);
        }
        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();
    }
}
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 ={
    0x0E,0x0C,0x0D,0x09,0x0B,0x03,0x07,0x06
    };
    if(beats != 0)
    {
        if(beats > 0)
        {
            index++;
            index = index & 0x07;
            beats--;
        }
        if(beats < 0)
        {
            index--;
            index = index & 0x07;
            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();
    div = ~div;
    if(div == 1)
    {
        TurnMortor();
    }
}

Original: https://www.cnblogs.com/Tayoou/p/15257617.html
Author: Tayoou
Title: STC单片机控制28BYJ-48步进电机

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/593998/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 根据ip查询 地址信息

    这次调用了jsoup.jar包,使用它获取网上的资源获取网址https://ip138.com/的数据进行返回数据 import org.jsoup.Jsoup; import j…

    Java 2023年6月7日
    0112
  • Spring(六):Spring配置说明

    一、bean    <bean id="user" class="com.jms.pojo.User" n…

    Java 2023年6月15日
    090
  • 分布式ID详解(5种分布式ID生成方案)

    分布式架构会涉及到分布式全局唯一ID的生成,今天我就来详解分布式全局唯一ID,以及分布式全局唯一ID的实现方案@mikechen 什么是分布式系统唯一ID 在复杂分布式系统中,往往…

    Java 2023年6月15日
    089
  • SpringMVC(4)-接收前端参数以及返回前端结果

    还是在上一个项目的基础上做(SpringMVC(2)-注解开发MVC这个,这个里边已经写好了视图解析器,jsp文件等信息) 1.新建一个实体类在com.xiaoma文件夹下新建一个…

    Java 2023年6月9日
    089
  • 不重启tomcat,清空catalina.out的几种方式

    相信小伙伴们使用tomcat容器部署项目时,都会遇到这个问题 尤其是刚上线日志级别一般启动为DEBUG级别时,catalina.out文件过一会就会特别特别大,特别占我们服务器上的…

    Java 2023年6月16日
    0110
  • java读取文本文件的方法

    文本文件是我们日常开发中,常用的简单存储载体,那么如何读取文本文件呢? 下文笔者将通过示例的方式讲述读取文本文件的方法分享,如下所示: 文本文件是最常用的文件格式之一, 下文是笔者…

    Java 2023年6月15日
    092
  • 2 Java内存层面的对象认识

    1 对象的创建 2 对象的内存布局 3 对象的访问定位 3.1句柄访问 3.2 直接指针访问 说明:此分析基于HotSpot虚拟机 1 对象的创建 Java对象的创建方式有三种: …

    Java 2023年6月7日
    0100
  • 【SpringCloud-Alibaba系列教程】4.服务管理

    一、引出问题 上一节我们讲到了微服务相互调用的过程。那我们思考一下这样一个问题,我们某个微服务有没有可能有多个机器呢?或者说端口有没有可能不一样呢?那我们如何管理我们的微服务呢?其…

    Java 2023年6月5日
    0133
  • 制作自己的Docker镜像

    制作镜像有2种方式,一种是容器转换成镜像,另一种是使用dockerfile创建镜像,一般后者更常用。 使用 docker commit命令将容器转换成镜像 docker commi…

    Java 2023年6月7日
    086
  • @SuppressWarning

    看到一个没见过的SuppressWarning( @SuppressWarnings(“WeakerAccess”)),网上也没搜到。 找到一篇文章(@SuppressWarnin…

    Java 2023年6月6日
    094
  • Redis分布式锁实现

    Redis分布式锁实现 在分布式环境下,利用Redis实现锁机制,避免资源竞争的做法非常常见。这里探讨一下Redis分布式锁的实现方式、可能存在的问题以及适用场景。 setnx 最…

    Java 2023年6月13日
    0109
  • CentOS7部署文件服务器vsftp

    参考连接:https://www.cnblogs.com/gaoyanbing/p/12956914.html 本文来自博客园,作者:zhuzhu&you,转载请注明原文链…

    Java 2023年6月9日
    088
  • java多线程系列(1)

    1,为什么需要线程? 作用:提升cpu的利用率,如,早期的dos系统,执行2个命令时( command 1, command 2 ),如果command1【假如是磁盘遍历文件的IO…

    Java 2023年5月29日
    0113
  • 最简单的JVM内存结构图

    JVM内存结构图 大家好,好几天没有更新了,今天的内容有点多,我们详细介绍下JVM内部结构图,还是和之前一样, 案例先行,方便大家理解记忆。 如上代码:在主线程中for循环新建2个…

    Java 2023年6月8日
    091
  • Java入门到精通——基础篇之多线程实现简单的PV操作的进程同步

    一、概述 PV操作是对信号量进行的操作。进程同步是指在并发进程之间存在一种制约关系,一个进程的执行依赖另一个进程的消息,当一个进程没有得到另一个进程的消息时应等待,直到消息到达才被…

    Java 2023年5月29日
    097
  • 搜索精准度优化架构方案

    概述 实现公司对内容精准化搜索和用户精准化推送的目标。 采用方案 搜索技术+数据挖掘+机器学习(未来)+人工审核(现在) 人员配备 1-2人专职开发(未来深入,人数更多) 大致流程…

    Java 2023年6月8日
    092
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球