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}