通过IO口模拟产生PWM,并通过占空比的控制Led灯的亮度。定时器0用来产生PWM,定时器1用来调整占空比的数值。
代码已经在KST-51 v1.3.2开发板验证通过。
效果展示:
#include <reg52.h>
sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
unsigned long periodCnt = 0;
unsigned char highRH = 0; //高电平重载值的高字节
unsigned char highRL = 0; //高电平重载值的低字节
unsigned char lowRH = 0; //低电平重载值的高字节
unsigned char lowRL = 0; //低电平重载值的低字节
unsigned char T1RH = 0;
unsigned char T1RL = 0;
void ConfigPWM(unsigned int freq, unsigned char dutyCycle);
void ConfigTimer1(unsigned int ms);
void AdjustDutyCycle(unsigned char dutyCycle);
void main()
{
EA = 1; //开总中断
ENLED = 0;
ADDR3 = 1;
ADDR2 = 1;
ADDR1 = 1;
ADDR0 = 0;
ConfigPWM(100, 10); //配置并启动PWM
ConfigTimer1(100); //用Timer1定时调整占空比
while (1);
}
void ConfigPWM(unsigned int freq, unsigned char dutyCycle)
{
unsigned int high, low;
periodCnt = 11059200 / 12 / freq; //计算一个周期所需的计数值
high = (periodCnt * dutyCycle) / 100; //计算高电平所需的计数值
low = periodCnt - high; //计算低电平所需的计数值
high = 65536 - high + 12; //计算高电平的定时器重载值并补偿中断延时
low = 65536 - low + 12; //计算低电平的定时器重载值并补偿中断延时
highRH = (unsigned char)(high >> 8); //高电平重载值拆分为高低字节
highRL = (unsigned char)high;
lowRH = (unsigned char)(low >> 8); //低电平重载值拆分为高低字节
lowRL = (unsigned char)low;
TMOD &= 0xF0; //清零Timer0的控制位
TMOD |= 0x01; //配置Timer0为模式1
TH0 = highRH; //加载Timer0重载值
TL0 = highRL;
ET0 = 1; //使能Timer0中断
TR0 = 1; //启动Timer0
P0 = 0xFF; //P0口全部输出高电平
}
void ConfigTimer1(unsigned int ms)
{
unsigned long temp;
temp = 11059200 / 12; //定时器计数频率
temp = (temp * ms) / 1000; //计算所需的计数值
temp = 65536 - temp + 12; //计算定时器重载值
T1RH = (unsigned char)(temp >> 8); //定时器重载值拆分为高低字节
T1RL = (unsigned char)temp;
TMOD &= 0x0F; //清零Timer1的控制位
TMOD |= 0x10; //配置Timer1为模式1
TH1 = T1RH; //加载Timer1重载值
TL1 = T1RL;
ET1 = 1; //使能Timer1中断
TR1 = 1; //启动Timer1
}
void AdjustDutyCycle(unsigned char dutyCycle)
{
unsigned int high, low;
high = (periodCnt * dutyCycle) / 100;
low = periodCnt - high;
high = 65536 - high + 12;
low = 65536 - low + 12;
highRH = (unsigned char)(high >> 8);
highRL = (unsigned char)high;
lowRH = (unsigned char)(low >> 8);
lowRL = (unsigned char)low;
}
void InterruptTimer0() interrupt 1
{
if (P0 == 0xFF) //当前P0口输出为高电平时,装载低电平值并输出低电平
{
TH0 = lowRH;
TL0 = lowRL;
P0 = 0x00;
}
else if (P0 == 0x00) //当前P0口输出为低电平时,装载高电平值并输出高电平
{
TH0 = highRH;
TL0 = highRL;
P0 = 0xFF;
}
}
void InterruptTimer1() interrupt 3
{
static bit dir = 1;
static signed char index = 0;
unsigned char code table[19] = {
5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95
};
TH1 = T1RH;
TL1 = T1RL;
AdjustDutyCycle(table[index]);
if (dir)
{
index++;
if (index >= 18)
{
dir = 0;
}
}
else
{
index--;
if (index <= 0)
{
dir = 1;
}
}
}