单片机c51参考资料.docx

上传人:b****6 文档编号:6153644 上传时间:2023-01-04 格式:DOCX 页数:63 大小:313.48KB
下载 相关 举报
单片机c51参考资料.docx_第1页
第1页 / 共63页
单片机c51参考资料.docx_第2页
第2页 / 共63页
单片机c51参考资料.docx_第3页
第3页 / 共63页
单片机c51参考资料.docx_第4页
第4页 / 共63页
单片机c51参考资料.docx_第5页
第5页 / 共63页
点击查看更多>>
下载资源
资源描述

单片机c51参考资料.docx

《单片机c51参考资料.docx》由会员分享,可在线阅读,更多相关《单片机c51参考资料.docx(63页珍藏版)》请在冰豆网上搜索。

单片机c51参考资料.docx

单片机c51参考资料

第4章学习C51例题,入门C简单程序设计

本章的例题都是简单C语言例题,例题中没有复杂的C语言语法与难以理解的数据结构,因此特别适合入门学习C语言。

本章所有例题都在实验板上实验过,实验用51单片机电路板的接线图如图4-1所示。

图4-1实验电路接线图

图中单片机为AT89S51或是STC89C51RC,其P0、P1口连接共阳极数码管,P2口连接8个低电平有效的LED灯(发光二极管),P3口连接8个低电平有效的按钮。

若是采用STC89C51RC单片机,由于ISP需要占用串行口,若是不切换引脚,则与P3.0和P3.1相连的按钮不能使用。

若是采用AT89S51,由于SPI编程需要P1.5、P1.6和P1.7引脚通信,所以编程后,需要切换引脚,才能使P1口连接的数码管正常显示。

4.1第一部分

[例题4-1]控制P2_0引脚相连的LED灯闪烁。

该程序只能用软件单步仿真,因为在实际的单片机上运行,使P2_0引脚变为低电平后,立刻又将其变为高电平,眼睛不能看到LED灯闪烁,但是使用软件单步仿真,可以看到P2_0引脚的电平变化。

#include"AT89X51.H"//包含头文件

voidmain(void)//主程序

{

while

(1)//无限循环

{

P2_0=0;//亮灯

P2_0=1;//灭灯

}}

软件单步仿真的窗口如图4-2所示。

图4-2软件单步仿真的窗口

[例题4-2]使P2_0引脚相连的LED灯闪烁。

C源程序如下:

#include"AT89X51.H"//头文件

voidmain(void)//主程序

{

unsignedintn;//声明变量

while

(1)//无限循环

{

P2_0=~p2_0;//引脚取反,与引脚相连的LED灯闪烁

for(n=0;n<2000;n++);//循环语句延时

}}

编译并链接后,在图4-3所示的仿真窗口仿真。

图4-3例题1-2所示的仿真窗口

可以在图4-4所示屏幕左下角的命令窗口输入变量名后回车的方法查看变量;也可以屏幕右下角的Local窗口或是Watch窗口观察变量。

图4-4查看变量

选择View/MomoryWindow菜单,屏幕弹出图4-5所示的存储器窗口。

图4-5存储器窗口

在Address对话框输入:

“d:

0x00”就可以看到data空间的从0x00开始的所有内存。

如图4-5。

输入“i:

0x00”,就可以看到idata空间的所有内存的值。

输入“x:

0x00”,就可以看到xdata空间的所有内存的值。

输入“c:

0x00”,就可以看到code空间的所有程序。

[例题4-3]如下程序实现流水灯的方法是,依次灭掉前一个灯,然后点亮后一个灯,再延时一会,不断循环,就可以看到流水灯的效果了。

源程序如下:

#include"AT89X51.H"//头文件

voidmain(void)//主程序

{

unsignedintn;

while

(1)

{

P2_3=1;P2_0=0;//灭掉P2_3,点亮P2_0=0

for(n=0;n<20000;n++);//循环延时

P2_0=1;P2_1=0;//灭掉P2_0,点亮P2_1=0

for(n=0;n<20000;n++);//循环延时

P2_1=1;P2_2=0;//灭掉P2_1,点亮P2_2=0

for(n=0;n<20000;n++);//循环延时

P2_2=1;P2_3=0;//灭掉P2_2,点亮P2_3=0

for(n=0;n<20000;n++);//循环延时

}}

