C51-流水灯实验程序
注意:请结合前面已发布文章一起阅读,因为前面已经提及到的不再重复讲述。
1 项目需求
基于STC89C52单片机,使用多种方式完成流水灯实验;并扩展实现了对参与流水灯队列的led灯数量的控制。
2 LED硬件原理图分析
开发板的LED正极都是通过串联电阻与VCC相连接,负极均连接在单片机的P1寄存器上。
以LED0为例,LED0(即发光二极管D1)的负极连接在与单片机的P1.0口;正极通过串联电阻与VCC相连接;所以,当单片机的P1.0口输出低电平时,二极管D1就会导通,点亮LED0,当P1.0口输出高电平,LED0熄灭。
3 项目实施
3.1 项目分析
假如从D1-D8分别称为led0-led7,实现流水灯的效果就是led0亮一段时间再熄灭,然后led1亮一段时间再熄灭,。。。,led7亮一段时间再熄灭,然后循环到led0亮一段时间再熄灭。
即:led0亮->延时->led0灭且led1亮->延时->led1灭且led2亮->延时->…->延时->led6灭且led7亮->延时->led7灭且led0亮。
其中,LED的亮灭控制在上一期中已经实现了;所以还需要实现延时和流水灯策略。
对于延时分为软件延时和硬件延时:
- 硬件延时是通过定时器来实现,较为精确,但是需要占用更多硬件资源。
- 软件延时主要采用循环体进行延时,想要做到精确延时可借助示波器确定延时时间。
该实验中通过软件延时来实现延时功能,经典延时函数如下:内循环执行112次为1ms(通过示波器波形可以确定,每个板子存在差异),外循环通过给入时间来实现对定时时间的控制,精度为1ms;
/* ms延时函数-软件延时 */
void delay_soft_ms(unsigned int ms_time)
{
unsigned int i = 0, j = 0;
for(i = ms_time; i > 0; i--)
{
for(j = 112; j > 0; j--);
}
}
3.2 代码编写
以下通过4种方式实现了流水灯效果,且方式2中的3种子方法可控制参与流水灯队列的led灯个数。
/************************************
功能:多种方法实现LED流水灯
作者:eCoderY 时间:2024.01.15
************************************/
#include <reg52.h>
#define SWITCH_CODER (2) /* 选择方法 */
#define SUB_SWITCH_CODER (3) /* 选择方式2中的子方法 */
#define LED P1 /* 定义LED为P1寄存器 */
#define ON_LED(led, x) do{led &= ~(1 << x);}while(0);
#define OFF_LED(led, x) do{led |= (1 << x);}while(0);
/* 函数声明 */
void delay_soft_ms(unsigned int ms_time);
void led_method1(unsigned int ms_time);
void led_method2(unsigned int ms_time, unsigned char ledNum);
void main(void)
{
while(1)
{
/* 方式1:依次对每个led进行点亮、延时、关闭操作 */
#if(SWITCH_CODER == 1)
led_method1(100);
#endif
/* 方式2:又可分为3种子方法 */
#if(SWITCH_CODER == 2)
led_method2(200, 6);
#endif
}
}
/* ms延时函数-软件延时 */
void delay_soft_ms(unsigned int ms_time)
{
unsigned int i = 0, j = 0;
for(i = ms_time; i > 0; i--)
{
for(j = 112; j > 0; j--);
}
}
/* 方式1:依次对每个led进行点亮、延时、关闭操作 */
void led_method1(unsigned int ms_time)
{
while(1)
{
/* LED0 */
ON_LED(LED, 0)
delay_soft_ms(ms_time);
OFF_LED(LED, 0)
/* LED1 */
ON_LED(LED, 1)
delay_soft_ms(ms_time);
OFF_LED(LED, 1)
/* LED2 */
ON_LED(LED, 2)
delay_soft_ms(ms_time);
OFF_LED(LED, 2)
/* LED3 */
ON_LED(LED, 3)
delay_soft_ms(ms_time);
OFF_LED(LED, 3)
/* LED4 */
ON_LED(LED, 4)
delay_soft_ms(ms_time);
OFF_LED(LED, 4)
/* LED5 */
ON_LED(LED, 5)
delay_soft_ms(ms_time);
OFF_LED(LED, 5)
/* LED6 */
ON_LED(LED, 6)
delay_soft_ms(ms_time);
OFF_LED(LED, 6)
/* LED7 */
ON_LED(LED, 7)
delay_soft_ms(ms_time);
OFF_LED(LED, 7)
}
}
/* 方式2:优化方式1,且可控制使用led灯的数量1~8个 */
void led_method2(unsigned int ms_time, unsigned char ledNum)
{
unsigned char num = 0;
/* 检查led灯数量是否合法 */
if(ledNum < 1 || ledNum > 8)
{
/* 非法数量输入,灯全亮闪烁标识出错警告 */
while(1)
{
LED = 0x0;
delay_soft_ms(ms_time);
LED = 0xff;
delay_soft_ms(ms_time);
}
}
/* 流水灯实现 */
while(1)
{
#if(SUB_SWITCH_CODER == 2)
LED = 0xfe;
#endif
for(num = 0; num < ledNum; num++)
{
/* 方式2.1:优化方式1 */
#if(SUB_SWITCH_CODER == 1)
ON_LED(LED, num)
delay_soft_ms(ms_time);
OFF_LED(LED, num)
#endif
/* 方式2.2:通过移位实现流水灯 */
#if(SUB_SWITCH_CODER == 2)
delay_soft_ms(ms_time);
LED <<= 1; /* 1111 1110 << 1 = 1111 1100 */
LED |= 0x01; /* 灭掉led0,右侧补位添的0 */
#endif
/* 方式2.3:普通位操作 */
#if(SUB_SWITCH_CODER == 3)
LED &= ~(1 << num); /* 亮灯 */
delay_soft_ms(ms_time);
LED |= (1 << num); /* 灭灯 */
#endif
}
}
}
3.3 实验现象
通过更改SWITCH_CODER
(1, 2)和SUB_SWITCH_CODER
(1, 2, 3)的值,选择4种方式,然后编译烧录,均能达到流水灯效果,且SWITCH_CODER == 2
时,3种方式均可实现对参与流水灯队列的led灯数量的控制。