在《51单片机(五)》和《51单片机(六)》中,我们对LED的控制比较简单,都只是点亮的操作,在本文中,我们进行一些相对复杂的操作,来更加熟悉单片机IO口的使用。
一、闪灯
在这个实验中,我们只对LED1进行控制,让它闪烁,代码如下所示
#include<reg52.h> //包含头文件,一般情况不需要改动,
sbit LED1=P0^0; //用sbit关键字定义LED1到P00端口,
/********************************************************************/
/* 延时函数 */
/* x决定延时时间的长短,x值越大,延时越长 */
/* x的取值范围为0~65535 */
/********************************************************************/
void delay(unsigned int x)
{
unsigned int i;
for(i=0;i<x;i++);
}
/********************************************************************/
/* 主函数 */
/********************************************************************/
void main (void)
{
for(;;) //主循环
{
LED1=0; //将P00口赋值0,对外输出低电平,灯点亮。
delay(10000); //调用延时函数
LED1=1; //将P00口赋值1,对外输出高电平,灯熄灭。
delay(10000); //调用延时函数
}
}
程序往往由多个函数组成,函数是C源程序的基本模块,通过对函数模块的调用实现特定的功能。用户可把自己的算法编成一个个相对独立的函数模块,然后用调用的方法来使用函数。
C语言的函数兼有其它语言中的函数和过程两种功能,从这个角度看,可把函数分为有返回值函数和无返回值函数两种。有返回值函数:此类函数被调用执行完后将向调用者返回一个执行结果,称为函数返回值。由用户定义的这种要返回函数值的函数,必须在函数定义和函数说明中明确返回值的类型。无返回值函数:此类函数用于完成某项特定的处理任务,执行完成后不向调用者返回函数值。这类函数类似于其它语言的过程。由于函数无须返回值,用户在定义此类函数时可指定它的返回为“空类型”, 空类型的说明符为“void”。
可以看出例程中的delay()函数没有返回值。
函数调用。程序中是通过对函数的调用来执行函数体的,其过程与其它语言的子程序调用相似。在C语言中,可以用以下几种方式调用函数:
1、函数表达式:函数作为表达式中的一项出现在表达式中,以函数返回值参与表达式的运算。这种方式要求函数是有返回值的。例如:z=max(x,y)是一个赋值表达式,把max的返回值赋予变量z。
2、函数语句:函数调用的一般形式加上分号即构成函数语句。例如:delay();以函数语句的方式调用函数。
3、函数实参:函数作为另一个函数调用的实际参数出现。这种情况是把该函数的返回值作为实参进行传送,因此要求该函数必须是有返回值的。
在程序用delay()函数是一个延时函数,由于为了实现灯闪烁的效果,因此delay()函数的延时时间没有经过精确计算,在以后的实验中,我们将会对如何精确计算延时函数的延时时间进行介绍。
二、流水灯
这个实验的现象是, 8个LED灯从LED1开始点亮,依次点亮LED2直到点亮LED8,然后开始循环,任何时刻只有一个LED灯亮。
#include<reg52.h> //包含头文件,一般情况不需要改动,
#define LED P0 //宏定义,定义P0为LED
/********************************************************************/
/* 延时函数 */
/* x决定延时时间的长短,x值越大,延时越长 */
/* x的取值范围为0~65535 */
/********************************************************************/
void delay(unsigned int x)
{
unsigned int i;
for(i=0;i<x;i++);
}
/********************************************************************/
/* 主函数 */
/********************************************************************/
void main (void)
{
unsigned char q;
LED=0xfe; //给LED赋初值0xfe,即1111 1110,LED1点亮
for(;;)
{
for(q=0;q<8;q++)
{
delay(30000);
LED<<=1; //LED左移一位
LED=LED|0x01; //LED与0x01按位做或,即LED最低位置1,其它位不变。
}
LED=0xfe;
}
}
在上面的代码中,用到了“|”运算。“|”是按位或运算。在数字电路中,或运算的规则是
1|1=1;
1|0=1;
0|1=1;
0|0=0;
程序中LED有8位,例如LED=0xfe,其二进制值为1111 1110,则LED左移一位为1111 1100,再与0x01做按位或运算1111 1100|0000 0001=1111 1101
三、查表显示
在这个实验中,我们使LED以更加复杂的方式点亮,代码如下
#include<reg52.h> //包含头文件,一般情况不需要改动
#define LED P0 //宏定义,定义P0为LED
/********************************************************************/
/* 显示表格 */
/********************************************************************/
unsigned char code table[]={
0xe7,0xc3,0x81,0x00,
0x18,0x3c,0x7e,0xff,
0x7e,0xbd,0xdb,0xe7, 0xe7,0xdb,0xbd,0x7e,
0xff,0x55,0xaa,0x55,
0xaa,0xff,0x00,0xff
};
/********************************************************************/
/* 延时函数 */
/* x决定延时时间的长短,x值越大,延时越长 */
/* x的取值范围为0~65535 */
/********************************************************************/
void delay(unsigned int x)
{
unsigned int i;
for(i=0;i<x;i++);
}
/********************************************************************/
/* 主函数 */
/********************************************************************/
void main (void)
{
unsigned char q;
for(;;) //主循环
{
for(q=0;q<24;q++) //for循环,循环24个周期
{
LED=table[q]; //显示表格中的内容
delay(30000); //延时
}
}
}
1、在程序中,对table的定义如下:
unsigned char code table[]
在这个定义中使用了关键字code,那么带code与不带code有什么区别呢?带code关键字的是常量,被存放在单片机的ROM存储器中;不带code关键字的是变量,被存放在单片机的RAM存储器中。由于单片机的RAM空间相对较小,资源比较宝贵,一般把不需要变化的值和一些固定参数定义为code类型的常量。
2、数组。程序中定义的table[],实际上是一个一维数组,一维数组的定义方式为:
类型说明符 数组名 [常量表达式];
其中:类型说明符是任一种基本数据类型或构造数据类型。数组名是用户定义的数组标识符。方括号中的常量表达式表示数据元素的个数,也称为数组的长度。程序中定义数组时,方括号中为空,这种数组的长度不是固定的,可以在数组中添加新的元素。在数组使用过程中,数组方括号中的内容代表数组中的元素,table[0]表示数组中的第一个元素,table[15]表示数组中的第十六个元素。