应广单片机很多几乎所有型号单片机都自带硬件pwm,但是这些硬件pwm基本都是给调光调速或是电压调节等使用,如果硬件pwm已经用完了。那么还能实现LED的呼吸灯吗?答案是可以的。
原理大概是这样的,使用T16定时器中断来说实现,假如定时器中断时间为20us,使用25个中断作为一个pwm 周期那么这个pwm的频率就是 1/(20u*25)=2K,因此,由于受中断频率限制, 这个软pwm的频率不会太高,阶数也不能很多。但用来驱动一个亮度不高的led,看上去还是像模像样的。
不多说上代码。
#include "extern.h"
#define DISABLE 0
#define ENABLE 1
#define HIGH 1
#define LOW 0
#define EMPTY 0
#define FULL 1
#define ON 0
#define OFF 1
//#define PER_MS_CNT 99
#define PER_MS_CNT 30
/*呼吸灯输出端口*/
BIT LED1 : PA.7;
/*中断输出测试端口*/
BIT TEST : PA.6;
BYTE BitVal1;
BIT bMsFlag :BitVal1.2; /*历史状态*/
//BIT bOnOff :BitVal1.1; /*历史状态*/
byte ucPwmDtSet;
/*系统变量*/
Word usTmrCnt;
byte ucSysPwmDt;
byte ucSystick;
bit ubBthFlag;
byte ucMsCnt;
void Tmr16InterInit ( void )
{
usTmrCnt=225;
$ T16M IHRC, /1,BIT9/*8位pwm,使用IHRC*/
INTEN.T16 =1;/*开启定时中断*/
Intrq.T16 =0;/*清除中断请求*/
ENGINT //全局中断开启
}
void GpioInit (void)
{
$ LED1 out,high;
$ Test out,low;
}
/*mdf by zhongvv@163.com*/
void FPPA0 (void)
{
.ADJUST_IC SYSCLK=IHRC/2, IHRC=16MHz, VDD=5V,Init_ram;
$ CLKMD IHRC/2,En_IHRC,En_ILRC;
GpioInit();
Tmr16InterInit();
ucSystick=0;
ucSysPwmDt=0;
ucMsCnt=0;
/*pwm默认增加*/
ubBthFlag=1;
/*pwm 默认为0*/
ucPwmDtSet=0;
while (1)
{
if(bMsFlag)
{
/*更新pwm 时间间隔*/
ucMsCnt++;
/*每20ms 更新一次pwm值*/
if(ucMsCnt>19)
{
ucMsCnt=0;
/*pwm上升*/
if(ubBthFlag)
{
/*pwm增加*/
ucPwmDtSet++;
/*切换*/
if(ucPwmDtSet>=PER_MS_CNT)
{
ubBthFlag=0;
}
}
/*pwm 下降*/
else
{
/*pwm减少*/
ucPwmDtSet--;
if(!ucPwmDtSet)
{
ubBthFlag=1;
}
}
}
bMsFlag=0;
}
}
}
void Interrupt (void)
{
pushaf;
if (Intrq.T16)
{
/*重新设置计数值*/
stt16 usTmrCnt;
ucSystick++;
if(ucSystick>49)
{
ucSystick=0;
/*1ms 定时标记*/
bMsFlag=1;
}
/*测试,用于查看中断时间*/
if(TEST)
{
$ TEST out,low;
}
else
{
$ TEST out,High;
}
ucSysPwmDt++;
/*pwm阶数*/
if (ucSysPwmDt>PER_MS_CNT )
{
ucSysPwmDt=0;
}
/*设置占空比*/
if(ucSysPwmDt<ucPwmDtSet)
{
LED1=ON;
}
else
{
LED1=OFF;
}
Intrq.T16 = 0;
//...
}
popaf;
}