[例题4-4]双按键控制的LED灯。

用单片机读取按键的值,并使用一个与P3-1引脚相连的按键点亮与P2_0引脚连接的LED,用另一个与P3_2引脚相连的按键关闭与P2_0引脚连接的LED。

用单片机可以读取某个IO的值,因为51单片机的I/O口,如果处于输出1的状态(51上电后I/O就默认为1),这时的I/O口内部简化成为一个几十K的电阻上拉到电源VCC(P0口除外),因此可以作为输入引脚。

P0口没有上拉电阻,相当于一个悬空的引脚,就是高阻状态,如果用P0口,必须在外部接上拉电阻。

这里用的是内部有上拉电阻的P3口连接按键。

如果直接读一个没有与地短路按键的I/O引脚,就会读到高电平,就是逻辑1。

如果这个I/O引脚通过按键与地短路。

这时会读到低电平,就是逻辑0。

该例的源程序如下:

#include"AT89X51.H"

voidmain(void)//主程序

{

while

(1)

{

if(P3_1==0)//判断按键,如果按键按下(逻辑0),则执行将LED点亮的动作

{P2_0=0;}

if(P3_2==0)//判断按键,如果按键按下(逻辑0),则执行将LED灭掉的动作

{P2_0=1;}

}}

该例的仿真窗口如图4-6所示。

图4-6例题1-4的仿真窗口

[例题4-5]单按键控制LED发光,就是用一个与P3_0引脚连接的按键控制P2_0相连LED的亮和灭两种状态。

按一次按键灯亮,再按一次按键灯灭。

再按一次又亮,再按一次灯又灭。

程序用一个位(bit)变量Mark来做一个标记,然后在按键的控制下,使标记变化,再根据这个标记的值,使LED亮或灭。

因为按键按下时可能会有抖动的情况,就是每次按下按键时,可能会发生多次接点的接触,相当于按键一下子按下了很多次,导致程序识别多次按键的输入。

按键的抖动一般都是发生在刚按下按键和松开按键的时候,只要避开这一段时间,等按键稳定按下或者松开时,就可以正确读取按键的状态。

所以,当读到第一次按键的值时,要等待一会,躲过按键抖动后,再处理其他操作;在松开后,也需要等一会,免得程序检测到松开的抖动以为又是按键按下。

(在更复杂的应用中,需要在按下延时之后重新验证按键是否按下)

因为程序是循环运行的,当一次按键处理后,又会再循环回来继续检测,如果您的按键这时还没有松开,就又会被读到一次,并做处理。

所以还要做一个特殊的处理,识别到一个按键并处理完成之后,还要等待这个按键松开后,再继续循环运行。

源程序如下:

#include"AT89X51.H"

voidmain(void)//主程序

{

bitmark;//定义位变量

unsignedintn;//定义循环变量

while

(1)//无限循环

{

if(P3_0==0)//如果按键按下

{

for(n=0;n<1000;n++);//延时一段时间,等待按键完全按下

mark=~mark;//翻转标记

while(!

P3_0)//等待按键弹起

for(n=0;n<1000;n++);//等待按键完全弹起

}

P2_0=mark;//点亮或是关灭LED灯

}}

[例题4-6]定时器中断控制LED闪烁。

由于51单片机从中断发生到进入中断的时间不定,是3至8个机器周期,在进入了中断后,软件才重新置定时器初始值,这样就会存在定时误差。

不是精确定时,如果要精确定时,需要使用定时器自动装载方式,也就是在定时器溢出的同时,硬件逻辑就自动把定时器初始值装载进去,而不是在中断服务程序里赋初始值,只有这样才可以实现精确定时,在精确定时的情况下,定时误差由晶振的频率不稳定引起。

源程序如下:

#include"AT89X51.H"

voidmain(void)//主程序

{

TMOD=0X01;//定时器0,工作模式1,16位定时模式,GATE=0,C/T=0

TR0=1;//启动定时器

ET0=1;//允许定时器中断

EA=1;//允许总中断

while

(1);//无限循环

}

timer0()interrupt1//定时器0中断服务程序

{

TH0=0X00;//写入定时器TH初值00H

TL0=0X06;//写入定时器TL初值06H,计数器溢出值为65530

P2_0=~P2_0;//闪烁LED

}

