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

发布者:梦想启航最新更新时间:2016-12-08 来源: eefocus关键字:单片机  PWM  控制程序 手机看文章 扫描二维码
随时随地手机看文章

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

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

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


图 10-1  PWM 波形


这是一个周期是 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,与纯定时不同的是,这里我们每周期内都要重载两次定时器初值,即用两个不同的初值来控制高低电平的不同持续时间。为了使亮度的变化更加明显,程序中使用的占空比差距更大。

  1. #include 

  2. sbit PWMOUT = P0^0;

  3. sbit ADDR0 = P1^0;

  4. sbit ADDR1 = P1^1;

  5. sbit ADDR2 = P1^2;

  6. sbit ADDR3 = P1^3;

  7. sbit ENLED = P1^4;


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

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

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

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


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

  13. void ClosePWM();


  14. void main(){

  15. unsigned int i;

  16. EA = 1; //开总中断

  17. ENLED = 0; //使能独立 LED

  18. ADDR3 = 1;

  19. ADDR2 = 1;

  20. ADDR1 = 1;

  21. ADDR0 = 0;


  22. while (1){

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

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

  25. ClosePWM();

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

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

  28. ClosePWM();

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

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

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

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

  33. }

  34. }

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

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

  37. unsigned int high, low;

  38. unsigned long tmp;


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

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

  41. low = tmp - high; //计算低电平所需的计数值

  42. high = 65536 - high + 12; //计算高电平的重载值并补偿中断延时

  43. low = 65536 - low + 12;//计算低电平的重载值并补偿中断延时


  44. HighRH = (unsigned char)(high>>8); //高电平重载值拆分为高低字节

  45. HighRL = (unsigned char)high;

  46. LowRH = (unsigned char)(low>>8); //低电平重载值拆分为高低字节

  47. LowRL = (unsigned char)low;


  48. TMOD &= 0xF0; //清零 T0 的控制位

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

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

  51. TL0 = HighRL;

  52. ET0 = 1; //使能 T0 中断

  53. TR0 = 1; //启动 T0

  54. PWMOUT = 1; //输出高电平

  55. }

  56. /* 关闭 PWM */

  57. void ClosePWM(){

  58. TR0 = 0; //停止定时器

  59. ET0 = 0; //禁止中断

  60. PWMOUT = 1; //输出高电平

  61. }

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

  63. void InterruptTimer0() interrupt 1{

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

  65. TH0 = LowRH;

  66. TL0 = LowRL;

  67. PWMOUT = 0;

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

  69. TH0 = HighRH;

  70. TL0 = HighRL;

  71. PWMOUT = 1;

  72. }

  73. }

需要提醒大家的是,由于标准 51 单片机中没有专门的 PWM 模块,所以我们用定时器加中断的方式来产生 PWM,而现在有很多的单片机都会集成硬件的 PWM 模块,这种情况下需要我们做的就仅仅是计算一下周期计数值和占空比计数值然后配置到相关的 SFR 中即可,既使程序得到了简化又确保了 PWM 的输出品质(因为消除了中断延时的影响)。

