forked from arduino/ArduinoCore-mbed
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPDM.cpp
232 lines (180 loc) · 5.87 KB
/
PDM.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
/*
PDM.cpp - library to interface with nRF52840 PDM peripheral
Part of Arduino - http://www.arduino.cc/
Copyright (c) 2019 Arduino SA
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
#include "PDM.h"
#if defined(ARDUINO_ARCH_NRF52840)
#include <hal/nrf_pdm.h>
#define DEFAULT_PDM_GAIN 20
#define PDM_IRQ_PRIORITY 7
#define NRF_PDM_FREQ_1280K (nrf_pdm_freq_t)(0x0A000000UL) ///< PDM_CLK= 1.280 MHz (32 MHz / 25) => Fs= 20000 Hz
#define NRF_PDM_FREQ_2000K (nrf_pdm_freq_t)(0x10000000UL) ///< PDM_CLK= 2.000 MHz (32 MHz / 16) => Fs= 31250 Hz
#define NRF_PDM_FREQ_2667K (nrf_pdm_freq_t)(0x15000000UL) ///< PDM_CLK= 2.667 MHz (32 MHz / 12) => Fs= 41667 Hz
#define NRF_PDM_FREQ_3200K (nrf_pdm_freq_t)(0x19000000UL) ///< PDM_CLK= 3.200 MHz (32 MHz / 10) => Fs= 50000 Hz
#define NRF_PDM_FREQ_4000K (nrf_pdm_freq_t)(0x20000000UL) ///< PDM_CLK= 4.000 MHz (32 MHz / 8) => Fs= 62500 Hz
PDMClass::PDMClass(int dinPin, int clkPin, int pwrPin) :
_dinPin(dinPin),
_clkPin(clkPin),
_pwrPin(pwrPin),
_onReceive(NULL),
_gain(-1),
_channels(-1),
_samplerate(-1),
_init(-1),
_cutSamples(0)
{
}
PDMClass::~PDMClass()
{
}
int PDMClass::begin(int channels, int sampleRate)
{
_channels = channels;
_samplerate = sampleRate;
// Enable high frequency oscillator if not already enabled
if (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) {
NRF_CLOCK->TASKS_HFCLKSTART = 1;
while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) { }
}
// configure the sample rate and channels
switch (sampleRate) {
case 16000:
NRF_PDM->RATIO = ((PDM_RATIO_RATIO_Ratio80 << PDM_RATIO_RATIO_Pos) & PDM_RATIO_RATIO_Msk);
nrf_pdm_clock_set(NRF_PDM_FREQ_1280K);
break;
case 41667:
nrf_pdm_clock_set(NRF_PDM_FREQ_2667K);
break;
default:
return 0; // unsupported
}
switch (channels) {
case 2:
nrf_pdm_mode_set(NRF_PDM_MODE_STEREO, NRF_PDM_EDGE_LEFTFALLING);
break;
case 1:
nrf_pdm_mode_set(NRF_PDM_MODE_MONO, NRF_PDM_EDGE_LEFTFALLING);
break;
default:
return 0; // unsupported
}
if(_gain == -1) {
_gain = DEFAULT_PDM_GAIN;
}
nrf_pdm_gain_set(_gain, _gain);
// configure the I/O and mux
pinMode(_clkPin, OUTPUT);
digitalWrite(_clkPin, LOW);
pinMode(_dinPin, INPUT);
nrf_pdm_psel_connect(digitalPinToPinName(_clkPin), digitalPinToPinName(_dinPin));
// clear events and enable PDM interrupts
nrf_pdm_event_clear(NRF_PDM_EVENT_STARTED);
nrf_pdm_event_clear(NRF_PDM_EVENT_END);
nrf_pdm_event_clear(NRF_PDM_EVENT_STOPPED);
nrf_pdm_int_enable(NRF_PDM_INT_STARTED | NRF_PDM_INT_STOPPED);
if (_pwrPin > -1) {
// power the mic on
pinMode(_pwrPin, OUTPUT);
digitalWrite(_pwrPin, HIGH);
}
// clear the buffer
_doubleBuffer.reset();
// set the PDM IRQ priority and enable
NVIC_SetPriority(PDM_IRQn, PDM_IRQ_PRIORITY);
NVIC_ClearPendingIRQ(PDM_IRQn);
NVIC_EnableIRQ(PDM_IRQn);
// set the buffer for transfer
// nrf_pdm_buffer_set((uint32_t*)_doubleBuffer.data(), _doubleBuffer.availableForWrite() / (sizeof(int16_t) * _channels));
// _doubleBuffer.swap();
// enable and trigger start task
nrf_pdm_enable();
nrf_pdm_event_clear(NRF_PDM_EVENT_STARTED);
nrf_pdm_task_trigger(NRF_PDM_TASK_START);
return 1;
}
void PDMClass::end()
{
// disable PDM and IRQ
nrf_pdm_disable();
NVIC_DisableIRQ(PDM_IRQn);
if (_pwrPin > -1) {
// power the mic off
digitalWrite(_pwrPin, LOW);
pinMode(_pwrPin, INPUT);
}
// Don't disable high frequency oscillator since it could be in use by RADIO
// unconfigure the I/O and un-mux
nrf_pdm_psel_disconnect();
pinMode(_clkPin, INPUT);
}
int PDMClass::available()
{
NVIC_DisableIRQ(PDM_IRQn);
size_t avail = _doubleBuffer.available();
NVIC_EnableIRQ(PDM_IRQn);
return avail;
}
int PDMClass::read(void* buffer, size_t size)
{
NVIC_DisableIRQ(PDM_IRQn);
int read = _doubleBuffer.read(buffer, size);
NVIC_EnableIRQ(PDM_IRQn);
return read;
}
void PDMClass::onReceive(void(*function)(void))
{
_onReceive = function;
}
void PDMClass::setGain(int gain)
{
_gain = gain;
nrf_pdm_gain_set(_gain, _gain);
}
void PDMClass::setBufferSize(int bufferSize)
{
_doubleBuffer.setSize(bufferSize);
}
void PDMClass::IrqHandler(bool halftranfer)
{
if (nrf_pdm_event_check(NRF_PDM_EVENT_STARTED)) {
nrf_pdm_event_clear(NRF_PDM_EVENT_STARTED);
if (_doubleBuffer.available() == 0) {
// switch to the next buffer
nrf_pdm_buffer_set((uint32_t*)_doubleBuffer.data(), _doubleBuffer.availableForWrite() / (sizeof(int16_t) * _channels));
// make the current one available for reading
_doubleBuffer.swap(_doubleBuffer.availableForWrite());
// call receive callback if provided
if (_onReceive) {
_onReceive();
}
} else {
// buffer overflow, stop
nrf_pdm_disable();
}
} else if (nrf_pdm_event_check(NRF_PDM_EVENT_STOPPED)) {
nrf_pdm_event_clear(NRF_PDM_EVENT_STOPPED);
} else if (nrf_pdm_event_check(NRF_PDM_EVENT_END)) {
nrf_pdm_event_clear(NRF_PDM_EVENT_END);
}
}
extern "C" {
__attribute__((__used__)) void PDM_IRQHandler_v(void)
{
PDM.IrqHandler(true);
}
}
PDMClass PDM(PIN_PDM_DIN, PIN_PDM_CLK, PIN_PDM_PWR);
#endif