[原创] PIC32MZ之PWM

强仔00001   2015-5-14 00:29 楼主
这段时间有点忙,所以一直没有时间更新博客了
今天我就来教PICMZ32PWM模块。首先我来普及一下PWM的相关概念吧。
脉冲宽度调制是一种模拟控制方式,其根据相应载荷的变化来调制晶体管基极或MOS管栅极的偏置,来实现晶体管或MOS管导通时间的改变,从而实现开关稳压电源输出的改变。这种方式能使电源的输出电压在工作条件变化时保持恒定,是利用微处理器的数字信号对模拟电路进行控制的一种非常有效的技术。
PWM控制技术以其控制简单,灵活和动态响应好的优点而成为电力电子技术最广泛应用的控制方式,也是人们研究的热点。由于当今科学技术的发展已经没有了学科之间的界限,结合现代控制理论思想或实现无谐振波开关技术将会成为PWM控制技术发展的主要方向之一。
下面我来讲解PIC32MZPWM模块吧。
大家可以参考PIC32MZ的芯片手册。PWMPIC32MZ这里叫OC,就是输出比较两个英语的缩写滴。闲话少说,马上进入主题吧。
PIC32MZ的手册上可以看到,PIC32MZ只有9OC通道,如下图:
QQ截图20141018150017.png
这里要提醒大家的是,凡事看到有PPS的标志的,就说明这些引脚可以重映射的。关于重映射这部分说明,大家可以参考手册的第235页的说明,注意重映射有分输入和输出的区别的,我们这里用到的PWM输出,就要看输出重映射了。这里我就贴出一些说明的截图吧:
PWM2.png
怎么来选中引脚呢,如下图:
PWM1.png
假设我选择OC1的话,然后我要PORTD1作为OC1,这里就要对RPD1R寄存器写入相应的数值了,例如RPD1R = 0x0C(二进制是0x00001100,相当于RPD1R = 0x000011000),注意RPXXR的寄存器的位数只有4个而已,同理。如果想把OC1重映射到PORTG9的话,就在RPG9R寄存器写入0x0C即可,其他的外设模块连接到某个管脚也是对RPXXR寄存器写入相应的数值,相信大家看这个表就一目了然了。关于重映射的概念大家还可以参考STM32F10XX增强系列的,它的重映射功能还分部分映射和完全映射呢,这部分大家可以自行去了解,这里我就不多说了。
接下来,OC模块肯定有个时钟源的,PIC32MZOC模块的时钟选择相对灵活些,他可以配置成OC1~OC9的时钟源是timer2或者timer3。还可以是如下图画着黄色框的分配。OC模块的时钟源的分配可以通过OCACLK寄存器来配置(也就是CFGCON的第16位),写0就是红色框的时钟源分配,写1就是黄色框的时钟分配。
QQ截图20141018150520.png
关于CFGCON寄存器每一位的说明如下图:
QQ截图20141018151325.png
还有PIC32MZ的比较模式还可以配置位32位定时器源比较或者16位定时器源比较,如下图:
QQ截图20141018150643.png
一般的应用开16位定时器就足以应付了。这个要看大家的需求来配置。现在我分别用寄存器和库来配置PWM吧。
首先我们得先配置定时器,这点各类的MCU都基本上是一样。
1)首先要配置管脚,把管脚复用为OC模式。
2)设置OC管脚相对应的定时器的参数。
3)设置OC输出的模式。
下面是寄存器的写法:
  1. int main(void)
  2. {
  3. SYS_DEVCON_PerformanceConfig( 200000000 );//将主频设置为200M
  4. PLIB_PORTS_RemapOutput(PORTS_ID_0,OTPUT_FUNC_OC1,OUTPUT_PIN_RPD1); //端口重映射函数 把OC1重映射到RD1管脚,详细可以参阅PIC32MZ 嵌入式连接性(EC)系列 第239页
  5. PLIB_PORTS_RemapOutput(PORTS_ID_0,OTPUT_FUNC_OC2,OUTPUT_PIN_RPD0); //端口重映射函数 把OC2重映射到RD0管脚,详细可以参阅PIC32MZ 嵌入式连接性(EC)系列 第239页,下面的如此类推
  6. PLIB_PORTS_RemapOutput(PORTS_ID_0,OTPUT_FUNC_OC3,OUTPUT_PIN_RPD10);
  7. PLIB_PORTS_RemapOutput(PORTS_ID_0,OTPUT_FUNC_OC4,OUTPUT_PIN_RPG7);
  8. PLIB_PORTS_RemapOutput(PORTS_ID_0,OTPUT_FUNC_OC5,OUTPUT_PIN_RPB15);
  9. PLIB_PORTS_RemapOutput(PORTS_ID_0,OTPUT_FUNC_OC6,OUTPUT_PIN_RPC14);
  10. OC1CON = 0x0000; // 当要设置OC1模块时要关闭OC1输出
  11. OC1R = 0x1388; // 初始化第一功能的比较寄存器,这个是设置初始化刚开始的PWM的占空比
  12. OC1RS = 0x1388; // 初始化第二功能的比较寄存器,这个是设置初始化后的PWM的占空比,通过赋值实现
  13. OC1CON = 0x0006; // 配置PWM模块的OC1输出
  14. /***************以下的其他通道如此类推,PWM的频率计算公式Pwm_Clk =((SYS_CLK_FREQUENCY)/(1+PR2)),占空比计算占空比 = 100% * OCxR / (PRy + 1)
  15. OC2CON = 0x0000; // Turn off the OC1 when performing the setup
  16. OC2R = 0x1388; // Initialize primary Compare register
  17. OC2RS = 0x1388; // Initialize secondary Compare register
  18. OC2CON = 0x0006; // Configure for PWM mode without Fault pin enabled
  19. OC3CON = 0x0000; // Turn off the OC1 when performing the setup
  20. OC3R = 0x1388; // Initialize primary Compare register
  21. OC3RS = 0x1388; // Initialize secondary Compare register
  22. OC3CON = 0x0006; // Configure for PWM mode without Fault pin enabled
  23. OC4CON = 0x0000; // Turn off the OC1 when performing the setup
  24. OC4R = 0x1388; // Initialize primary Compare register
  25. OC4RS = 0x1388; // Initialize secondary Compare register
  26. OC4CON = 0x0006; // Configure for PWM mode without Fault pin enabled
  27. OC5CON = 0x0000; // Turn off the OC1 when performing the setup
  28. OC5R = 0x1388; // Initialize primary Compare register
  29. OC5RS = 0x1388; // Initialize secondary Compare register
  30. OC5CON = 0x0006; // Configure for PWM mode without Fault pin enabled
  31. OC6CON = 0x0000; // Turn off the OC1 when performing the setup
  32. OC6R = 0x1388; // Initialize primary Compare register
  33. OC6RS = 0x1388; // Initialize secondary Compare register
  34. OC6CON = 0x0006; // Configure for PWM mode without Fault pin enabled
  35. PR2 = 0x2710; // 设置频率
  36. // IFS0CLR = 0x00000100; // Clear the T2 interrupt flag
  37. // IEC0SET = 0x00000100; // Enable T2 interrupt
  38. // IPC2SET = 0x0000001C; // Set T2 interrupt priority to 7
  39. T2CONSET = 0x8000; // 使能定时器2
  40. OC1CONSET = 0x8000; // 使能OC1模块
  41. OC5CONSET = 0x8000;
  42. OC3CONSET = 0x8000;
  43. OC2CONSET = 0x8000;
  44. OC4CONSET = 0x8000;
  45. OC6CONSET = 0x8000;
  46. while(1);
  47. /* {
  48. if(IFS0bits.T2IF = 1)
  49. {
  50. if ( Mode )
  51. {
  52. if ( Pwm < 0xFFFF ) // Ramp up mode
  53. {
  54. Pwm ++; // If the duty cycle is not at max, increase
  55. OC1RS = Pwm; // Write new duty cycle
  56. }
  57. else
  58. {
  59. Mode = 0; // PWM is at max, change mode to ramp down
  60. }
  61. } // End of ramp up
  62. else
  63. {
  64. if ( Pwm ) // Ramp Down mode
  65. {
  66. Pwm --; // If the duty cycle is not at min, increase
  67. OC1RS = Pwm; // Write new duty cycle
  68. }
  69. else
  70. {
  71. Mode = 1; // PWM is at min, change mode to ramp up
  72. }
  73. } // End of ramp down
  74. }
  75. IFS0bits.T2IF = 0;
  76. }
下面是库函数的写法:
初始化部分:
  1. void hw_PWM_init(void)
  2. {
  3. /*管脚的复用配置*/
  4. PLIB_PORTS_RemapOutput(PORTS_ID_0,OTPUT_FUNC_OC1,OUTPUT_PIN_RPF2);//Set OC1 to output on pin RD0
  5. PLIB_PORTS_RemapOutput(PORTS_ID_0,OTPUT_FUNC_OC3,OUTPUT_PIN_RPD2);//Set OC2 to output on pin RD10
  6. PLIB_PORTS_RemapOutput(PORTS_ID_0,OTPUT_FUNC_OC4,OUTPUT_PIN_RPG7);
  7. PLIB_PORTS_RemapOutput(PORTS_ID_0,OTPUT_FUNC_OC5,OUTPUT_PIN_RPB15);
  8. PLIB_PORTS_RemapOutput(PORTS_ID_0,OTPUT_FUNC_OC8,OUTPUT_PIN_RPE9);
  9. /* Set the PBCLK3 divisor to 2 and enable PBCLK3 (for timer 2) */
  10. PLIB_OSC_PBClockDivisorSet(OSC_ID_0, OSC_PERIPHERAL_BUS_3, 2);//这里我分频系数是2,所以定时器的总线3的频率是100M
  11. PLIB_OSC_PBOutputClockEnable(OSC_ID_0, OSC_PERIPHERAL_BUS_3);
  12. /*我这里设置了定时器的分频系数为4,所以定时器的的频率为 100M/4 = 25M
  13. 然后设置了定时器的重载数为2500,也就是说,定时器计数到了250,就是开始翻转电平,
  14. 所以 PWM的频率为 25M / 2500 = 10K,PWM的量程范围为0到2500,对应的精度就是 0.04%,
  15. */
  16. PLIB_TMR_ClockSourceSelect(TMR_ID_2, TMR_CLOCK_SOURCE_PERIPHERAL_CLOCK);//定时器的时钟源选择内部的
  17. PLIB_TMR_PrescaleSelect(TMR_ID_2, TMR_PRESCALE_VALUE_4); //设置定时器的分频系数为4
  18. PLIB_TMR_Mode16BitEnable(TMR_ID_2); //使能16位定时
  19. PLIB_TMR_Counter16BitClear(TMR_ID_2); //清空定时器的计数值
  20. PLIB_TMR_Period16BitSet(TMR_ID_2, 2500); //设置定时器的装载数为2500,注意这个数值不要超过65536,因为定时器是16的
  21. /*以下就是配置一些OC通道相关的参数 */
  22. PLIB_OC_TimerSelect(OC_ID_1, OC_TIMER_16BIT_TMR2); //OC模块的时钟源为16位的定时器2
  23. PLIB_OC_ModeSelect(OC_ID_1, OC_COMPARE_PWM_EDGE_ALIGNED_MODE);//设置为PWM输出模式
  24. PLIB_OC_FaultInputSelect(OC_ID_1, OC_FAULT_DISABLE);
  25. PLIB_OC_BufferSizeSelect(OC_ID_1, OC_BUFFER_SIZE_16BIT);
  26. PLIB_OC_Buffer16BitSet(OC_ID_1, 0);
  27. PLIB_OC_PulseWidth16BitSet(OC_ID_1, 0);
  28. PLIB_OC_TimerSelect(OC_ID_3, OC_TIMER_16BIT_TMR2);
  29. PLIB_OC_ModeSelect(OC_ID_3, OC_COMPARE_PWM_EDGE_ALIGNED_MODE);
  30. PLIB_OC_FaultInputSelect(OC_ID_3, OC_FAULT_DISABLE);
  31. PLIB_OC_BufferSizeSelect(OC_ID_3, OC_BUFFER_SIZE_16BIT);
  32. PLIB_OC_Buffer16BitSet(OC_ID_3, 0);
  33. PLIB_OC_PulseWidth16BitSet(OC_ID_3, 0);
  34. PLIB_OC_TimerSelect(OC_ID_4, OC_TIMER_16BIT_TMR2);
  35. PLIB_OC_ModeSelect(OC_ID_4, OC_COMPARE_PWM_EDGE_ALIGNED_MODE);
  36. PLIB_OC_FaultInputSelect(OC_ID_4, OC_FAULT_DISABLE);
  37. PLIB_OC_BufferSizeSelect(OC_ID_4, OC_BUFFER_SIZE_16BIT);
  38. PLIB_OC_Buffer16BitSet(OC_ID_4, 0);
  39. PLIB_OC_PulseWidth16BitSet(OC_ID_4, 0);
  40. PLIB_OC_TimerSelect(OC_ID_5, OC_TIMER_16BIT_TMR2);
  41. PLIB_OC_ModeSelect(OC_ID_5, OC_COMPARE_PWM_EDGE_ALIGNED_MODE);
  42. PLIB_OC_FaultInputSelect(OC_ID_5, OC_FAULT_DISABLE);
  43. PLIB_OC_BufferSizeSelect(OC_ID_5, OC_BUFFER_SIZE_16BIT);
  44. PLIB_OC_Buffer16BitSet(OC_ID_5, 0);
  45. PLIB_OC_PulseWidth16BitSet(OC_ID_5, 0);
  46. PLIB_OC_TimerSelect(OC_ID_8, OC_TIMER_16BIT_TMR2);
  47. PLIB_OC_ModeSelect(OC_ID_8, OC_COMPARE_PWM_EDGE_ALIGNED_MODE);
  48. PLIB_OC_FaultInputSelect(OC_ID_8, OC_FAULT_DISABLE);
  49. PLIB_OC_BufferSizeSelect(OC_ID_8, OC_BUFFER_SIZE_16BIT);
  50. PLIB_OC_Buffer16BitSet(OC_ID_8, 0);
  51. PLIB_OC_PulseWidth16BitSet(OC_ID_8, 0);
  52. /* 使能OC输出 */
  53. PLIB_OC_Enable(OC_ID_1);
  54. PLIB_OC_Enable(OC_ID_3);
  55. PLIB_OC_Enable(OC_ID_4);
  56. PLIB_OC_Enable(OC_ID_5);
  57. PLIB_OC_Enable(OC_ID_8);
  58. PLIB_TMR_Start(TMR_ID_2);
  59. }
