aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/brightness/BrightnessController.cpp
diff options
context:
space:
mode:
authormark9064 <30447455+mark9064@users.noreply.github.com>2024-01-22 23:37:52 +0000
committerJF <JF002@users.noreply.github.com>2024-08-05 20:32:43 +0200
commit3dca742b6566859aee89e1b943ae2ea5fc0eaa95 (patch)
tree8520a6872ede1d78475211000d319b5e40b255c3 /src/components/brightness/BrightnessController.cpp
parent20ac7e8df38836d0a72c378b900b99c827c996c2 (diff)
aod: PPI/RTC-based backlight brightness
Diffstat (limited to 'src/components/brightness/BrightnessController.cpp')
-rw-r--r--src/components/brightness/BrightnessController.cpp124
1 files changed, 112 insertions, 12 deletions
diff --git a/src/components/brightness/BrightnessController.cpp b/src/components/brightness/BrightnessController.cpp
index 0392158c..4d1eba6a 100644
--- a/src/components/brightness/BrightnessController.cpp
+++ b/src/components/brightness/BrightnessController.cpp
@@ -2,38 +2,138 @@
#include <hal/nrf_gpio.h>
#include "displayapp/screens/Symbols.h"
#include "drivers/PinMap.h"
+#include <libraries/delay/nrf_delay.h>
using namespace Pinetime::Controllers;
+namespace {
+ // reinterpret_cast is not constexpr so this is the best we can do
+ static NRF_RTC_Type* const RTC = reinterpret_cast<NRF_RTC_Type*>(NRF_RTC2_BASE);
+}
+
void BrightnessController::Init() {
nrf_gpio_cfg_output(PinMap::LcdBacklightLow);
nrf_gpio_cfg_output(PinMap::LcdBacklightMedium);
nrf_gpio_cfg_output(PinMap::LcdBacklightHigh);
+
+ nrf_gpio_pin_clear(PinMap::LcdBacklightLow);
+ nrf_gpio_pin_clear(PinMap::LcdBacklightMedium);
+ nrf_gpio_pin_clear(PinMap::LcdBacklightHigh);
+
+ static_assert(timerFrequency == 32768, "Change the prescaler below");
+ RTC->PRESCALER = 0;
+ // CC1 switches the backlight on (pin transitions from high to low) and resets the counter to 0
+ RTC->CC[1] = timerPeriod;
+ // Enable compare events for CC0,CC1
+ RTC->EVTEN = 0b0000'0000'0000'0011'0000'0000'0000'0000;
+ // Disable all interrupts
+ RTC->INTENCLR = 0b0000'0000'0000'1111'0000'0000'0000'0011;
Set(level);
}
+void BrightnessController::ApplyBrightness(uint16_t rawBrightness) {
+ // The classic off, low, medium, high brightnesses are at {0, timerPeriod, timerPeriod*2, timerPeriod*3}
+ // These brightness levels do not use PWM: they only set/clear the corresponding pins
+ // Any brightness level between the above levels is achieved with efficient RTC based PWM on the next pin up
+ // E.g 2.5*timerPeriod corresponds to medium brightness with 50% PWM on the high pin
+ // Note: Raw brightness does not necessarily correspond to a linear perceived brightness
+
+ uint8_t pin;
+ if (rawBrightness > 2 * timerPeriod) {
+ rawBrightness -= 2 * timerPeriod;
+ pin = PinMap::LcdBacklightHigh;
+ } else if (rawBrightness > timerPeriod) {
+ rawBrightness -= timerPeriod;
+ pin = PinMap::LcdBacklightMedium;
+ } else {
+ pin = PinMap::LcdBacklightLow;
+ }
+ if (rawBrightness == timerPeriod || rawBrightness == 0) {
+ if (lastPin != UNSET) {
+ RTC->TASKS_STOP = 1;
+ nrf_delay_us(rtcStopTime);
+ nrf_ppi_channel_disable(ppiBacklightOff);
+ nrf_ppi_channel_disable(ppiBacklightOn);
+ nrfx_gpiote_out_uninit(lastPin);
+ nrf_gpio_cfg_output(lastPin);
+ }
+ lastPin = UNSET;
+ if (rawBrightness == 0) {
+ nrf_gpio_pin_set(pin);
+ } else {
+ nrf_gpio_pin_clear(pin);
+ }
+ } else {
+ // If the pin on which we are doing PWM is changing
+ // Disable old PWM channel (if exists) and set up new one
+ if (lastPin != pin) {
+ if (lastPin != UNSET) {
+ RTC->TASKS_STOP = 1;
+ nrf_delay_us(rtcStopTime);
+ nrf_ppi_channel_disable(ppiBacklightOff);
+ nrf_ppi_channel_disable(ppiBacklightOn);
+ nrfx_gpiote_out_uninit(lastPin);
+ nrf_gpio_cfg_output(lastPin);
+ }
+ nrfx_gpiote_out_config_t gpioteCfg = {.action = NRF_GPIOTE_POLARITY_TOGGLE,
+ .init_state = NRF_GPIOTE_INITIAL_VALUE_LOW,
+ .task_pin = true};
+ APP_ERROR_CHECK(nrfx_gpiote_out_init(pin, &gpioteCfg));
+ nrfx_gpiote_out_task_enable(pin);
+ nrf_ppi_channel_endpoint_setup(ppiBacklightOff,
+ reinterpret_cast<uint32_t>(&RTC->EVENTS_COMPARE[0]),
+ nrfx_gpiote_out_task_addr_get(pin));
+ nrf_ppi_channel_endpoint_setup(ppiBacklightOn,
+ reinterpret_cast<uint32_t>(&RTC->EVENTS_COMPARE[1]),
+ nrfx_gpiote_out_task_addr_get(pin));
+ nrf_ppi_fork_endpoint_setup(ppiBacklightOn, reinterpret_cast<uint32_t>(&RTC->TASKS_CLEAR));
+ nrf_ppi_channel_enable(ppiBacklightOff);
+ nrf_ppi_channel_enable(ppiBacklightOn);
+ } else {
+ // If the pin used for PWM isn't changing, we only need to set the pin state to the initial value (low)
+ RTC->TASKS_STOP = 1;
+ nrf_delay_us(rtcStopTime);
+ // Due to errata 20,179 and the intricacies of RTC timing, keep it simple: override the pin state
+ nrfx_gpiote_out_task_force(pin, false);
+ }
+ // CC0 switches the backlight off (pin transitions from low to high)
+ RTC->CC[0] = rawBrightness;
+ RTC->TASKS_CLEAR = 1;
+ RTC->TASKS_START = 1;
+ lastPin = pin;
+ }
+ switch (pin) {
+ case PinMap::LcdBacklightHigh:
+ nrf_gpio_pin_clear(PinMap::LcdBacklightLow);
+ nrf_gpio_pin_clear(PinMap::LcdBacklightMedium);
+ break;
+ case PinMap::LcdBacklightMedium:
+ nrf_gpio_pin_clear(PinMap::LcdBacklightLow);
+ nrf_gpio_pin_set(PinMap::LcdBacklightHigh);
+ break;
+ case PinMap::LcdBacklightLow:
+ nrf_gpio_pin_set(PinMap::LcdBacklightMedium);
+ nrf_gpio_pin_set(PinMap::LcdBacklightHigh);
+ }
+}
+
void BrightnessController::Set(BrightnessController::Levels level) {
this->level = level;
switch (level) {
default:
case Levels::High:
- nrf_gpio_pin_clear(PinMap::LcdBacklightLow);
- nrf_gpio_pin_clear(PinMap::LcdBacklightMedium);
- nrf_gpio_pin_clear(PinMap::LcdBacklightHigh);
+ ApplyBrightness(3 * timerPeriod);
break;
case Levels::Medium:
- nrf_gpio_pin_clear(PinMap::LcdBacklightLow);
- nrf_gpio_pin_clear(PinMap::LcdBacklightMedium);
- nrf_gpio_pin_set(PinMap::LcdBacklightHigh);
+ ApplyBrightness(2 * timerPeriod);
break;
case Levels::Low:
- nrf_gpio_pin_clear(PinMap::LcdBacklightLow);
- nrf_gpio_pin_set(PinMap::LcdBacklightMedium);
- nrf_gpio_pin_set(PinMap::LcdBacklightHigh);
+ ApplyBrightness(timerPeriod);
+ break;
+ case Levels::AlwaysOn:
+ ApplyBrightness(timerPeriod / 10);
break;
case Levels::Off:
- nrf_gpio_pin_set(PinMap::LcdBacklightLow);
- nrf_gpio_pin_set(PinMap::LcdBacklightMedium);
- nrf_gpio_pin_set(PinMap::LcdBacklightHigh);
+ ApplyBrightness(0);
break;
}
}