Unverified Commit 011ecdb5 authored by Earle F. Philhower, III's avatar Earle F. Philhower, III Committed by GitHub

More multicore safety plumbing, IRQs, analogXXX (#107)

Keep a per-core IRQ stack for noInterrupts since each core has its own
enable/disable.

Make analogRead/analogWrite multicore safe
parent b793ad55
...@@ -33,6 +33,7 @@ public: ...@@ -33,6 +33,7 @@ public:
_acquired = false; _acquired = false;
if (!mutex_try_enter(_mutex, &owner)) { if (!mutex_try_enter(_mutex, &owner)) {
if (owner == get_core_num()) { // Deadlock! if (owner == get_core_num()) { // Deadlock!
DEBUGCORE("CoreMutex - Deadlock detected!\n");
return; return;
} }
mutex_enter_blocking(_mutex); mutex_enter_blocking(_mutex);
......
...@@ -31,20 +31,14 @@ typedef struct { ...@@ -31,20 +31,14 @@ typedef struct {
} Tone; } Tone;
// Keep std::map safe for multicore use // Keep std::map safe for multicore use
static bool _mutexInitted = false; auto_init_mutex(_toneMutex);
static mutex_t _mutex;
#include "tone.pio.h" #include "tone.pio.h"
static PIOProgram _tonePgm(&tone_program); static PIOProgram _tonePgm(&tone_program);
static std::map<pin_size_t, Tone *> _toneMap; static std::map<pin_size_t, Tone *> _toneMap;
void tone(uint8_t pin, unsigned int frequency, unsigned long duration) { void tone(uint8_t pin, unsigned int frequency, unsigned long duration) {
if (!_mutexInitted) {
mutex_init(&_mutex);
_mutexInitted = true;
}
if ((pin < 0) || (pin > 29)) { if ((pin < 0) || (pin > 29)) {
DEBUGCORE("ERROR: Illegal pin in tone (%d)\n", pin); DEBUGCORE("ERROR: Illegal pin in tone (%d)\n", pin);
return; return;
...@@ -55,7 +49,7 @@ void tone(uint8_t pin, unsigned int frequency, unsigned long duration) { ...@@ -55,7 +49,7 @@ void tone(uint8_t pin, unsigned int frequency, unsigned long duration) {
} }
// Ensure only 1 core can start or stop at a time // Ensure only 1 core can start or stop at a time
CoreMutex m(&_mutex); CoreMutex m(&_toneMutex);
if (!m) return; // Weird deadlock case if (!m) return; // Weird deadlock case
int us = 1000000 / frequency / 2; int us = 1000000 / frequency / 2;
...@@ -91,12 +85,7 @@ void tone(uint8_t pin, unsigned int frequency, unsigned long duration) { ...@@ -91,12 +85,7 @@ void tone(uint8_t pin, unsigned int frequency, unsigned long duration) {
} }
void noTone(uint8_t pin) { void noTone(uint8_t pin) {
if (!_mutexInitted) { CoreMutex m(&_toneMutex);
mutex_init(&_mutex);
_mutexInitted = true;
}
CoreMutex m(&_mutex);
if ((pin < 0) || (pin > 29) || !m) { if ((pin < 0) || (pin > 29) || !m) {
DEBUGCORE("ERROR: Illegal pin in tone (%d)\n", pin); DEBUGCORE("ERROR: Illegal pin in tone (%d)\n", pin);
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
*/ */
#include <Arduino.h> #include <Arduino.h>
#include <CoreMutex.h>
#include <hardware/gpio.h> #include <hardware/gpio.h>
#include <hardware/pwm.h> #include <hardware/pwm.h>
#include <hardware/clocks.h> #include <hardware/clocks.h>
...@@ -31,6 +32,8 @@ static uint16_t analogFreq = 1000; ...@@ -31,6 +32,8 @@ static uint16_t analogFreq = 1000;
static bool pwmInitted = false; static bool pwmInitted = false;
static bool adcInitted = false; static bool adcInitted = false;
auto_init_mutex(_dacMutex);
extern "C" void analogWriteFreq(uint32_t freq) { extern "C" void analogWriteFreq(uint32_t freq) {
if (freq == analogFreq) { if (freq == analogFreq) {
return; return;
...@@ -68,7 +71,9 @@ extern "C" void analogWriteResolution(int res) { ...@@ -68,7 +71,9 @@ extern "C" void analogWriteResolution(int res) {
} }
extern "C" void analogWrite(pin_size_t pin, int val) { extern "C" void analogWrite(pin_size_t pin, int val) {
if ((pin < 0) || (pin > 29)) { CoreMutex m(&_dacMutex);
if ((pin < 0) || (pin > 29) || !m) {
DEBUGCORE("ERROR: Illegal analogWrite pin (%d)\n", pin); DEBUGCORE("ERROR: Illegal analogWrite pin (%d)\n", pin);
return; return;
} }
...@@ -92,8 +97,12 @@ extern "C" void analogWrite(pin_size_t pin, int val) { ...@@ -92,8 +97,12 @@ extern "C" void analogWrite(pin_size_t pin, int val) {
pwm_set_gpio_level(pin, val); pwm_set_gpio_level(pin, val);
} }
auto_init_mutex(_adcMutex);
extern "C" int analogRead(pin_size_t pin) { extern "C" int analogRead(pin_size_t pin) {
if ((pin < A0) || (pin > A3)) { CoreMutex m(&_adcMutex);
if ((pin < A0) || (pin > A3) || !m) {
DEBUGCORE("ERROR: Illegal analogRead pin (%d)\n", pin); DEBUGCORE("ERROR: Illegal analogRead pin (%d)\n", pin);
return 0; return 0;
} }
...@@ -106,6 +115,11 @@ extern "C" int analogRead(pin_size_t pin) { ...@@ -106,6 +115,11 @@ extern "C" int analogRead(pin_size_t pin) {
} }
extern "C" float analogReadTemp() { extern "C" float analogReadTemp() {
CoreMutex m(&_adcMutex);
if (!m) {
return 0.0f; // Deadlock
}
if (!adcInitted) { if (!adcInitted) {
adc_init(); adc_init();
} }
......
...@@ -19,42 +19,58 @@ ...@@ -19,42 +19,58 @@
*/ */
#include <Arduino.h> #include <Arduino.h>
#include <CoreMutex.h>
#include <hardware/gpio.h> #include <hardware/gpio.h>
#include <hardware/sync.h> #include <hardware/sync.h>
#include <stack> #include <stack>
#include <map> #include <map>
// Support nested IRQ disable/re-enable // Support nested IRQ disable/re-enable
static std::stack<uint32_t> _irqStack; static std::stack<uint32_t> _irqStack[2];
extern "C" void interrupts() { extern "C" void interrupts() {
if (_irqStack.empty()) { if (_irqStack[get_core_num()].empty()) {
// ERROR // ERROR
return; return;
} }
restore_interrupts(_irqStack.top()); restore_interrupts(_irqStack[get_core_num()].top());
_irqStack.pop(); _irqStack[get_core_num()].pop();
} }
extern "C" void noInterrupts() { extern "C" void noInterrupts() {
_irqStack.push(save_and_disable_interrupts()); _irqStack[get_core_num()].push(save_and_disable_interrupts());
} }
// Only 1 GPIO IRQ callback for all pins, so we need to look at the pin it's for and // Only 1 GPIO IRQ callback for all pins, so we need to look at the pin it's for and
// dispatch to the real callback manually // dispatch to the real callback manually
auto_init_mutex(_irqMutex);
static std::map<pin_size_t, voidFuncPtr> _map; static std::map<pin_size_t, voidFuncPtr> _map;
void _gpioInterruptDispatcher(uint gpio, uint32_t events) { void _gpioInterruptDispatcher(uint gpio, uint32_t events) {
auto irq = _map.find(gpio); // Only need to lock around the std::map check, not the whole IRQ callback
if (irq != _map.end()) { voidFuncPtr cb = nullptr;
// Ignore events, only one event per pin supported by Arduino {
irq->second(); // Do the callback CoreMutex m(&_irqMutex);
if (m) {
auto irq = _map.find(gpio);
if (irq != _map.end()) {
cb = irq->second;
}
}
}
if (cb) {
cb(); // Do the callback
} else { } else {
// ERROR, but we're in an IRQ so do nothing // ERROR, but we're in an IRQ so do nothing
} }
} }
extern "C" void attachInterrupt(pin_size_t pin, voidFuncPtr callback, PinStatus mode) { extern "C" void attachInterrupt(pin_size_t pin, voidFuncPtr callback, PinStatus mode) {
CoreMutex m(&_irqMutex);
if (!m) {
return;
}
uint32_t events; uint32_t events;
switch (mode) { switch (mode) {
case LOW: events = 1; break; case LOW: events = 1; break;
...@@ -72,6 +88,11 @@ extern "C" void attachInterrupt(pin_size_t pin, voidFuncPtr callback, PinStatus ...@@ -72,6 +88,11 @@ extern "C" void attachInterrupt(pin_size_t pin, voidFuncPtr callback, PinStatus
} }
extern "C" void detachInterrupt(pin_size_t pin) { extern "C" void detachInterrupt(pin_size_t pin) {
CoreMutex m(&_irqMutex);
if (!m) {
return;
}
noInterrupts(); noInterrupts();
auto irq = _map.find(pin); auto irq = _map.find(pin);
if (irq != _map.end()) { if (irq != _map.end()) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment