51单片机入门LED篇

51单片机入门LED篇

参考教程: 2-1 点亮一个LED_哔哩哔哩_bilibili

文章目录

一、LED(发光二极管)原理

LED的电路符号如下所示,左边是正极,右边是负极( 正极的电压高于负极,电流从正极流向负极,即为有正向电流,LED在此条件下可以被点亮 )。

二、开发板中的LED模块

1、电路图分析

LED正极接电源VCC (VCC提供正向5V电压);红框内的是限流电阻,如果不加限流电阻,那么流过LED的电流会很大,可能会对LED造成破坏; LED负极对接MCU的引脚 (引脚输出的电平由 P2寄存器 控制, 图中的P20-P27其实是P2的8位分别控制0-7这8个引脚 ),如果引脚输出高电平,LED不会被点亮, 如果引脚输出低电平,LED才会有正向电流,从而会被点亮

2、通过写代码点亮LED的原理

程序通过CPU给寄存器进行配置,寄存器通过驱动器向外部引脚输出高电平or低电平

从上往下对应低位到高位(D0到D7)

三、点灯实验

1、点亮第一个LED灯实验

(1)在main.c文件中输入以下代码,然后点击"编译"。

cpp 复制代码
#include <REGX52.H>  //头文件
 
void main()
{
	
	P2 = 0xFE;  //给P2寄存器配置成1111 1110从而控制只有1个LED被点亮
	
	while(1)
	{
		//可以写一个死循环让这段代码一直执行
		//没有死循环的话,其实void main()包含的代码也是一直重复执行
	}
}

或者

c 复制代码
#include <REGX52.H>  //头文件

sbit LED1=P2^0;	//将P2.0管脚定义为LED1

void main()
{
	LED1=0;	//LED1端口设置为低电平
	while(1)
	{
		
	}		
}

单个LED闪烁(粗略计算)

c 复制代码
#include <REGX52.H>  //一般写程序前都先添加这个头文件

typedef unsigned int u16;	//对系统默认数据类型进行重定义
typedef unsigned char u8;

sbit LED1=P2^0;	//将P2.0管脚定义为LED1

//1个机器周期 = 12 ÷ 11.0592MHz ≈ 1.085微秒 (μs),每次循环需要消耗 约8个机器周期(包含取数、减1、跳转等指令)。
void delay_10us(u16 ten_us)
{
	while(ten_us--);	

}
void main()
{	
	while(1)
	{
		LED1=0;	//点亮
		delay_10us(50000); //大约延时450ms
		LED1=1;	//熄灭
		delay_10us(50000); 	
	}		
}

(2)注意要在"Output"中勾选"Create HEXFile",这样才能得到编译生成的.hex文件。

(3)拿到编译产生的文件后,打开STC-ISP,选择相应的单片机型号(这个在开发板的芯片上有写,一定要选择对应型号)以及串口号,再选择"打开程序文件",找到刚刚生成的.hex文件,接着就可以把该文件下载到开发板中完成点灯。

2、8个LED灯同时进行闪烁实验

(1)由于程序本身的执行速度很快,仅靠编写两句不同的代码配置P2寄存器是无法观察到LED闪烁的现象的,这时就需要借助延时函数。在STC-ISP中可以直接生成延时函数,其中 系统频率要与开发板的晶振相同(我的晶振频率是11.0592MHz) ,指令集改为STC-Y1(右侧适用系列符合情况就行),然后就能得到延时函数。

(2)在main.c文件中输入以下代码,然后点击"编译"。

由于原理图的设计的缘故,数码管也会跳(详看74HC138译码器部分)

C51 数据类型分类和数值范围:

数据类型 长度 范围
unsigned char 1字节 0~255
signed char 1字节 -128~127
unsigned int 2字节 0~65535
signed int 2字节 -32768~32767
unsigned long 4字节 0~4294967295
signed long 4字节 -2147483648~2147483647
float 4字节 ±1.175494E-38~±3.402823E+38
bit 1位 0或1

原则:编写程序时,对于变量只用小不用大。

移位指令

左移运算符 (<<)

语法value << n

规则 :将value的二进制位全部向左移动n位,右边空出的位一律补 0,左边移出的高位被丢弃。

右移运算符 (>>)

语法value >> n

规则 :将value的二进制位全部向右移动n位。右移分为两种模式,取决于数据是否有符号:

(1) 逻辑右移 (针对无符号数 unsigned)

  • 规则 :左边空出的位一律补 0

(2) 算术右移 (针对有符号数 signed)

  • 规则 :左边空出的位补符号位(正数补0,负数补1)。
