一、简介
本篇以SimpleBLEPeripheral工程为例,介绍如何利用timer4实现PWM的占空比控制,从而调整led灯的亮度。
二、实验平台
协议栈版本:BLE-CC254x-1.4.0
编译软件: IAR 8.20.2
硬件平台: Smart RF开发板
三、版权声明
博主:甜甜的大香瓜
声明:喝水不忘挖井人,转载请注明出处。
原文地址:http://blog.csdn.net/feilusia
联系方式:[email protected]
CC254x技术交流QQ群:127442605
四、CC2541的PWM简介
1、什么是PWM?
答:PWM是脉冲宽度调制的意思,是通过调整PWM的占空比来实现电机转动速度、led亮度等。
2、什么是占空比?
答:占空比就是高电平所占一个周期时间的比值。
比如一个周期是4S,而0~1S都是高电平、1S~4S都是低电平,那么占空比=1/4=25%
比如一个周期是6S,而0~3S都是高电平、3S~6S都是低电平,那么占空比=3/6=50%
比如一个周期是8S,周期内的8S都是高电平,那么占空比=8/8=100%
注意:占空比都是先高电平、后低电平。
3、如何利用定时器实现PWM?
答:比如香瓜想利用timer4的第0通道(P10)实现30%的占空比。大体的重要步骤如下:
1)根据占空比和计数方式算出T4CC0的值
选择计数方式为循环计数0x00~0xFF,也就是counter计数器会不断地从0跑到255。
则计算出T4CC0=255*30%=76。
2)将timer4的第0通道设置为输出比较模式。
3)设置当counter与T4CC0相等时,输出0;当counter为0时,输出1。
上面这几个重要步骤,就能在counter计数到0时输出高电平,在counter计数到76时为低电平,也就实现了占空比为30%。
五、实验目的
通过按键来控制P10与P11两路PWM,依次变化为0%、50%、100%的占空比,从而控制对应的led灯亮度渐变。
六、实验步骤
1、工程中添加PWM.c
#include <ioCC2540.h> #include "PWM.h" #ifndef BV #define BV(n) (1 << (n)) #endif static U8 sPWM_P10 = 0; //P10的pwm static U8 sPWM_P11 = 0; //P11的pwm //****************************************************************************** //name: PWM_Init //introduce: PWM的初始化 //parameter: none //return: none //author: 甜甜的大香瓜 //changetime: 2016.01.05 //****************************************************************************** void PWM_Init(void) { P1DIR |= BV(0)|BV(1); //P11和P10定义为输出 P1SEL |= BV(0)|BV(1); //将P11和P10设置为外设功能; T4CTL = 0x00; //1分频(32M/256=125K)、关timer、自由运行模式 T4CC0 = 255 - sPWM_P10; //P10的初始化值 T4CC1 = 255 - sPWM_P11; //P11的初始化值 T4CCTL0 = 0x2C; //00 101 100无中断、Set output on compare, clear on 0xFF、比较模式、No Capture T4CCTL1 = 0x2C; //00 101 100无中断、Set output on compare, clear on 0xFF、比较模式、No Capture T4CTL |= BV(4); //开始定时器 } //****************************************************************************** //name: PWM_SetLed //introduce: PWM的两通道值设置 //parameter: nPWM_P10:p10的pwm值 // nPWM_P11:p11的pwm值 //return: none //author: 甜甜的大香瓜 //changetime: 2016.01.05 //****************************************************************************** void PWM_SetLed(U8 nPWM_P10, U8 nPWM_P11) { sPWM_P10 = nPWM_P10; sPWM_P11 = nPWM_P11; } //****************************************************************************** //name: PWM_GetLed //introduce: PWM的两通道值读取 //parameter: nPWM_P10:p10的pwm值 // nPWM_P11:p11的pwm值 //return: none //author: 甜甜的大香瓜 //changetime: 2016.01.05 //****************************************************************************** void PWM_GetLed(U8 *nPWM_P10, U8 *nPWM_P11) { *nPWM_P10 = sPWM_P10; *nPWM_P11 = sPWM_P11; } //****************************************************************************** //name: PWM_Pulse //introduce: PWM的值更新 //parameter: none //return: none //author: 甜甜的大香瓜 //changetime: 2016.01.05 //****************************************************************************** void PWM_Pulse(void) { //重新配置PWM值 T4CC0 = 255 - sPWM_P10; T4CC1 = 255 - sPWM_P11; //复位timer4的counter T4CTL &= ~BV(2); //使能定时器4 T4CTL |= BV(4); }上述代码中,T4CC0的赋值之所以需要255放在前面减,是因为上面的配置是先低电平后高电平,与实际需要的先高后低的占空比相反了。
#ifndef PWM_H #define PWM_H #ifndef U8 typedef unsigned char U8; #endif #ifndef U16 typedef unsigned short U16; #endif extern void PWM_Init(void); extern void PWM_SetLed(U8 nPWM_P10, U8 nPWM_P11); extern void PWM_GetLed(U8 *nPWM_P10, U8 *nPWM_P11); extern void PWM_Pulse(void); #endif
#include "PWM.h"
typedef struct { uint8 PWM_P10; //P10的pwm值 uint8 PWM_P11; //P11的pwm值 }LED_CONFIG; LED_CONFIG led_config;
void SimpleBLEPeripheral_Init( uint8 task_id ) { simpleBLEPeripheral_TaskID = task_id; //给结构体数据赋初值 led_config.PWM_P10 = 0xFF; led_config.PWM_P11 = 0xFF; //PWM初始化 PWM_SetLed(led_config.PWM_P10, led_config.PWM_P11); PWM_Init(); //默认是ENABLE的,会让RF期间停止MCU HCI_EXT_HaltDuringRfCmd(HCI_EXT_HALT_DURING_RF_DISABLE); …… }
两路led都初始化为100%占空比。
6、添加一个PWM处理事件
1)添加PWM处理事件的宏(simpleBLEPeripheral.h中)
#define SBP_PWM_DEAL_EVT 0x4000 //PWM处理事件
#define SBP_PWM_DEAL_EVT_PERIOD 1这里定义为1ms,每1ms就会执行一次PWM处理事件
3)添加PWM处理事件代码(simpleBLEPeripheral.c的SimpleBLEPeripheral_ProcessEvent函数中)
//PWM处理事件 if ( events & SBP_PWM_DEAL_EVT ) { U8 nPWM_P10 = 0; U8 nPWM_P11 = 0; //获取当前的pwm值 PWM_GetLed(&nPWM_P10, &nPWM_P11); //nPWM_P10的pwm值更新,慢慢变化1的pwm值 if(nPWM_P10 > led_config.PWM_P10)//当前的值比预计值大时 { PWM_SetLed(--nPWM_P10, nPWM_P11); //先更新pwm值 PWM_Pulse(); //执行到计数器上 osal_start_timerEx( simpleBLEPeripheral_TaskID, SBP_PWM_DEAL_EVT, SBP_PWM_DEAL_EVT_PERIOD ); return (events ^ SBP_PWM_DEAL_EVT); } else if(nPWM_P10 < led_config.PWM_P10)//当前的值比预计值小时 { PWM_SetLed(++nPWM_P10, nPWM_P11); //先更新pwm值 PWM_Pulse(); //执行到计数器上 osal_start_timerEx( simpleBLEPeripheral_TaskID, SBP_PWM_DEAL_EVT, SBP_PWM_DEAL_EVT_PERIOD ); return (events ^ SBP_PWM_DEAL_EVT); } //nPWM_P11的pwm值更新,慢慢变化1的pwm值 if(nPWM_P11 > led_config.PWM_P11)//当前的值比预计值大时 { PWM_SetLed(nPWM_P10, --nPWM_P11); //先更新pwm值 PWM_Pulse(); //执行到计数器上 osal_start_timerEx( simpleBLEPeripheral_TaskID, SBP_PWM_DEAL_EVT, SBP_PWM_DEAL_EVT_PERIOD ); return (events ^ SBP_PWM_DEAL_EVT); } else if(nPWM_P11 < led_config.PWM_P11)//当前的值比预计值小时 { PWM_SetLed(nPWM_P10, ++nPWM_P11); //先更新pwm值 PWM_Pulse(); //执行到计数器上 osal_start_timerEx( simpleBLEPeripheral_TaskID, SBP_PWM_DEAL_EVT, SBP_PWM_DEAL_EVT_PERIOD ); return (events ^ SBP_PWM_DEAL_EVT); } return (events ^ SBP_PWM_DEAL_EVT); }
static void simpleBLEPeripheral_HandleKeys( uint8 shift, uint8 keys ) { VOID shift; // Intentionally unreferenced parameter static uint8 sPWM_status = 0; if ( keys & HAL_KEY_SW_6 ) { switch(sPWM_status) { //0%占空比、灭灯 case 0: led_config.PWM_P10 = 0x00; led_config.PWM_P11 = 0x00; sPWM_status = 1; break; //50%占空比、半亮灯 case 1: led_config.PWM_P10 = 0x7F; led_config.PWM_P11 = 0x7F; sPWM_status = 2; break; //100%占空比、亮灯 case 2: led_config.PWM_P10 = 0xFF; led_config.PWM_P11 = 0xFF; sPWM_status = 0; break; //其他情况时都为0%占空比、灭灯 default: led_config.PWM_P10 = 0x00; led_config.PWM_P11 = 0x00; sPWM_status = 1; break; } //执行pwm处理事件变化led osal_start_timerEx( simpleBLEPeripheral_TaskID, SBP_PWM_DEAL_EVT, SBP_PWM_DEAL_EVT_PERIOD ); } }上述代码的意思是通过按键,实现0%、50%、100%占空比的切换。
七、实验结果
开发板上电为P10和P11对应的D1和D2灯亮。
第一次按下按键S1,灯从D1渐灭,随后D2也渐灭。此时P10与P11的电压值均为0V。
第二次按下按键S1,灯从D1渐亮,随后D2也渐亮。但亮度均不够明亮。此时P10与P11的电压值均为1.5V。
第三次按下按键S1,D1与D2的亮度均达到最亮值。此时P10与P11的电压值均为3.1V。
因此,实现了通过按键触发改变LED渐变亮度,实验成功。
八、PWM相关问题
1、板子上电时P10对应的LED为什么会闪烁一下?
答:协议栈的LCD部分代码,在初始化时将P10设置为了IO输出状态,而后又被PWM设置为第二功能,因此电平会有个波动。
解决办法是:
HAL_LCD=FALSE