T-Echo Card audio support initial stages - codec2 attempts

This commit is contained in:
pelgraine
2026-05-07 05:47:18 +10:00
parent 4b734f1bac
commit 708b96e0e8
109 changed files with 87144 additions and 24 deletions
+261
View File
@@ -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);
+65
View File
@@ -0,0 +1,65 @@
/*
Copyright (c) 2019 Arduino LLC. All right reserved.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _PDM_H_INCLUDED
#define _PDM_H_INCLUDED
#if !defined(ARDUINO_NRF52_ADAFRUIT)
#error "This library targets only Adafruit NRF52840 boards"
#endif
#include <Arduino.h>
#include <Adafruit_TinyUSB.h> // for Serial
#include "utility/PDMDoubleBuffer.h"
class PDMClass
{
public:
PDMClass(int dataPin, int clkPin, int pwrPin);
virtual ~PDMClass();
void setPins(int dataPin, int clkPin, int pwrPin);
int begin(int channels, long sampleRate);
void end();
virtual int available();
virtual int read(void* buffer, size_t size);
void onReceive(void(*)(void));
void setGain(int gain);
void setBufferSize(int bufferSize);
// private:
void IrqHandler();
private:
int _dinPin;
int _clkPin;
int _pwrPin;
int _channels;
PDMDoubleBuffer _doubleBuffer;
void (*_onReceive)(void);
};
extern PDMClass PDM;
#endif
@@ -0,0 +1,133 @@
/*
Copyright (c) 2016 Arduino LLC. All right reserved.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdlib.h>
#include <string.h>
#include "PDMDoubleBuffer.h"
PDMDoubleBuffer::PDMDoubleBuffer() :
_size(DEFAULT_PDM_BUFFER_SIZE)
{
reset();
}
PDMDoubleBuffer::~PDMDoubleBuffer()
{
}
void PDMDoubleBuffer::setSize(int size)
{
_size = size;
}
void PDMDoubleBuffer::reset()
{
_buffer[0] = (uint8_t*)realloc(_buffer[0], _size);
_buffer[1] = (uint8_t*)realloc(_buffer[1], _size);
memset(_buffer[0], 0x00, _size);
memset(_buffer[1], 0x00, _size);
_index = 0;
_length[0] = 0;
_length[1] = 0;
_readOffset[0] = 0;
_readOffset[1] = 0;
}
size_t PDMDoubleBuffer::availableForWrite()
{
return (_size - (_length[_index] - _readOffset[_index]));
}
size_t PDMDoubleBuffer::write(const void *buffer, size_t size)
{
size_t space = availableForWrite();
if (size > space) {
size = space;
}
if (size == 0) {
return 0;
}
memcpy(&_buffer[_index][_length[_index]], buffer, size);
_length[_index] += size;
return size;
}
size_t PDMDoubleBuffer::read(void *buffer, size_t size)
{
size_t avail = available();
if (size > avail) {
size = avail;
}
if (size == 0) {
return 0;
}
memcpy(buffer, &_buffer[_index][_readOffset[_index]], size);
_readOffset[_index] += size;
return size;
}
size_t PDMDoubleBuffer::peek(void *buffer, size_t size)
{
size_t avail = available();
if (size > avail) {
size = avail;
}
if (size == 0) {
return 0;
}
memcpy(buffer, &_buffer[_index][_readOffset[_index]], size);
return size;
}
void* PDMDoubleBuffer::data()
{
return (void*)_buffer[_index];
}
size_t PDMDoubleBuffer::available()
{
return _length[_index] - _readOffset[_index];
}
void PDMDoubleBuffer::swap(int length)
{
if (_index == 0) {
_index = 1;
} else {
_index = 0;
}
_length[_index] = length;
_readOffset[_index] = 0;
}
@@ -0,0 +1,53 @@
/*
Copyright (c) 2016 Arduino LLC. All right reserved.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _PDM_DOUBLE_BUFFER_H_INCLUDED
#define _PDM_DOUBLE_BUFFER_H_INCLUDED
#include <stddef.h>
#include <stdint.h>
#define DEFAULT_PDM_BUFFER_SIZE 512
class PDMDoubleBuffer
{
public:
PDMDoubleBuffer();
virtual ~PDMDoubleBuffer();
void setSize(int size);
void reset();
size_t availableForWrite();
size_t write(const void *buffer, size_t size);
size_t read(void *buffer, size_t size);
size_t peek(void *buffer, size_t size);
void* data();
size_t available();
void swap(int length = 0);
private:
uint8_t* _buffer[2];
int _size;
volatile int _length[2];
volatile int _readOffset[2];
volatile int _index;
};
#endif