在嵌入式开发的学习过程中,LED流水灯无疑是一个经典的入门项目,它就像是编程领域的“Hello World”。今天,我们将通过Proteus仿真,在上一篇实现从上到下从下到上双向流水灯滚动的基础上,深入探究如何利用独立按键控制LED流水灯实现四种不同的滚动显示效果。

        这个项目不仅能够帮助初学者更好地理解单片机的基本操作,还能让大家掌握如何通过按键实现功能切换,为后续的复杂项目打下坚实的基础。这里直接给出干货,proteus仿真电路和所有程序代码,更多单片机仿真项目学习案例,请搜索并关注小智单片机微信公众号获取工程源文件。

          这里将使用经典的AT89C51单片机,配合Keil C51开发环境,在Proteus中完成从电路设计到程序编写的全流程实现。此项目代码具有较强移植性,非常方便移植到开发板实物上完成相关流水灯实验。

一、具体电路连接如下:

二、电路工作原理

电源部分:5V电源为整个系统提供能量。LED通过限流电阻连接到VCC,当单片机引脚输出低电平时,形成电流通路,LED点亮。

时钟电路:12MHz晶振与两个30pF电容构成振荡电路,为单片机提供稳定的时钟信号,决定程序执行的速度和定时器的计时基准。

复位电路:上电时,电容充电过程会在RST引脚产生一个短暂的高电平脉冲,使单片机复位,程序从起始位置开始执行。

LED控制:当P2端口的某个引脚输出低电平(0)时,对应的LED两端形成电位差,电流流过LED使其发光;输出高电平(1)时,LED两端电位接近,没有电流,LED熄灭。

按键控制:按键与P3.0端口连接,当按键按下时,检测P3.0端口为低电平,通过检测端口电平判断按键是否按下。

、程序设计原理与代码解析

3.1 程序源码如下:

#include <reg51.h>   //引用头文件

#define uchar unsigned char   //宏定义

#define uint unsigned int     //宏定义

sbit key = P3^0; // 定义按键连接的P3.0端口

uchar key_flag = 0; // 按键标志位,用于记录按键按下次数

uchar led_state = 0; // LED状态变量,用于控制LED的显示模式

uchar led_data = 0x00; // LED数据变量,用于存储LED的点亮状态

bit key_pressed = 0; // 按键按下标志位

void delay(uint z) // 延时函数

{

    uint x, y;

    for(x=z; x>0; x--)

      for(y=110; y>0; y--);

}

void key_scan() // 按键扫描函数

{

    if(key == 0) // 检测按键是否按下

    {

        delay(20); // 消抖延时

        if(key == 0) // 再次确认按键是否按下

        {

            key_pressed = 1; // 设置按键按下标志位

            while(!key); // 等待按键释放

        }

    }

    else

    {

        key_pressed = 0; // 清除按键按下标志位

    }

}

void main()  //主函数

{

    key = 1; // 初始化按键端口为高电平

    while(1)

    {

        key_scan(); // 调用按键扫描函数

        if(key_pressed) // 如果按键被按下

        {

            key_flag++; // 按键标志位加1

            key_pressed = 0; // 清除按键按下标志位

        }

        switch(key_flag % 4) // 根据按键标志位的值切换LED显示模式

        {

            case 0: // 模式0:从上到下依次点亮

                if(led_state < 8)

                {

                    led_data = 1 << led_state; // 设置LED数据

                    P2 = ~led_data; // 输出到P2口,点亮对应的LED

                    delay(500); // 延时

                    led_state++; // LED状态变量加1

                }

                else

                {

                    led_state = 0; // 重置LED状态变量

                    led_data = 0x00; // 清空LED数据

                    P2 = ~led_data; // 关闭所有LED

                }

                break;

            case 1: // 模式1:从下到上依次点亮

                if(led_state < 8)

                {

                    led_data = 1 << (7 - led_state); // 设置LED数据

                    P2 = ~led_data; // 输出到P2口,点亮对应的LED

                    delay(500); // 延时

                    led_state++; // LED状态变量加1

                }

                else

                {

                    led_state = 0; // 重置LED状态变量

                    led_data = 0x00; // 清空LED数据

                    P2 = ~led_data; // 关闭所有LED

                }

                break;

            case 2: // 模式2:交替闪烁

                if(led_state == 0)

                {

                    led_data = 0x55; // 设置LED数据,点亮第1、3、5、7个LED

                    P2 = ~led_data; // 输出到P2口

                    delay(500); // 延时

                    led_state = 1; // 改变LED状态变量

                }

                else

                {

                    led_data = 0xAA; // 设置LED数据,点亮第2、4、6、8个LED

                    P2 = ~led_data; // 输出到P2口

                    delay(500); // 延时

                    led_state = 0; // 改变LED状态变量

                }

                break;

            case 3: // 模式3:两两一组交替点亮

                if(led_state == 0)

                {

                    led_data = 0x33; // 设置LED数据,点亮第1、2、5、6个LED

                    P2 = ~led_data; // 输出到P2口

                    delay(500); // 延时

                    led_state = 1; // 改变LED状态变量

                }

                else

                {

                    led_data = 0xCC; // 设置LED数据,点亮第3、4、7、8个LED

                    P2 = ~led_data; // 输出到P2口

                    delay(500); // 延时

                    led_state = 0; // 改变LED状态变量

                }

                break;

        }

    }

}

