2
2
#include < hal/nrf_gpio.h>
3
3
#include " displayapp/screens/Symbols.h"
4
4
#include " drivers/PinMap.h"
5
+ #include < libraries/delay/nrf_delay.h>
5
6
using namespace Pinetime ::Controllers;
6
7
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
+
7
13
void BrightnessController::Init () {
8
14
nrf_gpio_cfg_output (PinMap::LcdBacklightLow);
9
15
nrf_gpio_cfg_output (PinMap::LcdBacklightMedium);
@@ -13,55 +19,56 @@ void BrightnessController::Init() {
13
19
nrf_gpio_pin_clear (PinMap::LcdBacklightMedium);
14
20
nrf_gpio_pin_clear (PinMap::LcdBacklightHigh);
15
21
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 ;
30
30
Set (level);
31
31
}
32
32
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
+
34
40
uint8_t pin;
35
- if (val > 2000 ) {
36
- val -= 2000 ;
41
+ if (rawBrightness > 2 * timerPeriod ) {
42
+ rawBrightness -= 2 * timerPeriod ;
37
43
pin = PinMap::LcdBacklightHigh;
38
- } else if (val > 1000 ) {
39
- val -= 1000 ;
44
+ } else if (rawBrightness > timerPeriod ) {
45
+ rawBrightness -= timerPeriod ;
40
46
pin = PinMap::LcdBacklightMedium;
41
47
} else {
42
48
pin = PinMap::LcdBacklightLow;
43
49
}
44
- if (val == 1000 || val == 0 ) {
50
+ if (rawBrightness == timerPeriod || rawBrightness == 0 ) {
45
51
if (lastPin != UNSET) {
46
- nrfx_timer_disable (&timer) ;
47
- nrfx_timer_clear (&timer );
52
+ RTC-> TASKS_STOP = 1 ;
53
+ nrf_delay_us (rtcStopTime );
48
54
nrf_ppi_channel_disable (ppiBacklightOff);
49
55
nrf_ppi_channel_disable (ppiBacklightOn);
50
56
nrfx_gpiote_out_uninit (lastPin);
51
57
nrf_gpio_cfg_output (lastPin);
52
58
}
53
59
lastPin = UNSET;
54
- if (val == 0 ) {
60
+ if (rawBrightness == 0 ) {
55
61
nrf_gpio_pin_set (pin);
56
62
} else {
57
63
nrf_gpio_pin_clear (pin);
58
64
}
59
65
} 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
61
68
if (lastPin != pin) {
62
69
if (lastPin != UNSET) {
63
- nrfx_timer_disable (&timer) ;
64
- nrfx_timer_clear (&timer );
70
+ RTC-> TASKS_STOP = 1 ;
71
+ nrf_delay_us (rtcStopTime );
65
72
nrf_ppi_channel_disable (ppiBacklightOff);
66
73
nrf_ppi_channel_disable (ppiBacklightOn);
67
74
nrfx_gpiote_out_uninit (lastPin);
@@ -73,33 +80,25 @@ void BrightnessController::ApplyBrightness(uint16_t val) {
73
80
APP_ERROR_CHECK (nrfx_gpiote_out_init (pin, &gpioteCfg));
74
81
nrfx_gpiote_out_task_enable (pin);
75
82
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 ] ),
77
84
nrfx_gpiote_out_task_addr_get (pin));
78
85
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 ] ),
80
87
nrfx_gpiote_out_task_addr_get (pin));
88
+ nrf_ppi_fork_endpoint_setup (ppiBacklightOn, reinterpret_cast <uint32_t >(&RTC->TASKS_CLEAR ));
81
89
nrf_ppi_channel_enable (ppiBacklightOff);
82
90
nrf_ppi_channel_enable (ppiBacklightOn);
83
91
} 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 );
102
97
}
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 ;
103
102
lastPin = pin;
104
103
}
105
104
switch (pin) {
@@ -122,16 +121,16 @@ void BrightnessController::Set(BrightnessController::Levels level) {
122
121
switch (level) {
123
122
default :
124
123
case Levels::High:
125
- ApplyBrightness (3000 );
124
+ ApplyBrightness (3 * timerPeriod );
126
125
break ;
127
126
case Levels::Medium:
128
- ApplyBrightness (2000 );
127
+ ApplyBrightness (2 * timerPeriod );
129
128
break ;
130
129
case Levels::Low:
131
- ApplyBrightness (1000 );
130
+ ApplyBrightness (timerPeriod );
132
131
break ;
133
132
case Levels::AlwaysOn:
134
- ApplyBrightness (100 );
133
+ ApplyBrightness (timerPeriod / 10 );
135
134
break ;
136
135
case Levels::Off:
137
136
ApplyBrightness (0 );
0 commit comments