LowPower
LowPowerRP2040.h
1 #pragma once
2 
3 #include "LowPowerCommon.h"
4 #include "drivers/rp2040/pico_sleep.h"
5 #include "hardware/vreg.h"
6 #include "vector"
7 
8 namespace low_power {
9 
10 class ArduinoLowPowerRP2040;
11 static ArduinoLowPowerRP2040 *selfArduinoLowPowerRP2040 = nullptr;
12 
29  public:
30  ArduinoLowPowerRP2040() { selfArduinoLowPowerRP2040 = this; }
31 
32  bool isProcessingOnSleep(sleep_mode_enum_t sleep_mode) {
33  bool result = false;
34  switch (sleep_mode) {
35  case sleep_mode_enum_t::modemSleep:
36  result = true;
37  break;
38  case sleep_mode_enum_t::noSleep:
39  result = true;
40  break;
41  case sleep_mode_enum_t::lightSleep:
42  result = true;
43  break;
44  case sleep_mode_enum_t::deepSleep:
45  result = false;
46  break;
47  }
48  return result;
49  }
50 
52  bool sleep(void) override {
53  bool rc = false;
54  switch (sleep_mode) {
55  case sleep_mode_enum_t::lightSleep: {
56  int idx = 0;
57  for (auto pin : wakeup_pins) {
58  attachInterrupt(digitalPinToInterrupt(pin.pin), interrupt_cb,
59  toMode(pin.change_type));
60  is_wait_for_pin = true;
61  }
62  if (is_wait_for_pin) {
63  light_sleep_begin();
64  while (is_wait_for_pin) delay(timer_update_delay);
65  light_sleep_end();
66  } else {
67  light_sleep();
68  }
69  } break;
70 
71  case sleep_mode_enum_t::deepSleep: {
72  // use wakup pins
73  if (wakeup_pins.size() >= 0) {
74  if (wakeup_pins.size() > 1) return false;
75  if (wakeup_pins[0].change_type == pin_change_t::on_high) {
76  sleep_goto_dormant_until_edge_high(wakeup_pins[0].pin);
77  } else {
78  sleep_goto_dormant_until_edge_low(wakeup_pins[0].pin);
79  }
80  } else if (sleep_time_us > 0) {
81  // use time to sleep
82  sleep_goto_sleep_for(sleep_time_us / 1000, timer_cb);
83  if (is_restart) rp2040.reboot();
84 
85  } else {
86  // no wakeup !
87  processor_deep_sleep();
88  if (is_restart) rp2040.reboot();
89  }
90  return true;
91  }
92 
93  case sleep_mode_enum_t::modemSleep:
94  delay(sleep_time_us / 1000);
95  return true;
96 
97  case sleep_mode_enum_t::noSleep:
98  delay(sleep_time_us / 1000);
99  light_sleep_end();
100  return true;
101  }
102 
103  return false;
104  }
105 
106  bool setSleepTime(uint32_t time, time_unit_t time_unit_type) override {
107  sleep_time_us = (toUs(time, time_unit_type));
108  return sleep_mode == sleep_mode_enum_t::lightSleep ||
109  wakeup_pins.size() == 0;
110  }
111 
112  bool addWakeupPin(int pin, pin_change_t change_type) override {
113  PinChangeDef pin_change_def{pin, change_type};
114  wakeup_pins.push_back(pin_change_def);
115  return sleep_mode == sleep_mode_enum_t::lightSleep || sleep_time_us == 0;
116  }
117 
118  void clear() {
120  sleep_time_us = 0;
121  is_wait_for_pin = false;
122  wakeup_pins.clear();
123  }
124 
126  void setRestart(bool flag) { is_restart = flag; }
127 
128  protected:
129  struct PinChangeDef {
130  int pin;
131  pin_change_t change_type;
132  PinChangeDef(int p, pin_change_t ct) {
133  pin = p;
134  change_type = ct;
135  }
136  };
137  std::vector<PinChangeDef> wakeup_pins;
138  uint64_t sleep_time_us = 0;
139  bool is_wait_for_pin = false;
140  bool is_restart = false;
141  int timer_update_delay = 2;
142 
143  static void timer_cb(unsigned int) {}
144 
145  static void interrupt_cb() {
146  selfArduinoLowPowerRP2040->is_wait_for_pin = false;
147  if (selfArduinoLowPowerRP2040->is_restart) rp2040.reboot();
148  }
149 
150  PinStatus toMode(pin_change_t ct) {
151  switch (ct) {
152  case pin_change_t::on_high:
153  return RISING;
154  case pin_change_t::on_low:
155  return FALLING;
156  default:
157  return CHANGE;
158  }
159  }
160 
161  void light_sleep() {
162  light_sleep_begin();
163  delay(sleep_time_us / 1000);
164  light_sleep_end();
165  }
166 
167  void light_sleep_begin() {
168  delay(timer_update_delay);
169  set_sys_clock_khz(
170  10000,
171  false); // Set System clock to 10 MHz, sys_clock_khz(10000,
172  // true); did not work for me
173  delay(timer_update_delay);
174  vreg_set_voltage(VREG_VOLTAGE_0_95); // 0.85V did not work, 0.95V
175  // seetime_unit_t::ms pretty stable
176  }
177  void light_sleep_end() {
178  if (is_restart) rp2040.reboot();
179  vreg_set_voltage(
180  VREG_VOLTAGE_DEFAULT); // corresponds to 1.10V, not sure if
181  // that is really required for 48 MHz
182  set_sys_clock_48mhz(); // Set System clock back to 48 MHz
183  delay(timer_update_delay);
184  }
185 
186  inline void sleep_goto_dormant_until_edge_high(uint gpio_pin) {
187  sleep_goto_dormant_until_pin(gpio_pin, true, true);
188  }
189  inline void sleep_goto_dormant_until_edge_low(uint gpio_pin) {
190  sleep_goto_dormant_until_pin(gpio_pin, true, false);
191  }
192 };
193 
194 static ArduinoLowPowerRP2040 LowPower;
195 
196 } // namespace low_power
Common API for power saving modes for different processor architectures.
Definition: LowPowerCommon.h:33
virtual void clear()
reset the processing
Definition: LowPowerCommon.h:117
Low Power Management for RP2040: In lightSleep we just reduce the processor system clock and voltage....
Definition: LowPowerRP2040.h:28
bool isProcessingOnSleep(sleep_mode_enum_t sleep_mode)
Returns true if processing is possible in the current sleep mode.
Definition: LowPowerRP2040.h:32
bool setSleepTime(uint32_t time, time_unit_t time_unit_type) override
Defines the sleep time.
Definition: LowPowerRP2040.h:106
void setRestart(bool flag)
We force a restart after we wake up from sleep.
Definition: LowPowerRP2040.h:126
bool sleep(void) override
sets processor into sleep mode
Definition: LowPowerRP2040.h:52
bool addWakeupPin(int pin, pin_change_t change_type) override
Defines the wakup pin.
Definition: LowPowerRP2040.h:112
void clear()
reset the processing
Definition: LowPowerRP2040.h:118
Definition: LowPowerRP2040.h:129