PWM在高频情况下,一个很好的用处就是通过控制占空比来控制输出的功率,比如控制风扇转速、LED灯的亮度等。这次就利用PWM的中断功能,动态改变脉冲的占空比,来实现呼吸灯的效果。

一、实现思路

PWM可以选择让计数器在周期结束产生中断(在周期中央对齐时,可能选择在周期中央也产生中断),并且可以在运行的时候动态地调整占空比、周期、极性等属性。所以可以在中断处理函数中动态地改变占空比以改变LED灯的亮度。
这次也将使用通道0和引脚PA0。

二、PWM设置

这里需要用到较高频率的时钟,所以选择使用主时钟经32分频后的时钟(12.5 kHz)。计数器周期为400,即输出脉冲频率为125000/400 = 312.5 Hz。同时需要使能相应的中断。
PWM的主要配置代码如下:

  1. #define PERIOD_VALUE 400
  2.  
  3. /* 时钟选择 */
  4. PWM->PWM_CH_NUM[0].PWM_CMR = PWM_CMR_CPRE_MCK_DIV_32;
  5. /* 启用中断 */
  6. PWM->PWM_IER1 = PWM_IER1_CHID0;
  7. /* 周期及占空比 */
  8. PWM->PWM_CH_NUM[0].PWM_CPRD= PWM_CPRD_CPRD(PERIOD_VALUE);
  9. PWM->PWM_CH_NUM[0].PWM_CDTY = PWM_CDTY_CDTY(0);
  10. /* 使能中断 */
  11. NVIC_ClearPendingIRQ(PWM_IRQn);
  12. NVIC_SetPriority(PWM_IRQn, 0);
  13. NVIC_EnableIRQ(PWM_IRQn);

三、PWM中断处理

在每个周期结束后,会产生一个中断。然后在中断处理函数中,改变占空比。需要注意的是,在PWM使能时,需要通过写入PWM占空比修改寄存器(PWM_CDTYUPD)来改变占空比。默认情况下,该修改在下一个周期生效。 
为得到更好的效果,可以在两次呼吸之间设置一断间隔。

注意,需要通过读取PWM_ISR1来拉低产生的中断。中断处理函数在后面的完整代码中贴出。

