实用的 28BYJ-48 步进电机控制程序

发布者:乐观向前最新更新时间:2017-11-15 来源: eefocus关键字:28BYJ-48  步进电机  控制程序 手机看文章 扫描二维码
随时随地手机看文章

上面我们虽然完成了用中断控制电机转动的程序,但实际上这个程序还是没多少实用价值的,我们不能每次想让它转动的时候都上下电啊,是吧。还有就是它不但能正转还得能反转啊,也就是说不但能转过去,还得能转回来呀。好吧,我们就来做一个实例程序吧,结合第8章的按键程序,我们设计这样一个功能程序:按数字键1~9,控制电机转过1~9圈;配合上下键改变转动方向,按向上键后正向转1~9圈,向下键则反向转1~9圈;左键固定正转90度,右键固定反转90;Esc 键终止转动。通过这个程序,我们也可以进一步体会到如何用按键来控制程序完成复杂的功能,以及控制和执行模块之间如何协调工作,而你的编程水平也可以在这样的实践练习中得到锻炼和提升。

#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 }, //数字键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}
};
signed long beats = 0; //电机转动节拍总数
void KeyDriver();

void main(){
    EA = 1; //使能总中断
    TMOD = 0x01; //设置 T0 为模式1
    TH0 = 0xFC; //为 T0 赋初值 0xFC67,定时 1 ms
    TL0 = 0x67;
    ET0 = 1; //使能 T0 中断
    TR0 = 1; //启动 T0

    while (1){
        KeyDriver(); //调用按键驱动函数
    }
}
/* 步进电机启动函数,angle-需转过的角度 */
void StartMotor(signed long angle){
    //在计算前关闭中断,完成后再打开,以避免中断打断计算过程而造成错误
    EA = 0;
    beats = (angle * 4076) / 360; //实测为4076拍转动一圈
    EA = 1;
}
/* 步进电机停止函数 */
void StopMotor(){
    EA = 0;
    beats = 0;
    EA = 1;
}
/* 按键动作函数,根据键码执行相应的操作,keycode-按键键码 */
void KeyAction(unsigned char keycode){
    static bit dirMotor = 0; //电机转动方向
    //控制电机转动 1-9 圈
    if ((keycode>=0x30) && (keycode<=0x39)){
        if (dirMotor == 0){
            StartMotor(360*(keycode-0x30));
        }else{
            StartMotor(-360*(keycode-0x30));
        }
    }else if (keycode == 0x26){ //向上键,控制转动方向为正转
        dirMotor = 0;
    }else if (keycode == 0x28){ //向下键,控制转动方向为反转
        dirMotor = 1;
    }else if (keycode == 0x25){ //向左键,固定正转90度
        StartMotor(90);
    }else if (keycode == 0x27){ //向右键,固定反转90度
        StartMotor(-90);
    }else if (keycode == 0x1B){ //Esc 键,停止转动
        StopMotor();
    }
}
/* 按键驱动函数,检测按键动作,调度相应动作函数,需在主循环中调用 */
void KeyDriver(){
    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++){ //循环检测4*4的矩阵按键
        for (j=0; j<4; j++){
            if (backup[i][j] != KeySta[i][j]){ //检测按键动作
                if (backup[i][j] != 0){ //按键按下时执行动作
                    KeyAction(KeyCodeMap[i][j]); //调用按键动作函数
                }
                backup[i][j] = KeySta[i][j]; //刷新前一次的备份值
            }
        }
    }
}
/* 按键扫描函数,需在定时中断中调用,推荐调用间隔 1 ms */
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}
    };

    //将一行的4个按键值移入缓冲区
    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++){ //每行4个按键,所以循环4次
        if ((keybuf[keyout][i] & 0x0F) == 0x00){
            //连续4次扫描值为0,即 4*4 ms 内都是按下状态时,可认为按键已稳定的按下
            KeySta[keyout][i] = 0;
        }else if ((keybuf[keyout][i] & 0x0F) == 0x0F){
            //连续4次扫描值为1,即 4*4 ms 内都是弹起状态时,可认为按键已稳定的弹起
            KeySta[keyout][i] = 1;
        }
    }
    //执行下一次的扫描输出
    keyout++; //输出索引递增
    keyout = keyout & 0x03; //索引值加到4即归零
    //根据索引,释放当前输出引脚,拉低下次的输出引脚
    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 TurnMotor(){
    unsigned char tmp; //临时变量
    static unsigned char index = 0; //节拍输出索引
    unsigned char code BeatCode[8] = { //步进电机节拍对应的 IO 控制代码
        0xE, 0xC, 0xD, 0x9, 0xB, 0x3, 0x7, 0x6
    };

    if (beats != 0){ //节拍数不为0则产生一个驱动节拍
        if (beats > 0){ //节拍数大于0时正转
            index++; //正转时节拍输出索引递增
            index = index & 0x07; //用&操作实现到8归零
            beats--; //正转时节拍计数递减
            }else{ //节拍数小于0时反转
            index--; //反转时节拍输出索引递减
            index = index & 0x07; //用&操作同样可以实现到-1时归7
            beats++; //反转时节拍计数递增
        }
        tmp = P1; //用 tmp 把 P1 口当前值暂存
        tmp = tmp & 0xF0; //用&操作清零低4位
        tmp = tmp | BeatCode[index]; //用|操作把节拍代码写到低4位
        P1 = tmp; //把低4位的节拍代码和高4位的原值送回 P1
    }else{ //节拍数为0则关闭电机所有的相
        P1 = P1 | 0x0F;
    }
}
/* T0 中断服务函数,用于按键扫描与电机转动控制 */
void InterruptTimer0() interrupt 1{
    static bit div = 0;
    TH0 = 0xFC; //重新加载初值
    TL0 = 0x67;
    KeyScan(); //执行按键扫描
    //用一个静态 bit 变量实现二分频,即 2 ms 定时,用于控制电机
    div = ~div;
    if (div == 1){
        TurnMotor();
    }
}