[例题4-7]精确定时0.5s。

在定时器中断服务函数里,设置了一个静态变量kk,静态变量kk的值在进入函数时是不会被初始化的,而是保持上次的值。

它用来计数定时器的溢出次数,也就是T0中断服务函数进入的次数,每溢出2000次,就是间隔0.5秒,使LED灯亮灭一次。

源程序如下:

#include"AT89X51.H"

voidmain(void)//主程序

{

TMOD=0x02;//定时器0,工作模式2(0000,0010),8位定时模式

//GATE=0,C/T=0,

TH0=0x06;//写入预置初值到定时器TH,预置6,使250微秒溢出一次(12MHz)

TL0=0x06;//写入预置值

TR0=1;//启动定时器

ET0=1;//允许定时器中断

EA=1;//允许总中断

while

(1);//无限循环

}

timer0()interrupt1//定时器0中断服务程序

{

staticunsignedintkk;//设置局部静态变量

kk++;//每中断一次加1

if(kk==2000)//当中断2000次后,相当于0.5秒0.25ms*2000=0.5s

{kk=0;

P2_0=~P2_0;//闪烁LED

}}

[例题4-8]精确定时的流水灯。

所有的中断都要尽快的运行和退出,中断服务程序越短越好,这样才不至于干扰主程序的工作和其他中断的运行。

所以应该尽量把程序代码从中断服务函数里搬到主程序中运行。

对于定时器的中断的工作方式,可以建立一个全局变量的标记,在中断服务程序中置位这个标记,然后就退出中断服务。

在主程序里检查到这个全局变量标记之后,就运行相关的程序。

对于CPU任务比较多的程序来说,这种工作方式可以获得较好的工作效率。

采用查表的方式,将要点亮的LED灯预先设置好,到了指定的时间,就一起将LED灯亮灭信息送到P2口。

源程序如下:

#include"AT89X51.H"

unsignedintldelay=0;//长定时溢出标记ldelayt,预置值是0

voidmain(void)//主程序

{

unsignedcharcodeledp[4]={0xfe,0xfd,0xfb,0xf7};//预定的灯亮灭顺序,写入P2

unsignedintledi;//用来确定表格位置的变量

TMOD=0x02;//定时器0,工作模式2(0000,0010),8位定时模式

//GATE=0,C/T=0,

TH0=0x06;//写入预置初值到定时器TH,预置6,250微秒溢出一次(12MHz)

TL0=0x06;//写入预置值

TR0=1;//启动定时器

ET0=1;//允许定时器中断

EA=1;//允许总中断

while

(1)//无限循环

{

if(ldelay==1)//时间溢出标记为1,处理如下事件

{

ldelay=0;//清除溢出标记

P2=ledp[ledi];//读出一个表格值送到P2口

ledi++;//指向下一个表格值

if(ledi==4)//如果表格查过一遍

ledi=0;//指向第一个表格值

}}}

timer0()interrupt1//定时器0中断服务程序

{

staticunsignedintkk;//定义静态局部变量

kk++;//每次中断服务程序执行,kk增加1

if(kk==2000)//T0的预置值0x06,溢出2000次就是0.5秒钟,晶振12MHz

{

kk=0;//如果中断服务程序执行2000次,则执行下一个语句

ldelay=1;//将该标记置1,以便主程序处理

}}

[例题4-9]变速流水LED灯。

控制流水LED灯的流动速度从慢到快自动变化。

程序中设置了一个变量sp,用来保存流水灯的移动速度,也是定时器的累计时间溢出次数。

在主程序中定时修改sp的数值,溢出的时间就会改变,流水LED灯的移动速度也就改变了。

在每跑完一圈,就改变一次速度。

源程序如下:

#include"AT89X51.H"

unsignedintldelay=0;//定义长定时溢出标记,预置是0

unsignedintsp=23;//定义变速标记,预置是20

voidmain(void)//主程序

