调用内置函数实现流水灯_第1页
调用内置函数实现流水灯_第2页
调用内置函数实现流水灯_第3页
调用内置函数实现流水灯_第4页
调用内置函数实现流水灯_第5页
已阅读5页,还剩5页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第四节 调用内置函数实现流水灯4.1实现流水灯我们是不是也可以让几个LED依次按顺序亮呢?其实显示的原理很简单,就是让一个LED灭后,延时一小会,然后点亮另一个,依次轮流下去。为了实现流水灯,我们首先要将8个LED分别接在P1口的8个引脚上,如图4-1所示。图4-1程序代码如下:代码4.1 (实现一个流水灯)#include <reg52.h> /包含52系列单片机头文件#define uint unsigned int /宏定义 将unsigned int 替换为较为简单的uint写法/分别声明单片机P1.0P17引脚,命名为led1led8,表示这八个引脚上接着八个LED灯sb

2、it led1 = P1 0; sbit led2 = P1 1; sbit led3 = P1 2; sbit led4 = P1 3; sbit led5 = P1 4; sbit led6 = P1 5; sbit led7 = P1 6; sbit led8 = P1 7; /*功能:延时函数输入参数:ms 需要延时的毫秒数输出参数:无*/void DelayMs(uint ms)uint i,j; /定义两个无符号整形的循环变量for(i=ms;i>0;i-) /延时ms毫秒for(j=100;j>0;j-);/延时约1msvoid main(void) whi

3、le(1) led1 = 0; /点亮LED1DelayMs(250); /延时250msled1 = 1; /将LED1熄灭led2 = 0; /点亮LED2DelayMs(250); /延时250msled2 = 1; /将LED2熄灭led3 = 0; /点亮LED3DelayMs(250); /延时250msled3 = 1; /将LED3熄灭led4 = 0; /点亮LED4DelayMs(250); /延时250msled4 = 1; /将LED4熄灭led5 = 0; /点亮LED5DelayMs(250); /延时250msled5 = 1; /将LED5熄灭led6 = 0;

4、 /点亮LED6DelayMs(250); /延时250msled6 = 1; /将LED6熄灭led7 = 0; /点亮LED7DelayMs(250); /延时250msled7 = 1; /将LED7熄灭led8 = 0; /点亮LED7DelayMs(250); /延时250msled8 = 1; /将LED7熄灭 我们可以看到,程序实现的思路就是在一个大循环中,依次按顺序点亮每一个LED,然后延时250ms(即让LED亮250ms),然后熄灭它;按照此方法依次点亮每一个LED,我们看到的就是流水灯效果了。4.2调用内置库函数实现流水灯上面我们讲了如何使LED产生流动效果,但是你是否发

5、现一个问题:写的太冗长了!能不能再简单点呢?可以!可以使用C51的库函数_crol_()来实现。程序代码如下:代码4.2 (实现一个流水灯)#include <reg52.h> /包含52系列单片机头文件#include <intrins.h> /包含“有C51库函数_crol_()定义”的头文件#define uint unsigned int /宏定义 将unsigned int 替换为较为简单的uint写法#define uchar unsigned char /宏定义 将unsigned char 替换为较为简单的uchar写法/延时函数void DelayMs

6、(uint ms)uint i,j; /定义两个无符号整形的循环变量for(i=ms;i>0;i-) /延时ms毫秒for(j=100;j>0;j-);/延时约1msvoid main()uchar a=0xfe; /定义一个变量a,并给他赋初始值 11111110 即0xfewhile(1)P1 = a;/将a的值赋给P1口,即让P1的八个引脚分别输出a的每一位,/第一次循环时,P1口赋值为11111110,即只有P1.0亮,/其余引脚LED都熄灭DelayMs(250);/让P1.0亮250毫秒a=_crol_(a,1); /a循环左移1位后再赋值给a,则a变为11111101

