Skip to content

Commit 298756e

Browse files
authored
Merge pull request #1280 from tannewt/precondition_dac
Ramp values to and from a default value while active.
2 parents 8161178 + 4eb1fe1 commit 298756e

File tree

6 files changed

+94
-20
lines changed

6 files changed

+94
-20
lines changed

ports/atmel-samd/common-hal/analogio/AnalogOut.c

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -138,16 +138,5 @@ void common_hal_analogio_analogout_set_value(analogio_analogout_obj_t *self,
138138
}
139139

140140
void analogout_reset(void) {
141-
#if defined(SAMD21) && !defined(PIN_PA02)
142-
return;
143-
#endif
144-
#ifdef SAMD21
145-
while (DAC->STATUS.reg & DAC_STATUS_SYNCBUSY) {}
146-
#endif
147-
#ifdef SAMD51
148-
while (DAC->SYNCBUSY.reg & DAC_SYNCBUSY_SWRST) {}
149-
#endif
150-
DAC->CTRLA.reg |= DAC_CTRLA_SWRST;
151-
152-
// TODO(tannewt): Turn off the DAC clocks to save power.
141+
// AudioOut resets the DAC in case its been used for audio which requires special handling.
153142
}

ports/atmel-samd/common-hal/audioio/AudioOut.c

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "py/runtime.h"
3434
#include "common-hal/audioio/AudioOut.h"
3535
#include "shared-bindings/audioio/AudioOut.h"
36+
#include "shared-bindings/microcontroller/__init__.h"
3637
#include "shared-bindings/microcontroller/Pin.h"
3738
#include "supervisor/shared/translate.h"
3839

@@ -52,11 +53,73 @@
5253
#include "samd/pins.h"
5354
#include "samd/timers.h"
5455

56+
#ifdef SAMD21
57+
static void ramp_value(uint16_t start, uint16_t end) {
58+
start = DAC->DATA.reg;
59+
int32_t diff = (int32_t) end - start;
60+
int32_t step = 49;
61+
int32_t steps = diff / step;
62+
if (diff < 0) {
63+
steps = -steps;
64+
step = -step;
65+
}
66+
for (int32_t i = 0; i < steps; i++) {
67+
uint32_t value = start + step * i;
68+
DAC->DATA.reg = value;
69+
DAC->DATABUF.reg = value;
70+
common_hal_mcu_delay_us(50);
71+
#ifdef MICROPY_VM_HOOK_LOOP
72+
MICROPY_VM_HOOK_LOOP
73+
#endif
74+
}
75+
}
76+
#endif
77+
78+
#ifdef SAMD51
79+
static void ramp_value(uint16_t start, uint16_t end) {
80+
int32_t diff = (int32_t) end - start;
81+
int32_t step = 49;
82+
int32_t steps = diff / step;
83+
if (diff < 0) {
84+
steps = -steps;
85+
step = -step;
86+
}
87+
88+
for (int32_t i = 0; i < steps; i++) {
89+
uint16_t value = start + step * i;
90+
DAC->DATA[0].reg = value;
91+
DAC->DATABUF[0].reg = value;
92+
DAC->DATA[1].reg = value;
93+
DAC->DATABUF[1].reg = value;
94+
95+
common_hal_mcu_delay_us(50);
96+
#ifdef MICROPY_VM_HOOK_LOOP
97+
MICROPY_VM_HOOK_LOOP
98+
#endif
99+
}
100+
}
101+
#endif
102+
55103
void audioout_reset(void) {
104+
#if defined(SAMD21) && !defined(PIN_PA02)
105+
return;
106+
#endif
107+
#ifdef SAMD21
108+
while (DAC->STATUS.reg & DAC_STATUS_SYNCBUSY) {}
109+
#endif
110+
#ifdef SAMD51
111+
while (DAC->SYNCBUSY.reg & DAC_SYNCBUSY_SWRST) {}
112+
#endif
113+
if (DAC->CTRLA.bit.ENABLE) {
114+
ramp_value(0x8000, 0);
115+
}
116+
DAC->CTRLA.reg |= DAC_CTRLA_SWRST;
117+
118+
// TODO(tannewt): Turn off the DAC clocks to save power.
56119
}
57120

