Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions cores/arduino/PendSV.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#include "PendSV.h"

#include <Arduino.h>
#include <limits.h>

namespace {
PendSV pendSv;
}

PendSV &PendSV::instance() {
return pendSv;
}

uint32_t PendSV::enterCritical() {
const uint32_t primask = __get_PRIMASK();
__disable_irq();
return primask;
}

void PendSV::exitCritical(uint32_t primask) {
__set_PRIMASK(primask);
}

bool PendSV::registerService(uint8_t serviceId, ServiceFn fn, void *context) {
if (serviceId >= kMaxServices || fn == nullptr)
return false;

const uint32_t primask = enterCritical();
pendingCount_[serviceId] = 0;
pendingMask_ &= ~(1u << serviceId);
services_[serviceId].fn = fn;
services_[serviceId].context = context;
exitCritical(primask);

return true;
}

void PendSV::clearService(uint8_t serviceId) {
if (serviceId >= kMaxServices)
return;

const uint32_t primask = enterCritical();
services_[serviceId].fn = nullptr;
services_[serviceId].context = nullptr;
pendingCount_[serviceId] = 0;
pendingMask_ &= ~(1u << serviceId);
exitCritical(primask);
}

void PendSV::setPending(uint8_t serviceId) {
if (serviceId >= kMaxServices)
return;

const uint32_t primask = enterCritical();
uint16_t &pendingCount = pendingCount_[serviceId];
if (pendingCount < UINT16_MAX)
++pendingCount;
pendingMask_ |= (1u << serviceId);
exitCritical(primask);

__DMB();
SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
}

void PendSV::dispatchPending() {
// Bound one PendSV entry so high-rate producers do not monopolize return to
// thread mode. Remaining work re-pends PendSV below.
uint8_t dispatched = 0;

while (dispatched < kDispatchBudget) {
uint32_t primask = enterCritical();
const uint32_t pending = pendingMask_;
if (pending == 0) {
exitCritical(primask);
return;
}

const uint8_t serviceId = static_cast<uint8_t>(__builtin_ctz(pending));
uint16_t &pendingCount = pendingCount_[serviceId];

if (pendingCount == 0) {
// Defensive scrub in case mask and count drift out of sync.
pendingMask_ &= ~(1u << serviceId);
exitCritical(primask);
continue;
}

--pendingCount;
if (pendingCount == 0)
pendingMask_ &= ~(1u << serviceId);

ServiceEntry entry = services_[serviceId];
exitCritical(primask);

// Pending work without a registered service is intentionally dropped.
// Producers are expected to register before calling setPending(), and
// clearService() cancels queued work for that service.
if (entry.fn != nullptr)
entry.fn(serviceId, entry.context);

++dispatched;
}

const uint32_t primask = enterCritical();
const bool hasRemaining = (pendingMask_ != 0);
exitCritical(primask);

if (hasRemaining) {
__DMB();
SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
}
}

extern "C" void PendSV_Handler(void) {
PendSV::instance().dispatchPending();
}
34 changes: 34 additions & 0 deletions cores/arduino/PendSV.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#pragma once

#include <array>
#include <stdint.h>

class PendSV {
public:
using ServiceFn = void (*)(uint8_t serviceId, void *context);
static constexpr uint8_t kMaxServices = 32;
static constexpr uint8_t kDispatchBudget = 16;
static_assert(kMaxServices <= 32, "PendSV pending mask supports at most 32 services");

static PendSV &instance();

bool registerService(uint8_t serviceId, ServiceFn fn, void *context = nullptr);
// Cancels queued work that has not started dispatching. If a callback was
// already copied for dispatch, its context must remain valid until it returns.
void clearService(uint8_t serviceId);
void dispatchPending();
void setPending(uint8_t serviceId);

private:
static uint32_t enterCritical();
static void exitCritical(uint32_t primask);

struct ServiceEntry {
ServiceFn fn = nullptr;
void *context = nullptr;
};

std::array<ServiceEntry, kMaxServices> services_{};
std::array<uint16_t, kMaxServices> pendingCount_{};
volatile uint32_t pendingMask_ = 0;
};
112 changes: 112 additions & 0 deletions libraries/ADC/examples/AsyncChipTemperature/AsyncChipTemperature.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#include <ADC.h>

volatile bool g_readComplete = false;
volatile uint16_t g_lastValue = 0;