3.2 关键代码解析

让我们深入分析程序的关键部分:

1、#include <reg51.h>:包含51单片机的头文件,该文件定义了51单片机的特殊功能寄存器(如P0、P1、P2、P3等端口),以及一些其他与硬件相关的定义。这是编写基于51单片机的C语言程序时必须包含的头文件。

宏定义ucharuint分别定义为unsigned charunsigned int,这是为了简化代码,使代码更简洁易读。

2、sbit key = P3^0;:定义了一个特殊功能位变量key,它对应于P3.0端口,用于检测按键是否被按下。uchar key_flag = 0;:定义了一个无符号字符变量key_flag,用于记录按键按下的次数。每次按键按下时,该变量会增加。

uchar led_state = 0;:定义了一个无符号字符变量led_state,用于控制LED的显示模式。它在不同的模式下有不同的作用。

uchar led_data = 0x00;:定义了一个无符号字符变量led_data,用于存储LED的点亮状态。通过改变这个变量的值,可以控制哪些LED点亮。

bit key_pressed = 0;:定义了一个位变量key_pressed,用于标记按键是否被按下。当按键被按下时,该变量被设置为1;当按键释放时,该变量被清除。

3、按键扫描函数:void key_scan():功能:检测按键是否被按下,并在按键按下时设置标志位。原理:首先检查key是否为0(低电平,表示按键被按下)。如果按键被按下,调用delay(20)进行消抖处理,以避免误判。再次确认按键是否仍然被按下,如果是,则设置key_pressed标志位为1,并等待按键释放。如果按键未被按下,则清除key_pressed标志位。

4、 主函数功能:主函数负责初始化按键端口,并在无限循环中调用按键扫描函数,根据按键次数切换LED的显示模式。

原理首先将按键端口key初始化为高电平。无限循环:在while(1)循环中,不断调用key_scan()函数来检测按键状态。按键处理:如果检测到按键被按下(key_pressed为1),则增加key_flag的值,并清除key_pressed标志位。模式切换:根据key_flag % 4的值,切换不同的LED显示模式:

模式0:从上到下依次点亮LED。通过左移操作1 << led_state来控制LED的点亮顺序。

模式1:从下到上依次点亮LED。通过右移操作1 << (7 - led_state)来控制LED的点亮顺序。

模式2:交替闪烁。通过设置led_data0x550xAA来控制LED的闪烁模式。

模式3:两两一组交替点亮。通过设置led_data0x330xCC来控制LED的点亮模式。

四、实验结果及现象

    到这里整个实验过程就结束了,文中给出了详细的电路设计和代码编写,并深入阐述了实现原理,下一篇将在此篇基础上加入按键控制,实现按键控制流水灯的花样流动功能,敬请期待《彻底玩转LED流水灯系列》的后续文章!如果需要本篇的proteus工程源文件和Keil 程序完整版,请收藏点赞本文并搜索关注微信公众号:小智单片机。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