单片机C51之1:流水灯

前言:51系列单片机硬件驱动

            入门基础

流水灯是第一个单片机程序,或者说是最简单的单片机程序,但是里面蕴含了一个最基本的原理:用数据直接控制外设,小小的led灯即是数据的最直接体现。

目录

1.C语言移位指令实现连续点亮;

2.C51库函数实现循环点亮;

3.万能方法---硬写入数值并延时;

4.C语言掩码实现连续点亮;

5.C语言实现循环点亮

6.连续亮灭 


实验准备:

实验板P1口连接8个led小灯的阴极,共阳极接法,有限流电阻10kΩ。图略

1.C语言移位指令实现连续点亮

#include<reg52.h>
typedef unsigned int u16;    //typedef定义数据类型
void delay(u16 i);

int main(void)                //主函数
{
	int j;

	while(1)
	{
	
		for(j=0;j<7;j++)
		{
			P1=0xfe<<j;
			delay(50000);
		}
	}

}

void delay(u16 i)            //延时函数
{
	while(i--);	
}

说明:把输出口赋值和移位指令放一起,每次都从头开始。

效果:点亮第一个led;点亮1,2号led;点亮1,2,3号led。。。依次进行,每次延时约0.5ms

自动循环点亮。 

2.C51库函数实现循环点亮

#include<reg52.h>            //添加库函数支持,可以调用左循环移位函数_crol_
#include<intrins.h>			 //因为要用到左右移函数,所以加入这个头文?
typedef unsigned int u16;
void delay(u16 i);

void delay(u16 i)
{
	while(i--);	
}

int main(void)
{
	int j;
	P1=0xfe;
	delay(50000);                //给初值添加延时,不然只闪一下
	while(1)
	{
	
		for(j=0;j<7;j++)        //循环移位,和j值已经无关
		{
			P1=_crol_(P1,1);    //循环移位
			delay(50000);
		}
	}

}

说明:每次延时后只点亮一个led灯,依次是1号led,2号led。。。。和方法1有区别 

3.万能方法---硬写入数值并延时

#include<reg52.h>
typedef unsigned int u16;
void delay(u16 i);

void delay(u16 i)
{
	while(i--);	
}


int main(void)
{
	P1=0xfe;
	delay(50000);
	P1=0xfd;
	delay(50000);
	P1=0xfb;
	delay(50000);
	P1=0xf7;
	delay(50000);
	P1=0xef;
	delay(50000);
	P1=0xdf;
	delay(50000);
	P1=0xbf;
	delay(50000);
	P1=0x7f;
	delay(50000);

	while(1);
}

说明:这种办法当然看起来最笨拙了。但是您有没有发现,不管你想让灯亮几个,用什么顺序亮,都可以用这个最笨拙的办法去实现(如果想不到移位循环什么的)。为什么要写得这么麻烦,原因有二,一是因为突出了单片机的核心思想,给端口一个1或者0,就会按照人的思路去走。二是因为看见有个同学在问,怎么让流水灯从外侧向内侧亮,如果把main()函数改一改就会有所发现了

int main(void)
{
	P1=0x7e;
	delay(50000);
	P1=0xbd;
	delay(50000);
	P1=0xdb;
	delay(50000);
	P1=0xe7;
	delay(50000);
	P1=0xdb;
	delay(50000);
	P1=0xbd;
	delay(50000);
}

是不是想玩什么花样的led灯,都能写出来(虽然很麻烦。。。)

4.C语言掩码实现连续点亮

#include<reg52.h>
typedef unsigned int u16;
void delay(u16 i);

int main(void)
{
	int j;

	while(1)
	{
	
		for(j=0;j<7;j++)
		{
			
		    P1&=~(1<<j);
			delay(50000);
		}
        P1=0xfe;
	}


}

void delay(u16 i)
{
	while(i--);	
}

 说明:和方法1含义一样,用掩码给字节写入数据,是典型的C语言方法。

led流水灯是51单片机的起点,从这里认识到通过数据操作控制硬件的概念

 5.C语言实现循环点亮

