Processing math: 100%

PM module reference

Header :
#include <pm.h>
Makefile : pm is already included by default

Description

The PM (Power Manager) has two main purposes : routing clocks from clock sources to peripherals, and managing the reset controller of the device.

API

General-purpose

void setMainClockSource(MainClockSource clockSource, unsigned long cpudiv=0)
The main clock is used by the CPU and the peripheral buses and can be connected to any of the clock sources listed in MainClockSource. If cpudiv is greater than 0, the CPU frequency will be equal to the main clock frequency divided by cpudiv.
unsigned long getMainClockFrequency()
Return the frequency of the main clock.
unsigned long getCPUClockFrequency()
Return the frequency of the CPU clock.
unsigned long getModuleClockFrequency(uint8_t peripheral)
Return the frequency of the specified peripheral. See the CLK_* constants in the header for more details.
void enablePeripheralClock(uint8_t peripheral)
Enable the clock for the specified peripheral. See the CLK_* constants in the header for more details.
void enablePBADivClock(uint8_t pow)
Enable the divided PBA peripheral clock 2pow, with pow between 1 and 7.

Wake-up and reset

ResetCause resetCause()
Return the cause of the last reset among :
  • POR : power-on reset (default)
  • BOD : brown-out detector, there was probably a glitch in the power supply
  • EXT : external reset using the dedicated RESET_N pin
  • WDT : watchdog timer reset
  • BKUP : reset from backup (low-power) mode when a wake up source occurs
  • SYSRESETREQ : reset using the Core::reset() function
  • POR33 : 3.3V power-on reset
  • BOD33 : 3.3V brown-out detector
void enableWakeUpSource(WakeUpSource src)
Enable a wake-up source for a peripheral among :
  • TWIS0
  • TWIS1
  • USBC
  • PSOK
  • BOD18
  • BOD33
  • PICOUART
  • LCDA
void disableWakeUpSource(WakeUpSource src)
Disable a wake-up source for a peripheral.
void disableWakeUpSources()
Disable all wake-up sources.

Interrupts

void enableInterrupt(void (*handler)(), Interrupt interrupt=Interrupt::PSOK)
Enable the specified interrupt to call the specified handler.
void disableInterrupt(Interrupt interrupt=Interrupt::PSOK)
Disable the specified interrupt.

Hacking

This module doesn't cover all the features offered by the peripheral. The different clocks could be differentiated and run at different frequencies in order to reduce power consumption. There are a few more settings that could be used or customized, such as the Peripheral Power Control Register or the Fast Sleep Register.

Code