这个程序是第8章和本章知识的一个综合——用按键控制步进电机转动。程序中有这么几点值得注意,我们分述如下:

  • 针对电机要完成正转和反转两个不同的操作,我们并没有使用正转启动函数和反转启动函数这么两个函数来完成,也没有在启动函数定义的时候增加一个形式参数来指明其方向。我们这里的启动函数 void StartMotor(signed long angle)与单向正转时的启动函数唯一的区别就是把形式参数 angle 的类型从 unsigned long 改为了 signed long,我们用有符号数固有的正负特性来区分正转与反转,正数表示正转 angle 度,负数就表示反转 angle 度,这样处理是不是很简洁又很明了呢?而你对有符号数和无符号数的区别用法是不是也更有体会了?

  • 针对终止电机转动的操作,我们定义了一个单独的 StopMotor 函数来完成,尽管这个函数非常简单,尽管它也只在 Esc 按键分支内被调用了,但我们仍然把它单独提出来作为了一个函数。而这种做法就是基于这样一条编程原则:尽可能用单独的函数来完成硬件的某种操作,当一个硬件包含多个操作时,把这些操作函数组织在一起,形成一个对上层的统一接口。这样的层次化处理,会使得整个程序条理清晰,既有利于程序的调试维护,又有利于功能的扩充。

  • 中断函数中要处理按键扫描和电机驱动两件事情,而为了避免中断函数过于复杂,我们就又分出了按键扫描和电机驱动两个函数(这也同样符合上述2的编程原则),而中断函数的逻辑就变得简洁而清晰了。这里还有个矛盾,就是按键扫描我们选择的定时时间是 1 ms,而本章之前的实例中电机节拍持续时间都是 2 ms;很显然,用 1 ms 的定时可以定出 2 ms 的间隔,而用 2 ms 的定时却得不到准确的 1 ms 间隔;所以我们的做法就是,定时器依然定时 1 ms,然后用一个 bit 变量做标志,每 1 ms 改变一次它的值,而我们只选择值为1的时候执行一次动作,这样就是 2 ms 的间隔了;如果我要 3 ms、4 ms„„呢,把 bit 改为 char 或 int 型,然后对它们递增,判断到哪个值该归零,就可以了。这就是在硬件定时器的基础上实现准确的软件定时,其实类似的操作我们在讲数码管的时候也用过了,回想一下吧。


