Skip to content

Commit d3ecb1b

Browse files
committed
tone() auto-release ownership
1 parent e26d8b2 commit d3ecb1b

File tree

3 files changed

+65
-34
lines changed

3 files changed

+65
-34
lines changed

cores/nRF5/HardwarePWM.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ bool can_stringify_token(uintptr_t token)
6565
return true;
6666
}
6767

68+
6869
void HardwarePWM::DebugOutput(Stream& logger)
6970
{
7071
const size_t count = arrcount(HwPWMx);
@@ -79,7 +80,7 @@ void HardwarePWM::DebugOutput(Stream& logger)
7980
logger.printf(" \"%c%c%c%c\"", t[0], t[1], t[2], t[3] );
8081
} else {
8182
static_assert(sizeof(uintptr_t) == 4);
82-
logger.printf(" %08x ", token);
83+
logger.printf(" %08x", token);
8384
}
8485
for (size_t j = 0; j < MAX_CHANNELS; j++) {
8586
uint32_t r = pwm->_pwm->PSEL.OUT[i]; // only read it once
@@ -108,6 +109,14 @@ bool HardwarePWM::takeOwnership(uintptr_t token)
108109
// returns true ONLY when (1) no PWM channel has a pin attached, and (2) the owner token matches
109110
bool HardwarePWM::releaseOwnership(uintptr_t token)
110111
{
112+
if (!releaseOwnershipFromISR(token)) {
113+
LOG_LV1("HwPWM", "Failed to release ownership of PWM %p using token %x", _pwm, token);
114+
return false;
115+
}
116+
return true;
117+
}
118+
bool HardwarePWM::releaseOwnershipFromISR(uintptr_t token) {
119+
// Do not do any logging if called from ISR ...
111120
if (token == 0) return false; // cannot release ownership with nullptr
112121
if (!this->isOwner(token) ) return false; // don't even look at peripheral
113122
if ( this->usedChannelCount() != 0) return false; // fail if any channels still have pins

cores/nRF5/HardwarePWM.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class HardwarePWM
5050
private:
5151
enum { MAX_CHANNELS = 4 }; // Max channel per group
5252
NRF_PWM_Type * const _pwm;
53-
uintptr_t _owner_token = 0;
53+
volatile uintptr_t _owner_token = 0;
5454

5555
uint16_t _seq0[MAX_CHANNELS];
5656

@@ -74,6 +74,8 @@ class HardwarePWM
7474
bool takeOwnership (uintptr_t token);
7575
// returns true ONLY when (1) no PWM channel has a pin attached, and (2) the owner token matches
7676
bool releaseOwnership(uintptr_t token);
77+
// As above, but ensured safe to call from ISR
78+
bool releaseOwnershipFromISR(uintptr_t token);
7779

7880
// allows caller to verify that they own the peripheral
7981
__INLINE bool isOwner(uintptr_t token) const

cores/nRF5/Tone.cpp

Lines changed: 52 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,23 @@ Version Modified By Date Comments
3838
#include "Tone.h"
3939
#include "WVariant.h"
4040

41-
unsigned long int count_duration=0;
41+
volatile unsigned long int count_duration=0;
4242
volatile bool no_stop = false;
4343
uint8_t pin_sound=0;
4444
static uintptr_t _toneToken = 0x656e6f54; // 'T' 'o' 'n' 'e'
4545

46+
// NOTE: Currently hard-coded to only use PWM2 ...
47+
// These are the relvant hard-coded variables
48+
// (plus the ISR PWM2_IRQHandler)
49+
static IRQn_Type const _IntNo = PWM2_IRQn;
50+
static NRF_PWM_Type * const _PWMInstance = NRF_PWM2;
51+
static HardwarePWM * const _HwPWM = HwPWMx[2];
52+
4653
void tone(uint8_t pin, unsigned int frequency, unsigned long duration)
4754
{
4855
static_assert(sizeof(unsigned long) == sizeof(uint32_t));
49-
56+
bool new_no_stop;
57+
unsigned long int new_count_duration = (unsigned long int)-1L;
5058
unsigned int time_per=0;
5159

5260
// limit frequency to reasonable audible range
@@ -55,9 +63,15 @@ void tone(uint8_t pin, unsigned int frequency, unsigned long duration)
5563
return;
5664
}
5765

66+
// set nostop to true to avoid race condition.
67+
// Specifically, race between a tone finishing
68+
// after checking for ownership (which releases ownership)
69+
// No effect if a tone is not playing....
70+
no_stop = true;
71+
5872
// Use fixed PWM2 (due to need to connect interrupt)
59-
if (!HwPWMx[2]->isOwner(_toneToken) &&
60-
!HwPWMx[2]->takeOwnership(_toneToken)) {
73+
if (!_HwPWM->isOwner(_toneToken) &&
74+
!_HwPWM->takeOwnership(_toneToken)) {
6175
LOG_LV1("TON", "unable to allocate PWM2 to Tone");
6276
return;
6377
}
@@ -66,15 +80,15 @@ void tone(uint8_t pin, unsigned int frequency, unsigned long duration)
6680
time_per=per/0.000008;
6781
unsigned int duty=time_per/2;
6882
if(duration > 0) {
69-
no_stop = false;
83+
new_no_stop = false;
7084
float mil=float(duration)/1000;
7185
if(per>mil) {
72-
count_duration=1;
86+
new_count_duration = 1;
7387
} else {
74-
count_duration= mil/per;
88+
new_count_duration = mil/per;
7589
}
7690
} else {
77-
no_stop = true;
91+
new_no_stop = true;
7892
}
7993

8094
// Configure PWM
@@ -88,42 +102,45 @@ void tone(uint8_t pin, unsigned int frequency, unsigned long duration)
88102
0,
89103
0
90104
};
91-
92-
// Use fixed PWM2, TODO could conflict with other usage
105+
93106
uint32_t pins[NRF_PWM_CHANNEL_COUNT]={NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED};
94107
pins[0] = g_ADigitalPinMap[pin];
95108

96-
IRQn_Type IntNo = PWM2_IRQn;
97-
NRF_PWM_Type * PWMInstance = NRF_PWM2;
98-
99-
nrf_pwm_pins_set(PWMInstance, pins);
100-
nrf_pwm_enable(PWMInstance);
101-
nrf_pwm_configure(PWMInstance, NRF_PWM_CLK_125kHz, NRF_PWM_MODE_UP, time_per);
102-
nrf_pwm_decoder_set(PWMInstance, NRF_PWM_LOAD_COMMON, NRF_PWM_STEP_AUTO);
103-
nrf_pwm_sequence_set(PWMInstance, 0, &seq);
104-
nrf_pwm_shorts_enable(PWMInstance, NRF_PWM_SHORT_SEQEND0_STOP_MASK); // shortcut for when SEQ0 ends, PWM output will automatically stop
105-
106109
// enable interrupt
107-
nrf_pwm_event_clear(PWMInstance, NRF_PWM_EVENT_PWMPERIODEND);
108-
nrf_pwm_int_enable(PWMInstance, NRF_PWM_INT_PWMPERIODEND_MASK);
109-
NVIC_SetPriority(IntNo, 6); //low priority
110-
NVIC_ClearPendingIRQ(IntNo);
111-
NVIC_EnableIRQ(IntNo);
112-
113-
nrf_pwm_task_trigger(PWMInstance, NRF_PWM_TASK_SEQSTART0);
110+
count_duration = 0x6FFF; // large enough to avoid hitting zero in next few lines
111+
no_stop = new_no_stop;
112+
113+
nrf_pwm_pins_set(_PWMInstance, pins);
114+
nrf_pwm_enable(_PWMInstance);
115+
nrf_pwm_configure(_PWMInstance, NRF_PWM_CLK_125kHz, NRF_PWM_MODE_UP, time_per);
116+
nrf_pwm_decoder_set(_PWMInstance, NRF_PWM_LOAD_COMMON, NRF_PWM_STEP_AUTO);
117+
nrf_pwm_sequence_set(_PWMInstance, 0, &seq);
118+
nrf_pwm_shorts_enable(_PWMInstance, NRF_PWM_SHORT_SEQEND0_STOP_MASK); // shortcut for when SEQ0 ends, PWM output will automatically stop
119+
nrf_pwm_event_clear(_PWMInstance, NRF_PWM_EVENT_PWMPERIODEND);
120+
nrf_pwm_int_enable(_PWMInstance, NRF_PWM_INT_PWMPERIODEND_MASK);
121+
NVIC_SetPriority(_IntNo, 6); //low priority
122+
NVIC_ClearPendingIRQ(_IntNo);
123+
NVIC_EnableIRQ(_IntNo);
124+
count_duration = new_count_duration;
125+
nrf_pwm_task_trigger(_PWMInstance, NRF_PWM_TASK_SEQSTART0);
114126
}
115127

116128

117129
void noTone(uint8_t pin)
118130
{
119-
if (!HwPWMx[2]->isOwner(_toneToken)) {
131+
if (!_HwPWM->isOwner(_toneToken)) {
120132
LOG_LV1("TON", "Attempt to set noTone when not the owner of the PWM peripheral. Ignoring call....");
121133
return;
122134
}
123-
124-
NRF_PWM_Type * PWMInstance = NRF_PWM2;
125-
nrf_pwm_task_trigger(PWMInstance, NRF_PWM_TASK_STOP);
126-
nrf_pwm_disable(PWMInstance);
135+
nrf_pwm_task_trigger(_PWMInstance, NRF_PWM_TASK_STOP);
136+
nrf_pwm_disable(_PWMInstance);
137+
_PWMInstance->PSEL.OUT[0] = NRF_PWM_PIN_NOT_CONNECTED;
138+
NVIC_DisableIRQ(_IntNo);
139+
_HwPWM->releaseOwnership(_toneToken);
140+
if (_HwPWM->isOwner(_toneToken)) {
141+
LOG_LV1("TON", "stopped tone, but failed to release ownership of PWM peripheral?");
142+
return;
143+
}
127144
}
128145

129146
#ifdef __cplusplus
@@ -137,6 +154,9 @@ void PWM2_IRQHandler(void){
137154
if(count_duration == 0) {
138155
nrf_pwm_task_trigger(NRF_PWM2, NRF_PWM_TASK_STOP);
139156
nrf_pwm_disable(NRF_PWM2);
157+
_PWMInstance->PSEL.OUT[0] = NRF_PWM_PIN_NOT_CONNECTED;
158+
NVIC_DisableIRQ(PWM2_IRQn);
159+
_HwPWM->releaseOwnershipFromISR(_toneToken);
140160
} else {
141161
nrf_pwm_task_trigger(NRF_PWM2, NRF_PWM_TASK_SEQSTART0);
142162
}

0 commit comments

Comments
 (0)