int main(void)     //C语言自身的循环移位
{

	P1=0xfe;
	delay(50000);
	while(1)
	{
		P1=P1<<1|P1>>7;     //位或符号不是序列点,P1中途不发生变化
	  	delay(50000);
	}
}

6.连续亮灭 

方法1的加强版,反复的移位实现不同效果

#include<reg52.h>
typedef unsigned int u16;
void delay(u16 i);

void delay(u16 i)
{
	while(i--);	
}

int main(void)
{
	int j;

	while(1)
	{
		
		
		for(j=0;j<8;j++)
		{
			P1=0xfe<<j;
		  	delay(50000);
		}
		for(j=0;j<8;j++)
		{
			P1|=0x80>>j;
		  	delay(50000);
		}
	}
}

 --------------------2022.3.20update-------------------------------------------------------------------------------------

抽象1:流水灯的实现包括写入数值和延时两个方面(方法3是最直接体现),要实现灯光移动的视觉效果,则加上移位。为什么要这样解释到最细,显得啰嗦,因为对初学编程的人来说,能像搭积木一样理解程序,会比较友好。比如P1=0xfe<<j;表示写入数值,外面加上for(j=0;j<8;j++)表示移位,或者写成

	while(1)
	{
		P1=0xfe;
		delay(50000);
		for(j=1;j<=7;j++)
		{
			P1=0xfe<<j;
		  	delay(50000);
		}
		for(j=0;j<8;j++)
		{
			P1|=0x80>>j;
		  	delay(50000);
		}
	}

更直观,赋值,移位,延时一目了然。 会写了之后,改成下面那段代码更简洁。

抽象2:流水灯效果常通过移位来实现。移位写法不同,效果不同。

一.其中‘连续亮起’的含义是每次写入0或者1(由led灯接法确定,本例共阳极接法写入0),并且移动1位(或n位)。具体代码由循环(for循环或者while循环都可以)加每次移动1位(或n位)完成。这种方法每次给出的初值是确定的,例如移动1位初值是0xfe和0x80。 以上述方法5为例,如果main函数改成

int main(void)
{
	int j;

	while(1)
	{
		
		
		for(j=0;j<8;j+=2)
		{
			P1=0xfc<<j;
		  	delay(50000);
		}
		for(j=0;j<8;j+=2)
		{
			P1=0xc0>>j;
		  	delay(50000);
		}
	}
}

可以发现如果每次移动2位,初值需要修改成0xfc和0xc0。

循环灭led灯有一个小bug,修改如下:

int main(void)
{
	int j;

	while(1)
	{
		
		
		for(j=0;j<8;j+=2)
		{
			P1=0xfc<<j;
		  	delay(50000);
		}
		for(j=0;j<8;j+=2)
		{
			P1|=0xc0>>j;        //修改后达到逐渐灭灯的效果
		  	delay(50000);
		}
	}
}

产生的原因:右移时补0或者补1是有可能不同的,如果需要逐渐灭灯的目的,需要持续写入1,所以做了修改,前面的代码做了相应修改。 

二.“循环点亮”的含义是每次只有一个灯的亮灭发生变化,用循环移位实现,本例中对应方法2,直接调用库函数。(C语言也可以写循环移位,但俺写了以后没成功实现)。因为直接参加循环移位,所以可能把初状态单独列出来写并延时。

另:谈到C语言的移位,在后面的接收字节数据和发送字节数据中也常常用到,被称为“移位或”。在硬件通信中,最基本的单位是“位”,而对于计算机来说,最基本的单位是“字节”。所以需要将位整合成字节实现通信。思路是接收变量所在的字节移动1位(左移或右移根据情况而定),与将要接收或发送的位相或,外部加循环(for和while都可以)8次,得到一个字节。具体代码见后续

-----------------------------------------------------------2022.4.6Update------------------------------------------------

再次发现用C语言移位指令实现连续移位最方便,下面第j位

形如:  P1&=~(1<<j);   //写0

             P1|=1<<j;         //写1

加上循环指令,则可以实现连续写入。

