一、实现思路
原理:利用PWM信号控制LED灯的亮暗程度,实现LED灯由暗到亮又由亮到暗的渐变过程。
方法:通过定时器设置LED所接引脚的高低电平持续时间,模拟出PWM波。
二、关键问题——PWM的频率和占空比
1.设定PWM的频率:
由于人眼的视觉暂留效果,为避免视觉上灯的闪烁,PWM方波的频率至少大于30Hz。可以取PWM的周期为10ms,即频率为100Hz。
实现方式:使用定时器0的实现10ms定时。并使用定时器2控制PWM高低电平的持续时间。
2.设定占空比的步进间隔
步进时间间隔应小于人眼的视觉暂留效果,即小于30Hz,保证LED亮暗的变化能够被人眼捕捉到,我们设定步进时间的间隔为100ms(10Hz)。
实现方式:
(1)每隔100ms修改一次占空比,可通过进入10次定时器0(上文提到的)的溢出中断实现。
(2)定时器中断ISR中修改占空比:占空比从0开始,每次步进10%,递增到100%;再从100%每次递减10%到0%。
3.总结:利用两个定时器互相配合来模拟PWM波的生成,PWM波的频率和占空比均可控。
三、实现代码
注:C8051F020是增强型51单片机,与传统51相比增加了看门狗(watchdog)、交叉开关(crossbar)等外设,编程时应额外注意它们的使用。
步骤:各外设的初始化——打开定时器中断——在中断服务函数中写逻辑
#include <c8051f020.h>
#define Duty_Max 10 //10代表占空比最大为100%
#define STEP 1 //1代表每次步进10%
#define SYSCLK 2000000 //2MHz system clock
sfr16 RCAP2 = 0xCA; // Timer2 Reload Register
sfr16 TMR2 = 0xCC; // Timer2 Register
unsigned char duty = 0; //占空比的范围0~10, 代表0%~100%
sbit LED = P2^4; //开发板上P2.4连接LED
bit direction = 1; //占空比增加/减少的方向: 1代表增加; 0代表减少
void Timer0_Init(void);
void Port_Init (void);
void Wait_MS(unsigned char ms); //定时器2轮询方式实现ms级延时
void main()
{
WDTCN = 0xDE; // Disable watchdog timer
WDTCN = 0xAD;
OSCICN = 0x04; //2MHz system clock
Port_Init();
Timer0_Init();
LED = 0;
EA = 1;
while(1);
}
void Timer0_ISR(void) interrupt 1
{
static unsigned char n = 0;
TH0 = 0xB1;
TL0 = 0xE0;
n++;
LED = 1;
Wait_MS(duty);
LED = 0;
Wait_MS(10-duty);
if(n==10)
{
n = 0;
if(direction == 1)
{
duty += STEP;
if(duty == Duty_Max)
{
direction = 0;
}
}
if(direction == 0)
{
duty -= STEP;
if(duty == 0)
{
direction = 1;
}
}
}
}
void Port_Init (void)
{
XBR2 = 0x40; //enable crossbar
P2MDOUT = 0xFF;
}
void Wait_MS(unsigned char ms)
{
CKCON |= 0x20; //use SYSCLK
RCAP2 = -(SYSCLK/1000); // Timer 2 overflows at 1 kHz
TMR2 = RCAP2;
TR2 = 1; // Start Timer 2
while(ms)
{
TF2 = 0;
while(!TF2); // Wait until timer overflows
ms--; // Decrement ms
}
TR2 = 0; // Stop Timer 2
}
void Timer0_Init(void)
{
CKCON |= 0x08; //use SYSCLK
TMOD = 0x01; // 16-bit Mode Timer0
TH0 = 0xB1; // 10ms
TL0 = 0xE0;
ET0 = 1; // Timer0 interrupt enabled
TR0 = 1;
}