附 完整代码

  1. #include <sam.h>
  2.  
  3. #define PERIOD_VALUE 400
  4. #define BREATH_INTERVAL_PERIOD 200 /* 两次呼吸间隔的周期 */
  5.  
  6. void ConfigPWM(void)
  7. {
  8. /* PMC 启用
  9. * PWM的ID大于31,需要在PMC_PCER1中启用
  10. */
  11. PMC->PMC_PCER1 = 1 << (ID_PWM - 32);
  12.  
  13. /* 禁用通道0,以进行配置 */
  14. PWM->PWM_DIS = PWM_DIS_CHID0;
  15.  
  16. /* 配置通道0 */
  17. PWM->PWM_CH_NUM[0].PWM_CMR =
  18. PWM_CMR_CPRE_MCK_DIV_32 /* 计数器时钟选择为CLKA */
  19. ; /* 周期左对齐,先输出低电平,不使用死区发生器 */
  20. /* 启用中断 */
  21. PWM->PWM_IER1 = PWM_IER1_CHID0;
  22.  
  23. PWM->PWM_CH_NUM[0].PWM_CPRD = PWM_CPRD_CPRD(PERIOD_VALUE); /* 周期 */
  24. PWM->PWM_CH_NUM[0].PWM_CDTY = PWM_CDTY_CDTY(0); /* 占空比,准确来说是阀值 */
  25.  
  26. /* 使能中断 */
  27. NVIC_ClearPendingIRQ(PWM_IRQn);
  28. NVIC_SetPriority(PWM_IRQn, 0);
  29. NVIC_EnableIRQ(PWM_IRQn);
  30.  
  31. /* 使能 PWM */
  32. PWM->PWM_ENA = PWM_ENA_CHID0;
  33. }
  34.  
  35. /* PWM 中断处理函数 */
  36. void PWM_Handler(void)
  37. {
  38. static uint32_t ul_duty = 0; /* PWM 占空比*/
  39. static uint8_t fade_in = 1; /* LED 淡入标志 */
  40. static uint8_t dark_period = 0; /* LED 完全暗下来的周期 */
  41.  
  42. /* 读取PWM_ISR1,同时可以拉低中断 */
  43. uint32_t events = PWM->PWM_ISR1;
  44.  
  45. /* 先确定是否是指定的中断 */
  46. if ((events & PWM_ISR1_CHID0) != 0)
  47. {
  48. if (dark_period != 0)
  49. {
  50. dark_period--;
  51. return;
  52. }
  53.  
  54. /* 淡入 */
  55. if (fade_in)
  56. {
  57. ul_duty++;
  58. if (ul_duty == PERIOD_VALUE)
  59. {
  60. fade_in = 0;
  61. }
  62. }
  63. else
  64. {
  65. /* 淡出 */
  66. ul_duty--;
  67. if (ul_duty == 0)
  68. {
  69. fade_in = 1;
  70. /* LED暗下来一定的周期再淡入 */
  71. dark_period = BREATH_INTERVAL_PERIOD;
  72. }
  73. }
  74.  
  75. /* 设置新的占空比 */
  76. PWM->PWM_CH_NUM[0].PWM_CDTYUPD = PWM_CDTY_CDTY(ul_duty);
  77. }
  78. }
  79.  
  80. void ConfigPIO(void)
  81. {
  82. /* 引脚由外设控制 */
  83. PIOA->PIO_PDR = PIO_PA0;
  84. /* 选择外设 */
  85. /* PIOA选择外设A(将影响PA所有引脚) */
  86. PIOA->PIO_ABCDSR[0] = 0;
  87. PIOA->PIO_ABCDSR[1] = 0;
  88. }
  89.  
  90. int main(void)
  91. {
  92. /* Disable WDT */
  93. WDT->WDT_MR = WDT_MR_WDDIS;
  94.  
  95. ConfigPWM();
  96. ConfigPIO();
  97.  
  98. while (1) {
  99. }
  100. return 0;
  101. }

SAM4E单片机之旅——5、LED呼吸和PWM的更多相关文章

  1. SAM4E单片机之旅——3、LED闪烁之定时器中断

    让一个LED灯闪烁不过瘾,我们应该让这块开发板完成一点更高难度的任务:比如让两个LED灯闪烁. …… 当然了,以我们的现在使用的空循环技术,还是可以实现这点的.但是这样显得略为低端.所以我们使用一个高 ...

  2. SAM4E单片机之旅——2、LED闪烁之轮询定时器

    之前我们使用空循环,达到了延迟的目的,但是这样子的延迟比较不精确.现在就使用实时定时器(RTT)来进行更为精确的计时.RTT虽然不是特别通用,在某些单片机上可能没有,但它较为简单. RTT内部有一个计 ...

  3. SAM4E单片机之旅——1、LED闪烁之空循环

    最近因为导师要写一本关于SAME4单片机的书籍,而我也作为一个嵌入式的初学者看了这本书.现在也让我写写几个小的程序,做做示例.既然写了文档之类的,就发到博客上来吧. 目前关于这芯片能参考的书籍大概就只 ...

  4. SAM4E单片机之旅——7、LED闪烁之TC中断

    RTT主要用做一个全局的定时器,而且不太通用.现在尝试使用一个更为通用的定时器进行定时:定时计数器(Timer Counter, TC). TC提供了广泛的功能,主要可以分为对输入的测量,以及波形的输 ...

  5. SAM4E单片机之旅——6、LED闪烁之按钮控制

    现在试试用按钮控制LED灯……让LED在一个按钮按下时亮起:弹起时灭掉. 主要目的是学习GPIO的输入及中断. 一. 电路 图中的J39-n是几个跳线插座,位置在开发板LCD附近,往下进行前要先确保跳 ...

  6. SAM4E单片机之旅——4、LED闪烁之PWM

    两个LED灯虽然可以闪了,但是总是需要CPU的参与.现在尝试使用一种更为自动化的方法:让脉宽调制(PWM)控制器输出具有一定周期和占空比的方波,以此控制LED灯的亮灭. 一.实现思路 依然使用蓝色和琥 ...

  7. SAM4E单片机之旅——23、在AS6(GCC)中使用FPU

    浮点单元(Floating Point Unit,FPU),是用于处理浮点数运算的单元. 为使用FPU,除了需要启用FPU外,还需要对编译器进行设置,以使其针对浮点运算生成特殊的指令.虽然在Atmel ...

  8. SAM4E单片机之旅——24、使用DSP库求向量数量积

    DSP(Digital Signal Processing,数字信号处理)中会使用大量的数学运算.Cortex-M4中,配置了一些强大的部件,以提高DSP能力.同时CMSIS提供了一个DSP库,提供了 ...

  9. SAM4E单片机之旅——18、通过AFEC(ADC)获取输入的电压

    很多时候,一个电压不仅仅需要定性(高电平或者低电平),而且要定量(了解具体电压的数值).这个时候就可以用到模数转换器(ADC)了.这次的内容是测量开发板搭载的滑动变阻器(VR1)的电压,然后把ADC转 ...

