From 3b626bc53792ceec36b45e7dbca649ce836e6183 Mon Sep 17 00:00:00 2001 From: Sandeep Mistry Date: Thu, 13 May 2021 11:48:57 -0400 Subject: [PATCH] Updated API --- examples/hello_pdm/main.c | 37 +++-- examples/usb_microphone/main.c | 19 +-- src/include/pico/microphone/pdm.h | 20 ++- src/pdm-microphone.c | 261 ++++++++++++++++++++++-------- src/pdm-microphone.pio | 1 - 5 files changed, 245 insertions(+), 93 deletions(-) diff --git a/examples/hello_pdm/main.c b/examples/hello_pdm/main.c index b701de2..c3b2cf1 100644 --- a/examples/hello_pdm/main.c +++ b/examples/hello_pdm/main.c @@ -13,21 +13,20 @@ #include "tusb.h" struct pdm_microphone_config config = { + .gpio_clk = 2, + .gpio_data = 3, .pio = pio0, .pio_sm = 0, .sample_rate = 8000, - .gpio_clk = 2, - .gpio_data = 3, + .sample_buffer_size = 256, }; int16_t sample_buffer[256]; volatile int samples_read = 0; -void on_pdm_data(int16_t* samples, uint num_samples) +void on_pdm_samples_ready() { - memcpy(sample_buffer, samples, num_samples * sizeof(samples[0])); - - samples_read = num_samples; + samples_read = pdm_microphone_read(sample_buffer, 256); } int main( void ) @@ -38,21 +37,31 @@ int main( void ) tight_loop_contents(); } - // printf("hello PDM microphone\n"); - - pdm_microphone_init(&config); - pdm_microphone_start(on_pdm_data); + printf("hello PDM microphone\n"); - while (1) { - while (samples_read == 0) { + if (pdm_microphone_init(&config) < 0) { + printf("PDM microphone initialization failed!\n"); + while (1) { tight_loop_contents(); } + } - for (int i = 0; i < samples_read; i++) { - printf("%d\n", sample_buffer[i]); + pdm_microphone_set_samples_ready_handler(on_pdm_samples_ready); + + if (pdm_microphone_start() < 0) { + printf("PDM microphone start failed!\n"); + while (1) { + tight_loop_contents(); } + } + while (1) { + int sample_count = samples_read; samples_read = 0; + + for (int i = 0; i < sample_count; i++) { + printf("%d\n", sample_buffer[i]); + } } return 0; diff --git a/examples/usb_microphone/main.c b/examples/usb_microphone/main.c index c6045d1..a5c7e93 100644 --- a/examples/usb_microphone/main.c +++ b/examples/usb_microphone/main.c @@ -67,19 +67,19 @@ void led_blinking_task(void); void audio_task(void); struct pdm_microphone_config config = { - .pio = pio0, - .pio_sm = 0, - .sample_rate = 16000, - .gpio_clk = 2, - .gpio_data = 3, + .gpio_clk = 2, + .gpio_data = 3, + .pio = pio0, + .pio_sm = 0, + .sample_rate = 16000, + .sample_buffer_size = CFG_TUD_AUDIO_TX_FIFO_SIZE/2, }; -void on_pdm_data(int16_t* samples, uint num_samples) +void on_pdm_samples_ready() { - memcpy(test_buffer_audio, samples, num_samples * sizeof(samples[0])); + pdm_microphone_read(test_buffer_audio, CFG_TUD_AUDIO_TX_FIFO_SIZE/2); } - /*------------- MAIN -------------*/ int main(void) { @@ -97,7 +97,8 @@ int main(void) sampleFreqRng.subrange[0].bRes = 0; pdm_microphone_init(&config); - pdm_microphone_start(on_pdm_data); + pdm_microphone_set_samples_ready_handler(on_pdm_samples_ready); + pdm_microphone_start(); while (1) { diff --git a/src/include/pico/microphone/pdm.h b/src/include/pico/microphone/pdm.h index 575b350..3145793 100644 --- a/src/include/pico/microphone/pdm.h +++ b/src/include/pico/microphone/pdm.h @@ -10,18 +10,28 @@ #include "hardware/pio.h" -typedef void (*pdm_data_handler_t)(int16_t*, uint); +typedef void (*pdm_samples_ready_handler_t)(void); struct pdm_microphone_config { + uint gpio_clk; + uint gpio_data; PIO pio; uint pio_sm; uint sample_rate; - uint gpio_clk; - uint gpio_data; - // TODO: pass in buffer??? + uint sample_buffer_size; }; int pdm_microphone_init(const struct pdm_microphone_config* config); -int pdm_microphone_start(pdm_data_handler_t handler); +void pdm_microphone_deinit(); + +int pdm_microphone_start(); +void pdm_microphone_stop(); + +void pdm_microphone_set_samples_ready_handler(pdm_samples_ready_handler_t handler); +void pdm_microphone_set_filter_max_volume(uint8_t max_volume); +void pdm_microphone_set_filter_gain(uint8_t gain); +void pdm_microphone_set_filter_volume(uint16_t volume); + +int pdm_microphone_read(int16_t* buffer, size_t samples); #endif diff --git a/src/pdm-microphone.c b/src/pdm-microphone.c index 334a1c5..4227326 100644 --- a/src/pdm-microphone.c +++ b/src/pdm-microphone.c @@ -5,6 +5,9 @@ * */ +#include +#include + #include "hardware/clocks.h" #include "hardware/dma.h" #include "hardware/irq.h" @@ -15,54 +18,50 @@ #include "pico/microphone/pdm.h" -#define PDM_DECIMATION 64 -#define PCM_BUFFER_SIZE 16 - -static int dma_channel = -1; -static uint8_t pdm_buffer[2][PCM_BUFFER_SIZE * (PDM_DECIMATION / 8)]; -static volatile int pdm_buffer_index = 0; -static int16_t pcm_buffer[PCM_BUFFER_SIZE]; - -static TPDMFilter_InitStruct pdm_filter; -static pdm_data_handler_t pdm_data_handler = NULL; +#define PDM_DECIMATION 64 +#define PDM_RAW_BUFFER_COUNT 2 + +static struct { + struct pdm_microphone_config config; + int dma_channel; + uint8_t* raw_buffer[PDM_RAW_BUFFER_COUNT]; + volatile int raw_buffer_write_index; + volatile int raw_buffer_read_index; + uint raw_buffer_size; + uint dma_irq; + TPDMFilter_InitStruct filter; + uint16_t filter_volume; + pdm_samples_ready_handler_t samples_ready_handler; +} pdm_mic; + +static void pdm_dma_handler(); + +int pdm_microphone_init(const struct pdm_microphone_config* config) { + memset(&pdm_mic, 0x00, sizeof(pdm_mic)); + memcpy(&pdm_mic.config, config, sizeof(pdm_mic.config)); + + if (config->sample_buffer_size % (config->sample_rate / 1000)) { + return -1; + } -static void dma_handler() -{ - // clear IRQ - dma_hw->ints0 = 1u << dma_channel; - - // get the current buffer index - int read_index = pdm_buffer_index; - - // get the next capture index to send the dma to start - pdm_buffer_index = (pdm_buffer_index + 1) % 2; - - // give the channel a new buffer to write to and re-trigger it - dma_channel_transfer_to_buffer_now(dma_channel, pdm_buffer[pdm_buffer_index], sizeof(pdm_buffer[0])); + pdm_mic.raw_buffer_size = config->sample_buffer_size * (PDM_DECIMATION / 8); - uint8_t* in = pdm_buffer[read_index]; - int16_t* out = pcm_buffer; + for (int i = 0; i < PDM_RAW_BUFFER_COUNT; i++) { + pdm_mic.raw_buffer[i] = malloc(pdm_mic.raw_buffer_size); + if (pdm_mic.raw_buffer[i] == NULL) { + pdm_microphone_deinit(); - int filter_stride = (pdm_filter.Fs / 1000); + return -1; + } + } - for (int i = 0; i < (PCM_BUFFER_SIZE / filter_stride); i++) { -#if PDM_DECIMATION == 64 - Open_PDM_Filter_64(in, out, 16, &pdm_filter); -#elif PDM_DECIMATION == 128 - Open_PDM_Filter_128(in, out, 16, &pdm_filter); -#else - #error "Unsupported PDM_DECIMATION value!" -#endif + pdm_mic.dma_channel = dma_claim_unused_channel(true); + if (pdm_mic.dma_channel < 0) { + pdm_microphone_deinit(); - in += filter_stride * (PDM_DECIMATION / 8); - out += filter_stride; + return -1; } - pdm_data_handler(pcm_buffer, PCM_BUFFER_SIZE); -} - -int pdm_microphone_init(const struct pdm_microphone_config* config) -{ uint pio_sm_offset = pio_add_program(config->pio, &pdm_microphone_data_program); float clk_div = clock_get_hz(clk_sys) / (config->sample_rate * PDM_DECIMATION * 2); @@ -76,43 +75,177 @@ int pdm_microphone_init(const struct pdm_microphone_config* config) config->gpio_data ); - dma_channel = dma_claim_unused_channel(true); - - // TODO: handle claim failure - - dma_channel_config dma_channel_cfg = dma_channel_get_default_config(dma_channel); + dma_channel_config dma_channel_cfg = dma_channel_get_default_config(pdm_mic.dma_channel); channel_config_set_transfer_data_size(&dma_channel_cfg, DMA_SIZE_8); channel_config_set_read_increment(&dma_channel_cfg, false); channel_config_set_write_increment(&dma_channel_cfg, true); channel_config_set_dreq(&dma_channel_cfg, pio_get_dreq(config->pio, config->pio_sm, false)); - irq_set_enabled(DMA_IRQ_0, true); - irq_set_exclusive_handler(DMA_IRQ_0, dma_handler); + pdm_mic.dma_irq = DMA_IRQ_0; + + dma_channel_configure( + pdm_mic.dma_channel, + &dma_channel_cfg, + pdm_mic.raw_buffer[0], + &config->pio->rxf[config->pio_sm], + pdm_mic.raw_buffer_size, + false + ); + + pdm_mic.filter.Fs = config->sample_rate; + pdm_mic.filter.LP_HZ = config->sample_rate / 2; + pdm_mic.filter.HP_HZ = 10; + pdm_mic.filter.In_MicChannels = 1; + pdm_mic.filter.Out_MicChannels = 1; + pdm_mic.filter.Decimation = PDM_DECIMATION; + pdm_mic.filter.MaxVolume = 64; + pdm_mic.filter.Gain = 16; + + pdm_mic.filter_volume = pdm_mic.filter.MaxVolume; +} + +void pdm_microphone_deinit() { + for (int i = 0; i < PDM_RAW_BUFFER_COUNT; i++) { + if (pdm_mic.raw_buffer[i]) { + free(pdm_mic.raw_buffer[i]); + + pdm_mic.raw_buffer[i] = NULL; + } + } + + if (pdm_mic.dma_channel > -1) { + dma_channel_unclaim(pdm_mic.dma_channel); + + pdm_mic.dma_channel = -1; + } +} + +int pdm_microphone_start() { + irq_set_enabled(pdm_mic.dma_irq, true); + irq_set_exclusive_handler(pdm_mic.dma_irq, pdm_dma_handler); + + if (pdm_mic.dma_irq == DMA_IRQ_0) { + dma_channel_set_irq0_enabled(pdm_mic.dma_channel, true); + } else if (pdm_mic.dma_irq == DMA_IRQ_1) { + dma_channel_set_irq1_enabled(pdm_mic.dma_channel, true); + } else { + return -1; + } + + Open_PDM_Filter_Init(&pdm_mic.filter); + + pio_sm_set_enabled( + pdm_mic.config.pio, + pdm_mic.config.pio_sm, + true + ); + + pdm_mic.raw_buffer_write_index = 0; + pdm_mic.raw_buffer_read_index = 0; + + dma_channel_transfer_to_buffer_now( + pdm_mic.dma_channel, + pdm_mic.raw_buffer[0], + pdm_mic.raw_buffer_size + ); - dma_channel_set_irq0_enabled(dma_channel, true); + pio_sm_set_enabled( + pdm_mic.config.pio, + pdm_mic.config.pio_sm, + true + ); +} - dma_channel_configure(dma_channel, &dma_channel_cfg, NULL, &config->pio->rxf[config->pio_sm], sizeof(pdm_buffer[pdm_buffer_index]), false); +void pdm_microphone_stop() { + pio_sm_set_enabled( + pdm_mic.config.pio, + pdm_mic.config.pio_sm, + false + ); - pdm_filter.Fs = config->sample_rate; - pdm_filter.LP_HZ = config->sample_rate / 2; - pdm_filter.HP_HZ = 10; - pdm_filter.In_MicChannels = 1; - pdm_filter.Out_MicChannels = 1; - pdm_filter.Decimation = PDM_DECIMATION; - pdm_filter.MaxVolume = 0; + dma_channel_abort(pdm_mic.dma_channel); - Open_PDM_Filter_Init(&pdm_filter); + if (pdm_mic.dma_irq == DMA_IRQ_0) { + dma_channel_set_irq0_enabled(pdm_mic.dma_channel, false); + } else if (pdm_mic.dma_irq == DMA_IRQ_1) { + dma_channel_set_irq1_enabled(pdm_mic.dma_channel, false); + } - return -1; + irq_set_enabled(pdm_mic.dma_irq, false); } -int pdm_microphone_start(pdm_data_handler_t handler) -{ - pdm_data_handler = handler; +static void pdm_dma_handler() { + // clear IRQ + if (pdm_mic.dma_irq == DMA_IRQ_0) { + dma_hw->ints0 = (1u << pdm_mic.dma_channel); + } else if (pdm_mic.dma_irq == DMA_IRQ_1) { + dma_hw->ints1 = (1u << pdm_mic.dma_channel); + } + + // get the current buffer index + pdm_mic.raw_buffer_read_index = pdm_mic.raw_buffer_write_index; + + // get the next capture index to send the dma to start + pdm_mic.raw_buffer_write_index = (pdm_mic.raw_buffer_write_index + 1) % PDM_RAW_BUFFER_COUNT; // give the channel a new buffer to write to and re-trigger it - dma_channel_transfer_to_buffer_now(dma_channel, pdm_buffer[pdm_buffer_index], sizeof(pdm_buffer[0])); + dma_channel_transfer_to_buffer_now( + pdm_mic.dma_channel, + pdm_mic.raw_buffer[pdm_mic.raw_buffer_write_index], + pdm_mic.raw_buffer_size + ); + + if (pdm_mic.samples_ready_handler) { + pdm_mic.samples_ready_handler(); + } +} + +void pdm_microphone_set_samples_ready_handler(pdm_samples_ready_handler_t handler) { + pdm_mic.samples_ready_handler = handler; +} + +void pdm_microphone_set_filter_max_volume(uint8_t max_volume) { + pdm_mic.filter.MaxVolume = max_volume; +} + +void pdm_microphone_set_filter_gain(uint8_t gain) { + pdm_mic.filter.Gain = gain; +} + +void pdm_microphone_set_filter_volume(uint16_t volume) { + pdm_mic.filter_volume = volume; +} + +int pdm_microphone_read(int16_t* buffer, size_t samples) { + int filter_stride = (pdm_mic.filter.Fs / 1000); + samples = (samples / filter_stride) * filter_stride; + + if (samples > pdm_mic.config.sample_buffer_size) { + samples = pdm_mic.config.sample_buffer_size; + } + + if (pdm_mic.raw_buffer_write_index == pdm_mic.raw_buffer_read_index) { + return 0; + } + + uint8_t* in = pdm_mic.raw_buffer[pdm_mic.raw_buffer_read_index]; + int16_t* out = buffer; + + pdm_mic.raw_buffer_read_index++; + + for (int i = 0; i < samples; i += filter_stride) { +#if PDM_DECIMATION == 64 + Open_PDM_Filter_64(in, out, pdm_mic.filter_volume, &pdm_mic.filter); +#elif PDM_DECIMATION == 128 + Open_PDM_Filter_128(in, out, pdm_mic.filter_volume, &pdm_mic.filter); +#else + #error "Unsupported PDM_DECIMATION value!" +#endif + + in += filter_stride * (PDM_DECIMATION / 8); + out += filter_stride; + } - return -1; + return samples; } diff --git a/src/pdm-microphone.pio b/src/pdm-microphone.pio index 79a14fd..063cb7a 100644 --- a/src/pdm-microphone.pio +++ b/src/pdm-microphone.pio @@ -32,6 +32,5 @@ static inline void pdm_microphone_data_init(PIO pio, uint sm, uint offset, float sm_config_set_clkdiv(&c, clk_div); pio_sm_init(pio, sm, offset, &c); - pio_sm_set_enabled(pio, sm, true); } %}