Skip to content

Commit 58a6b3f

Browse files
committed
analogWrite() avoids hostile takeover of PWM peripherals
1 parent 4e98fb4 commit 58a6b3f

File tree

1 file changed

+58
-8
lines changed

1 file changed

+58
-8
lines changed

cores/nRF5/wiring_analog.cpp

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,30 @@
4545
extern "C"
4646
{
4747

48+
class INTERNAL_ANALOG_STATE {
49+
public:
50+
uint8_t _lastAnalogWriteResolution = 8; // default value is 8-bit resolution (0..255)
51+
uint8_t _moduleUsedByAnalogWrites[HWPWM_MODULE_NUM];
52+
INTERNAL_ANALOG_STATE() : _lastAnalogWriteResolution(8) {
53+
for (int i = 0; i < HWPWM_MODULE_NUM; i++) {
54+
_moduleUsedByAnalogWrites[i] = 0;
55+
}
56+
}
57+
};
58+
59+
static INTERNAL_ANALOG_STATE _AnalogState;
60+
4861
/**
49-
* This will apply to all PWM Hardware
62+
* This will apply to all PWM Hardware currently used by analogWrite(),
63+
* and automatically apply to future calls to analogWrite().
5064
*/
5165
void analogWriteResolution( uint8_t res )
5266
{
67+
// save the resolution for when adding a new instance
68+
_AnalogState._lastAnalogWriteResolution = res;
5369
for (int i = 0; i<HWPWM_MODULE_NUM; i++)
5470
{
71+
if (!_AnalogState._moduleUsedByAnalogWrites[i]) continue;
5572
HwPWMx[i]->setResolution(res);
5673
}
5774
}
@@ -65,17 +82,50 @@ void analogWriteResolution( uint8_t res )
6582
*/
6683
void analogWrite( uint32_t pin, uint32_t value )
6784
{
85+
// If the pin is already in use for analogWrite, this should be fast
86+
// If the pin is not already in use, then it's OK to take slightly more time to setup
87+
88+
// first attempt to find the pin in any of the HWPWM modules used for analog writes
6889
for(int i=0; i<HWPWM_MODULE_NUM; i++)
6990
{
70-
// Added by if needed
71-
if ( HwPWMx[i]->addPin(pin) )
72-
{
73-
HwPWMx[i]->writePin(pin, value);
74-
return;
75-
}
91+
if (!_AnalogState._moduleUsedByAnalogWrites[i]) continue;
92+
int const ch = HwPWMx[i]->pin2channel(pin);
93+
if (ch < 0) continue; // pin not in use by the PWM instance
94+
// already in use by this PWM instance as channel ch ...
95+
HwPWMx[i]->writeChannel(ch, value);
96+
return;
7697
}
77-
}
7898

99+
// Next try adding only to those modules that are already in use
100+
for(int i=0; i<HWPWM_MODULE_NUM; i++)
101+
{
102+
if (!_AnalogState._moduleUsedByAnalogWrites[i]) continue;
103+
if (!HwPWMx[i]->addPin(pin)) continue;
104+
// added to an already-used PWM instance, so write the value
105+
HwPWMx[i]->writePin(pin, value);
106+
return;
107+
}
108+
109+
// Attempt to acquire a new HwPWMx instance ... but only where
110+
// 1. it's not one already used for analog, and
111+
// 2. it currently has no pins in use.
112+
for(int i=0; i<HWPWM_MODULE_NUM; i++)
113+
{
114+
if (_AnalogState._moduleUsedByAnalogWrites[i]) continue;
115+
if (HwPWMx[i]->usedChannelCount() != 0) continue;
116+
// added to an already-used PWM instance, so write the value
117+
HwPWMx[i]->setResolution(_AnalogState._lastAnalogWriteResolution);
118+
HwPWMx[i]->writePin(pin, value);
119+
return;
120+
}
121+
122+
// failed to allocate a HwPWM instance.
123+
// output appropriate debug message.
124+
LOG_LV1("ANA", "Unable to find a free PWM peripheral");
125+
// TODO: Add additional diagnostics to function at higher log levels
126+
return;
79127
}
80128

129+
} // end extern "C"
130+
81131

0 commit comments

Comments
 (0)