探析单片机中PWM的原理与控制程序

发布者:cwm6269310最新更新时间:2020-06-06 来源: elecfans关键字:单片机  PWM  控制程序 手机看文章 扫描二维码
随时随地手机看文章

PWM 在单片机中的应用是非常广泛的,它的基本原理很简单,但往往应用于不同场合上意义也不完全一样,这里我先把基本概念和基本原理给大家介绍一下,后边遇到用的时候起码知道是个什么东西。


PWM 是 Pulse Width Modulation 的缩写,它的中文名字是脉冲宽度调制,一种说法是它利用微处理器的数字输出来对模拟电路进行控制的一种有效的技术,其实就是使用数字信号达到一个模拟信号的效果。这是个什么概念呢?我们一步步来介绍。首先从它的名字来看,脉冲宽度调制,就是改变脉冲宽度来实现不同的效果。我们先来看三组不同的脉冲信号,如图 10-1 所示。

这是一个周期是 10ms,即频率是 100Hz 的波形,但是每个周期内,高低电平脉冲宽度各不相同,这就是 PWM 的本质。在这里大家要记住一个概念,叫做“占空比”。占空比是指高电平的时间占整个周期的比例。比如第一部分波形的占空比是 40%,第二部分波形占空比是 60%,第三部分波形占空比是 80%,这就是 PWM 的解释。那为何它能对模拟电路进行控制呢?大家想一想,我们数字电路里,只有 0 和 1 两种状态,比如我们第 2 章学会的点亮 LED 小灯那个程序,当我们写一个 LED = 0;小灯就会长亮,当我们写一个 LED = 1;小灯就会灭掉。当我们让小灯亮和灭间隔运行的时候,小灯是闪烁。如果我们把这个间隔不断的减小,减小到我们的肉眼分辨不出来,也就是 100Hz 以上的频率,这个时候小灯表现出来的现象就是既保持亮的状态,但亮度又没有 LED = 0;时的亮度高。那我们不断改变时间参数,让 LED = 0;的时间大于或者小于 LED = 1;的时间,会发现亮度都不一样,这就是模拟电路的感觉了,不再是纯粹的 0 和 1,还有亮度不断变化。大家会发现,如果我们用 100Hz 的信号,如图 10-1 所示,假如高电平熄灭小灯,低电平点亮小灯的话,第一部分波形熄灭 4ms,点亮 6ms,亮度最高,第二部分熄灭 6ms,点亮 4ms,亮度次之,第三部分熄灭 8ms,点亮 2ms,亮度最低。那么用程序验证一下我们的理论,我们用定时器T0 定时改变 P0.0 的输出来实现 PWM,与纯定时不同的是,这里我们每周期内都要重载两次定时器初值,即用两个不同的初值来控制高低电平的不同持续时间。为了使亮度的变化更加明显,程序中使用的占空比差距更大。


#include

sbitPWMOUT = P0^0;

sbitADDR0 = P1^0;

sbitADDR1 = P1^1;

sbitADDR2 = P1^2;

sbitADDR3 = P1^3;

sbitENLED = P1^4;

unsigned char HighRH = 0; //高电平重载值的高字节

unsigned char HighRL = 0; //高电平重载值的低字节

unsigned char LowRH = 0; //低电平重载值的高字节

unsigned char LowRL = 0; //低电平重载值的低字节

void ConfigPWM(unsigned int fr, unsigned char dc);

void ClosePWM();

void main(){

unsigned int i;

EA = 1; //开总中断

ENLED = 0; //使能独立 LED

ADDR3 = 1;

ADDR2 = 1;

ADDR1 = 1;

ADDR0 = 0;

while (1){

ConfigPWM(100, 10); //频率 100Hz,占空比 10%

for (i=0; i<40000; i++);

ClosePWM();

ConfigPWM(100, 40); //频率 100Hz,占空比 40%

for (i=0; i<40000; i++);

ClosePWM();

ConfigPWM(100, 90); //频率 100Hz,占空比 90%

for (i=0; i<40000; i++);

ClosePWM(); //关闭 PWM,相当于占空比 100%

for (i=0; i<40000; i++);

}

}

/* 配置并启动 PWM,fr-频率,dc-占空比 */

