目录
序言
本文主要讲解pwm的具体(基础)使用,并未诠释pwm的核心概念,并使用更快的定时器初始值计算方法,讲解了常见计算精度上的一些小处理。
PWM
pwm脉冲示例图
PWM函数
代码
要输入两个变量分别为:周期和占控比给pwm函数
void pwm_config( unsigned int T, unsigned char Ratio)
{
unsigned int HighVoltTime,LowVoltTime;
long int temp;
//计算定时器初始值
temp=(long int)T*Ratio;
HighVoltTime=temp/100;
LowVoltTime=T-HighVoltTime;
TH0_LowVolt=(65536-LowVoltTime)>>8;
TL0_LowVolt=65536-LowVoltTime;
TH0_HighVolt=(65536-HighVoltTime)>>8;
TL0_HighVolt=65536-HighVoltTime;
}
代码细节部分解读
首先是进行定时器初始值的计算,周期×占控比得到高八位的定时器的值,再用周期减去高八位持续的时间即可得到低八位的定时器的数值。
小细节操作
-
高八位的配置:
原始
TH0=(65535-LowVoltTime)/256
升级
TH0_LowVolt=(65536-LowVoltTime)>>8
在计算机的加减乘除运算之中,除法运算最慢,使用位操作符来替换除法,加快运算效率,减少计算时间
-
低八位的配置:
原始
TL0=(65535-HighVoltTime)%256
升级
TL0_LowVolt=65536-LowVoltTime
这是因为c语言在运算超出数据类型的范围时,默认保留低八位,利用这个特性可以省去一步取余计算。基于数据存储形式优化计算速度
-
初始值计算
周期还是使用的微秒作为单位,在计算过程之中会面临数值过大的问题
倘若使用以下操作进行计算,会出现精度损失的问题
HighVoltTime=T/100*Ratio
所以选择调换计算顺序,并使用更大的数据类型来解决精度损失的问题(要注意数据类型转换)
long int temp;
temp=(long int)T*Ratio;
HighVoltTime=temp/100;
-
PWM函数内完成初始值的计算
目的是不在中断之中运算,加快运算速度,减少cpu损耗,中断之中仅进行赋值操作
应用
呼吸灯调的是占控比,体现灯亮度的变换
蜂鸣器调的是周期,通过调整周期使得频率不一样,发出的声音也不一样(这里用的是无源蜂鸣器,此蜂鸣器一般配合按键一起食用)
呼吸灯
小细节操作
- 占控比为10%和90%的灯都是亮的,按原理上来说,占控比低的更亮(在我的程序之中,占控比越高,高电平占比越高),但实际上人眼可能感受不出来,因为分辨率不够
代码
#include<reg51.h>
sbit led=P2^0;
unsigned int code beattime[7]={261,293,329,349,393,440,493};//音律的频率
unsigned int code SongCode[7]={3822,3406,3034,2864,2551,2272,2025}; //音律的周期
unsigned char code R[8]={10,30,50,70,90,70,50,30};//呼吸灯占控比
unsigned char TH0_LowVolt;
unsigned char TL0_LowVolt;
unsigned char TH0_HighVolt;
unsigned char TL0_HighVolt;
void pwm_config( unsigned int T, unsigned char Ratio)
{
unsigned int HighVoltTime,LowVoltTime;
long int temp;
//计算定时器的初始值
temp=(long int)T*Ratio;
HighVoltTime=temp/100;
LowVoltTime=T-HighVoltTime;
//完成赋值操作
TH0_LowVolt=(65536-LowVoltTime)>>8;
TL0_LowVolt=65536-LowVoltTime;
TH0_HighVolt=(65536-HighVoltTime)>>8;
TL0_HighVolt=65536-HighVoltTime;
}
void timer_config()
{
EA=1;
ET0=1;
TR0=1;
TMOD=0x01;
TH0=(65536-8000)>>8;
TL0=65536-8000;
}
void main()
{
timer_config();
pwm_config(10000,R[]);
while(1)
{
}
}
void timer_5ms() interrupt 1
{
static unsigned int count=0,j=0;
count++;
if(count==200) //1 sec
{
count=0;
j++;
j=j%7;
pwm_config(10000,R[]);
}
//通过前一个状态决定不同的定时器初始值的配置
if(led)
{
TH0=TH0_LowVolt;
TL0=TL0_LowVolt;
}
else
{
TH0=TH0_HighVolt;
TL0=TL0_HighVolt;
}
P2=~P2;
}
蜂鸣器奏乐
小细节操作
- 倘若要蜂鸣器响1s,大概只要设置0.8秒就可以了,考虑到声音的传播,并且使得两个节拍之间的衔接更加柔和,也不容易出现两个节拍重叠发声的情况(具体时间得根据不同场景进行调整)
- 蜂鸣器的音质差,因为输入给蜂鸣器的是方波信号,一般声音是很复杂的波形,单单使用一个I/O口无法实现复杂波形的输入(有点复杂)
- 蜂鸣器是否好听取决于各音调“比例”恰不恰当,只要比例合适,不管是“哪个人唱”,别人都会觉得好听,和谐
- 通过不同的声音频率,来计算出对应声音要持续的周期长度,进而发出不同的声音
代码
#include<reg51.h>
sbit led=P2^0;
unsigned int code beattime[7]={261,293,329,349,393,440,493};//音律的频率
unsigned int code SongCode[7]={3822,3406,3034,2864,2551,2272,2025}; //音律的周期
unsigned char code R[8]={10,30,50,70,90,70,50,30};//呼吸灯占控比
unsigned char TH0_LowVolt;
unsigned char TL0_LowVolt;
unsigned char TH0_HighVolt;
unsigned char TL0_HighVolt;
void pwm_config( unsigned int T, unsigned char Ratio)
{
unsigned int HighVoltTime,LowVoltTime;
long int temp;
//计算定时器的初始值
temp=(long int)T*Ratio;
HighVoltTime=temp/100;
LowVoltTime=T-HighVoltTime;
//完成赋值操作
TH0_LowVolt=(65536-LowVoltTime)>>8;
TL0_LowVolt=65536-LowVoltTime;
TH0_HighVolt=(65536-HighVoltTime)>>8;
TL0_HighVolt=65536-HighVoltTime;
}
void timer_config()
{
EA=1;
ET0=1;
TR0=1;
TMOD=0x01;
TH0=(65536-8000)>>8;
TL0=65536-8000;
}
void main()
{
timer_config();
pwm_config(SongCode[0],50);
while(1)
{
}
}
void timer_5ms() interrupt 1
{
static unsigned int count=0,j=0;
count++;
if(count==beattime[j]) //0.5 sec
{
count=0;
j++;
j=j%7;
pwm_config(SongCode[j],50);
}
//通过前一个状态决定不同的定时器初始值的配置
if(led)
{
TH0=TH0_LowVolt;
TL0=TL0_LowVolt;
}
else
{
TH0=TH0_HighVolt;
TL0=TL0_HighVolt;
}
P2=~P2;
}
总结
pwm的应用有很多,更多的应用是在于电机和舵机的控制
pwm的理解:
PWM就是在合适的信号频率下,通过一个周期里改变占空比的方式来改变输出的有效电压