mirror of
https://github.com/pelgraine/Meck.git
synced 2026-08-04 15:52:42 +02:00
T-Echo Card audio support initial stages - codec2 attempts
This commit is contained in:
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
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"
|
||||
#include <hal/nrf_pdm.h>
|
||||
|
||||
#include <Adafruit_TinyUSB.h> // for Serial
|
||||
|
||||
#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)
|
||||
{
|
||||
}
|
||||
|
||||
PDMClass::~PDMClass()
|
||||
{
|
||||
}
|
||||
|
||||
void PDMClass::setPins(int dataPin, int clkPin, int pwrPin)
|
||||
{
|
||||
_dinPin = dataPin;
|
||||
_clkPin = clkPin;
|
||||
_pwrPin = pwrPin;
|
||||
}
|
||||
|
||||
int PDMClass::begin(int channels, long sampleRate)
|
||||
{
|
||||
_channels = channels;
|
||||
|
||||
// Enable high frequency oscillator if not already enabled
|
||||
uint8_t sd_en = 0;
|
||||
sd_softdevice_is_enabled(&sd_en);
|
||||
|
||||
if (sd_en)
|
||||
{
|
||||
uint32_t is_running;
|
||||
sd_clock_hfclk_is_running(&is_running);
|
||||
|
||||
if (!is_running) {
|
||||
sd_clock_hfclk_request();
|
||||
|
||||
while(!is_running) {
|
||||
yield();
|
||||
sd_clock_hfclk_is_running(&is_running);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) {
|
||||
NRF_CLOCK->TASKS_HFCLKSTART = 1;
|
||||
while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) yield();
|
||||
}
|
||||
}
|
||||
|
||||
// configure the sample rate and channels
|
||||
switch (sampleRate) {
|
||||
case 16000:
|
||||
#ifndef NRF52832_XXAA
|
||||
NRF_PDM->RATIO = ((PDM_RATIO_RATIO_Ratio80 << PDM_RATIO_RATIO_Pos) & PDM_RATIO_RATIO_Msk);
|
||||
#endif
|
||||
nrf_pdm_clock_set(NRF_PDM, NRF_PDM_FREQ_1280K);
|
||||
break;
|
||||
case 41667:
|
||||
nrf_pdm_clock_set(NRF_PDM, NRF_PDM_FREQ_2667K);
|
||||
break;
|
||||
default:
|
||||
return 0; // unsupported
|
||||
}
|
||||
|
||||
switch (channels) {
|
||||
case 2:
|
||||
nrf_pdm_mode_set(NRF_PDM, NRF_PDM_MODE_STEREO, NRF_PDM_EDGE_LEFTFALLING);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
nrf_pdm_mode_set(NRF_PDM, NRF_PDM_MODE_MONO, NRF_PDM_EDGE_LEFTFALLING);
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0; // unsupported
|
||||
}
|
||||
|
||||
setGain(DEFAULT_PDM_GAIN);
|
||||
|
||||
// configure the I/O and mux
|
||||
pinMode(_clkPin, OUTPUT);
|
||||
digitalWrite(_clkPin, LOW);
|
||||
|
||||
pinMode(_dinPin, INPUT);
|
||||
|
||||
nrf_pdm_psel_connect(NRF_PDM, digitalPinToPinName(_clkPin), digitalPinToPinName(_dinPin));
|
||||
|
||||
// clear events and enable PDM interrupts
|
||||
nrf_pdm_event_clear(NRF_PDM, NRF_PDM_EVENT_STARTED);
|
||||
nrf_pdm_event_clear(NRF_PDM, NRF_PDM_EVENT_END);
|
||||
nrf_pdm_event_clear(NRF_PDM, NRF_PDM_EVENT_STOPPED);
|
||||
nrf_pdm_int_enable(NRF_PDM, 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);
|
||||
nrf_pdm_event_clear(NRF_PDM, NRF_PDM_EVENT_STARTED);
|
||||
nrf_pdm_task_trigger(NRF_PDM, NRF_PDM_TASK_START);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void PDMClass::end()
|
||||
{
|
||||
// disable PDM and IRQ
|
||||
nrf_pdm_disable(NRF_PDM);
|
||||
|
||||
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(NRF_PDM);
|
||||
|
||||
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)
|
||||
{
|
||||
nrf_pdm_gain_set(NRF_PDM, gain, gain);
|
||||
}
|
||||
|
||||
void PDMClass::setBufferSize(int bufferSize)
|
||||
{
|
||||
_doubleBuffer.setSize(bufferSize);
|
||||
}
|
||||
|
||||
void PDMClass::IrqHandler()
|
||||
{
|
||||
if (nrf_pdm_event_check(NRF_PDM, NRF_PDM_EVENT_STARTED)) {
|
||||
nrf_pdm_event_clear(NRF_PDM, NRF_PDM_EVENT_STARTED);
|
||||
|
||||
if (_doubleBuffer.available() == 0) {
|
||||
// switch to the next buffer
|
||||
nrf_pdm_buffer_set(NRF_PDM, (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(NRF_PDM);
|
||||
}
|
||||
} else if (nrf_pdm_event_check(NRF_PDM, NRF_PDM_EVENT_STOPPED)) {
|
||||
nrf_pdm_event_clear(NRF_PDM, NRF_PDM_EVENT_STOPPED);
|
||||
} else if (nrf_pdm_event_check(NRF_PDM, NRF_PDM_EVENT_END)) {
|
||||
nrf_pdm_event_clear(NRF_PDM, NRF_PDM_EVENT_END);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
void PDM_IRQHandler(void)
|
||||
{
|
||||
PDM.IrqHandler();
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef PIN_PDM_DIN
|
||||
#define PIN_PDM_DIN -1
|
||||
#endif
|
||||
|
||||
#ifndef PIN_PDM_CLK
|
||||
#define PIN_PDM_CLK -1
|
||||
#endif
|
||||
|
||||
#ifndef PIN_PDM_PWR
|
||||
#define PIN_PDM_PWR -1
|
||||
#endif
|
||||
|
||||
PDMClass PDM(PIN_PDM_DIN, PIN_PDM_CLK, PIN_PDM_PWR);
|
||||
Reference in New Issue
Block a user