atmel-samd: Move heap objects into MICROPY_PORT_ROOT_POINTERS so they don't get garbage collected while we are using them.

This commit is contained in:
Scott Shawcroft 2017-05-04 11:57:51 -07:00
parent 6512ccf32e
commit 5ad426124b
4 changed files with 89 additions and 89 deletions

View File

@ -92,7 +92,7 @@ STATIC const mp_map_elem_t board_global_dict_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_ACCELEROMETER_SDA), (mp_obj_t)&pin_PA00 },
{ MP_OBJ_NEW_QSTR(MP_QSTR_ACCELEROMETER_SCL), (mp_obj_t)&pin_PA01 },
{ MP_OBJ_NEW_QSTR(MP_QSTR_SPEAKER_SHUTDOWN), (mp_obj_t)&pin_PA30 },
{ MP_OBJ_NEW_QSTR(MP_QSTR_SPEAKER_ENABLE), (mp_obj_t)&pin_PA30 },
{ MP_OBJ_NEW_QSTR(MP_QSTR_SCK), (mp_obj_t)&pin_PA21 },
{ MP_OBJ_NEW_QSTR(MP_QSTR_MOSI), (mp_obj_t)&pin_PA20 },

View File

@ -49,11 +49,9 @@ extern uint8_t timer_refcount[TC_INST_NUM + TCC_INST_NUM];
extern const uint16_t prescaler[8];
// This timer is shared amongst all AudioOut objects under the assumption that
// the code is single threaded.
static struct tc_module* tc_instance;
static struct dac_module* dac_instance;
static struct events_resource* tc_event;
static struct events_resource* dac_event;
// the code is single threaded. The audioout_tc_instance, audioout_dac_instance,
// audioout_tc_event, and audioout_dac_event pointers live in
// MICROPY_PORT_ROOT_POINTERS so they don't get garbage collected.
// The AudioOut object is being currently played. Only it can pause the timer
// and change its frequency.
@ -62,25 +60,22 @@ static audioio_audioout_obj_t* active_audioout;
static uint8_t refcount = 0;
void audioout_reset(void) {
// Only reset DMA. PWMOut will reset the timer.
// Only reset DMA. PWMOut will reset the timer. Other code will reset the DAC.
refcount = 0;
tc_instance = NULL;
if (dac_instance != NULL) {
dac_reset(dac_instance);
}
dac_instance = NULL;
MP_STATE_VM(audioout_tc_instance) = NULL;
MP_STATE_VM(audioout_dac_instance) = NULL;
if (tc_event != NULL) {
events_detach_user(tc_event, EVSYS_ID_USER_DAC_START);
events_release(tc_event);
if (MP_STATE_VM(audioout_tc_event) != NULL) {
events_detach_user(MP_STATE_VM(audioout_tc_event), EVSYS_ID_USER_DAC_START);
events_release(MP_STATE_VM(audioout_tc_event));
}
tc_event = NULL;
MP_STATE_VM(audioout_tc_event) = NULL;
if (dac_event != NULL) {
events_detach_user(dac_event, EVSYS_ID_USER_DMAC_CH_0);
events_release(dac_event);
if (MP_STATE_VM(audioout_dac_event) != NULL) {
events_detach_user(MP_STATE_VM(audioout_dac_event), EVSYS_ID_USER_DMAC_CH_0);
events_release(MP_STATE_VM(audioout_dac_event));
}
dac_event = NULL;
MP_STATE_VM(audioout_dac_event) = NULL;
dma_abort_job(&audio_dma);
}
@ -90,8 +85,8 @@ static void shared_construct(audioio_audioout_obj_t* self, const mcu_pin_obj_t*
// Configure the DAC to output on input event and to output an empty event
// that triggers the DMA to load the next sample.
dac_instance = gc_alloc(sizeof(struct dac_module), false);
if (dac_instance == NULL) {
MP_STATE_VM(audioout_dac_instance) = gc_alloc(sizeof(struct dac_module), false);
if (MP_STATE_VM(audioout_dac_instance) == NULL) {
mp_raise_msg(&mp_type_MemoryError, "");
}
struct dac_config config_dac;
@ -99,7 +94,7 @@ static void shared_construct(audioio_audioout_obj_t* self, const mcu_pin_obj_t*
config_dac.left_adjust = true;
config_dac.reference = DAC_REFERENCE_AVCC;
config_dac.clock_source = GCLK_GENERATOR_0;
enum status_code status = dac_init(dac_instance, DAC, &config_dac);
enum status_code status = dac_init(MP_STATE_VM(audioout_dac_instance), DAC, &config_dac);
if (status != STATUS_OK) {
common_hal_audioio_audioout_deinit(self);
mp_raise_OSError(MP_EIO);
@ -108,14 +103,12 @@ static void shared_construct(audioio_audioout_obj_t* self, const mcu_pin_obj_t*
struct dac_chan_config channel_config;
dac_chan_get_config_defaults(&channel_config);
dac_chan_set_config(dac_instance, DAC_CHANNEL_0, &channel_config);
dac_chan_enable(dac_instance, DAC_CHANNEL_0);
dac_chan_set_config(MP_STATE_VM(audioout_dac_instance), DAC_CHANNEL_0, &channel_config);
dac_chan_enable(MP_STATE_VM(audioout_dac_instance), DAC_CHANNEL_0);
struct dac_events events_dac = { .generate_event_on_buffer_empty = true,
.on_event_start_conversion = true };
dac_enable_events(dac_instance, &events_dac);
dac_enable(dac_instance);
dac_enable_events(MP_STATE_VM(audioout_dac_instance), &events_dac);
// Figure out which timer we are using.
Tc *t = NULL;
@ -131,8 +124,8 @@ static void shared_construct(audioio_audioout_obj_t* self, const mcu_pin_obj_t*
mp_raise_RuntimeError("All timers in use");
return;
}
tc_instance = gc_alloc(sizeof(struct tc_module), false);
if (tc_instance == NULL) {
MP_STATE_VM(audioout_tc_instance) = gc_alloc(sizeof(struct tc_module), false);
if (MP_STATE_VM(audioout_tc_instance) == NULL) {
common_hal_audioio_audioout_deinit(self);
mp_raise_msg(&mp_type_MemoryError, "");
}
@ -140,28 +133,28 @@ static void shared_construct(audioio_audioout_obj_t* self, const mcu_pin_obj_t*
// Don't bother setting the period. We set it before you playback anything.
struct tc_config config_tc;
tc_get_config_defaults(&config_tc);
config_tc.counter_size = TC_COUNTER_SIZE_16BIT;
config_tc.clock_prescaler = TC_CLOCK_PRESCALER_DIV1;
config_tc.wave_generation = TC_WAVE_GENERATION_MATCH_FREQ;
if (tc_init(tc_instance, t, &config_tc) != STATUS_OK) {
if (tc_init(MP_STATE_VM(audioout_tc_instance), t, &config_tc) != STATUS_OK) {
common_hal_audioio_audioout_deinit(self);
mp_printf(&mp_plat_print, "tc \n");
mp_raise_OSError(MP_EIO);
return;
};
struct tc_events events_tc;
events_tc.generate_event_on_overflow = true;
tc_enable_events(tc_instance, &events_tc);
events_tc.on_event_perform_action = false;
events_tc.event_action = TC_EVENT_ACTION_OFF;
tc_enable_events(MP_STATE_VM(audioout_tc_instance), &events_tc);
tc_enable(tc_instance);
tc_stop_counter(tc_instance);
tc_enable(MP_STATE_VM(audioout_tc_instance));
tc_stop_counter(MP_STATE_VM(audioout_tc_instance));
// Connect the timer overflow event, which happens at the target frequency,
// to the DAC conversion trigger.
tc_event = gc_alloc(sizeof(struct events_resource), false);
if (tc_event == NULL) {
MP_STATE_VM(audioout_tc_event) = gc_alloc(sizeof(struct events_resource), false);
if (MP_STATE_VM(audioout_tc_event) == NULL) {
common_hal_audioio_audioout_deinit(self);
mp_raise_msg(&mp_type_MemoryError, "");
}
@ -185,24 +178,24 @@ static void shared_construct(audioio_audioout_obj_t* self, const mcu_pin_obj_t*
config.generator = generator;
config.path = EVENTS_PATH_ASYNCHRONOUS;
if (events_allocate(tc_event, &config) != STATUS_OK ||
events_attach_user(tc_event, EVSYS_ID_USER_DAC_START) != STATUS_OK) {
if (events_allocate(MP_STATE_VM(audioout_tc_event), &config) != STATUS_OK ||
events_attach_user(MP_STATE_VM(audioout_tc_event), EVSYS_ID_USER_DAC_START) != STATUS_OK) {
common_hal_audioio_audioout_deinit(self);
mp_raise_OSError(MP_EIO);
return;
}
// Connect the DAC to DMA
dac_event = gc_alloc(sizeof(struct events_resource), false);
if (dac_event == NULL) {
MP_STATE_VM(audioout_dac_event) = gc_alloc(sizeof(struct events_resource), false);
if (MP_STATE_VM(audioout_dac_event) == NULL) {
common_hal_audioio_audioout_deinit(self);
mp_raise_msg(&mp_type_MemoryError, "");
}
events_get_config_defaults(&config);
config.generator = EVSYS_ID_GEN_DAC_EMPTY;
config.path = EVENTS_PATH_ASYNCHRONOUS;
if (events_allocate(dac_event, &config) != STATUS_OK ||
events_attach_user(dac_event, EVSYS_ID_USER_DMAC_CH_0) != STATUS_OK) {
if (events_allocate(MP_STATE_VM(audioout_dac_event), &config) != STATUS_OK ||
events_attach_user(MP_STATE_VM(audioout_dac_event), EVSYS_ID_USER_DMAC_CH_0) != STATUS_OK) {
common_hal_audioio_audioout_deinit(self);
mp_raise_OSError(MP_EIO);
return;
@ -239,26 +232,26 @@ void common_hal_audioio_audioout_construct_from_file(audioio_audioout_obj_t* sel
void common_hal_audioio_audioout_deinit(audioio_audioout_obj_t* self) {
refcount--;
if (refcount == 0) {
if (tc_instance != NULL) {
tc_reset(tc_instance);
gc_free(tc_instance);
tc_instance = NULL;
if (MP_STATE_VM(audioout_tc_instance) != NULL) {
tc_reset(MP_STATE_VM(audioout_tc_instance));
gc_free(MP_STATE_VM(audioout_tc_instance));
MP_STATE_VM(audioout_tc_instance) = NULL;
}
if (dac_instance != NULL) {
dac_reset(dac_instance);
gc_free(dac_instance);
dac_instance = NULL;
if (MP_STATE_VM(audioout_dac_instance) != NULL) {
dac_reset(MP_STATE_VM(audioout_dac_instance));
gc_free(MP_STATE_VM(audioout_dac_instance));
MP_STATE_VM(audioout_dac_instance) = NULL;
}
if (tc_event != NULL) {
events_detach_user(tc_event, EVSYS_ID_USER_DAC_START);
events_release(tc_event);
gc_free(tc_event);
tc_event = NULL;
if (MP_STATE_VM(audioout_tc_event) != NULL) {
events_detach_user(MP_STATE_VM(audioout_tc_event), EVSYS_ID_USER_DAC_START);
events_release(MP_STATE_VM(audioout_tc_event));
gc_free(MP_STATE_VM(audioout_tc_event));
MP_STATE_VM(audioout_tc_event) = NULL;
}
if (dac_event != NULL) {
events_release(dac_event);
gc_free(dac_event);
dac_event = NULL;
if (MP_STATE_VM(audioout_dac_event) != NULL) {
events_release(MP_STATE_VM(audioout_dac_event));
gc_free(MP_STATE_VM(audioout_dac_event));
MP_STATE_VM(audioout_dac_event) = NULL;
}
reset_pin(self->pin->pin);
}
@ -274,17 +267,17 @@ static void set_timer_frequency(uint32_t frequency) {
break;
}
}
uint8_t old_divisor = tc_instance->hw->COUNT16.CTRLA.bit.PRESCALER;
uint8_t old_divisor = MP_STATE_VM(audioout_tc_instance)->hw->COUNT16.CTRLA.bit.PRESCALER;
if (new_divisor != old_divisor) {
tc_disable(tc_instance);
tc_instance->hw->COUNT16.CTRLA.bit.PRESCALER = new_divisor;
tc_enable(tc_instance);
tc_disable(MP_STATE_VM(audioout_tc_instance));
MP_STATE_VM(audioout_tc_instance)->hw->COUNT16.CTRLA.bit.PRESCALER = new_divisor;
tc_enable(MP_STATE_VM(audioout_tc_instance));
}
while (tc_is_syncing(tc_instance)) {
while (tc_is_syncing(MP_STATE_VM(audioout_tc_instance))) {
/* Wait for sync */
}
tc_instance->hw->COUNT16.CC[0].reg = new_top;
while (tc_is_syncing(tc_instance)) {
MP_STATE_VM(audioout_tc_instance)->hw->COUNT16.CC[0].reg = new_top;
while (tc_is_syncing(MP_STATE_VM(audioout_tc_instance))) {
/* Wait for sync */
}
}
@ -293,8 +286,10 @@ void common_hal_audioio_audioout_play(audioio_audioout_obj_t* self, bool loop) {
common_hal_audioio_audioout_get_playing(self);
// Shut down any active playback.
if (active_audioout != NULL) {
tc_stop_counter(tc_instance);
tc_stop_counter(MP_STATE_VM(audioout_tc_instance));
dma_abort_job(&audio_dma);
} else {
dac_enable(MP_STATE_VM(audioout_dac_instance));
}
struct dma_descriptor_config descriptor_config;
dma_descriptor_get_config_defaults(&descriptor_config);
@ -314,15 +309,15 @@ void common_hal_audioio_audioout_play(audioio_audioout_obj_t* self, bool loop) {
dma_start_transfer_job(&audio_dma);
set_timer_frequency(self->frequency);
tc_start_counter(tc_instance);
tc_start_counter(MP_STATE_VM(audioout_tc_instance));
}
void common_hal_audioio_audioout_stop(audioio_audioout_obj_t* self) {
if (common_hal_audioio_audioout_get_playing(self)) {
tc_stop_counter(tc_instance);
tc_stop_counter(MP_STATE_VM(audioout_tc_instance));
dma_abort_job(&audio_dma);
active_audioout = NULL;
// TODO(tannewt): Disable the DAC to save power.
dac_disable(MP_STATE_VM(audioout_dac_instance));
}
}

View File

@ -39,8 +39,8 @@
#undef ENABLE
// This timer is shared amongst all PulseOut objects under the assumption that
// the code is single threaded.
static struct tc_module* tc_instance;
// the code is single threaded. Its stored in MICROPY_PORT_ROOT_POINTERS so it
// doesn't get garbage collected.
static uint8_t refcount = 0;
static __IO PORT_PINCFG_Type *active_pincfg = NULL;
@ -69,7 +69,7 @@ void pulse_finish(struct tc_module *const module) {
return;
}
current_compare = (current_compare + pulse_buffer[pulse_index] * 3 / 4) & 0xffff;
tc_set_compare_value(tc_instance, TC_COMPARE_CAPTURE_CHANNEL_0, current_compare);
tc_set_compare_value(MP_STATE_VM(pulseout_tc_instance), TC_COMPARE_CAPTURE_CHANNEL_0, current_compare);
if (pulse_index % 2 == 0) {
turn_on(active_pincfg);
}
@ -77,7 +77,7 @@ void pulse_finish(struct tc_module *const module) {
void pulseout_reset() {
refcount = 0;
tc_instance = 0;
MP_STATE_VM(pulseout_tc_instance) = NULL;
active_pincfg = NULL;
}
@ -96,7 +96,7 @@ void common_hal_pulseio_pulseout_construct(pulseio_pulseout_obj_t* self,
if (t == NULL) {
mp_raise_RuntimeError("All timers in use");
}
tc_instance = gc_alloc(sizeof(struct tc_module), false);
MP_STATE_VM(pulseout_tc_instance) = gc_alloc(sizeof(struct tc_module), false);
if (t == NULL) {
mp_raise_msg(&mp_type_MemoryError, "");
}
@ -108,10 +108,10 @@ void common_hal_pulseio_pulseout_construct(pulseio_pulseout_obj_t* self,
config_tc.clock_prescaler = TC_CTRLA_PRESCALER_DIV64;
config_tc.wave_generation = TC_WAVE_GENERATION_NORMAL_FREQ;
tc_init(tc_instance, t, &config_tc);
tc_register_callback(tc_instance, pulse_finish, TC_CALLBACK_CC_CHANNEL0);
tc_enable(tc_instance);
tc_stop_counter(tc_instance);
tc_init(MP_STATE_VM(pulseout_tc_instance), t, &config_tc);
tc_register_callback(MP_STATE_VM(pulseout_tc_instance), pulse_finish, TC_CALLBACK_CC_CHANNEL0);
tc_enable(MP_STATE_VM(pulseout_tc_instance));
tc_stop_counter(MP_STATE_VM(pulseout_tc_instance));
}
refcount++;
@ -136,9 +136,9 @@ void common_hal_pulseio_pulseout_deinit(pulseio_pulseout_obj_t* self) {
refcount--;
if (refcount == 0) {
tc_reset(tc_instance);
gc_free(tc_instance);
tc_instance = NULL;
tc_reset(MP_STATE_VM(pulseout_tc_instance));
gc_free(MP_STATE_VM(pulseout_tc_instance));
MP_STATE_VM(pulseout_tc_instance) = NULL;
}
}
@ -152,11 +152,11 @@ void common_hal_pulseio_pulseout_send(pulseio_pulseout_obj_t* self, uint16_t* pu
pulse_length = length;
current_compare = pulses[0] * 3 / 4;
tc_set_compare_value(tc_instance, TC_COMPARE_CAPTURE_CHANNEL_0, current_compare);
tc_set_compare_value(MP_STATE_VM(pulseout_tc_instance), TC_COMPARE_CAPTURE_CHANNEL_0, current_compare);
tc_enable_callback(tc_instance, TC_CALLBACK_CC_CHANNEL0);
tc_enable_callback(MP_STATE_VM(pulseout_tc_instance), TC_CALLBACK_CC_CHANNEL0);
turn_on(active_pincfg);
tc_start_counter(tc_instance);
tc_start_counter(MP_STATE_VM(pulseout_tc_instance));
while(pulse_index < length) {
// Do other things while we wait. The interrupts will handle sending the
@ -166,7 +166,7 @@ void common_hal_pulseio_pulseout_send(pulseio_pulseout_obj_t* self, uint16_t* pu
#endif
}
tc_stop_counter(tc_instance);
tc_disable_callback(tc_instance, TC_CALLBACK_CC_CHANNEL0);
tc_stop_counter(MP_STATE_VM(pulseout_tc_instance));
tc_disable_callback(MP_STATE_VM(pulseout_tc_instance), TC_CALLBACK_CC_CHANNEL0);
active_pincfg = NULL;
}

View File

@ -192,6 +192,11 @@ extern const struct _mp_obj_module_t usb_hid_module;
#define MICROPY_PORT_ROOT_POINTERS \
const char *readline_hist[8]; \
vstr_t *repl_line; \
struct tc_module* audioout_tc_instance; \
struct dac_module* audioout_dac_instance; \
struct events_resource* audioout_tc_event; \
struct events_resource* audioout_dac_event; \
struct tc_module* pulseout_tc_instance; \
FLASH_ROOT_POINTERS \
bool udi_msc_process_trans(void);