7、,即/只有P1.1亮,其余引脚都熄灭,如此循环,可以看到LED  /轮流被点亮的效果在这段代码中,我们使用了一个C51的库函数“_crol_”,我们先打开keil的Help,查看一下该函数的说明。我们使用鼠标在代码中的“_crol_” 函数上点一下,然后按键盘的“F1”按键,函数“_crol_”的使用说明就会弹出来,如图4-2所示。图4-2首先,我们如果想要使用该函数,必须在程序的开头处包含“intrins.h”头文件。然后,我们在看看这个函数的声明。unsigned char _crol_ ( unsigned char c, /* character to rotate left

8、 */ unsigned char b); /* bit positions to rotate */Description 所描述的大概意思就是:函数_crol_将字符c循环左移b位,然后将结果输出。为了帮助理解,我们可以看看help提供的例子代码,如代码4.3所示。代码4.3#include <intrins.h>void test_crol (void) char a; char b; a = 0xA5; / a = 10100101 b = _crol_(a,3); /* 将a循环左移三位后,得到b=0x2D */Ø 代码分析:首先,a=0xA5 注意,0xA5表

9、示它是十六进制数(C51中,0x前缀表示十六进制数),换算成二进制就是a=10100101。现在我们将其循环左移三位(即最左边的一位转到最右边,剩下的位依次向左挪一位),过程如下:第一次循环移位:移位前:a=10100101 移位后:a=01001011第二次循环移位:移位前:a=01001011 移位后:a=10010110第三次循环移位:移位前:a=10010110 移位后:a=00101101循环左移三位后,a=00101101 换算成十六进制即 a=0x2D将这个结果作为函数运算的结果输出,即b=0x2DØ 知识点:循环左移和循环右移C51库函数中,unsigned char

10、 _cror_(unsigned char c, unsigned char b)函数实现循环右移,其中参数c是待移动的字符,参数b是移动的位数,函数的返回值是移动后的值。例如:a = 0xA5; / a = 10100101b = _cror_(a,3); /* 将a循环右移(即最右边的一位转到最左边,剩下的位依次往右挪一位)三位后,得到b=0xB4 */第一次循环移位:移位前:a=10100101 移位后:a=11010010第二次循环移位:移位前:a=11010010 移位后:a=01101001第三次循环移位:移位前:a=01101001 移位后:a=10110100循环左移三位后,a

11、=10110100 换算成十六进制即 a=0xB4将这个结果作为函数运算的结果输出,即b=0xB4Ø 知识点:C51中移位操作符和库函数“_crol_”和“_cror_”的区别C51中,有“>>”和“<<”两个常用的运算符。“>>”为右移运算符,“<<”为左移运算符。其中,“>>”运算符和“_cror_”库函数的区别是,“>>”每移动一位,最左边的那位会填一个“0”,而最右边那位会“丢弃”,不像“_cror_”库函数那样循环移位。例如:a=0xA5那么,a=a>>3 表示将a右移三位,过程如下:第一次

12、>>移位:移位前:a=10100101 移位后:a=01010010 第二次>>移位:移位前:a=01010010 移位后:a=00101001第三次>>移位:移位前:a=00101001 移位后:a=00010100>>右移三位后,a=00010100 换算成十六进制即 a=0x14同理,“<<”运算符和“_crol_”库函数的区别是,“<<”每移动一位,最右边的那位会填一个“0”, 而最左边那位会“丢弃”,不像“_crol_”库函数那样循环移位。例如:a=0xA5,那么,a=a<<3 表示将a左移三位,过程

13、如下:第一次<<移位:移位前:a=10100101 移位后:a=01001010第二次<<移位:移位前:a=01001010 移位后:a=10010100第三次<<移位:移位前:a=10010100 移位后:a=00101000>>右移三位后,a=00101000 换算成十六进制即 a=0x28作业:1、请同学们编写代码,实现流水灯“由下向上”流动的效果。2、请同学们编写代码,实现8只LED左右来回点亮的效果。4.3将函数放在不同的文件中通过上面的例子,我们可能已经注意到了 #include <intrins.h> 语句,即当我们想使