关键字:28BYJ-48  步进电机  控制程序 引用地址:实用的 28BYJ-48 步进电机控制程序

上一篇:28BYJ-48 步进电机控制程序基础
下一篇:单片机开发环境搭建--Keil uVision4安装教程

推荐阅读最新更新时间:2024-03-16 15:45

意法半导体:通用电机与步进电机控制方案(一)
意法半导体拥有强大的、面向电机控制应用的产品组合。 运转顺畅和高效率推动了电机控制的发展。 面向PMSM、PMAC和感应电机(其采用高性能微控制器、功率晶体管与高压栅极驱动器IC)的磁场定向控制(FOC)软件的发展满足了对更高效率的需求。 对于功率较低的应用而言,面向步进电机的新型高集成度控制器/驱动器IC可以为步进电机实现更顺畅的运转和更高的定位精度。 通用电机 通用电机属于串励电机,可由AC或DC电源供电。 由于励磁(定子)绕组与电枢(转子)绕组串联,所以任一极性的电流都会产生方向相同的转矩。 由于2个绕组内的电流和磁场同时反转,所以在AC电源供电的情况下,电机运转良好。 通用电机的优点在于起动转矩高、设计紧凑、运行速度快
[模拟电子]
意法半导体:通用电机与<font color='red'>步进电机</font>控制方案(一)
步进电机最简单的驱动方法_步进电机控制方法
  步进电机最简单的驱动方法   最简单的步进电机驱动方法是单相双极驱动方式,也称为双相步进驱动方式。该方式只需要使用一个 H桥驱动器和一个脉冲发生器,即可实现步进电机的驱动控制。   具体的驱动过程如下:   将步进电机的两个相连接到 H桥驱动器的两个输出端口。   将脉冲发生器连接到 H桥驱动器的输入端口,通过控制脉冲的频率和占空比,可以控制步进电机的转速和方向。   通过控制脉冲的数量和频率,可以控制步进电机旋转的步数和转角。   需要注意的是,单相双极驱动方式只能实现单个步进电机的基本驱动控制,对于需要高精度、高速度和高扭矩的应用场景,需要采用更复杂的驱动方式和控制算法。   步进电机控制方法   步进电机的控制方法主
