Skip to content

Commit e3afe07

Browse files
committed
aod: RTC based backlight control
1 parent be98013 commit e3afe07

File tree

4 files changed

+61
-56
lines changed

4 files changed

+61
-56
lines changed

src/CMakeLists.txt

-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ set(SDK_SOURCE_FILES
4040
"${NRF5_SDK_PATH}/integration/nrfx/legacy/nrf_drv_clock.h"
4141
"${NRF5_SDK_PATH}/modules/nrfx/drivers/src/nrfx_clock.c"
4242
"${NRF5_SDK_PATH}/modules/nrfx/drivers/src/nrfx_gpiote.c"
43-
"${NRF5_SDK_PATH}/modules/nrfx/drivers/src/nrfx_timer.c"
4443
"${NRF5_SDK_PATH}/modules/nrfx/soc/nrfx_atomic.c"
4544
"${NRF5_SDK_PATH}/modules/nrfx/drivers/src/nrfx_saadc.c"
4645

src/components/brightness/BrightnessController.cpp

+49-50
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,14 @@
22
#include <hal/nrf_gpio.h>
33
#include "displayapp/screens/Symbols.h"
44
#include "drivers/PinMap.h"
5+
#include <libraries/delay/nrf_delay.h>
56
using namespace Pinetime::Controllers;
67

8+
namespace {
9+
// reinterpret_cast is not constexpr so this is the best we can do
10+
static NRF_RTC_Type* const RTC = reinterpret_cast<NRF_RTC_Type*>(NRF_RTC2_BASE);
11+
}
12+
713
void BrightnessController::Init() {
814
nrf_gpio_cfg_output(PinMap::LcdBacklightLow);
915
nrf_gpio_cfg_output(PinMap::LcdBacklightMedium);
@@ -13,55 +19,56 @@ void BrightnessController::Init() {
1319
nrf_gpio_pin_clear(PinMap::LcdBacklightMedium);
1420
nrf_gpio_pin_clear(PinMap::LcdBacklightHigh);
1521

16-
timer = NRFX_TIMER_INSTANCE(1);
17-
nrfx_timer_config_t timerCfg = {.frequency = NRF_TIMER_FREQ_1MHz,
18-
.mode = NRF_TIMER_MODE_TIMER,
19-
.bit_width = NRF_TIMER_BIT_WIDTH_32,
20-
.interrupt_priority = 6,
21-
.p_context = nullptr};
22-
// Callback will never fire, use empty expression
23-
APP_ERROR_CHECK(nrfx_timer_init(&timer, &timerCfg, [](auto, auto) {
24-
}));
25-
nrfx_timer_extended_compare(&timer,
26-
NRF_TIMER_CC_CHANNEL1,
27-
nrfx_timer_us_to_ticks(&timer, 1000),
28-
NRF_TIMER_SHORT_COMPARE1_CLEAR_MASK,
29-
false);
22+
static_assert(timerFrequency == 32768, "Change the prescaler below");
23+
RTC->PRESCALER = 0;
24+
// CC1 switches the backlight on (pin transitions from high to low) and resets the counter to 0
25+
RTC->CC[1] = timerPeriod;
26+
// Enable compare events for CC0,CC1
27+
RTC->EVTEN = 0b0000'0000'0000'0011'0000'0000'0000'0000;
28+
// Disable all interrupts
29+
RTC->INTENCLR = 0b0000'0000'0000'1111'0000'0000'0000'0011;
3030
Set(level);
3131
}
3232

33-
void BrightnessController::ApplyBrightness(uint16_t val) {
33+
void BrightnessController::ApplyBrightness(uint16_t rawBrightness) {
34+
// The classic off, low, medium, high brightnesses are at {0, timerPeriod, timerPeriod*2, timerPeriod*3}
35+
// These brightness levels do not use PWM: they only set/clear the corresponding pins
36+
// Any brightness level between the above levels is achieved with efficient RTC based PWM on the next pin up
37+
// E.g 2.5*timerPeriod corresponds to medium brightness with 50% PWM on the high pin
38+
// Note: Raw brightness does not necessarily correspond to a linear perceived brightness
39+
3440
uint8_t pin;
35-
if (val > 2000) {
36-
val -= 2000;
41+
if (rawBrightness > 2 * timerPeriod) {
42+
rawBrightness -= 2 * timerPeriod;
3743
pin = PinMap::LcdBacklightHigh;
38-
} else if (val > 1000) {
39-
val -= 1000;
44+
} else if (rawBrightness > timerPeriod) {
45+
rawBrightness -= timerPeriod;
4046
pin = PinMap::LcdBacklightMedium;
4147
} else {
4248
pin = PinMap::LcdBacklightLow;
4349
}
44-
if (val == 1000 || val == 0) {
50+
if (rawBrightness == timerPeriod || rawBrightness == 0) {
4551
if (lastPin != UNSET) {
46-
nrfx_timer_disable(&timer);
47-
nrfx_timer_clear(&timer);
52+
RTC->TASKS_STOP = 1;
53+
nrf_delay_us(rtcStopTime);
4854
nrf_ppi_channel_disable(ppiBacklightOff);
4955
nrf_ppi_channel_disable(ppiBacklightOn);
5056
nrfx_gpiote_out_uninit(lastPin);
5157
nrf_gpio_cfg_output(lastPin);
5258
}
5359
lastPin = UNSET;
54-
if (val == 0) {
60+
if (rawBrightness == 0) {
5561
nrf_gpio_pin_set(pin);
5662
} else {
5763
nrf_gpio_pin_clear(pin);
5864
}
5965
} else {
60-
uint32_t newThreshold = nrfx_timer_us_to_ticks(&timer, val);
66+
// If the pin on which we are doing PWM is changing
67+
// Disable old PWM channel (if exists) and set up new one
6168
if (lastPin != pin) {
6269
if (lastPin != UNSET) {
63-
nrfx_timer_disable(&timer);
64-
nrfx_timer_clear(&timer);
70+
RTC->TASKS_STOP = 1;
71+
nrf_delay_us(rtcStopTime);
6572
nrf_ppi_channel_disable(ppiBacklightOff);
6673
nrf_ppi_channel_disable(ppiBacklightOn);
6774
nrfx_gpiote_out_uninit(lastPin);
@@ -73,33 +80,25 @@ void BrightnessController::ApplyBrightness(uint16_t val) {
7380
APP_ERROR_CHECK(nrfx_gpiote_out_init(pin, &gpioteCfg));
7481
nrfx_gpiote_out_task_enable(pin);
7582
nrf_ppi_channel_endpoint_setup(ppiBacklightOff,
76-
nrfx_timer_event_address_get(&timer, NRF_TIMER_EVENT_COMPARE0),
83+
reinterpret_cast<uint32_t>(&RTC->EVENTS_COMPARE[0]),
7784
nrfx_gpiote_out_task_addr_get(pin));
7885
nrf_ppi_channel_endpoint_setup(ppiBacklightOn,
79-
nrfx_timer_event_address_get(&timer, NRF_TIMER_EVENT_COMPARE1),
86+
reinterpret_cast<uint32_t>(&RTC->EVENTS_COMPARE[1]),
8087
nrfx_gpiote_out_task_addr_get(pin));
88+
nrf_ppi_fork_endpoint_setup(ppiBacklightOn, reinterpret_cast<uint32_t>(&RTC->TASKS_CLEAR));
8189
nrf_ppi_channel_enable(ppiBacklightOff);
8290
nrf_ppi_channel_enable(ppiBacklightOn);
8391
} else {
84-
// Pause the timer, check where it is before changing the threshold
85-
// If we have already triggered CC0 and then move CC0 into the future
86-
// without triggering CC1 first, the modulation will be inverted
87-
// (e.g on 100us off 900us becomes off 100us on 900us)
88-
nrfx_timer_pause(&timer);
89-
uint32_t currentCycle = nrfx_timer_capture(&timer, NRF_TIMER_CC_CHANNEL2);
90-
uint32_t oldThreshold = nrfx_timer_capture_get(&timer, NRF_TIMER_CC_CHANNEL0);
91-
// If the new threshold now in future and we have already triggered the old threshold, switch backlight back on
92-
// If we haven't yet triggered CC0 and then move CC0 into the past, we get the same issue: switch the backlight off
93-
if ((currentCycle >= oldThreshold && newThreshold > currentCycle) || (currentCycle < oldThreshold && newThreshold <= currentCycle)) {
94-
nrfx_gpiote_out_task_trigger(pin);
95-
}
96-
}
97-
nrfx_timer_compare(&timer, NRF_TIMER_CC_CHANNEL0, newThreshold, false);
98-
if (lastPin != pin) {
99-
nrfx_timer_enable(&timer);
100-
} else {
101-
nrfx_timer_resume(&timer);
92+
// If the pin used for PWM isn't changing, we only need to set the pin state to the initial value (low)
93+
RTC->TASKS_STOP = 1;
94+
nrf_delay_us(rtcStopTime);
95+
// Due to errata 20,179 and the intricacies of RTC timing, keep it simple: override the pin state
96+
nrfx_gpiote_out_task_force(pin, false);
10297
}
98+
// CC0 switches the backlight off (pin transitions from low to high)
99+
RTC->CC[0] = rawBrightness;
100+
RTC->TASKS_CLEAR = 1;
101+
RTC->TASKS_START = 1;
103102
lastPin = pin;
104103
}
105104
switch (pin) {
@@ -122,16 +121,16 @@ void BrightnessController::Set(BrightnessController::Levels level) {
122121
switch (level) {
123122
default:
124123
case Levels::High:
125-
ApplyBrightness(3000);
124+
ApplyBrightness(3 * timerPeriod);
126125
break;
127126
case Levels::Medium:
128-
ApplyBrightness(2000);
127+
ApplyBrightness(2 * timerPeriod);
129128
break;
130129
case Levels::Low:
131-
ApplyBrightness(1000);
130+
ApplyBrightness(timerPeriod);
132131
break;
133132
case Levels::AlwaysOn:
134-
ApplyBrightness(100);
133+
ApplyBrightness(timerPeriod / 10);
135134
break;
136135
case Levels::Off:
137136
ApplyBrightness(0);

src/components/brightness/BrightnessController.h

+9-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
#include <cstdint>
44

55
#include "nrf_ppi.h"
6-
#include "nrfx_timer.h"
76
#include "nrfx_gpiote.h"
87

98
namespace Pinetime {
@@ -26,7 +25,15 @@ namespace Pinetime {
2625
Levels level = Levels::High;
2726
static constexpr uint8_t UNSET = UINT8_MAX;
2827
uint8_t lastPin = UNSET;
29-
nrfx_timer_t timer;
28+
// Maximum time (μs) it takes for the RTC to fully stop
29+
static constexpr uint8_t rtcStopTime = 46;
30+
// Frequency of timer used for PWM (Hz)
31+
static constexpr uint16_t timerFrequency = 32768;
32+
// Backlight PWM frequency (Hz)
33+
static constexpr uint16_t pwmFreq = 1000;
34+
// Wraparound point in timer ticks
35+
// Defines the number of brightness levels between each pin
36+
static constexpr uint16_t timerPeriod = timerFrequency / pwmFreq;
3037
// Warning: nimble reserves some PPIs
3138
// https://github.com/InfiniTimeOrg/InfiniTime/blob/034d83fe6baf1ab3875a34f8cee387e24410a824/src/libs/mynewt-nimble/nimble/drivers/nrf52/src/ble_phy.c#L53
3239
// SpiMaster uses PPI 0 for an erratum workaround

src/sdk_config.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -4678,7 +4678,7 @@
46784678
// <q> NRFX_TIMER1_ENABLED - Enable TIMER1 instance
46794679

46804680
#ifndef NRFX_TIMER1_ENABLED
4681-
#define NRFX_TIMER1_ENABLED 1
4681+
#define NRFX_TIMER1_ENABLED 0
46824682
#endif
46834683

46844684
// <q> NRFX_TIMER2_ENABLED - Enable TIMER2 instance
@@ -6319,7 +6319,7 @@
63196319
// <e> TIMER_ENABLED - nrf_drv_timer - TIMER periperal driver - legacy layer
63206320
//==========================================================
63216321
#ifndef TIMER_ENABLED
6322-
#define TIMER_ENABLED 1
6322+
#define TIMER_ENABLED 0
63236323
#endif
63246324
// <o> TIMER_DEFAULT_CONFIG_FREQUENCY - Timer frequency if in Timer mode
63256325

@@ -6383,7 +6383,7 @@
63836383
// <q> TIMER1_ENABLED - Enable TIMER1 instance
63846384

63856385
#ifndef TIMER1_ENABLED
6386-
#define TIMER1_ENABLED 1
6386+
#define TIMER1_ENABLED 0
63876387
#endif
63886388

63896389
// <q> TIMER2_ENABLED - Enable TIMER2 instance

0 commit comments

Comments
 (0)