Re-enable PDMIn without ASF and using the helpers added with
I2SOut. The API is almost the same except the frequency attribute has been renamed to sample_rate so that its less likely to be confused with frequencies within the audio itself. Fixes #263.
This commit is contained in:
parent
dd0f8689a1
commit
cfea51ec68
@ -299,13 +299,13 @@ SRC_COMMON_HAL = \
|
||||
usb_hid/Device.c \
|
||||
audioio/__init__.c \
|
||||
audioio/AudioOut.c \
|
||||
# audiobusio/PDMIn.c \
|
||||
touchio/__init__.c \
|
||||
# touchio/__init__.c \
|
||||
touchio/TouchIn.c \
|
||||
|
||||
ifeq ($(INTERNAL_LIBM),1)
|
||||
SRC_LIBM = $(addprefix lib/,\
|
||||
libm/math.c \
|
||||
libm/roundf.c \
|
||||
libm/fmodf.c \
|
||||
libm/nearbyintf.c \
|
||||
libm/ef_sqrt.c \
|
||||
@ -363,16 +363,14 @@ SRC_SHARED_MODULE = \
|
||||
uheap/__init__.c \
|
||||
ustack/__init__.c
|
||||
|
||||
ifeq ($(CHIP_FAMILY),samd21)
|
||||
SRC_COMMON_HAL += \
|
||||
audiobusio/__init__.c \
|
||||
audiobusio/I2SOut.c
|
||||
endif
|
||||
# The smallest SAMD51 packages don't have I2S. Everything else does.
|
||||
ifneq ($(CHIP_VARIANT),SAMD51G18A)
|
||||
ifneq ($(CHIP_VARIANT),SAMD51G19A)
|
||||
SRC_COMMON_HAL += \
|
||||
audiobusio/__init__.c \
|
||||
audiobusio/I2SOut.c
|
||||
audiobusio/I2SOut.c \
|
||||
audiobusio/PDMIn.c
|
||||
SRC_C += i2s.c
|
||||
endif
|
||||
endif
|
||||
|
||||
|
@ -267,7 +267,6 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t* dma,
|
||||
turn_on_event_system();
|
||||
dma->event_channel = find_sync_event_channel();
|
||||
init_event_channel_interrupt(dma->event_channel, CORE_GCLK, EVSYS_ID_GEN_DMAC_CH_0 + dma_channel);
|
||||
find_sync_event_channel();
|
||||
|
||||
// We keep the audio_dma_t for internal use and the sample as a root pointer because it
|
||||
// contains the audiodma structure.
|
||||
|
@ -64,6 +64,8 @@ uint8_t audiosample_channel_count(mp_obj_t sample_obj);
|
||||
void audio_dma_init(audio_dma_t* dma);
|
||||
void audio_dma_reset(void);
|
||||
|
||||
uint8_t find_free_audio_dma_channel(void);
|
||||
|
||||
// This sets everything up but doesn't start the timer.
|
||||
// Sample is the python object for the sample to play.
|
||||
// loop is true if we should loop the sample.
|
||||
|
@ -48,7 +48,8 @@
|
||||
#include "audio_dma.h"
|
||||
#include "clocks.h"
|
||||
#include "events.h"
|
||||
#include "samd21_pins.h"
|
||||
#include "i2s.h"
|
||||
#include "pins.h"
|
||||
#include "shared_dma.h"
|
||||
#include "timers.h"
|
||||
|
||||
@ -155,18 +156,7 @@ void common_hal_audiobusio_i2sout_construct(audiobusio_i2sout_obj_t* self,
|
||||
self->clock_unit = ws_clock_unit;
|
||||
self->serializer = serializer;
|
||||
|
||||
// Make sure the I2S peripheral is running so we can see if the resources we need are free.
|
||||
#ifdef SAMD51
|
||||
hri_mclk_set_APBDMASK_I2S_bit(MCLK);
|
||||
|
||||
// Connect the clock units to the 2mhz clock by default. They can't reset without it.
|
||||
connect_gclk_to_peripheral(5, I2S_GCLK_ID_0);
|
||||
connect_gclk_to_peripheral(5, I2S_GCLK_ID_1);
|
||||
#endif
|
||||
|
||||
#ifdef SAMD21
|
||||
_pm_enable_bus_clock(PM_BUS_APBC, I2S);
|
||||
#endif
|
||||
turn_on_i2s();
|
||||
|
||||
if (I2S->CTRLA.bit.ENABLE == 0) {
|
||||
I2S->CTRLA.bit.SWRST = 1;
|
||||
@ -281,8 +271,7 @@ void common_hal_audiobusio_i2sout_play(audiobusio_i2sout_obj_t* self,
|
||||
}
|
||||
|
||||
// Configure the I2S peripheral
|
||||
I2S->CTRLA.bit.ENABLE = 0;
|
||||
while (I2S->SYNCBUSY.bit.ENABLE == 1) {}
|
||||
i2s_set_enable(false);
|
||||
|
||||
I2S->CLKCTRL[self->clock_unit].reg = clkctrl;
|
||||
#ifdef SAMD21
|
||||
@ -296,8 +285,7 @@ void common_hal_audiobusio_i2sout_play(audiobusio_i2sout_obj_t* self,
|
||||
enable_clock_generator(self->gclk, CLOCK_48MHZ, divisor);
|
||||
connect_gclk_to_peripheral(self->gclk, I2S_GCLK_ID_0 + self->clock_unit);
|
||||
|
||||
I2S->CTRLA.bit.ENABLE = 1;
|
||||
while (I2S->SYNCBUSY.bit.ENABLE == 1) {}
|
||||
i2s_set_enable(true);
|
||||
|
||||
#ifdef SAMD21
|
||||
uint32_t tx_register = (uint32_t) &I2S->DATA[self->serializer].reg;
|
||||
|
@ -37,17 +37,32 @@
|
||||
#include "shared-bindings/audiobusio/PDMIn.h"
|
||||
#include "shared-bindings/microcontroller/Pin.h"
|
||||
|
||||
#include "samd21_pins.h"
|
||||
#include "atmel_start_pins.h"
|
||||
#include "hal/include/hal_gpio.h"
|
||||
#include "hal/utils/include/utils.h"
|
||||
|
||||
#include "audio_dma.h"
|
||||
#include "clocks.h"
|
||||
#include "events.h"
|
||||
#include "i2s.h"
|
||||
#include "pins.h"
|
||||
#include "shared_dma.h"
|
||||
#include "tick.h"
|
||||
|
||||
#define OVERSAMPLING 64
|
||||
#define SAMPLES_PER_BUFFER 32
|
||||
#define SAMPLES_PER_BUFFER 64
|
||||
|
||||
// MEMS microphones must be clocked at at least 1MHz.
|
||||
#define MIN_MIC_CLOCK 1000000
|
||||
|
||||
#ifdef SAMD21
|
||||
#define SERCTRL(name) I2S_SERCTRL_ ## name
|
||||
#endif
|
||||
|
||||
#ifdef SAMD51
|
||||
#define SERCTRL(name) I2S_RXCTRL_ ## name
|
||||
#endif
|
||||
|
||||
void pdmin_reset(void) {
|
||||
while (I2S->SYNCBUSY.reg & I2S_SYNCBUSY_ENABLE) {}
|
||||
I2S->INTENCLR.reg = I2S_INTENCLR_MASK;
|
||||
@ -60,20 +75,33 @@ void pdmin_reset(void) {
|
||||
void common_hal_audiobusio_pdmin_construct(audiobusio_pdmin_obj_t* self,
|
||||
const mcu_pin_obj_t* clock_pin,
|
||||
const mcu_pin_obj_t* data_pin,
|
||||
uint32_t frequency,
|
||||
uint32_t sample_rate,
|
||||
uint8_t bit_depth,
|
||||
bool mono,
|
||||
uint8_t oversample) {
|
||||
self->clock_pin = clock_pin; // PA10, PA20 -> SCK0, PB11 -> SCK1
|
||||
if (clock_pin == &pin_PA10
|
||||
#ifdef PIN_PA20
|
||||
|| clock_pin == &pin_PA20
|
||||
#ifdef SAMD21
|
||||
if (clock_pin == &pin_PA10
|
||||
#ifdef PIN_PA20
|
||||
|| clock_pin == &pin_PA20
|
||||
#endif
|
||||
) {
|
||||
self->clock_unit = 0;
|
||||
#ifdef PIN_PB11
|
||||
} else if (clock_pin == &pin_PB11) {
|
||||
self->clock_unit = 1;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef SAMD51
|
||||
if (clock_pin == &pin_PA10 || clock_pin == &pin_PB16) {
|
||||
self->clock_unit = 0;
|
||||
} else if (clock_pin == &pin_PB12
|
||||
#ifdef PIN_PB28
|
||||
|| data_pin == &pin_PB28) {
|
||||
#else
|
||||
) {
|
||||
self->clock_unit = 0;
|
||||
#ifdef PIN_PB11
|
||||
} else if (clock_pin == &pin_PB11) {
|
||||
self->clock_unit = 1;
|
||||
#endif
|
||||
self->clock_unit = 1;
|
||||
#endif
|
||||
} else {
|
||||
mp_raise_ValueError("Invalid clock pin");
|
||||
@ -81,80 +109,108 @@ void common_hal_audiobusio_pdmin_construct(audiobusio_pdmin_obj_t* self,
|
||||
|
||||
self->data_pin = data_pin; // PA07, PA19 -> SD0, PA08, PB16 -> SD1
|
||||
|
||||
#ifdef SAMD21
|
||||
if (data_pin == &pin_PA07 || data_pin == &pin_PA19) {
|
||||
self->serializer = 0;
|
||||
} else if (data_pin == &pin_PA08
|
||||
#ifdef PB16
|
||||
#ifdef PIN_PB16
|
||||
|| data_pin == &pin_PB16) {
|
||||
#else
|
||||
) {
|
||||
#endif
|
||||
self->serializer = 1;
|
||||
#endif
|
||||
#ifdef SAMD51
|
||||
if (data_pin == &pin_PB10 || data_pin == &pin_PA22) {
|
||||
self->serializer = 1;
|
||||
#endif
|
||||
} else {
|
||||
mp_raise_ValueError("Invalid data pin");
|
||||
}
|
||||
|
||||
claim_pin(clock_pin);
|
||||
claim_pin(data_pin);
|
||||
|
||||
if (MP_STATE_VM(audiodma_block_counter) == NULL &&
|
||||
!allocate_block_counter()) {
|
||||
mp_raise_RuntimeError("Unable to allocate audio DMA block counter.");
|
||||
}
|
||||
|
||||
if (!(bit_depth == 16 || bit_depth == 8) || !mono || oversample != OVERSAMPLING) {
|
||||
mp_raise_NotImplementedError("Only 8 or 16 bit mono with " MP_STRINGIFY(OVERSAMPLING) "x oversampling is supported.");
|
||||
}
|
||||
|
||||
// TODO(tannewt): Use the DPLL to get a more precise sampling rate.
|
||||
// DFLL -> GCLK (/600 for 8khz, /300 for 16khz and /150 for 32khz) -> DPLL (*(63 + 1)) -> GCLK ( / 10) -> 512khz
|
||||
turn_on_i2s();
|
||||
|
||||
i2s_init(&self->i2s_instance, I2S);
|
||||
struct i2s_clock_unit_config config_clock_unit;
|
||||
i2s_clock_unit_get_config_defaults(&config_clock_unit);
|
||||
config_clock_unit.clock.gclk_src = GCLK_GENERATOR_3;
|
||||
|
||||
config_clock_unit.clock.mck_src = I2S_MASTER_CLOCK_SOURCE_GCLK;
|
||||
config_clock_unit.clock.mck_out_enable = false;
|
||||
|
||||
config_clock_unit.clock.sck_src = I2S_SERIAL_CLOCK_SOURCE_MCKDIV;
|
||||
uint32_t clock_divisor = (uint32_t) roundf( 8000000.0f / frequency / oversample);
|
||||
config_clock_unit.clock.sck_div = clock_divisor;
|
||||
float mic_clock_freq = 8000000.0f / clock_divisor;
|
||||
self->frequency = mic_clock_freq / oversample;
|
||||
if (mic_clock_freq < MIN_MIC_CLOCK || clock_divisor == 0 || clock_divisor > 255) {
|
||||
mp_raise_ValueError("sampling frequency out of range");
|
||||
if (I2S->CTRLA.bit.ENABLE == 0) {
|
||||
I2S->CTRLA.bit.SWRST = 1;
|
||||
while (I2S->CTRLA.bit.SWRST == 1) {}
|
||||
} else {
|
||||
#ifdef SAMD21
|
||||
if ((I2S->CTRLA.vec.SEREN & (1 << self->serializer)) != 0) {
|
||||
mp_raise_RuntimeError("Serializer in use");
|
||||
}
|
||||
#endif
|
||||
#ifdef SAMD51
|
||||
if (I2S->CTRLA.bit.RXEN == 1) {
|
||||
mp_raise_RuntimeError("Serializer in use");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#ifdef SAMD51
|
||||
#define GPIO_I2S_FUNCTION GPIO_PIN_FUNCTION_J
|
||||
#endif
|
||||
#ifdef SAMD21
|
||||
#define GPIO_I2S_FUNCTION GPIO_PIN_FUNCTION_G
|
||||
#endif
|
||||
assert_pin_free(clock_pin);
|
||||
assert_pin_free(data_pin);
|
||||
|
||||
config_clock_unit.frame.number_slots = 2;
|
||||
config_clock_unit.frame.slot_size = I2S_SLOT_SIZE_16_BIT;
|
||||
config_clock_unit.frame.data_delay = I2S_DATA_DELAY_0;
|
||||
uint32_t clock_divisor = (uint32_t) roundf( 48000000.0f / sample_rate / oversample);
|
||||
float mic_clock_freq = 48000000.0f / clock_divisor;
|
||||
self->sample_rate = mic_clock_freq / oversample;
|
||||
if (mic_clock_freq < MIN_MIC_CLOCK || clock_divisor == 0) {
|
||||
mp_raise_ValueError("sampling rate out of range");
|
||||
}
|
||||
// Find a free GCLK to generate the MCLK signal.
|
||||
uint8_t gclk = find_free_gclk(clock_divisor);
|
||||
if (gclk > GCLK_GEN_NUM) {
|
||||
mp_raise_RuntimeError("Unable to find free GCLK");
|
||||
}
|
||||
self->gclk = gclk;
|
||||
|
||||
config_clock_unit.frame.frame_sync.width = I2S_FRAME_SYNC_WIDTH_SLOT;
|
||||
enable_clock_generator(self->gclk, CLOCK_48MHZ, clock_divisor);
|
||||
connect_gclk_to_peripheral(self->gclk, I2S_GCLK_ID_0 + self->clock_unit);
|
||||
|
||||
config_clock_unit.mck_pin.enable = false;
|
||||
config_clock_unit.sck_pin.enable = true;
|
||||
config_clock_unit.sck_pin.gpio = self->clock_pin->pin;
|
||||
// Mux is always the same.
|
||||
config_clock_unit.sck_pin.mux = 6L;
|
||||
config_clock_unit.fs_pin.enable = false;
|
||||
i2s_clock_unit_set_config(&self->i2s_instance, self->clock_unit, &config_clock_unit);
|
||||
// Clock unit configuration
|
||||
|
||||
struct i2s_serializer_config config_serializer;
|
||||
i2s_serializer_get_config_defaults(&config_serializer);
|
||||
config_serializer.clock_unit = self->clock_unit;
|
||||
config_serializer.mode = I2S_SERIALIZER_PDM2;
|
||||
config_serializer.data_size = I2S_DATA_SIZE_32BIT;
|
||||
config_serializer.data_pin.gpio = self->data_pin->pin;
|
||||
// Mux is always the same.
|
||||
config_serializer.data_pin.mux = 6L;
|
||||
config_serializer.data_pin.enable = true;
|
||||
i2s_serializer_set_config(&self->i2s_instance, self->serializer, &config_serializer);
|
||||
i2s_enable(&self->i2s_instance);
|
||||
uint32_t clkctrl = I2S_CLKCTRL_MCKSEL_GCLK |
|
||||
I2S_CLKCTRL_NBSLOTS(2) |
|
||||
I2S_CLKCTRL_FSWIDTH_SLOT |
|
||||
I2S_CLKCTRL_SLOTSIZE_16;
|
||||
|
||||
// Serializer configuration
|
||||
#ifdef SAMD21
|
||||
uint32_t serctrl = (self->clock_unit << I2S_SERCTRL_CLKSEL_Pos) | SERCTRL(SERMODE_PDM2) | SERCTRL(DATASIZE_32);
|
||||
#endif
|
||||
#ifdef SAMD51
|
||||
uint32_t serctrl = (self->clock_unit << I2S_RXCTRL_CLKSEL_Pos) | SERCTRL(SERMODE_PDM2) | SERCTRL(DATASIZE_32);
|
||||
#endif
|
||||
|
||||
// Configure the I2S peripheral
|
||||
i2s_set_enable(false);
|
||||
|
||||
I2S->CLKCTRL[self->clock_unit].reg = clkctrl;
|
||||
#ifdef SAMD21
|
||||
I2S->SERCTRL[self->serializer].reg = serctrl;
|
||||
#endif
|
||||
#ifdef SAMD51
|
||||
I2S->RXCTRL.reg = serctrl;
|
||||
#endif
|
||||
|
||||
i2s_set_enable(true);
|
||||
|
||||
// Run the serializer all the time. This eliminates startup delay for the microphone.
|
||||
i2s_clock_unit_enable(&self->i2s_instance, self->clock_unit);
|
||||
i2s_serializer_enable(&self->i2s_instance, self->serializer);
|
||||
i2s_set_clock_unit_enable(self->clock_unit, true);
|
||||
i2s_set_serializer_enable(self->serializer, true);
|
||||
|
||||
claim_pin(clock_pin);
|
||||
claim_pin(data_pin);
|
||||
|
||||
gpio_set_pin_function(self->clock_pin->pin, GPIO_I2S_FUNCTION);
|
||||
gpio_set_pin_function(self->data_pin->pin, GPIO_I2S_FUNCTION);
|
||||
|
||||
self->bytes_per_sample = oversample >> 3;
|
||||
self->bit_depth = bit_depth;
|
||||
@ -168,10 +224,15 @@ void common_hal_audiobusio_pdmin_deinit(audiobusio_pdmin_obj_t* self) {
|
||||
if (common_hal_audiobusio_pdmin_deinited(self)) {
|
||||
return;
|
||||
}
|
||||
i2s_disable(&self->i2s_instance);
|
||||
i2s_serializer_disable(&self->i2s_instance, self->serializer);
|
||||
i2s_clock_unit_disable(&self->i2s_instance, self->clock_unit);
|
||||
i2s_reset(&self->i2s_instance);
|
||||
|
||||
i2s_set_serializer_enable(self->serializer, false);
|
||||
i2s_set_clock_unit_enable(self->clock_unit, false);
|
||||
|
||||
i2s_set_enable(false);
|
||||
|
||||
disconnect_gclk_from_peripheral(self->gclk, I2S_GCLK_ID_0 + self->clock_unit);
|
||||
disable_clock_generator(self->gclk);
|
||||
|
||||
reset_pin(self->clock_pin->pin);
|
||||
reset_pin(self->data_pin->pin);
|
||||
self->clock_pin = mp_const_none;
|
||||
@ -182,65 +243,67 @@ uint8_t common_hal_audiobusio_pdmin_get_bit_depth(audiobusio_pdmin_obj_t* self)
|
||||
return self->bit_depth;
|
||||
}
|
||||
|
||||
uint32_t common_hal_audiobusio_pdmin_get_frequency(audiobusio_pdmin_obj_t* self) {
|
||||
return self->frequency;
|
||||
uint32_t common_hal_audiobusio_pdmin_get_sample_rate(audiobusio_pdmin_obj_t* self) {
|
||||
return self->sample_rate;
|
||||
}
|
||||
|
||||
static void setup_dma(audiobusio_pdmin_obj_t* self, uint32_t length,
|
||||
DmacDescriptor* second_descriptor,
|
||||
uint8_t words_per_buffer, uint8_t words_per_sample,
|
||||
uint32_t* first_buffer, uint32_t* second_buffer) {
|
||||
// Set up the DMA
|
||||
struct dma_descriptor_config descriptor_config;
|
||||
dma_descriptor_get_config_defaults(&descriptor_config);
|
||||
descriptor_config.beat_size = DMA_BEAT_SIZE_WORD;
|
||||
descriptor_config.step_selection = DMA_STEPSEL_SRC;
|
||||
descriptor_config.source_address = (uint32_t)&I2S->DATA[self->serializer];
|
||||
descriptor_config.src_increment_enable = false;
|
||||
DmacDescriptor* descriptor,
|
||||
DmacDescriptor* second_descriptor,
|
||||
uint32_t words_per_buffer, uint8_t words_per_sample,
|
||||
uint32_t* first_buffer, uint32_t* second_buffer) {
|
||||
descriptor->BTCTRL.reg = DMAC_BTCTRL_VALID |
|
||||
DMAC_BTCTRL_BLOCKACT_NOACT |
|
||||
DMAC_BTCTRL_EVOSEL_BLOCK |
|
||||
DMAC_BTCTRL_DSTINC |
|
||||
DMAC_BTCTRL_BEATSIZE_WORD;
|
||||
|
||||
// Block transfer count is the number of beats per block (aka descriptor).
|
||||
// In this case there are two bytes per beat so divide the length by two.
|
||||
uint16_t block_transfer_count = words_per_buffer;
|
||||
if (length * words_per_sample < words_per_buffer) {
|
||||
block_transfer_count = length * words_per_sample;
|
||||
}
|
||||
descriptor_config.block_transfer_count = block_transfer_count;
|
||||
descriptor_config.destination_address = ((uint32_t) first_buffer + sizeof(uint32_t) * block_transfer_count);
|
||||
descriptor_config.event_output_selection = DMA_EVENT_OUTPUT_BLOCK;
|
||||
descriptor_config.next_descriptor_address = 0;
|
||||
|
||||
descriptor->BTCNT.reg = block_transfer_count;
|
||||
descriptor->DSTADDR.reg = ((uint32_t) first_buffer + sizeof(uint32_t) * block_transfer_count);
|
||||
descriptor->DESCADDR.reg = 0;
|
||||
if (length * words_per_sample > words_per_buffer) {
|
||||
descriptor_config.next_descriptor_address = ((uint32_t)second_descriptor);
|
||||
descriptor->DESCADDR.reg = ((uint32_t)second_descriptor);
|
||||
}
|
||||
dma_descriptor_create(audio_dma.descriptor, &descriptor_config);
|
||||
#ifdef SAMD21
|
||||
descriptor->SRCADDR.reg = (uint32_t)&I2S->DATA[self->serializer];
|
||||
#endif
|
||||
#ifdef SAMD51
|
||||
descriptor->SRCADDR.reg = (uint32_t)&I2S->RXDATA;
|
||||
#endif
|
||||
|
||||
// Do we need more values than will fit in the first buffer?
|
||||
// If so, set up a second buffer chained to be filled after the first buffer.
|
||||
if (length * words_per_sample > words_per_buffer) {
|
||||
block_transfer_count = words_per_buffer;
|
||||
descriptor_config.next_descriptor_address = ((uint32_t)audio_dma.descriptor);
|
||||
second_descriptor->DESCADDR.reg = ((uint32_t)descriptor);
|
||||
if (length * words_per_sample < 2 * words_per_buffer) {
|
||||
// Length needed is more than one buffer but less than two.
|
||||
// Subtract off the size of the first buffer, and what remains is the count we need.
|
||||
block_transfer_count = length * words_per_sample - words_per_buffer;
|
||||
descriptor_config.next_descriptor_address = 0;
|
||||
second_descriptor->DESCADDR.reg = 0;
|
||||
}
|
||||
descriptor_config.block_transfer_count = block_transfer_count;
|
||||
descriptor_config.destination_address = ((uint32_t) second_buffer + sizeof(uint32_t) * block_transfer_count);
|
||||
dma_descriptor_create(second_descriptor, &descriptor_config);
|
||||
second_descriptor->DSTADDR.reg = ((uint32_t) second_buffer + sizeof(uint32_t) * block_transfer_count);
|
||||
|
||||
second_descriptor->BTCNT.reg = block_transfer_count;
|
||||
#ifdef SAMD21
|
||||
second_descriptor->SRCADDR.reg = (uint32_t)&I2S->DATA[self->serializer];
|
||||
#endif
|
||||
#ifdef SAMD51
|
||||
second_descriptor->SRCADDR.reg = (uint32_t)&I2S->RXDATA;
|
||||
#endif
|
||||
second_descriptor->BTCTRL.reg = DMAC_BTCTRL_VALID |
|
||||
DMAC_BTCTRL_BLOCKACT_NOACT |
|
||||
DMAC_BTCTRL_EVOSEL_BLOCK |
|
||||
DMAC_BTCTRL_DSTINC |
|
||||
DMAC_BTCTRL_BEATSIZE_WORD;
|
||||
}
|
||||
|
||||
switch_audiodma_trigger(I2S_DMAC_ID_RX_0 + self->serializer);
|
||||
}
|
||||
|
||||
void start_dma(audiobusio_pdmin_obj_t* self) {
|
||||
dma_start_transfer_job(&audio_dma);
|
||||
tc_start_counter(MP_STATE_VM(audiodma_block_counter));
|
||||
I2S->DATA[1].reg = I2S->DATA[1].reg;
|
||||
}
|
||||
|
||||
void stop_dma(audiobusio_pdmin_obj_t* self) {
|
||||
// Shutdown the DMA: serializer keeps running.
|
||||
tc_stop_counter(MP_STATE_VM(audiodma_block_counter));
|
||||
dma_abort_job(&audio_dma);
|
||||
}
|
||||
|
||||
// a windowed sinc filter for 44 khz, 64 samples
|
||||
@ -290,20 +353,27 @@ static uint16_t filter_sample(uint32_t pdm_samples[4]) {
|
||||
// output_buffer_length is the number of slots, not the number of bytes.
|
||||
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 = find_free_audio_dma_channel();
|
||||
uint8_t event_channel = find_sync_event_channel();
|
||||
|
||||
// We allocate two buffers on the stack to use for double buffering.
|
||||
const uint8_t samples_per_buffer = SAMPLES_PER_BUFFER;
|
||||
// For every word we record, we throw away 2 bytes of a phantom second channel.
|
||||
const uint8_t words_per_sample = self->bytes_per_sample / 2;
|
||||
const uint8_t words_per_buffer = samples_per_buffer * words_per_sample;
|
||||
uint8_t words_per_sample = self->bytes_per_sample / 2;
|
||||
uint32_t words_per_buffer = samples_per_buffer * words_per_sample;
|
||||
uint32_t first_buffer[words_per_buffer];
|
||||
uint32_t second_buffer[words_per_buffer];
|
||||
|
||||
turn_on_event_system();
|
||||
|
||||
COMPILER_ALIGNED(16) DmacDescriptor second_descriptor;
|
||||
|
||||
setup_dma(self, output_buffer_length, &second_descriptor, words_per_buffer,
|
||||
words_per_sample, first_buffer, second_buffer);
|
||||
setup_dma(self, output_buffer_length, dma_descriptor(dma_channel), &second_descriptor,
|
||||
words_per_buffer, words_per_sample, first_buffer, second_buffer);
|
||||
|
||||
start_dma(self);
|
||||
dma_configure(dma_channel, I2S_DMAC_ID_RX_0, true);
|
||||
init_event_channel_interrupt(event_channel, CORE_GCLK, EVSYS_ID_GEN_DMAC_CH_0 + dma_channel);
|
||||
dma_enable_channel(dma_channel);
|
||||
|
||||
// Record
|
||||
uint32_t buffers_processed = 0;
|
||||
@ -311,24 +381,23 @@ 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 block_counter;
|
||||
while ((block_counter = tc_get_count_value(MP_STATE_VM(audiodma_block_counter))) == buffers_processed) {
|
||||
while (!event_interrupt_active(event_channel)) {
|
||||
#ifdef MICROPY_VM_HOOK_LOOP
|
||||
MICROPY_VM_HOOK_LOOP
|
||||
#endif
|
||||
}
|
||||
if (block_counter != (buffers_processed + 1)) {
|
||||
// Looks like we aren't keeping up. We shouldn't skip a buffer.
|
||||
break;
|
||||
}
|
||||
|
||||
// The mic is running all the time, so we don't need to wait the usual 10msec or 100msec
|
||||
// for it to start up.
|
||||
|
||||
// Flip back and forth between processing the first and second buffers.
|
||||
uint32_t *buffer = first_buffer;
|
||||
DmacDescriptor* descriptor = audio_dma.descriptor;
|
||||
DmacDescriptor* descriptor = dma_descriptor(dma_channel);
|
||||
if (buffers_processed % 2 == 1) {
|
||||
buffer = second_buffer;
|
||||
descriptor = &second_descriptor;
|
||||
@ -375,7 +444,8 @@ uint32_t common_hal_audiobusio_pdmin_record_to_buffer(audiobusio_pdmin_obj_t* se
|
||||
}
|
||||
}
|
||||
|
||||
stop_dma(self);
|
||||
disable_event_channel(event_channel);
|
||||
dma_disable_channel(dma_channel);
|
||||
|
||||
return values_output;
|
||||
}
|
||||
|
@ -36,11 +36,12 @@ typedef struct {
|
||||
mp_obj_base_t base;
|
||||
const mcu_pin_obj_t *clock_pin;
|
||||
const mcu_pin_obj_t *data_pin;
|
||||
uint32_t frequency;
|
||||
uint32_t sample_rate;
|
||||
uint8_t serializer;
|
||||
uint8_t clock_unit;
|
||||
uint8_t bytes_per_sample;
|
||||
uint8_t bit_depth;
|
||||
uint8_t gclk;
|
||||
} audiobusio_pdmin_obj_t;
|
||||
|
||||
void pdmin_reset(void);
|
||||
|
@ -91,7 +91,7 @@ uint8_t find_async_event_channel(void) {
|
||||
#define EVSYS_SYNCH_NUM EVSYS_CHANNELS
|
||||
#endif
|
||||
uint8_t find_sync_event_channel(void) {
|
||||
int8_t channel;
|
||||
uint8_t channel;
|
||||
for (channel = 0; channel < EVSYS_SYNCH_NUM; channel++) {
|
||||
if (channel_free(channel)) {
|
||||
break;
|
||||
@ -145,6 +145,8 @@ void init_event_channel_interrupt(uint8_t channel, uint8_t gclk, uint8_t generat
|
||||
EVSYS->Channel[channel].CHANNEL.reg = EVSYS_CHANNEL_EVGEN(generator) |
|
||||
EVSYS_CHANNEL_PATH_SYNCHRONOUS |
|
||||
EVSYS_CHANNEL_EDGSEL_RISING_EDGE;
|
||||
EVSYS->Channel[channel].CHINTFLAG.reg = EVSYS_CHINTFLAG_EVD | EVSYS_CHINTFLAG_OVR;
|
||||
EVSYS->Channel[channel].CHINTENSET.reg = EVSYS_CHINTENSET_EVD | EVSYS_CHINTENSET_OVR;
|
||||
#endif
|
||||
#ifdef SAMD21
|
||||
EVSYS->CHANNEL.reg = EVSYS_CHANNEL_CHANNEL(channel) |
|
||||
@ -187,8 +189,25 @@ bool event_interrupt_active(uint8_t channel) {
|
||||
// Only clear if we know its active, otherwise there is the possibility it becomes after we
|
||||
// check but before we clear.
|
||||
if (active) {
|
||||
EVSYS->Channel[channel].CHINTFLAG.reg = EVSYS_CHINTFLAG_EVD;
|
||||
EVSYS->Channel[channel].CHINTFLAG.reg = EVSYS_CHINTFLAG_EVD | EVSYS_CHINTFLAG_OVR;
|
||||
}
|
||||
#endif
|
||||
return active;
|
||||
}
|
||||
|
||||
bool event_interrupt_overflow(uint8_t channel) {
|
||||
bool overflow = false;
|
||||
#ifdef SAMD21
|
||||
if (channel > 7) {
|
||||
uint8_t value = 1 << (channel - 7);
|
||||
overflow = (EVSYS->INTFLAG.reg & EVSYS_INTFLAG_OVRp8(value)) != 0;
|
||||
} else {
|
||||
uint8_t value = 1 << channel;
|
||||
overflow = (EVSYS->INTFLAG.reg & EVSYS_INTFLAG_OVR(value)) != 0;
|
||||
}
|
||||
#endif
|
||||
#ifdef SAMD51
|
||||
overflow = EVSYS->Channel[channel].CHINTFLAG.bit.OVR;
|
||||
#endif
|
||||
return overflow;
|
||||
}
|
||||
|
@ -42,5 +42,6 @@ void connect_event_user_to_channel(uint8_t user, uint8_t channel);
|
||||
void init_async_event_channel(uint8_t channel, uint8_t generator);
|
||||
void init_event_channel_interrupt(uint8_t channel, uint8_t gclk, uint8_t generator);
|
||||
bool event_interrupt_active(uint8_t channel);
|
||||
bool event_interrupt_overflow(uint8_t channel);
|
||||
|
||||
#endif // MICROPY_INCLUDED_ATMEL_SAMD_EVENTS_H
|
||||
|
84
ports/atmel-samd/i2s.c
Normal file
84
ports/atmel-samd/i2s.c
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 Scott Shawcroft for Adafruit Industries
|
||||
*
|
||||
* 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 "i2s.h"
|
||||
|
||||
#include "clocks.h"
|
||||
|
||||
#include "hpl/gclk/hpl_gclk_base.h"
|
||||
#ifdef SAMD21
|
||||
#include "hpl/pm/hpl_pm_base.h"
|
||||
#endif
|
||||
|
||||
void turn_on_i2s(void) {
|
||||
// Make sure the I2S peripheral is running so we can see if the resources we need are free.
|
||||
#ifdef SAMD51
|
||||
hri_mclk_set_APBDMASK_I2S_bit(MCLK);
|
||||
|
||||
// Connect the clock units to the 2mhz clock by default. They can't reset without it.
|
||||
connect_gclk_to_peripheral(5, I2S_GCLK_ID_0);
|
||||
connect_gclk_to_peripheral(5, I2S_GCLK_ID_1);
|
||||
#endif
|
||||
|
||||
#ifdef SAMD21
|
||||
_pm_enable_bus_clock(PM_BUS_APBC, I2S);
|
||||
#endif
|
||||
}
|
||||
|
||||
void i2s_set_enable(bool enable) {
|
||||
while (I2S->SYNCBUSY.bit.ENABLE == 1) {}
|
||||
I2S->CTRLA.bit.ENABLE = enable;
|
||||
while (I2S->SYNCBUSY.bit.ENABLE == 1) {}
|
||||
}
|
||||
|
||||
void i2s_set_clock_unit_enable(uint8_t clock_unit, bool enable) {
|
||||
while ((I2S->SYNCBUSY.vec.CKEN & (1 << clock_unit)) != 0) {}
|
||||
I2S->CTRLA.vec.CKEN = 1 << clock_unit;
|
||||
while ((I2S->SYNCBUSY.vec.CKEN & (1 << clock_unit)) != 0) {}
|
||||
}
|
||||
|
||||
void i2s_set_serializer_enable(uint8_t serializer, bool enable) {
|
||||
#ifdef SAMD21
|
||||
while ((I2S->SYNCBUSY.vec.SEREN & (1 << serializer)) != 0) {}
|
||||
if (enable) {
|
||||
I2S->CTRLA.vec.SEREN = 1 << serializer;
|
||||
} else {
|
||||
I2S->CTRLA.vec.SEREN &= ~(1 << serializer);
|
||||
}
|
||||
while ((I2S->SYNCBUSY.vec.SEREN & (1 << serializer)) != 0) {}
|
||||
#endif
|
||||
#ifdef SAMD51
|
||||
if (serializer == 0) {
|
||||
while (I2S->SYNCBUSY.bit.TXEN == 1) {}
|
||||
I2S->CTRLA.bit.TXEN = enable;
|
||||
while (I2S->SYNCBUSY.bit.TXEN == 1) {}
|
||||
} else {
|
||||
while (I2S->SYNCBUSY.bit.RXEN == 1) {}
|
||||
I2S->CTRLA.bit.RXEN = enable;
|
||||
while (I2S->SYNCBUSY.bit.RXEN == 1) {}
|
||||
}
|
||||
#endif
|
||||
}
|
41
ports/atmel-samd/i2s.h
Normal file
41
ports/atmel-samd/i2s.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 Scott Shawcroft for Adafruit Industries
|
||||
*
|
||||
* 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_I2S_H
|
||||
#define MICROPY_INCLUDED_ATMEL_SAMD_I2S_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "include/sam.h"
|
||||
|
||||
void turn_on_i2s(void);
|
||||
|
||||
void i2s_set_enable(bool enable);
|
||||
void i2s_set_clock_unit_enable(uint8_t clock, bool enable);
|
||||
void i2s_set_serializer_enable(uint8_t serializer, bool enable);
|
||||
|
||||
#endif // MICROPY_INCLUDED_ATMEL_SAMD_I2S_H
|
@ -42,18 +42,18 @@
|
||||
//|
|
||||
//| PDMIn can be used to record an input audio signal on a given set of pins.
|
||||
//|
|
||||
//| .. class:: PDMIn(clock_pin, data_pin, \*, frequency=16000, bit_depth=8, mono=True, oversample=64, startup_delay=0.11)
|
||||
//| .. class:: PDMIn(clock_pin, data_pin, \*, sample_rate=16000, bit_depth=8, mono=True, oversample=64, startup_delay=0.11)
|
||||
//|
|
||||
//| Create a PDMIn object associated with the given pins. This allows you to
|
||||
//| record audio signals from the given pins. Individual ports may put further
|
||||
//| restrictions on the recording parameters. The overall frequency is
|
||||
//| determined by `frequency` x ``oversample``, and the total must be 1MHz or
|
||||
//| higher, so `frequency` must be a minimum of 16000.
|
||||
//| restrictions on the recording parameters. The overall sample rate is
|
||||
//| determined by `sample_rate` x ``oversample``, and the total must be 1MHz or
|
||||
//| higher, so `sample_rate` must be a minimum of 16000.
|
||||
//|
|
||||
//| :param ~microcontroller.Pin clock_pin: The pin to output the clock to
|
||||
//| :param ~microcontroller.Pin data_pin: The pin to read the data from
|
||||
//| :param int frequency: Target frequency of the resulting samples. Check `frequency` for actual value.
|
||||
//| Minimum frequency is about 16000 Hz.
|
||||
//| :param int sample_rate: Target sample_rate of the resulting samples. Check `sample_rate` for actual value.
|
||||
//| Minimum sample_rate is about 16000 Hz.
|
||||
//| :param int bit_depth: Final number of bits per sample. Must be divisible by 8
|
||||
//| :param bool mono: True when capturing a single channel of audio, captures two channels otherwise
|
||||
//| :param int oversample: Number of single bit samples to decimate into a final sample. Must be divisible by 8
|
||||
@ -69,7 +69,7 @@
|
||||
//|
|
||||
//| # Prep a buffer to record into
|
||||
//| b = bytearray(200)
|
||||
//| with audiobusio.PDMIn(board.MICROPHONE_CLOCK, board.MICROPHONE_DATA, frequency=16000) as mic:
|
||||
//| with audiobusio.PDMIn(board.MICROPHONE_CLOCK, board.MICROPHONE_DATA, sample_rate=16000) as mic:
|
||||
//| mic.record(b, len(b))
|
||||
//|
|
||||
//| Record 16-bit unsigned samples to buffer::
|
||||
@ -83,15 +83,15 @@
|
||||
//| b = array.array("H")
|
||||
//| for i in range(200):
|
||||
//| b.append(0)
|
||||
//| with audiobusio.PDMIn(board.MICROPHONE_CLOCK, board.MICROPHONE_DATA, frequency=16000, bit_depth=16) as mic:
|
||||
//| with audiobusio.PDMIn(board.MICROPHONE_CLOCK, board.MICROPHONE_DATA, sample_rate=16000, bit_depth=16) as mic:
|
||||
//| mic.record(b, len(b))
|
||||
//|
|
||||
STATIC mp_obj_t audiobusio_pdmin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *pos_args) {
|
||||
enum { ARG_frequency, ARG_bit_depth, ARG_mono, ARG_oversample, ARG_startup_delay };
|
||||
enum { ARG_sample_rate, ARG_bit_depth, ARG_mono, ARG_oversample, ARG_startup_delay };
|
||||
mp_map_t kw_args;
|
||||
mp_map_init_fixed_table(&kw_args, n_kw, pos_args + n_args);
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_frequency, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 16000} },
|
||||
{ MP_QSTR_sample_rate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 16000} },
|
||||
{ MP_QSTR_bit_depth, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} },
|
||||
{ MP_QSTR_mono, MP_ARG_KW_ONLY | MP_ARG_BOOL,{.u_bool = true} },
|
||||
{ MP_QSTR_oversample, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 64} },
|
||||
@ -117,7 +117,7 @@ STATIC mp_obj_t audiobusio_pdmin_make_new(const mp_obj_type_t *type, size_t n_ar
|
||||
audiobusio_pdmin_obj_t *self = m_new_obj(audiobusio_pdmin_obj_t);
|
||||
self->base.type = &audiobusio_pdmin_type;
|
||||
|
||||
uint32_t frequency = args[ARG_frequency].u_int;
|
||||
uint32_t sample_rate = args[ARG_sample_rate].u_int;
|
||||
uint8_t bit_depth = args[ARG_bit_depth].u_int;
|
||||
if (bit_depth % 8 != 0) {
|
||||
mp_raise_ValueError("Bit depth must be multiple of 8.");
|
||||
@ -135,7 +135,7 @@ STATIC mp_obj_t audiobusio_pdmin_make_new(const mp_obj_type_t *type, size_t n_ar
|
||||
mp_raise_ValueError("Microphone startup delay must be in range 0.0 to 1.0");
|
||||
}
|
||||
|
||||
common_hal_audiobusio_pdmin_construct(self, clock_pin, data_pin, frequency,
|
||||
common_hal_audiobusio_pdmin_construct(self, clock_pin, data_pin, sample_rate,
|
||||
bit_depth, mono, oversample);
|
||||
|
||||
// Wait for the microphone to start up. Some start in 10 msecs; some take as much as 100 msecs.
|
||||
@ -215,21 +215,21 @@ STATIC mp_obj_t audiobusio_pdmin_obj_record(mp_obj_t self_obj, mp_obj_t destinat
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_3(audiobusio_pdmin_record_obj, audiobusio_pdmin_obj_record);
|
||||
|
||||
//| .. attribute:: frequency
|
||||
//| .. attribute:: sample_rate
|
||||
//|
|
||||
//| The actual frequency of the recording. This may not match the constructed
|
||||
//| frequency due to internal clock limitations.
|
||||
//| The actual sample_rate of the recording. This may not match the constructed
|
||||
//| sample rate due to internal clock limitations.
|
||||
//|
|
||||
STATIC mp_obj_t audiobusio_pdmin_obj_get_frequency(mp_obj_t self_in) {
|
||||
STATIC mp_obj_t audiobusio_pdmin_obj_get_sample_rate(mp_obj_t self_in) {
|
||||
audiobusio_pdmin_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
raise_error_if_deinited(common_hal_audiobusio_pdmin_deinited(self));
|
||||
return MP_OBJ_NEW_SMALL_INT(common_hal_audiobusio_pdmin_get_frequency(self));
|
||||
return MP_OBJ_NEW_SMALL_INT(common_hal_audiobusio_pdmin_get_sample_rate(self));
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(audiobusio_pdmin_get_frequency_obj, audiobusio_pdmin_obj_get_frequency);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(audiobusio_pdmin_get_sample_rate_obj, audiobusio_pdmin_obj_get_sample_rate);
|
||||
|
||||
const mp_obj_property_t audiobusio_pdmin_frequency_obj = {
|
||||
const mp_obj_property_t audiobusio_pdmin_sample_rate_obj = {
|
||||
.base.type = &mp_type_property,
|
||||
.proxy = {(mp_obj_t)&audiobusio_pdmin_get_frequency_obj,
|
||||
.proxy = {(mp_obj_t)&audiobusio_pdmin_get_sample_rate_obj,
|
||||
(mp_obj_t)&mp_const_none_obj,
|
||||
(mp_obj_t)&mp_const_none_obj},
|
||||
};
|
||||
@ -240,7 +240,7 @@ STATIC const mp_rom_map_elem_t audiobusio_pdmin_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audiobusio_pdmin___exit___obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_record), MP_ROM_PTR(&audiobusio_pdmin_record_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_frequency), MP_ROM_PTR(&audiobusio_pdmin_frequency_obj) }
|
||||
{ MP_ROM_QSTR(MP_QSTR_sample_rate), MP_ROM_PTR(&audiobusio_pdmin_sample_rate_obj) }
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(audiobusio_pdmin_locals_dict, audiobusio_pdmin_locals_dict_table);
|
||||
|
||||
|
@ -35,13 +35,13 @@ extern const mp_obj_type_t audiobusio_pdmin_type;
|
||||
|
||||
void common_hal_audiobusio_pdmin_construct(audiobusio_pdmin_obj_t* self,
|
||||
const mcu_pin_obj_t* clock_pin, const mcu_pin_obj_t* data_pin,
|
||||
uint32_t frequency, uint8_t bit_depth, bool mono, uint8_t oversample);
|
||||
uint32_t sample_rate, uint8_t bit_depth, bool mono, uint8_t oversample);
|
||||
void common_hal_audiobusio_pdmin_deinit(audiobusio_pdmin_obj_t* self);
|
||||
bool common_hal_audiobusio_pdmin_deinited(audiobusio_pdmin_obj_t* self);
|
||||
uint32_t common_hal_audiobusio_pdmin_record_to_buffer(audiobusio_pdmin_obj_t* self,
|
||||
uint16_t* buffer, uint32_t length);
|
||||
uint8_t common_hal_audiobusio_pdmin_get_bit_depth(audiobusio_pdmin_obj_t* self);
|
||||
uint32_t common_hal_audiobusio_pdmin_get_frequency(audiobusio_pdmin_obj_t* self);
|
||||
uint32_t common_hal_audiobusio_pdmin_get_sample_rate(audiobusio_pdmin_obj_t* self);
|
||||
// TODO(tannewt): Add record to file
|
||||
|
||||
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOBUSIO_AUDIOOUT_H
|
||||
|
@ -62,7 +62,7 @@
|
||||
STATIC const mp_rom_map_elem_t audiobusio_module_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_audiobusio) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_I2SOut), MP_ROM_PTR(&audiobusio_i2sout_type) },
|
||||
//{ MP_ROM_QSTR(MP_QSTR_PDMIn), MP_ROM_PTR(&audiobusio_pdmin_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_PDMIn), MP_ROM_PTR(&audiobusio_pdmin_type) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(audiobusio_module_globals, audiobusio_module_globals_table);
|
||||
|
Loading…
Reference in New Issue
Block a user