void ConfigPWM(unsigned int fr, unsigned char dc){

unsigned int high, low;

unsigned long tmp;

tmp = (11059200/12) / fr; //计算一个周期所需的计数值

high = (tmp*dc) / 100; //计算高电平所需的计数值

low = tmp - 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; //清零 T0 的控制位

TMOD |= 0x01; //配置 T0 为模式 1

TH0 = HighRH; //加载 T0 重载值

TL0 = HighRL;

ET0 = 1; //使能 T0 中断

TR0 = 1; //启动 T0

PWMOUT = 1; //输出高电平

}

/* 关闭 PWM */

void ClosePWM(){

TR0 = 0; //停止定时器

ET0 = 0; //禁止中断

PWMOUT = 1; //输出高电平

}

/* T0 中断服务函数,产生 PWM 输出 */

void InterruptTimer0() interrupt 1{

if (PWMOUT == 1){ //当前输出为高电平时,装载低电平值并输出低电平

TH0 = LowRH;

TL0 = LowRL;

PWMOUT = 0;

}else{ //当前输出为低电平时,装载高电平值并输出高电平

TH0 = HighRH;

TL0 = HighRL;

PWMOUT = 1;

}

}


需要提醒大家的是,由于标准 51 单片机中没有专门的 PWM 模块,所以我们用定时器加中断的方式来产生 PWM,而现在有很多的单片机都会集成硬件的 PWM 模块,这种情况下需要我们做的就仅仅是计算一下周期计数值和占空比计数值然后配置到相关的 SFR 中即可,既使程序得到了简化又确保了 PWM 的输出品质(因为消除了中断延时的影响)。大家编译下载程序后,会发现小灯从最亮到灭一共 4 个亮度等级。如果我们让亮度等级更多,并且让亮度等级连续起来,会产生一个小灯渐变的效果,与呼吸有点类似,所以我们习惯上称之为呼吸灯,程序代码如下,这个程序用了 2 个定时器 2 个中断,这是我们第一次这样用,大家可以学习一下。我们来试试这个程序,试完了大家一定要能自己把程序写出来,切记。


#include

sbitPWMOUT = P0^0;

sbitADDR0 = P1^0;

sbitADDR1 = P1^1;

sbitADDR2 = P1^2;

sbitADDR3 = P1^3;

sbitENLED = P1^4;

unsigned long PeriodCnt = 0; //PWM 周期计数值

unsigned char HighRH = 0; //高电平重载值的高字节

unsigned char HighRL = 0; //高电平重载值的低字节

unsigned char LowRH = 0; //低电平重载值的高字节

unsigned char LowRL = 0; //低电平重载值的低字节

unsigned char T1RH = 0; //T1 重载值的高字节

unsigned char T1RL = 0; //T1 重载值的低字节

void ConfigTimer1(unsigned int ms);

void ConfigPWM(unsigned int fr, unsigned char dc);

void main(){

EA = 1; //开总中断

ENLED = 0; //使能独立 LED

ADDR3 = 1;

ADDR2 = 1;

ADDR1 = 1;

ADDR0 = 0;

ConfigPWM(100, 10); //配置并启动 PWM

ConfigTimer1(50); //用 T1 定时调整占空比

while (1);

}

/* 配置并启动 T1,ms-定时时间 */

void ConfigTimer1(unsigned int ms){

unsigned long tmp; //临时变量

tmp = 11059200 / 12; //定时器计数频率

tmp = (tmp * ms) / 1000; //计算所需的计数值

tmp = 65536 - tmp; //计算定时器重载值

tmp = tmp + 12; //补偿中断响应延时造成的误差

T1RH = (unsigned char)(tmp>>8); //定时器重载值拆分为高低字节

T1RL = (unsigned char)tmp;

TMOD &= 0x0F; //清零 T1 的控制位

TMOD |= 0x10; //配置 T1 为模式 1

TH1 = T1RH; //加载 T1 重载值

TL1 = T1RL;

ET1 = 1; //使能 T1 中断

TR1 = 1; //启动 T1

}

/* 配置并启动 PWM,fr-频率,dc-占空比 */

void ConfigPWM(unsigned int fr, unsigned char dc){

unsigned int high, low;

PeriodCnt = (11059200/12) / fr; //计算一个周期所需的计数值

high = (PeriodCnt*dc) / 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; //清零 T0 的控制位

TMOD |= 0x01; //配置 T0 为模式 1

TH0 = HighRH; //加载 T0 重载值

TL0 = HighRL;

ET0 = 1; //使能 T0 中断

TR0 = 1; //启动 T0

PWMOUT = 1; //输出高电平

}

