蓝桥杯单片机组-基础篇11:利用PWM波实现LED亮度控制

#include <REGX52.H>

sbit S7=P3^0;
sbit L1=P0^0;

void Delay(unsigned int t)
{
	while(t--);
}

void InitHC573()
{
	P2=(P2&0x1f)|0x80;
}

unsigned char stat=0;
unsigned char pwm_duty;

//======================按键扫描模块
void scankey()
{
	if(S7==0)
	{
		Delay(100);
		if(S7==0)
		{
			switch(stat)
			{
			case 0://按下1次,LED由灭转亮(亮度10%)
				L1=0;
				TR0=1;
				pwm_duty=10;
				stat=1;
			break;
			
			case 1://按下2次,亮度50%
				pwm_duty=50;
				stat=2;
			break;
			
			case 2://按下3次,亮度90%
				pwm_duty=90;
				stat=3;
			break;
			
			case 3://按下4次,LED熄灭
				L1=1;
				stat=0;
				TR0=0;//注意一定要关闭定时器,否则定时器继续运行,灯会以90%亮度继续点亮
			break;
			}
			while(S7==0);
		}
	}
}
//=========================

//=========================定时器模块

void InitTimer0()
{
	TMOD=0x01;
	TH0=(65535-100)/256;
	TL0=(65535-100)%256;
	EA=1;
	ET0=1;
}
unsigned char count=0;
void ServiceT0()interrupt 1
{
	TH0=(65535-100)/256;
	TL0=(65535-100)%256;
	count++;
	if(count==pwm_duty)//当count<pwm_duty时,L1默认为0
		L1=1;
	else if(count==100)
	{
		count=0;
		L1=0;
	}
}
//=========================


void main()
{
	InitHC573();//控制锁存器进入LED模式
	InitTimer0();//初始化定时器
	while(1)
	{
		scankey();//按键扫描
	}
}

博主看的是小蜜蜂老师的视频。最开始我采用的是按键扫描和LED点亮分开编程的思路,但总是无法实现功能,猜想是按键扫描过程中定时器仍然在运行所致。

原本思路:

LED处于模式1(定时器不断计数)-按下按键,进行扫描(此时定时器仍在计数,LED按原来亮度点亮)-消抖(LED原亮度)-确认按下,刷新标志变量(LED原亮度)-等待按键松开(LED原亮度)-LED函数读取标志变量,刷新pwm波的占空比,模式2(LED下一亮度)

原本代码:

void LEDRunning()
{
	switch(stat)
			{
			case 1:
				L1=0;
				TR0=1;
				pwm_duty=10;
//				stat=1;
			break;
			
			case 2:
				pwm_duty=50;
//				stat=2;
			break;
			
			case 3:
				pwm_duty=90;
//				stat=3;
			break;
			
			case 4:
				L1=1;
//				stat=0;
				TR0=0;
			break;
			}
}


void scankey()
{
	if(S7==0)
	{
		Delay(100);
		if(S7==0)
		{
			stat++;
			if(stat==5)
				stat=0;
            while(S7==0);
		}
	}
}

void main()
{
	InitHC573();
	InitTimer0();
	L1=1;
	while(1)
	{
		scankey();
		LEDRunning();
	}
}

原本代码产生的现象:

蓝桥杯PWM波-错误示范版

如视频所示,上电后LED1熄灭。按下按键一次,LED1高亮;按下两次,LED1中亮;按下三次,LED1高亮;按下四次,LED1熄灭。与题目要求相比,发现第一次按下按键现象不符。

因此分析问题出在case1。

我的代码在LEDRunning()中,用switch语句时,case1中多写了一个令L1=0的操作,由于IAP15速度很快,导致在只按下一次按键时,会反复多次执行L1=0造成亮度过高,远远超过case1的10%亮度。

老师的代码在第一次按下按键之后就刷新stat=1,避免了重复执行L1=0;的操作。

附上改进后代码:

#include <REGX52.H>
#include "INTRINS.H"

sbit S7=P3^0;//S7位定义
sbit L1=P0^0;//LED1位定义

void Delay(unsigned int t)//延时
{	unsigned char i, j;
	while(t--){ // @11.0592M
	_nop_();
	_nop_();
	_nop_();
	i = 11;
	j = 190;
	do
	{
		while (--j);
	} while (--i);
	}
}

void InitHC573()//选择LED模式
{
	P2=(P2&0x1f)|0x80;
}

unsigned char stat=0;//LED状态变量
unsigned char pwm_duty;//PWM占空比

void LEDRunning()//LED显示函数
{
	switch(stat)
			{
			case 1:
//				L1=0;这一步错了,case1本来是亮度10%,IAP15快速执行会使亮度过高
				TR0=1;
				pwm_duty=10;//亮度10%
			break;
			
			case 2:
				pwm_duty=50;//亮度50%
			break;
			
			case 3:
				pwm_duty=90;//亮度90%
			break;
			
			case 4:
				L1=1;
				TR0=0;//熄灭
			break;
			}
}
 
 
void scankey()//按键扫描
{
	if(S7==0)
	{
		Delay(10);
		if(S7==0)
		{
			stat++;
			if(stat==5)
				stat=1;
           		while(S7==0);//防止重复扫描
		}
	}
}
 


//=========================定时器模块

void InitTimer0()
{
	TMOD=0x01;
	TH0=(65535-100)/256;
	TL0=(65535-100)%256;
	EA=1;
	ET0=1;
}

unsigned char count=0;
void ServiceT0()interrupt 1
{
	TH0=(65535-100)/256;
	TL0=(65535-100)%256;
	count++;
	if(count<pwm_duty)
		L1=0;
	else if(count==pwm_duty)
		L1=1;
	else if(count==100)
	{
		count=0;
		L1=0;
	}
}
//=========================

void main()
{
	InitHC573();//LED模式
	InitTimer0();//初始化T0
	L1=1;//上电L1熄灭
	while(1)
	{
		scankey();
		LEDRunning();
	}
}

感悟:单片机是要软件和硬件结合起来玩的,只考虑编程不考虑硬件情况,就可能在细节上栽跟头。希望自己能更清楚单片机各个外设的底层原理,理解单片机是如何通过控制IO口和不断扫描读取执行来实现外设的功能的。其中,“扫描”是单片机初学时经常遇到的概念,我认为不仅读取按键状态是一种扫描,反复执行某段代码、某个函数也是一种扫描,单片机的应用是在死循环“不断扫描”代码中实现的。循环、重复的操作,在人看来枯燥乏味,执行起来效率也很低,对单片机来说却不是这样。单片机CPU速度很快,不同单片机的速度也不尽相同,这一点编写代码时必须考虑。不同于电脑有操作系统,可以多线程运行,单片机是单线程运行却用很快的循环速度弥补了这一缺陷,使得它可以实现多种功能。也正因为单片机单线程运行的特点,在写代码时要注意避免长时间的空指令或局部死循环,以免浪费CPU的运行资源。

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值