求助:如何使型号为28BYJ-48步进电动机型号机转动90°后停止?

步进电机控制_百度知道
步进电机控制
04v.04v左右)电机停止工作;
按键K3按下时电机一步一步连续正向转动用28BYJ-48步进电机带动电位器,模数转换用的是ADC0809。
不知到程序该怎么写。万分感谢,步进电机用的是28BYJ-48步进电机,电压精确到0,松开按键电机停止。用的是stc89c52单片机,步进电机带动电位器转动一圈.50v左右)电机停止工作。
电机带动电位器转动时数码管实时显示当前电压。用按键控制相关功能,请高手能给出完整程序。
按键K4按下时电机一步一步连续反向转动;
按键K2按一下电机带动电位器转找到电位器的最大电压位置点(约4,轻触按键K1按一下电机带动电位器转找到电位器的最低电压位置点(约0,将电压送到4位数码管显示电压,数码管用的是四位共阴极,并读取电位器一圈对应的电压值,松开按键电机停止,能给说下电路链接顺序或给出电路图,电位器一圈对应的电压为0~5v
我有更好的答案
按默认排序
最后要要软硬件联合调试,这是8如8出的控制器,硬件方面要设计电路,软件要编程.baidu、设计电路板.com/zhidao/wh%3D600%2C800/sign=270b0b595b82b2b7a7ca31c/fa3aeeec479://g、制作电路板及硬件调试。而且需要较长研发周期.jpg" target="_blank" title="点击查看大图" class="ikqb_img_alink"><img class="ikqb_img" src="http.hiphotos。以上介绍没有用到模数转换器1://g、用的是STC单片机实现是没有问题的://g。但是用单片机控制还是比较麻烦的; &nbsp,不如使用现成的PLC或者表控:& & 图中是使用表控TPC8-8TD的接线图.jpg" esrc="http
其他类似问题
29人觉得有用
步进电机的相关知识
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁查看: 583|回复: 6
51单片机控制步进电机的转动,加减速,停止,反转
51单片机控制步进电机的转动,加减速,停止,反转,单双八拍工作方式:A-AB-B-BC-C-CD-D-DA (即一个脉冲,转 3.75 度)#include &reg52.h&
sbit inc=P3^2;
sbit dec=P3^3;
sbit zhzhd=P3^6;
sbit fazhd=P3^7;
bit flag=1;
unsigned char t=0x00; //表正反速度
void delay(unsigned int t);
void&&motor_ffw();
unsigned char code led7code[]=
{0x81,0xe7,0x92,0xc2,0xe4,0xc8,0x88,0xe3,0x00,0xc0};
unsigned int num=0;
unsigned char code FFW[8]={0x40,0x60,0x20,0x30,0x10,0x18,0x08,0x48};
unsigned char code FFZ[8]={0x48,0x08,0x18,0x10,0x30,0x20,0x60,0x40};&&//反转
void main()
& &&&
& & EA=1;
& & IT0=1;
& & EX0=1;
& & IT1=1;
& & EX1=1;
& & TMOD=0x06;
& & TL0=0
& & TH0=0
& & TR0=1;
& & ET0=1;
& & P3=0x3f;
& & P0=led7code[num%10];
& & while(1)
& & {
& &&&motor_ffw();
& & }
void&&motor_ffw()&&/* 步进电机驱动*/ //
{
& &
& &
& && &&&while(1)
& && &&&{
& && && && &
& && && && &for(j=0;j&12;j++)& &&&//12个周期转一圈
& && && &&&{& & for (i=0; i&8; i++)& && & //一个周期转30度
& && && && && &&&{&&
& && && && &if(flag==1)
& && && && && & P2 = FFW[i];&&//取数据
& && && && & else
& && && && && & P2 = FFZ[i];
& && && && && &delay(t);& && && && && && & //t调节转速
& && && && && & }
& && && && &}
& && &&&}
}
void int0(void) interrupt 0
{
& & EX0=0;
& & delay(10);
& & if(inc==0)
& & {
& & num++;
& &&&
& & P0=led7code[num%10];
& & if(num%10!=0&&flag){zhzhd=0;fazhd=1;}
& & else if (num%10==0){zhzhd=0;fazhd=0;}
& & else {zhzhd=1;fazhd=0;}
& &&&
& & switch(num%10)
& & {
& && &&&case 0:t=0x00;
& && &&&case 1:t=0x12;
& && &&&case 2:t=0x11;
& && &&&case 3:t=0x10;
& && &&&case 4:t=0x09;
& && &&&case 5:t=0x08;
& && &&&case 6:t=0x07;
& && &&&case 7:t=0x06;
& && &&&case 8:t=0x05;
& && &&&case 9:t=0x04;
& &&&
& & }
& &&&
& & }
& & while(!inc);
& & EX0=1;
}
void int1(void) interrupt 2
{
& & EX1=0;
& & delay(10);
& & if(dec==0)
& & {
& & num--;
& & if(num==65535)num=65529;
& & P0=led7code[num%10];
& & if(num%10!=0&&flag){zhzhd=0;fazhd=1;}
& & else if (num%10==0){zhzhd=0;fazhd=0;}
& & else {zhzhd=1;fazhd=0;}
& &&&
& & if(num==65535)num=65529;
& & switch(num%10)
& & {
& && &&&case 0:t=0x00;
& && &&&case 1:t=0x12;
& && &&&case 2:t=0x11;
& && &&&case 3:t=0x10;
& && &&&case 4:t=0x09;
& && &&&case 5:t=0x08;
& && &&&case 6:t=0x07;
& && &&&case 7:t=0x06;
& && &&&case 8:t=0x05;
& && &&&case 9:t=0x04;
& &&&
& & }
& & }
& & while(!dec);
& & EX1=1;
}
void huanx(void) interrupt 1
{& &
& & ET0=0;
& & TR0=0;
& & delay(10);
& & if(P3^4==0)
& & {
& & if(flag==1) {flag = 0;zhzhd=1;delay(500);fazhd=0;}
& & else&&{flag = 1;fazhd=1;delay(500);zhzhd=0;}
& & }
& & while(!(P3^4));
& & ET0=1;
& & TR0=1;
}
//& & 延时程序
void delay(unsigned int t)
{& && && && && && && && && &
& &
& &while(t--)
& &{
& &&&for(k=0; k&80; k++);
& && &
& &}
}复制代码
楼主用的是仿真还是开发板的
昨天用的28BYJ-48电机做了一个实验,四相八拍的,但转速特别慢,一分钟最多十圈的样子,问下楼主这款电机额定转速就是这么多么?
楼主有汇编的吗?
**** 作者被禁止或删除 内容自动屏蔽 ****
我也买了一个28BYJ-48电机
正在学习呢
能把你的代码发来看看吗?
学习来了,正在学习呢
学习了。谢谢。
Powered by关于步进电机_28BYJ-48的控制问题
关于步进电机_28BYJ-48的控制问题
(1)单片出来信号1000 (0x08)经运放后 接步进电机 橙 黄 粉 蓝(0 1 1 1 ,万用表已测),这样的信号一直保持,步进电机是否会从任意起始位置转动到对应的(0x08)的位置停止???(2)如果上面的成立,而步进电机没有转动,是否可以判断该电机坏了?(3)小弟在做以上测试时不成功,换成如下程序&& while(1)&& {&&&& P2=0x09;&&&& &&& delay(10);&& &&&& P2=0x01;&&&&&&& //A B相导通&&&&&& delay(10);&&&& P2=0x03;&&&&&&& //B相导通&&&&&& delay(10);&&&& P2=0x02;&&&&&& //B C相导通&&&&&& delay(10);&&&& P2=0x06;&&&&&&& //C相导通&&&&&& delay(10);& P2=0x04;&&&&&&& //C D相导通&&& delay(10);&&&& P2=0x0C;&&&&&&& //D相导通&&&&&& delay(10);&&&& P2=0x08;&&&&&&& //D A相导通& &&&&&& delay(10);&& &&& } 电机还是没有反应,考虑到频率问题,延时有大范围改动,但还不是成功(4)主要是第一个问题,一直在网上找不到 解答,望大拿们指点
(1)转子上的磁极密度很大,给一个脉冲最多走一步,并且是经过减速,看不出来,也感觉不出来。
(2)判断电机是否正常的最直接办法就是测四相绕组电阻,都有电阻并且都一样可以判定正常。
(3)走程序时输出是否正常?还有延时是多少?
第一个问题,我给的信号不算是一个脉冲,而是一个持续的高电平。但是步进电机的起始位置是不确定的,假设在08,这时给了一个持续的电平02,那么,步进电机会转到02的位置吗?我问的是这个意思。
知道你想问什么,假如转子上只有两个磁极S、N就会像你说的那样,现在是转子上的磁极是N个,A相给电的时候不是特定的磁极转到A相处,而是离A相最近的那个磁极转到A处,4相的磁极有8个,最近的磁极距离A不会超过1步,所以你不管怎么启动给一个脉冲最多就是走一步,再算上减速比转动的角度大概是0.1度。
相关知识等待您来回答
家用电器领域专家《手把手教你学51单片机-C语言》之九--------步进电机与蜂鸣器_手把手教51单片机_ICKey电子工程师论坛-ICKey电子工程师社区
发表于& 15:45:10
第9章&步进电机与蜂鸣器
& & & 对于技术的学习,希望大家一定要有足够的耐性和韧性。如果你决定从事单片机这门技术,那就一定要坚持学习下去,不能半途而废,当你坚持学习一段时间后你会发现自己慢慢会喜欢这些玩意,对这些东西有了浓厚的兴趣和感情,那你离成功就不远了。学到第九课了,鼓励鼓励自己,再加把劲哦!在本章中我们首先来了解单片机IO的一些细节内容,然后在此基础上再学习两种常用设备的使用方法——步进电机和蜂鸣器。
1.1&单片机IO口的结构
& & & 上节课我们提到了单片机IO口的其中一种“准双向IO”的内部结构,实际上我们的单片机IO口还有另外三种状态,分别是开漏、推挽、高阻态,我们通过图9-1来分析下另外这三种状态。
图9-1&&单片机IO结构示意图
& & & 前边我们简单介绍“准双向IO”的时候,我们是用三极管来说明的,出于严谨的态度,我们这里按照实际情况用MOS管画图示意。实际上三极管是靠电流导通的,而MOS管是靠电压导通的,具体缘由和它们的内部构造有关系,在这里我们暂且不必关心,如果今后有必要了解可以直接查找模拟电子书或者百度相关资料进行细致学习。在单片机IO口状态这一块内容上,我们可以把MOS管当三极管来理解。在图9-1中,T1相当于一个PNP三极管,T2相当于一个NPN三极管。
& & & 其中准双向IO口原理已经讲过了,开漏输出和准双向IO的唯一区别,就是开漏输出把内部的上拉电阻去掉了。开漏输出如果要输出高电平时,T2关断,IO电平要靠外部的上拉电阻才能拉成高电平,如果没有外部上拉电阻IO电平就是一个不确定态。标准51单片机的P0口默认就是开漏输出,如果要用的时候外部需要加上拉电阻。而强推挽输出就是有比较强的驱动能力,如图9-1中第三张小图,当内部输出一个高电平时,通过MOS管直接输出电流,没有电阻的限流,电流输出能力也比较大;如果内部输出一个低电平,那反向电流也可以很大,强推挽的一个特点就是驱动能力强。
& & &单片机IO还有一种状态叫高阻态。通常我们用来做输入引脚的时候,可以将IO口设置成高阻态,高阻态引脚本身如果悬空,用万用表测量的时候可能是高可能是低,它的状态完全取决于外部输入信号的电平,高阻态引脚对GND的等效电阻很大(理论上相当于无穷大,但实际上总是有限值而非无穷大),所以称之为高阻。
这就是单片机的IO口的四种状态,在我们51单片机的学习过程中,主要应用的是准双向IO口,随着我们学习的深入,其它状态也会有接触,在这里介绍给大家学习一下。
1.2&上下拉电阻
& & & 前边似乎我们很多次提到了上拉电阻,下拉电阻,具体到底什么样的电阻算是上下拉电阻,上下拉电阻都有何作用呢?
& & & 上拉电阻就是将不确定的信号通过一个电阻拉到高电平,同时此电阻也起到一个限流作用,下拉就是下拉到低电平。
& & & &比如我们的IO设置为开漏输出高电平或者是高阻态时,默认的电平就是不确定的,外部经一个电阻接到VCC,也就是上拉电阻,那么相应的引脚就是高电平;经一个电阻到GND,也就是下拉电阻,那么相应的引脚就是一个低电平。
上拉电阻应用很多,都可以起到什么作用呢?我们现在主要先了解最常用的以下4点。
1、OC门要输出高电平,必须外部加上拉电阻才能正常使用,其实OC门就相当于单片机IO的开漏输出,其原理可参照图9-1中的开漏电路。
2、加大普通IO口的驱动能力。标准51单片机的内部IO口的上拉电阻,一般都是在几十K欧,比如STC89C52内部是20K的上拉电阻,所以最大输出电流是250uA,因此外部加个上拉电阻,可以形成和内部上拉电阻的并联结构,增大高电平时电流的输出能力。
3、在电平转换电路中,比如我们前边讲的5V转12V的电路中,上拉电阻其实起到的是限流电阻的作用,可以回顾一下图3-8。
4、单片机中未使用的引脚,比如总线引脚,引脚悬空时,容易受到电磁干扰而处于紊乱状态,虽然不会对程序造成什么影响,但通常会增加单片机的功耗,加上一个对VCC的上拉电阻或者一个对GND的下拉电阻后,可以有效的抵抗电磁干扰。
那么我们在进行电路设计的时候,又该如何选择合适的上下拉电阻的阻值呢?
1、从降低功耗的方面考虑应当足够大,因为电阻越大,电流越小。
2、从确保足够的引脚驱动能力考虑应当足够小,电阻小了,电流才能大。
3、在开漏输出时,过大的上拉电阻会导致信号上升沿变缓。我们来解释一下:实际电平的变化都是需要时间的,虽然很小,但永远都达不到零,而开漏输出时上拉电阻的大小就直接影响了这个上升过程所需要的时间,如图9-2所示。想一下,如果电阻很大,而信号频率又很快的话,最终将导致信号还没等上升到高电平就又变为低了,于是信号就无法正确传送了。
图9-2&&上拉电阻阻值对波形的影响
& & & 综合考虑各种情况,我们常用的上下拉电阻值大多选取在1K到10K之间,具体到底多大通常要根据实际需求来选,通常情况下在标准范围内就可以了,不一定是一个固定的值。
1.3&28BYJ-48型步进电机详解与实例
1.3.1&电机的分类
& & & &电机的分类方式有很多,从用途角度可划分为驱动类电机和控制类电机。直流电机属于驱动类电机,这种电机是将电能转换成机械能,主要应用在电钻、小车轮子、电风扇、洗衣机等设备上。步进电机属于控制类电机,它是将脉冲信号转换成一个转动角度的电机,在非超载的情况下,电机的转速、停止的位置只取决于脉冲信号的频率和脉冲数,主要应用在自动化仪表、机器人、自动生产流水线、空调扇叶转动等设备。
& & & 步进电机又分为反应式、永磁式和混合式三种。
反应式步进电机:结构简单成本低,但是动态性能差、效率低、发热大、可靠性难以保证,所以现在基本已经被淘汰了。
永磁式步进电机:动态性能好、输出力矩较大,但误差相对来说大一些,因其价格低而广泛应用于消费性产品。
混合式步进电机:综合了反应式和永磁式的优点,力矩大、动态性能好、步距角小,精度高,但是结构相对来说复杂,价格也相对高,主要应用于工业。
& & &&我们本章内容主要讲解28BYJ-48这款步进电机,先介绍型号中包含的具体含义:
28——步进电机的有效最大外径是28毫米
B——表示是步进电机
Y——表示是永磁式
J——表示是减速型
48——表示四相八拍
1.3.2&28BYJ-48型步进电机原理详解
28BYJ-48是4相永磁式减速步进电机,其外观如图9-3所示:
图9-3&&步进电机外观
& & & 我们先来解释“4相永磁式”的概念,28BYJ-48的内部结构示意图9-4所示。先看里圈,它上面有6个齿,分别标注为0~5,这个叫做转子,顾名思义,它是要转动的,转子的每个齿上都带有永久的磁性,是一块永磁体,这就是“永磁式”的概念。再看外圈,这个就是定子,它是保持不动的,实际上它是跟电机的外壳固定在一起的,它上面有8个齿,而每个齿上都缠上了一个线圈绕组,正对着的2个齿上的绕组又是串联在一起的,也就是说正对着的2个绕组总是会同时导通或关断的,如此就形成了4相,在图中分别标注为A-B-C-D,这就是“4相”的概念。
图9-4&&步进电机内部结构示意图
& & & 现在我们分析一下它的工作原理:
& & &&假定电机的起始状态就如图9-4所示,逆时针方向转动,起始时是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度,这个值就叫做步进角度。而上述这种工作模式就是步进电机的单四拍模式——单相绕组通电四节拍。
& & & 我们再来讲解一种具有更优性能的工作模式,那就是在单四拍的每两个节拍之间再插入一个双绕组导通的中间节拍,组成八拍模式。比如,在从B相导通到C项导通的过程中,假如一个B相和C相同时导通的节拍,这个时候,由于B、C两个绕组的定子齿对它们附近的转子齿同时产生相同的吸引力,这将导致这两个转子齿的中心线对比到B、C两个绕组的中心线上,也就是新插入的这个节拍使转子转过了上述单四拍模式中步进角度的一半,即5.625度。这样一来,就使转动精度增加了一倍,而转子转动一圈则需要8*8=64拍了。另外,新增加的这个中间节拍,还会在原来单四拍的两个节拍引力之间又加了一把引力,从而可以大大增加电机的整体扭力输出,使电机更“有劲”了。
& & & 除了上述的单四拍和八拍的工作模式外,还有一个双四拍的工作模式——双绕组通电四节拍。其实就是把八拍模式中的两个绕组同时通电的那四拍单独拿出来,而舍弃掉单绕组通电的那四拍而已。其步进角度同单四拍是一样的,但由于它是两个绕组同时导通,所以扭矩会比单四拍模式大,在此就不做过多解释了。
& & &&八拍模式是这类4相步进电机的最佳工作模式,能最大限度的发挥电机的各项性能,也是绝大多数实际工程中所选择的模式,因此我们就重点来讲解如何用单片机程序来控制电机按八拍模式工作。
1.3.3&让电机转起来
& & &再重新看一下上面的步进电机外观图和内部结构图:步进电机一共有5根引线,其中红色的是公共端,连接到5V电源,接下来的橙、黄、粉、蓝就对应了A、B、C、D相;那么如果要导通A相绕组,就只需将橙色线接地即可,B相则黄色接地,依此类推;再根据上述单四拍和八拍工作过程的讲解,可以得出下面的绕组控制顺序表,如表9-1所示:
& & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & &表9-1&&八拍模式绕组控制顺序表
我们板子上控制步进电机部分是和板子上的显示控制的74HC138译码器部分复用的P1.0~P1.3,关于跳线我们在第3章已经讲过了,通过调整跳线帽的位置可以让P1.0~P1.3控制步进电机的四个绕组,如图9-5所示。
图9-5&&显示译码与步进电机的选择跳线
& & & 如果要使用电机的话,需要把4个跳线帽都调到跳线组的左侧(开发板上的实际位置),即左侧针和中间针连通(对应原理图中的中间和下边的针),就可以使用P1.0到P1.3控制步进电机了,如要再使用显示部分的话,就要再换回到右侧了。那如果大家既想让显示部分正常工作,又想让电机工作该怎么办呢?跳线帽保持在右侧,用杜邦线把步进电机的控制引脚(即左侧的排针)连接到其它的暂不使用的单片机IO上即可。
& & &再来看一下我们步进电机的原理图,步进电机的控制电路如图9-6所示。
图9-6&&步进电机控制电路
& & &&诚然,单片机的IO口可以直接输出0V和5V的电压,但是电流驱动能力,也就是带载能力非常有限,所以我们在每相的控制线上都增加一个三极管来提高驱动能力。由图中可以看出,若要使A相导通,则必须是Q2导通,此时A相也就是橙色线就相当于接地了,于是A相绕组导通,此时单片机P1口低4位应输出0b1110,即0xE;如要A、B相同时导通,那么就是Q2、Q3导通,P1口低4位应输出0b1100,即0xC,依此类推,我们可以得到下面的八拍节拍的IO控制代码数组:
&&&&unsigned&char&code&BeatCode[8]&=&{&0xE,&0xC,&0xD,&0x9,&0xB,&0x3,&0x7,&0x6&};
& & & 到这里,似乎所有的逻辑问题都解决了,循环将这个数组内的值送到P1口就行了。但是,只要再深入想一下就会发现还有个问题:多长时间送一次数据,也就是说一个节拍要持续多长时间合适呢?是随意的吗?当然不是了,这个时间是由步进电机的启动频率决定的。启动频率,就是步进电机在空载情况下能够正常启动的最高脉冲频率,如果脉冲频率高于该值,电机就不能正常启动。表9-2就是由厂家提供的步进电机参数表,我们来看一下。
& & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & &表9-2&&28BYJ-48步进电机参数表
& & & 表中给出的参数是≥550,单位是P.P.S,即每秒脉冲数,这里的意思就是说:电机保证在你每秒给出550个步进脉冲的情况下,可以正常启动。那么换算成单节拍持续时间就是1s/550=1.8ms,为了让电机能够启动,我们控制节拍刷新时间大于1.8ms就可以了。有了这个参数,我们就可以动手写出最简单的电机转动程序了,如下:
#include&&reg52.h&
unsigned&char&code&BeatCode[8]&=&{&&//步进电机节拍对应的IO控制代码
&&&&0xE,&0xC,&0xD,&0x9,&0xB,&0x3,&0x7,&0x6
void&delay();
void&main()
&&&&unsigned&char&&&//定义一个临时变量
&&&&unsigned&char&index&=&0;&&//定义节拍输出索引
&&&&&&&&tmp&=&P1;&&&&&&&&&&&&&&&&&&&&&&&&//用tmp把P1口当前值暂存
&&&&&&&&tmp&=&tmp&&&0xF0;&&&&&&&&&&&&&&//用&操作清零低4位
&&&&&&&&tmp&=&tmp&|&BeatCode[index];&//用|操作把节拍代码写到低4位
&&&&&&&&P1&&=&&&&&&&&&&&&&&&&&&&&&&&&//把低4位的节拍代码和高4位的原值送回P1
&&&&&&&&index++;&&&&&&&&&&&&&&&&&&&&&&&&&//节拍输出索引递增
&&&&&&&&index&=&index&&&0x07;&&&&&&&&&//用&操作实现到8归零
&&&&&&&&delay();&&&&&&&&&&&&&&&&&&&&&&&&&//延时2ms,即2ms执行一拍
/*&软件延时函数,延时约2ms&*/
void&delay()
&&&&unsigned&int&i&=&200;
&&&&while&(i--);
把程序编译下载到板子上试试吧!看看电机转了没有?要记得换跳线哦!
1.3.4&转动精度与深入分析
& & & 转是转了,但是不是感觉有点不太对劲呢?太慢了?别急,咱们继续。根据本章开头讲解的原理,八拍模式时,步进电机转过一圈是需要64个节拍,而我们程序中是每个节拍持续2ms,那么转一圈就应该是128ms,即1秒钟转7圈多,可怎么看上去它好像是7秒多才转了一圈呢?
& & & 那么,是时候来了解“永磁式减速步进电机”中这个“减速”的概念了。图9-7是这个28BYJ-48步进电机的拆解图,从图中可以看到,位于最中心的那个白色小齿轮才是步进电机的转子输出,64个节拍只是让这个小齿轮转了一圈,然后它带动那个浅蓝色的大齿轮,这就是一级减速。大家看一下右上方的白色齿轮的结构,除电机转子和最终输出轴外的3个传动齿轮都是这样的结构,由一层多齿和一层少齿构成,而每一个齿轮都用自己的少齿层去驱动下一个齿轮的多齿层,这样每2个齿轮都构成一级减速,一共就有了4级减速,那么总的减速比是多少呢?即转子要转多少圈最终输出轴才转一圈呢?
图9-7&&步进电机内部齿轮示意图
& & & 回头看一下电机参数表中的减速比这个参数吧——1:64,转子转64圈,最终输出轴才会转一圈,也就是需要64*64=4096个节拍输出轴才转过一圈,2ms*ms,8秒多才转一圈呢,是不是跟刚才的实验结果正好吻合了?4096个节拍转动一圈,那么一个节拍转动的角度——步进角度就是360/4096,看一下表中的步进角度参数5.625/64,算一下就知道这两个值是相等的,一切都已吻合了。
& & & 关于基本的控制原理本该到这里就全部结束了,但是,我们希望大家都能培养一种“实践是检验真理的唯一标准”的思维方式!回想一下,步进电机最大的特点是什么?精确控制转动量!那么我们是不是应该检验一下它到底是不是能精确呢?精确到什么程度呢?怎么来检验呢?让它转过90度,然后量一下准不准?也行,但是如果它只差了1度甚至不到1度,你能准确测量出来吗?在没有精密仪器的情况很难。我们还是让它多转几个整圈,看看它最后停下的位置还是不是原来的位置。对应的,我们把程序修改一下,以方便控制电机转过任意的圈数。
#include&&reg52.h&
void&TurnMotor(unsigned&long&angle);
void&main()
&&&&TurnMotor(360*25);&&//360度*25,即25圈
while&(1);
/*&软件延时函数,延时约2ms&*/
void&delay()
&&&&unsigned&int&i&=&200;
&&&&while&(i--);
/*&步进电机转动函数,angle-需转过的角度&*/
void&TurnMotor(unsigned&long&angle)
&&&&unsigned&char&&&//临时变量
&&&&unsigned&char&index&=&0;&&//节拍输出索引
&&&&unsigned&long&beats&=&0;&&//所需节拍总数
&&&&unsigned&char&code&BeatCode[8]&=&{&&//步进电机节拍对应的IO控制代码
&&&&&&&&0xE,&0xC,&0xD,&0x9,&0xB,&0x3,&0x7,&0x6
&&&&beats&=&(angle*4096)&/&360;&//计算需要的节拍总数,4096拍对应一圈
&&&&while&(beats--)&&//判断beats不为0时执行循环,然后自减1
&&&&&&&&tmp&=&P1;&&&&&&&&&&&&&&&&&&&&&&&&//用tmp把P1口当前值暂存
&&&&&&&&tmp&=&tmp&&&0xF0;&&&&&&&&&&&&&&//用&操作清零低4位
&&&&&&&&tmp&=&tmp&|&BeatCode[index];&//用|操作把节拍代码写到低4位
&&&&&&&&P1&&=&&&&&&&&&&&&&&&&&&&&&&&&//把低4位的节拍代码和高4位的原值送回P1
&&&&&&&&index++;&&&&&&&&&&&&&&&&&&&&&&&&&//节拍输出索引递增
&&&&&&&&index&=&index&&&0x07;&&&&&&&&&//用&操作实现到8归零
&&&&&&&&delay();&&&&&&&&&&&&&&&&&&&&&&&&//延时2ms,即2ms执行一拍
&&&&P1&=&P1&|&0x0F;&&//关闭电机所有的相
& & & 上述程序中,我们先编写了一个控制电机转过指定角度的函数,这个角度值由函数的形式参数给出,然后在主函数中就可以方便的通过更改调用时的实际参数来控制电机转过任意的角度了。我们用了360*25,也就是25圈,当然你也可以随意改为其它的值,看看是什么结果。我们的程序会执行25*8=200秒的时间,先记下输出轴的初始位置,然后上电并耐心等它执行完毕,看一下,是不是……有误差?怎么回事,哪儿出问题了,不是说能精确控制转动量吗?
& & & 这个问题其实是出在了减速比上,再来看一下,厂家给出的减速比是1:64,不管是哪个厂家生产的电机,只要型号是28BYJ-48,其标称的减速比就都是1:64。但实际上呢?经过我们的拆解计算发现:真实准确的减速比并不是这个值1:64,而是1:63.684!得出这个数据的方法也很简单,实际数一下每个齿轮的齿数,然后将各级减速比相乘,就可以得出结果了,实测的减速比为(32/9)*(22/11)*(26/9)*(31/10)≈63.684,从而得出实际误差为0.0049,即约为百分之0.5,转100圈就会差出半圈,那么我们刚才转了25圈,是不是就差了八分之一圈了,也就是45度,看一下刚才的误差是45度吧。那么按照1:63.684的实际减速比,可以得出转过一圈所需要节拍数是64*63.684≈4076。那么就把上面程序中电机驱动函数里的4096改成4076再试一下吧。是不是看不出丝毫的误差了?但实际上误差还是存在的,因为上面的计算结果都是约等得出的,实际误差大约是0.000056,即万分之0.56,转一万圈才会差出半圈,已经可以忽略不计了。
& & & 那么厂家的参数为什么会有误差呢?难道厂家不知道吗?要解释这个问题,我们得回到实际应用中,步进电机最通常的目的是控制目标转过一定的角度,通常都是在360度以内的,而这个28BYJ-48最初的设计目的是用来控制空调的扇叶的,扇叶的活动范围是不会超过180度的,所以在这种应用场合下,厂商给出一个近似的整数减速比1:64已经足够精确了,这也是合情合理的。然而,正如我们的程序那样,我们不一定是要用它来驱动空调扇叶,我们可以让它转动很多圈来干别的,这个时候就需要更为精确的数据了,这也是我们希望同学们都能了解并掌握的,就是说我们要能自己“设计”系统并解决其中发现的问题,而不要被所谓的“现成的方案”限制住思路。
1.3.5&编写实用程序的基础
& & & 解决了精度问题,让我们再次回到我们的电机控制程序上吧。上面给出的两个例程都不是实用的程序,为什么?因为程序中存在大段的延时,而在延时的时候是什么其它的事都干不了的,想想第二个程序,整整200秒什么别的事都干不了,这在实际的控制系统中是绝对不允许的。那么怎么改造一下呢?当然还是用定时中断来完成了,既然每个节拍持续时间是2ms,那我们直接用定时器定时2ms来刷新节拍就行了。改造后的程序如下:
#include&&reg52.h&
unsigned&long&beats&=&0;&&//电机转动节拍总数
void&StartMotor(unsigned&long&angle);
void&main()
&&&&EA&=&1;&&&&&&&&//使能总中断
&&&&TMOD&=&0x01;&&//设置T0为模式1
&&&&TH0&&=&0xF8;&&//为T0赋初值0xF8CD,定时2ms
&&&&TL0&&=&0xCD;
&&&&ET0&&=&1;&&&&&//使能T0中断
&&&&TR0&&=&1;&&&&&//启动T0
&&&&StartMotor(360*2+180);&&//控制电机转动2圈半
while&(1);
/*&步进电机启动函数,angle-需转过的角度&*/
void&StartMotor(unsigned&long&angle)
&&&&//在计算前关闭中断,完成后再打开,以避免中断打断计算过程而造成错误
&&&&EA&=&0;
&&&&beats&=&(angle&*&4076)&/&360;&&//实测为4076拍转动一圈
&&&&EA&=&1;
/*&T0中断服务函数,用于驱动步进电机旋转&*/
void&InterruptTimer0()&interrupt&1
&&&&unsigned&char&&&//临时变量
&&&&static&unsigned&char&index&=&0;&&//节拍输出索引
&&&&unsigned&char&code&BeatCode[8]&=&{&&//步进电机节拍对应的IO控制代码
&&&&&&&&0xE,&0xC,&0xD,&0x9,&0xB,&0x3,&0x7,&0x6
&&&&TH0&=&0xF8;&&//重新加载初值
&&&&TL0&=&0xCD;
&&&&if&(beats&!=&0)&&//节拍数不为0则产生一个驱动节拍
&&&&&&&&tmp&=&P1;&&&&&&&&&&&&&&&&&&&&&&&&//用tmp把<s
这个家伙很懒,什么也没有留下
上一帖子:
下一帖子:
发表于& 15:45:23
& & & & tmp&=&tmp&&&0xF0;&&&&&&&&&&&&&&//用&操作清零低4位
&&&&&&&&tmp&=&tmp&|&BeatCode[index];&//用|操作把节拍代码写到低4位
&&&&&&&&P1&&=&&&&&&&&&&&&&&&&&&&&&&&&//把低4位的节拍代码和高4位的原值送回P1
&&&&&&&&index++;&&&&&&&&&&&&&&&&&&&&&&&&&//节拍输出索引递增
&&&&&&&&index&=&index&&&0x07;&&&&&&&&&//用&操作实现到8归零
&&&&&&&&beats--;&&&&&&&&&&&&&&&&&&&&&&&&//总节拍数-1
&&&&else&&//节拍数为0则关闭电机所有的相
&&&&&&&&P1&=&P1&|&0x0F;
& & & 程序还是比较简单的,电机转动的启动函数StartMotor只负责计算一个需要的总节拍数beats,然后在中断函数内检测这个变量,不为0时就执行节拍操作,同时将其减1,直到减到0为止。
& & & 这里,我们要特别说明一下的是StartMotor函数中对EA的两次操作。我们可以看到对beats的赋值计算语句是夹在EA=0;EA=1;这两行语句中间的,也就是说这行赋值计算语句在执行前先关闭了中断,而等它执行完后,才又重新打开了中断。在它执行过程中单片机是不会响应中断的,即中断函数InterruptTimer0不会被执行,即使这时候定时器溢出了,中断发生了,也只能等待EA重新置1后,才能得到响应,中断函数InterruptTimer0才会被执行。
& & & 那么为什么要这么做呢?我们来想一下:在本书开始我们就曾提到,我们所使用的STC89C52单片机是8位单片机,这个8位的概念就是说单片机操作数据时都是按8位即按1个字节进行的,那么要操作多个字节(不论是读还是写)就必须分多次进行了。而我们程序中定义的beats这个变量是unsigned&long型,它要占用4个字节,那么对它的赋值最少也要分4次才能完成了。我们想象一下,假如在完成了其中第一个字节的赋值后,恰好中断发生了,InterruptTimer0函数得到执行,而这个函数内可能会对beats进行减1的操作,减法就有可能发生借位,借位就会改变其它的字节,但因为此时其它的字节还没有被赋入新值,于是错误就会发生了,减1所得到的结果就不是预期的值了!所以要避免这种错误的发生就得先暂时关闭中断,等赋值完成后再打开中断。而如果我们使用的是char或bit型变量的话,因为它们都是在CPU的一次操作中就完成的,所以即使不关中断,也不会发生错误。问题分析清楚了,如何取舍还得根据实际情况来,遇上这类问题的时候多多考虑考虑吧。
1.1.1&包含综合应用的实用程序
& & & 上面我们虽然完成了用中断控制电机转动的程序,但实际上这个程序还是没多少实用价值的,我们不能每次想让它转动的时候都上下电啊,是吧。还有就是它不但能正转还得能反转啊,也就是说不但能转过去,还得能转回来呀。好吧,我们就来做一个实例程序吧,结合第8章的按键程序,我们设计这样一个功能程序:按数字键1~9,控制电机转过1~9圈;配合上下键改变转动方向,按向上键后正向转1~9圈,向下键则反向转1~9圈;左键固定正转90度,右键固定反转90;Esc键终止转动。通过这个程序,我们也可以进一步体会到如何用按键来控制程序完成复杂的功能,以及控制和执行模块之间如何协调工作,而你的编程水平也可以在这样的实践练习中得到锻炼和提升。
#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}
signed&long&beats&=&0;&&//电机转动节拍总数
void&KeyDriver();
void&main()
&&&&EA&=&1;&&&&&&&&//使能总中断
&&&&TMOD&=&0x01;&&//设置T0为模式1
&&&&TH0&&=&0xFC;&&//为T0赋初值0xFC67,定时1ms
&&&&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;&&//电机转动方向
&&&&if&((keycode&=0x30)&&&&(keycode&=0x39))&&//控制电机转动1-9圈
&&&&&&&&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];&&&&&//刷新前一次的备份值
&&&&&&&&&&&&}
/*&按键扫描函数,需在定时中断中调用,推荐调用间隔1ms&*/
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*4ms内都是按下状态时,可认为按键已稳定的按下
&&&&&&&&&&&&KeySta[keyout][i]&=&0;
&&&&&&&&else&if&((keybuf[keyout][i]&&&0x0F)&==&0x0F)
&&&&&&&&{&&&//连续4次扫描值为1,即4*4ms内都是弹起状态时,可认为按键已稳定的弹起
&&&&&&&&&&&&KeySta[keyout][i]&=&1;
&&&&//执行下一次的扫描输出
&&&&keyout++;&&&&&&&&&&&&&&&&&&&//输出索引递增
&&&&keyout&=&keyout&&&0x03;&&//索引值加到4即归零
&&&&switch&(keyout)&&&&&&&&&&&//根据索引,释放当前输出引脚,拉低下次的输出引脚
&&&&&&&&case&0:&KEY_OUT_4&=&1;&KEY_OUT_1&=&0;&
&&&&&&&&case&1:&KEY_OUT_1&=&1;&KEY_OUT_2&=&0;&
&&&&&&&&case&2:&KEY_OUT_2&=&1;&KEY_OUT_3&=&0;&
&&&&&&&&case&3:&KEY_OUT_3&=&1;&KEY_OUT_4&=&0;&
&&&&&&&&default:&
/*&电机转动控制函数&*/
void&TurnMotor()
&&&&unsigned&char&&&//临时变量
&&&&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&&=&&&&&&&&&&&&&&&&&&&&&&&&//把低4位的节拍代码和高4位的原值送回P1
&&&&else&&//节拍数为0则关闭电机所有的相
&&&&&&&&P1&=&P1&|&0x0F;
/*&T0中断服务函数,用于按键扫描与电机转动控制&*/
void&InterruptTimer0()&interrupt&1
&&&&static&bit&div&=&0;
&&&&TH0&=&0xFC;&&//重新加载初值
&&&&TL0&=&0x67;
&&&&KeyScan();&&&//执行按键扫描
&&&&//用一个静态bit变量实现二分频,即2ms定时,用于控制电机
&&&&div&=&~
&&&&if&(div&==&1)
&&&&&&&&TurnMotor();
& & & 这个程序是第8章和本章知识的一个综合——用按键控制步进电机转动。程序中有这么几点值得注意,我们分述如下:
1、针对电机要完成正转和反转两个不同的操作,我们并没有使用正转启动函数和反转启动函数这么两个函数来完成,也没有在启动函数定义的时候增加一个形式参数来指明其方向。我们这里的启动函数void&StartMotor(signed&long&angle)与单向正转时的启动函数唯一的区别就是把形式参数angle的类型从unsigned&long改为了signed&long,我们用有符号数固有的正负特性来区分正转与反转,正数表示正转angle度,负数就表示反转angle度,这样处理是不是很简洁又很明了呢?而你对有符号数和无符号数的区别用法是不是也更有体会了?
2、针对终止电机转动的操作,我们定义了一个单独的StopMotor函数来完成,尽管这个函数非常简单,尽管它也只在Esc按键分支内被调用了,但我们仍然把它单独提出来作为了一个函数。而这种做法就是基于这样一条编程原则:尽可能用单独的函数来完成硬件的某种操作,当一个硬件包含多个操作时,把这些操作函数组织在一起,形成一个对上层的统一接口。这样的层次化处理,会使得整个程序条理清晰,既有利于程序的调试维护,又有利于功能的扩充。
3、中断函数中要处理按键扫描和电机驱动两件事情,而为了避免中断函数过于复杂,我们就又分出了按键扫描和电机驱动两个函数(这也同样符合上述2的编程原则),而中断函数的逻辑就变得简洁而清晰了。这里还有个矛盾,就是按键扫描我们选择的定时时间是1ms,而本章之前的实例中电机节拍持续时间都是2ms;很显然,用1ms的定时可以定出2ms的间隔,而用2ms的定时却得不到准确的1ms间隔;所以我们的做法就是,定时器依然定时1ms,然后用一个bit变量做标志,每1ms改变一次它的值,而我们只选择值为1的时候执行一次动作,这样就是2ms的间隔了;如果我要3ms、4ms……呢,把bit改为char或int型,然后对它们递增,判断到哪个值该归零,就可以了。这就是在硬件定时器的基础上实现准确的软件定时,其实类似的操作我们在讲数码管的时候也用过了,回想一下吧。
1.1&蜂鸣器
& & &&蜂鸣器从结构区分分为压电式蜂鸣器和电磁式蜂鸣器。压电式为压电陶瓷片发音,电流比较小一些,电磁式蜂鸣器为线圈通电震动发音,体积比较小。
按照驱动方式分为有源蜂鸣器和无源蜂鸣器。这里的有源和无源不是指电源,而是振荡源。有源蜂鸣器内部带了振荡源,如图9-8所示中,给了BUZZ引脚一个低电平,蜂鸣器就会直接响。而无源蜂鸣器内部是不带振荡源的,要让他响必须给500Hz~4.5KHz之间的脉冲频率信号来驱动它才会响。有源蜂鸣器往往比无源蜂鸣器贵一些,因为里边多了振荡电路,驱动发音也简单,靠电平就可以驱动,而无源蜂鸣器价格比较便宜,此外无源蜂鸣器声音频率可以控制,而音阶与频率又有确定的对应关系,因此就可以做出来“do&re&mi&fa&sol&la&si”的效果,可以用它制作出简单的音乐曲目,比如生日歌、两只老虎等等。
图9-8&&蜂鸣器电路原理图
& & &&我们来看一下图9-8的电路,蜂鸣器电流依然相对较大,因此需要用三极管驱动,并且加了一个100欧的电阻作为限流电阻。此外还加了一个D4二极管,这个二极管叫做续流二极管。我们的蜂鸣器是感性器件,当三极管导通给蜂鸣器供电时,就会有导通电流流过蜂鸣器。而我们知道,电感的一个特点就是电流不能突变,导通时电流是逐渐加大的,这点没有问题,但当关断时,经“电源-三极管-蜂鸣器-地”这条回路就截断了,过不了任何电流了,那么储存的电流往哪儿去呢,就是经过这个D4和蜂鸣器自身的环路来消耗掉了,从而就避免了关断时由于电感电流造成的反向冲击。接续关断时的电流,这就是续流二极管名称的由来。
& & &&蜂鸣器经常用于电脑、打印机、万用表这些设备上做提示音,提示音一般也很简单,就是简单发出个声音就行,我们用程序简单做了个4KHZ频率下的发声和1KHZ频率下的发声程序,同学们可以自己研究下程序,比较下实际效果。
#include&&reg52.h&
sbit&BUZZ&=&P1^6;&&//蜂鸣器控制引脚
unsigned&char&T0RH&=&0;&&//T0重载值的高字节
unsigned&char&T0RL&=&0;&&//T0重载值的低字节
void&OpenBuzz(unsigned&int&frequ);
void&StopBuzz();
void&main()
&&&&unsigned&int&i;
&&&&TMOD&=&0x01;&&//配置T0工作在模式1,但先不启动
&&&&EA&=&1;&&&&&&&&//使能全局中断
&&&&while&(1)
&&&&&&&&OpenBuzz(4000);&&&&&&&&&&//以4KHz的频率启动蜂鸣器
&&&&&&&&for&(i=0;&i&40000;&i++);
&&&&&&&&StopBuzz();&&&&&&&&&&&&&&&//停止蜂鸣器
&&&&&&&&for&(i=0;&i&40000;&i++);
&&&&&&&&OpenBuzz(1000);&&&&&&&&&&//以1KHz的频率启动蜂鸣器
&&&&&&&&for&(i=0;&i&40000;&i++);
&&&&&&&&StopBuzz();&&&&&&&&&&&&&&//停止蜂鸣器
&&&&&&&&for&(i=0;&i&40000;&i++);
/*&蜂鸣器启动函数,frequ-工作频率&*/
void&OpenBuzz(unsigned&int&frequ)
&&&&unsigned&int&&&&&//计算所需的定时器重载值
&&&&reload&=&65536&-&()/(frequ*2);&&//由给定频率计算定时器重载值
&&&&T0RH&=&(unsigned&char)(reload&&&&8);&&//16位重载值分解为高低两个字节
&&&&T0RL&=&(unsigned&char)
&&&&TH0&&=&0xFF;&&//设定一个接近溢出的初值,以使定时器马上投入工作
&&&&TL0&&=&0xFE;
&&&&ET0&&=&1;&&&&&//使能T0中断
&&&&TR0&&=&1;&&&&&//启动T0
/*&蜂鸣器停止函数&*/
void&StopBuzz()
&&&&ET0&=&0;&&&//禁用T0中断
&&&&TR0&=&0;&&&//停止T0
/*&T0中断服务函数,用于控制蜂鸣器发声&*/
void&InterruptTimer0()&interrupt&1
&&&&TH0&=&T0RH;&&&//重新加载重载值
&&&&TL0&=&T0RL;
&&&&BUZZ&=&~BUZZ;&//反转蜂鸣器控制电平
& & &&另外用蜂鸣器来输出音乐,仅仅是好玩而已,应用很少,里边包含了音阶、乐谱的相关内容,程序也有一点复杂,所以就不详细给大家去讲解了。仅提供一个可以播放《两只老虎》的程序,大家可以下载到板子上玩玩,满足一下好奇心。
#include&&reg52.h&
sbit&BUZZ&=&P1^6;&&//蜂鸣器控制引脚
unsigned&int&code&NoteFrequ[]&=&{&&//中音1-7和高音1-7对应频率列表
&&&&523,&&587,&&659,&&698,&&784,&&880,&&988,&&//中音1-7
&&&&,&,&,&1976&&//高音1-7
unsigned&int&code&NoteReload[]&=&{&//中音1-7和高音1-7对应的定时器重载值
&&&&65536&-&()&/&(523*2),&&//中音1
&&&&65536&-&()&/&(587*2),&&//2
&&&&65536&-&()&/&(659*2),&&//3
&&&&65536&-&()&/&(698*2),&&//4
&&&&65536&-&()&/&(784*2),&&//5
&&&&65536&-&()&/&(880*2),&&//6
&&&&65536&-&()&/&(988*2),&&//7
&&&&65536&-&()&/&(1047*2),&//高音1
&&&&65536&-&()&/&(1175*2),&//2
&&&&65536&-&()&/&(1319*2),&//3
&&&&65536&-&()&/&(1397*2),&//4
&&&&65536&-&()&/&(1568*2),&//5
&&&&65536&-&()&/&(1760*2),&//6
&&&&65536&-&()&/&(1976*2),&//7
bit&enable&=&1;&&&//蜂鸣器发声使能标志
bit&tmrflag&=&0;&&//定时器中断完成标志
unsigned&char&T0RH&=&0xFF;&&//T0重载值的高字节
unsigned&char&T0RL&=&0x00;&&//T0重载值的低字节
void&PlayTwoTiger();
void&main()
&&&&unsigned&int&i;
&&&&EA&=&1;&&&&&&&&//使能全局中断
&&&&TMOD&=&0x01;&&//配置T0工作在模式1
&&&&TH0&=&T0RH;
&&&&TL0&=&T0RL;
&&&&ET0&=&1;&&&&&&//使能T0中断
&&&&TR0&=&1;&&&&&&//启动T0
&&&&while&(1)
&&&&&&&&PlayTwoTiger();&&//播放乐曲--两支老虎
&&&&&&&&for&(i=0;&i&40000;&i++);&&//停止一段时间
/*&两只老虎乐曲播放函数&*/
void&PlayTwoTiger()
&&&&unsigned&char&&&&//当前节拍索引
&&&&unsigned&char&&&&//当前节拍对应的音符
&&&&unsigned&int&time&=&0;&&&&&&&//当前节拍计时
&&&&unsigned&int&beatTime&=&0;&&//当前节拍总时间
&&&&unsigned&int&soundTime&=&0;&//当前节拍需发声时间
&&&&//两只老虎音符表
&&&&unsigned&char&code&TwoTigerNote[]&=&{
&&&&&&&&1,&&&2,&&&3,&1,&&&&1,&&&2,&&&3,&1,&&&3,&4,&5,&&&3,&4,&5,
&&&&&&&&5,6,&5,4,&3,&1,&&&&5,6,&5,4,&3,&1,&&&1,&5,&1,&&&1,&5,&1,
&&&&//两只老虎节拍表,4表示一拍,1就是1/4拍,8就是2拍
&&&&unsigned&char&code&TwoTigerBeat[]&=&{
&&&&&&&&4,&&&4,&&&4,&4,&&&&4,&&&4,&&&4,&4,&&&4,&4,&8,&&&4,&4,&8,
&&&&&&&&3,1,&3,1,&4,&4,&&&&3,1,&3,1,&4,&4,&&&4,&4,&8,&&&4,&4,&8,
&&&&for&(beat=0;&beat&sizeof(TwoTigerNote);&)&&//用节拍索引作为循环变量
&&&&&&&&while&(!tmrflag);&&//每次定时器中断完成后,检测并处理节拍
&&&&&&&&tmrflag&=&0;
&&&&&&&&if&(time&==&0)&&//当前节拍播完则启动一个新节拍
&&&&&&&&&&&&note&=&TwoTigerNote[beat]&-&1;
&&&&&&&&&&&&T0RH&=&NoteReload[note]&&&&8;
&&&&&&&&&&&&T0RL&=&NoteReload[note];
&&&&&&&&&&&&//计算节拍总时间,右移2位相当于除4,移位代替除法可以加快执行速度
&&&&&&&&&&&&beatTime&=&(TwoTigerBeat[beat]&*&NoteFrequ[note])&&&&2;
&&&&&&&&&&&&//计算发声时间,为总时间的0.75,移位原理同上
&&&&&&&&&&&&soundTime&=&beatTime&-&(beatTime&&&&2);
&&&&&&&&&&&&enable&=&1;&&//指示蜂鸣器开始发声
&&&&&&&&&&&&time++;
&&&&&&&&else&&//当前节拍未播完则处理当前节拍
&&&&&&&&&&&&if&(time&&=&beatTime)&&//当前持续时间到达节拍总时间时归零,
&&&&&&&&&&&&{&&&&&&&&&&&&&&&&&&&&&&&&&&//并递增节拍索引,以准备启动新节拍
&&&&&&&&&&&&&&&&time&=&0;
&&&&&&&&&&&&&&&&beat++;
&&&&&&&&&&&&}
&&&&&&&&&&&&else&&//当前持续时间未达到总时间时,
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&time++;&&&//累加时间计数
&&&&&&&&&&&&&&&&if&(time&==&soundTime)&&//到达发声时间后,指示关闭蜂鸣器,
&&&&&&&&&&&&&&&&{&&&&&&&&&&&&&&&&&&&&&&&&&&&//插入0.25*总时间的静音间隔,
&&&&&&&&&&&&&&&&&&&&enable&=&0;&&&&&&&&&&&//用以区分连续的两个节拍
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&}
/*&T0中断服务函数,用于控制蜂鸣器发声&*/
void&InterruptTimer0()&interrupt&1
&&&&TH0&=&T0RH;&&&//重新加载重载值
&&&&TL0&=&T0RL;
&&&&tmrflag&=&1;
&&&&if&(enable)&&&//使能时反转蜂鸣器控制电平
&&&&&&&&BUZZ&=&~BUZZ;
&&&&else&&&&&&&&&&&//未使能时关闭蜂鸣器
&&&&&&&&BUZZ&=&1;
1.2&练习题
1、能够理解清楚单片机IO口的结构。
2、能够看懂上下拉电阻的电路应用并且熟练使用上下拉电阻。
3、理解28BYJ-48减速步进电机的工作原理。
4、能够熟练编写步进电机正反转任意角度的程序。
5、学会蜂鸣器发声的方法。
这个家伙很懒,什么也没有留下
发表于& 15:45:57
这个家伙很懒,什么也没有留下
发表于& 10:25:30
这个家伙很懒,什么也没有留下
发表于& 15:22:50
这个家伙很懒,什么也没有留下
地址:上海市漕河泾松江新兴产业园32号11层(莘砖公路258号)
客服热线:400-:00-18:30)
投诉电话:021-8}

我要回帖

更多关于 步进电机型号 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信