{

unsignedcharcodeledp[4]={0xfe,0xfd,0xfb,0xf7};

//预定的灯亮灭顺序表格,用于写入P2口

unsignedintledi;//用来确定表格位置的变量

TMOD=0x02;//定时器0,工作模式2(0000,0010),8位定时模式

TH0=0x06;//写入预置初值到定时器TH,预置6,则250微秒溢出一次(12MHz)

TL0=0x06;//写入预置值

TR0=1;//启动定时器

ET0=1;//允许定时器中断

EA=1;//允许总中断

while

(1)//无限循环

{

if(ldelay==1)//若是时间溢出标记为1,处理如下语句

{

ldelay=0;//清除时间溢出标记

P2=ledp[ledi];//读出一个表格值送到P2口

ledi++;//指向下一个表格值

if(ledi==4)

{

ledi=0;//到了最后一个灯就换到第一个

sp--;//速度级别减1

if(sp==3)//如果速度级别减到3

sp=23;//恢复速度最慢的级别23

}}}}

timer0()interrupt1//定时器0中断服务程序

{

staticunsignedintkk;//定义静态局部变量

kk++;//每次中断服务,kk加1

if(kk==(100*sp))//最少次数为100*sp=300,时间为300次*0.25ms

//最多次数为2300次,时间为2300次*0.25ms

//如果kk=(100*sp),执行如下语句

{

kk=0;

ldelay=1;//当kk值与100*sp值相等,将ldelay标记置1,则处理灯亮与显示速度

}}

[例题4-10]4个按键控制4个流水速度的流水LED灯

用4个按键,控制流水LED灯的4种不同的跑动速度。

每按一个键,就赋给定时器0一个不同的溢出次数,使流水速度发生变化。

主程序执行了2个任务:

一个是流水LED灯,一个是检测按键。

源程序如下:

#include"AT89X51.H"

unsignedintldelay=0;//长定时溢出标记,预置是0

unsignedintsp=10;//变速标记,预置是10

voidmain(void)//主程序

{

unsignedcharcodeledp[4]={0xfe,0xfd,0xfb,0xf7};//预定的LED灯亮灭顺序数组

unsignedintledi;//用来确定表格位置的变量

TMOD=0x02;//定时器0,工作模式2(0000,0010),8位定时模式

TH0=0x06;//写入预置初值到定时器TH,预置6,则250微秒溢出一次(12MHz)

TL0=0x06;//写入预置值

TR0=1;//启动定时器

ET0=1;//允许定时器中断

EA=1;//允许总中断

while

(1)//无限循环

{

if(ldelay==1)//如果溢出标记为1,处理如下语句

{

ldelay=0;//清除溢出标记

P2=ledp[ledi];//读出一个表格值送到P2口

ledi++;//指向下一个表格值

if(ledi==4)

{

ledi=0;//返回第1个表格值

}}

if(P3_0==0)sp=3;//按键P3_0设置流水灯速度

if(P3_1==0)sp=8;

if(P3_2==0)sp=13;

if(P3_3==0)sp=18;

}}

timer0()interrupt1//定时器0中断服务程序

{

staticunsignedintkk;//定义静态局部变量

kk++;//每次中断服务,kk加1

if(kk==(100*sp)||(kk>2000))//kk=(100*sp)||(kk>2000)时,执行如下语句

//kk溢出次数为100*sp,最少为300次,最多为2000次

{

kk=0;

ldelay=1;//每次溢出后,将ldelay标记置1

}}

[例题4-11]单按键控制流水LED灯的流水速度。

用一个按键来实现流水LED灯的10级调速。

每按一次按键,流水速度就降低一级,共10级。

源程序如下:

#include"AT89X51.H"

unsignedintldelay=0;//长定时溢出标记,预置是0

unsignedintsp=10;//变速标记,预置是10

unsignedintspr=1;//按键按下次数标记

voidmain(void)//主程序

{

unsignedcharcodeledp[4]={0xfe,0xfd,0xfb,0xf7};//预定LED灯亮灭的表格

unsignedintledi,n;//ledi用来指示显示顺序,n是循环变量

TMOD=0x02;//定时器0,工作模式2(0000,0010),8位定时模式

TH0=0x06;//写入预置初值6到定时器TH,则250微秒溢出一次(12MHz)

TL0=0x06;//写入预置值

TR0=1;//启动定时器

ET0=1;//允许定时器中断

EA=1;//允许总中断

while

(1)//无限循环

{

if(ldelay==1)//溢出标记为1,处理如下语句

{

ldelay=0;//清除溢出标记

P2=ledp[ledi];//读出一个值送到P2口

ledi++;//指向下一个显示值

if(ledi==4)

{

ledi=0;//指向表格的第一个显示值

}}

if(P3_0==0)//如果读到按键K1为0

{

for(n=0;n<1000;n++);//等待按键稳定延时

while(!

P3_0);//等待按键松开循环

for(n=0;n<1000;n++);//等待按键稳定松开延时

spr++;//按键次数加1

if(spr==10)spr=1;//如果按键按下10次,则返回1

sp=spr*2;//流水LED灯速度等于按键次数乘以2

}}}

