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:
Scott Shawcroft 2018-04-26 12:51:37 -07:00
parent dd0f8689a1
commit cfea51ec68
13 changed files with 370 additions and 167 deletions

View File

@ -299,13 +299,13 @@ SRC_COMMON_HAL = \
usb_hid/Device.c \ usb_hid/Device.c \
audioio/__init__.c \ audioio/__init__.c \
audioio/AudioOut.c \ audioio/AudioOut.c \
# audiobusio/PDMIn.c \ # touchio/__init__.c \
touchio/__init__.c \
touchio/TouchIn.c \ touchio/TouchIn.c \
ifeq ($(INTERNAL_LIBM),1) ifeq ($(INTERNAL_LIBM),1)
SRC_LIBM = $(addprefix lib/,\ SRC_LIBM = $(addprefix lib/,\
libm/math.c \ libm/math.c \
libm/roundf.c \
libm/fmodf.c \ libm/fmodf.c \
libm/nearbyintf.c \ libm/nearbyintf.c \
libm/ef_sqrt.c \ libm/ef_sqrt.c \
@ -363,16 +363,14 @@ SRC_SHARED_MODULE = \
uheap/__init__.c \ uheap/__init__.c \
ustack/__init__.c ustack/__init__.c
ifeq ($(CHIP_FAMILY),samd21) # The smallest SAMD51 packages don't have I2S. Everything else does.
SRC_COMMON_HAL += \
audiobusio/__init__.c \
audiobusio/I2SOut.c
endif
ifneq ($(CHIP_VARIANT),SAMD51G18A) ifneq ($(CHIP_VARIANT),SAMD51G18A)
ifneq ($(CHIP_VARIANT),SAMD51G19A) ifneq ($(CHIP_VARIANT),SAMD51G19A)
SRC_COMMON_HAL += \ SRC_COMMON_HAL += \
audiobusio/__init__.c \ audiobusio/__init__.c \
audiobusio/I2SOut.c audiobusio/I2SOut.c \
audiobusio/PDMIn.c
SRC_C += i2s.c
endif endif
endif endif

View File

@ -267,7 +267,6 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t* dma,
turn_on_event_system(); turn_on_event_system();
dma->event_channel = find_sync_event_channel(); dma->event_channel = find_sync_event_channel();
init_event_channel_interrupt(dma->event_channel, CORE_GCLK, EVSYS_ID_GEN_DMAC_CH_0 + dma_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 // We keep the audio_dma_t for internal use and the sample as a root pointer because it
// contains the audiodma structure. // contains the audiodma structure.

View File

@ -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_init(audio_dma_t* dma);
void audio_dma_reset(void); void audio_dma_reset(void);
uint8_t find_free_audio_dma_channel(void);
// This sets everything up but doesn't start the timer. // This sets everything up but doesn't start the timer.
// Sample is the python object for the sample to play. // Sample is the python object for the sample to play.
// loop is true if we should loop the sample. // loop is true if we should loop the sample.

View File

@ -48,7 +48,8 @@
#include "audio_dma.h" #include "audio_dma.h"
#include "clocks.h" #include "clocks.h"
#include "events.h" #include "events.h"
#include "samd21_pins.h" #include "i2s.h"
#include "pins.h"
#include "shared_dma.h" #include "shared_dma.h"
#include "timers.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->clock_unit = ws_clock_unit;
self->serializer = serializer; self->serializer = serializer;
// Make sure the I2S peripheral is running so we can see if the resources we need are free. turn_on_i2s();
#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
if (I2S->CTRLA.bit.ENABLE == 0) { if (I2S->CTRLA.bit.ENABLE == 0) {
I2S->CTRLA.bit.SWRST = 1; I2S->CTRLA.bit.SWRST = 1;
@ -281,8 +271,7 @@ void common_hal_audiobusio_i2sout_play(audiobusio_i2sout_obj_t* self,
} }
// Configure the I2S peripheral // Configure the I2S peripheral
I2S->CTRLA.bit.ENABLE = 0; i2s_set_enable(false);
while (I2S->SYNCBUSY.bit.ENABLE == 1) {}
I2S->CLKCTRL[self->clock_unit].reg = clkctrl; I2S->CLKCTRL[self->clock_unit].reg = clkctrl;
#ifdef SAMD21 #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); enable_clock_generator(self->gclk, CLOCK_48MHZ, divisor);
connect_gclk_to_peripheral(self->gclk, I2S_GCLK_ID_0 + self->clock_unit); connect_gclk_to_peripheral(self->gclk, I2S_GCLK_ID_0 + self->clock_unit);
I2S->CTRLA.bit.ENABLE = 1; i2s_set_enable(true);
while (I2S->SYNCBUSY.bit.ENABLE == 1) {}
#ifdef SAMD21 #ifdef SAMD21
uint32_t tx_register = (uint32_t) &I2S->DATA[self->serializer].reg; uint32_t tx_register = (uint32_t) &I2S->DATA[self->serializer].reg;

View File

@ -37,17 +37,32 @@
#include "shared-bindings/audiobusio/PDMIn.h" #include "shared-bindings/audiobusio/PDMIn.h"
#include "shared-bindings/microcontroller/Pin.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 "shared_dma.h"
#include "tick.h" #include "tick.h"
#define OVERSAMPLING 64 #define OVERSAMPLING 64
#define SAMPLES_PER_BUFFER 32 #define SAMPLES_PER_BUFFER 64
// MEMS microphones must be clocked at at least 1MHz. // MEMS microphones must be clocked at at least 1MHz.
#define MIN_MIC_CLOCK 1000000 #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) { void pdmin_reset(void) {
while (I2S->SYNCBUSY.reg & I2S_SYNCBUSY_ENABLE) {} while (I2S->SYNCBUSY.reg & I2S_SYNCBUSY_ENABLE) {}
I2S->INTENCLR.reg = I2S_INTENCLR_MASK; 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, void common_hal_audiobusio_pdmin_construct(audiobusio_pdmin_obj_t* self,
const mcu_pin_obj_t* clock_pin, const mcu_pin_obj_t* clock_pin,
const mcu_pin_obj_t* data_pin, const mcu_pin_obj_t* data_pin,
uint32_t frequency, uint32_t sample_rate,
uint8_t bit_depth, uint8_t bit_depth,
bool mono, bool mono,
uint8_t oversample) { uint8_t oversample) {
self->clock_pin = clock_pin; // PA10, PA20 -> SCK0, PB11 -> SCK1 self->clock_pin = clock_pin; // PA10, PA20 -> SCK0, PB11 -> SCK1
if (clock_pin == &pin_PA10 #ifdef SAMD21
#ifdef PIN_PA20 if (clock_pin == &pin_PA10
|| clock_pin == &pin_PA20 #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 #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; #endif
#ifdef PIN_PB11 self->clock_unit = 1;
} else if (clock_pin == &pin_PB11) {
self->clock_unit = 1;
#endif #endif
} else { } else {
mp_raise_ValueError("Invalid clock pin"); 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 self->data_pin = data_pin; // PA07, PA19 -> SD0, PA08, PB16 -> SD1
#ifdef SAMD21
if (data_pin == &pin_PA07 || data_pin == &pin_PA19) { if (data_pin == &pin_PA07 || data_pin == &pin_PA19) {
self->serializer = 0; self->serializer = 0;
} else if (data_pin == &pin_PA08 } else if (data_pin == &pin_PA08
#ifdef PB16 #ifdef PIN_PB16
|| data_pin == &pin_PB16) { || data_pin == &pin_PB16) {
#else #else
) { ) {
#endif #endif
self->serializer = 1; self->serializer = 1;
#endif
#ifdef SAMD51
if (data_pin == &pin_PB10 || data_pin == &pin_PA22) {
self->serializer = 1;
#endif
} else { } else {
mp_raise_ValueError("Invalid data pin"); 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) { 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."); 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. turn_on_i2s();
// DFLL -> GCLK (/600 for 8khz, /300 for 16khz and /150 for 32khz) -> DPLL (*(63 + 1)) -> GCLK ( / 10) -> 512khz
i2s_init(&self->i2s_instance, I2S); if (I2S->CTRLA.bit.ENABLE == 0) {
struct i2s_clock_unit_config config_clock_unit; I2S->CTRLA.bit.SWRST = 1;
i2s_clock_unit_get_config_defaults(&config_clock_unit); while (I2S->CTRLA.bit.SWRST == 1) {}
config_clock_unit.clock.gclk_src = GCLK_GENERATOR_3; } else {
#ifdef SAMD21
config_clock_unit.clock.mck_src = I2S_MASTER_CLOCK_SOURCE_GCLK; if ((I2S->CTRLA.vec.SEREN & (1 << self->serializer)) != 0) {
config_clock_unit.clock.mck_out_enable = false; mp_raise_RuntimeError("Serializer in use");
}
config_clock_unit.clock.sck_src = I2S_SERIAL_CLOCK_SOURCE_MCKDIV; #endif
uint32_t clock_divisor = (uint32_t) roundf( 8000000.0f / frequency / oversample); #ifdef SAMD51
config_clock_unit.clock.sck_div = clock_divisor; if (I2S->CTRLA.bit.RXEN == 1) {
float mic_clock_freq = 8000000.0f / clock_divisor; mp_raise_RuntimeError("Serializer in use");
self->frequency = mic_clock_freq / oversample; }
if (mic_clock_freq < MIN_MIC_CLOCK || clock_divisor == 0 || clock_divisor > 255) { #endif
mp_raise_ValueError("sampling frequency out of range");
} }
#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; uint32_t clock_divisor = (uint32_t) roundf( 48000000.0f / sample_rate / oversample);
config_clock_unit.frame.slot_size = I2S_SLOT_SIZE_16_BIT; float mic_clock_freq = 48000000.0f / clock_divisor;
config_clock_unit.frame.data_delay = I2S_DATA_DELAY_0; 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; // Clock unit configuration
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);
struct i2s_serializer_config config_serializer; uint32_t clkctrl = I2S_CLKCTRL_MCKSEL_GCLK |
i2s_serializer_get_config_defaults(&config_serializer); I2S_CLKCTRL_NBSLOTS(2) |
config_serializer.clock_unit = self->clock_unit; I2S_CLKCTRL_FSWIDTH_SLOT |
config_serializer.mode = I2S_SERIALIZER_PDM2; I2S_CLKCTRL_SLOTSIZE_16;
config_serializer.data_size = I2S_DATA_SIZE_32BIT;
config_serializer.data_pin.gpio = self->data_pin->pin; // Serializer configuration
// Mux is always the same. #ifdef SAMD21
config_serializer.data_pin.mux = 6L; uint32_t serctrl = (self->clock_unit << I2S_SERCTRL_CLKSEL_Pos) | SERCTRL(SERMODE_PDM2) | SERCTRL(DATASIZE_32);
config_serializer.data_pin.enable = true; #endif
i2s_serializer_set_config(&self->i2s_instance, self->serializer, &config_serializer); #ifdef SAMD51
i2s_enable(&self->i2s_instance); 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. // Run the serializer all the time. This eliminates startup delay for the microphone.
i2s_clock_unit_enable(&self->i2s_instance, self->clock_unit); i2s_set_clock_unit_enable(self->clock_unit, true);
i2s_serializer_enable(&self->i2s_instance, self->serializer); 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->bytes_per_sample = oversample >> 3;
self->bit_depth = bit_depth; 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)) { if (common_hal_audiobusio_pdmin_deinited(self)) {
return; return;
} }
i2s_disable(&self->i2s_instance);
i2s_serializer_disable(&self->i2s_instance, self->serializer); i2s_set_serializer_enable(self->serializer, false);
i2s_clock_unit_disable(&self->i2s_instance, self->clock_unit); i2s_set_clock_unit_enable(self->clock_unit, false);
i2s_reset(&self->i2s_instance);
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->clock_pin->pin);
reset_pin(self->data_pin->pin); reset_pin(self->data_pin->pin);
self->clock_pin = mp_const_none; 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; return self->bit_depth;
} }
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) {
return self->frequency; return self->sample_rate;
} }
static void setup_dma(audiobusio_pdmin_obj_t* self, uint32_t length, static void setup_dma(audiobusio_pdmin_obj_t* self, uint32_t length,
DmacDescriptor* second_descriptor, DmacDescriptor* descriptor,
uint8_t words_per_buffer, uint8_t words_per_sample, DmacDescriptor* second_descriptor,
uint32_t* first_buffer, uint32_t* second_buffer) { uint32_t words_per_buffer, uint8_t words_per_sample,
// Set up the DMA uint32_t* first_buffer, uint32_t* second_buffer) {
struct dma_descriptor_config descriptor_config; descriptor->BTCTRL.reg = DMAC_BTCTRL_VALID |
dma_descriptor_get_config_defaults(&descriptor_config); DMAC_BTCTRL_BLOCKACT_NOACT |
descriptor_config.beat_size = DMA_BEAT_SIZE_WORD; DMAC_BTCTRL_EVOSEL_BLOCK |
descriptor_config.step_selection = DMA_STEPSEL_SRC; DMAC_BTCTRL_DSTINC |
descriptor_config.source_address = (uint32_t)&I2S->DATA[self->serializer]; DMAC_BTCTRL_BEATSIZE_WORD;
descriptor_config.src_increment_enable = false;
// Block transfer count is the number of beats per block (aka descriptor). // 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. // In this case there are two bytes per beat so divide the length by two.
uint16_t block_transfer_count = words_per_buffer; uint16_t block_transfer_count = words_per_buffer;
if (length * words_per_sample < words_per_buffer) { if (length * words_per_sample < words_per_buffer) {
block_transfer_count = length * words_per_sample; 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->BTCNT.reg = block_transfer_count;
descriptor_config.event_output_selection = DMA_EVENT_OUTPUT_BLOCK; descriptor->DSTADDR.reg = ((uint32_t) first_buffer + sizeof(uint32_t) * block_transfer_count);
descriptor_config.next_descriptor_address = 0; descriptor->DESCADDR.reg = 0;
if (length * words_per_sample > words_per_buffer) { 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? // 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 so, set up a second buffer chained to be filled after the first buffer.
if (length * words_per_sample > words_per_buffer) { if (length * words_per_sample > words_per_buffer) {
block_transfer_count = 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) { if (length * words_per_sample < 2 * words_per_buffer) {
// Length needed is more than one buffer but less than two. // 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. // 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; 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; second_descriptor->DSTADDR.reg = ((uint32_t) second_buffer + sizeof(uint32_t) * 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->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 // 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. // 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, uint32_t common_hal_audiobusio_pdmin_record_to_buffer(audiobusio_pdmin_obj_t* self,
uint16_t* output_buffer, uint32_t output_buffer_length) { 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. // We allocate two buffers on the stack to use for double buffering.
const uint8_t samples_per_buffer = SAMPLES_PER_BUFFER; const uint8_t samples_per_buffer = SAMPLES_PER_BUFFER;
// For every word we record, we throw away 2 bytes of a phantom second channel. // 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; uint8_t words_per_sample = self->bytes_per_sample / 2;
const uint8_t words_per_buffer = samples_per_buffer * words_per_sample; uint32_t words_per_buffer = samples_per_buffer * words_per_sample;
uint32_t first_buffer[words_per_buffer]; uint32_t first_buffer[words_per_buffer];
uint32_t second_buffer[words_per_buffer]; uint32_t second_buffer[words_per_buffer];
turn_on_event_system();
COMPILER_ALIGNED(16) DmacDescriptor second_descriptor; COMPILER_ALIGNED(16) DmacDescriptor second_descriptor;
setup_dma(self, output_buffer_length, &second_descriptor, words_per_buffer, setup_dma(self, output_buffer_length, dma_descriptor(dma_channel), &second_descriptor,
words_per_sample, first_buffer, second_buffer); 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 // Record
uint32_t buffers_processed = 0; 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; uint32_t remaining_samples_needed = output_buffer_length;
while (values_output < 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 // Wait for the next buffer to fill
uint32_t block_counter; while (!event_interrupt_active(event_channel)) {
while ((block_counter = tc_get_count_value(MP_STATE_VM(audiodma_block_counter))) == buffers_processed) {
#ifdef MICROPY_VM_HOOK_LOOP #ifdef MICROPY_VM_HOOK_LOOP
MICROPY_VM_HOOK_LOOP MICROPY_VM_HOOK_LOOP
#endif #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 // The mic is running all the time, so we don't need to wait the usual 10msec or 100msec
// for it to start up. // for it to start up.
// Flip back and forth between processing the first and second buffers. // Flip back and forth between processing the first and second buffers.
uint32_t *buffer = first_buffer; uint32_t *buffer = first_buffer;
DmacDescriptor* descriptor = audio_dma.descriptor; DmacDescriptor* descriptor = dma_descriptor(dma_channel);
if (buffers_processed % 2 == 1) { if (buffers_processed % 2 == 1) {
buffer = second_buffer; buffer = second_buffer;
descriptor = &second_descriptor; 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; return values_output;
} }

View File

@ -36,11 +36,12 @@ typedef struct {
mp_obj_base_t base; mp_obj_base_t base;
const mcu_pin_obj_t *clock_pin; const mcu_pin_obj_t *clock_pin;
const mcu_pin_obj_t *data_pin; const mcu_pin_obj_t *data_pin;
uint32_t frequency; uint32_t sample_rate;
uint8_t serializer; uint8_t serializer;
uint8_t clock_unit; uint8_t clock_unit;
uint8_t bytes_per_sample; uint8_t bytes_per_sample;
uint8_t bit_depth; uint8_t bit_depth;
uint8_t gclk;
} audiobusio_pdmin_obj_t; } audiobusio_pdmin_obj_t;
void pdmin_reset(void); void pdmin_reset(void);

View File

@ -91,7 +91,7 @@ uint8_t find_async_event_channel(void) {
#define EVSYS_SYNCH_NUM EVSYS_CHANNELS #define EVSYS_SYNCH_NUM EVSYS_CHANNELS
#endif #endif
uint8_t find_sync_event_channel(void) { uint8_t find_sync_event_channel(void) {
int8_t channel; uint8_t channel;
for (channel = 0; channel < EVSYS_SYNCH_NUM; channel++) { for (channel = 0; channel < EVSYS_SYNCH_NUM; channel++) {
if (channel_free(channel)) { if (channel_free(channel)) {
break; 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[channel].CHANNEL.reg = EVSYS_CHANNEL_EVGEN(generator) |
EVSYS_CHANNEL_PATH_SYNCHRONOUS | EVSYS_CHANNEL_PATH_SYNCHRONOUS |
EVSYS_CHANNEL_EDGSEL_RISING_EDGE; 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 #endif
#ifdef SAMD21 #ifdef SAMD21
EVSYS->CHANNEL.reg = EVSYS_CHANNEL_CHANNEL(channel) | 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 // Only clear if we know its active, otherwise there is the possibility it becomes after we
// check but before we clear. // check but before we clear.
if (active) { if (active) {
EVSYS->Channel[channel].CHINTFLAG.reg = EVSYS_CHINTFLAG_EVD; EVSYS->Channel[channel].CHINTFLAG.reg = EVSYS_CHINTFLAG_EVD | EVSYS_CHINTFLAG_OVR;
} }
#endif #endif
return active; 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;
}

View File

@ -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_async_event_channel(uint8_t channel, uint8_t generator);
void init_event_channel_interrupt(uint8_t channel, uint8_t gclk, 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_active(uint8_t channel);
bool event_interrupt_overflow(uint8_t channel);
#endif // MICROPY_INCLUDED_ATMEL_SAMD_EVENTS_H #endif // MICROPY_INCLUDED_ATMEL_SAMD_EVENTS_H

84
ports/atmel-samd/i2s.c Normal file
View 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
View 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

View File

@ -42,18 +42,18 @@
//| //|
//| PDMIn can be used to record an input audio signal on a given set of pins. //| 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 //| 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 //| record audio signals from the given pins. Individual ports may put further
//| restrictions on the recording parameters. The overall frequency is //| restrictions on the recording parameters. The overall sample rate is
//| determined by `frequency` x ``oversample``, and the total must be 1MHz or //| determined by `sample_rate` x ``oversample``, and the total must be 1MHz or
//| higher, so `frequency` must be a minimum of 16000. //| 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 clock_pin: The pin to output the clock to
//| :param ~microcontroller.Pin data_pin: The pin to read the data from //| :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. //| :param int sample_rate: Target sample_rate of the resulting samples. Check `sample_rate` for actual value.
//| Minimum frequency is about 16000 Hz. //| Minimum sample_rate is about 16000 Hz.
//| :param int bit_depth: Final number of bits per sample. Must be divisible by 8 //| :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 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 //| :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 //| # Prep a buffer to record into
//| b = bytearray(200) //| 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)) //| mic.record(b, len(b))
//| //|
//| Record 16-bit unsigned samples to buffer:: //| Record 16-bit unsigned samples to buffer::
@ -83,15 +83,15 @@
//| b = array.array("H") //| b = array.array("H")
//| for i in range(200): //| for i in range(200):
//| b.append(0) //| 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)) //| 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) { 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_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);
static const mp_arg_t allowed_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_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_mono, MP_ARG_KW_ONLY | MP_ARG_BOOL,{.u_bool = true} },
{ MP_QSTR_oversample, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 64} }, { 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); audiobusio_pdmin_obj_t *self = m_new_obj(audiobusio_pdmin_obj_t);
self->base.type = &audiobusio_pdmin_type; 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; uint8_t bit_depth = args[ARG_bit_depth].u_int;
if (bit_depth % 8 != 0) { if (bit_depth % 8 != 0) {
mp_raise_ValueError("Bit depth must be multiple of 8."); 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"); 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); bit_depth, mono, oversample);
// Wait for the microphone to start up. Some start in 10 msecs; some take as much as 100 msecs. // 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); 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 //| The actual sample_rate of the recording. This may not match the constructed
//| frequency due to internal clock limitations. //| 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); audiobusio_pdmin_obj_t *self = MP_OBJ_TO_PTR(self_in);
raise_error_if_deinited(common_hal_audiobusio_pdmin_deinited(self)); 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, .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,
(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___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___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_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); STATIC MP_DEFINE_CONST_DICT(audiobusio_pdmin_locals_dict, audiobusio_pdmin_locals_dict_table);

View File

@ -35,13 +35,13 @@ extern const mp_obj_type_t audiobusio_pdmin_type;
void common_hal_audiobusio_pdmin_construct(audiobusio_pdmin_obj_t* self, 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, 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); void common_hal_audiobusio_pdmin_deinit(audiobusio_pdmin_obj_t* self);
bool common_hal_audiobusio_pdmin_deinited(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, uint32_t common_hal_audiobusio_pdmin_record_to_buffer(audiobusio_pdmin_obj_t* self,
uint16_t* buffer, uint32_t length); uint16_t* buffer, uint32_t length);
uint8_t common_hal_audiobusio_pdmin_get_bit_depth(audiobusio_pdmin_obj_t* self); 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 // TODO(tannewt): Add record to file
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOBUSIO_AUDIOOUT_H #endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOBUSIO_AUDIOOUT_H

View File

@ -62,7 +62,7 @@
STATIC const mp_rom_map_elem_t audiobusio_module_globals_table[] = { 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___name__), MP_ROM_QSTR(MP_QSTR_audiobusio) },
{ MP_ROM_QSTR(MP_QSTR_I2SOut), MP_ROM_PTR(&audiobusio_i2sout_type) }, { 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); STATIC MP_DEFINE_CONST_DICT(audiobusio_module_globals, audiobusio_module_globals_table);