eve: Update LED behavior to match new spec

The previous LED implementation was modelled on Kevin and enhanced to
give developer feedback. This CL implements the Eve specific LED
operation.

BUG=b:35584895
BRANCH=none
TEST=Manual
Used EC console command 'battfake' to force different charge levels
and verified that the expected LED patterns were generated.

Change-Id: I46bc2889540f320e8aadf3d9d634c36dbc38c161
Signed-off-by: Scott Collyer <scollyer@google.com>
Reviewed-on: https://chromium-review.googlesource.com/516548
Commit-Ready: Scott Collyer <scollyer@chromium.org>
Tested-by: Scott Collyer <scollyer@chromium.org>
Reviewed-by: Duncan Laurie <dlaurie@google.com>
This commit is contained in:
Scott Collyer
2017-05-25 16:40:13 -07:00
committed by chrome-bot
parent 4e53e01c2c
commit 8bb308cc6b
3 changed files with 180 additions and 97 deletions

View File

@@ -733,6 +733,7 @@ void sensor_board_proc_double_tap(void)
{
/* TODO: Call led update function */
CPRINTS("Call LED status update");
led_register_double_tap();
}
/* Base Sensor mutex */

View File

@@ -298,6 +298,7 @@ enum adc_channel {
int board_get_version(void);
void board_reset_pd_mcu(void);
void board_set_tcpc_power_mode(int port, int mode);
void led_register_double_tap(void);
/* Sensors without hardware FIFO are in forced mode */
#define CONFIG_ACCEL_FORCE_MODE_MASK ((1 << LID_ACCEL) | (1 << LID_LIGHT))

View File

@@ -20,43 +20,118 @@
#define CPRINTF(format, args...) cprintf(CC_PWM, format, ## args)
#define CPRINTS(format, args...) cprints(CC_PWM, format, ## args)
#define LED_TOTAL_TICKS 16
#define LED_ON_TICKS 8
#define LED_TICKS_PER_BEAT 2
#define LED_BEATS_PER_PHASE 2
#define NUM_PHASE 2
#define DOUBLE_TAP_TICK_LEN (LED_TICKS_PER_BEAT * 8)
static int led_debug;
static int double_tap;
static int double_tap_tick_count;
static int led_pattern;
static int led_ticks;
const enum ec_led_id supported_led_ids[] = {
EC_LED_ID_LEFT_LED, EC_LED_ID_RIGHT_LED};
const int supported_led_ids_count = ARRAY_SIZE(supported_led_ids);
/* List of LED colors used */
enum led_color {
LED_OFF = 0,
LED_RED,
LED_GREEN,
LED_BLUE,
LED_WHITE,
LED_AMBER,
LED_RED_2_3,
LED_RED_1_3,
/* Number of colors, not a color itself */
LED_COLOR_COUNT
};
/* List of supported LED patterns */
enum led_pattern {
SOLID_GREEN = 0,
WHITE_GREEN,
SOLID_WHITE,
WHITE_RED,
SOLID_RED,
PULSE_RED_1,
PULSE_RED_2,
BLINK_RED,
OFF,
LED_NUM_PATTERNS,
};
enum led_side {
LED_LEFT = 0,
LED_RIGHT,
LED_BOTH
};
/* Brightness vs. color, in the order of off, red, green and blue */
/*
* LED patterns are described as two phases. Each phase has an associated LED
* color and length in beats. The length of each beat is defined by the macro
* LED_TICKS_PER_BEAT.
*/
struct led_phase {
uint8_t color[NUM_PHASE];
uint8_t len[NUM_PHASE];
};
/*
* Pattern table. The len field is beats per color. 0 for len indicates that a
* particular pattern never changes from the first phase.
*/
static const struct led_phase pattern[LED_NUM_PATTERNS] = {
{ {LED_GREEN, LED_GREEN}, {0, 0} },
{ {LED_WHITE, LED_GREEN}, {2, 4} },
{ {LED_WHITE, LED_WHITE}, {0, 0} },
{ {LED_WHITE, LED_RED}, {2, 4} },
{ {LED_RED, LED_RED}, {0, 0} },
{ {LED_RED, LED_RED_2_3}, {4, 4} },
{ {LED_RED, LED_RED_1_3}, {2, 4} },
{ {LED_RED, LED_OFF}, {1, 6} },
{ {LED_OFF, LED_OFF}, {0, 0} },
};
/*
* Brightness vs. color, in the order of off, red, green and blue. Values are
* for % on PWM duty cycle time.
*/
#define PWM_CHAN_PER_LED 3
static const uint8_t color_brightness[LED_COLOR_COUNT][PWM_CHAN_PER_LED] = {
/* {Red, Green, Blue}, */
[LED_OFF] = {100, 100, 100},
[LED_RED] = {20, 100, 100},
[LED_GREEN] = {100, 20, 100},
[LED_BLUE] = {100, 100, 20},
[LED_WHITE] = {0, 0, 0},
[LED_AMBER] = {0, 87, 100},
[LED_OFF] = {0, 0, 0},
[LED_RED] = {80, 0, 0},
[LED_GREEN] = {0, 80, 0},
[LED_BLUE] = {0, 0, 80},
[LED_WHITE] = {100, 100, 100},
[LED_RED_2_3] = {53, 0, 0},
[LED_RED_1_3] = {27, 0, 0},
};
/*
* When a double tap event occurs, a LED pattern is displayed based on the
* current battery charge level. The LED patterns used for double tap under low
* battery conditions are same patterns displayed when the battery is not
* charging. The table below shows what battery charge level displays which
* pattern.
*/
struct range_map {
uint8_t max;
uint8_t pattern;
};
static const struct range_map pattern_tbl[] = {
{2, BLINK_RED},
{4, PULSE_RED_2},
{10, PULSE_RED_1},
{15, SOLID_RED},
{30, WHITE_RED},
{90, SOLID_WHITE},
{98, WHITE_GREEN},
{100, SOLID_GREEN},
};
/**
@@ -76,7 +151,7 @@ static void set_color(enum led_color color, enum led_side side)
if (saved_duty[LED_LEFT][i] !=
color_brightness[color][i]) {
pwm_set_duty(PWM_CH_LED_L_RED + i,
color_brightness[color][i]);
100 - color_brightness[color][i]);
saved_duty[LED_LEFT][i] =
color_brightness[color][i];
}
@@ -89,7 +164,7 @@ static void set_color(enum led_color color, enum led_side side)
if (saved_duty[LED_RIGHT][i] !=
color_brightness[color][i]) {
pwm_set_duty(PWM_CH_LED_R_RED + i,
color_brightness[color][i]);
100 - color_brightness[color][i]);
saved_duty[LED_RIGHT][i] =
color_brightness[color][i];
}
@@ -131,107 +206,112 @@ int led_set_brightness(enum ec_led_id led_id, const uint8_t *brightness)
return EC_SUCCESS;
}
void led_register_double_tap(void)
{
double_tap = 1;
}
static void led_manage_pattern(int side)
{
int color;
int phase;
/* Determine pattern phase */
phase = led_ticks < LED_TICKS_PER_BEAT * pattern[led_pattern].len[0] ?
0 : 1;
color = pattern[led_pattern].color[phase];
/* Set color for the current phase */
set_color(color, side);
/*
* Update led_ticks. If the len field is 0, then the pattern
* being used is just one color so no need to increase the tick count.
*/
if (pattern[led_pattern].len[0])
if (++led_ticks == LED_TICKS_PER_BEAT *
(pattern[led_pattern].len[0] +
pattern[led_pattern].len[1]))
led_ticks = 0;
/* If double tap display is active, decrement its counter */
if (double_tap_tick_count)
double_tap_tick_count--;
}
static void eve_led_set_power_battery(void)
{
static int power_ticks;
enum charge_state chg_state = charge_get_state();
int side;
int percent_chg;
enum led_pattern pattern;
int tap = 0;
if (double_tap) {
/* Clear double tap indication */
if (!chipset_in_state(CHIPSET_STATE_ON))
/* If not in S0, then set tap on */
tap = 1;
double_tap = 0;
}
/* Get active charge port which maps directly to left/right LED */
side = charge_manager_get_active_charge_port();
/* Ensure that side can be safely used as an index */
if (side < 0 || side >= CONFIG_USB_PD_PORT_COUNT)
side = LED_BOTH;
if (chipset_in_state(CHIPSET_STATE_ON)) {
enum led_side blueside = LED_BOTH;
if (chg_state == PWR_STATE_CHARGE) {
set_color(LED_AMBER, side);
blueside = !side;
} else if (chg_state == PWR_STATE_IDLE || chg_state ==
PWR_STATE_CHARGE_NEAR_FULL) {
set_color(LED_GREEN, side);
blueside = !side;
} else if (chg_state == PWR_STATE_DISCHARGE_FULL &&
extpower_is_present()) {
set_color(LED_GREEN, side);
blueside = !side;
}
set_color(LED_BLUE, blueside);
return;
}
/* Flash red on critical battery, which usually inhibits AP power-on. */
if (battery_is_present() != BP_YES ||
charge_get_percent() < CONFIG_CHARGER_MIN_BAT_PCT_FOR_POWER_ON) {
set_color(((power_ticks++ % LED_TOTAL_TICKS) < LED_ON_TICKS) ?
LED_RED : LED_OFF, LED_BOTH);
return;
}
/* Suspend or Standby state */
if (chipset_in_state(CHIPSET_STATE_SUSPEND) ||
chipset_in_state(CHIPSET_STATE_STANDBY)) {
enum led_side blinkside = LED_BOTH;
/* Get percent charge */
percent_chg = charge_get_percent();
/*
* If a double tap update is underway, let that complete before allowing
* the pattern to change.
*/
if (!double_tap_tick_count) {
if (chg_state == PWR_STATE_CHARGE_NEAR_FULL ||
chg_state == PWR_STATE_IDLE) {
set_color(LED_GREEN, side);
blinkside = !side;
((chg_state == PWR_STATE_DISCHARGE_FULL)
&& extpower_is_present())) {
pattern = SOLID_GREEN;
} else if (chg_state == PWR_STATE_CHARGE) {
set_color(LED_AMBER, side);
blinkside = !side;
} else if (chg_state == PWR_STATE_DISCHARGE_FULL &&
extpower_is_present()) {
set_color(LED_GREEN, side);
blinkside = !side;
}
if (chg_state == PWR_STATE_DISCHARGE ||
chg_state == PWR_STATE_DISCHARGE_FULL ||
chg_state == PWR_STATE_CHARGE ||
chg_state == PWR_STATE_CHARGE_NEAR_FULL ||
chg_state == PWR_STATE_IDLE) {
pattern = SOLID_WHITE;
} else {
int i;
/*
* If in S3/S0iX and not in some error
* state, then flash non-charging LEDs white.
* Not currently charging. Select the pattern based on
* the battery charge level. If there is no double tap
* event to process, then only the low battery patterns
* are relevant.
*/
set_color(((power_ticks++ % LED_TOTAL_TICKS) <
LED_ON_TICKS) ?
LED_WHITE : LED_OFF, blinkside);
return;
for (i = 0; i < ARRAY_SIZE(pattern_tbl); i++) {
if (percent_chg <= pattern_tbl[i].max) {
pattern = pattern_tbl[i].pattern;
break;
}
}
/*
* The patterns used for double tap and for not charging
* state are the same for low battery cases. But, if
* battery charge is high enough to be above SOLID_RED,
* then only display LED pattern if double tap has
* occurred.
*/
if (tap == 0 && pattern <= WHITE_RED)
pattern = OFF;
else
/* Start double tap LED sequence */
double_tap_tick_count = DOUBLE_TAP_TICK_LEN;
}
/* If the LED pattern will change, then reset tick count and set
* new pattern.
*/
if (pattern != led_pattern) {
led_ticks = 0;
led_pattern = pattern;
}
}
/* CHIPSET_STATE_OFF */
switch (chg_state) {
case PWR_STATE_DISCHARGE_FULL:
set_color(LED_OFF, LED_BOTH);
if (extpower_is_present())
set_color(LED_GREEN, side);
break;
case PWR_STATE_DISCHARGE:
set_color(LED_OFF, LED_BOTH);
break;
case PWR_STATE_CHARGE:
set_color(LED_OFF, LED_BOTH);
set_color(LED_AMBER, side);
break;
case PWR_STATE_ERROR:
set_color(((power_ticks++ % LED_TOTAL_TICKS)
< LED_ON_TICKS) ? LED_RED : LED_GREEN, LED_BOTH);
break;
case PWR_STATE_CHARGE_NEAR_FULL:
case PWR_STATE_IDLE: /* External power connected in IDLE. */
set_color(LED_OFF, LED_BOTH);
set_color(LED_GREEN, side);
break;
default:
set_color(LED_RED, LED_BOTH);
break;
}
if (chg_state != PWR_STATE_ERROR)
power_ticks = 0;
led_manage_pattern(side);
}
static void led_init(void)
@@ -251,6 +331,9 @@ static void led_init(void)
pwm_enable(PWM_CH_LED_R_BLUE, 1);
set_color(LED_OFF, LED_BOTH);
led_pattern = OFF;
led_ticks = 0;
double_tap_tick_count = 0;
}
/* After pwm_pin_init() */
DECLARE_HOOK(HOOK_INIT, led_init, HOOK_PRIO_DEFAULT);
@@ -260,7 +343,7 @@ DECLARE_HOOK(HOOK_INIT, led_init, HOOK_PRIO_DEFAULT);
*/
static void led_tick(void)
{
if (led_debug)
if (led_debug == 1)
return;
if (led_auto_control_is_enabled(EC_LED_ID_POWER_LED) &&
@@ -300,8 +383,6 @@ static int command_led(int argc, char **argv)
set_color(LED_BLUE, side);
} else if (!strcasecmp(argv[1], "white")) {
set_color(LED_WHITE, side);
} else if (!strcasecmp(argv[1], "amber")) {
set_color(LED_AMBER, side);
} else {
/* maybe handle charger_discharge_on_ac() too? */
return EC_ERROR_PARAM1;