本篇博文最后修改时间:2017年01月06日,11:06。
一、简介
本文以SimpleBLEPeripheral工程为例,介绍如何通过按键来控制P10与P11两路PWM,依次变化为0%、50%、100%的占空比,从而控制对应的led灯亮度渐变。
二、实验平台
协议栈版本:BLE-CC254x-1.4.0
编译软件: IAR 8.20.2
硬件平台: Smart RF开发板(主芯片CC2541)
三、版权声明
博主:甜甜的大香瓜
声明:喝水不忘挖井人,转载请注明出处。
原文地址:http://blog.csdn.NET/feilusia
联系方式:[email protected]
香瓜BLE之CC2541群:127442605
香瓜BLE之CC2640群:557278427
香瓜BLE之Android群:541462902
五、基础知识
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%。
六、实验步骤
1、工程中添加PWM.c
#include
#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