Header

  1. #ifndef _PM_H_
  2. #define _PM_H_
  3.  
  4. #include <stdint.h>
  5.  
  6. // Power Manager
  7. // This module controls the clock gating from clock sources to peripherals
  8. // as well as the reset logic.
  9. namespace PM {
  10.  
  11. // Peripheral memory space base address
  12. const uint32_t BASE = 0x400E0000;
  13.  
  14.  
  15. // Registers addresses
  16. const uint32_t OFFSET_MCCTRL = 0x000; // Main Clock Control
  17. const uint32_t OFFSET_CPUSEL = 0x004; // CPU Clock Select
  18. const uint32_t OFFSET_PBASEL = 0x00C; // PBA Clock Select
  19. const uint32_t OFFSET_PBBSEL = 0x010; // PBB Clock Select
  20. const uint32_t OFFSET_PBCSEL = 0x014; // PBC Clock Select
  21. const uint32_t OFFSET_PBDSEL = 0x018; // PBD Clock Select
  22. const uint32_t OFFSET_CPUMASK = 0x020; // CPU Mask
  23. const uint32_t OFFSET_HSBMASK = 0x024; // HSB Mask
  24. const uint32_t OFFSET_PBAMASK = 0x028; // PBA Mask
  25. const uint32_t OFFSET_PBBMASK = 0x02C; // PBB Mask
  26. const uint32_t OFFSET_PBCMASK = 0x030; // PBC Mask
  27. const uint32_t OFFSET_PBDMASK = 0x034; // PBD Mask
  28. const uint32_t OFFSET_PBADIVMASK = 0x040; // PBA Divided Mask
  29. const uint32_t OFFSET_CFDCTRL = 0x054; // Clock Failure Detector Control
  30. const uint32_t OFFSET_UNLOCK = 0x058; // Unlock Register
  31. const uint32_t OFFSET_IER = 0x0C0; // Interrupt Enable Register
  32. const uint32_t OFFSET_IDR = 0x0C4; // Interrupt Disable Register
  33. const uint32_t OFFSET_IMR = 0x0C8; // Interrupt Mask Register
  34. const uint32_t OFFSET_ISR = 0x0CC; // Interrupt Status Register
  35. const uint32_t OFFSET_ICR = 0x0D0; // Interrupt Clear Register
  36. const uint32_t OFFSET_SR = 0x0D4; // Status Register
  37. const uint32_t OFFSET_PPCR = 0x160; // Peripheral Power Control Register
  38. const uint32_t OFFSET_RCAUSE = 0x180; // Reset Cause Register
  39. const uint32_t OFFSET_WCAUSE = 0x184; // Wake Cause Register
  40. const uint32_t OFFSET_AWEN = 0x188; // Asynchronous Wake Enable
  41. const uint32_t OFFSET_PROTCTRL = 0x18C; // Protection Control Register
  42. const uint32_t OFFSET_FASTSLEEP = 0x194; // Fast Sleep Register
  43. const uint32_t OFFSET_CONFIG = 0x3F8; // Configuration Register
  44.  
  45.  
  46. // Subregisters
  47. const uint32_t CPUSEL_CPUSEL = 0;
  48. const uint32_t CPUSEL_CPUDIV = 7;
  49. const uint32_t MCCTRL_MCSEL = 0;
  50. const uint32_t SR_CFD = 0;
  51. const uint32_t SR_CKRDY = 5;
  52. const uint32_t SR_WAKE = 8;
  53.  
  54.  
  55. // Constants
  56. const uint32_t UNLOCK_KEY = 0xAA << 24;
  57. const uint32_t MCCTRL_MCSEL_RCSYS = 0;
  58. const uint32_t MCCTRL_MCSEL_OSC0 = 1;
  59. const uint32_t MCCTRL_MCSEL_PLL = 2;
  60. const uint32_t MCCTRL_MCSEL_DFLL = 3;
  61. const uint32_t MCCTRL_MCSEL_RC80M = 4;
  62. const uint32_t MCCTRL_MCSEL_RCFAST = 5;
  63. const uint32_t MCCTRL_MCSEL_RC1M = 6;
  64.  
  65. const uint32_t HSBMASK = 0;
  66. const uint32_t HSBMASK_PDCA = 0;
  67. const uint32_t HSBMASK_FLASHCALW = 1;
  68. const uint32_t HSBMASK_FLASHCALW_PICOCACHE = 2;
  69. const uint32_t HSBMASK_USBC = 3;
  70. const uint32_t HSBMASK_CRCCU = 4;
  71. const uint32_t HSBMASK_AESA = 9;
  72. const uint32_t PBAMASK = 32;
  73. const uint32_t PBAMASK_IISC = 0;
  74. const uint32_t PBAMASK_SPI = 1;
  75. const uint32_t PBAMASK_TC0 = 2;
  76. const uint32_t PBAMASK_TC1 = 3;
  77. const uint32_t PBAMASK_TWIM0 = 4;
  78. const uint32_t PBAMASK_TWIS0 = 5;
  79. const uint32_t PBAMASK_TWIM1 = 6;
  80. const uint32_t PBAMASK_TWIS1 = 7;
  81. const uint32_t PBAMASK_USART0 = 8;
  82. const uint32_t PBAMASK_USART1 = 9;
  83. const uint32_t PBAMASK_USART2 = 10;
  84. const uint32_t PBAMASK_USART3 = 11;
  85. const uint32_t PBAMASK_ADCIFE = 12;
  86. const uint32_t PBAMASK_DACC = 13;
  87. const uint32_t PBAMASK_ACIFC = 14;
  88. const uint32_t PBAMASK_GLOC = 15;
  89. const uint32_t PBAMASK_ABDACB = 16;
  90. const uint32_t PBAMASK_TRNG = 17;
  91. const uint32_t PBAMASK_PARC = 18;
  92. const uint32_t PBAMASK_CATB = 19;
  93. const uint32_t PBAMASK_TWIM2 = 21;
  94. const uint32_t PBAMASK_TWIM3 = 22;
  95. const uint32_t PBAMASK_LCDCA = 23;
  96. const uint32_t PBBMASK = 64;
  97. const uint32_t PBBMASK_HRAMC1 = 1;
  98. const uint32_t PBBMASK_HMATRIX = 2;
  99. const uint32_t PBBMASK_PDCA = 3;
  100. const uint32_t PBBMASK_CRCCU = 4;
  101. const uint32_t PBBMASK_USBC = 5;
  102. const uint32_t PBBMASK_PEVC = 6;
  103.  
  104. // Default clock frequencies
  105. const unsigned long RCSYS_FREQUENCY = 115000UL;
  106. const unsigned long RC80M_FREQUENCY = 80000000UL;
  107. const unsigned long PBA_MAX_FREQUENCY = 8000000UL;
  108.  
  109. // Peripheral clocks
  110. const int CLK_DMA = HSBMASK + HSBMASK_PDCA;
  111. const int CLK_USB_HSB = HSBMASK + HSBMASK_USBC; // The USB has 2 clocks
  112. const int CLK_CRC_HSB = HSBMASK + HSBMASK_CRCCU; // The CRC has 2 clocks
  113. const int CLK_AES = HSBMASK + HSBMASK_AESA;
  114. const int CLK_IIS = PBAMASK + PBAMASK_IISC;
  115. const int CLK_SPI = PBAMASK + PBAMASK_SPI;
  116. const int CLK_TC0 = PBAMASK + PBAMASK_TC0;
  117. const int CLK_TC1 = PBAMASK + PBAMASK_TC1;
  118. const int CLK_I2CM0 = PBAMASK + PBAMASK_TWIM0;
  119. const int CLK_I2CS0 = PBAMASK + PBAMASK_TWIS0;
  120. const int CLK_I2CM1 = PBAMASK + PBAMASK_TWIM1;
  121. const int CLK_I2CS1 = PBAMASK + PBAMASK_TWIS1;
  122. const int CLK_I2CM2 = PBAMASK + PBAMASK_TWIM2;
  123. const int CLK_I2CM3 = PBAMASK + PBAMASK_TWIM3;
  124. const int CLK_USART0 = PBAMASK + PBAMASK_USART0;
  125. const int CLK_USART1 = PBAMASK + PBAMASK_USART1;
  126. const int CLK_USART2 = PBAMASK + PBAMASK_USART2;
  127. const int CLK_USART3 = PBAMASK + PBAMASK_USART3;
  128. const int CLK_ADC = PBAMASK + PBAMASK_ADCIFE;
  129. const int CLK_DAC = PBAMASK + PBAMASK_DACC;
  130. const int CLK_GLOC = PBAMASK + PBAMASK_GLOC;
  131. const int CLK_TRNG = PBAMASK + PBAMASK_TRNG;
  132. const int CLK_CRC = PBBMASK + PBBMASK_CRCCU;
  133. const int CLK_USB = PBBMASK + PBBMASK_USBC;
  134.  
  135.  
  136. // The main clock is used by the CPU and the peripheral buses
  137. // and can be connected to any of these clock sources
  138. enum class MainClockSource {
  139. RCSYS = MCCTRL_MCSEL_RCSYS,
  140. OSC0 = MCCTRL_MCSEL_OSC0,
  141. PLL = MCCTRL_MCSEL_PLL,
  142. DFLL = MCCTRL_MCSEL_DFLL,
  143. RC80M = MCCTRL_MCSEL_RC80M,
  144. RCFAST = MCCTRL_MCSEL_RCFAST,
  145. };
  146.  
  147. // Some peripherals can wake up the chip from sleep mode
  148. // See enableWakeUpSource() for more details
  149. enum class WakeUpSource {
  150. I2C_SLAVE_0 = 0,
  151. I2C_SLAVE_1 = 1,
  152. USBC = 2,
  153. PSOK = 3,
  154. BOD18 = 4,
  155. BOD33 = 5,
  156. PICOUART = 6,
  157. LCDA = 7
  158. };
  159.  
  160. // The PM can be used to determine the cause of the last reset
  161. // See resetCause() for more details
  162. enum class ResetCause {
  163. UNKNOWN = -1,
  164. POR = 0, // Power-on reset (core power supply)
  165. BOD = 1, // Brown-out reset (core power supply)
  166. EXT = 2, // External reset pin
  167. WDT = 3, // Watchdog reset
  168. BKUP = 6, // Backup reset
  169. SYSRESETREQ = 8, // Software reset
  170. POR33 = 10, // Power-on reset (IO 3.3V supply)
  171. BOD33 = 13 // Brown-out reset (IO 3.3V supply)
  172. };
  173.  
  174. enum class WakeUpCause {
  175. UNKNOWN = -1,
  176. I2C_SLAVE_0 = 0,
  177. I2C_SLAVE_1 = 1,
  178. USB = 2,
  179. PSOK = 3,
  180. BOD18 = 4,
  181. BOD33 = 5,
  182. PICOUART = 6,
  183. LCD = 7,
  184. EIC = 16,
  185. AST = 17,
  186. };
  187.  
  188. const int N_INTERRUPTS = 3;
  189. enum class Interrupt {
  190. CLOCK_FAILURE = 0,
  191. CLOCK_READY = 5,
  192. WAKE = 8,
  193. };
  194.  
  195. // Clock frequency values used by inline functions below
  196. extern unsigned long _mainClockFrequency;
  197. extern unsigned long _cpuClockFrequency;
  198.  
  199.  
  200. // Module API
  201.  
  202. // Clock management
  203. void setMainClockSource(MainClockSource clockSource, unsigned long cpudiv=0);
  204. inline unsigned long getMainClockFrequency() { return _mainClockFrequency; }
  205. inline unsigned long getCPUClockFrequency() { return _cpuClockFrequency; }
  206. unsigned long getModuleClockFrequency(uint8_t peripheral);
  207. void enablePeripheralClock(uint8_t peripheral, bool enabled=true);
  208. void disablePeripheralClock(uint8_t peripheral);
  209. void enablePBADivClock(uint8_t pow);
  210.  
  211. // Wake-up and reset
  212. ResetCause resetCause();
  213. WakeUpCause wakeUpCause();
  214. inline volatile bool isWakeUpCauseUnknown() { return (*(volatile uint32_t*)(BASE + OFFSET_WCAUSE)) == 0; }
  215. void enableWakeUpSource(WakeUpSource src);
  216. void disableWakeUpSource(WakeUpSource src);
  217. void disableWakeUpSources();
  218.  
  219. // Interrupts
  220. void enableInterrupt(void (*handler)(), Interrupt interrupt=Interrupt::WAKE);
  221. void disableInterrupt(Interrupt interrupt=Interrupt::WAKE);
  222.  
  223. }
  224.  
  225.  
  226. #endif

