BES平台 LED 配置(包括GPIO模拟呼吸灯实现)

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;
};
  1. 一般来说,cfg0为蓝灯,cfg1为红灯
  2. level表示当前LED电平状态,为0或1
  3. time表示当前LED电平状态操持时间,以ms为单位
  4. part表示一次操作,一亮一闪为两次操作
  5. parttotal表示操作总次数
  6. startlevel表示初始电平
  7. periodic表示是否需要对以上操作进行周期性操作,若状态不变,则无限循环
  8. 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  后续模块更新,敬请关注! 谢谢!

 

  • 11
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值