/* 占空比调整函数,频率不变只调整占空比 */

void AdjustDutyCycle(unsigned char dc){

unsigned int high, low;

high = (PeriodCnt*dc) / 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;

}

/* T0 中断服务函数,产生 PWM 输出 */

void InterruptTimer0() interrupt 1{

if (PWMOUT == 1){ //当前输出为高电平时,装载低电平值并输出低电平

TH0 = LowRH;

TL0 = LowRL;

PWMOUT = 0;

}else{ //当前输出为低电平时,装载高电平值并输出高电平

TH0 = HighRH;

TL0 = HighRL;

PWMOUT = 1;

}

}

/* T1 中断服务函数,定时动态调整占空比 */

void InterruptTimer1() interrupt 3{

static bitdir = 0;

static unsigned char index = 0;

unsigned char codetable[13] = { //占空比调整表

5, 18, 30, 41, 51, 60, 68, 75, 81, 86, 90, 93, 95

};

TH1 = T1RH; //重新加载 T1 重载值

TL1 = T1RL;

AdjustDutyCycle(table[index]); //调整 PWM 的占空比

if (dir == 0){ //逐步增大占空比

index++;

if (index >= 12){

dir = 1;

}

}else{ //逐步减小占空比

index--;

if (index == 0){

dir = 0;

}

}

}

关键字:单片机  PWM  控制程序 引用地址:探析单片机中PWM的原理与控制程序

上一篇:探析上拉电阻和下拉电阻的用处和区别
下一篇:STM8读取AD值偶尔跳变出错的问题

推荐阅读最新更新时间:2024-11-05 22:08