14、用_crol_或_cror_库函数时,我们只需要将库函数声明的头文件包含进来,就可以使用该库函数了,是不是很方便。在工程上,为了方便函数的复用,我们通常是采用将不同的功能的函数放在不同的文件中,然后再需要使用的时候,将这个文件包含进来的方法,来复用函数。这种组织代码的方式,在大的项目中具有明显的优势。下面举个例子,将延时函数“DelayMs”放在另外的文件中,它的基本方法如下:void DelayMs ();这是function.h文件,它声明了DelayMs函数原型,别的文件如果想使用DelayMs函数,必须使用#include function.h"语句才能够调用DelayMs函

15、数#include "function.h"void DelayMs ()这是function.c文件,它实现了DelayMs函数,并且使用#include "function.h"语句和function.h文件挂着一起#include "function.h"void main()DelayMs(10);这是main.c文件,它调用了DelayMs函数,注意,这个函数并没有写在main.c文件中,它必须通过#include "function.h"语句才能调用DelayMs函数DelayMs 具体的做法如下:1.

16、首先在keil项目中添加一个源程序代码文件“function.c”,并键入DelayMs函数的实现代码,如代码4.4所示。代码4.4 void DelayMs(unsigned int ms)uint i,j; /定义两个无符号整形的循环变量for(i=ms;i>0;i-) /延时ms毫秒for(j=100;j>0;j-);/延时约1ms2. 在keil工程中添加一个头文件代码文件“function.h”(使用其他文件名也行,比如“func.h”),并键入如下代码,如代码4.5所示。代码4.5 extern void DelayMs(unsigned int ms); /在头文件中

17、声明函数(对function.c中DelayMs /函数的声明)为了让“function.c”文件和“function.h”文件组合在一起,需要在“function.c”文件中,将写有DelayMs函数声明的头文件包含进来,如图4-3所示。将写有DelayMs函数声明的头文件包含进来图4-3注意:“function.c”文件中的 #include “function.h” 这条预编译指令中,“function.h”文件名必须是写有DelayMs函数声明的头文件的名称,例如,如果添加的头文件名称是“func.h”,那么,“function.c”文件中的 #include “function.h”

18、 这条预编译指令就要改为 #include “func.h” 。3.在main.c中,如需调用“DelayMs”函数(注意,此时,main.c中并没有实现DelayMs),则只需将“function.h”头文件包含进来,如代码4.6所示。代码4.6 (实现一个流水灯,函数在不同的源文件中)#include <reg52.h> /包含52系列单片机头文件#include <intrins.h> /包含C51库函数的头文件#include "function.h" /将含有DelayMs函数声明的头文件包含进来#define uint unsigned

19、int /宏定义 将unsigned int 替换为较为简单的uint写法#define uchar unsigned char /宏定义 将unsigned char 替换为较为简单的uchar写法void main()uchar a=0xfe; /定义一个变量,并给他赋初始值 11111110 即0xfewhile(1)P1 = a;/将a的值赋给P1口,即让P1的八个引脚分别输出a的每一位,/第一次循环时,P1口赋值为11111110,即只有P1.0亮,其余/引脚LED都熄灭DelayMs(250);/让P1.0亮250毫秒(此时,DelayMs函数在其他文件中,/但我们依然可以正常使用

20、它)a=_crol_(a,1); /a循环左移1位后再赋值给a,则a变为11111101,即/只有P1.1亮,其余引脚都熄灭,如此循环,可以看到LED  /轮流被点亮的效果 关键点如图4-4所示。因为我们想在“main.c”中使用“DelayMs”函数,因此,将写有“DelayMs”函数声明的头文件包含进来图4-4Ø 知识点 头文件的作用头文件是给其他文件调用函数的接口,比如,我们在调用库函数_crol_时,并不知道_crol_实现的方法(该函数已经被编译为了一个二进制的库函数,我们看不到,实际也无需知道),我们只是通过引用声明了_crol_函数的头文件“intrins.h