输出PWM很简单,我这里输出50%的占空比。
  1. int main ( void )
  2. {
  3. //SYS_DEVCON_PerformanceConfig(SYS_CLK_FREQUENCY);
  4. hw_PWM_init();
  5. SYS_Initialize(NULL);
  6. while (1)
  7. {
  8. PLIB_OC_PulseWidth16BitSet(OC_ID_1,1250);
  9. PLIB_OC_PulseWidth16BitSet(OC_ID_3,1250);
  10. PLIB_OC_PulseWidth16BitSet(OC_ID_4,1250);
  11. PLIB_OC_PulseWidth16BitSet(OC_ID_5,1250);
  12. }
  13. }
关于PIC32的PWM的频率,占空比的可以参考以下信息:
QQ截图20141111001045.png
QQ截图20141111001430.png
QQ截图20141111001453.png
下面就是我的测试图:
IMG_20150514_000057.jpg IMG_20141018_144112.jpg IMG_20141018_144118.jpg
后面我稍稍谈谈PIC32MZ库模板的一些结构,如下图:
QQ截图20150420233439.png
可以看出,工程的结构很有条理,方便用户进行管理。
下面我来讲解下MPLAB X IDE填写头文件的路径。
我们在弄一些大的工程是不可避免的要引用多个的头文件,而MPLAB X IDE好处就是强大的代码补全功能对于我们的开发者来说是不错的利器。
当我们输入#include然后空格一下就会出现如下:
QQ截图20150420235232.png
这个就像切换到了文件夹的方式。我们只需要一步步的找到你所需要添加的头文件即可。
我这里就是演示了添加某个头文件的操作。
QQ截图20150420235247.png
QQ截图20150420235350.png
QQ截图20150420235419.png
QQ截图20150420235633.png
最后确定就可以了。
或者直接
#include "peripheral/osc/plib_osc.h"
也是一样的,其实等效的
写完,要早点休息了,明天还有考试
本帖最后由 强仔00001 于 2015-5-14 00:37 编辑

