实验准备:使用第一课已装配好的实验板
课程内容:
着重左上角的 LED电路部分。
LED正极接电源5V,现程序控制 LED负极(原理图中管脚P1^0)为0V,LED两端正向有电压差,于是LED导通,发光。
那如何控制让 LED灯 不发光呢?
只要LED两端没有电压差就可以了,现在LED正极通过电阻已接上5V电压,只要LED负极也是5V电压,LED就不会导通,不发光了。
上一课中,控制LED负极电压的代码是:
P10 = 0;
表示控制管脚 P1^0的电压为 0V,现在要改成控制管脚 P1^0的电压为 5V,这块单片机芯片只认识两个数字0与1,0代表0V,1代表5V。于是让LED灯不发光,只须改成以下程序代码:
P10 = 1;
相当简单。
动手改程序,编译,下载到实验板中观察运行结果。
实验板没反应?不会坏了吧。
当然不是坏,分析下电路原理吧。
在没有加电的时候,LED灯两端电压为0,没电的状态下肯定不会发光。
当下载程序后,实验板加上电了,LED正极接上了5V电压,LED负极让程序控制了,也是5V电压,两端没有产生能让LED导通的电压差,于是LED不发光。
修改控制LED的代码改为 P10 = 0; 下载实验板,LED灯又发光了。
再修改控制LED的代码改为 P10 = 1; 下载实验板,LED灯又不发光了。
再修改控制LED的代码改为 P10 = 0; 下载实验板,LED灯又发光了。
… …
我想让LED 像星星一样闪啊闪,而这样的只能是不发光或只发光的状态不合要求。
那就是 先控制LED 发光一段时间,然后再控制LED不发光一段时间,如此重复下去。
单片机的工作状态是:只要给它加电,它就永不停息的工作,不断执行设定的程序,直到断电或烧坏。
所以,单片机是不会自己暂停一小段时间来让LED转状态的,必须由程序来控制。那单片机既然是永不停息的工作,又如何让编程序控制单片机暂停一小段时间呢?方法很简单,就是让它空跑,做无用的工作,达到消耗时间的目的,也就是浪费一下它的时间,实现延时效果。
开发单片机的前辈已设计好这个消耗时间的程序,称为延时函数,如下:
void mDelay(uint Delay)
{
uint i;
for(;Delay>0;Delay--)
{
for(i=0;i<>
{
;
}
}
}
先不管这代码是如何设计,先把这段代码输入到上一课的程序中,并控制LED灯的状态切换。
#include 'reg52.h'
#define uint unsigned int
#define uchar unsigned char
sbit P10= P1^0;
void mDelay(uint Delay)
{
uint i;
for(;Delay>0;Delay--)
{
for(i=0;i<>
{
;
}
}
}
void main()
{
P10=0;
mDelay(1000);
P10=1;
mDelay(1000);
}
要注意,输入延时函数时,两行#define和整个延时函数代码要放在主程序段 void main()之前。因为要前面先有代码功能,后面才可能拿来使用,也就是调用延时功能。
代码量增加了,要注意按花括号分层级缩进代码,方便以后分析程序。
代码量增加了,这时可能编译时又会报错,必须要花时间去排除错误,这就叫查BUG和修复BUG。编代码,查BUG,修复BUG,测试,这几步重重复复,就是编程序的工作过程。
编译正确,下载到实验板上,可以看到达到了让LED闪的效果,可是闪得很慢,一秒才闪一下。
想改快点,也很简单,代码中的
mDelay(1000);
括号中的数据称为参数,给不同的数据,延时函数就有不同的工作效果。可以分别把1000改成500、100、20等做实验检验下效果,实践是检验真理的唯一方法,必须动手做实验。
虽然实验有了效果,但是我们却仍然不知道为何能有这正确的结果。
我们来分块分析。
首先,主程序 main()里面的代码,容易理解,只要知道代码
mDelay(1000);
能延时一秒就行了。
然后再看mDelay 延时函数内部代码及相关代码,这回多了两行代码,这两行称为预编译定义:
#define uint unsigned int //用 uint 代表unsigned int
#define uchar unsigned char //用uchar 代码unsigned char
以后的程序中所有的uint 和uchar 在代码编译时会自动换成unsigned int和unsigned char,也就是就输入代码量减少了,但结效果不变,省时省力。
这两行基本代码以后的程序中都会出现。
void mDelay(uint Delay) //功能模块:延时函数,带控制参数
{
uint i; //后面要用到的临时变量,使用前要先定义
for(;Delay>0;Delay--) //不断重复运行括号的代码,直到Delay减到0
{
for(i=0;i<124;i++)>124;i++)>
//直到i加到124才停止
{
; // 仅分号,没命令,效果是运行一次就空转一次
}
}
}
要点:其中 for()这行括号里面,第一位置放的是初始值,留空的话表示不用管初始值,中间位置放置控制结束的条件,条件符合就继续工作,条件不符合时就退出,第三位置放置的是每完成一次工作后要做的小小改变,达到控制程序的目的。
这个for()命令,称为循环结构,用于重复做一段相同的工作。
优化程序,能让编程素质提高。
细心的同学可能会发现,在main()程序段中
P10=0;
mDelay(1000);
P10=1;
mDelay(1000);
先控制了LED发光,然后延时1秒,再控制LED不发光,然后延时1秒,然后 … … 没有然后了,程序结束了。但是为何程序却能按原意重复一闪一闪?
没错,程序的确是结束了。
于是LED不发光了。
但是,由于单片机加电之后是永不停息的工作,于是它重新回到初始加电状态,于是又从头再次执行了 main()代码,于是LED又闪了。
也就是说,这个LED不停闪动。并不是我们的程序控制,而是单片机需要不停工作。
这可不行,这方式中有部分时间不是由程序控制,也就是不可控。这是编程大忌,绝不能让程序编着编着变成不由我控制了。
那个不停的工作也必须由代码控制。
刚好,刚学会使用的 for()循环结构派上用场了。
给LED灯加上永远循环的控制,main()主程序变成:
void main()
{
for(;;)
{
P10=0;
mDelay(1000);
P10=1;
mDelay(1000);
}
}
也就是不用管初始条件,不用管是否结束,只要做下去就是了。
继续优化。
功能增加了,代码也增加了,单片机做的工作也多了,这样工作时间就花多了,于是工作效率就下降了。
为提高效率,程序可以修改成如下:
#include 'reg52.h'
#define uint unsigned int
#define uchar unsigned char
sbit P10= P1^0;
void mDelay(uint Delay)
{
uint i;
for(;Delay>0;Delay--)
for(i=0;i<>
}
void main()
{
P10=!P10;
mDelay(1000);
}
可以看到,程序长度一下子少了一半,这是因为 C语言中,如果控制的命令只有一行,那么可以省去配对的花括号。
主程序main()中,命令由原来的4行变成2行,只因一个叹号!
符号!是C语言的一个运算符,就像数学课中的 +(加) –(减)一样,它的作用是取反,单片机只认识0与1两个数,0取反就是1,1取反就是0 。
P10=!P10;
这代码就实现了每次循环让P10在0与1之间改变,达到控制闪的目的。
为增加程序可读性,可以在代码行后面使用//符号,并加入文字说明,帮助理解这段代码的功能,还可以使用 /* 说明文字 */ 方式注释,其中说明文字部分可以分多行。
联系客服