58121
void common_hal_audioio_audioout_construct(audioio_audioout_obj_t* self,
59-
const mcu_pin_obj_t* left_channel, const mcu_pin_obj_t* right_channel) {
122+
const mcu_pin_obj_t* left_channel, const mcu_pin_obj_t* right_channel, uint16_t quiescent_value) {
60123
#ifdef SAMD51
61124
bool dac_clock_enabled = hri_mclk_get_APBDMASK_DAC_bit(MCLK);
62125
#endif
@@ -94,12 +157,10 @@ void common_hal_audioio_audioout_construct(audioio_audioout_obj_t* self,
94157
if (right_channel != NULL) {
95158
claim_pin(right_channel);
96159
self->right_channel = right_channel;
97-
gpio_set_pin_function(self->right_channel->number, GPIO_PIN_FUNCTION_B);
98160
audio_dma_init(&self->right_dma);
99161
}
100162
#endif
101163
self->left_channel = left_channel;
102-
gpio_set_pin_function(self->left_channel->number, GPIO_PIN_FUNCTION_B);
103164
audio_dma_init(&self->left_dma);
104165

105166
#ifdef SAMD51
@@ -118,6 +179,10 @@ void common_hal_audioio_audioout_construct(audioio_audioout_obj_t* self,
118179

119180
DAC->CTRLA.bit.SWRST = 1;
120181
while (DAC->CTRLA.bit.SWRST == 1) {}
182+
// Make sure there are no outstanding access errors. (Reading DATA can cause this.)
183+
#ifdef SAMD51
184+
PAC->INTFLAGD.reg = PAC_INTFLAGD_DAC;
185+
#endif
121186

122187
bool channel0_enabled = true;
123188
#ifdef SAMD51
@@ -159,6 +224,8 @@ void common_hal_audioio_audioout_construct(audioio_audioout_obj_t* self,
159224
#endif
160225
#ifdef SAMD51
161226
while (DAC->SYNCBUSY.bit.ENABLE == 1) {}
227+
while (channel0_enabled && DAC->STATUS.bit.READY0 == 0) {}
228+
while (channel1_enabled && DAC->STATUS.bit.READY1 == 0) {}
162229
#endif
163230

164231
// Use a timer to coordinate when DAC conversions occur.
@@ -220,13 +287,21 @@ void common_hal_audioio_audioout_construct(audioio_audioout_obj_t* self,
220287

221288
#ifdef SAMD51
222289
connect_event_user_to_channel(EVSYS_ID_USER_DAC_START_1, channel);
290+
if (right_channel != NULL) {
291+
gpio_set_pin_function(self->right_channel->number, GPIO_PIN_FUNCTION_B);
292+
}
223293
#define EVSYS_ID_USER_DAC_START EVSYS_ID_USER_DAC_START_0
224294
#endif
225295
connect_event_user_to_channel(EVSYS_ID_USER_DAC_START, channel);
296+
gpio_set_pin_function(self->left_channel->number, GPIO_PIN_FUNCTION_B);
226297
init_async_event_channel(channel, tc_gen_id);
227298

228299
self->tc_to_dac_event_channel = channel;
229300

301+
// Ramp the DAC up.
302+
self->quiescent_value = quiescent_value;
303+
ramp_value(0, quiescent_value);
304+
230305
// Leave the DMA setup to playback.
231306
}
232307

@@ -239,6 +314,9 @@ void common_hal_audioio_audioout_deinit(audioio_audioout_obj_t* self) {
239314
return;
240315
}
241316

317+
// Ramp the DAC down.
318+
ramp_value(self->quiescent_value, 0);
319+
242320
DAC->CTRLA.bit.ENABLE = 0;
243321
#ifdef SAMD21
244322
while (DAC->STATUS.bit.SYNCBUSY == 1) {}
@@ -381,6 +459,9 @@ void common_hal_audioio_audioout_stop(audioio_audioout_obj_t* self) {
381459
#ifdef SAMD51
382460
audio_dma_stop(&self->right_dma);
383461
#endif
462+
// Ramp the DAC to default. The start is ignored when the current value can be readback.
463+
// Otherwise, we just set it immediately.
464+
ramp_value(self->quiescent_value, self->quiescent_value);
384465
}
385466

386467
bool common_hal_audioio_audioout_get_playing(audioio_audioout_obj_t* self) {

ports/atmel-samd/common-hal/audioio/AudioOut.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ typedef struct {
4444

4545
uint8_t tc_to_dac_event_channel;
4646
bool playing;
47+
uint16_t quiescent_value;
4748
} audioio_audioout_obj_t;
4849

4950
void audioout_reset(void);

ports/atmel-samd/supervisor/port.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,11 +249,11 @@ void reset_port(void) {
249249
}
250250

251251
#if defined(EXPRESS_BOARD) && !defined(__SAMR21G18A__)
252+
audio_dma_reset();
252253
audioout_reset();
253254
#if !defined(__SAMD51G19A__) && !defined(__SAMD51G18A__)
254255
i2sout_reset();
255256
#endif
256-
audio_dma_reset();
257257
//pdmin_reset();
258258
#endif
259259
#ifdef SAMD21

shared-bindings/audioio/AudioOut.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,15 @@
4343
//|
4444
//| AudioOut can be used to output an analog audio signal on a given pin.
4545
//|
46-
//| .. class:: AudioOut(left_channel, right_channel=None)
46+
//| .. class:: AudioOut(left_channel, *, right_channel=None, quiescent_value=0x8000)
4747
//|
4848
//| Create a AudioOut object associated with the given pin(s). This allows you to
4949
//| play audio signals out on the given pin(s).
5050
//|
5151
//| :param ~microcontroller.Pin left_channel: The pin to output the left channel to
5252
//| :param ~microcontroller.Pin right_channel: The pin to output the right channel to
53+
//| :param int quiescent_value: The output value when no signal is present. Samples should start
54+
//| and end with this value to prevent audible popping.
5355
//|
5456
//| Simple 8ksps 440 Hz sin wave::
5557
//|
@@ -95,10 +97,11 @@ STATIC mp_obj_t audioio_audioout_make_new(const mp_obj_type_t *type, size_t n_ar
9597
mp_arg_check_num(n_args, n_kw, 1, 2, true);
9698
mp_map_t kw_args;
9799
mp_map_init_fixed_table(&kw_args, n_kw, pos_args + n_args);
98-
enum { ARG_left_channel, ARG_right_channel };
100+
enum { ARG_left_channel, ARG_right_channel, ARG_quiescent_value };
99101
static const mp_arg_t allowed_args[] = {
100102
{ MP_QSTR_left_channel, MP_ARG_OBJ | MP_ARG_REQUIRED },
101103
{ MP_QSTR_right_channel, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = mp_const_none} },
104+
{ MP_QSTR_quiescent_value, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_int = 0x8000} },
102105
};
103106
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
104107
mp_arg_parse_all(n_args, pos_args, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
@@ -117,7 +120,7 @@ STATIC mp_obj_t audioio_audioout_make_new(const mp_obj_type_t *type, size_t n_ar
117120
// create AudioOut object from the given pin
118121
audioio_audioout_obj_t *self = m_new_obj(audioio_audioout_obj_t);
119122
self->base.type = &audioio_audioout_type;
120-
common_hal_audioio_audioout_construct(self, left_channel_pin, right_channel_pin);
123+
common_hal_audioio_audioout_construct(self, left_channel_pin, right_channel_pin, args[ARG_quiescent_value].u_int);
121124

122125
return MP_OBJ_FROM_PTR(self);
123126
}

shared-bindings/audioio/AudioOut.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ extern const mp_obj_type_t audioio_audioout_type;
3535

3636
// left_channel will always be non-NULL but right_channel may be for mono output.
3737
void common_hal_audioio_audioout_construct(audioio_audioout_obj_t* self,
38-
const mcu_pin_obj_t* left_channel, const mcu_pin_obj_t* right_channel);
38+
const mcu_pin_obj_t* left_channel, const mcu_pin_obj_t* right_channel, uint16_t default_value);
3939

4040
void common_hal_audioio_audioout_deinit(audioio_audioout_obj_t* self);
4141
bool common_hal_audioio_audioout_deinited(audioio_audioout_obj_t* self);

0 commit comments

Comments
 (0)