单片机学习之PWM原理与控制程序

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

PWM 是 Pulse Width Modulation 的缩写,它的中文名字是脉冲宽度调制,一种说法是它利用微处理器的数字输出来对模拟电路进行控制的一种有效的技术,其实就是使用数字信号达到一个模拟信号的效果。这是个什么概念呢?我们一步步来介绍。

首先从它的名字来看,脉冲宽度调制,就是改变脉冲宽度来实现不同的效果。我们先来看三组不同的脉冲信号,如图 所示。

这是一个周期是 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 <reg52.h>

sbit PWMOUT = P0^0;

sbit ADDR0 = P1^0;

sbit ADDR1 = P1^1;

sbit ADDR2 = P1^2;

sbit ADDR3 = P1^3;

sbit ENLED = 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 <reg52.h>

sbit PWMOUT = P0^0;

sbit ADDR0 = P1^0;

sbit ADDR1 = P1^1;

sbit ADDR2 = P1^2;

sbit ADDR3 = P1^3;

sbit ENLED = 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 bit dir = 0;

static unsigned char index = 0;

unsigned char code table[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;

}

}

}

呼吸灯效果做出来后,利用这个基本原理,其它各种效果的灯光闪烁都应该可以做出来,大家看到的 KTV 里边那绚丽的灯光闪烁,其实就是采用的 PWM 技术控制的。

时间: 2024-10-10 06:33:07

单片机学习之PWM原理与控制程序的相关文章

PIC单片机之PWM PWM原理

1,什么是PWM      什么是PWM,专业点说脉宽调制,通俗点说 就是不断的输出高低电平. 比如 单片机 40ms的时间 输出高电平然后60ms的时间 输出低电平接着一直不断周期性的交替输出高低电平 这就是典型的PWM波.高电平输出的时间40ms就是传说中的脉宽,40ms+60ms=100ms 便是PWM的周期.假定我们把周期定为100ms.我们可以根据我们的需要 把高电平的时间加大或者减小,比如 高电平输出时间加大为80ms.那么低电平的时间就变为20ms. 也可以 高电平输出时间减小为2

单片机学习笔记1

一.单片机的概念.应用. 单片机也叫MCU(微控制器).是一种集成电路芯片,是采用超大规模集成电路技术把具有数据处理能力的中央处理器CPU.随机存储器RAM.只读存储器ROM.多种I/O口和中断系统.定时器/计数器等功能(可能还包括显示驱动电路.脉宽调制电路.模拟多路转换器.A/D转换器等电路)集成到一块硅片上构成的一个小而完善的微型计算机系统.单片机的使用领域已十分广泛,如智能仪表.实时工控.通讯设备.导航系统.家用电器等. 基本功能:(以51单片机为例) 1.8位数据总线,16位地址总线的C

[51单片机学习笔记TWO]----蜂鸣器

蜂鸣器音乐播放实验 首先应该了解一下蜂鸣器音乐播放的原理,在这里我只讲一下电磁式蜂鸣器驱动原理(还有一种是压电式蜂鸣器): 电磁式蜂鸣器驱动原理: 蜂鸣器发声原理是电流通过电磁线圈,使电磁圈产生磁场来驱动振动膜发声的.因此需要一定的电流才能驱动它,而单片机I/O引脚输出的电压较小.单片机输出的TTLK电平基本驱动不了蜂鸣器,因需要增加一个放大电路.这里用三极管作为放大电路.下面是原理图: 我这里的J8端是跟芯片的P1^5端口相连,当P1^5输出高电平时,三极管截止,蜂鸣器不发声,反之,输出低电平

5年单片机学习总结-ED China【转】

人的一生是一个不断学习不断成长的过程转眼间,研究生的学习生涯结束了,走上新的工作岗位回头看看,发现自己不知不觉已经走过了五年的单片机学习之旅 在2003年最初接触单片机的时候,在我心中觉得51就是单片机,单片机就是51,根本不知道还有其他单片机的存在那时候老师只教会了我们汇编语言,根本不知道用C语言也可以进行单片机开发幸运的是,我加入了单片机兴趣小组,在老师的指导下,做了一系列实验,有基于DS18B20的温度采集系统有基于164的移位寄存器的灯光控制系统有步进电机和直流电机的控制系统这时候我才发

51单片机学习笔记【八】——步进电机

一.步进电机基础 1.定义 步进电机是将电脉冲转化为角位移的执行机构,主要使用永磁性步进电机,本实验使用的步进电机为四线双性步进电机: 步进电机的相数指产生不同极性N,S磁场的激磁线圈对数,常用m表示: 步进电机的拍数指完成一个磁场周期性变化所需脉冲数,以四线二相电机为例: 单向四拍:A/-B-A-B/ 双向四拍:A/B-AB-AB/-A/B/ 单&双八拍:A/-A/B-B-AB-C-A-AB/-B/-A/B/ 步进角指定子没改变一次通电状态,转子转过的角度,与磁极数,定子相数,通电方式节拍成负

本科项目——基于单片机的直流电机PWM控制系统

报告下载地址:http://download.csdn.net/detail/cxp2205455256/8192199 程序下载地址:http://download.csdn.net/detail/cxp2205455256/8192191 基于单片机的直流电机 PWM 控制系统.PWM 控制提高了调速 范围,提高了调速精度,改善了快速性能.功率和功率因数.系统在设计中被控对象采用 5V 的直流电,以 MCS-51 单片机为控制核心,采用 LCD12864 液晶作为显示元件,进行软硬件的设计.

义隆单片机学习笔记之(四) 编程及烧录

工具说明: 1.UIDE,编程环境 2.UWriter:烧录软件 下载地址: (见官网) 一 UIDE  1.1 创建工程: 1.2 选择型号: 1.3 编程及编译 二 UWriter 2.1 安装 (Serial number在购买烧录器的时候,从代理经销商处获得!) 2.2 选择目标芯片  2.3 配置选择 (注:由于是OTP,很多功能都是在配置的时候指定,所以这里非常重要.晶振.时钟的选择,延时程序要与之对应: 是否需要保护等,根据自己的需要而定) 编译之后生成的.map文件,详细的描述了

[51单片机学习笔记ONE]-----LED灯的多种使用方法

一.交替闪烁8个LED灯,时间间隔为1s 1 /****************************************************** 2 实验名称: 交替闪烁8个LED灯,时间间隔1s 3 实验时间: 2014年12月2日 4 ******************************************************/ 5 6 #include <reg51.h> 7 8 void delay(unsigned char a); 9 10 void m

义隆单片机学习笔记之(三) 应用例程

常用寄存器: 0x01 (R1) 计时器 0x02 (R2)程序计数器 PC 0x03 (R3)状态寄存器 0x04 (R4)间址寄存器 0x05 (R5)IO PORT 5 0x06 (R6)IO PORT 6 ----- (IOC5)P5的输入输出配置 ----- (IOC6)P6的输入输出配置 0x0f (ISR,读)中断信号指示寄存器(第三位有效,分别对应于3个中断源) 0x0f (IOCF,写)中断屏蔽标志 0x0E (IOCE)(IO60作为中断输入的配置与看门狗的开关在一个寄存器中