随机推荐

  1. 【Codevs1034】家园(最大流,裂点)

    题意:由于人类对自然的疯狂破坏,人们意识到在大约2300年之后,地球不能再居住了,于是在月球上建立了新的绿地,以便在需要时移民.令人意想不到的是,2177年冬由于未知的原因,地球环境发生了连锁崩溃,人 ...

  2. 诡异的malloc函数和%S的输出

    今天测试代码过程中无意间发现,代码如下: #include <stdio.h> #include <string.h> #include <stdlib.h> #d ...

  3. 微信JSSDK分享功能详解

    本文以微信分享到朋友圈,分享给微信好友为例为参考,进行调用测试,想添加其他的功能,自行查看开发人员文档即可 工欲善其事,必先利其器,好好利用下边的帮助工具,都是腾讯给开发人员的工具 1.微信开发者说明 ...

  4. 十六进制字符串jpg图片互转

    十六制字符串jpg图片互转(格式:FFD8FFE000104A******)如:FFD8FFE000104A46494600010100000100010000FFDB0043000806060706 ...

  5. js-在url后面添加时间戳清除浏览器打开页面的缓存

    这个解决办法还是在网上搜出来的,我还没有测试呢: 我有想既然可以添加时间戳,那可以添加随机数吗?我感觉是可以的,尽管没有测试过. 2018-3-13 几天前我就这个问题询问过我们的后台,加时间戳能否真 ...

  6. 网络入侵检测规避工具fragrouter

    网络入侵检测规避工具fragrouter   网络入侵检测系统可以通过拦截数据包,获取内容进而判断是否为恶意数据包.对于传输较大的数据包,通常会采用分片的方式,将大数据包拆分为小数据包进行传输.如果入 ...

  7. Light oj 1134 - Be Efficient (前缀和)

    题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1134 题意: 给你n个数,问你多少个连续的数的和是m的倍数. 思路: 前缀和取 ...

  8. python装饰器的深度探究

    1.讲装饰器一般讲到这种代码就可以了,但这篇博客会介绍更多: def deco(func): def wrapper(): print("start") func() #调用函数 ...

  9. linux 用户管理命令学习

    groupadd www-data 添加组 useradd phpcomposer -g www-data 添加用户并加入组中 passwd phpcomposer 添加密码 usermod -g p ...

  10. ORACLE MOS 翻译

    http://blog.csdn.net/msdnchina/article/details/53174196