26
26
27
27
#include "py/mphal.h"
28
28
#include "shared-bindings/neopixel_write/__init__.h"
29
+ #include "nrf_pwm.h"
29
30
30
31
// https://github.com/adafruit/Adafruit_NeoPixel/blob/master/Adafruit_NeoPixel.cpp
31
32
// [[[Begin of the Neopixel NRF52 EasyDMA implementation
76
77
77
78
// find a free PWM device, which is not enabled and has no connected pins
78
79
static NRF_PWM_Type * find_free_pwm (void ) {
79
- NRF_PWM_Type * PWM [3 ] = { NRF_PWM0 , NRF_PWM1 , NRF_PWM2 };
80
+ NRF_PWM_Type * PWM [] = {
81
+ NRF_PWM0 , NRF_PWM1 , NRF_PWM2
82
+ #ifdef NRF_PWM3
83
+ , NRF_PWM3
84
+ #endif
85
+ };
80
86
81
- for ( int device = 0 ; device < 3 ; device ++ ) {
82
- if ( (PWM [device ]-> ENABLE == 0 ) && (PWM [device ]-> PSEL .OUT [0 ] & PWM_PSEL_OUT_CONNECT_Msk )
83
- && (PWM [device ]-> PSEL .OUT [1 ] & PWM_PSEL_OUT_CONNECT_Msk )
84
- && (PWM [device ]-> PSEL .OUT [2 ] & PWM_PSEL_OUT_CONNECT_Msk )
85
- && (PWM [device ]-> PSEL .OUT [3 ] & PWM_PSEL_OUT_CONNECT_Msk ) ) {
87
+ for ( int device = 0 ; device < ARRAY_SIZE (PWM ); device ++ ) {
88
+ if ( (PWM [device ]-> ENABLE == 0 ) &&
89
+ (PWM [device ]-> PSEL .OUT [0 ] & PWM_PSEL_OUT_CONNECT_Msk ) && (PWM [device ]-> PSEL .OUT [1 ] & PWM_PSEL_OUT_CONNECT_Msk ) &&
90
+ (PWM [device ]-> PSEL .OUT [2 ] & PWM_PSEL_OUT_CONNECT_Msk ) && (PWM [device ]-> PSEL .OUT [3 ] & PWM_PSEL_OUT_CONNECT_Msk ) ) {
86
91
return PWM [device ];
87
92
}
88
93
}
@@ -130,34 +135,28 @@ void common_hal_neopixel_write (const digitalio_digitalinout_obj_t* digitalinout
130
135
pixels_pattern [++ pos ] = 0 | (0x8000 ); // Seq end
131
136
132
137
// Set the wave mode to count UP
133
- pwm -> MODE = (PWM_MODE_UPDOWN_Up << PWM_MODE_UPDOWN_Pos );
134
-
135
138
// Set the PWM to use the 16MHz clock
136
- pwm -> PRESCALER = (PWM_PRESCALER_PRESCALER_DIV_1 << PWM_PRESCALER_PRESCALER_Pos );
137
-
138
139
// Setting of the maximum count
139
140
// but keeping it on 16Mhz allows for more granularity just
140
141
// in case someone wants to do more fine-tuning of the timing.
141
- pwm -> COUNTERTOP = ( CTOPVAL << PWM_COUNTERTOP_COUNTERTOP_Pos );
142
+ nrf_pwm_configure ( pwm , NRF_PWM_CLK_16MHz , NRF_PWM_MODE_UP , CTOPVAL );
142
143
143
144
// Disable loops, we want the sequence to repeat only once
144
- pwm -> LOOP = ( PWM_LOOP_CNT_Disabled << PWM_LOOP_CNT_Pos );
145
+ nrf_pwm_loop_set ( pwm , 0 );
145
146
146
147
// On the "Common" setting the PWM uses the same pattern for the
147
- // for supported sequences. The pattern is stored on half-word
148
- // of 16bits
149
- pwm -> DECODER = (PWM_DECODER_LOAD_Common << PWM_DECODER_LOAD_Pos )
150
- | (PWM_DECODER_MODE_RefreshCount << PWM_DECODER_MODE_Pos );
148
+ // for supported sequences. The pattern is stored on half-word of 16bits
149
+ nrf_pwm_decoder_set (pwm , PWM_DECODER_LOAD_Common , PWM_DECODER_MODE_RefreshCount );
151
150
152
- // Pointer to the memory storing the patter
153
- pwm -> SEQ [ 0 ]. PTR = ( uint32_t ) ( pixels_pattern ) << PWM_SEQ_PTR_PTR_Pos ;
151
+ // Pointer to the memory storing the pattern
152
+ nrf_pwm_seq_ptr_set ( pwm , 0 , pixels_pattern );
154
153
155
154
// Calculation of the number of steps loaded from memory.
156
- pwm -> SEQ [ 0 ]. CNT = ( pattern_size / sizeof (uint16_t )) << PWM_SEQ_CNT_CNT_Pos ;
155
+ nrf_pwm_seq_cnt_set ( pwm , 0 , pattern_size / sizeof (uint16_t ));
157
156
158
157
// The following settings are ignored with the current config.
159
- pwm -> SEQ [ 0 ]. REFRESH = 0 ;
160
- pwm -> SEQ [ 0 ]. ENDDELAY = 0 ;
158
+ nrf_pwm_seq_refresh_set ( pwm , 0 , 0 ) ;
159
+ nrf_pwm_seq_end_delay_set ( pwm , 0 , 0 ) ;
161
160
162
161
// The Neopixel implementation is a blocking algorithm. DMA
163
162
// allows for non-blocking operation. To "simulate" a blocking
@@ -167,32 +166,32 @@ void common_hal_neopixel_write (const digitalio_digitalinout_obj_t* digitalinout
167
166
// pwm->INTEN |= (PWM_INTEN_SEQEND0_Enabled<<PWM_INTEN_SEQEND0_Pos);
168
167
169
168
// PSEL must be configured before enabling PWM
170
- pwm -> PSEL . OUT [ 0 ] = ( digitalinout -> pin -> port * 32 + digitalinout -> pin -> pin );
169
+ nrf_pwm_pins_set ( pwm , ( uint32_t []) { digitalinout -> pin -> port * 32 + digitalinout -> pin -> pin , 0xFFFFFFFFUL , 0xFFFFFFFFUL , 0xFFFFFFFFUL } );
171
170
172
171
// Enable the PWM
173
- pwm -> ENABLE = 1 ;
172
+ nrf_pwm_enable ( pwm ) ;
174
173
175
174
// After all of this and many hours of reading the documentation
176
175
// we are ready to start the sequence...
177
- pwm -> EVENTS_SEQEND [ 0 ] = 0 ;
178
- pwm -> TASKS_SEQSTART [ 0 ] = 1 ;
176
+ nrf_pwm_event_clear ( pwm , NRF_PWM_EVENT_SEQEND0 ) ;
177
+ nrf_pwm_task_trigger ( pwm , NRF_PWM_TASK_SEQSTART0 ) ;
179
178
180
179
// But we have to wait for the flag to be set.
181
- while ( !pwm -> EVENTS_SEQEND [ 0 ] ) {
180
+ while ( !nrf_pwm_event_check ( pwm , NRF_PWM_EVENT_SEQEND0 ) ) {
182
181
#ifdef MICROPY_VM_HOOK_LOOP
183
182
MICROPY_VM_HOOK_LOOP
184
183
#endif
185
184
}
186
185
187
186
// Before leave we clear the flag for the event.
188
- pwm -> EVENTS_SEQEND [ 0 ] = 0 ;
187
+ nrf_pwm_event_clear ( pwm , NRF_PWM_EVENT_SEQEND0 ) ;
189
188
190
189
// We need to disable the device and disconnect
191
190
// all the outputs before leave or the device will not
192
191
// be selected on the next call.
193
192
// TODO: Check if disabling the device causes performance issues.
194
- pwm -> ENABLE = 0 ;
195
- pwm -> PSEL . OUT [ 0 ] = 0xFFFFFFFFUL ;
193
+ nrf_pwm_disable ( pwm ) ;
194
+ nrf_pwm_pins_set ( pwm , ( uint32_t []) { 0xFFFFFFFFUL , 0xFFFFFFFFUL , 0xFFFFFFFFUL , 0xFFFFFFFFUL } ) ;
196
195
197
196
m_free (pixels_pattern );
198
197
} // End of DMA implementation
0 commit comments