@@ -54,10 +54,10 @@ HardwarePWM* HwPWMx[] =
5454};
5555
5656#if CFG_DEBUG
57- bool can_stringify_token (uintptr_t token)
57+ bool can_stringify_token (uint32_t token)
5858{
5959 uint8_t * t = (uint8_t *)&token;
60- for (size_t i = 0 ; i < sizeof (uintptr_t ); ++i, ++t)
60+ for (size_t i = 0 ; i < sizeof (uint32_t ); ++i, ++t)
6161 {
6262 uint8_t x = *t;
6363 if ((x < 0x20 ) || (x > 0x7E )) return false ;
@@ -71,14 +71,12 @@ void HardwarePWM::DebugOutput(Stream& logger)
7171 logger.printf (" HwPWM Debug:" );
7272 for (size_t i = 0 ; i < count; i++) {
7373 HardwarePWM const * pwm = HwPWMx[i];
74- uintptr_t token = pwm->_owner_token ;
74+ uint32_t token = pwm->_owner_token ;
7575 logger.printf (" || %d:" , i);
7676 if (can_stringify_token (token)) {
7777 uint8_t * t = (uint8_t *)(&token);
78- static_assert (sizeof (uintptr_t ) == 4 );
7978 logger.printf (" \" %c%c%c%c\" " , t[0 ], t[1 ], t[2 ], t[3 ] );
8079 } else {
81- static_assert (sizeof (uintptr_t ) == 4 );
8280 logger.printf (" %08x" , token);
8381 }
8482 for (size_t j = 0 ; j < MAX_CHANNELS; j++) {
@@ -96,76 +94,6 @@ void HardwarePWM::DebugOutput(Stream& logger)
9694void HardwarePWM::DebugOutput (Stream& logger) {}
9795#endif // CFG_DEBUG
9896
99- // returns true ONLY when (1) no PWM channel has a pin, and (2) the owner token is nullptr
100- bool HardwarePWM::takeOwnership (uintptr_t token)
101- {
102- bool notInIsr = !isInISR ();
103- if (token == 0 ) {
104- if (notInIsr) {
105- LOG_LV1 (" HwPWM" , " zero / nullptr is not a valid ownership token (attempted use in takeOwnership)" );
106- }
107- return false ; // cannot take ownership with nullptr
108- }
109- if (token == this ->_owner_token ) {
110- if (notInIsr) {
111- LOG_LV1 (" HwPWM" , " failing to acquire ownership because already owned by requesting token (cannot take ownership twice)" );
112- }
113- }
114- if (this ->_owner_token != 0 ) {
115- return false ;
116- }
117- if (this ->usedChannelCount () != 0 ) {
118- return false ;
119- }
120- if (this ->enabled ()) {
121- return false ;
122- }
123- // TODO: warn, but do not fail, if taking ownership with IRQs already enabled
124- // NVIC_GetActive
125-
126- // Use C++11 atomic CAS operation
127- uintptr_t newValue = 0U ;
128- return this ->_owner_token .compare_exchange_strong (newValue, token);
129- }
130- // returns true ONLY when (1) no PWM channel has a pin attached, and (2) the owner token matches
131- bool HardwarePWM::releaseOwnership (uintptr_t token)
132- {
133- bool notInIsr = !isInISR ();
134- if (token == 0 ) {
135- if (notInIsr) {
136- LOG_LV1 (" HwPWM" , " zero / nullptr is not a valid ownership token (attempted use in releaseOwnership)" );
137- }
138- return false ;
139- }
140- if (!this ->isOwner (token)) {
141- if (notInIsr) {
142- LOG_LV1 (" HwPWM" , " attempt to release ownership when not the current owner" );
143- }
144- return false ;
145- }
146- if (this ->usedChannelCount () != 0 ) {
147- if (notInIsr) {
148- LOG_LV1 (" HwPWM" , " attempt to release ownership when at least on channel is still connected" );
149- }
150- return false ;
151- }
152- if (this ->enabled ()) {
153- if (notInIsr) {
154- LOG_LV1 (" HwPWM" , " attempt to release ownership when PWM peripheral is still enabled" );
155- }
156- return false ; // if it's enabled, do not allow ownership to be released, even with no pins in use
157- }
158- // TODO: warn, but do not fail, if releasing ownership with IRQs enabled
159- // NVIC_GetActive
160-
161- // Use C++11 atomic CAS operation
162- bool result = this ->_owner_token .compare_exchange_strong (token, 0U );
163- if (!result) {
164- LOG_LV1 (" HwPWM" , " race condition resulted in failure to acquire ownership" );
165- }
166- return result;
167- }
168-
16997HardwarePWM::HardwarePWM (NRF_PWM_Type* pwm) :
17098 _pwm(pwm)
17199{
@@ -179,6 +107,38 @@ HardwarePWM::HardwarePWM(NRF_PWM_Type* pwm) :
179107 _pwm->PSEL .OUT [1 ] = 0xFFFFFFFFUL ;
180108}
181109
110+ void HardwarePWM::begin (void )
111+ {
112+ // Initialize Registers
113+ _pwm->MODE = PWM_MODE_UPDOWN_Up;
114+ _pwm->COUNTERTOP = _max_value; // default is 255 (8 bit), can be configured before begin()
115+ _pwm->PRESCALER = _clock_div;
116+ _pwm->DECODER = PWM_DECODER_LOAD_Individual;
117+ _pwm->LOOP = 0 ;
118+
119+ _pwm->SEQ [0 ].PTR = (uint32_t ) _seq0;
120+ _pwm->SEQ [0 ].CNT = MAX_CHANNELS; // default mode is Individual --> count must be 4
121+ _pwm->SEQ [0 ].REFRESH = 0 ;
122+ _pwm->SEQ [0 ].ENDDELAY = 0 ;
123+
124+ _pwm->SEQ [1 ].PTR = 0 ;
125+ _pwm->SEQ [1 ].CNT = 0 ;
126+ _pwm->SEQ [1 ].REFRESH = 0 ;
127+ _pwm->SEQ [1 ].ENDDELAY = 0 ;
128+
129+ _pwm->ENABLE = 1 ;
130+ }
131+
132+ void HardwarePWM::stop (void )
133+ {
134+ _pwm->ENABLE = 0 ;
135+ }
136+
137+ bool HardwarePWM::enabled (void )
138+ {
139+ return _pwm->ENABLE ;
140+ }
141+
182142void HardwarePWM::setResolution (uint8_t bitnum)
183143{
184144 setMaxValue ( bit (min8 (bitnum, 15 )) -1 );
@@ -196,6 +156,25 @@ void HardwarePWM::setClockDiv(uint8_t div)
196156 _pwm->PRESCALER = _clock_div;
197157}
198158
159+ void HardwarePWM::_set_psel (int ch, uint32_t value)
160+ {
161+ // Must disable before changing PSEL
162+ if ( enabled () )
163+ {
164+ _pwm->ENABLE = 0 ;
165+ _pwm->PSEL .OUT [ch] = value;
166+ _seq0[ch] = 0 ;
167+ _pwm->ENABLE = 1 ;
168+
169+ // re-start sequence
170+ if ( usedChannelCount () ) _pwm->TASKS_SEQSTART [0 ] = 1 ;
171+ }else
172+ {
173+ _pwm->PSEL .OUT [ch] = value;
174+ _seq0[ch] = 0 ;
175+ }
176+ }
177+
199178/* *
200179 * Add pin to this group.
201180 * @param pin Pin to add
@@ -222,17 +201,7 @@ bool HardwarePWM::addPin(uint8_t pin)
222201 pinMode (pin, OUTPUT);
223202 digitalWrite (pin, LOW);
224203
225- // Must disable before changing PSEL
226- if ( enabled () )
227- {
228- _pwm->ENABLE = 0 ;
229- _pwm->PSEL .OUT [ch] = g_ADigitalPinMap[pin];
230- _pwm->ENABLE = 1 ;
231- _start ();
232- }else
233- {
234- _pwm->PSEL .OUT [ch] = g_ADigitalPinMap[pin];
235- }
204+ _set_psel (ch, g_ADigitalPinMap[pin]);
236205
237206 return true ;
238207}
@@ -242,70 +211,21 @@ bool HardwarePWM::removePin(uint8_t pin)
242211 int ch = pin2channel (pin);
243212 VERIFY ( ch >= 0 );
244213
245- bool const en = enabled ();
246-
247- // Must disable before changing PSEL
248- if ( en ) _pwm->ENABLE = 0 ;
249-
250- _pwm->PSEL .OUT [ch] = 0xFFFFFFFFUL ;
251- _seq0[ch] = 0 ;
252-
253- if ( en ) _pwm->ENABLE = 1 ;
254-
214+ _set_psel (ch, 0xFFFFFFFFUL );
255215 return true ;
256216}
257217
258- bool HardwarePWM::enabled (void )
259- {
260- return _pwm->ENABLE ;
261- }
262-
263- void HardwarePWM::begin (void )
264- {
265- // Initialize Registers
266- _pwm->MODE = PWM_MODE_UPDOWN_Up;
267- _pwm->COUNTERTOP = _max_value; // default is 255 (8 bit), can be configured before begin()
268- _pwm->PRESCALER = _clock_div;
269- _pwm->DECODER = PWM_DECODER_LOAD_Individual;
270- _pwm->LOOP = 0 ;
271-
272- _pwm->SEQ [0 ].PTR = (uint32_t ) _seq0;
273- _pwm->SEQ [0 ].CNT = MAX_CHANNELS; // default mode is Individual --> count must be 4
274- _pwm->SEQ [0 ].REFRESH = 0 ;
275- _pwm->SEQ [0 ].ENDDELAY = 0 ;
276-
277- _pwm->SEQ [1 ].PTR = 0 ;
278- _pwm->SEQ [1 ].CNT = 0 ;
279- _pwm->SEQ [1 ].REFRESH = 0 ;
280- _pwm->SEQ [1 ].ENDDELAY = 0 ;
281-
282- _pwm->ENABLE = 1 ;
283- }
284-
285- void HardwarePWM::_start (void )
286- {
287- // update sequence count (depending on mode)
288- // _pwm->SEQ[0].CNT = MAX_CHANNELS;
289-
290- // start sequence
291- _pwm->TASKS_SEQSTART [0 ] = 1 ;
292- }
293-
294- void HardwarePWM::stop (void )
295- {
296- _pwm->ENABLE = 0 ;
297- }
298-
299- bool HardwarePWM::writeChannel (uint8_t ch, uint16_t value, bool inverted )
218+ bool HardwarePWM::writeChannel (uint8_t ch, uint16_t value, bool inverted)
300219{
301220 VERIFY ( ch < MAX_CHANNELS );
302221
303222 _seq0[ch] = value | (inverted ? 0 : bit (15 ));
304223
305- // Start PWM if not already
224+ // Initialize PWM if not already
306225 if ( !enabled () ) begin ();
307226
308- _start ();
227+ // start sequence
228+ _pwm->TASKS_SEQSTART [0 ] = 1 ;
309229
310230 return true ;
311231}
@@ -350,3 +270,55 @@ uint8_t HardwarePWM::freeChannelCount(void) const
350270 return MAX_CHANNELS - usedChannelCount ();
351271}
352272
273+ // returns true ONLY when (1) no PWM channel has a pin, and (2) the owner token is nullptr
274+ bool HardwarePWM::takeOwnership (uint32_t token)
275+ {
276+ bool const thread_mode = !isInISR ();
277+
278+ if (token == 0 ) {
279+ if (thread_mode) LOG_LV1 (" HwPWM" , " zero is not a valid ownership token (attempted use in takeOwnership)" );
280+ return false ;
281+ }
282+
283+ if (token == this ->_owner_token ) {
284+ if (thread_mode) LOG_LV1 (" HwPWM" , " failing to acquire ownership because already owned by requesting token (cannot take ownership twice)" );
285+ return false ;
286+ }
287+
288+ if ( this ->_owner_token != 0 ) return false ;
289+ if ( this ->usedChannelCount () != 0 ) return false ;
290+ if ( this ->enabled () ) return false ;
291+
292+ // Use C++11 atomic CAS operation
293+ uint32_t expectedValue = 0U ;
294+ return this ->_owner_token .compare_exchange_strong (expectedValue, token);
295+ }
296+
297+ // returns true ONLY when (1) no PWM channel has a pin attached, and (2) the owner token matches
298+ bool HardwarePWM::releaseOwnership (uint32_t token)
299+ {
300+ bool const thread_mode = !isInISR ();
301+
302+ if (token == 0 ) {
303+ if (thread_mode) LOG_LV1 (" HwPWM" , " zero is not a valid ownership token (attempted use in releaseOwnership)" );
304+ return false ;
305+ }
306+
307+ if (!this ->isOwner (token)) {
308+ if (thread_mode) LOG_LV1 (" HwPWM" , " attempt to release ownership when not the current owner" );
309+ return false ;
310+ }
311+
312+ if (this ->usedChannelCount () != 0 ) {
313+ if (thread_mode) LOG_LV1 (" HwPWM" , " attempt to release ownership when at least on channel is still connected" );
314+ return false ;
315+ }
316+
317+ if (this ->enabled ()) {
318+ if (thread_mode) LOG_LV1 (" HwPWM" , " attempt to release ownership when PWM peripheral is still enabled" );
319+ return false ; // if it's enabled, do not allow ownership to be released, even with no pins in use
320+ }
321+
322+ // Use C++11 atomic CAS operation
323+ return this ->_owner_token .compare_exchange_strong (token, 0U );
324+ }
0 commit comments