cpp 复制代码
#include <REGX52.H>  //一般写程序前都先添加这个头文件
#include <INTRINS.H>  //_nop_()需要这个头文件
 
void Delay500ms()	//借助STC-ISP生成的500ms延时函数(开发板不同,这个函数也会不同!)
{
	unsigned char i, j, k;
 
	_nop_();//在循环嵌套开始前插入一个 _nop_();,是为了补偿循环初始化时的时钟周期误差,精确达到 500ms。
	i = 4;
	j = 129;
	k = 119;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
 
void main()
{
	
	while(1)
	{
		P2 = 0xF0;  //前4个LED灯亮,后4个LED灯灭
		Delay500ms();  //在两句代码间调用延时函数,那么程序将在这里暂停500ms,然后再继续往下执行
		P2 = 0x0F;  //前4个LED灯灭,后4个LED灯亮
		Delay500ms();  //程序将在这里再暂停500ms,然后再继续往下执行,否则灯灭后会马上再亮
	}
	
}

或者

c 复制代码
#include <REGX52.H>  //一般写程序前都先添加这个头文件
#include <INTRINS.H>  //_nop_()需要这个头文件

typedef unsigned int u16;	//对系统默认数据类型进行重定义
typedef unsigned char u8;

#define LED_PORT	P2	//使用宏定义P2端口

/*******************************************************************************
* 函 数 名       : delay_10us
* 函数功能		 : 延时函数,ten_us=1时,大约延时10us
* 输    入       : ten_us
* 输    出    	 : 无
*******************************************************************************/
void delay_10us(u16 ten_us)
{
	while(ten_us--);	
}


void main()
{	
   	u8 i=0;

	LED_PORT=~0x01;
	delay_10us(50000);
	while(1)
	{
		//方法1:使用移位+循环实现流水灯
//		for(i=0;i<8;i++)
//		{
//			LED_PORT=~(0x01<<i);	//将1右移i位,然后取反将结果赋值到LED_PORT
//			delay_10us(50000);
//		}
		
		//方法2:使用循环+_crol_或_cror_函数实现流水灯
		for(i=0;i<7;i++)	 //将led左移一位
		{									  
			LED_PORT=_crol_(LED_PORT,1);
			delay_10us(50000); 	
		}
		for(i=0;i<7;i++)	//将led右移一位
		{
			LED_PORT=_cror_(LED_PORT,1);
			delay_10us(50000);	
		}	
	}		
}

(3)按照上一例的操作将生成的.hex文件写进开发板,即可观察LED闪烁的现象。

3、8个LED灯轮流闪烁(流水灯)实验

cpp 复制代码
#include <REGX52.H>
#include <INTRINS.H>  //_nop_()需要这个头文件
 
void Delay500ms()	//借助STC-ISP生成的500ms延时函数(开发板不同,这个函数也会不同!)
{
	unsigned char i, j, k;
 
	_nop_();
	i = 4;
	j = 129;
	k = 119;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
 
void main()
{
	while(1)
	{
		P2 = 0xFE; //1111 1110,第一盏LED灯被点亮
		Delay500ms();
		P2 = 0xFD; //1111 1101,第二盏LED灯被点亮
		Delay500ms();
		P2 = 0xFB; //1111 1011,第三盏LED灯被点亮
		Delay500ms();
		P2 = 0xF7; //1111 0111,第四盏LED灯被点亮
		Delay500ms();
		P2 = 0xEF; //1110 1111,第五盏LED灯被点亮
		Delay500ms();
		P2 = 0xDF; //1101 1111,第六盏LED灯被点亮
		Delay500ms();
		P2 = 0xBF; //1011 1111,第七盏LED灯被点亮
		Delay500ms();
		P2 = 0x7F; //0111 1111,第八盏LED灯被点亮
		Delay500ms();
	}
	
}

代码优化建议

c 复制代码
#include <REGX52.H>
#include <INTRINS.H>  //_nop_()需要这个头文件
 
void Delay500ms()	//借助STC-ISP生成的500ms延时函数(开发板不同,这个函数也会不同!)
{
	unsigned char i, j, k;
 
	_nop_();
	i = 4;
	j = 129;
	k = 119;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
 
void main()
{
		unsigned char i;
		while(1)
    {
        switch(i)
        {   
            case 1: P2 = 0xFE; break;
            case 2: P2 = 0xFD; break;
            case 3: P2 = 0xFB; break;   
            case 4: P2 = 0xF7; break;
            case 5: P2 = 0xEF; break;   
            case 6: P2 = 0xDF; break;
            case 7: P2 = 0xBF; break;  
            case 8: P2 = 0x7F; i=0; break;//i归0使语句又从case 1执行
        } 
        i++;//i从1变到8使switch语句轮流调用"P2 = xxxx;"
        Delay500ms();
		}
	
}
c 复制代码
#include <REGX52.H>
#include <INTRINS.H>  //_nop_()需要这个头文件
 
void Delay500ms()	//借助STC-ISP生成的500ms延时函数(开发板不同,这个函数也会不同!)
{
	unsigned char i, j, k;
 
	_nop_();
	i = 4;
	j = 129;
	k = 119;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
 
void main()
{
	unsigned char i = 0;  
    unsigned char LEDSET[8]={0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F};
    while(1)
    {
        P2=LEDSET[i];
        i++;
        if(i>=8)i=0;//i需归0使P0只能使用数组中的8个元素,防止使用到超出数组元素以外的值,其实就是让i在0~7之间变化
        Delay500ms();
    }
	
}
c 复制代码
#include <REGX52.H>
#include <INTRINS.H>  //_nop_()需要这个头文件
 
void Delay500ms()	//借助STC-ISP生成的500ms延时函数(开发板不同,这个函数也会不同!)
{
	unsigned char i, j, k;
 
	_nop_();
	i = 4;
	j = 129;
	k = 119;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
 
void main()
{
		unsigned char i;  
    while(1)
    {
        for(i=0;i<8;i++)
				{
						P2 = ~(0x01<<i);	//将1右移i位,然后取反将结果赋值到P2
						delay500ms();
				}
				for(i=0;i<8;i++)
				{
						P2 = ~(0x80>>i);	//将左移i位,然后取反将结果赋值到P2
						delay500ms();
				}
		
    }
	
}

(2)按照上一例的操作将生成的.hex文件写进开发板,即可观察LED流水的现象。

4、8个LED灯轮流闪烁(进阶版)实验

(1)在main.c文件中输入以下代码,然后点击"编译"。

cpp 复制代码
#include <REGX52.H>
#include <INTRINS.H>  //_nop_()需要这个头文件
 
void Delay1ms(unsigned int xms)		//对借助STC-ISP生成的1ms延时函数进行修改
{
	while(xms)  //让这个1ms延时函数重复执行xms次
	{
		unsigned char i, j;
 
	_nop_();
	i = 2;
	j = 199;
	do
	{
		while (--j);
	} while (--i);
	xms--;  //smx作为计数器
	}
}
 
void main()
{
	P2 = 0xFE;  //1111 1110,初始状态为第一个LED灯亮
	while(1)
	{
		if(P2 == 0xFF)  //一轮循环结束,重置为初始状态(第一个LED灯亮)
			P2 = 0xFE;
		Delay1ms(500);  //500ms延时(根据需求可以传入不同的延时时间)
		P2 = P2 << 1;  //下一个LED灯亮(P2中的0左移)
		P2 += 1;  //第一轮循环结束前亮过的LED灯不能再亮(左移会导致第一位为0,需要将其修改为1)
		
	}
}

(2)按照上一例的操作将生成的.hex文件写进开发板,即可观察LED流水的现象。

相关推荐
Jack153027682791 小时前
D2596HV高压异步DC-DC降压芯片简介
嵌入式硬件·汽车·51单片机·无人机·工业自动化
振浩微433射频芯片4 小时前
从门锁到考勤,VRC522B如何在近场读卡场景中“稳坐C位”?
网络·人工智能·单片机·物联网
Groundwork Explorer16 小时前
W5500 网卡编程初始化与使用指南
python·单片机
单片机仿真设计17 小时前
【proteus仿真】基于 STM32 的智能垃圾桶设计(仿真图+程序)
c语言·stm32·stm32单片机·proteus·毕设·仿真
智闲电子设计18 小时前
STM32 SD 卡 + FatFS 实战:掉电丢数据?f_sync 和簇对齐写救你
stm32·单片机·嵌入式硬件
ヾChen19 小时前
ESP32 OTA 踩坑实录
stm32·单片机·嵌入式硬件·物联网·学习
小僧景贤20 小时前
嵌入式ADC高阶深度解析:基于STM32底层原理、误差溯源、硬件调校与工业级落地
单片机·嵌入式硬件·stm32 adc·工业级高精度采集·adc 误差校准·量产固件开发
黑猫学长呀20 小时前
【驱动开发常见问题】编译出现rm: cannot remove `asm/arch‘: Is a directory
驱动开发·单片机·嵌入式硬件·bug·编译报错·kernrl
雨田言炎21 小时前
STM32专题之内部FLASH详解
笔记·stm32·单片机