大家编译下载程序后,会发现小灯从最亮到灭一共 4 个亮度等级。如果我们让亮度等级更多,并且让亮度等级连续起来,会产生一个小灯渐变的效果,与呼吸有点类似,所以我们习惯上称之为呼吸灯,程序代码如下,这个程序用了 2 个定时器 2 个中断,这是我们第一次这样用,大家可以学习一下。我们来试试这个程序,试完了大家一定要能自己把程序写出来,切记。

  1. #include 

  2. sbit PWMOUT = P0^0;

  3. sbit ADDR0 = P1^0;

  4. sbit ADDR1 = P1^1;

  5. sbit ADDR2 = P1^2;

  6. sbit ADDR3 = P1^3;

  7. sbit ENLED = P1^4;


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

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

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

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

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

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

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


  15. void ConfigTimer1(unsigned int ms);

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


  17. void main(){

  18. EA = 1; //开总中断

  19. ENLED = 0; //使能独立 LED

  20. ADDR3 = 1;

  21. ADDR2 = 1;

  22. ADDR1 = 1;

  23. ADDR0 = 0;


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

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

  26. while (1);

  27. }

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

  29. void ConfigTimer1(unsigned int ms){

  30. unsigned long tmp; //临时变量

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

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

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

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

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

  36. T1RL = (unsigned char)tmp;

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

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

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

  40. TL1 = T1RL;

  41. ET1 = 1; //使能 T1 中断

  42. TR1 = 1; //启动 T1

  43. }

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

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

  46. unsigned int high, low;

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

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

  49. low = PeriodCnt - high; //计算低电平所需的计数值

  50. high = 65536 - high + 12; //计算高电平的定时器重载值并补偿中断延时

  51. low = 65536 - low + 12; //计算低电平的定时器重载值并补偿中断延时

  52. HighRH = (unsigned char)(high>>8); //高电平重载值拆分为高低字节

  53. HighRL = (unsigned char)high;

  54. LowRH = (unsigned char)(low>>8); //低电平重载值拆分为高低字节

  55. LowRL = (unsigned char)low;


  56. TMOD &= 0xF0; //清零 T0 的控制位

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

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

  59. TL0 = HighRL;

  60. ET0 = 1; //使能 T0 中断

  61. TR0 = 1; //启动 T0

  62. PWMOUT = 1; //输出高电平

  63. }

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

  65. void AdjustDutyCycle(unsigned char dc){

  66. unsigned int high, low;

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

  68. low = PeriodCnt - high; //计算低电平所需的计数值

  69. high = 65536 - high + 12; //计算高电平的定时器重载值并补偿中断延时

  70. low = 65536 - low + 12; //计算低电平的定时器重载值并补偿中断延时


  71. HighRH = (unsigned char)(high>>8); //高电平重载值拆分为高低字节

  72. HighRL = (unsigned char)high;

  73. LowRH = (unsigned char)(low>>8); //低电平重载值拆分为高低字节

  74. LowRL = (unsigned char)low;

  75. }

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

  77. void InterruptTimer0() interrupt 1{

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

  79. TH0 = LowRH;

  80. TL0 = LowRL;

  81. PWMOUT = 0;

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

  83. TH0 = HighRH;

  84. TL0 = HighRL;

  85. PWMOUT = 1;

  86. }

  87. }

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

  89. void InterruptTimer1() interrupt 3{

  90. static bit dir = 0;

  91. static unsigned char index = 0;

  92. unsigned char code table[13] = { //占空比调整表

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

  94. };


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

  96. TL1 = T1RL;

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

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

  99. index++;

  100. if (index >= 12){

  101. dir = 1;

  102. }

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

  104. index--;

  105. if (index == 0){

  106. dir = 0;

  107. }

  108. }

  109. }

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


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

上一篇:单片机与控制实验(5)——重量测量并在LCD12864显示
下一篇:基于AT89C51单片机的温度自动监控系统

推荐阅读最新更新时间:2024-03-16 15:24

