46
46
47
47
#define NUM_DMA_TIMERS 4
48
48
49
+ // The PWM clock frequency is base_clock_rate / PWM_TOP, typically 125_000_000 / PWM_TOP.
50
+ // We pick BITS_PER_SAMPLE so we get a clock frequency that is above what would cause aliasing.
51
+ #define BITS_PER_SAMPLE 10
52
+ #define SAMPLE_BITS_TO_DISCARD (16 - BITS_PER_SAMPLE)
53
+ #define PWM_TOP ((1 << BITS_PER_SAMPLE) - 1)
54
+
49
55
void audiopwmout_reset () {
50
56
for (size_t i = 0 ; i < NUM_DMA_TIMERS ; i ++ ) {
51
57
dma_hw -> timer [i ] = 0 ;
@@ -55,7 +61,10 @@ void audiopwmout_reset() {
55
61
// Caller validates that pins are free.
56
62
void common_hal_audiopwmio_pwmaudioout_construct (audiopwmio_pwmaudioout_obj_t * self ,
57
63
const mcu_pin_obj_t * left_channel , const mcu_pin_obj_t * right_channel , uint16_t quiescent_value ) {
58
- if (left_channel != NULL && right_channel != NULL ) {
64
+
65
+ self -> stereo = right_channel != NULL ;
66
+
67
+ if (self -> stereo ) {
59
68
if (pwm_gpio_to_slice_num (left_channel -> number ) != pwm_gpio_to_slice_num (right_channel -> number )) {
60
69
mp_raise_ValueError (translate ("Pins must share PWM slice" ));
61
70
}
@@ -72,22 +81,20 @@ void common_hal_audiopwmio_pwmaudioout_construct(audiopwmio_pwmaudioout_obj_t *s
72
81
// we want. ;-) We mark ourselves variable only if we're a mono output to
73
82
// prevent other PWM use on the other channel. If stereo, we say fixed
74
83
// frequency so we can allocate with ourselves.
75
- bool mono = right_channel == NULL ;
76
84
77
- // We don't actually know our frequency yet so just pick one that shouldn't
78
- // match anyone else. (We'll only know the frequency once we play something
79
- // back.)
80
- uint32_t frequency = 12500 ;
85
+ // We don't actually know our frequency yet. It is set when
86
+ // pwmio_pwmout_set_top() is called. This value is unimportant; it just needs to be valid.
87
+ const uint32_t frequency = 12000000 ;
81
88
82
89
// Make sure the PWMOut's are "deinited" by default.
83
90
self -> left_pwm .pin = NULL ;
84
91
self -> right_pwm .pin = NULL ;
85
92
86
- pwmout_result_t result = common_hal_pwmio_pwmout_construct ( & self -> left_pwm ,
87
- left_channel , 0 , frequency , mono );
93
+ pwmout_result_t result =
94
+ common_hal_pwmio_pwmout_construct ( & self -> left_pwm , left_channel , 0 , frequency , ! self -> stereo );
88
95
if (result == PWMOUT_OK && right_channel != NULL ) {
89
- result = common_hal_pwmio_pwmout_construct ( & self -> right_pwm ,
90
- right_channel , 0 , frequency , false);
96
+ result =
97
+ common_hal_pwmio_pwmout_construct ( & self -> right_pwm , right_channel , 0 , frequency , false);
91
98
if (result != PWMOUT_OK ) {
92
99
common_hal_pwmio_pwmout_deinit (& self -> left_pwm );
93
100
}
@@ -96,10 +103,16 @@ void common_hal_audiopwmio_pwmaudioout_construct(audiopwmio_pwmaudioout_obj_t *s
96
103
mp_raise_RuntimeError (translate ("All timers in use" ));
97
104
}
98
105
106
+ self -> quiescent_value = quiescent_value >> SAMPLE_BITS_TO_DISCARD ;
107
+ common_hal_pwmio_pwmout_set_duty_cycle (& self -> left_pwm , self -> quiescent_value );
108
+ pwmio_pwmout_set_top (& self -> left_pwm , PWM_TOP );
109
+ if (self -> stereo ) {
110
+ common_hal_pwmio_pwmout_set_duty_cycle (& self -> right_pwm , self -> quiescent_value );
111
+ pwmio_pwmout_set_top (& self -> right_pwm , PWM_TOP );
112
+ }
113
+
99
114
audio_dma_init (& self -> dma );
100
115
self -> pacing_timer = NUM_DMA_TIMERS ;
101
-
102
- self -> quiescent_value = quiescent_value ;
103
116
}
104
117
105
118
bool common_hal_audiopwmio_pwmaudioout_deinited (audiopwmio_pwmaudioout_obj_t * self ) {
@@ -110,6 +123,7 @@ void common_hal_audiopwmio_pwmaudioout_deinit(audiopwmio_pwmaudioout_obj_t *self
110
123
if (common_hal_audiopwmio_pwmaudioout_deinited (self )) {
111
124
return ;
112
125
}
126
+
113
127
if (common_hal_audiopwmio_pwmaudioout_get_playing (self )) {
114
128
common_hal_audiopwmio_pwmaudioout_stop (self );
115
129
}
@@ -139,29 +153,28 @@ void common_hal_audiopwmio_pwmaudioout_play(audiopwmio_pwmaudioout_obj_t *self,
139
153
mp_raise_RuntimeError (translate ("No DMA pacing timer found" ));
140
154
}
141
155
uint32_t tx_register = (uint32_t )& pwm_hw -> slice [self -> left_pwm .slice ].cc ;
142
- if (common_hal_pwmio_pwmout_deinited ( & self -> right_pwm ) ) {
143
- // Shift the destination if we are outputting to the second PWM channel .
156
+ if (self -> stereo ) {
157
+ // Shift the destination if we are outputting to both PWM channels .
144
158
tx_register += self -> left_pwm .channel * sizeof (uint16_t );
145
159
}
146
160
147
- pwmio_pwmout_set_top (& self -> left_pwm , 1023 );
148
-
149
161
audio_dma_result result = audio_dma_setup_playback (
150
162
& self -> dma ,
151
163
sample ,
152
164
loop ,
153
165
false, // single channel
154
166
0 , // audio channel
155
167
false, // output signed
156
- 10 ,
157
- (uint32_t )tx_register , // output register
168
+ BITS_PER_SAMPLE ,
169
+ (uint32_t )tx_register , // output register: PWM cc register
158
170
0x3b + pacing_timer ); // data request line
159
171
160
172
if (result == AUDIO_DMA_DMA_BUSY ) {
161
- // common_hal_audiobusio_i2sout_stop (self);
173
+ common_hal_audiopwmio_pwmaudioout_stop (self );
162
174
mp_raise_RuntimeError (translate ("No DMA channel found" ));
163
- } else if (result == AUDIO_DMA_MEMORY_ERROR ) {
164
- // common_hal_audiobusio_i2sout_stop(self);
175
+ }
176
+ if (result == AUDIO_DMA_MEMORY_ERROR ) {
177
+ common_hal_audiopwmio_pwmaudioout_stop (self );
165
178
mp_raise_RuntimeError (translate ("Unable to allocate buffers for signed conversion" ));
166
179
}
167
180
@@ -177,6 +190,7 @@ void common_hal_audiopwmio_pwmaudioout_play(audiopwmio_pwmaudioout_obj_t *self,
177
190
// are 16-bit.
178
191
179
192
uint32_t sample_rate = audiosample_sample_rate (sample );
193
+
180
194
uint32_t system_clock = common_hal_mcu_processor_get_frequency ();
181
195
uint32_t best_numerator = 0 ;
182
196
uint32_t best_denominator = 0 ;
@@ -204,19 +218,25 @@ void common_hal_audiopwmio_pwmaudioout_play(audiopwmio_pwmaudioout_obj_t *self,
204
218
}
205
219
206
220
void common_hal_audiopwmio_pwmaudioout_stop (audiopwmio_pwmaudioout_obj_t * self ) {
207
- dma_hw -> timer [self -> pacing_timer ] = 0 ;
208
- self -> pacing_timer = NUM_DMA_TIMERS ;
221
+ if (self -> pacing_timer < NUM_DMA_TIMERS ) {
222
+ dma_hw -> timer [self -> pacing_timer ] = 0 ;
223
+ self -> pacing_timer = NUM_DMA_TIMERS ;
224
+ }
209
225
210
226
audio_dma_stop (& self -> dma );
227
+
228
+ // Set to quiescent level.
229
+ pwm_hw -> slice [self -> left_pwm .slice ].cc = self -> quiescent_value ;
230
+ if (self -> stereo ) {
231
+ pwm_hw -> slice [self -> right_pwm .slice ].cc = self -> quiescent_value ;
232
+ }
211
233
}
212
234
213
235
bool common_hal_audiopwmio_pwmaudioout_get_playing (audiopwmio_pwmaudioout_obj_t * self ) {
214
236
bool playing = audio_dma_get_playing (& self -> dma );
215
- if (!playing && self -> pacing_timer < NUM_DMA_TIMERS ) {
216
- dma_hw -> timer [self -> pacing_timer ] = 0 ;
217
- self -> pacing_timer = NUM_DMA_TIMERS ;
218
237
219
- audio_dma_stop (& self -> dma );
238
+ if (!playing && self -> pacing_timer < NUM_DMA_TIMERS ) {
239
+ common_hal_audiopwmio_pwmaudioout_stop (self );
220
240
}
221
241
222
242
return playing ;
0 commit comments