Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 1 addition & 12 deletions ports/atmel-samd/common-hal/analogio/AnalogOut.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,16 +138,5 @@ void common_hal_analogio_analogout_set_value(analogio_analogout_obj_t *self,
}

void analogout_reset(void) {
Comment thread
tannewt marked this conversation as resolved.
#if defined(SAMD21) && !defined(PIN_PA02)
return;
#endif
#ifdef SAMD21
while (DAC->STATUS.reg & DAC_STATUS_SYNCBUSY) {}
#endif
#ifdef SAMD51
while (DAC->SYNCBUSY.reg & DAC_SYNCBUSY_SWRST) {}
#endif
DAC->CTRLA.reg |= DAC_CTRLA_SWRST;

// TODO(tannewt): Turn off the DAC clocks to save power.
// AudioOut resets the DAC in case its been used for audio which requires special handling.
}
87 changes: 84 additions & 3 deletions ports/atmel-samd/common-hal/audioio/AudioOut.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "py/runtime.h"
#include "common-hal/audioio/AudioOut.h"
#include "shared-bindings/audioio/AudioOut.h"
#include "shared-bindings/microcontroller/__init__.h"
#include "shared-bindings/microcontroller/Pin.h"
#include "supervisor/shared/translate.h"

Expand All @@ -52,11 +53,73 @@
#include "samd/pins.h"
#include "samd/timers.h"

#ifdef SAMD21
static void ramp_value(uint16_t start, uint16_t end) {
start = DAC->DATA.reg;
int32_t diff = (int32_t) end - start;
int32_t step = 49;
int32_t steps = diff / step;
if (diff < 0) {
steps = -steps;
step = -step;
}
for (int32_t i = 0; i < steps; i++) {
uint32_t value = start + step * i;
DAC->DATA.reg = value;
DAC->DATABUF.reg = value;
common_hal_mcu_delay_us(50);
#ifdef MICROPY_VM_HOOK_LOOP
MICROPY_VM_HOOK_LOOP
#endif
}
}
#endif

#ifdef SAMD51
static void ramp_value(uint16_t start, uint16_t end) {
int32_t diff = (int32_t) end - start;
int32_t step = 49;
int32_t steps = diff / step;
if (diff < 0) {
steps = -steps;
step = -step;
}

for (int32_t i = 0; i < steps; i++) {
uint16_t value = start + step * i;
DAC->DATA[0].reg = value;
DAC->DATABUF[0].reg = value;
DAC->DATA[1].reg = value;
DAC->DATABUF[1].reg = value;

common_hal_mcu_delay_us(50);
#ifdef MICROPY_VM_HOOK_LOOP
MICROPY_VM_HOOK_LOOP
#endif
}
}
#endif

void audioout_reset(void) {
#if defined(SAMD21) && !defined(PIN_PA02)
return;
#endif
#ifdef SAMD21
while (DAC->STATUS.reg & DAC_STATUS_SYNCBUSY) {}
#endif
#ifdef SAMD51
while (DAC->SYNCBUSY.reg & DAC_SYNCBUSY_SWRST) {}
#endif
if (DAC->CTRLA.bit.ENABLE) {
ramp_value(0x8000, 0);
}
DAC->CTRLA.reg |= DAC_CTRLA_SWRST;

// TODO(tannewt): Turn off the DAC clocks to save power.
}

