历史上的今天
返回首页

历史上的今天

今天是:2026年01月06日(星期二)

正在发生

2023年01月06日 | 51单片机(入门保姆级教程)——LED闪烁及流水灯

2023-01-06 来源:zhihu

一、LED基本原理和知识

图1 LED共阳极原理图

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

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


1.2 共阴极和共阳极的区别

耗能不一样。共阳极二极管的公共端接在高电平,需要高电平时时刻刻处在工作状态;而共阴极二极管公共端接低(GND),只需指定二极管工作时给某个二极管高电平即可,有效降低了功耗,对长时间工作LED来言有效降低了屏热。

线路复杂程度不同。一般在实际工程中共阴极二极管比共阳极二极管的线路复杂,共阳极不需要那么多的高电平线路驱动。

二、单个LED的闪烁

2.1 KEIL文件的建立

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


2.2 LED的闪烁程序

#include //定义单片机的一些特殊功能寄存器

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


#include //定义单片机的一些特殊功能寄存器

#include //定义单片机的一些特殊功能寄存器,里面包含_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 //定义单片机的一些特殊功能寄存器


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


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


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


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

#include


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

#include


#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库函数,其中_crol_表示左移(字符循环左移),_cror_表示右移(字符循环右移)。具体应用中_crol_(LED,1)表示把LED左移1位(例如:11111110执行完后为11111101),_cror_(LED,2)表示左移2位等(例如:10111111执行完后为11101111)。


两个程序的小区别

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


四、小结

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


推荐阅读

史海拾趣

G-Two Inc公司的发展小趣事

背景:随着全球光伏产业的快速发展,对光伏胶膜的需求急剧增加。

发展:福斯特抓住市场机遇,不断扩大产能,完善产品品类,提高产品质量。同时,公司还积极开拓国际市场,与全球多家知名光伏组件企业建立了长期合作关系。

成果:到2021年,福斯特的光伏胶膜全球市占率超过50%,成为全球光伏胶膜行业的龙头企业。

Flamar公司的发展小趣事

Flamar公司自成立以来,始终将技术创新视为企业发展的核心驱动力。在电子元件领域,公司研发团队成功开发出一种新型高性能磁耦合传送臂,这一创新产品凭借其高效、稳定的传输性能,在半导体设备制造中迅速获得市场认可。通过不断的技术迭代和优化,Flamar公司的磁耦合传送臂逐渐成为行业内的标杆产品,帮助公司在激烈的市场竞争中脱颖而出。

HEC Inc公司的发展小趣事

随着电子行业的全球化趋势日益明显,Flamar公司积极实施国际化战略,通过设立海外研发中心、生产基地和销售网络,不断拓展全球市场。公司先后在欧洲、亚洲等地建立分支机构,与当地企业建立紧密的合作关系,共同推动电子技术的创新与应用。通过国际市场的开拓,Flamar公司不仅实现了业务规模的快速增长,还进一步提升了品牌的国际知名度和影响力。

Galaxy Semi-Conductor Co Ltd公司的发展小趣事

飞虹半导体自2002年建厂之初,便意识到大功率半导体产品市场几乎被国外进口产品垄断。为了改变国内电子厂家在价格与交货期上的被动局面,飞虹半导体毅然踏上了大功率半导体国产化生产研发的道路。尽管面临重重困难,但飞虹半导体坚持不懈,逐步在LED驱动、工业开关电源、新能源电动车等多个领域取得突破。其功率器件产品因性能优越,逐渐获得市场认可,为国产半导体替代进口产品贡献了重要力量。

Chino-Excel公司的发展小趣事

随着公司规模的扩大,Chino-Excel公司开始实施全球化战略布局。公司先后在多个国家和地区设立研发中心和生产基地,以充分利用当地资源和市场优势。同时,公司加强与全球合作伙伴的紧密合作,共同开发新产品和技术,进一步巩固了在全球市场的地位。

HDP_Power公司的发展小趣事

随着公司规模的扩大,Chino-Excel公司开始实施全球化战略布局。公司先后在多个国家和地区设立研发中心和生产基地,以充分利用当地资源和市场优势。同时,公司加强与全球合作伙伴的紧密合作,共同开发新产品和技术,进一步巩固了在全球市场的地位。

问答坊 | AI 解惑

IBDN六类网线,AVAYA六类网线

深圳市连讯达电子技术开发有限公司 优价供应IBDN、康普(AVAYA)、AMP,科龙综合布线产品IBDN、康普AVAYA深圳金牌代理 供应超五类线、模块、24口配线架,机柜,FLUKE网络测试仪-13424326831余振维IBDN深圳代理,康普(AVAYA)深圳代理,IBDN超五类双 ...…

查看全部问答>

多路数据采集

本帖最后由 paulhyde 于 2014-9-15 03:01 编辑 多路数据采集 多路数据采集  …

查看全部问答>

有用过TLC2543的进来看下

用TLC2543做AD转换的时候,假如说是测量电压,那么在模拟输入通道前是否应该加旁路电路,用以去除干扰,如果用的话,这个电路该怎么接?…

查看全部问答>

多通道AD采样

大家好,最近想编一个多通道AD采样的程序,没有思路,请高手指点下。   我用的是带AD转换的51单片机,可是只有一个ad中断标志位啊,那如果ad产生中断了,会是由哪个ad通道引起的呢,   弄不明白,给点意见吧,谢谢啦…

查看全部问答>

wince/mobile怎样使用自定义光标,如放大,缩小图标

wince/mobile怎样使用自定义光标,如放大,缩小图标,语言C++(VS2005)…

查看全部问答>

Mobile的MBR

    最近才开始看Mobile,发现没有见到Mobile的MBR,在bootloader中没有明显发现有进行格式化,创建MBR的代码。很是困惑,可是在IPL中有读分区的操作,友情达人指教。不胜感谢!!!!…

查看全部问答>

SPI总线读取SD卡

先用读卡器在windows FAT格式 格式化,再用单片机SPI总线读取SD卡,在初始化成功后,读取相关寄存器都成功,当读取第一个扇区时,数据始终不对,这是为什么? 地址0000处应该是启动扇区,里面有一些描述,但我读的一大部分是0x00,最后以0x55 0xAA结束.…

查看全部问答>

evc显示汉字问题

我在查询程序中,在“类型”一项里需要有中文的显示。但在开发板里显示的是□□□,请问高手怎样才能让它显示汉字?我的查询显示语句是strTemp.Format(_T(\"%s-车号%s-类型%s-毛重%s-皮重%s-净重%s\"),q.fieldValue(1),q.fieldValue(2),q.fieldValu ...…

查看全部问答>

protel生成的pdf原理图查找不准确

各位,用protel生成了一个pdf格式的原理图,然后在pdf里面,我想去查引脚之间相连的情况,比如输入R1,但找到的却不是R1,而是SI03,SIO1,2,1,之类的,我用全字匹配去查,也是查到SIO3,SIO1,这样一来我看原理图就很不方便了,有没有哪位也碰到 ...…

查看全部问答>

k120s通讯指令MODCOM疑问

现有一台K120S与温度控制器使用COM1口(即侧面的485口)通讯,使用的是MODBUS ASCII协议,使用MODCOM指令读取温度表的温度和写温度值到温控器,现在两个功能都可以使用,但是有时PLC关机再通电这个就不正常,只可以读到温度,而写不到温度值到温控 ...…

查看全部问答>