mirror of
https://github.com/google/pebble.git
synced 2025-05-02 08:11:39 -04:00
181 lines
6.4 KiB
C
181 lines
6.4 KiB
C
/*
|
|
* Copyright 2024 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "drivers/backlight.h"
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "board/board.h"
|
|
#include "debug/power_tracking.h"
|
|
#include "console/prompt.h"
|
|
#include "drivers/gpio.h"
|
|
#include "drivers/led_controller.h"
|
|
#include "drivers/periph_config.h"
|
|
#include "drivers/pwm.h"
|
|
#include "drivers/timer.h"
|
|
#include "kernel/util/stop.h"
|
|
#include "system/logging.h"
|
|
#include "system/passert.h"
|
|
|
|
// Parameters to a timer based PWM.
|
|
//
|
|
// ---------------- ---------- ---------- ---------- ----------
|
|
// | | | | | | | |
|
|
// | | | | | | | |
|
|
// | | | | | | | |
|
|
// | | | | | | | |
|
|
// | | | | | | | |
|
|
// | | | | | | | |
|
|
// | | | | | | | |
|
|
// ------- ------- ------- --------
|
|
//
|
|
// The resulting waveform has a frequency of PWM_OUTPUT_FREQUENCY_HZ. Inside each period, the timer
|
|
// counts up to TIMER_PERIOD_RESOLUTION. This means the counter increments at a rate of
|
|
// PWM_OUTPUT_FREQUENCY_HZ * TIMER_PERIOD_RESOLUTION, which is the frequency that our timer
|
|
// prescalar has to calculate. The duty cycle is defined by the TIM_Pulse parameter, which
|
|
// controls after which counter value the output waveform will become active. For example, a
|
|
// TIM_Pulse value of TIMER_PERIOD_RESOLUTION / 4 will result in an output waveform that will go
|
|
// active after 25% of it's period has elapsed.
|
|
|
|
//! The counter reload value. The timer will count from 0 to this value and then reset again.
|
|
//! The TIM_Pulse member below controls for how many of these counts the resulting PWM signal is
|
|
//! active for.
|
|
static const uint32_t TIMER_PERIOD_RESOLUTION = 1024;
|
|
|
|
//! The number of periods we have per second.
|
|
//! Note that we want BOARD_CONFIG_BACKLIGHT.timer.peripheral to have as short a period as
|
|
//! possible for power reasons.
|
|
static const uint32_t PWM_OUTPUT_FREQUENCY_HZ = 256;
|
|
|
|
static bool s_initialized = false;
|
|
|
|
static bool s_backlight_pwm_enabled = false;
|
|
|
|
//! Bitmask of who wants to hold the LED enable on.
|
|
//! see \ref led_enable, \ref led_disable, \ref LEDEnabler
|
|
static uint32_t s_led_enable;
|
|
|
|
static void prv_backlight_pwm_enable(bool on) {
|
|
pwm_enable(&BOARD_CONFIG_BACKLIGHT.pwm, on);
|
|
|
|
if (on != s_backlight_pwm_enabled) {
|
|
if (on) {
|
|
stop_mode_disable(InhibitorBacklight);
|
|
} else {
|
|
stop_mode_enable(InhibitorBacklight);
|
|
}
|
|
}
|
|
|
|
s_backlight_pwm_enabled = on;
|
|
}
|
|
|
|
void backlight_init(void) {
|
|
if (s_initialized) {
|
|
return;
|
|
}
|
|
|
|
s_led_enable = 0;
|
|
|
|
if (BOARD_CONFIG_BACKLIGHT.options & ActuatorOptions_Ctl) {
|
|
periph_config_acquire_lock();
|
|
gpio_output_init(&BOARD_CONFIG_BACKLIGHT.ctl, GPIO_OType_PP, GPIO_Speed_2MHz);
|
|
gpio_output_set(&BOARD_CONFIG_BACKLIGHT.ctl, false);
|
|
periph_config_release_lock();
|
|
s_initialized = true;
|
|
}
|
|
|
|
if (BOARD_CONFIG_BACKLIGHT.options & ActuatorOptions_Pwm) {
|
|
periph_config_acquire_lock();
|
|
pwm_init(&BOARD_CONFIG_BACKLIGHT.pwm,
|
|
TIMER_PERIOD_RESOLUTION,
|
|
TIMER_PERIOD_RESOLUTION * PWM_OUTPUT_FREQUENCY_HZ);
|
|
periph_config_release_lock();
|
|
s_initialized = true;
|
|
}
|
|
|
|
if (BOARD_CONFIG_BACKLIGHT.options & ActuatorOptions_IssiI2C) {
|
|
led_controller_init();
|
|
s_initialized = true;
|
|
}
|
|
}
|
|
|
|
// TODO: PBL-36077 Move to a generic 4v5 enable
|
|
void led_enable(LEDEnabler enabler) {
|
|
if (s_led_enable == 0) {
|
|
gpio_output_set(&BOARD_CONFIG_BACKLIGHT.ctl, true);
|
|
}
|
|
s_led_enable |= enabler;
|
|
}
|
|
|
|
// TODO: PBL-36077 Move to a generic 4v5 disable
|
|
void led_disable(LEDEnabler enabler) {
|
|
s_led_enable &= ~enabler;
|
|
if (s_led_enable == 0) {
|
|
gpio_output_set(&BOARD_CONFIG_BACKLIGHT.ctl, false);
|
|
}
|
|
}
|
|
|
|
void backlight_set_brightness(uint16_t brightness) {
|
|
if (BOARD_CONFIG_BACKLIGHT.options & ActuatorOptions_Ctl) {
|
|
if (brightness == 0) {
|
|
led_disable(LEDEnablerBacklight);
|
|
} else {
|
|
led_enable(LEDEnablerBacklight);
|
|
}
|
|
}
|
|
|
|
if (BOARD_CONFIG_BACKLIGHT.options & ActuatorOptions_Pwm) {
|
|
if (brightness == 0) {
|
|
if (s_backlight_pwm_enabled) {
|
|
prv_backlight_pwm_enable(false);
|
|
}
|
|
PWR_TRACK_BACKLIGHT("OFF", PWM_OUTPUT_FREQUENCY_HZ, 0);
|
|
} else {
|
|
if (!s_backlight_pwm_enabled) {
|
|
prv_backlight_pwm_enable(true);
|
|
}
|
|
|
|
// By setting higher values in the TIM_Pulse register, we're causing the output waveform
|
|
// to be low for a longer period of time, which causes the backlight to be brighter.
|
|
//
|
|
// The brightness value has a range of 0 to 0x3fff which is 2^15. The period of the timer
|
|
// counter is 2^10. We want to rescale the brightness range into a subset of the timer
|
|
// counter range. Different boards will have a different duty cycle that represent the
|
|
// "fully on" state.
|
|
const uint32_t pwm_scaling_factor = BACKLIGHT_BRIGHTNESS_MAX / TIMER_PERIOD_RESOLUTION;
|
|
const uint32_t desired_duty_cycle = brightness * BOARD_CONFIG.backlight_max_duty_cycle_percent
|
|
/ pwm_scaling_factor / 100;
|
|
pwm_set_duty_cycle(&BOARD_CONFIG_BACKLIGHT.pwm, desired_duty_cycle);
|
|
PWR_TRACK_BACKLIGHT("ON", PWM_OUTPUT_FREQUENCY_HZ,
|
|
(desired_duty_cycle * 100) / TIMER_PERIOD_RESOLUTION);
|
|
}
|
|
}
|
|
|
|
if (BOARD_CONFIG_BACKLIGHT.options & ActuatorOptions_IssiI2C) {
|
|
led_controller_backlight_set_brightness(brightness >> 8);
|
|
}
|
|
}
|
|
|
|
void command_backlight_ctl(const char *arg) {
|
|
const int bright_percent = atoi(arg);
|
|
if (bright_percent < 0 || bright_percent > 100) {
|
|
prompt_send_response("Invalid Brightness");
|
|
return;
|
|
}
|
|
backlight_set_brightness((BACKLIGHT_BRIGHTNESS_MAX * bright_percent) / 100);
|
|
prompt_send_response("OK");
|
|
}
|