timer0()interrupt1//定时器0中断

{

staticunsignedintkk;//定义静态局部变量

kk++;//每次中断服务,kk加1

if(kk==(100*sp)||(kk>2200))//kk溢出次数为100*sp,最少为200次,最多为2000次

{

kk=0;

ldelay=1;//每次溢出,使ldelay=1

}}

[例题4-12]按照预定的流水速度控制流水LED灯。

首先预先定义了一个变化的顺序speedcode,每次流水灯循环一圈,就根据预定设置的表格数据来决定下一圈的流水速度。

这样就实现了流水LED灯速度按照预定的顺序自动变化。

#include"AT89X51.H"

unsignedintldelay=0;//溢出标记,预置是0

unsignedintsp=10;//变速标记,预置是20

unsignedintcodespr[10]={3,1,5,12,3,20,2,10,1,4};//10个预定义流水速度的表格

voidmain(void)//主程序

{

unsignedcharcodeledp[4]={0xfe,0xfd,0xfb,0xf7};//预定的流水LED灯亮灭表格

unsignedintledi,i;//定义指向灯亮灭顺序表格ledi与流水速度表格的元素的变量i

TMOD=0x02;//定时器0,工作模式2(0000,0010),8位定时模式

TH0=0x06;//写入预置初值6到定时器TH,则250微秒溢出一次(12MHz)

TL0=0x06;//写入预置值

TR0=1;//启动定时器启动

ET0=1;//允许定时器中断

EA=1;//允许总中断

while

(1)//无限循环

{

if(ldelay==1)//溢出标记为1,执行如下语句

{

ldelay=0;//清除溢出标记

P2=ledp[ledi];//读出灯亮灭顺序表格到P2口

ledi++;//指向表格的下1个元素

if(ledi==4)

{

ledi=0;//指向表格的第1个元素

sp=spr[i];//按照流水速度表格设定流水灯速度

i++;//指向速度表格中的下1个元素

if(i==10)i=0;//如果速度为10,则返回速度0

}}}}

timer0()interrupt1//定时器0中断服务程序

{

staticunsignedintkk;//设置静态局部变量

kk++;//每次中断,kk加1

if(kk==(100*sp)||(kk>2200))//溢出次数为100*sp

{

kk=0;//溢出次数清零

ldelay=1;//溢出标记为1

}}

[例题4-13]外引脚中断控制LED灯。

外引脚中断,就是外部I/O引脚INT0和INT1产生的中断。

对应的引脚是P3_2和P3_3。

当按下连接在P3_2引脚的按键,使其接地时,可以触发一个INT0中断,控制LED灯的亮或灭。

源程序如下:

#include"AT89X51.H"

voidmain(void)//主程序

{

IT0=1;//外中断负边沿产生中断

EX0=1;//外中断0使能

EA=1;//允许总中断

while

(1);//主程序无限循环,没有循环体

}

int0()interrupt0//外中断0服务程序

{P2_0=!

P2_0;}//在中断服务程序中闪烁LED灯

[例题4-14]脉冲宽度调制(PWM)方式控制LED灯亮度

在一定的频率的方波中,调整高电平和低电平的占空比,即可实现LED灯亮度控制。

如图4-7所示,程序中使用定时器0产生2.5ms周期脉冲,使用占空比控制变量scale控制占空比,在低电平期间使LED灯亮,在高电平期间使LED灯灭,改变scale就改变了高电平与低电平的时间,因此也就控制了LED灯的亮度。

图4-7PWM占空比控制示意图

源程序如下:

#include"AT89X51.H"//模拟PWM输出控制灯的10个亮度级

unsignedintscale;//占空比控制变量

voidmain(void)//主程序

{

unsignedintn;//延时循环

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 自然科学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1