回复评论 (12)

Good luck!
点赞  2015-5-14 10:21
引用: nmg 发表于 2015-5-14 10:21
Good luck!

已考完,还行
点赞  2015-5-14 10:41

4楼 nmg 

够快啊
别忘记智能手环 :)
点赞  2015-5-14 10:43
引用: nmg 发表于 2015-5-14 10:43
够快啊
别忘记智能手环 :)

好吧
点赞  2015-5-14 10:48
你好,小弟最近也在搞PIC32MZ,打算自己做开发板,看了你的应该是在网上买的开发板吧!所以想讨一份原理图参考一下,感激不尽啊!
是金子总会发光1
点赞  2015-6-11 14:36
引用: 李俊锋 发表于 2015-6-11 14:36
你好,小弟最近也在搞PIC32MZ,打算自己做开发板,看了你的应该是在网上买的开发板吧!所以想讨一份原理图参考一下,感激不尽啊!

我的板子不用钱的,参加PIC32比赛赠送的,资料官网都有的,或者你加我qq,我的资料里有的
点赞  2015-6-11 23:12
引用: 强仔00001 发表于 2015-6-11 23:12
我的板子不用钱的,参加PIC32比赛赠送的,资料官网都有的,或者你加我qq,我的资料里有的

好厉害!小弟我也要努力啊
是金子总会发光1
点赞  2015-6-22 13:31
引用: 李俊锋 发表于 2015-6-22 13:31
好厉害!小弟我也要努力啊

你也在读大学的??
点赞  2015-6-22 23:32
菜鸟路过
点赞  2015-6-30 16:01

我曾经也是菜鸟
点赞  2015-7-1 17:00
你的MZ开发板有眉目了吗?
是金子总会发光1
点赞  2015-7-14 17:20
引用: 李俊锋 发表于 2015-7-14 17:20
你的MZ开发板有眉目了吗?

考试复习中,后天考完试马上去厦门比赛了,MZ的我用官方套件的
点赞  2015-7-14 23:54
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复