1/*
  2 * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved.
  3 *
  4 * SPDX-License-Identifier: Apache-2.0
  5 * 
  6 */
  7
  8#include <stdlib.h>
  9#include <string.h>
 10
 11#include "hardware/clocks.h"
 12#include "hardware/dma.h"
 13#include "hardware/irq.h"
 14
 15#include "OpenPDM2PCM/OpenPDMFilter.h"
 16
 17#include "pdm_microphone.pio.h"
 18
 19#include "pico/pdm_microphone.h"
 20
 21#define PDM_DECIMATION       64
 22#define PDM_RAW_BUFFER_COUNT 2
 23
 24static struct {
 25    struct pdm_microphone_config config;
 26    int dma_channel;
 27    uint8_t* raw_buffer[PDM_RAW_BUFFER_COUNT];
 28    volatile int raw_buffer_write_index;
 29    volatile int raw_buffer_read_index;
 30    uint raw_buffer_size;
 31    uint dma_irq;
 32    TPDMFilter_InitStruct filter;
 33    uint16_t filter_volume;
 34    pdm_samples_ready_handler_t samples_ready_handler;
 35} pdm_mic;
 36
 37static void pdm_dma_handler();
 38
 39int pdm_microphone_init(const struct pdm_microphone_config* config) {
 40    memset(&pdm_mic, 0x00, sizeof(pdm_mic));
 41    memcpy(&pdm_mic.config, config, sizeof(pdm_mic.config));
 42
 43    if (config->sample_buffer_size % (config->sample_rate / 1000)) {
 44        return -1;
 45    }
 46
 47    pdm_mic.raw_buffer_size = config->sample_buffer_size * (PDM_DECIMATION / 8);
 48
 49    for (int i = 0; i < PDM_RAW_BUFFER_COUNT; i++) {
 50        pdm_mic.raw_buffer[i] = malloc(pdm_mic.raw_buffer_size);
 51        if (pdm_mic.raw_buffer[i] == NULL) {
 52            pdm_microphone_deinit();
 53
 54            return -1;   
 55        }
 56    }
 57
 58    pdm_mic.dma_channel = dma_claim_unused_channel(true);
 59    if (pdm_mic.dma_channel < 0) {
 60        pdm_microphone_deinit();
 61
 62        return -1;
 63    }
 64
 65    uint pio_sm_offset = pio_add_program(config->pio, &pdm_microphone_data_program);
 66
 67    float clk_div = clock_get_hz(clk_sys) / (config->sample_rate * PDM_DECIMATION * 4.0);
 68
 69    pdm_microphone_data_init(
 70        config->pio,
 71        config->pio_sm,
 72        pio_sm_offset,
 73        clk_div,
 74        config->gpio_data,
 75        config->gpio_clk
 76    );
 77
 78    dma_channel_config dma_channel_cfg = dma_channel_get_default_config(pdm_mic.dma_channel);
 79
 80    channel_config_set_transfer_data_size(&dma_channel_cfg, DMA_SIZE_8);
 81    channel_config_set_read_increment(&dma_channel_cfg, false);
 82    channel_config_set_write_increment(&dma_channel_cfg, true);
 83    channel_config_set_dreq(&dma_channel_cfg, pio_get_dreq(config->pio, config->pio_sm, false));
 84
 85    pdm_mic.dma_irq = DMA_IRQ_0;
 86
 87    dma_channel_configure(
 88        pdm_mic.dma_channel,
 89        &dma_channel_cfg,
 90        pdm_mic.raw_buffer[0],
 91        &config->pio->rxf[config->pio_sm],
 92        pdm_mic.raw_buffer_size,
 93        false
 94    );
 95
 96    pdm_mic.filter.Fs = config->sample_rate;
 97    pdm_mic.filter.LP_HZ = config->sample_rate / 2;
 98    pdm_mic.filter.HP_HZ = 10;
 99    pdm_mic.filter.In_MicChannels = 1;
100    pdm_mic.filter.Out_MicChannels = 1;
101    pdm_mic.filter.Decimation = PDM_DECIMATION;
102    pdm_mic.filter.MaxVolume = 64;
103    pdm_mic.filter.Gain = 16;
104
105    pdm_mic.filter_volume = pdm_mic.filter.MaxVolume;
106}
107
108void pdm_microphone_deinit() {
109    for (int i = 0; i < PDM_RAW_BUFFER_COUNT; i++) {
110        if (pdm_mic.raw_buffer[i]) {
111            free(pdm_mic.raw_buffer[i]);
112
113            pdm_mic.raw_buffer[i] = NULL;
114        }
115    }
116
117    if (pdm_mic.dma_channel > -1) {
118        dma_channel_unclaim(pdm_mic.dma_channel);
119
120        pdm_mic.dma_channel = -1;
121    }
122}
123
124int pdm_microphone_start() {
125    irq_set_enabled(pdm_mic.dma_irq, true);
126    irq_set_exclusive_handler(pdm_mic.dma_irq, pdm_dma_handler);
127
128    if (pdm_mic.dma_irq == DMA_IRQ_0) {
129        dma_channel_set_irq0_enabled(pdm_mic.dma_channel, true);
130    } else if (pdm_mic.dma_irq == DMA_IRQ_1) {
131        dma_channel_set_irq1_enabled(pdm_mic.dma_channel, true);
132    } else {
133        return -1;
134    }
135
136    Open_PDM_Filter_Init(&pdm_mic.filter);
137
138    pio_sm_set_enabled(
139        pdm_mic.config.pio,
140        pdm_mic.config.pio_sm,
141        true
142    );
143
144    pdm_mic.raw_buffer_write_index = 0;
145    pdm_mic.raw_buffer_read_index = 0;
146
147    dma_channel_transfer_to_buffer_now(
148        pdm_mic.dma_channel,
149        pdm_mic.raw_buffer[0],
150        pdm_mic.raw_buffer_size
151    );
152
153    pio_sm_set_enabled(
154        pdm_mic.config.pio,
155        pdm_mic.config.pio_sm,
156        true
157    );
158}
159
160void pdm_microphone_stop() {
161    pio_sm_set_enabled(
162        pdm_mic.config.pio,
163        pdm_mic.config.pio_sm,
164        false
165    );
166
167    dma_channel_abort(pdm_mic.dma_channel);
168
169    if (pdm_mic.dma_irq == DMA_IRQ_0) {
170        dma_channel_set_irq0_enabled(pdm_mic.dma_channel, false);
171    } else if (pdm_mic.dma_irq == DMA_IRQ_1) {
172        dma_channel_set_irq1_enabled(pdm_mic.dma_channel, false);
173    }
174
175    irq_set_enabled(pdm_mic.dma_irq, false);
176}
177
178static void pdm_dma_handler() {
179    // clear IRQ
180    if (pdm_mic.dma_irq == DMA_IRQ_0) {
181        dma_hw->ints0 = (1u << pdm_mic.dma_channel);
182    } else if (pdm_mic.dma_irq == DMA_IRQ_1) {
183        dma_hw->ints1 = (1u << pdm_mic.dma_channel);
184    }
185
186    // get the current buffer index
187    pdm_mic.raw_buffer_read_index = pdm_mic.raw_buffer_write_index;
188
189    // get the next capture index to send the dma to start
190    pdm_mic.raw_buffer_write_index = (pdm_mic.raw_buffer_write_index + 1) % PDM_RAW_BUFFER_COUNT;
191
192    // give the channel a new buffer to write to and re-trigger it
193    dma_channel_transfer_to_buffer_now(
194        pdm_mic.dma_channel,
195        pdm_mic.raw_buffer[pdm_mic.raw_buffer_write_index],
196        pdm_mic.raw_buffer_size
197    );
198
199    if (pdm_mic.samples_ready_handler) {
200        pdm_mic.samples_ready_handler();
201    }
202}
203
204void pdm_microphone_set_samples_ready_handler(pdm_samples_ready_handler_t handler) {
205    pdm_mic.samples_ready_handler = handler;
206}
207
208void pdm_microphone_set_filter_max_volume(uint8_t max_volume) {
209    pdm_mic.filter.MaxVolume = max_volume;
210}
211
212void pdm_microphone_set_filter_gain(uint8_t gain) {
213    pdm_mic.filter.Gain = gain;
214}
215
216void pdm_microphone_set_filter_volume(uint16_t volume) {
217    pdm_mic.filter_volume = volume;
218}
219
220int pdm_microphone_read(int16_t* buffer, size_t samples) {
221    int filter_stride = (pdm_mic.filter.Fs / 1000);
222    samples = (samples / filter_stride) * filter_stride;
223
224    if (samples > pdm_mic.config.sample_buffer_size) {
225        samples = pdm_mic.config.sample_buffer_size;
226    }
227
228    if (pdm_mic.raw_buffer_write_index == pdm_mic.raw_buffer_read_index) {
229        return 0;
230    }
231
232    uint8_t* in = pdm_mic.raw_buffer[pdm_mic.raw_buffer_read_index];
233    int16_t* out = buffer;
234
235    pdm_mic.raw_buffer_read_index++;
236
237    for (int i = 0; i < samples; i += filter_stride) {
238#if PDM_DECIMATION == 64
239        Open_PDM_Filter_64(in, out, pdm_mic.filter_volume, &pdm_mic.filter);
240#elif PDM_DECIMATION == 128
241        Open_PDM_Filter_128(in, out, pdm_mic.filter_volume, &pdm_mic.filter);
242#else
243        #error "Unsupported PDM_DECIMATION value!"
244#endif
245
246        in += filter_stride * (PDM_DECIMATION / 8);
247        out += filter_stride;
248    }
249
250    return samples;
251}