Skip to content

fix SAMD21 PDMIn DMA event use #5484

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 20, 2021
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
2 changes: 1 addition & 1 deletion ports/atmel-samd/audio_dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ STATIC void dma_callback_fun(void *arg) {
}
}

void audio_evsys_handler(void) {
void audio_dma_evsys_handler(void) {
for (uint8_t i = 0; i < AUDIO_DMA_CHANNEL_COUNT; i++) {
audio_dma_t *dma = audio_dma_state[i];
if (dma == NULL) {
Expand Down
2 changes: 1 addition & 1 deletion ports/atmel-samd/audio_dma.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,6 @@ void audio_dma_background(void);

uint8_t find_sync_event_channel_raise(void);

void audio_evsys_handler(void);
void audio_dma_evsys_handler(void);

#endif // MICROPY_INCLUDED_ATMEL_SAMD_AUDIO_DMA_H
43 changes: 24 additions & 19 deletions ports/atmel-samd/common-hal/audiobusio/PDMIn.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "common-hal/audiobusio/PDMIn.h"
#include "shared-bindings/analogio/AnalogOut.h"
#include "shared-bindings/audiobusio/PDMIn.h"
#include "shared-bindings/microcontroller/__init__.h"
#include "shared-bindings/microcontroller/Pin.h"
#include "supervisor/shared/translate.h"

Expand Down Expand Up @@ -64,7 +65,20 @@
#define SERCTRL(name) I2S_RXCTRL_ ## name
#endif

// Set by interrupt handler when DMA block has finished transferring.
static bool pdmin_dma_block_done;
// Event channel used to trigger interrupt. Set to invalid value EVSYS_SYNCH_NUM when not in use.
static uint8_t pdmin_event_channel;

void pdmin_evsys_handler(void) {
if (pdmin_event_channel < EVSYS_SYNCH_NUM && event_interrupt_active(pdmin_event_channel)) {
pdmin_dma_block_done = true;
}
}

void pdmin_reset(void) {
pdmin_event_channel = EVSYS_SYNCH_NUM;

while (I2S->SYNCBUSY.reg & I2S_SYNCBUSY_ENABLE) {}
I2S->INTENCLR.reg = I2S_INTENCLR_MASK;
I2S->INTFLAG.reg = I2S_INTFLAG_MASK;
Expand Down Expand Up @@ -368,7 +382,8 @@ static uint16_t filter_sample(uint32_t pdm_samples[4]) {
uint32_t common_hal_audiobusio_pdmin_record_to_buffer(audiobusio_pdmin_obj_t* self,
uint16_t* output_buffer, uint32_t output_buffer_length) {
uint8_t dma_channel = dma_allocate_channel();
uint8_t event_channel = find_sync_event_channel_raise();
pdmin_event_channel = find_sync_event_channel_raise();
pdmin_dma_block_done = false;

// We allocate two buffers on the stack to use for double buffering.
const uint8_t samples_per_buffer = SAMPLES_PER_BUFFER;
Expand All @@ -391,7 +406,7 @@ uint32_t common_hal_audiobusio_pdmin_record_to_buffer(audiobusio_pdmin_obj_t* se
#endif

dma_configure(dma_channel, trigger_source, true);
init_event_channel_interrupt(event_channel, CORE_GCLK, EVSYS_ID_GEN_DMAC_CH_0 + dma_channel);
init_event_channel_interrupt(pdmin_event_channel, CORE_GCLK, EVSYS_ID_GEN_DMAC_CH_0 + dma_channel);
// Turn on serializer now to get it in sync with DMA.
i2s_set_serializer_enable(self->serializer, true);
audio_dma_enable_channel(dma_channel);
Expand All @@ -402,23 +417,12 @@ uint32_t common_hal_audiobusio_pdmin_record_to_buffer(audiobusio_pdmin_obj_t* se

uint32_t remaining_samples_needed = output_buffer_length;
while (values_output < output_buffer_length) {
if (event_interrupt_overflow(event_channel)) {
// Looks like we aren't keeping up. We shouldn't skip a buffer so stop early.
break;
}
// Wait for the next buffer to fill
uint32_t wait_counts = 0;
#ifdef SAMD21
#define MAX_WAIT_COUNTS 1000
#endif
#ifdef SAM_D5X_E5X
#define MAX_WAIT_COUNTS 6000
#endif
// If wait_counts exceeds the max count, buffer has probably stopped filling;
// DMA may have missed an I2S trigger event.
while (!event_interrupt_active(event_channel) && ++wait_counts < MAX_WAIT_COUNTS) {
while (!pdmin_dma_block_done) {
RUN_BACKGROUND_TASKS;
}
common_hal_mcu_disable_interrupts();
pdmin_dma_block_done = false;
common_hal_mcu_enable_interrupts();

// The mic is running all the time, so we don't need to wait the usual 10msec or 100msec
// for it to start up.
Expand All @@ -430,6 +434,7 @@ uint32_t common_hal_audiobusio_pdmin_record_to_buffer(audiobusio_pdmin_obj_t* se
buffer = second_buffer;
descriptor = &second_descriptor;
}

// Decimate and filter the buffer that was just filled.
uint32_t samples_gathered = descriptor->BTCNT.reg / words_per_sample;
// Don't run off the end of output buffer. Process only as many as needed.
Expand Down Expand Up @@ -472,7 +477,8 @@ uint32_t common_hal_audiobusio_pdmin_record_to_buffer(audiobusio_pdmin_obj_t* se
}
}

disable_event_channel(event_channel);
disable_event_channel(pdmin_event_channel);
pdmin_event_channel = EVSYS_SYNCH_NUM; // Invalid event_channel.
dma_free_channel(dma_channel);
// Turn off serializer, but leave clock on, to avoid mic startup delay.
i2s_set_serializer_enable(self->serializer, false);
Expand All @@ -481,5 +487,4 @@ uint32_t common_hal_audiobusio_pdmin_record_to_buffer(audiobusio_pdmin_obj_t* se
}

void common_hal_audiobusio_pdmin_record_to_file(audiobusio_pdmin_obj_t* self, uint8_t* buffer, uint32_t length) {

}
2 changes: 2 additions & 0 deletions ports/atmel-samd/common-hal/audiobusio/PDMIn.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ typedef struct {

void pdmin_reset(void);

void pdmin_evsys_handler(void);

void pdmin_background(void);

#endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_AUDIOBUSIO_AUDIOOUT_H
2 changes: 1 addition & 1 deletion ports/atmel-samd/common-hal/busio/I2C.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
#include "shared-bindings/microcontroller/__init__.h"
#include "supervisor/shared/translate.h"

#include "common-hal/busio/SPI.h" // for never_reset_sercom
#include "common-hal/busio/__init__.h"

// Number of times to try to send packet if failed.
#define ATTEMPTS 2
Expand Down
39 changes: 2 additions & 37 deletions ports/atmel-samd/common-hal/busio/SPI.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,51 +32,16 @@
#include "peripheral_clk_config.h"

#include "supervisor/board.h"
#include "common-hal/busio/__init__.h"
#include "common-hal/microcontroller/Pin.h"

#include "hal/include/hal_gpio.h"
#include "hal/include/hal_spi_m_sync.h"
#include "hal/include/hpl_spi_m_sync.h"

#include "samd/dma.h"
#include "samd/sercom.h"

bool never_reset_sercoms[SERCOM_INST_NUM];

void never_reset_sercom(Sercom *sercom) {
// Reset all SERCOMs except the ones being used by on-board devices.
Sercom *sercom_instances[SERCOM_INST_NUM] = SERCOM_INSTS;
for (int i = 0; i < SERCOM_INST_NUM; i++) {
if (sercom_instances[i] == sercom) {
never_reset_sercoms[i] = true;
break;
}
}
}

void allow_reset_sercom(Sercom *sercom) {
// Reset all SERCOMs except the ones being used by on-board devices.
Sercom *sercom_instances[SERCOM_INST_NUM] = SERCOM_INSTS;
for (int i = 0; i < SERCOM_INST_NUM; i++) {
if (sercom_instances[i] == sercom) {
never_reset_sercoms[i] = false;
break;
}
}
}

void reset_sercoms(void) {
// Reset all SERCOMs except the ones being used by on-board devices.
Sercom *sercom_instances[SERCOM_INST_NUM] = SERCOM_INSTS;
for (int i = 0; i < SERCOM_INST_NUM; i++) {
if (never_reset_sercoms[i]) {
continue;
}
// SWRST is same for all modes of SERCOMs.
sercom_instances[i]->SPI.CTRLA.bit.SWRST = 1;
}
}


void common_hal_busio_spi_construct(busio_spi_obj_t *self,
const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi,
const mcu_pin_obj_t *miso) {
Expand Down
4 changes: 0 additions & 4 deletions ports/atmel-samd/common-hal/busio/SPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,4 @@ typedef struct {
uint8_t MISO_pin;
} busio_spi_obj_t;

void reset_sercoms(void);
void never_reset_sercom(Sercom *sercom);


#endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_BUSIO_SPI_H
2 changes: 1 addition & 1 deletion ports/atmel-samd/common-hal/busio/UART.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@

#include "samd/sercom.h"

#include "common-hal/busio/SPI.h" // for never_reset_sercom
#include "common-hal/busio/__init__.h"

#define UART_DEBUG(...) (void)0
// #define UART_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__)
Expand Down
64 changes: 63 additions & 1 deletion ports/atmel-samd/common-hal/busio/__init__.c
Original file line number Diff line number Diff line change
@@ -1 +1,63 @@
// No busio module functions.
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Scott Shawcroft
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#include "samd/sercom.h"

static bool never_reset_sercoms[SERCOM_INST_NUM];

void never_reset_sercom(Sercom *sercom) {
// Reset all SERCOMs except the ones being used by on-board devices.
Sercom *sercom_instances[SERCOM_INST_NUM] = SERCOM_INSTS;
for (int i = 0; i < SERCOM_INST_NUM; i++) {
if (sercom_instances[i] == sercom) {
never_reset_sercoms[i] = true;
break;
}
}
}

void allow_reset_sercom(Sercom *sercom) {
// Reset all SERCOMs except the ones being used by on-board devices.
Sercom *sercom_instances[SERCOM_INST_NUM] = SERCOM_INSTS;
for (int i = 0; i < SERCOM_INST_NUM; i++) {
if (sercom_instances[i] == sercom) {
never_reset_sercoms[i] = false;
break;
}
}
}

void reset_sercoms(void) {
// Reset all SERCOMs except the ones being used by on-board devices.
Sercom *sercom_instances[SERCOM_INST_NUM] = SERCOM_INSTS;
for (int i = 0; i < SERCOM_INST_NUM; i++) {
if (never_reset_sercoms[i]) {
continue;
}
// SWRST is same for all modes of SERCOMs.
sercom_instances[i]->SPI.CTRLA.bit.SWRST = 1;
}
}
35 changes: 35 additions & 0 deletions ports/atmel-samd/common-hal/busio/__init__.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Scott Shawcroft
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#ifndef MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_BUSIO_INIT_H
#define MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_BUSIO_INIT_H

void reset_sercoms(void);
void allow_reset_sercom(Sercom *sercom);
void never_reset_sercom(Sercom *sercom);


#endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_BUSIO_INIT_H
33 changes: 31 additions & 2 deletions ports/atmel-samd/supervisor/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,42 @@
#error Unknown chip family
#endif

#if CIRCUITPY_ANALOGIO
#include "common-hal/analogio/AnalogIn.h"
#include "common-hal/analogio/AnalogOut.h"
#endif

#if CIRCUITPY_AUDIOBUSIO
#include "common-hal/audiobusio/PDMIn.h"
#include "common-hal/audiobusio/I2SOut.h"
#endif

#if CIRCUITPY_AUDIOIO
#include "common-hal/audioio/AudioOut.h"
#include "common-hal/busio/SPI.h"
#endif

#if CIRCUITPY_BUSIO
#include "common-hal/busio/__init__.h"
#endif

#include "common-hal/microcontroller/Pin.h"

#if CIRCUITPY_PULSEIO
#include "common-hal/pulseio/PulseIn.h"
#include "common-hal/pulseio/PulseOut.h"
#endif

#if CIRCUITPY_PWMIO
#include "common-hal/pwmio/PWMOut.h"
#endif

#if CIRCUITPY_PS2IO
#include "common-hal/ps2io/Ps2.h"
#endif

#if CIRCUITPY_RTC
#include "common-hal/rtc/RTC.h"
#endif

#if CIRCUITPY_TOUCHIO_USE_NATIVE
#include "common-hal/touchio/TouchIn.h"
Expand Down Expand Up @@ -516,8 +540,13 @@ void evsyshandler_common(void) {
supervisor_tick();
}
#endif

#if CIRCUITPY_AUDIOIO || CIRCUITPY_AUDIOBUSIO
audio_evsys_handler();
audio_dma_evsys_handler();
#endif

#if CIRCUITPY_AUDIOBUSIO
pdmin_evsys_handler();
#endif
}

Expand Down