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