nrfcxx  0.1.0
C++-17 Framework for Nordic nRF5 Devices
impl.hpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: Apache-2.0 */
2 /* Copyright 2018-2019 Peter A. Bigot */
3 
9 #ifndef NRFCXX_NRF52_IMPL_HPP
10 #define NRFCXX_NRF52_IMPL_HPP
11 #pragma once
12 
13 #if (NRF52832 - 0)
14 #include <nrf52_bitfields.h>
15 #elif (NRF52840 - 0)
16 #include <nrf52840_bitfields.h>
17 #else /* TARGET_PRODUCT */
18 #error Unsupported NRF52 MCU
19 #endif /* NRF52 */
20 
21 namespace nrfcxx {
22 namespace nrf5 {
23 namespace series {
24 
41 struct SAADC_Peripheral : public ADC_Base {
42  using mutex_type = mutex_irq<static_cast<IRQn_Type>(nrf5::SAADC.IRQn)>;
43 
44  static constexpr IRQn_Type IRQn = SAADC_IRQn;
45 
47  static constexpr unsigned int VBG_mV = 600;
48 
53  static constexpr uint16_t TOLERANCE_adc16 = (1U << 7);
54 
65  static constexpr bool near_zero (uint16_t v_adc16)
66  {
67  return ((TOLERANCE_adc16 > v_adc16)
68  || (v_adc16 >= ((1U << 16) - TOLERANCE_adc16)));
69  }
70 
117  static constexpr
118  unsigned int
119  make_config (unsigned int refsel = SAADC_CH_CONFIG_REFSEL_Internal,
120  unsigned int gain = SAADC_CH_CONFIG_GAIN_Gain1_6,
121  unsigned int tacq = SAADC_CH_CONFIG_TACQ_10us,
122  bool burst = true,
123  unsigned int resp = SAADC_CH_CONFIG_RESP_Bypass,
124  bool differential = false,
125  unsigned int resn = SAADC_CH_CONFIG_RESN_Bypass)
126  {
127  return 0
128  | (SAADC_CH_CONFIG_RESP_Msk & (resp << SAADC_CH_CONFIG_RESP_Pos))
129  | (SAADC_CH_CONFIG_RESN_Msk & (resn << SAADC_CH_CONFIG_RESN_Pos))
130  | (SAADC_CH_CONFIG_GAIN_Msk & (gain << SAADC_CH_CONFIG_GAIN_Pos))
131  | (SAADC_CH_CONFIG_REFSEL_Msk & (refsel << SAADC_CH_CONFIG_REFSEL_Pos))
132  | (SAADC_CH_CONFIG_TACQ_Msk & (tacq << SAADC_CH_CONFIG_TACQ_Pos))
133  | (SAADC_CH_CONFIG_MODE_Msk & (differential << SAADC_CH_CONFIG_MODE_Pos))
134  | (SAADC_CH_CONFIG_BURST_Msk & (burst << SAADC_CH_CONFIG_BURST_Pos));
135  }
136 
137  static constexpr uint32_t inten = 0
138  | (SAADC_INTENSET_CALIBRATEDONE_Set << SAADC_INTENSET_CALIBRATEDONE_Pos)
139  | (SAADC_INTENSET_STARTED_Set << SAADC_INTENSET_STARTED_Pos)
140  | (SAADC_INTENSET_DONE_Set << SAADC_INTENSET_DONE_Pos)
141  | (SAADC_INTENSET_RESULTDONE_Set << SAADC_INTENSET_RESULTDONE_Pos)
142  | (SAADC_INTENSET_END_Set << SAADC_INTENSET_END_Pos)
143  | (SAADC_INTENSET_STOPPED_Set << SAADC_INTENSET_STOPPED_Pos);
144 
145  static bool busy ()
146  {
147  return nrf5::SAADC->STATUS;
148  }
149 
150  /* PAN 74: SAADC: Started events fires prematurely.
151  * If TACQ <= 5 us EVENTS_STARTED may be spontaneously generated after ENABLE.
152  * (Workaround does not work)
153  *
154  * PAN 86: SAADC: Triggering START after calibration may write sample to RAM
155  * After CALIBRATEDONE issue STOP and wait for STOPPED before issuing START.
156  *
157  * PAN 150: SAADC: EVENT_STARTED does not fire
158  * Applies to PPI-initiated START with TACQ <= 5 us
159  *
160  * PAN 178: SAADC: END event firing too early
161  * END event occurs before data ready when CALIBRATE with TACQ < 10us before SAMPLE.
162  *
163  * Observations:
164  *
165  * With TACQ <= 5 us CALIBRATE without START generates:
166  * * DONE which remains asserted for 4.252 us (with TACQ 3 us)
167  * * anomalous STARTED 5 us after DONE asserted (PAN 74)
168  * * two more DONE events
169  * * CALIBRATEDONE 64 ns after the third DONE deasserts
170  * * anomalous DONE 4.248 us after CALIBRATEDONE
171  *
172  * With TACQ = 40 us CALIBRATE without START generates:
173  * * DONE which remains asserted for 41.252 us
174  * * two more DONE events
175  * * CALIBRATEDONE 104 ns after third DONE deasserts
176  * * anomalous DONE 41.144 us after CALIBRATEDONE
177  *
178  * The extra DONE event may be the fault underlying PAN 86. If STOP
179  * is issued quickly enough the extra DONE event does not happen.
180  *
181  * With TACQ <= 5 us START, [DONE], STARTED, CALIBRATE produces END
182  * This is consistent with PAN 178.
183  *
184  * With TACQ = 40 us START, STARTED, CALIBRATE produces END before CALIBRATEDONE
185  * This is consistent with PAN 178 except observed with long TACQ.
186  *
187  * Without ENABLE issuing START produces STARTED. Issuing SAMPLE or
188  * CALIBRATE has no effect.
189  *
190  * Workarounds:
191  * * Issue CALIBRATE after ENABLE before START.
192  * * Issue STOP after CALIBRATE_DONE and wait for STOPPED
193  * * Ignore STARTED except after explicit START.
194  */
195 
196  static void enable_bi ()
197  {
198  nrf5::SAADC->EVENTS_STARTED = 0;
199  nrf5::SAADC->EVENTS_END = 0;
200  nrf5::SAADC->EVENTS_DONE = 0;
201  nrf5::SAADC->EVENTS_STOPPED = 0;
202  nrf5::SAADC->EVENTS_CALIBRATEDONE = 0;
203  nrf5::SAADC->RESULT.PTR = reinterpret_cast<uint32_t>(result_ptr_);
204  nrf5::SAADC->RESULT.MAXCNT = result_maxcnt_;
205  nrf5::SAADC->INTENSET = inten;
206  nvic_ClearPendingIRQ(IRQn);
207  nvic_EnableIRQ(IRQn);
208  nrf5::SAADC->ENABLE = 1;
209  }
210 
211  static bool calibrating_bi_;
212 
213  static void disable_bi ()
214  {
215  nrf5::SAADC->ENABLE = 0;
216  nvic_DisableIRQ(IRQn);
217  nrf5::SAADC->INTENCLR = inten;
218  }
219 
220  static int calibrate_bi ()
221  {
222  enable_bi();
223  calibrating_bi_ = true;
224  nrf5::SAADC->TASKS_CALIBRATEOFFSET = 1;
225  return 1;
226  }
227 
228  static int start_bi ()
229  {
230  enable_bi();
231  calibrating_bi_ = false;
232  nrf5::SAADC->TASKS_START = 1;
233  return 1;
234  }
235 
236  static void stopped_bi ()
237  {
238  disable_bi();
239  }
240 
246  static unsigned int normalize_bi ()
247  {
248  volatile uint16_t* rp = result_ptr_;
249  volatile uint16_t* const rpe = rp + result_maxcnt_;
250 
251  // NB: RESOLUTION is a 7-bit value, but only the low two bits
252  // are defined to be supported. If we used
253  // SAADC_RESOLUTION_VAL_Msk we could end up with a negative
254  // shift.
255  unsigned int shift16 = 8 - 2 * ((nrf5::SAADC->RESOLUTION & 0x03) >> SAADC_RESOLUTION_VAL_Pos);
256  while (rp < rpe) {
257  *rp++ <<= shift16;
258  }
259  return *result_ptr_;
260  }
261 };
262 
263 using ADC_Variant = SAADC_Peripheral;
264 
265 } // ns series
266 } // ns nrf5
267 
268 
270 static constexpr auto IRQ_PRIORITY_SD_HIGH = 0;
271 
274 static constexpr auto IRQ_PRIORITY_SD_MEMORY = 1;
275 
277 static constexpr auto IRQ_PRIORITY_APP_HIGH = 2;
278 
280 static constexpr auto IRQ_PRIORITY_SD_LOW = 4;
281 
283 static constexpr auto IRQ_PRIORITY_APP_LOW = 5;
284 
285 } // ns nrfcxx
286 
287 #endif /* NRFCXX_NRF52_IMPL_HPP */
nrfcxx::nrf5::series::SAADC_Peripheral::TOLERANCE_adc16
static constexpr uint16_t TOLERANCE_adc16
The value corresponding to the 9th bit of a normalized acquisition.
Definition: impl.hpp:53
nrfcxx::IRQ_PRIORITY_APP_HIGH
static constexpr auto IRQ_PRIORITY_APP_HIGH
NVIC IRQ priority reserved for critical application interrupts.
Definition: impl.hpp:173
nrfcxx::nrf5::series::SAADC_Peripheral::near_zero
static constexpr bool near_zero(uint16_t v_adc16)
Test whether a value appears to be indistinguishable from zero.
Definition: impl.hpp:65
nrfcxx::nrf5::series::ADC_Base
Material common to all ADC peripheral implementations.
Definition: impl.hpp:22
nrfcxx::mutex_irq
nvic_BlockIRQ as a template type.
Definition: core.hpp:497
nrfcxx::nvic_ClearPendingIRQ
static void nvic_ClearPendingIRQ(int irqn)
Wrapper around NVIC_ClearPending to work around issues with peripheral::IRQn.
Definition: core.hpp:412
nrfcxx::nrf5::series::SAADC_Peripheral
Constants and function specific to the nRF52 SAADC peripheral.
Definition: impl.hpp:41
nrfcxx::nvic_EnableIRQ
static void nvic_EnableIRQ(int irqn)
Wrapper around NVIC_EnableIRQ to work around issues with peripheral::IRQn.
Definition: core.hpp:376
nrfcxx::nrf5::series::SAADC_Peripheral::make_config
static constexpr unsigned int make_config(unsigned int refsel=SAADC_CH_CONFIG_REFSEL_Internal, unsigned int gain=SAADC_CH_CONFIG_GAIN_Gain1_6, unsigned int tacq=SAADC_CH_CONFIG_TACQ_10us, bool burst=true, unsigned int resp=SAADC_CH_CONFIG_RESP_Bypass, bool differential=false, unsigned int resn=SAADC_CH_CONFIG_RESN_Bypass)
Helper to build up a ADC channel-specific CONFIG value.
Definition: impl.hpp:119
nrfcxx::IRQ_PRIORITY_SD_HIGH
static constexpr auto IRQ_PRIORITY_SD_HIGH
NVIC IRQ priority reserved for critical soft-device interrupts.
Definition: impl.hpp:170
nrfcxx::nrf5::series::SAADC_Peripheral::VBG_mV
static constexpr unsigned int VBG_mV
nRF52 reference voltage is 0.6 V.
Definition: impl.hpp:47
nrfcxx::IRQ_PRIORITY_SD_LOW
static constexpr auto IRQ_PRIORITY_SD_LOW
NVIC IRQ priority reserved for non-critical soft-device interrupts.
Definition: impl.hpp:176
nrfcxx::IRQ_PRIORITY_APP_LOW
static constexpr auto IRQ_PRIORITY_APP_LOW
NVIC IRQ priority reserved for non-critical application interrupts.
Definition: impl.hpp:179
nrfcxx::nvic_DisableIRQ
static void nvic_DisableIRQ(int irqn)
Wrapper around NVIC_DisableIRQ to work around issues with peripheral::IRQn.
Definition: core.hpp:385
nrfcxx::nrf5::series::SAADC_Peripheral::normalize_bi
static unsigned int normalize_bi()
Post-process the ADC results so they're normalized as if from a 16-bit ADC.
Definition: impl.hpp:246
nrfcxx::IRQ_PRIORITY_SD_MEMORY
static constexpr auto IRQ_PRIORITY_SD_MEMORY
NVIC IRQ priority reserved for critical soft-device memory isolation and runtime protection.
Definition: impl.hpp:274
nrfcxx
Primary namespace for nrfcxx functionality.
Definition: clock.hpp:17