[嵌入式]
锅炉筒打孔专机数控系统
1 前 言 为实现锅炉筒壁孔加工自动化而设计的数控系统,是以MCS-51单片机为控制核心、步进电机为驱动电机、机床大、小拖板为执行部件的两坐标开环控制系统。大拖板纵向移动以确定打孔位置,脉冲当量值为0.02mm/步,最大进给速度为5m/min。小拖板横向移动以实现刀具的快进和工进。系统具有点动对刀、自动加工、暂停和继续、急停报警、回零等主要功能。 2 硬件结构与功能 系统硬件分布于一块控制主板和两块驱动板上。 在以8031单片机为主的控制主板上,扩展一片27256EPROM,用于存放系统软件,一片2764EPROM,用于固化经过调试的零件加工程序,一片带掉电保护的6264RAM,用于随机存储手工编辑的零件加工程序。
[嵌入式]
步进电机相关的定义及硬件介绍
步进电机常用来做定位控制,它可以由PLC输出的脉冲数量控制旋转的角度(相对来说可以是距离),脉冲的频率控制步进电机旋转的速度。但用于控制精度不是很高的场合,简单、经济、控制方便;对于控制精度要求很高的场合,就得使用伺服控制系统了。 步进系统=步进驱动器+步进电机。步进电机由步进驱动器来驱动,相当于驱动电源,且它受外部的脉冲信号和方向信号控制(这里举例是西门子PLC输出脉冲),进而控制步进电机的旋转角度和速度。 步进驱动器+步进电机+西门子PLC(CPU 222 CN) 相关的定义 1、驱动器:用于PLC控制步进电机的媒介,负责把PLC给的脉冲信号经过放大后,输给步进电机,使电机按照PLC和驱动器给定的参数运行。 控
[嵌入式]
<font color='red'>步进电机</font>相关的定义及硬件介绍
基于GAL器件的步进电机控制器的研究与设计
摘要:介绍了利用阵列逻辑器件GAL16V8对三相六拍步进电机实现控制的方法及其电路设计。通过计算机编程器对GAL进行编程,可以满足各种控制要求。该电路简单,工作稳定可靠,编程灵活方便。 关键词:通用阵列逻辑器件 步进电机 GAL16V8 三相六拍 步进电机广泛应用于对精度要求比较高的运动控制系统中,如机器人、打印机、软盘驱动器、绘图仪、机械阀门控制器等。目前,对步进电机的控制主要有由分散器件组成的环形脉冲分配器、软件环形脉冲分配器、专用集成芯片环形脉冲分配器等。分散器件组成的环形脉冲分配器体积比较大,同时由于分散器件的延时,其可靠性大大降低;软件环形分配器要占用主机的运行时间,降低了速度;专用集成芯片环形脉冲分配器集成度高、可
[半导体设计/制造]
伺服电机和步进电机的技术问答
  伺服电机和步进电机的技术问答:   1、如何正确选择伺服电机和步进电机?   主要视具体应用情况而定,简单地说要确定:负载的性质(如水平还是垂直负载等),转矩、惯量、转速、精度、加减速等要求,上位控制要求(如对端口界面和通讯方面的要求),主要控制方式是位置、转矩还是速度方式。供电电源是直流还是交流电源,或电池供电,电压范围。据此以确定电机和配用驱动器或控制器的型号。   2、选择步进电机还是伺服电机系统?   其实,选择什么样的电机应根据具体应用情况而定,各有其特点。请见下表,自然明白。   3、如何配用步进电机驱动器?   根据电机的电流,配用大于或等于此电流的驱动器。如果需要低振动或高精度时,可配用细分型驱动器。对于大转矩电
[嵌入式]
永磁步进电机的基本概念、结构组成及工作原理
永磁步进电机是一种兼容且高效的设备,具有多种应用。由于转子由永磁体制成,不需要任何外部励磁,这使其在玩具、小型电机等应用中非常有用。 在设计方面,每次旋转的步距角可以轻松设计,这使得永磁步进电机在医疗仪器和航空结构等精密应用中非常有用。另外,由于体积小,它具有很高的移动性和易于使用等优势特点。 基本概念 永磁步进电机是一种机电能量转换装置,也就是将电能转换为机械能。在步进电机中,转子和定子磁场都被激发,因此转子磁场和定子磁场的相互作用产生扭矩。而在永磁步进电机中,转子线圈没有励磁,而是使用永磁体。 在传统的步进电机中,使用电磁铁,需要外部激励才能产生转子磁场。但在永磁步进电机当中,使用了永磁体,这减少了转子励磁系统并使
[嵌入式]
永磁<font color='red'>步进电机</font>的基本概念、结构组成及工作原理
使用MSP430G2 LaunchPad开发板连接步进电机的方法
在本篇文章中,我们将介绍如何使用MSP430 LaunchPad开发板连接一个步进电机。 MSP-EXP430G2是德州仪器(TI)提供的开发工具,又名LaunchPad,用于学习和练习如何使用他们提供的微控制器。该开发板属于MSP430产品线,我们可以使用它对所有MSP430系列微控制器进行编程。如果您是MSP430的新手,请查看MSP430 LaunchPad的入门教程。 所需的材料 ● MSP430 LaunchPad开发板 ● 35BYJ46或28-BYJ48步进电机 ● ULN2003驱动芯片 ● 连接导线 ● 面包板 步进电机 步进电机是一种无刷直流电机,可将电脉冲转换为不同的机械运动。步进电机的轴以
[单片机]
使用MSP430G2 LaunchPad开发板连接<font color='red'>步进电机</font>的方法
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
更多每日新闻
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved