PWM在高频情况下,一个很好的用处就是通过控制占空比来控制输出的功率,比如控制风扇转速、LED灯的亮度等。这次就利用PWM的中断功能,动态改变脉冲的占空比,来实现呼吸灯的效果。
一、实现思路
PWM可以选择让计数器在周期结束产生中断(在周期中央对齐时,可能选择在周期中央也产生中断),并且可以在运行的时候动态地调整占空比、周期、极性等属性。所以可以在中断处理函数中动态地改变占空比以改变LED灯的亮度。
这次也将使用通道0和引脚PA0。
二、PWM设置
这里需要用到较高频率的时钟,所以选择使用主时钟经32分频后的时钟(12.5 kHz)。计数器周期为400,即输出脉冲频率为125000/400 = 312.5 Hz。同时需要使能相应的中断。
PWM的主要配置代码如下:
#define PERIOD_VALUE 400 /* 时钟选择 */ PWM->PWM_CH_NUM[0].PWM_CMR = PWM_CMR_CPRE_MCK_DIV_32; /* 启用中断 */ PWM->PWM_IER1 = PWM_IER1_CHID0; /* 周期及占空比 */ PWM->PWM_CH_NUM[0].PWM_CPRD= PWM_CPRD_CPRD(PERIOD_VALUE); PWM->PWM_CH_NUM[0].PWM_CDTY = PWM_CDTY_CDTY(0); /* 使能中断 */ NVIC_ClearPendingIRQ(PWM_IRQn); NVIC_SetPriority(PWM_IRQn, 0); NVIC_EnableIRQ(PWM_IRQn);
三、PWM中断处理
在每个周期结束后,会产生一个中断。然后在中断处理函数中,改变占空比。需要注意的是,在PWM使能时,需要通过写入PWM占空比修改寄存器(PWM_CDTYUPD)来改变占空比。默认情况下,该修改在下一个周期生效。
为得到更好的效果,可以在两次呼吸之间设置一断间隔。
注意,需要通过读取PWM_ISR1来拉低产生的中断。中断处理函数在后面的完整代码中贴出。
附 完整代码
#include <sam.h> #define PERIOD_VALUE 400 #define BREATH_INTERVAL_PERIOD 200 /* 两次呼吸间隔的周期 */ void ConfigPWM(void) { /* PMC 启用 * PWM的ID大于31,需要在PMC_PCER1中启用 */ PMC->PMC_PCER1 = 1 << (ID_PWM - 32); /* 禁用通道0,以进行配置 */ PWM->PWM_DIS = PWM_DIS_CHID0; /* 配置通道0 */ PWM->PWM_CH_NUM[0].PWM_CMR = PWM_CMR_CPRE_MCK_DIV_32 /* 计数器时钟选择为CLKA */ ; /* 周期左对齐,先输出低电平,不使用死区发生器 */ /* 启用中断 */ PWM->PWM_IER1 = PWM_IER1_CHID0; PWM->PWM_CH_NUM[0].PWM_CPRD = PWM_CPRD_CPRD(PERIOD_VALUE); /* 周期 */ PWM->PWM_CH_NUM[0].PWM_CDTY = PWM_CDTY_CDTY(0); /* 占空比,准确来说是阀值 */ /* 使能中断 */ NVIC_ClearPendingIRQ(PWM_IRQn); NVIC_SetPriority(PWM_IRQn, 0); NVIC_EnableIRQ(PWM_IRQn); /* 使能 PWM */ PWM->PWM_ENA = PWM_ENA_CHID0; } /* PWM 中断处理函数 */ void PWM_Handler(void) { static uint32_t ul_duty = 0; /* PWM 占空比*/ static uint8_t fade_in = 1; /* LED 淡入标志 */ static uint8_t dark_period = 0; /* LED 完全暗下来的周期 */ /* 读取PWM_ISR1,同时可以拉低中断 */ uint32_t events = PWM->PWM_ISR1; /* 先确定是否是指定的中断 */ if ((events & PWM_ISR1_CHID0) != 0) { if (dark_period != 0) { dark_period--; return; } /* 淡入 */ if (fade_in) { ul_duty++; if (ul_duty == PERIOD_VALUE) { fade_in = 0; } } else { /* 淡出 */ ul_duty--; if (ul_duty == 0) { fade_in = 1; /* LED暗下来一定的周期再淡入 */ dark_period = BREATH_INTERVAL_PERIOD; } } /* 设置新的占空比 */ PWM->PWM_CH_NUM[0].PWM_CDTYUPD = PWM_CDTY_CDTY(ul_duty); } } void ConfigPIO(void) { /* 引脚由外设控制 */ PIOA->PIO_PDR = PIO_PA0; /* 选择外设 */ /* PIOA选择外设A(将影响PA所有引脚) */ PIOA->PIO_ABCDSR[0] = 0; PIOA->PIO_ABCDSR[1] = 0; } int main(void) { /* Disable WDT */ WDT->WDT_MR = WDT_MR_WDDIS; ConfigPWM(); ConfigPIO(); while (1) { } return 0; }