AVR单片机ATMEAG16L定时和计数器的应用方法解析
ATMEAG16L有两个8位定时/计数器(T/CO、T/C2)和一个16位定时/计数器(T/C1)。每一个计数器都支持PWM(脉冲宽度调制)输出功能。PWM输出在电机控制、开关电源、信号发生等领域有着广泛的应用。 ATMEAG16L的定时/计数器时钟是可以选择的。它的时钟部分包括预分频器和一个多路选择器。预分频器可被认为是一个有多级输出的分频器。ATMEAG16L用一个10位的计数器把输入时钟分为4种可选择的分频输出。多路选择器可设置使用某一个分频输出,或者不使用分频输出和使用外部引脚输入时钟,下图为预分频器的基本结构。 ATMEAG16L定时/计数器的时钟选择 1.使用系统时钟这种情况下使用系统时钟作为预分频器的输入,
[单片机]
AVR<font color='red'>单片机</font>ATMEAG16L定时和计数器的应用方法解析
教你在单片机上做插值算法
第一步:学你所学,不懂也得懂,最枯燥的数学公式来了 在数值分析中,拉格朗日插值法是以法国十八世纪数学家约瑟夫·拉格朗日命名的一种多项式插值方法。许多实际问题中都用函数来表示某种内在联系或规律,而不少函数都只能通过实验和观测来了解。 如对实践中的某个物理量进行观测,在若干个不同的地方得到相应的观测值,拉格朗日插值法可以找到一个多项式,其恰好在各个观测的点取到观测到的值。这样的多项式称为拉格朗日(插值)多项式。 数学上来说,拉格朗日插值法可以给出一个恰好穿过二维平面上若干个已知点的多项式函数。拉格朗日插值法最早被英国数学家爱德华·华林于1779年发现,不久后(1783年)由莱昂哈德·欧拉再次发现。1795年,拉格朗日在其著作《
[单片机]
教你在<font color='red'>单片机</font>上做插值算法
单片机中断源系统的设计
在AT89S52单片机中,只有两个外部中断请求输入端INT0和INT1。而实际应用系统中往往会出现两个以上的外部中断源,因此必须对外中断源进行扩展。其方法主要有:用定时器/计数器T0、T1扩展;采用中断和查询相结合的方法扩展;用串行口的中断扩展;用优先权编码器扩展等方法。这里重点介绍前两种方法。 一、用定时器/计数器作为中断源 AT89S52单片机的两个定时器/计数器T0、T1可工作在计数方式,计数初值一般设定为满量程(即定时器的最大技术值),则它们的计数输入端P3.4或P3.5引脚上发生负跳变时,T0或T1计数器就加l,产生溢出中断。利用此特性,可以把P3.4、P3.5作为外部中断请求输入线,而计数器的溢出中断作为外部中断请求
[单片机]
<font color='red'>单片机</font>中断源系统的设计
步进电机的单片机控制
本设计采用凌阳16 位单片机SPCE061A对步进电机进行控制,通过IO口输出的具有时序的方波作为步进电机的控制信号,信号经过芯片L298N驱动步进电机;同时,用4X4的键盘来对电机的状态进行控制,并用数码管显示电机的转速,采用74LS164作为4位单个数码管的显示驱动,从单片机输入信号;利用凌阳单片机的语音功能播报电机的转速。 摘要: 本设计采用凌阳16 位单片机SPCE061A对步进电机进行控制,通过IO口输出的具有时序的方波作为步进电机的控制信号,信号经过芯片L298N驱动步进电机;同时,用 4X4的键盘来对电机的状态进行控制,并用数码管显示电机的转速,采用74LS164作为4位单个数码管的显示驱动,从单片机输入信号;利用
[单片机]
步进电机的<font color='red'>单片机</font>控制
PIC8位单片机汇编语言常用指令的识读
各大类单片机的指令系统是没有通用性的,它是由单片机生产厂家规定的,所以用户必须遵循厂家规定的标准,才能达到应用单片机的目的。   PIC 8位单片机共有三个级别,有相对应的指令集。基本级PIC系列芯片共有指令33条,每条指令是12位字长;中级PIC系列芯片共有指令35条,每条指令是14位字长;高级PIC系列芯片共有指令58条,每条指令是16位字长。其指令向下兼容。   在这里笔者介绍PIC 8位单片机汇编语言指令的组成及指令中符号的功能,以供初学者阅读相关书籍和资料时快速入门。   一、 PIC单片机 汇编语言指令格式   PIC系列微控制器汇编语言指令与MCS-51系列单片机汇编语言一样,每条汇编语言指令由4个部分组成,其书写格式
[单片机]
飞思卡尔单片机DZ60---实时中断
//RTI实时中断实验 //RTC模块包括一个状态和控制寄存器、一个8位计数寄存器和一个8位模数寄存器 //实时中断功能用来产生周期性中断。RTI有三个可选时钟源:LPO 1KHZ内部振荡器,32KHZ内部时钟,以及ERCLK外部时钟 //8位比较器实时比较计数器RTCCNT与TRCMOD值,相等则产生中断 #include hidef.h /* for EnableInterrupts macro */ #include derivative.h /* include peripheral declarations */ #define LEDCPU PTDD_PTDD0 #define LEDCPU_dir PTDDD
[单片机]
外设一个一个学_PWM
PWM脉冲宽度调制(PWM)! 一般用途:电机、机器人。系统时钟(当然一般是RTC)、蜂鸣器、手机屏幕明暗调节: 1、书写流程。 根据芯片手册、 关于使用PWM驱动蜂鸣器的步骤。 ①:设置PWM的模式为PWMTOUT1 GPDCON 0XE0300080 在4~7位 写入 0x2 ②:PWM_ON PWM_OFF /*作为控制命令参数*/ ③:设置预分频函数:SET_PRE SET_CNT TCNTM=2TCMPB 占空比 ④:关于寄存器的修改 TCFG0 0XEA000000 TCFG1 4~7 TCON 0XEA000008 8~11 0x2 0010 0x9 1001 驱动如下: #if
[单片机]
N32A455系列车规MCU产品&N32S032车规安全芯片 | 国民技术确认申报2024金辑奖
申请技术丨N32S032车规安全芯片 申报领域丨车规级芯片 独特优势: N32S032车规安全芯片采用创新SOC安全架构设计技术、低功耗安全技术,支持安全启动、安全数据存储、网络安全认证、密钥安全保护及存储、代码保护、程序安全OTA等,创新推出多用户分区控制物理隔离机制和解决方案,具备较强的技术引领性。产品在安全和保护、超低功耗、高集成等技术方面具有创新和领先,提供QFN48/QFN32/QFN20/SOP8等多种封装,可满足不同应用场景需求。 应用场景: N32S032车规安全芯片适用于车载设备、车联网、物联网、网络身份认证、金融支付、智能家居、消费电子等广泛领域。 未来前景: N32S032车
[汽车电子]
N32A455系列车规<font color='red'>MCU</font>产品&N32S032车规安全芯片 | 国民技术确认申报2024金辑奖
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved