入门保姆级教程:51单片机LED闪烁与流水灯实现

一、LED基本原理和知识

图1 LED共阳极原理图

1.1 LED共阴极和共阳极的含义

LED的最小单元是发光二极管。发光二极管是区分正负极的,正向导通,反向截止。共阴极就是发光二极管的负极连接在一起,共阳极就是发光二极管的正极连接在一起。

1.2 共阴极和共阳极的区别

  • 耗能不一样。共阳极二极管的公共端接在高电平,需要高电平时时刻刻处在工作状态;而共阴极二极管公共端接低(GND),只需指定二极管工作时给某个二极管高电平即可,有效降低了功耗,对长时间工作LED来言有效降低了屏热。
  • 线路复杂程度不同。一般在实际工程中共阴极二极管比共阳极二极管的线路复杂,共阳极不需要那么多的高电平线路驱动。
  • 二、单个LED的闪烁

    2.1 KEIL文件的建立

    具体的keil工程文件创建可以参考[1]中的过程。

    2.2 LED的闪烁程序

    #include <reg52.h>			 //定义单片机的一些特殊功能寄存器
    sbit led=P2^0;				//定义指定LED灯的管脚
    
    void delay(unsigned int i)			//延时函数。unsigned int定义无符号整型,范围0-655535
    {
    	while(i--);	//i=1时,大约延时10us
    }
    
    void main()       //主函数,程序从这里开始执行
    {
    	while(1)
    	{
    		led=0;        //0表示低电平,共阳极二极管点亮
    		delay(50000); //大约延时450ms
    		led=1;        //1表示高电平,LED熄灭
    		delay(50000); //大约延时450ms	
    	}		
    }

    2.3 包含/区分/学习知识点

  • #include<reg52.h>和#include"reg52.h"区别
  • 两者来说区别不大,< >的引用是C51库函数里面的文件,而" "是引用我们自己创建的库函数里面的文件。

  • while循环函数
  • while(i<10)
    {
    	i=i+1;
    }
    

    while函数的定义为:执行程序里面的函数文件,即i+1,执行完后判断i是否<10,若小于,继续执行,若大于等于则跳出循环。

  • DELAY延时函数(掌握和学习)
  • 在单片机中有两种延时方式,一种是软件延时,一种是硬件延时(计时器/计数器)。本知识点主要介绍软件延时函数。

    void delay(unsigned int i)
    {
    	while(i--);	
    }

    延时函数,i=1时,大约延时10us

    图2 STC-ISP生成延时函数

    图2所示为STC-ISP自动设置生成延时函数,所使用的晶振为11.0592MHz,以10us为例,自动生成。

    void Delay10us()		//@11.0592MHz
    {
    	unsigned char i;
    	_nop_();
    	i = 25;
    	while (--i);
    }

    我们发现其中多了_nop_()指令,该指令包含在头文件#include<intrins.h>中,所以我们需要专门调用改文件。_nop_()的作用是产生下一条指令,对于延时很短的,要求在us级的,采用“_nop_”函数,这个函数的作用是使程序执行完成后延时几微秒。nop指令为单周期指令,可由晶振频率算出延时时间,对于12M晶振,延时1us。对于延时比较长的,要求在大于10us,采用C51中的循环语句来实现。对上述闪烁程序变换后可得如下程序。

    #include <reg52.h>			 //定义单片机的一些特殊功能寄存器
    #include <intrins.h>			 //定义单片机的一些特殊功能寄存器,里面包含_nop_()
    sbit led=P2^0;				//定义指定LED灯的管脚
    
    void main()       //主函数,程序从这里开始执行
    {
    	while(1)
    	{
    		led=0;        //0表示低电平,共阳极二极管点亮
    		_nop_();      
    		led=1;        //1表示高电平,LED熄灭
    		_nop_();      	
    	}		
    }

    三、LED流水灯的实现

    3.1最基础最原始的流水灯实现办法

    (1)概念

    8个LED灯的定义,以共阳极为例,让某一个灯点亮就是让这个LED灯为低电平。从左到右依次为01111111、10111111、11011111、11101111、11110111、11111011、11111101、11111110。其中1就是高电平、0位低电平,为低电平时LED点亮。

    (2)具体程序代码

    #include <reg52.h>			 //定义单片机的一些特殊功能寄存器
    
    sbit led1=P2^0;				//定义指定LED灯的管脚
    sbit led2=P2^1;
    sbit led3=P2^2;
    sbit led4=P2^3;
    sbit led5=P2^4;
    sbit led6=P2^5;
    sbit led7=P2^6;
    sbit led8=P2^7;
    
    void delay(unsigned int i)			//延时函数。unsigned int定义无符号整型,范围0-655535
    {
    	while(i--);	//i=1时,大约延时10us
    }
    
    void main()       //主函数,程序从这里开始执行
    {
    	while(1)
    	{
    		led1=0;        //0表示低电平,led1点亮
    		delay(5000); 
    		led1=1;        //1表示高电平,led1熄灭
    		delay(5000); 	
    		led2=0;        //0表示低电平,led2点亮
    		delay(5000); 
    		led2=1;        //1表示高电平,LED2熄灭
    		delay(5000); 	
    		led3=0;        //0表示低电平,LED3点亮
    		delay(5000); 
    		led3=1;        //1表示高电平,LED3熄灭
    		delay(5000); 	
    		led4=0;        //0表示低电平,LED4点亮
    		delay(5000); 
    		led4=1;        //1表示高电平,LED4熄灭
    		delay(5000); 	
    		led5=0;        //0表示低电平,LED5点亮
    		delay(5000); 
    		led5=1;        //1表示高电平,LED5熄灭
    		delay(5000);	
    		led6=0;        //0表示低电平,LED6点亮
    		delay(5000); 
    		led6=1;        //1表示高电平,LED6熄灭
    		delay(5000); 
    		led7=0;        //0表示低电平,LED7点亮
    		delay(5000); 
    		led7=1;        //1表示高电平,LED7熄灭
    		delay(5000); 	
    		led8=0;        //0表示低电平,LED8点亮
    		delay(5000); 
    		led8=1;        //1表示高电平,LED8熄灭
    		delay(5000); 	
    	}		
    }

    最基础最原始的流水灯方式,就是单纯让LED按照顺序依次点亮,从而达到流水灯的效果,看似笨拙、程序繁多,却突出流水灯的思想和原理,为后面库函数、左移右移、数组索引法奠定基础。

    3.2 移位法实现LED流水灯

    (1)具体程序代码

    左循环

    #include <reg51.h>
    
    void delay(unsigned int i)   //延时函数
    {
        while(i--);
    }
    
    void main(void)   //主函数,程序从这里开始执行
     {
         int j;   //引入变量,定义字符
         while(1)
        {
            P2=0xfe;   //即11111110,将8个LED的状态赋值给P2
            delay(50000);
    	for(j=0;j<8;j++)  //循环8次,因为有8个LED灯
             {
                P2=P2<<1 | 0x01; //实现程序左移1位,或运算,有1为1,全0为0
                delay(50000);
             }
         }
     }

    右循环

    #include <reg51.h>
    
    void delay(unsigned int i)   //延时函数
    {
        while(i--);
    }
    
    void main(void)   //主函数,程序从这里开始执行
     {
         int j;   //定义字符
         while(1)
        {
            P2=0x7f;   //即01111111,将8个LED的状态赋值给P2
            delay(50000);
    	for(j=0;j<8;j++)  //循环8次,因为有8个LED灯
             {    
                P2=P2>>1 | 0x80; //实现程序右移1位,或运算,有1为1,全0为0
                delay(50000);
             }
         }
     }

    (2)相关知识、逻辑解释

  • 或运算( | )
  • 基本运算规则为: 0|0=0; 0|1=1; 1|0=1; 1|1=1;只要运算中有一个为1,其值为1。

  • 为什么引入j变量?
  • 变量j:为了实现循环,让LED灯依次进行移动,文中实现LED灯左移或者右移8次。

  • 左移<<和右移>>
  • 左移和右移有其固定的形式,如P1<<1,就是让P1往左移动一位,若为P1<<2,就是让P1往左移动两位,依次推算,右移也是如此。

  • 此位移法相对简单基础,存在不定性,可在此基础上增加判断和计时器,精确控制LED流水灯的形式和花样。
  • 3.3 数组法

    (1)具体程序代码

    #include <reg51.h>
    
    unsigned char LED [8] = {0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f}; //定义8个LED灯点亮
    
    void delay(unsigned int i)   //延时函数
    {
        while(i--);
    }
    
    void main(void)   //主函数,程序从这里开始执行
    {
         int j;
         for(j=0; j<8; j++) //循环函数,实现流水效果
    	 {
    		P2 = LED[j];  //赋值给LED端口
    		delay(50000);		
    		P2 = 0xff;	 // 清屏,使所有LED熄灭 
    	 }    
    }

    (2)相关知识点

  • 数组的定义和表示——类型说明符 数组名 [常量表达式]
  • 这是数组最基本的形式,比较固定。其中:

    类型说明符:表示变量的长度单元格,常用为char、int、long;因为一般读取数据很少有负数,所以常用无符号整型。

    数组名:定义的数组名字,即数组标识符。

    常量表达式:表示定义的数组中装有元素的个数,即数组长度。

    3.4 库函数法

    (1)具体程序代码

    #include <reg51.h>
    #include <intrins.h>
    
    void delay(unsigned int i)   //延时函数
    {
        while(i--);
    }
    
    void main()   //主函数,程序从这里开始执行
    {
    	int i;
    	P2 = 0xfe;
    	for(i=0;i<7;i++)	 //LED左移一位
    		{
    			P2=_crol_(P2,1);
    			delay(50000); //延时
    		}
    	for(i=0;i<7;i++)	//LED右移一位
    		{
    			P2=_cror_(P2,1);
    			delay(50000); //延时	
    		}		   
    }

    (2)小修改后代码

    #include <reg51.h>
    #include <intrins.h>
    
    #define LED P2   //把P2口整体定义为LED,后面可以直接引用
    
    void delay(unsigned int i)   //延时函数
    {
        while(i--);
    }
    
    void main()   //主函数,程序从这里开始执行
    {
    	int i;
    	LED = 0xfe;
    	for(i=0;i<7;i++)	 //LED左移一位
    		{
    			LED=_crol_(LED,1);
    			delay(50000); //延时
    		}
    	for(i=0;i<7;i++)	//LED右移一位
    		{
    			LED=_cror_(LED,1);
    			delay(50000); //延时	
    		}		   
    }

    (3)相关知识点

  • 移位函数(最快实现流水灯程序)
  • 需要添加#include<intrins.h>库函数,其中_crol_表示左移(字符循环左移),_cror_表示右移(字符循环右移)。具体应用中_crol_(LED,1)表示把LED左移1位(例如:11111110执行完后为11111101),_cror_(LED,2)表示左移2位等(例如:10111111执行完后为11101111)。

  • 两个程序的小区别
  • 修改后的程序中多了#define LED P2定义,此句话意思为将P2口整体定义为LED,增加了程序的可读性。

    四、小结

    以上四种方法基本涵盖了流水灯的控制移动方法,可供借鉴学习,水平有限,若有不足,请理解谢谢,欢迎指出问题。

    物联沃分享整理
    物联沃-IOTWORD物联网 » 入门保姆级教程:51单片机LED闪烁与流水灯实现

    发表评论