3.2单片机的C51程序设计
3.2.1C51 基础知识
C语言是一种编译型程序设计语言,它兼顾了多种高级语言的特点,并具备汇编语言的功能。用C语言开发系统可以大大缩短开发周期,明显增强程序的可读性,便于改进、扩充和移植。而针对8051的C语言日趋成熟,成为了专业化的实用高级语言。
一、C51的特点
C语言作为一种非常方便的语言而得到广泛的支持,很多硬件开发都用C语言编程,如:各种单片机、DSP、ARM等。
C语言程序本身不依赖于机器硬件系统,基本上不作修改或仅做简单修改就可将程序从不同的单片机中移植过来直接使用。
C语言提供了很多数学函数并支持浮点运算,开发效率高,故可缩短开发时间,增加程序可读性和可维护性。
二.单片机的C51与汇编ASM—51相比,有如下优点:
(1)对单片机的指令系统不要求了解,仅要求对8051的存储器结构有初步了解;(2)寄存器分配、不同存储器的寻址及数据类型等细节可由编译器管理;(3)程序有规范的结构,可分成不同的函数,这种方式可使程序结构化;
(5)由于具有方便的模块化编程技术,使已编好程序容易移植;(4)提供的库包含许多标准子程序,具有较强的数据处理能力;
的,不能随意给一个变量赋任意的值,因为变量在单片机的内存中是要占据空间的,变量大小不同,
所占据的空间就不同。所以在设定一个变量之前,必须要给编译器声明这个变量的类型,以便让编译3.2.2 C51 的基本数据类型
器提前从单片机内存中分配给这个变量合适的空间。单片机的C语言中常用的数据类型如下表所示:
数据类型 | 关键字 | 长度(bit) | 长度(byte) | 值域范围 |
位类型 | bit | 1 | —— | 0,1 |
无符号字符型 | unsigned char | 8 | 1 | 0~255 |
有符号字符型 | char | 8 | 1 | -128~127 |
无符号整型 | unsigned int | 16 | 2 | 0~65535 |
有符号整型 | int | 16 | 2 | -32768~32767 |
无符号长整型 | unsigned long | 32 | 4 | 0~232-1 |
有符号长整型 | long | 32 | 4 | -231~-231-1 |
单精度实型 | float | 32 | 4 | 3.4e-38~3.4e38 |
双精度实型 | double |
| 8 | 1.7e-308~1.7e308 |
3.2.3 C51 的基本运算 |
|
1、算术运算符
顾名思义,算术运算符就是执行算术运算的操作符号。除了一般人所熟悉的四则运算(加减乘除)外,还有取余数运算,如下表所示:
符号 | 功能 | 范例 | 说明 |
+ | 加 | A=x+y | 将x 与y 的值相加,其和放入A 变量 |
- | 减 | B=x-y | 将x 变量的值减去y 变量的值,其差放入B 变量 |
* | 乘 | C=x*y | 将x 与y 的值相乘,其积放入B 变量 |
/ | 除 | D=x/y | 将x 变量的值除以y 变量的值,其商数放入D 变量 |
% | 取余数 | E=x%y | 将x 变量的值除以y 变量的值,其余数放入E 变量 |
程序范例:
main()
{
intA,B,C,D,E,x,y;
x=8;
y=3;
A=x+y;
B=x-y;
D=x/y; C=x*y;
}程序结果 E=x%y;
A=11、B=5、C=24、D=2、E=2
2、关系运算符
关系运算符用于处理两个变量间的大小关系,如下表所示。
符号 | 功能 | 范例 | 说明 |
== | 相等 | x==y | 比较x 与y 变量的值,相等则结果为1,不相等则为0 |
!= | 不相等 | x!=y | 比较x 与y 变量的值,不相等则结果为1,相等则为0 |
> | 大于 | x>y | 若x 变量的值大于y 变量的值,其结果为1,否则为0 |
< | 小于 | x<y | 若x 变量的值小于y 变量的值,其结果为1,否则为0 |
>= | 大等于 | x>=y | 若x 变量的值大于或等于y 变量的值,其结果为1,否则为0 |
<= | 小等于 | x<=y | 若x 变量的值小于或等于y 变量的值,其结果为1,否则为0 |
程序范例: |
|
main()
{
IntA,B,C,D,E,F,x,y;
x=9;
y=4;
A=(x==y);
B=(x!=y);
C=(x>y);
D=(x<y);
E=(x>=y);
F=(x<=y);
}
程序结果:
A=0、B=1、C=1、D=0、E=1、F=0
3、逻辑运算符
逻辑运算符就是执行逻辑运算功能的操作符号。
|
main()
{
intA,B,C,x,y,z;
x=9;
y=8;
z=10;
A=(x>y)&&(y<z);
B=(x==y)||(y<=z);
C=!(x>z);
}
程序结果: |
|
位运算符与逻辑运算符非常相似,它们之间的差异在于位运算符针对变量中的每一位,逻辑运算符则是对整个变量进行操作。位运算的运算方式如下表所示:
符号 | 功能 | 范例 | 说明 |
& | 及运算 | A=x&y | 将x 与y 变量的每个位,进行与运算,其结果放入A 变量 |
| | 或运算 | B=x|y | 将x 与y 变量的每个位,进行或运算,其结果放入B 变量 |
^ | 异或 | C=x^y | 将x 与y 变量的每个位,进行异或运算,其结果放入C 变量 |
~ | 取反 | D=~x | 将x 变量的每一位进行取反 |
<< | 左移 | E=x<<n | 将x 变量的值左移n 位,其结果放入E 变量 |
>> | 右移 | F=x>>n | 将x 变量的值右移n 位,其结果放入F 变量 |
程序范例:
main()
{
charA,B,C,D,E,F,x,y;
y=0x62; /*即0110 0010*/ x=0x25;/*即0010 0101*/
B=x|y; C=x^y; A=x&y;
D=~x
E=x<<3;
F=x>>2
}
程序结果:
x: 0010 0101 x: 0010 0101 x: 0010 0101 x:~00100101 y: &0110 0010 y: |0110 0010 y:^0110 0010 1101 1010 00100000 0110 0111 0100 0111
即A=0x20 即B=0x67 即C=0x47 即D=0xda将x的值左移三位的结果为:
0 0 1 0 0 1 0 1 0 0 1 0 0 1 0 1 0 0 0
移出的三位“001”丢失,后面三位用0 填充,因此运算后的结果是00101000B,即E=0x28。
将x 的值右移两位的结果为:
0 0 1 0 0 1 0 1
00 0 0 1 0 0 1 01
移出去的两位“01”丢失,前面两位用“0”填充;因此,运算后的结果是00001001B,即F=0x09。
6、递增/减运算符
递增/减运算符也是一种很有效率的运算符,其中包括递增与递减两种操作符号,如下表所示:
符号 | 功能 | 范例 | 说明 |
++ | 加1 | x++ | 将x 变量的值加1 |
-- | 减1 | x-- | 将x 变量的值减1 |
程序范例:
main()
{
intA,B,x,y;
x=6;
y=4;
A=x++;
B=y--;
}
A=7,B=3 程序结果:
3.2.4 C51 的流程控制语句C51中的基础语句 1、while循环语句的格式如下:
While(表达式)
{
语句;
}
特点:先判断表达式,后执行语句。
原则:若表达式不是0,即为真,那么执行语句。否则跳出while语句往下执行。
程序范例:
while(1)//表达式始终为1,形成死循环
{
语句;
}
2、for 循环语句
for 语句是一个很实用的计数循环,其格式如下:格式:for(表达示1;表达示2;表达示3)
{
语句;
}
执行过程:
(1)求解一次表达式1。
(2)求解表达式2,若其值为真(非0 即为真),则执行for中语句。然后执行第3步。否则结束for语句,直接跳出,不再执行第3步。
(3)求解表达式3。
(4)跳到第2步重复执行。
程序范例1:a=0;
for(i=0;i<8;i++)//控制循环执行8次
{
a++;
}
程序执行结果:a=8
程序范例2:a=0;
{ for(x=100;x>0;x--)//控制循环执行100次
3、if 选择语句
if-else语句提供条件判断的语句,称为条件选择语句,其格式如下:
if(表达示)
{
语句1;
}
else
{
语句2;
}
在这个语句里,将先判断表达式是否成立,若成立,则执行语句1;若不成立,则执行语句2。
如下图所示:
其中else部分也可以省略,写成如下格式,流程图如下所示:
if(表达示)
{ 语句; } 其它语句;
3.2.5C51 的函数
1、无返回值、不带参数的函数的写法
【例1】写出一个完整的调用子函数的例子,用单片机控制一个LED灯闪烁发光。用P1口的第
一个引脚控制一个LED灯,1秒钟闪烁一次。
#include<reg52.h> //头文件
#defineuint unsigned int //宏定义
sbitD1=P1^0; //声明单片机P1口的第一位
uintx,y;
voidmain()
{
while(1)//大循环
{
D1=0; //点亮第一个发光二极管
for(x=500;x>0;x--)
for(y=110;y>0;y--);
D1=1; //关闭第一个发光二极管
for(x=500;x>0;x--)
} for(y=110;y>0;y--);
套语句,for(x=500;x>0;x--) }
for(y=110;y>0;y--);
在C语言中,如果有些语句不止一次用到,而且语句的内容都相同,那么就可以把这样的一些
语句写成一个不带参数的子函数,当在主函数中需要这些语句时,直接调用这些语句就可以了。上面
的for嵌套语句就可以写成如下子函数的形式:
voiddelay() //延时子程序延时约z毫秒
{
for(x=500;x>0;x--)
for(y=110;y>0;y--);
}
其中void表示这个函数执行完后不返回任何数据,即它是一个无返回值的函数,delay是函数名,
一般写成方便记忆和读懂的名字,也就是一看到函数名就知道此函数实现的内容是什么,但注意不要
和C语言中的关键字相同。紧跟函数名的是一个空括号,这个括号里没有任何数据或符号(即C语
言中的参数),因此这个函数是一个无参数的函数。接下来的两个大括号中的语句是子函数中的语句。
这就是无返回值、无参数函数的写法。
需要注意的是,子函数可以写在主函数的前面或是后面,但是不可以写在主函数的里面。当写在
后面时,必须要在主函数之前声明子函数。声明方法是:将返回值特性、函数名及后面的小括号完全
复制,如果无参数,则小括号里面为空;若是带参数函数,则需要在小括号里依次写上参数类型,只
写参数类型,无须写参数,参数类型之间用逗号隔开,最后在小括号的后面加上分号“;”。当子函
数写在主函数前面时,不需要声明,因为写函数体的同时就已经相当于声明了函数本身。通俗地讲,
声明子函数的目的是为了编译器在编译主程序的时候,当它遇到一个子函数时知道有这样一个子函数
存在,并且知道它的类型和带参情况等信息,以方便为这个子函数分配必要的存储空间。
例2就是调用不带参数子函数的例子,通过调用子函数代替for嵌套语句,这样程序看起来简单。
【例2】
#include<reg52.h> //头文件
#defineuint unsigned int //宏定义
sbitD1=P1^0; //声明单片机P1口的第一位
voiddelay(); //声明子函数
voidmain()
{ | while(1) | //大循环 |
|
{ | |||
| |||
delay(); | //延时500 毫秒 | ||
}
}
voiddelay() //延时子程序延时约500毫秒
{
uintx,y;
for(x=500;x>0;x--)
for(y=110;y>0;y--);
}
2、带参数函数的写法及调用
有了前面第一节的铺垫,这一节会容易得多。对于前面讲的子函数delay(),调用一次延时
500ms,如果我们要延时300ms,那么就要在子函数里把x的值赋为300,要延时200ms就要把x 的值
赋为200,这样会很麻烦,如果会使用带参数的子函数会让问题简单化。将前面的子函数改为如下:
voiddelay(unsigned int z)
{
uintx,y;
for(x=z;x>0;x--) //x=z即延时约z毫秒
for(y=110;y>0;y--);
}
上面代码中delay后面的括号中多了一句“unsignedint z”,这就是这个函数所带的一个参数,z
是一个unsignedint型变量,又叫这个函数的形参,在调用此函数时用一个具体真实的数据代替此形
参,这个真实数据又被称为实参,在子函数里面所有和形参名相同的变量都被实参代替。使用这种带
参数的子函数会使问题方便很多,如要调用一个延时300ms的函数就可以写成“delay(300);”,要延
时200ms可以写成“delay(200);”例3是一个调用带参数函数的例子。
【例3】写出一个完整的调用子函数的例子,用单片机控制一个LED灯闪烁发光。用P1口的第
一个引脚控制一个LED灯,让它亮500ms,灭800ms。
#include<reg52.h> //头文件
#defineuint unsigned int //宏定义
sbitD1=P1^0; //声明单片机P1口的第一位
void main() void delay(uint z); //声明子函数
{
{ while(1)
D1=0; //点亮第一个发光二极管
delay(500); //延时500毫秒
D1=1; //关闭第一个发光二极管
delay(800); //延时800毫秒
}
}
voiddelay(unsigned int z) //延时子程序延时约500毫秒
{
uintx,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
3.3 电子钟设计制作
3.3.1工作任务与工作计划
工作任务:
制作一个24小时的电子钟,利用按键可以调整时间。
利用8位数码管分别显示小时、分钟、秒。三个按键分别为“功能键”、“加键”、“减键”,“功能键”用来选择要调整小时、分钟还是秒钟,按下一次可以调节秒钟,第一个LED灯点亮;按下两次 ;按下三次可以调节小时,对应第三个LED灯点亮。第四次按下时回到调节分钟,第二个LED灯点亮
正常走时状态。选好要调整的是哪一位后,按一次“加键”数码管的值加1,按一次“减键”数码管的值减1。
3.3.2硬件电路与软件程序设计
硬件电路设计:
1、数码管显示部分
采用八位共阴极的数码管,使用共阴极的数码管,在P0口通过上拉电阻增加趋动电流,公共端低电平扫描。
2、LED灯指示部分
如图所示,LED的阴极接I/O口,阳极通过限流电阻和电源相连。
放光二极管(LED)具有单向导电性,通过5mA左右的电流即可以发光,电流越大,其亮度越
联一个电阻的目的示为了通过发光二极管的电流不要太大,因此这个电阻又称为“限流电阻” 。强,但若电流过大,会烧毁二极管,一般将电流控制在3mA~20mA之间。在这里,给放光二极管串
的电压为1.7V,发光二极管的阴极为低电平,即0V,阳极串接一电阻,电阻的另一端为VCC,为5V,因此电阻两端的电压为5V-1.7V=3.3V,通过发光二极管的电流在3~20mA 之间,那么关于“限流电阻”大小的选择:欧姆定律是大家熟悉的:U=IR,当放光二极管正常导通时,其两端
限流电阻的范围在165~1000欧姆之间。如下图的仿真采用330欧姆的电阻。
3、按键电路部分
按键的连接方法很简单,如下图所示,按键一侧的端口与单片机的任一I/O口相连。按键按下时,其触电电压如图所示。从图中可以看出,理想波形和实际波形之间是有区别的,实际波形在按下和释放的瞬间都有抖动现象,抖动时间的长短和按键的机械特性有关,一般为5~10ms。通常手动按下键后立即释放,这个动作中稳定闭合的时间超过20ms。因此单片机在检测键盘是否按下时都要加上去抖动操作,有专用的去抖电路,也有专用的去抖芯片,但通常用软件延时的方法就能很容易解决抖动问题,而没有必要再添加多余的硬件电路。
软件程序设计:
/*24小时时钟电路,三个按键分别是:功能键、加键、减键*/
#include<reg52.h>
#define uint unsigned int
#define uchar unsigned char
char miao,fen,shi;/*定义按键*/
sbit s1=P3^0; //功能键,选择调节时钟位sbits2=P3^1; //加键
sbit led1=P1^0; //调整秒钟的指示灯sbitled2=P1^1; //调整分钟的指示灯sbit s3=P3^2; //减键
sbitled3=P1^2; //调整小时的指示灯
sbitwei0=P2^0; //定义八个数码管的位选
sbitwei1=P2^1;
sbitwei2=P2^2;
sbitwei3=P2^3;
sbitwei4=P2^4;
sbitwei5=P2^5;
sbitwei6=P2^6;
sbitwei7=P2^7;
ucharcode table[]={
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71,0x40}; //共阴极数码管编码
voiddelay(uint z) //延时函数
{
uintx,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
voidstart() //定时器初始化
{
TMOD=0x01;
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
EA=1;
ET0=1;
TR0=1;
}
voiddisplay() //显示子程序,分别显示秒、分、小时
{ | wei7=0; |
wei6=0;
P0=table[miao/10];
delay(1);
wei6=1;
wei5=0;
P0=table[16];
delay(1);
wei5=1;
wei4=0;
P0=table[fen%10];
delay(1);
wei4=1;
wei3=0;
P0=table[fen/10];
delay(1);
wei3=1;
wei2=0;
P0=table[16];
delay(1);
wei2=1;
wei1=0;
P0=table[shi%10];
delay(1);
wei1=1;
wei0=0;
delay(1); P0=table[shi/10];
void keyscan() //键盘扫描子程序{ wei0=1;
}
uchars1num;
if(s1==0)//功能键
{
delay(5);
if(s1==0) //确认功能键被按下
{
while(!s1);
s1num++; //功能键按下次数记录
if(s1num==1) //功能键第一次按下调整"秒钟" {
} | TR0=0; //关闭定时器 |
|
led1=0; //点亮第一个指示灯 |
|
if(s1num==2) //功能键第二次按下调整"分钟"
{
led2=0; //点亮第二个指示灯
led1=1; //关闭第一个指示灯
}
if(s1num==3) // 功能键第三次按下调整"小时"{
led3=0; //点亮第三个指示灯
led1=1; //关闭第一个指示灯
led2=1; //关闭第二个指示灯
}
if(s1num==4) // 功能键第四次按下
{
led1=1; //将三个指示灯都关闭
led2=1;
led3=1;
s1num=0; //记录按键数清零
} | } | } | TR0=1; //启动定时器开始走 |
| |||
{ | |||
if(s2==0)//加键
{
delay(5);
if(s2==0)//确认加键被按下
{
while(!s2); //释放确认
switch(s1num)
{
case1: //若功能键第一次被按下
miao++; // 则调整秒加1
if(miao==60) //若满60 后清零 miao=0;
break;
case2: //若功能键第二次被按下
fen++; // 则调整分钟加1
if(fen==60)//若满60后清零
fen=0;
break;
case3: //若功能键第三次被按下 shi++;// 则调整小时加1
if(shi==24)//若满24后清零 shi=0;
break;
}
}
}
if(s3==0)//减键
delay(5); {
switch(s1num)
{
case 1: //若功能键第一次被按下
miao--; // 则调整秒减1
if(miao<0) //若减到负数则将其重新设置为59
miao=59;
break;
case 2: //若功能键第二次被按下
fen--;// 则调整分减1
if(fen<0) //若减到负数则将其重新设置为59
fen=59;
break;
case 3: //若功能键第三次被按下 shi--; // 则调整分减1
if(shi<0) //若减到负数则将其重新设置为23
shi=23;
break;
}
}
}
}
}
voidmain()
{
start(); //调用初始化函数
while(1) //进入主程序大循环
{
keyscan(); //调用键盘扫描程序
display(); //调用数码管显示子程序
}
void my_timer0() interrupt 1 //中断服务程序}
TH0=(65536-50000)/256; //重新装定时器初值 TL0=(65536-50000)%256; {
uchar aa;
aa++; //中断累加次数
if(aa==20) //20 次50ms为1秒
{
aa=0;
miao++;
if(miao>=60)//秒加到60则进位分钟
{
miao=0; //同时秒数清零
fen++;
if(fen>=60) //分钟加到60则进位小时
{ | fen=0; |
|
| ||
shi++; |
if(shi>=24) //小时加到24则清零
{
shi=0;
}
}
}
}
}
3.3.3调试运行
第一次按下“功能键”。调整秒钟的指示灯亮,按下“加键”和“减键”可以调整秒表的时间。如图所示。
第二次按下“功能键”。调整分钟的指示灯亮,按下“加键”和“减键”可以调整分钟的时间。如图所示。
第三次按下“功能键”可以调整小时。
第四次按下后正常走时。
知识梳理与总结
用定时器T0定时,程序中有按键检测模块,数码管动态显示模块。综合利用这些模块,可以锻炼程序编写和调试能力。
习题6:试在此24小时程序中修改,实现整点提示功能。
学习情境4:简易洗衣机模型设计制作
教学导航
引言
洗衣机、电冰箱、空调机、电视机、微波炉、越来越多的消费类电子产品需要用到单片机,例如:
手机、IC卡、汽车电子设备等。单片机控制整个流程自动完成洗衣机的整个洗涤过程,了解这个控制过程对学习单片机有重要的作用,并能通过几个模块的综合利用,锻炼编程的能力。
1.1工作任务与分析
工作任务:简易洗衣机模型设计
产品设计制作任务书:(1)按下“运行”键控制洗衣机自动完成洗涤、清洗、脱水整个过程。洗涤过程为1分钟,此过程电机交替中速正反转。清洗过程为2分钟,此过程电机交替高速正反转。脱水过程为1min,此过程电机高速正转。(2)用四位数码管实时显示工作状态,第一位显示洗涤、清洗、脱水三个状态,分别对应显示“1”、“2”、“3”。第三位显示电机转速,“高”、“中”、“低”速
任务分析:从简易洗衣机模型设计的要求中我们可以分析得出:既要求电机能正反转,又要求能
度分别对应显示“3”、“2”、“1”。
后综合调用这两个模块就可以顺利完成此设计任务。1.2资讯的收集与分析控制电机的转速,从这两个要求出发可以先完成两个子模块的设计,第一个模块需要完成的功能是:趋动电机正反转,第二个模块完成的功能是:改变PWM(占空比)的输出从而控制电机的风速。最
(1)直流电机的趋动:用单片机趋动直流电机时,需要加趋动电路,为直流电机提供足够大的趋动电流。使用不同的直流电机,其趋动电流也不同,要根据实际需求选择合适的趋动电路,通常有以下几种趋动电路:三级管电流放大趋动电路、电机专用趋动模块和达林顿趋动器等。如果是趋动单个电机,并且电机的趋动电流不大时,可用三极管搭建趋动电路,不过这样稍微麻烦一些。如果电机这种模块接口简单,操作方便,所需要的趋动电流较大,可直接选用市场上现成的电机专用趋动模块,并可为电机提供较大的趋动电流。
在简易洗衣机模型设计中,只需要趋动单个电机,并且电机趋动电流不大,所以我们采用三极管搭建的趋动电路。
(2)直流电机转速的控制:当需要调节直流电机的转速时,使单片机的相应I/O口输出不同占空比的PWM波形即可。
PWM 是英文Pulse WidthModulation(脉冲宽度调制)的缩写,它是按一定规律改变脉冲序列的脉
冲宽度,以调节输出量和波形的一种调制方式,我们在控制系统中最常用的是矩形波PWM信号,在控 如下图所示,制时需要调节PWM波的占空比。
占空比是指高电平持续时间在一个周期时间内的百分比。控制电机转速时,占空比越大,速度越快,如果全为高电平,占空比为100%时,速度达到最快。
当用单片机I/O口输入PWM信号时,可采用以下三种方法:
(1)利用软件延时,当高电平延时时间到时,对I/O口电平取反变成低电平,然后再延时;当低电平延时时间到时,再对该I/O口电平取反,如此循环就可得到PWM信号。
(2)利用定时器。控制方法同上,只是在这里利用单片机的定时器来定时进行高、低电平的翻转,而不用软件延时。
(3)利用单片机自带的PWM控制器。STC12系列单片机自带PWM控制器,STC系列单片机无此功能,其他型号的很多单片机也带有PWM控制器,如PIC单片机,AVR单片机等。
占空比50%
占空比75%
1.3硬件电路与软件程序设计
1、硬件电路设计:
在下图中,在两个继电器的两端都反相接了一个二极管,这个二极管非常重要,当使用电磁继电
(1)二极管保护电路:
间会产生一个很强的反向电动势,通常这个二极管叫做消耗二极管,器时必须接。原因如下
上,很容易将三极管烧毁。
(2)正反转电路设计
用单片机ATC51控制直流电机正反转电路设计部分中,将由ATC52的P3.0,P3.1,P3.6通过晶体管控制继电器,当P3.0输出高电平,P3.1输出低电平,并P3.6输出高电平时,三极管Q1、Q3导通,而三极管Q2截止,从而导致与Q1相连的继电器吸合,电机因两端产生电压而顺时针转动。另一种情况下,当P3.0输出低电平,P3.1输出高电平,并P3.6输出高电平时,三极管Q2、Q3导通,而三极管Q1截止,从而导致与Q2相连的继电器吸合,电机因两端产生电压而逆时针转动。当“运行”“停止”键分别控制整个系统的自动运行和停止。
(3)电机速度控制部分
确定正反转的状态后,再通过改变P3.6输出的占空比控制电机的转速。(4)数码管显示电路设计
使用共阴极的数码管,在P0口通过上拉电阻增加趋动电流,公共端低电平扫描。
第一个子模块的程序设计:2、软件电路设计:
2.5s,反转5s,在数码管上正转显示1,反转显示0,停转显示"P"。
图1:电机停转
图2:电机顺时针转
图3:电机逆时针转
图1 P3.0、P3.1口均输出低电平,继电器RL1和RL2 均不吸合,电机停转
图2P3.0 口均输出高电平,P3.1口输出低电平,继电器RL1吸合,电机顺时针转
#include<reg51.h> #define uint unsigned int
#defineuchar unsigned char
uintdd,direction;
ucharcode table[]={
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71,0x73};
sbitm1=P3^0; //声明直流电机的正传位置
sbit m2=P3^1; //声明直流电机的反转位置sbit wei0=P2^7; //第一位数码管位选
void delay(uint z)
{
uintx,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
voidinit()
{
TMOD=0x10;
TH1=(65536-50000)/256;
TL1=(65536-50000)%256;
EA=1;
ET1=1;
TR1=1;
}
{ void time1() interrupt 3
dd++; if(dd<50) // 2.5s 内电机停转,数码管显示"P"字母TH1=(65536-50000)/256;
TL1=(65536-50000)%256;
{
m1=0;
m2=0;
direction=16; //数组中第16个数送给P0 口后显示字母"P"
}
if(dd>=50&&dd<150) //2.5s 到7.5s之间电机正转,数码管显示"1"
{
m1=1;
m2=0;
direction=1;
} if(dd>=150&&dd<200) //7.5s 到10s之间电机停转,数码管显示"P"
{
m1=0;
m2=0;
direction=16;
}
if(dd>=200&&dd<300) //10s 到15s之间电机反转,数码管显示"0" {
m1=0;
m2=1;
direction=0;
}
if(dd>=300) //大于15s清零
{
dd=0;
}
}
voiddisplay()
{ } | wei0=0; |
|
wei0=1; | ||
voidmain()
{
init();
while(1)
{
display(); //调用显示子函数
}
}
子模块2是通过PWM控制电机的风速,硬件设计和上一个模块是相似的,只是多加了一个按钮,通过按钮调节风速的高低,并且“高”、“中”、“低”速度分别对应在数码管上显示“3”、“2”、“1”。
图4:电机以低速转动
图5:电机以中速转动
图6 电机以高速转动
图4:电机以低速转动
图5:电机以中速转动
模块2 的程序设计:#include<reg52.h>
#defineuchar unsigned char
#defineuint unsigned int
uchartime,speedflag;
sbitm1=P3^0;
sbitm2=P3^1;
sbitpwm=P3^6;
sbitkey1=P1^0;
sbitkey2=P1^1;
sbit key3=P1^2;
sbit wei0=P2^7;
uchar code table[]={
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71};
voiddelay(uint z)
{
uintx,y;
for(x=z;x>0;x--)
for(y=120;y>0;y--);
}
voidinit_time() //使用定时器T0的方式2,200us中断一次{
TMOD=0x02;
EA=1;
TH0=(255-200);
ET0=1; TL0=(255-200);
} void sever0() interrupt 1 TR0=1;
{
time++;
if(speedflag==1) //占空比为2:3
{
if(time==1)
pwm=0;
if(time==3)
{
pwm=1;
time=0;
}
}
if(speedflag==2) //占空比为4:5
{
if(time==1)
pwm=0;
if(time==5)
{
pwm=1;
time=0;
}
}
if(speedflag==3)//占空比为6:7
{
if(time==1)
pwm=0;
if(time==7)
{
pwm=1;
time=0;
}
} }
void key() { if(key1==0)
{
delay(3);
if(key1==0)
{
m1=0;
m2=1;
speedflag++; //速度标志位
if(speedflag==4)
speedflag=1;
while(!key1);
} | } | } |
|
|
voiddisplay() //用一位数码管显示速度
{
wei0=0;
P0=table[speedflag];
delay(1);
wei0=1;
}
voidmain()
{
init_time(); //调用初始化函数
while(1)
{
key();//调用按键检测函数
} display(); //调用数码管显示函数
} void display() //用一位数码管显示速度{
wei0=0;
P0=table[speedflag];
delay(1);
wei0=1;
}
voidmain()
{
init_time(); //调用初始化函数
m1=0;
m2=1; | //调用按键检测函数 |
|
while(1) |
| |
{ | ||
key(); |
display();//调用数码管显示函数
}
}
简易洗衣机模块设计是以上两个子模块的综合利用。如下是简易洗衣机模块的程序设计:
#include<reg51.h>
#defineuint unsigned int
#defineuchar unsigned char
uchartime,direction,speedflag,state;
uintcc,dd;
ucharcode table[]={
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71,0x73};
sbitm1=P3^0; //声明直流电机的正传位置
sbit pwm=P3^6; //声明占空比输出口sbitm2=P3^1; //声明直流电机的反转位置
sbitwei0=P2^4;
sbit key1=P1^0; sbit key2=P1^1; //"停止"按键
sbitwei3=P2^7;
voiddelay(uint z)
{
uintx,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
voidinit()
{
TMOD=0x12; //同时使用定时器0 和定时器1
TH1=(65536-50000)/256; //T1 中断50ms 中断一次
TL1=(65536-50000)%256;
TH0=(255-200); //T0 中断200us 中断一次
TL0=(255-200);
ET1=1;
ET0=1;
TR1=1;
TR0=1;
}
voidsever0() interrupt 1
{
time++;
if(speedflag==1) //占空比为2:3
{
if(time==1)
pwm=0;
if(time==3)
{
time=0; pwm=1;
} if(speedflag==2) //占空比为4:5 }
{
if(time==1)
pwm=0;
if(time==5)
{
pwm=1;
time=0;
}
}
if(speedflag==3)//占空比为6:7
{
if(time==1) |
|
{
pwm=1;
time=0;
}
}
}
voidtime1() interrupt 3
{
TH1=(65536-5000)/256;
TL1=(65536-5000)%256;
cc++;
dd++;
if(cc<3600) //3分钟内电机都是交替正反转 {
if(dd<50) // 2.5s内电机停转
m1=1; {
} if(dd>=50&&dd<150) //2.5s 到7.5s之间电机正转
{
m1=1;
m2=0;
direction=1;
}
if(dd>=150&&dd<200) //7.5s 到10s 之间电机停转
{
m1=1;
m2=1;
direction=16;
}
if(dd>=200&&dd<300) //10s 到15s 之间电机反转
{
m1=0;
m2=1;
direction=0;
}
if(dd>=300) //大于15s清零
{
dd=0;
}
}
if(cc<1200) //洗涤过程为1分钟,此过程电机交替中速正反转{
state=1;
speedflag=2; //speedflag速度标志位
}
if(cc>=1200&&cc<3600)// 清洗过程为2分钟,此过程电机交替高速正反转。
{
speedflag=3; state=2;
{ }
if(cc>=3600&&cc<4800) //脱水过程为1分钟,此过程电机高速正转。
state=3;
speedflag=3;
m1=1;
m2=0;
}
if(cc>=4800) //三个过程完成后电机停止转动,状态显示位显示"P",风速显示"0"{
state=16;
speedflag=0;
m1=1;
m2=1;
} | } | EA=0; |
|
|
voiddisplay()
{
wei0=0;
P0=table[state];//第五位数码管显示状态
delay(1);
wei0=1;
wei3=0;
P0=table[speedflag]; //最后一位数码管显示速度 delay(1);
wei3=1;
}
voidkeyscan()
{
if(key1==0)//启动按钮
delay(3); {
{ if(key1==0)
while(!key1);
}
}
if(key2==0)//停止按钮
{
delay(3);
if(key2==0)
{
EA=0;
m1=1;
m2=1;
} | } | while(!key2); |
|
|
}
voidmain()
{
init();
while(1)
{
display(); //调用数码管显示子程序 keyscan();
//调用键盘扫描子程序 }
}
1.4运行演示与总结评估
图7洗衣机模型初始状态;
图8洗衣机模型处于“洗涤”状态,中速转动图9洗衣机模型处于“清洗”状态,高速转动图10洗衣机模型处于“脱水”状态,高速转动
图7 洗衣机模型初始状态
图8 洗衣机模型处于“洗涤”状态,中速转动
图9洗衣机模型处于“清洗”状态,高速转动
图10 洗衣机模型处于“脱水”状态,高速转动
图9完成整个过程,处于停止状态
知识梳理与总结
简易洗衣机模型设计这个程序设计中包括了电动机正反转、PWM调速、用定时器控制在连续的时间段中顺序做某些事情,这些模块。灵活地应用这些模块能实现多种效果,并锻炼调用已有的程序模块的能力。
习题8:设计一个电风扇模型。能完成以下功能:
(1)用4位数码管实时显示电风扇的工作状态,最高位显示风类,后三位显示定时时间。
(1)设计“1档”、“2档”、“3档”三个风类键用于设置风类。“1档”运行时PWM的占空比为1:2,“2档”运行时PWM的占空比为2:3,“3档”运行时PWM的占空比为3:4。 (2)设计一个“摇头”键用于控制电机摇头:电机停2.5s,正转5s,停2.5s,反转5s。
(3)设计一个“定时”键,用于定时时间长短设置,每按一次“定时”键,定时时间增加2min,工作功能如下:
00min | 02min | 04min | 06min | 08min | 10min |
Copyright © 2019- huatuo9.cn 版权所有 赣ICP备2023008801号-1
违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务