实现目标
使用AT89C52的P1口,接8个LED灯,实现流水灯。具体效果如下:
- 初始均为灭
- 从一边到另一边,灯一个接一个亮
- 当亮完所有灯时,全灭
- 再反方向,一个一个亮
- 当亮完所有灯时,全灭
- 全灭
- 全亮
- 全灭
- 全亮
- 每隔一个灯亮(两次)
- 重复上述步骤
Proteus仿真图
C51代码
#include <REG52.H>//80C52特殊功能寄存器定义
#define LED_PORT1 P1 //用P1口驱动灯,低亮,高灭
void time(unsigned int ucMs);/* 延时单位:毫秒 */
void main(void)
{
unsigned char ucTimes;
#define DELAY_TIME 400 //延时
while(1)
{
LED_PORT1=0xff;//灭P1口灯
time(200);
//从左往右依次点亮LED
for(ucTimes=0;ucTimes<8;ucTimes++){//循环点亮P1口灯
LED_PORT1 = LED_PORT1 - (0x80>>ucTimes);//亮灯需低电平驱动
time(DELAY_TIME);
}
LED_PORT1=0xff;//灭P1口灯
time(200);
//然后从右往左依次点亮LED
#include <REGX52.H>
for(ucTimes=0;ucTimes<8;ucTimes++){//循环点亮P1口灯
LED_PORT1 = LED_PORT1 - (0x01<<ucTimes);//亮灯需低电平驱动
time(DELAY_TIME);
}
LED_PORT1=0xff;time(DELAY_TIME);//全灭
LED_PORT1=0;time(DELAY_TIME);//全亮
LED_PORT1=0xff;time(DELAY_TIME);//全灭
LED_PORT1=0;time(DELAY_TIME);//全亮
LED_PORT1=0x55;time(DELAY_TIME);//隔一个点亮
LED_PORT1=0xaa;time(DELAY_TIME);//交换
LED_PORT1=0x55;time(DELAY_TIME);//隔一个点亮
LED_PORT1=0xaa;time(DELAY_TIME);//交换
}
}
/*********** time C **************/
void time(unsigned int ucMs)//延时单位:ms
{
#define DELAYTIMES 239
unsigned char ucCounter; // 延时设定的循环次数
while (ucMs!=0) {
for (ucCounter=0; ucCounter<DELAYTIMES; ucCounter++){}//延时
ucMs--;
}
}
个人理解
开始时,P1输出为1111 1111 (0xff),灯是低电平的时候才会亮,所有开始时8个灯均为灭
为了使第一个灯亮,那么就需要P1输出为0111 1111 (当然1111 1110 也可以, 具体看线路连接)
从左往右依次点亮LED的代码如下:
for(ucTimes=0;ucTimes<8;ucTimes++){//循环点亮P1口灯
LED_PORT1 = LED_PORT1 - (0x80>>ucTimes);//亮灯需低电平驱动
time(DELAY_TIME);
}
为什么这个代码可以实现灯依次点亮呢?
80=1000 0000(十六进制转二进制)
当ucTimes=0,0x80>>ucTimes=1000 0000
此时LED_PORT1=1111 1111
1111 1111 - 1000 000 = 0111 1111
LED_PORT1= 0111 1111
第一个灯亮 其余灯灭
当ucTimes=1,0x80>>ucTimes=1100 0000
此时LED_PORT1=0111 1111
0111 1111- 1100 0000 = 1011 1111
这里使用:减去一个数 等于 加上这个数的相反数
0111 1111 + 0100 0000 = 1011 1111 (相反数:取反再加一)
第二个灯亮 其余灯灭
其余就是类似的了
读者可以自行验证
- 注:这里有个易错的地方,0x80>>ucTimes右移,左边是自动补1的。
运行结果
流水灯
本文Proteus仿真图及源程序获取见:
说明
参考课本:单片机原理与嵌入式系统设计