Module

  1. #include "pm.h"
  2. #include "scif.h"
  3. #include "core.h"
  4. #include "error.h"
  5.  
  6. namespace PM {
  7.  
  8. // Clock frequencies
  9. unsigned long _mainClockFrequency = RCSYS_FREQUENCY;
  10. unsigned long _cpuClockFrequency = RCSYS_FREQUENCY;
  11. unsigned long _hsbClockFrequency = RCSYS_FREQUENCY;
  12. unsigned long _pbaClockFrequency = RCSYS_FREQUENCY;
  13.  
  14. // Interrupt handlers
  15. extern uint8_t INTERRUPT_PRIORITY;
  16. uint32_t _interruptHandlers[N_INTERRUPTS];
  17. const int _interruptBits[N_INTERRUPTS] = {SR_CFD, SR_CKRDY, SR_WAKE};
  18. void interruptHandlerWrapper();
  19.  
  20. // The main clock is used by the CPU and the peripheral buses and can be connected
  21. // to any of the clock sources listed in MainClockSource
  22. void setMainClockSource(MainClockSource clockSource, unsigned long cpudiv) {
  23. if (cpudiv >= 1) {
  24. // Unlock the CPUSEL register
  25. (*(volatile uint32_t*)(BASE + OFFSET_UNLOCK))
  26. = UNLOCK_KEY // KEY : Magic word (see datasheet)
  27. | OFFSET_CPUSEL; // ADDR : unlock CPUSEL
  28.  
  29. // Configure the CPU clock divider
  30. if (cpudiv > 7) {
  31. cpudiv = 7;
  32. }
  33. (*(volatile uint32_t*)(BASE + OFFSET_CPUSEL))
  34. = (cpudiv - 1) << CPUSEL_CPUSEL // CPUSEL : select divider factor
  35. | 1 << CPUSEL_CPUDIV; // CPUDIV : enable divider
  36.  
  37. // Wait for the divider to be ready
  38. while (!((*(volatile uint32_t*)(BASE + OFFSET_SR)) & (1 << SR_CKRDY)));
  39. }
  40.  
  41. // Unlock the MCCTRL register, which is locked by default as a safety mesure
  42. (*(volatile uint32_t*)(BASE + OFFSET_UNLOCK))
  43. = UNLOCK_KEY // KEY : Magic word (see datasheet)
  44. | OFFSET_MCCTRL; // ADDR : unlock MCCTRL
  45.  
  46. // Change the main clock source
  47. (*(volatile uint32_t*)(BASE + OFFSET_MCCTRL))
  48. = static_cast<int>(clockSource) << MCCTRL_MCSEL; // MCSEL : select clock source
  49.  
  50. // Save the frequency
  51. switch (clockSource) {
  52. case MainClockSource::RCSYS:
  53. _mainClockFrequency = RCSYS_FREQUENCY;
  54. break;
  55. case MainClockSource::OSC0:
  56. _mainClockFrequency = SCIF::getOSC0Frequency();
  57. break;
  58. case MainClockSource::PLL:
  59. _mainClockFrequency = SCIF::getPLLFrequency();
  60. break;
  61. case MainClockSource::DFLL:
  62. _mainClockFrequency = SCIF::getDFLLFrequency();
  63. break;
  64.  
  65. case MainClockSource::RCFAST:
  66. _mainClockFrequency = SCIF::getRCFASTFrequency();
  67. break;
  68.  
  69. case MainClockSource::RC80M:
  70. _mainClockFrequency = RC80M_FREQUENCY;
  71. break;
  72. }
  73. _cpuClockFrequency = _mainClockFrequency / (1 << cpudiv);
  74. _hsbClockFrequency = _mainClockFrequency;
  75. _pbaClockFrequency = _mainClockFrequency;
  76. }
  77.  
  78. unsigned long getModuleClockFrequency(uint8_t peripheral) {
  79. if (peripheral >= HSBMASK && peripheral < PBAMASK) {
  80. return _hsbClockFrequency;
  81. } else if (peripheral >= PBAMASK && peripheral < PBBMASK) {
  82. return _pbaClockFrequency;
  83. }
  84. return 0;
  85. }
  86.  
  87. void enablePeripheralClock(uint8_t peripheral, bool enabled) {
  88. // Select the correct register
  89. int offset = 0;
  90. if (peripheral >= HSBMASK && peripheral < PBAMASK) {
  91. offset = OFFSET_HSBMASK;
  92. peripheral -= HSBMASK;
  93. } else if (peripheral >= PBAMASK && peripheral < PBBMASK) {
  94. offset = OFFSET_PBAMASK;
  95. peripheral -= PBAMASK;
  96. } else if (peripheral >= PBBMASK) {
  97. offset = OFFSET_PBBMASK;
  98. peripheral -= PBBMASK;
  99. }
  100.  
  101. // Unlock the selected register, which is locked by default as a safety mesure
  102. (*(volatile uint32_t*)(BASE + OFFSET_UNLOCK))
  103. = UNLOCK_KEY // KEY : Magic word (see datasheet)
  104. | offset; // ADDR : unlock the selected register
  105.  
  106. if (enabled) {
  107. // Unmask the corresponding clock
  108. (*(volatile uint32_t*)(BASE + offset)) |= 1 << peripheral;
  109. } else {
  110. // Unmask the corresponding clock
  111. (*(volatile uint32_t*)(BASE + offset)) &= ~(uint32_t)(1 << peripheral);
  112. }
  113. }
  114.  
  115. void disablePeripheralClock(uint8_t peripheral) {
  116. enablePeripheralClock(peripheral, false);
  117. }
  118.  
  119. void enablePBADivClock(uint8_t pow) {
  120. // Unlock the selected register, which is locked by default as a safety mesure
  121. (*(volatile uint32_t*)(BASE + OFFSET_UNLOCK))
  122. = UNLOCK_KEY // KEY : Magic word (see datasheet)
  123. | OFFSET_PBADIVMASK; // ADDR : unlock PBADIVMASK
  124.  
  125. // Check that the power of two selected is in range
  126. if (pow < 1 || pow > 7) {
  127. return;
  128. }
  129.  
  130. // Unmask the corresponding divided clock
  131. (*(volatile uint32_t*)(BASE + OFFSET_PBADIVMASK)) |= 1 << (pow - 1);
  132. }
  133.  
  134.  
  135. // Returns the cause of the last reset. This is useful for example to handle faults
  136. // detected by the watchdog or the brown-out detectors.
  137. ResetCause resetCause() {
  138. uint32_t rcause = (*(volatile uint32_t*)(BASE + OFFSET_RCAUSE));
  139. if (rcause != 0) {
  140. for (int i = 0; i < 32; i++) {
  141. if (rcause & 1 << i) {
  142. return static_cast<ResetCause>(i);
  143. }
  144. }
  145. }
  146. return ResetCause::UNKNOWN;
  147. }
  148.  
  149. // Returns the cause of the last wake up
  150. WakeUpCause wakeUpCause() {
  151. uint32_t wcause = (*(volatile uint32_t*)(BASE + OFFSET_WCAUSE));
  152. if (wcause != 0) {
  153. for (int i = 0; i < 32; i++) {
  154. if (wcause & 1u << i) {
  155. return static_cast<WakeUpCause>(i);
  156. }
  157. }
  158. }
  159. return WakeUpCause::UNKNOWN;
  160. }
  161.  
  162. void enableWakeUpSource(WakeUpSource src) {
  163. // AWEN (Asynchronous Wake Up Enable Register) : set the corresponding bit
  164. (*(volatile uint32_t*)(BASE + OFFSET_AWEN))
  165. |= 1 << static_cast<int>(src);
  166. }
  167.  
  168. void disableWakeUpSource(WakeUpSource src) {
  169. // AWEN (Asynchronous Wake Up Enable Register) : clear the corresponding bit
  170. (*(volatile uint32_t*)(BASE + OFFSET_AWEN))
  171. &= ~(uint32_t)(1 << static_cast<int>(src));
  172. }
  173.  
  174. void disableWakeUpSources() {
  175. // AWEN (Asynchronous Wake Up Enable Register) : clear the register
  176. (*(volatile uint32_t*)(BASE + OFFSET_AWEN)) = 0;
  177. }
  178.  
  179.  
  180. void enableInterrupt(void (*handler)(), Interrupt interrupt) {
  181. // Save the user handler
  182. _interruptHandlers[static_cast<int>(interrupt)] = (uint32_t)handler;
  183.  
  184. // IER (Interrupt Enable Register) : enable the requested interrupt (WAKE by default)
  185. (*(volatile uint32_t*)(BASE + OFFSET_IER))
  186. = 1 << _interruptBits[static_cast<int>(interrupt)];
  187.  
  188. // Set the handler and enable the module interrupt at the Core level
  189. Core::setInterruptHandler(Core::Interrupt::PM, interruptHandlerWrapper);
  190. Core::enableInterrupt(Core::Interrupt::PM, INTERRUPT_PRIORITY);
  191. }
  192.  
  193. void disableInterrupt(Interrupt interrupt) {
  194. // IDR (Interrupt Disable Register) : disable the requested interrupt (WAKE by default)
  195. (*(volatile uint32_t*)(BASE + OFFSET_IDR))
  196. = 1 << _interruptBits[static_cast<int>(interrupt)];
  197.  
  198. // If no interrupt is enabled anymore, disable the module interrupt at the Core level
  199. if ((*(volatile uint32_t*)(BASE + OFFSET_IMR)) == 0) {
  200. Core::disableInterrupt(Core::Interrupt::PM);
  201. }
  202. }
  203.  
  204. void interruptHandlerWrapper() {
  205. // Call the user handler of every interrupt that is enabled and pending
  206. for (int i = 0; i < N_INTERRUPTS; i++) {
  207. if ((*(volatile uint32_t*)(BASE + OFFSET_IMR)) & (1 << _interruptBits[i]) // Interrupt is enabled
  208. && (*(volatile uint32_t*)(BASE + OFFSET_ISR)) & (1 << _interruptBits[i])) { // Interrupt is pending
  209. void (*handler)() = (void (*)())_interruptHandlers[i];
  210. if (handler != nullptr) {
  211. handler();
  212. }
  213.  
  214. // Clear the interrupt by reading ISR
  215. (*(volatile uint32_t*)(BASE + OFFSET_ICR)) = 1 << _interruptBits[i];
  216. }
  217. }
  218. }
  219.  
  220. }