Merge pull request #1280 from tannewt/precondition_dac
Ramp values to and from a default value while active.
This commit is contained in:
commit
298756eef8
@ -138,16 +138,5 @@ void common_hal_analogio_analogout_set_value(analogio_analogout_obj_t *self,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void analogout_reset(void) {
|
void analogout_reset(void) {
|
||||||
#if defined(SAMD21) && !defined(PIN_PA02)
|
// AudioOut resets the DAC in case its been used for audio which requires special handling.
|
||||||
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.
|
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include "py/runtime.h"
|
#include "py/runtime.h"
|
||||||
#include "common-hal/audioio/AudioOut.h"
|
#include "common-hal/audioio/AudioOut.h"
|
||||||
#include "shared-bindings/audioio/AudioOut.h"
|
#include "shared-bindings/audioio/AudioOut.h"
|
||||||
|
#include "shared-bindings/microcontroller/__init__.h"
|
||||||
#include "shared-bindings/microcontroller/Pin.h"
|
#include "shared-bindings/microcontroller/Pin.h"
|
||||||
#include "supervisor/shared/translate.h"
|
#include "supervisor/shared/translate.h"
|
||||||
|
|
||||||
@ -52,11 +53,73 @@
|
|||||||
#include "samd/pins.h"
|
#include "samd/pins.h"
|
||||||
#include "samd/timers.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) {
|
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,
|
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
|
#ifdef SAMD51
|
||||||
bool dac_clock_enabled = hri_mclk_get_APBDMASK_DAC_bit(MCLK);
|
bool dac_clock_enabled = hri_mclk_get_APBDMASK_DAC_bit(MCLK);
|
||||||
#endif
|
#endif
|
||||||
@ -94,12 +157,10 @@ void common_hal_audioio_audioout_construct(audioio_audioout_obj_t* self,
|
|||||||
if (right_channel != NULL) {
|
if (right_channel != NULL) {
|
||||||
claim_pin(right_channel);
|
claim_pin(right_channel);
|
||||||
self->right_channel = 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);
|
audio_dma_init(&self->right_dma);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
self->left_channel = left_channel;
|
self->left_channel = left_channel;
|
||||||
gpio_set_pin_function(self->left_channel->number, GPIO_PIN_FUNCTION_B);
|
|
||||||
audio_dma_init(&self->left_dma);
|
audio_dma_init(&self->left_dma);
|
||||||
|
|
||||||
#ifdef SAMD51
|
#ifdef SAMD51
|
||||||
@ -118,6 +179,10 @@ void common_hal_audioio_audioout_construct(audioio_audioout_obj_t* self,
|
|||||||
|
|
||||||
DAC->CTRLA.bit.SWRST = 1;
|
DAC->CTRLA.bit.SWRST = 1;
|
||||||
while (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;
|
bool channel0_enabled = true;
|
||||||
#ifdef SAMD51
|
#ifdef SAMD51
|
||||||
@ -159,6 +224,8 @@ void common_hal_audioio_audioout_construct(audioio_audioout_obj_t* self,
|
|||||||
#endif
|
#endif
|
||||||
#ifdef SAMD51
|
#ifdef SAMD51
|
||||||
while (DAC->SYNCBUSY.bit.ENABLE == 1) {}
|
while (DAC->SYNCBUSY.bit.ENABLE == 1) {}
|
||||||
|
while (channel0_enabled && DAC->STATUS.bit.READY0 == 0) {}
|
||||||
|
while (channel1_enabled && DAC->STATUS.bit.READY1 == 0) {}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Use a timer to coordinate when DAC conversions occur.
|
// Use a timer to coordinate when DAC conversions occur.
|
||||||
@ -220,13 +287,21 @@ void common_hal_audioio_audioout_construct(audioio_audioout_obj_t* self,
|
|||||||
|
|
||||||
#ifdef SAMD51
|
#ifdef SAMD51
|
||||||
connect_event_user_to_channel(EVSYS_ID_USER_DAC_START_1, channel);
|
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
|
#define EVSYS_ID_USER_DAC_START EVSYS_ID_USER_DAC_START_0
|
||||||
#endif
|
#endif
|
||||||
connect_event_user_to_channel(EVSYS_ID_USER_DAC_START, channel);
|
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);
|
init_async_event_channel(channel, tc_gen_id);
|
||||||
|
|
||||||
self->tc_to_dac_event_channel = channel;
|
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.
|
// Leave the DMA setup to playback.
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,6 +314,9 @@ void common_hal_audioio_audioout_deinit(audioio_audioout_obj_t* self) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ramp the DAC down.
|
||||||
|
ramp_value(self->quiescent_value, 0);
|
||||||
|
|
||||||
DAC->CTRLA.bit.ENABLE = 0;
|
DAC->CTRLA.bit.ENABLE = 0;
|
||||||
#ifdef SAMD21
|
#ifdef SAMD21
|
||||||
while (DAC->STATUS.bit.SYNCBUSY == 1) {}
|
while (DAC->STATUS.bit.SYNCBUSY == 1) {}
|
||||||
@ -381,6 +459,9 @@ void common_hal_audioio_audioout_stop(audioio_audioout_obj_t* self) {
|
|||||||
#ifdef SAMD51
|
#ifdef SAMD51
|
||||||
audio_dma_stop(&self->right_dma);
|
audio_dma_stop(&self->right_dma);
|
||||||
#endif
|
#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) {
|
bool common_hal_audioio_audioout_get_playing(audioio_audioout_obj_t* self) {
|
||||||
|
@ -44,6 +44,7 @@ typedef struct {
|
|||||||
|
|
||||||
uint8_t tc_to_dac_event_channel;
|
uint8_t tc_to_dac_event_channel;
|
||||||
bool playing;
|
bool playing;
|
||||||
|
uint16_t quiescent_value;
|
||||||
} audioio_audioout_obj_t;
|
} audioio_audioout_obj_t;
|
||||||
|
|
||||||
void audioout_reset(void);
|
void audioout_reset(void);
|
||||||
|
@ -249,11 +249,11 @@ void reset_port(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(EXPRESS_BOARD) && !defined(__SAMR21G18A__)
|
#if defined(EXPRESS_BOARD) && !defined(__SAMR21G18A__)
|
||||||
|
audio_dma_reset();
|
||||||
audioout_reset();
|
audioout_reset();
|
||||||
#if !defined(__SAMD51G19A__) && !defined(__SAMD51G18A__)
|
#if !defined(__SAMD51G19A__) && !defined(__SAMD51G18A__)
|
||||||
i2sout_reset();
|
i2sout_reset();
|
||||||
#endif
|
#endif
|
||||||
audio_dma_reset();
|
|
||||||
//pdmin_reset();
|
//pdmin_reset();
|
||||||
#endif
|
#endif
|
||||||
#ifdef SAMD21
|
#ifdef SAMD21
|
||||||
|
@ -43,13 +43,15 @@
|
|||||||
//|
|
//|
|
||||||
//| AudioOut can be used to output an analog audio signal on a given pin.
|
//| 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
|
//| Create a AudioOut object associated with the given pin(s). This allows you to
|
||||||
//| play audio signals out on the given pin(s).
|
//| 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 left_channel: The pin to output the left channel to
|
||||||
//| :param ~microcontroller.Pin right_channel: The pin to output the right 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::
|
//| Simple 8ksps 440 Hz sin wave::
|
||||||
//|
|
//|
|
||||||
@ -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_arg_check_num(n_args, n_kw, 1, 2, true);
|
||||||
mp_map_t kw_args;
|
mp_map_t kw_args;
|
||||||
mp_map_init_fixed_table(&kw_args, n_kw, pos_args + n_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[] = {
|
static const mp_arg_t allowed_args[] = {
|
||||||
{ MP_QSTR_left_channel, MP_ARG_OBJ | MP_ARG_REQUIRED },
|
{ 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_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_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);
|
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
|
|||||||
// create AudioOut object from the given pin
|
// create AudioOut object from the given pin
|
||||||
audioio_audioout_obj_t *self = m_new_obj(audioio_audioout_obj_t);
|
audioio_audioout_obj_t *self = m_new_obj(audioio_audioout_obj_t);
|
||||||
self->base.type = &audioio_audioout_type;
|
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);
|
return MP_OBJ_FROM_PTR(self);
|
||||||
}
|
}
|
||||||
|
@ -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.
|
// 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,
|
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);
|
void common_hal_audioio_audioout_deinit(audioio_audioout_obj_t* self);
|
||||||
bool common_hal_audioio_audioout_deinited(audioio_audioout_obj_t* self);
|
bool common_hal_audioio_audioout_deinited(audioio_audioout_obj_t* self);
|
||||||
|
Loading…
Reference in New Issue
Block a user