void common_hal_audioio_audioout_construct(audioio_audioout_obj_t* self,
const mcu_pin_obj_t* left_channel, const mcu_pin_obj_t* right_channel) {
const mcu_pin_obj_t* left_channel, const mcu_pin_obj_t* right_channel, uint16_t quiescent_value) {
#ifdef SAMD51
bool dac_clock_enabled = hri_mclk_get_APBDMASK_DAC_bit(MCLK);
#endif
Expand Down Expand Up @@ -94,12 +157,10 @@ void common_hal_audioio_audioout_construct(audioio_audioout_obj_t* self,
if (right_channel != NULL) {
claim_pin(right_channel);
self->right_channel = right_channel;
gpio_set_pin_function(self->right_channel->number, GPIO_PIN_FUNCTION_B);
audio_dma_init(&self->right_dma);
}
#endif
self->left_channel = left_channel;
gpio_set_pin_function(self->left_channel->number, GPIO_PIN_FUNCTION_B);
audio_dma_init(&self->left_dma);

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

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

bool channel0_enabled = true;
#ifdef SAMD51
Expand Down Expand Up @@ -159,6 +224,8 @@ void common_hal_audioio_audioout_construct(audioio_audioout_obj_t* self,
#endif
#ifdef SAMD51
while (DAC->SYNCBUSY.bit.ENABLE == 1) {}
while (channel0_enabled && DAC->STATUS.bit.READY0 == 0) {}
while (channel1_enabled && DAC->STATUS.bit.READY1 == 0) {}
#endif

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

#ifdef SAMD51
connect_event_user_to_channel(EVSYS_ID_USER_DAC_START_1, channel);
if (right_channel != NULL) {
gpio_set_pin_function(self->right_channel->number, GPIO_PIN_FUNCTION_B);
}
#define EVSYS_ID_USER_DAC_START EVSYS_ID_USER_DAC_START_0
#endif
connect_event_user_to_channel(EVSYS_ID_USER_DAC_START, channel);
gpio_set_pin_function(self->left_channel->number, GPIO_PIN_FUNCTION_B);
init_async_event_channel(channel, tc_gen_id);

self->tc_to_dac_event_channel = channel;

// Ramp the DAC up.
self->quiescent_value = quiescent_value;
ramp_value(0, quiescent_value);

// Leave the DMA setup to playback.
}

Expand All @@ -239,6 +314,9 @@ void common_hal_audioio_audioout_deinit(audioio_audioout_obj_t* self) {
return;
}

// Ramp the DAC down.
ramp_value(self->quiescent_value, 0);

DAC->CTRLA.bit.ENABLE = 0;
#ifdef SAMD21
while (DAC->STATUS.bit.SYNCBUSY == 1) {}
Expand Down Expand Up @@ -381,6 +459,9 @@ void common_hal_audioio_audioout_stop(audioio_audioout_obj_t* self) {
#ifdef SAMD51
audio_dma_stop(&self->right_dma);
#endif
// Ramp the DAC to default. The start is ignored when the current value can be readback.
// Otherwise, we just set it immediately.
ramp_value(self->quiescent_value, self->quiescent_value);
}

bool common_hal_audioio_audioout_get_playing(audioio_audioout_obj_t* self) {
Expand Down
1 change: 1 addition & 0 deletions ports/atmel-samd/common-hal/audioio/AudioOut.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ typedef struct {

uint8_t tc_to_dac_event_channel;
bool playing;
uint16_t quiescent_value;
} audioio_audioout_obj_t;

void audioout_reset(void);
Expand Down
2 changes: 1 addition & 1 deletion ports/atmel-samd/supervisor/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -249,11 +249,11 @@ void reset_port(void) {
}

#if defined(EXPRESS_BOARD) && !defined(__SAMR21G18A__)
audio_dma_reset();
audioout_reset();
#if !defined(__SAMD51G19A__) && !defined(__SAMD51G18A__)
i2sout_reset();
#endif
audio_dma_reset();
//pdmin_reset();
#endif
#ifdef SAMD21
Expand Down
9 changes: 6 additions & 3 deletions shared-bindings/audioio/AudioOut.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,15 @@
//|
//| AudioOut can be used to output an analog audio signal on a given pin.
//|
//| .. class:: AudioOut(left_channel, right_channel=None)
//| .. class:: AudioOut(left_channel, *, right_channel=None, quiescent_value=0x8000)
//|
//| Create a AudioOut object associated with the given pin(s). This allows you to
//| play audio signals out on the given pin(s).
//|
//| :param ~microcontroller.Pin left_channel: The pin to output the left channel to
//| :param ~microcontroller.Pin right_channel: The pin to output the right channel to
//| :param int quiescent_value: The output value when no signal is present. Samples should start
//| and end with this value to prevent audible popping.
//|
//| Simple 8ksps 440 Hz sin wave::
//|
Expand Down Expand Up @@ -95,10 +97,11 @@ STATIC mp_obj_t audioio_audioout_make_new(const mp_obj_type_t *type, size_t n_ar
mp_arg_check_num(n_args, n_kw, 1, 2, true);
mp_map_t kw_args;
mp_map_init_fixed_table(&kw_args, n_kw, pos_args + n_args);
enum { ARG_left_channel, ARG_right_channel };
enum { ARG_left_channel, ARG_right_channel, ARG_quiescent_value };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_left_channel, MP_ARG_OBJ | MP_ARG_REQUIRED },
{ MP_QSTR_right_channel, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = mp_const_none} },
{ MP_QSTR_quiescent_value, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_int = 0x8000} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
Expand All @@ -117,7 +120,7 @@ STATIC mp_obj_t audioio_audioout_make_new(const mp_obj_type_t *type, size_t n_ar
// create AudioOut object from the given pin
audioio_audioout_obj_t *self = m_new_obj(audioio_audioout_obj_t);
self->base.type = &audioio_audioout_type;
common_hal_audioio_audioout_construct(self, left_channel_pin, right_channel_pin);
common_hal_audioio_audioout_construct(self, left_channel_pin, right_channel_pin, args[ARG_quiescent_value].u_int);

return MP_OBJ_FROM_PTR(self);
}
Expand Down
2 changes: 1 addition & 1 deletion shared-bindings/audioio/AudioOut.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ extern const mp_obj_type_t audioio_audioout_type;

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

void common_hal_audioio_audioout_deinit(audioio_audioout_obj_t* self);
bool common_hal_audioio_audioout_deinited(audioio_audioout_obj_t* self);
Expand Down