NXP

Cortex-M3 (NXP LPC1788)之PWM

2019-07-12 11:16发布

        PWM即脉宽调制,可用于输出一定占空比的方波。LPC1788有两个PWM,每个PWM可以由6路的输出,PWM1~PWM6。下面介绍使用PWM0.1输出PWM波。         1,PWM使用公共的PCLK,因此要配置系统时钟和外设时钟。之前的文章中有具体的时钟配置过程。         2,使能PWM模块。配置外设功率配置寄存器PCONP,使能PWM0的时钟控制位。         3,PWM0.1的输出管脚和P1_2管脚复用,因此要配置IOCON_P1_02寄存器,将其设置成PWM0.1的输出。         4,设置PWM的脉冲宽度,基本的原理就是比较PWM定时器计数器TC和匹配寄存器MR中的值,如果匹配我们可以通过匹配控制寄存器MCR选择操作,如产生一个中断,复位TC,停止TC和预分频计数器PC且停止计数。匹配寄存器MR0通过在匹配是将计数器TC复位来控制PWM的周期频率。另一个匹配寄存器控制PWM沿的位置。如PWM0.1的输出,将使用MR0控制PWM的周期频率,MR1控制边沿的位置。         5,最后是对于PWM的具体控制,配置PWM预分频寄存器PWMPR,该32位寄存器规定了PWM预分频计数的最大值,PWM预分频计数器寄存器PWMPC在每个PCLK上递增一次,当PWMPC和PWMPR值相等时,PWMTC的值会递增,而PWMPR在系一个PCLK周期被复位。这样,当PWMPR=0时,PWMTC会在每个PCLK上递增,而当PWMPR=1时,在每2个PCLK上递增。匹配寄存器PWMMR中的值和PWMTC的值比较,如果相等则触发在PWMMCR中配置的操作。当MR0和TC相等时,我们进行复位TC从新计数从而固定了PWM的周期频率。当定时器处于PWM模式时,软件对PWM匹配寄存器MR的写操作,写入值实际上被保存在一个映像寄存器中,不会被立即使用。所以在我们需要操作PWM锁存使能寄存器PWMLER,典型序列为:将新值写入MR,写PWMLER中相应的位,更改的MR值将在下一次定时器复位时生效。         在下面的程序中,将给MR1中写入不同的匹配值,来控制PWM的占空比。为了方便使用LED灯进行示意。 #define CCLK 120000000 #define PCLK 60000000 #define rFIO1DIR (*(volatile unsigned*)(0x20098020)) #define rFIO1MASK (*(volatile unsigned*)(0x20098030)) #define rFIO1PIN (*(volatile unsigned*)(0x20098034)) #define rFIO1SET (*(volatile unsigned*)(0x20098038)) #define rFIO1CLR (*(volatile unsigned*)(0x2009803c)) #define rCLKSRCSEL (*(volatile unsigned *)(0x400FC10C)) //时钟源选择寄存器 #define rPLL0CON (*(volatile unsigned *)(0x400FC080)) //PLL0控制寄存器 #define rPLL0CFG (*(volatile unsigned *)(0x400FC084)) //PLL0配置寄存器 #define rPLL0STAT (*(volatile unsigned *)(0x400FC088)) //PLL0状态寄存器 #define rPLL0FEED (*(volatile unsigned *)(0x400FC08C)) //PLL0馈送寄存器 #define rPLL1CON (*(volatile unsigned *)(0x400FC0A0)) #define rPLL1CFG (*(volatile unsigned *)(0x400FC0A4)) #define rPLL1STAT (*(volatile unsigned *)(0x400FC0A8)) #define rPLL1FEED (*(volatile unsigned *)(0x400FC0AC)) #define rCCLKSEL (*(volatile unsigned *)(0x400FC104)) //CPU时钟选择寄存器 #define rUSBCLKSEL (*(volatile unsigned *)(0x400FC108)) //USB时钟选择寄存器 #define rPCLKSEL (*(volatile unsigned *)(0x400FC1A8)) //外设时钟寄存器 #define rPCON (*(volatile unsigned *)(0x400FC0C0)) #define rPXCONP (*(volatile unsigned *)(0x400FC0C4)) #define rSCS (*(volatile unsigned *)(0x400FC1A0)) //系统控制和状态寄存器 #define rCLKOUTCFG (*(volatile unsigned *)(0x400FC1C8)) #define rIOCON_P1_02 (*(volatile unsigned *)(0x4002C088)) #define rPCONP (*(volatile unsigned *)(0x400FC0C4)) #define rPWM0IR (*(volatile unsigned *)(0x40014000)) #define rPWM0TCR (*(volatile unsigned *)(0x40014004)) #define rPWM0TC (*(volatile unsigned *)(0x40014008)) #define rPWM0PR (*(volatile unsigned *)(0x4001400C)) #define rPWM0CTCR (*(volatile unsigned *)(0x40014070)) #define rPWM0MCR (*(volatile unsigned *)(0x40014014)) #define rPWM0MR0 (*(volatile unsigned *)(0x40014018)) #define rPWM0MR1 (*(volatile unsigned *)(0x4001401C)) #define rPWM0CCR (*(volatile unsigned *)(0x40014028)) #define rPWM0PCR (*(volatile unsigned *)(0x4001404C)) #define rPWM0LER (*(volatile unsigned *)(0x40014050)) #define rISER1 (*(volatile unsigned *)(0xE000E104)) #define rCER1 (*(volatile unsigned *)(0xE000E184)) unsigned int duty = 10; unsigned char match_cnt = 0; void PWM0_IRQHandler(void) { if(rPWM0IR&0x1) { rFIO1PIN |= (1<<18); match_cnt++; rPWM0IR |= 0x1; //MR0中断复位 } if(rPWM0IR&(0x1<<1)) { rFIO1PIN &= ~(1<<18); rPWM0IR |= 0x1<<1; //MR1中断复位 } } void SystemInit() { rSCS &= ~(0x1<<4); //频率12M rSCS |= (0x1<<5); //使能主振荡器 while(0 == (rSCS & (0x1<<6)));//等待主振荡器稳定 rCLKSRCSEL = 0x1; rPLL0CFG = 0x9; //配置CCLK = 120M rPLL0CON = 0x01; rPLL0FEED = 0xAA; rPLL0FEED =0x55; while( 0 == (rPLL0STAT & (0x1<<10))); rCCLKSEL = (0x1 | (0x1<<8)); rPCLKSEL = 0x2; //配置PCLK = 60M rCLKOUTCFG = 0x0 | (0xb<<4) | (0x1<<8); } void PWMInit() { rIOCON_P1_02 &= ~0x7; rIOCON_P1_02 |= 0x3; //P1.02配置成PWM0[1] rPCONP |= 0x1<<5; //使能PWM0外设 rPWM0IR = 0x73F; //初始化PWM相关控制寄存器 rPWM0TCR = 0; rPWM0CTCR = 0; rPWM0MCR = 0; rPWM0CCR = 0; rPWM0PCR = 0; rPWM0LER = 0; rPWM0PR = 0x1<<20; //每0x1<<20+1个PLCK上升沿,TC递增 rPWM0TCR |= 0x1<<1; //复位TC和PC rPWM0TCR &= ~(0x1<<1); rPWM0MR0 = 100; rPWM0LER |= 0x1; rPWM0MCR |= 0x1<<1 | 0x1; //MR0和TC匹配时复位TC和PC.并且产生中断 rPWM0MR1 = duty; rPWM0LER |= 0x1<<1; rPWM0MCR |= 0x1<<3; //MR1和TC匹配时产生中断 } int main () { PWMInit(); rFIO1DIR |= (0x1<<18); rISER1 |= 0x1<<7; //PWM0中断使能 rPWM0TCR |= 0x1<<1; //复位TC和PC rPWM0TCR &= ~(0x1<<1); rPWM0TCR |= 0x1; //PC和TC计数使能 rPWM0TCR |= 0x1<<3; //PWM模式使能 while(1) { if(match_cnt >= 1) { match_cnt = 0; duty = duty+10; if(duty >= 100) { duty = 0; } rPWM0MR1 = duty; rPWM0LER |= 0x1<<1; rPWM0MCR |= 0x1<<3; } } return 1; }         程序在MR0匹配时复位TC,在MR1匹配时触发边沿。可以看到随着MR1匹配值的改变,LED灯的亮灭时间对应改变。(程序中的预分频寄存器PR设置为了让LED效果明显)         LPC1788的PWM可以进行双边沿的控制。如PWM0.2可以用MR0控制PWM的周期频率,用MR1和MR2控制PWM0.2的边沿。