void onTempRead(ChannelADC *channel, uint16_t result, void *userData) {
(void)channel;
(void)userData;
g_lastValue = result;
g_readComplete = true;
}

ChannelADC g_temp;
#ifdef ADC_HAS_D5X_E5X_REGISTERS
ChannelADC g_ctat;
#endif

void setup() {
Serial.begin(115200);
while (!Serial);

g_temp.setReadCallback(onTempRead, nullptr);
#ifdef ADC_HAS_D5X_E5X_REGISTERS
// On SAMD5x/SAME5x, the PTAT/CTAT sensors are routed by SUPC.VREF.TSSEL
// with VREFOE disabled. Use a non-SUPC ADC reference for these channels.
g_temp.setReference(AdcRefSel::ADC_REFSEL_INTVCC1);
#else
g_temp.setReference(AdcRefSel::ADC_REFSEL_INT1V);
#endif
g_temp.setCtrlB(AdcResSel::ADC_RESSEL_12BIT, AdcPrescaler::ADC_PRESCALER_DIV32);

#ifdef ADC_HAS_D5X_E5X_REGISTERS
g_ctat.setReadCallback(onTempRead, nullptr);
g_ctat.setReference(AdcRefSel::ADC_REFSEL_INTVCC1);
g_ctat.setCtrlB(AdcResSel::ADC_RESSEL_12BIT, AdcPrescaler::ADC_PRESCALER_DIV32);

if (!g_temp.attach(AdcMuxPos::ADC_MUXPOS_TEMP, AdcMuxNeg::ADC_MUXNEG_GND,
AdcSampleNum::ADC_SAMPLENUM_16)) {
Serial.println("Attach PTAT failed");
}

if (!g_ctat.attach(AdcMuxPos::ADC_MUXPOS_TEMP_CTAT, AdcMuxNeg::ADC_MUXNEG_GND,
AdcSampleNum::ADC_SAMPLENUM_16)) {
Serial.println("Attach CTAT failed");
}
#else
if (!g_temp.attach(AdcMuxPos::ADC_MUXPOS_TEMP, AdcMuxNeg::ADC_MUXNEG_GND,
AdcSampleNum::ADC_SAMPLENUM_16)) {
Serial.println("Attach temperature channel failed");
}
#endif
}

void loop() {
#ifdef ADC_HAS_D5X_E5X_REGISTERS
g_readComplete = false;
if (!g_temp.read()) {
Serial.println("PTAT async enqueue failed");
delay(1000);
return;
}
while (!g_readComplete) {
AdcEngine::instance().service();
}
const uint16_t ptat = g_lastValue;

g_readComplete = false;
if (!g_ctat.read()) {
Serial.println("CTAT async enqueue failed");
delay(1000);
return;
}
while (!g_readComplete) {
AdcEngine::instance().service();
}
const uint16_t ctat = g_lastValue;

const float tempC = analogReadTemperatureC(ptat, ctat);
Serial.print("PTAT raw: ");
Serial.print(ptat);
Serial.print(" CTAT raw: ");
Serial.print(ctat);
Serial.print(" ");
Serial.print("Async chip temp (C): ");
Serial.println(tempC, 2);
#else
g_readComplete = false;
if (!g_temp.read()) {
Serial.println("Async enqueue failed");
delay(1000);
return;
}
while (!g_readComplete) {
AdcEngine::instance().service();
}

const uint16_t raw = g_lastValue;
const float tempC = analogReadTemperatureC(raw);

Serial.print("Async raw: ");
Serial.print(raw);
Serial.print(" tempC: ");
Serial.print(tempC, 2);
if (tempC >= 20.0f && tempC <= 24.0f) {
Serial.println(" (PASS 20-24C)");
} else {
Serial.println(" (OUTSIDE 20-24C)");
}
#endif

delay(1000);
}
25 changes: 25 additions & 0 deletions libraries/ADC/examples/BasicRead/BasicRead.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include <ADC.h>

ChannelADC adc;

void setup() {
Serial.begin(115200);
while (!Serial)
;

// Attach a single-ended channel on A0.
if (!adc.attach(A0))
Serial.println("ADC attach failed");
}

void loop() {
if (adc.read()) {
delay(5);
Serial.print("A0: ");
Serial.println(adc.value());
} else {
Serial.println("ADC read enqueue failed");
}

delay(250);
}
Loading
Loading