一个一个写入的,就用循环移位指令

<think>好的,用户需要C51单片机实现流水灯的示例代码。首先,我需要回忆一下C51单片机的基本知识。流水灯通常是通过控制IO口的高低电平,依次点亮LED灯,形成流动的效果。根据引用[2]和[3],流水灯的核心是依次移位,可能需要使用移位操作或者延时函数来控制灯的亮灭顺序。 接下来,用户可能已经具备硬件,如C51开发板、LED、连接线等,如引用[1]所述。所以代码需要兼容常见的C51型号,比如AT89C51或STC89C52。IO口方面,一般使用P1或P2口,这里可能需要根据开发板的实际连接来调整,比如假设LED接在P1口,共阴极或共阳极连接方式。这里假设是共阴极,低电平点亮,所以初始值设为0xFE(11111110),即第一个灯亮。 然后,代码结构方面,需要包含头文件reg51.h,定义主函数,初始化端口,然后进入循环。循环中,使用左移或右移指令,配合延时函数。延时函数通常用for循环实现,但需要注意C51的晶振频率,比如11.0592MHz,调整延时参数。这里可能需要用户根据实际效果调整延时时间,比如500ms。 另外,引用[3]提到流水灯的控制复杂度较低,通常通过移位实现。所以代码中使用左移操作,并加上进位处理,防止数据溢出。例如,使用_crol_或_cror_函数,这两个函数在intrins.h头文件中,需要包含进来。不过,有些编译器可能不支持,可能需要用移位运算符和位操作来实现。比如,每次左移一位,当最高位被移出时,重新赋值。例如,使用temp = temp << 1 | 0x01,这样当最高位为1后,左移会补0,但需要保持其他位,可能更简单的方法是使用循环左移函数。 检查示例代码是否有错误,比如端口定义是否正确,延时函数是否合理。同时,要提醒用户根据硬件连接修改端口,比如如果LED接在P0口,需要上拉电阻,而P1口可能不需要。此外,延时函数的参数可能需要根据实际晶振频率调整,否则流水速度可能过快或过慢。 最后,生成相关问题,考虑用户可能的后续问题,比如如何调整速度、改变方向、添加模式,或者硬件连接问题。确保引用相关的参考资料,如引用[3]提到的流水灯和跑马灯的区别,可能用户后续会问到这些,所以相关问题可以包括这些方面。</think>### C51单片机流水灯实现步骤及示例代码 根据引用[2]和[3]的实验原理,流水灯的实现需要结合硬件设计和软件编程。以下是基于8051内核单片机的实现方案: #### 一、硬件要求 1. C51单片机开发板(如STC89C52) 2. 8个LED灯(建议连接P1端口) 3. 220Ω限流电阻*8 4. 5V电源(开发板通常自带) #### 二、软件实现 ```c #include <reg51.h> #include <intrins.h> // 包含移位函数 #define LED_PORT P1 void delay_ms(unsigned int ms) { unsigned int i, j; for(i=0; i<ms; i++) for(j=0; j<123; j++); // 11.0592MHz时钟校准 } void main() { LED_PORT = 0xFE; // 初始值:1111 1110 while(1) { LED_PORT = _crol_(LED_PORT, 1); // 循环左移 delay_ms(500); // 500ms延时 } } ``` #### 三、代码解析 1. `#include <intrins.h>` 提供`_crol_`循环左移函数 2. `LED_PORT`宏定义指定控制端口(默认P1口) 3. `delay_ms`函数实现毫秒级延时 4. `_crol_`函数实现带进位循环移位,避免数据丢失[^3] #### 四、硬件连接建议 - LED阳极通过电阻接VCC - LED阴极接单片机IO口 - 共阳极接法时,IO输出低电平点亮LED[^1] #### 五、效果调整方法 1. 修改延时参数:`delay_ms(200)`加快流动速度 2. 改用`_cror_`函数实现右移效果 3. 修改初始值`0xFE`为其他模式(如`0xF0`实现两组交替)
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

重庆彭枫

你的鼓励是我创作的动力,谢谢

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值