HI, 昨天我对BES 软件 框架做了简单的介绍,这篇文章就详细叙述下 BES 蓝牙模块 LED 的配置 和总结 。
一、简单LED闪烁 、常亮
这个模块在BES 默认的SDK 是有实现的,就是通过软件定时器对GPIO周期性控制拉高或拉低。
LED的控制全部在函数app_status_indication_set内设置,根据上层传送事件,来调用相应的LED控制。
#if (CFG_HW_PWL_NUM > 0) /*CFG_HW_PWL_NUM 是LED的个数*/
static void app_pwl_timehandler(void const *param);
osTimerDef (APP_PWL_TIMER0, app_pwl_timehandler); // define timers
#if (CFG_HW_PWL_NUM == 2)
osTimerDef (APP_PWL_TIMER1, app_pwl_timehandler); //默认最多定义两个LED 控制,可以根据用户需求更改 LED 和定时器个数 ,一一对应就好
#endif
struct APP_PWL_T {
enum APP_PWL_ID_T id;
struct APP_PWL_CFG_T config;
uint8_t partidx;
osTimerId timer;
};
- 一般来说,cfg0为蓝灯,cfg1为红灯
- level表示当前LED电平状态,为0或1
- time表示当前LED电平状态操持时间,以ms为单位
- part表示一次操作,一亮一闪为两次操作
- parttotal表示操作总次数
- startlevel表示初始电平
- periodic表示是否需要对以上操作进行周期性操作,若状态不变,则无限循环
- app_pwl_setup和app_pwl_start配套使用,把LED灯操作真正写进配置,并生效.
例如在 LED 设置 总接口函数中:
int app_status_indication_set(APP_STATUS_INDICATION_T status)
{
uint8_t i=0;
struct APP_PWL_CFG_T cfg0;
struct APP_PWL_CFG_T cfg1;
TRACE(3,"[%s],old=%d new=%d",__func__, app_status,status);
if(app_status_indication_filter(status)||app_status == status)
{
return 0;
}
app_status = status;
memset(&cfg0, 0, sizeof(struct APP_PWL_CFG_T));
memset(&cfg1, 0, sizeof(struct APP_PWL_CFG_T));
app_pwl_stop(APP_PWL_ID_0);
app_pwl_stop(APP_PWL_ID_1);
/* pwl0 -- Blue pwl1 -- White */
struct APP_PWL_CFG_T cfg0;
struct APP_PWL_CFG_T cfg1;
switch(status){
case APP_STATUS_INDICATION_POWERON:
case APP_STATUS_INDICATION_POWEROFF:
cfg0.part[0].level = 1;
cfg0.part[0].time = (300);
cfg0.part[1].level = 0;
cfg0.part[1].time = (300);
cfg0.part[2].level = 1;
cfg0.part[2].time = (300);
cfg0.part[3].level = 0;
cfg0.part[3].time = (300);
cfg0.parttotal = 4;
cfg0.startlevel = 1;
cfg0.periodic = false;
app_pwl_setup(APP_PWL_ID_0, &cfg0);
app_pwl_start(APP_PWL_ID_0);
break;
}
}
app_pwl_setup(enum APP_PWL_ID_T id, struct APP_PWL_CFG_T *cfg) // 这个函数主要是 对当前LED 所在的GPIO 进行初始化 (拉高或者拉低,然后配置LED属性)。
int app_pwl_start(enum APP_PWL_ID_T id) //主要用于启动定时器 对LED 进行周期性 控制
int app_pwl_start(enum APP_PWL_ID_T id)
{
#if (CFG_HW_PWL_NUM > 0)
struct APP_PWL_T *pwl = NULL;
struct APP_PWL_CFG_T *cfg = NULL;
if (id >= APP_PWL_ID_QTY) {
return -1;
}
pwl = &app_pwl[id];
cfg = &(pwl->config);
if (pwl->id == APP_PWL_ID_QTY){
return -1;
}
pwl->partidx = 0;
if (pwl->partidx >= cfg->parttotal){
return -1;
}
osTimerStop(pwl->timer);
if(cfg->part[pwl->partidx].level){
hal_gpio_pin_set((enum HAL_GPIO_PIN_T)cfg_hw_pinmux_pwl[pwl->id].pin);
}else{
hal_gpio_pin_clr((enum HAL_GPIO_PIN_T)cfg_hw_pinmux_pwl[pwl->id].pin);
}
osTimerStart(pwl->timer, cfg->part[pwl->partidx].time);
#endif
return 0;
}
上层 应用接口和底层 定时器控制 结合看起来 就一目了然,这个也没有什么难点。
二 、基于自带LED口的呼吸灯配置
BES 比较坑,在app_pwl.cpp文件里面 有一个宏定义
#if defined(__PMU_VIO_DYNAMIC_CTRL_MODE__)
pmu_viorise_req(pwl->id == APP_PWL_ID_0 ? PMU_VIORISE_REQ_USER_PWL0 : PMU_VIORISE_REQ_USER_PWL1, true);
#endif
看起来就是呼吸灯配置的 其实不然 ,这个并不能把LED 普通控制改为PWM 呼吸灯配置。
关于LED IO 的PWM 控制 在 ..platform/drivers/ana/best1305/pmu_best1305.c(芯片型号) 我们先看下代码结构:
enum HAL_IOMUX_PIN_T {
HAL_IOMUX_PIN_P0_0 = 0,
....
HAL_IOMUX_PIN_NUM,
HAL_IOMUX_PIN_LED1 = HAL_IOMUX_PIN_NUM,
HAL_IOMUX_PIN_LED2,
HAL_IOMUX_PIN_LED_NUM,
};//gpio ---->选择PWM 接口
struct PMU_LED_BR_CFG_T {
uint16_t off_time_ms;
uint16_t on_time_ms;
uint16_t fade_time_ms;
};//------>PWM 相关配置 off_time_ms 熄灭时间 on_time_ms 常亮时间 fade_time_ms 渐变时间 ms
void pmu_led_breathing_enable(enum HAL_IOMUX_PIN_T pin, const struct PMU_LED_BR_CFG_T *cfg)
{
uint32_t st1;
uint32_t st2;
uint32_t subcnt_data;
uint8_t tg;
uint16_t val;
uint32_t lock;
if (pin == HAL_IOMUX_PIN_LED1 || pin == HAL_IOMUX_PIN_LED2) {
st1 = MS_TO_TICKS(cfg->off_time_ms);
if (st1 > 0xFFFF) {
st1 = 0xFFFF;
}
st2 = MS_TO_TICKS(cfg->on_time_ms);
if (st2 > 0xFFFF) {
st2 = 0xFFFF;
}
subcnt_data = MS_TO_TICKS(cfg->fade_time_ms);
subcnt_data = integer_sqrt_nearest(subcnt_data);
if (subcnt_data > (SUBCNT_DATA2_MASK >> SUBCNT_DATA2_SHIFT)) {
subcnt_data = (SUBCNT_DATA2_MASK >> SUBCNT_DATA2_SHIFT);
}
// TODO: Keep compatible with digital PWM module (can be removed after 2500)
if (subcnt_data > 0xFE) {
subcnt_data = 0xFE;
}
tg = 1;
pmu_led_set_direction((enum HAL_GPIO_PIN_T)pin, HAL_GPIO_DIR_OUT);
pmu_led_set_pull_select(pin, HAL_IOMUX_PIN_NOPULL);
pmu_led_set_voltage_domains(pin, HAL_IOMUX_PIN_VOLTAGE_VBAT);
lock = int_lock();
if (pin == HAL_IOMUX_PIN_LED1) {
pmu_write(PMU_REG_PWM2_TOGGLE, st2);
pmu_write(PMU_REG_PWM2_ST1, st1);
val = SUBCNT_DATA2(subcnt_data) | TG_SUBCNT_D2_ST(tg);
pmu_write(PMU_REG_PWM2_EN, val);
pmu_read(PMU_REG_PWM2_BR_EN, &val);
val = (val & ~REG_CLK_PWM_DIV_MASK) | REG_CLK_PWM_DIV(0) |
REG_PWM_CLK_EN | REG_PWM2_BR_EN | PWM_SELECT_EN;
pmu_write(PMU_REG_PWM2_BR_EN, val);
} else {
pmu_write(PMU_REG_PWMB_TOGGLE, st2);
pmu_write(PMU_REG_PWMB_ST1, st1);
val = SUBCNT_DATAB(subcnt_data) | TG_SUBCNT_DB_ST(tg);
pmu_write(PMU_REG_PWMB_EN, val);
pmu_read(PMU_REG_PWMB_BR_EN, &val);
val = (val & ~REG_CLK_PWMB_DIV_MASK) | REG_CLK_PWMB_DIV(0) |
REG_PWMB_CLK_EN | REG_PWMB_BR_EN | PWMB_SELECT_EN;
pmu_write(PMU_REG_PWMB_BR_EN, val);
}
int_unlock(lock);
}
}
void pmu_led_breathing_disable(enum HAL_IOMUX_PIN_T pin)
{
uint16_t val;
uint32_t lock;
if (pin == HAL_IOMUX_PIN_LED1 || pin == HAL_IOMUX_PIN_LED2) {
lock = int_lock();
if (pin == HAL_IOMUX_PIN_LED1) {
pmu_read(PMU_REG_PWM2_BR_EN, &val);
val &= ~(REG_PWM_CLK_EN | REG_PWM2_BR_EN | PWM_SELECT_EN);
pmu_write(PMU_REG_PWM2_BR_EN, val);
} else {
pmu_read(PMU_REG_PWMB_BR_EN, &val);
val &= ~(REG_PWMB_CLK_EN | REG_PWMB_BR_EN | PWMB_SELECT_EN);
pmu_write(PMU_REG_PWMB_BR_EN, val);
}
int_unlock(lock);
pmu_led_set_direction((enum HAL_GPIO_PIN_T)pin, HAL_GPIO_DIR_IN);
pmu_led_set_pull_select(pin, HAL_IOMUX_PIN_PULLUP_ENABLE);
}
}
上述LED 开启后处于一直循环状态,需要手动关闭
实列使用:
#include "apps.h"
#include "pmu.h"
#include "pmu_best1305.h"
#include "hal_timer.h"
#define BREATH_LED_NULL 0
#define BREATH_LED_0 1
#define BREATH_LED_1 2
static bool app_breath_pair_enable=false;
static u8 app_breath_number=BREATH_LED_NULL;
osTimerId app_breath_led_timer=NULL;
static void app_breath_led_break_down(void){ //关闭当前在工作的LED呼吸灯
osTimerStop(app_breath_led_timer);
switch(app_breath_number){
case BREATH_LED_0:
pmu_pwm_led0_breathing_disable();
break;
case BREATH_LED_1:
pmu_pwm_led1_breathing_disable();
break;
default:
break;
}
app_breath_number=BREATH_LED_NULL;
app_breath_pair_enable=false;
}
static void app_breath_led_timehandler(void const *param){ //呼吸灯时间到了后手动关闭
//app_breath_pair_enable=false;
app_breath_led_break_down(); //关闭当前呼吸灯
app_pwl_end_callback_handler(); //外部函数 用于回调显示其它啊状态
TRACE("app_breath_led_timehandler,breath led stop!");
}
osTimerDef (APP_BREATH_TIMER, app_breath_led_timehandler); //呼吸灯手动关闭 定时器
void app_breath_led_timer_creat(void){ //呼吸灯手动关闭 定时器 创建
app_breath_led_timer = osTimerCreate (osTimer(APP_BREATH_TIMER), osTimerOnce, NULL);
}
void app_status_indication_close(void) //关闭所有LED 接口
{
app_breath_led_break_down(); //关闭呼吸灯
app_pwl_close();//关闭普通GPIO 灯
}
int app_status_indication_set(APP_STATUS_INDICATION_T status)
{
uint8_t i=0;
struct APP_PWL_CFG_T cfg0;
struct APP_PWL_CFG_T cfg1;
struct PMU_LED_BR_CFG_T breath_led_cfg; //呼吸灯配置结构体
TRACE("[%s],old=%d new=%d",__func__, app_status,status);
if(app_breath_number!=BREATH_LED_NULL){ //如果当前有呼吸灯在工作,需先关闭呼吸灯
app_breath_led_break_down();
}
memset(&cfg0, 0, sizeof(struct APP_PWL_CFG_T));
memset(&cfg1, 0, sizeof(struct APP_PWL_CFG_T));
app_pwl_stop(APP_PWL_ID_0);
app_pwl_stop(APP_PWL_ID_1);
switch (status) {
case APP_STATUS_INDICATION_CONNECTED:
case APP_STATUS_INDICATION_TWS_CONNECTED:
breath_led_cfg.fade_time_ms=600;
breath_led_cfg.on_time_ms=400;
breath_led_cfg.off_time_ms=500;
if(app_battery_is_low()){
app_breath_number=BREATH_LED_1;
pmu_led_breathing_enable(HAL_GPIO_PIN_LED2,&breath_led_cfg);
}
else{
app_breath_number=BREATH_LED_0;
pmu_led_breathing_enable(HAL_GPIO_PIN_LED1,&breath_led_cfg);
}
osTimerStart(app_breath_led_timer,10800);//时间到后 手动关闭pwm呼吸灯
app_breath_pair_enable=true;
break;
case APP_STATUS_INDICATION_PRESS_KEY:
breath_led_cfg.fade_time_ms=600;
breath_led_cfg.on_time_ms=400;
breath_led_cfg.off_time_ms=500;
if(app_battery_is_low()){
app_breath_number=BREATH_LED_1;
pmu_led_breathing_enable(HAL_GPIO_PIN_LED2,&breath_led_cfg);
}
else{
app_breath_number=BREATH_LED_0;
pmu_led_breathing_enable(HAL_GPIO_PIN_LED1,&breath_led_cfg);
}
osTimerStop(app_breath_led_timer);
osTimerStart(app_breath_led_timer,4800);
break;
}
return 0;
}
如果您使用的是芯片自带的PWM LED IO 那么呼吸灯就可以和上面一样配置。
三、GPIO模拟PMW输出 呼吸灯设置
一般来说 很多时候 LED1/LED2(可以在关机状态保持高阻,漏电小) 由于其特殊性被占用了,但是客户又需要用到呼吸LED,这个时候只能用GPIO 模拟呼吸灯了 。
首先,需要搞清楚 GPIO模拟呼吸灯的原理:
就是通过调整GPIO输出波形的占空比改变LED 的输入电压,动态调整LED 亮度。
#if defined (__ENA_LED_BREATH__)
typedef struct APP_BREATH{
uint16_t on_time; // 淡入时间 ms
uint16_t off_time; //渐灭时间 ms
bool is_work; // 是否在工作
uint8_t cur_ratio; //当前占空比,也就是LED亮度
HWTIMER_ID timer; //硬件定时器
}APP_BREATH_CFG_T;
static APP_BREATH_CFG_T app_breath_blue_cfg;
static void app_pwm0_set(uint8_t level)
{
struct HAL_IOMUX_PIN_FUNCTION_MAP cfg_hw_pinmux = {HAL_IOMUX_PIN_NUM, HAL_IOMUX_FUNC_AS_GPIO, HAL_IOMUX_PIN_VOLTAGE_VIO, HAL_IOMUX_PIN_PULLUP_ENABLE};
struct HAL_PWM_CFG_T cfg;
// make sure breath led run normal, which add power
if(level == 0)
{
app_sysfreq_req(APP_SYSFREQ_USER_APP_12, APP_SYSFREQ_32K);
}
else if(level == 1)
{
app_sysfreq_req(APP_SYSFREQ_USER_APP_12, APP_SYSFREQ_26M);
}
if(level == 0 || level == 100)
{
hal_pwm_disable(HAL_PWM_ID_0); //需要注意这个HAL_PWM_ID_0要与GPIO 匹配
cfg_hw_pinmux.pin = cfg_hw_pinmux_pwl[0].pin;
cfg_hw_pinmux.function= HAL_IOMUX_FUNC_AS_GPIO;
hal_iomux_init((struct HAL_IOMUX_PIN_FUNCTION_MAP *)&cfg_hw_pinmux, 1);
hal_gpio_pin_set_dir((enum HAL_GPIO_PIN_T)cfg_hw_pinmux_pwl[0].pin, HAL_GPIO_DIR_OUT, level?1:0);
}
else
{
cfg.freq = 1000;
cfg.inv = false;
cfg.sleep_on = true;
cfg.ratio = level;
hal_pwm_enable(HAL_PWM_ID_0, &cfg); /**HAL_PWM_ID_3**/
cfg_hw_pinmux.pin = cfg_hw_pinmux_pwl[0].pin;
cfg_hw_pinmux.function = HAL_IOMUX_FUNC_PWM0; /**HAL_IOMUX_FUNC_PWM3**/
hal_iomux_init((struct HAL_IOMUX_PIN_FUNCTION_MAP *)&cfg_hw_pinmux, 1);
}
//TRACE("PWM3 SET:%d",level);
}
void app_breath_led1_start(bool en, uint16_t on_time, uint16_t off_time, bool start_level)
{
//osTimerStop(app_breath_blue_cfg.timer);
hwtimer_stop(app_breath_blue_cfg.timer);
if(en)
{
if(start_level)
{
app_breath_blue_cfg.cur_ratio = 100;
app_pwm0_set(100);
}
else
{
app_breath_blue_cfg.cur_ratio = 0;
app_pwm0_set(0);
}
app_breath_blue_cfg.on_time = on_time;
app_breath_blue_cfg.off_time = off_time;
hwtimer_start(app_breath_blue_cfg.timer,MS_TO_TICKS(50));
}
else
{
app_pwm0_set(0);
}
app_breath_blue_cfg.is_work = en;
}
static void app_breath_led1_timehandler(void *param)
{
uint8_t level = 0;
if(app_breath_blue_cfg.is_work == false) return;
app_breath_blue_cfg.cur_ratio+=1;
if(app_breath_blue_cfg.cur_ratio > 100)
{
level = 200 - app_breath_blue_cfg.cur_ratio;
}
else
{
level = app_breath_blue_cfg.cur_ratio;
}
app_pwm0_set(level);
if(level == 0)
{
app_breath_blue_cfg.cur_ratio = 0;
hwtimer_start(app_breath_blue_cfg.timer,MS_TO_TICKS(app_breath_blue_cfg.off_time));
}
else
{
hwtimer_start(app_breath_blue_cfg.timer,MS_TO_TICKS(app_breath_blue_cfg.on_time/200));
}
}
#endif
调试的时候可以直接用 app_pwm0_set(level) //然后用示波器接对应GPIO 口查看是否有矩形方波输出。
如果没有的话 请查看配置的GPIO 和PWM_ID 是否对应。(我这里用的是2500IU GPIO_0 所以对应的PWM_ID 就是PWM0)
整个LED 的实现 代码 可以参考 链接:
https://share.weiyun.com/ExSPAB9D
LED 的配置和讲解就到了 QQ:1902026113 后续模块更新,敬请关注! 谢谢!