mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-05 22:41:44 +00:00
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:
committed by
chrome-bot
parent
4e53e01c2c
commit
8bb308cc6b
@@ -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 */
|
||||
|
||||
@@ -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))
|
||||
|
||||
275
board/eve/led.c
275
board/eve/led.c
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user