CC3200一共有4组定时器TimerA0-A3,每个定时器有2个子定时器,如TimerA0A、TimerA0B、TimerA1A、TimerA1B等。根据不同的功能需要,可以在引脚配置文件中选择不同的模式,如下图,模式3代表输出PWM,模式12代表捕获外部脉冲。
图源自:CC3200-PWM
PWM示例程序在“example\pwm”目录中。C3200 GPTA2B、GPTA3B、GPTA3A分别通过PWM05(PIN_64)、PWM06(PIN_01)、PWM07(PIN_02)输出PWM信号(占空比由0到1)控制3个LED的亮度(由灭逐渐到亮)。PWM项目包含主函数文件、引脚配置程序文件和CCS启动程序文件。
- PWM操作
PWM操作主要是初始化PWM模块InitPWMModules(),主要内容是设置PWM模式SetupTimerPWMMode()。
void InitPWMModules()
{
//
// Initialization of timers to generate PWM output
//
MAP_PRCMPeripheralClkEnable(PRCM_TIMERA2, PRCM_RUN_MODE_CLK);
MAP_PRCMPeripheralClkEnable(PRCM_TIMERA3, PRCM_RUN_MODE_CLK);
//
// TIMERA2 (TIMER B) as RED of RGB light. GPIO 9 --> PWM_5
//
SetupTimerPWMMode(TIMERA2_BASE, TIMER_B,
(TIMER_CFG_SPLIT_PAIR | TIMER_CFG_B_PWM), 1);
//
// TIMERA3 (TIMER B) as YELLOW of RGB light. GPIO 10 --> PWM_6
//
SetupTimerPWMMode(TIMERA3_BASE, TIMER_A,
(TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_PWM | TIMER_CFG_B_PWM), 1);
//
// TIMERA3 (TIMER A) as GREEN of RGB light. GPIO 11 --> PWM_7
//
SetupTimerPWMMode(TIMERA3_BASE, TIMER_B,
(TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_PWM | TIMER_CFG_B_PWM), 1);
MAP_TimerEnable(TIMERA2_BASE,TIMER_B);
MAP_TimerEnable(TIMERA3_BASE,TIMER_A);
MAP_TimerEnable(TIMERA3_BASE,TIMER_B);
}
void SetupTimerPWMMode(unsigned long ulBase, unsigned long ulTimer,
unsigned long ulConfig, unsigned char ucInvert)
{
//
// Set GPT - Configured Timer in PWM mode.
//
MAP_TimerConfigure(ulBase,ulConfig);
MAP_TimerPrescaleSet(ulBase,ulTimer,0);
//
// Inverting the timer output if required
//
MAP_TimerControlLevel(ulBase,ulTimer,ucInvert);
//
// Load value set to ~0.5 ms time period
//
MAP_TimerLoadSet(ulBase,ulTimer,TIMER_INTERVAL_RELOAD);
//
// Match value set so as to output level 0
//
MAP_TimerMatchSet(ulBase,ulTimer,TIMER_INTERVAL_RELOAD);
}
SetupTimerPWMMode()主要包括下列内容。
- 配置定时器:TimerConfigure()
- 设置预分频:TimerPrescaleSet()
- 控制输出电平:TimerControlLevel()
- 设置定时器初值:TimerLoadSet()
- 设置定时器匹配:TimerMatchSet()
(1)配置定时器
void TimerConfigure(unsigned long ulBase, unsigned long ulConfig);
参数说明:
-
ulBase:定时器基地址,TimerA2和TimerA3的基地址分别是0x4003 2000和0x4003 3000,在hw_memmap.h中定义。
-
ulConfig:定时器配置,主要包括下列值。
1.TIMER_CFG_SPLIT_PAIR:双16位定时器
2.TIMER_CFG_A_PWM:子定时器A PWM输出
3.TIMER_CFG_B_PWM:子定时器B PWM输出
TimerA2的设置值是1和3,因为此程序涉及到了TimerA2B。
TimerA3的设置值是1、2和3,因为此程序涉及到了TimerA3A、TimerA3B。
当需要同时配置一个定时器组中的两个pwm输出的时候,模式设置函数中要2和3进行或运算,如下所示。不能单独配置,否则前一个配置模式会被后面的配置给覆盖掉。注意:Timer项目和PWM项目中TimerConfigure()的主要区别是配置值的不同,Timer项目中配置值是TIMER_CFG_PERIODIC(32位周期定期器)。
(2)设置预分频
void TimerPrescaleSet(unsigned long ulBase, unsigned long ulTimer,unsigned long ulValue)
参数说明:
- ulBase:定时器基地址。
- ulTimer:子定时器选择,TimerA2的设置值是TIMER_B,TimerA3的设置值是TIMER_A和TIMER_B。
- ulValue:预分频值,设置值都是0,即不分频。
(3)控制输出电平
void TimerControlLevel(unsigned long ulBase, unsigned long ulTimer,tBoolean bInvert)
参数说明:
- ulBase:定时器基地址。
- ulTimer:子定时器选择。
- bInvert:定时器输出电平,true表示低电平,false表示高电平。此示例程序都设置为1,也就是PWM模式下输出的有效电平为低电平,对应的LED的熄灭状态。
(4)设置定时器初值
void TimerLoadSet(unsigned long ulBase, unsigned long ulTimer,unsigned long ulValue)
参数说明:
- ulBase:定时器基地址。
- ulTimer:子定时器选择。
- ulValue:定时器初值。此示例程序设置的是40035.
MAP_TimerLoadSet(ulBase,ulTimer,TIMER_INTERVAL_RELOAD);
.....
#define TIMER_INTERVAL_RELOAD 40035 /* =(255*157) */
(5)设置定时器匹配值
void TimerMatchSet(unsigned long ulBase, unsigned long ulTimer,unsigned long ulValue)
参数说明:
- ulBase:定时器基地址。
- ulTimer:子定时器选择。
- ulValue:定时器初值。
(6)允许定时器
void TimerEnable(unsigned long ulBase, unsigned long ulTimer)
(7)更新PWM占空比
void UpdateDutyCycle(unsigned long ulBase, unsigned long ulTimer,
unsigned char ucLevel)
{
//
// Match value is updated to reflect the new dutycycle settings
//
MAP_TimerMatchSet(ulBase,ulTimer,(ucLevel*DUTYCYCLE_GRANULARITY));
}
...
#define DUTYCYCLE_GRANULARITY 157
- 占空比的计算
系统的时钟是80MHz的,首先确定PWM的周期,比如说0.5ms,据此确定定时器装载的初始值。(0.5*10的-3次方)/(1/80M) = 40000,也就是说定时器从40000向下计数就是0.5ms。
从另外一张图举例,图和上面40000不是对应的,只是为了形象点。看出,定时器的初值为0xC350(十进制50000),然后比较值为0x411A(十进制16666),PWM输出的原理是定时器向下计数,当定时器的值大于比较值时输出有效电平,小于比较值时输出另一电平。也就是定时器从50000数到16666这一段时间输出有效电平,16666到0这一段时间输出另一电平。而有效电平的设置是在控制输出电平TimerControlLevel()中的bInvert参数设定的,此示例程序都设置为1,也就是PWM模式下输出的有效电平为高电平,对应的LED的点亮状态。
然后我们要考虑占空比可调,我们需要考虑LED亮度一共有多少等级可以调节。示例中设置的是0-254一共255个等级,那每个等级之间的步长就是40000/255约等于157,不能除尽。所以我们调节定时器的初值为255*157=40035(周期0.5004735ms),这样就可以达到255等级,每个等级步长均为157。所以其实我们是通过改变UpdataDutyCycle函数的uclevel值,去改变那个比较值,让PWM输出有效电平的时间产生改变。
下图是主函数循环函数,主要是改变iLoopCnt以此来改变LED灯亮度等级,从0开始往上递增。
while(1)
{
//
// RYB - Update the duty cycle of the corresponding timers.
// This changes the brightness of the LEDs appropriately.
// The timers used are as per LP schematics.
//
for(iLoopCnt = 0; iLoopCnt < 255; iLoopCnt++)
{
UpdateDutyCycle(TIMERA2_BASE, TIMER_B, iLoopCnt);
UpdateDutyCycle(TIMERA3_BASE, TIMER_B, iLoopCnt);
UpdateDutyCycle(TIMERA3_BASE, TIMER_A, iLoopCnt);
MAP_UtilsDelay(800000);
}
}
以下为具体的占空比更新函数,引用了TimerMatchSet函数改变匹配值,也就是匹配值。我们可以看到匹配值从等级0,慢慢增加,对应的就是比较值0、157、314…所以我们可以推出在前面一段时间,整个周期低电平是占了一大半时间的,对应的就是LED处于熄灭状态。当比较值慢慢增加,有效电平即低电平所占的时间慢慢减少,高电平所占的时间慢慢增加,也就是LED点亮的时间慢慢增加,从视觉上来说,就是LED变亮了。
void UpdateDutyCycle(unsigned long ulBase, unsigned long ulTimer,
unsigned char ucLevel)
{
//
// Match value is updated to reflect the new dutycycle settings
//
MAP_TimerMatchSet(ulBase,ulTimer,(ucLevel*DUTYCYCLE_GRANULARITY));
}
参考文献:《ARM Cortex-M4+Wi-Fi MCU应用指南-CC3200 CCS基础篇》郭书军编著 电子工业出版社