对于STC15F2K60S2以及比赛用的板子来说,单纯的LED灯并不算难,这次我们就结合PWM实现呼吸灯的操作。
在展示写法前,我们先来介绍一下呼吸灯。单片机对于LED灯的操控一般是0/1,也就是0/5V,呼吸灯的亮度为从0缓慢增加至全亮,再缓慢减小至0,这是一个呼吸的过程。同样的,要想实现亮度的渐变,单片机对于LED所连接的IO口的输出电压也应该逐渐变化。
然而单片机对于IO口的输出只有两种情况,0或者1,因此我们选择利用PWM。
先看一下官方说法。
PWM,全称是Pulse Width Modulation,即脉冲宽度调制。它是一种数字信号,主要由占空比和频率来定义。占空比是指在一个周期内,信号处于高电平的时间占据整个信号周期的百分比,而频率则代表PWM完成一个周期的速度,决定了信号在高低电平状态之间的切换速度。
PWM技术通过调节占空比的变化来调节信号、能量等的变化。在实际应用中,例如,当一个数字信号的高电平为5V,低电平为0V时,若需要这个数字信号源输出相当于3V的模拟信号,那么可以通过PWM占空比60%的方式来实现,即一个信号周期内有60%的时间输出5V,40%的时间输出0V。只要信号周期足够短,频率足够快,就可以得到一个输出电平无限接近3V的信号源。
PWM控制技术是变频技术的核心技术之一,具有控制简单、灵活和动态响应好等优点,因此在各种应用场合中占据主导地位,成为电力电子技术最广泛应用的控制方式。其应用领域包括测量、通信、功率控制与变换、电动机控制、伺服控制、调光、开关电源,甚至某些音频放大器。
如需更多关于PWM的详细信息,建议查阅电子工程或相关领域的专业书籍,或者咨询该领域的专家,以获取更准确、更深入的解释和指导。
这一段话中,我们需要抓住的关键词就是占空比。调节占空比,也就是调节周期内输出高电平所在的时间,就能够实现最终呈现(高电平*占空比)的电压效果。
原理了解了,我们首先完成通过按键调节LED亮度的练手任务。
#include <STC15F2K60S2.H>
#include <intrins.h>
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;
u8 key=0;
u8 flag_key=0;
u8 point=50;
u8 count=0;
void ctrl(u8 ctrl)
{
u8 p2data=P2;
P2=ctrl<<5|(P2&0x1f);
P2=p2data;
}
void light(u8 count)
{
if(count<point)P0=0xfe;
else P0=0xff;ctrl(4);
}
u8 key_val(void)
{
u8 adc=0;
P3=0x0f;
P42=0;P44=0;
if(P30==0)adc=7;
if(P31==0)adc=6;
if(P32==0)adc=5;
if(P33==0)adc=4;
return adc;
}
u8 key_sta(void)
{
static u8 sta=0;
u8 val=key_val(),ret=0;
switch(sta)
{
case 0:if(val!=0)sta=1;break;
case 1:if(val==0)sta=0;else{sta=2;ret=val;}break;
case 2:if(val==0)sta=0;break;
default:break;
}
return ret;
}
void Timer2_Init(void) //100??@12.000MHz
{
AUXR &= 0xFB; //?????12T??
T2L = 0xFF; //???????
T2H = 0xFF; //???????
AUXR |= 0x10; //???2????
IE2|=0X04;
EA=1;
}
void Timer0_Init(void) //10??@12.000MHz
{
AUXR |= 0x80; //?????1T??
TMOD &= 0xF0; //???????
TL0 = 0x88; //???????
TH0 = 0xFF; //???????
TF0 = 0; //??TF0??
TR0 = 1; //???0????
ET0 = 1; //?????0??
}
void service2(void) interrupt 12
{
flag_key++;
}
void service0(void) interrupt 1
{
count++;
count=count%100;
light(count);
}
void Process(void)
{
if(flag_key>10){flag_key=0;key=key_sta();}
}
void main()
{
P0=0x00;ctrl(5);
P0=0xff;ctrl(4);
P0=0x00;ctrl(6);
Timer2_Init();
Timer0_Init();
while(1)
{
Process();
if(key==4){point=point+10;key=0;}
if(key==5){if(point<=10){point+100;point=point-10;key=0;}else{point=point-10;}}
point=point%100;
}
}
占空比的初值设置为50,即50%的亮度,通过按键4增亮,按键5变暗。除了板子上的外设,我还通过万用电表检测P1^0的电压,因为LED属于共阳极接法,所测电压为(1-占空比)*5V。
完成了练手任务,设置呼吸灯就显得比较简单了。我们在设置占空比的同时,也要注意设置呼吸的频率。
设频率=0.25HZ,则周期=4S。代码如下:
#include <STC15F2K60S2.H>
#include <intrins.h>
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;
u16 flag_point=0;
u8 final=1;
u8 key=0;
u8 flag_key=0;
u8 point=0;
u8 count=0;
void ctrl(u8 ctrl)
{
u8 p2data=P2;
P2=ctrl<<5|(P2&0x1f);
P2=p2data;
}
void light(u8 count)
{
if(count<point)P0=0xfe;
else P0=0xff;ctrl(4);
}
u8 key_val(void)
{
u8 adc=0;
P3=0x0f;
P42=0;P44=0;
if(P30==0)adc=7;
if(P31==0)adc=6;
if(P32==0)adc=5;
if(P33==0)adc=4;
return adc;
}
u8 key_sta(void)
{
static u8 sta=0;
u8 val=key_val(),ret=0;
switch(sta)
{
case 0:if(val!=0)sta=1;break;
case 1:if(val==0)sta=0;else{sta=2;ret=val;}break;
case 2:if(val==0)sta=0;break;
default:break;
}
return ret;
}
void live(void)
{
if(point==0)final=1;
if(point==100){point=99;final=0;}
if(final)
{
point++;
}
else
{
point--;
}
}
void Timer2_Init(void) //100??@12.000MHz
{
AUXR &= 0xFB; //?????12T??
T2L = 0xFF; //???????
T2H = 0xFF; //???????
AUXR |= 0x10; //???2????
IE2|=0X04;
EA=1;
}
void Timer0_Init(void) //10??@12.000MHz
{
AUXR |= 0x80; //?????1T??
TMOD &= 0xF0; //???????
TL0 = 0x88; //???????
TH0 = 0xFF; //???????
TF0 = 0; //??TF0??
TR0 = 1; //???0????
ET0 = 1; //?????0??
}
void service2(void) interrupt 12
{
flag_key++;
flag_point++;
if(flag_point>200)
{
flag_point=0;
live();
}
}
void service0(void) interrupt 1
{
count++;
count=count%100;
light(count);
}
void Process(void)
{
if(flag_key>10){flag_key=0;key=key_sta();}
}
void main()
{
P0=0x00;ctrl(5);
P0=0xff;ctrl(4);
P0=0x00;ctrl(6);
Timer2_Init();
Timer0_Init();
while(1)
{
Process();
}
}
以上代码已经关闭了蜂鸣器、继电器,数码管保持全息,建议大家手打一遍,加深印象。如有问题欢迎私信!