21、”,就可以调用这个函数,方法就是,使用#include <intrins.h> 预编译指令即可。Ø 知识点 头文件中应该写什么内容头文件是给其他文件调用函数的接口,从语义上来讲,头文件中应该只写需要被其他文件调用的函数声明,对于不需要被外部文件调用的函数(也就是不需要暴露给外部文件的函数),不应该声明在头文件中,当然,更不应该在头文件中书写源代码。Ø 知识点 extern关键字extern是C语言的一个关键字(extern本意是外面的,外部的),当一个函数声明使用extern关键字时,说明这个函数的实现部分(即源代码部分)是在其他文件中,而不是在和函数声明所在的

22、同一个文件中。Ø 知识点 使用#ifndef #define #endif 预编译指令来防止头文件重复包含在头文件中,我们通常使用#ifndef XXXXX /如果没有定义变量XXXXX 注意#define XXXXX /那么定义变量XXXXX。头文件的内容。#endif这样的结构来防止头文件被重复包含,其中 XXXXX 名称应该在整个项目中是唯一的,不能重复。这样,当某个文件第一次include 这个头文件时,XXXXX这个变量还没有被定义,那么#define XXXXX 到 #endif 指令之间的内容能够被预编译器展开。当某个文件第二次include 这个头文件时,XXXXX这

23、个变量已经被定义,那么,#ifndef XXXXX 条件将不成立了,预编译器会直接跳转到#endif语句,结束预编译。那么#define XXXXX 到 #endif 指令之间的内容就不能够被预编译器展开。我们会在以后举个例子来说明。4.4将工程结构再优化(再抽象)通过上面的例子,我们可能已经将DelayMs函数放在一个单独的文件中,并且通过头文件来调用这个函数。我们注意到,其实:#define uint unsigned int /宏定义 将unsigned int 替换为较为简单的uint写法#define uchar unsigned char /宏定义 将unsigned char 替

24、换为较为简单的uchar写法这两个宏定义也是经常使用的公共定义,那么我们能否将它们也放在一个文件中,使用头文件调用呢?答案是肯定的。做法如下:1. 在keil项目中添加一个头文件“common.h”,并键入如代码4.7所示的代码:代码4.7 #ifndef _COMMON_H#define _COMMON_H#define uint unsigned int /宏定义 将unsigned int 替换为较为简单的uint写法#define uchar unsigned char /宏定义 将unsigned char 替换为较为简单的uchar写法#endif2.将头文件代码文件“functi

25、on.h”,改写为如下代码,如图所示。/代码4.8 #ifndef _FUNCTION_H#define _FUNCTION_H#include "common.h"/将写有宏定义声明的头文件包含进来(因为函数声明使用了uint) /#define uint unsigned int /这个宏定义可以不要了,因为我们已经在common.h中定义了extern void DelayMs(uint ms); #endif3.将源代码文件“function.c”,改写为如下代码,如图所示#include “common.h” /将写有宏定义声明的头文件包含进来 注意头文件的顺序#

26、include “function.h” /将写有DelayMs函数声明的头文件包含进来 注意:/#define uint unsigned int /这个宏定义可以不要了,因为我们已经在common.h中定义了/*功能:延时函数输入参数:ms 需要延时的毫秒数输出参数:无*/void DelayMs(uint ms)uint i,j; /定义两个无符号整形的循环变量for(i=ms;i>0;i-) /延时ms毫秒for(j=100;j>0;j-);/延时约1ms4. 将源代码文件“main.c”,改写为如下代码,如图所示/代码4.8 (实现一个流水灯,函数在不同的源文件中)#include <

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论