【BLE】CC2541之PWM

一、简介

本篇以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放在前面减,是因为上面的配置是先低电平后高电平,与实际需要的先高后低的占空比相反了。
因此取补后即可得到正确的占空比。

2、工程中添加PWM.h

#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

3、应用层代码中包含PWM头文件(simpleBLEPeripheral.c中)

#include "PWM.h"

4、定义一组led的PWM结构体(simpleBLEPeripheral.c中)

typedef struct 
{
    uint8 PWM_P10;              //P10的pwm值
    uint8 PWM_P11;              //P11的pwm值       
}LED_CONFIG;
LED_CONFIG led_config;

5、初始化PWM (simpleBLEPeripheral.c中)

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处理事件  


2)添加PWM处理的事件周期( simpleBLEPeripheral.c中

#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);
  }  

7、添加按键测试代码( simpleBLEPeripheral.c中

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












你可能感兴趣的:(【BLE】CC2541之PWM)