《中学生C51单片机易学实战入门教程》第二课 让LED闪起来实验准备:使用第一课已装配好的实验板 课程内容: 一、分析LED接线原理图:着重左上角的 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之间改变,达到控制闪的目的。 五、增补C语言注释功能为增加程序可读性,可以在代码行后面使用//符号,并加入文字说明,帮助理解这段代码的功能,还可以使用 /* 说明文字 */ 方式注释,其中说明文字部分可以分多行。 附记: |
|