MCS-51单片机外部引脚及总线接口
51系列单片机一般采用40只引脚的双列直插式(DIP——Dual In-line Package)封装结构。 除DIP封装外, 51单片机还采用44只引脚的方形扁平(QFP——Quad Flat Package) 封装方式(4只引脚无用)。 三类 端口引脚 (共32只) 电源及晶振引脚(共4只) 控制引脚(共4只) (1)端口引脚 P0.0~P0.7(39~32脚)——P0口 P1.0~P1.7(1~8脚)——P1口 P2.0~P2.7(21~28脚)——P2口 P3.0~P3.7(10~17脚)——P3口 8只/组×4 组= 32 只引脚 P0口~P3口是单片机对外联络的重要通道 (2)电源及晶振引脚 VC
[单片机]
MCS-51<font color='red'>单片机</font>外部引脚及总线接口
几种单片机的中断函数写法
写单片机程 序,中断是免不了的。我比较喜欢用C写单片机程序,简单而且可读性高,当然程序效率没有汇编的高。目前写过51单片机跟AVR单片机的C程序,最近在看 MSP430的书。用C写不同的单片机程序其实都是大同小异,因此能对不熟悉的单片机也能很快上手写程序。不过中断函数的写法,各个编译器往往都会有些差 别。 最早写的C程序是51单片机的,用的编译器自然是大名鼎鼎的keil c了。Keil的功能还是非常强劲的,不仅能编译,还有软件仿真调试与硬件调试的功能。由于条件简陋,没用过什么仿真器,一直都是靠软件仿真调试程序的。 Keil 中的中断函数一般格式如下: void 函数名() interrupt n using n
[单片机]
基于8位微控制器控制硬盘进行HDTV码流读写
摘要:介绍用C8051F015微控制器控制硬盘进行HDTV码流存取的高清码流回放机的硬件设计和软件编程。该系统作为解码器的前端,可以实现HDTV码流的适时回放。 关键词:HDTV 码流 ATA/ATAPI-4标准 LBA模式 CPLD 随着HDTV在全球的兴起以及数字技术的日趋成熟,音/视频产品数字化、高清晰度化已成为未来家电视听产品的大趋势。高清晰度电视HDTV(High-Definition Television)采用了国际上流行的MPEG-2标准,MPEG-2规定了数字图像的音视频编解码算法以及传送的码流应该符合的语法和语义,而其编解码算法又参考了听觉心理模型和视觉心理模型。HDTV的图像格式为1920%26;#215
[应用]
DDS芯片AD9850的工作原理及其与单片机的接口
1 AD9850简介 随着数字技术的飞速发展,用数字控制方法从一个参考频率源产生多种频率的技术,即直接数字频率合成(DDS)技术异军突起。美国AD公司推出的高集成度频率合成器AD9850便是采用DDS技术的典型产品之一。 AD9850 采用先地蝗CMOS工艺,其功耗在3.3V供电时仅为155mW,扩展工业级温度范围为-40~80℃,采用28脚SSOP表面封装形式。AD9850的引脚排列如图1所示,图2为其组成框图。图2中层虚线内是一个完整的可编程DDS系统,外层虚线内包含了AD9850的主要组成部分。 AD9850 内含可编程DDS系统和高速比较器,能实现全数字编程控制的频率合成。可编程DDS系统的核心是相位累加器
[单片机]
DDS芯片AD9850的工作原理及其与<font color='red'>单片机</font>的接口
单片机脚本语言移植lua到stm32MDK
Lua简单介绍 Lua 是一个小巧的脚本语言。作者是巴西人。该语言的设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。 Lua脚本能够非常easy的被C/C++ 代码调用,也能够反过来调用C/C++的函数,这使得Lua在应用程序中能够被广泛应用。不只作为扩展脚本,也能够作为普通的配置文件,取代XML,Ini等文件格式,而且更easy理解和维护。 Lua的目标是成为一个非常easy嵌入其他语言中使用的语言。大多数程序猿也觉得它的确做到了这一点。 非常多应用程序使用LUA作为自己的嵌入式脚本语言,以此来实现可配置性、可扩展性。这当中包含魔兽世界、博德之门、愤慨的小鸟、VOCALOID3 等。 单片机使用
[单片机]
51单片机——定时/计数器
一.定时器操作步骤 定时器操作步骤 二.工作方式控制寄存器TMOD 工作方式控制寄存器TMOD TMOD的低半字节(D0,D1,D2,D3)用来控制定时器/计数器0 TMOD的高半字节(D4,D5,D6,D7)用来控制定时器/计数器1 对TMOD中的内容说明: GATE 门控制  GATE=1时,由外部中断引脚INT0、INT1来启动定时器T0、T1。    当INT0引脚为高电平时TR0置位,启动定时器T0;    当INT1引脚为高电平时TR1置位,启动定时器T1。    GATE=0时,仅由TR0,TR1置位分别启动定
[单片机]
TI详解其MCU设计策略
  TI根据什么来设计其单片机?TI的单片机产品营销工程师Jennifer Barry解释了ARM架构和MSP 430 MCU产品系列,以及片上安全的重要性。    Q:TI如何在市场中定位16位的MSP430和32位的ARM Cortex微控制器?    A: TI将16位的MSP430定位于满足超低功耗应用,以延长电池寿命和节能。   我们的FRAM系列MSP430 MCU除了超低功耗,还非常适用于实时数据记录,而ARM Cortex微控制器在性能和实时控制方面表现出色。   Cortex-R4和R5系列 MCU除了提供出色的性能、实时响应性,还能基于双核锁步为开发人员提供功能安全性应用。    Q:MCU设计主要
[单片机]
基于8051F350单片机实现温度采集监控系统的应用方案
1 引言 温度是生产过程和科学试验中普遍且重要的物理参数。在工业生产中,为了高效生产,必须对生产过程中的主要参数,如温度、压力、流量、速度等进行有效控制。其中温度控制在生产过程中占有相当大的比例。准确地测量和有效地控制温度是优质、高产、低耗和安全生产的主要条件。 2 系统概述 整个温度控制系统主要由计算机控制系统(上位机)、单片机测控系统(下位机)、温度传感器组、功率加热系统等部分组成。系统采用了模块化的设计思想,组建方式灵活,并可利用多块单片机测控系统组合的方法增加测量点,具有良好的扩展性。系统结构框图如图1所示。 温度测量采用高精度的温度传感器PT100获得物体当前温度,经过低功耗、低输入失调电压、线性好的OP07A进
[单片机]
基于8051F350<font color='red'>单片机</font>实现温度采集监控系统的应用方案
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

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

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

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