From 3f7b94de5b396b5223942a6ca1d0e46eedd1de84 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Tue, 2 Apr 2024 06:50:42 -0600 Subject: [PATCH] Support for non-MBED cores ADC and PWM working now on non-mbed cores --- src/AutoAnalogAudio.h | 30 +++- src/NRF52840/AutoAnalogAudio.cpp | 259 ++++++++++++++++++++++++++----- 2 files changed, 238 insertions(+), 51 deletions(-) diff --git a/src/AutoAnalogAudio.h b/src/AutoAnalogAudio.h index b0fb8e8..d9e66e5 100644 --- a/src/AutoAnalogAudio.h +++ b/src/AutoAnalogAudio.h @@ -100,10 +100,12 @@ class AutoAnalog * 10 or 12-bit samples are read directly from this buffer after calling getADC()
* @see adcBitsPerSample */ - #if !defined(ARDUINO_ARCH_NRF52840) && !defined (ARDUINO_ARCH_NRF52) || defined ARDUINO_NRF52840_FEATHER + #if !defined (ARDUINO_ARCH_NRF52840) && !defined (ARDUINO_ARCH_NRF52) && !defined ARDUINO_NRF52840_FEATHER uint16_t adcBuffer16[MAX_BUFFER_SIZE]; + #elif defined __MBED__ + inline static uint16_t adcBuffer16[MAX_BUFFER_SIZE]; #else - inline static int16_t adcBuffer16[MAX_BUFFER_SIZE]; + static uint16_t adcBuffer16[MAX_BUFFER_SIZE]; #endif /** Set sample rate. 0 enables the default rate specified in AutoAnalog_config.h @@ -189,7 +191,7 @@ class AutoAnalog /**@}*/ -#if defined (ARDUINO_ARCH_NRF52840) || defined (ARDUINO_ARCH_NRF52) && !defined ARDUINO_NRF52840_FEATHER +#if defined (ARDUINO_ARCH_NRF52840) || defined (ARDUINO_ARCH_NRF52) && !defined ARDUINO_NRF52840_FEATHER && defined __MBED__ inline static uint8_t aCtr; inline static uint32_t aSize; inline static uint16_t *buf0 = NULL; @@ -198,19 +200,31 @@ class AutoAnalog inline static void adcCallback(uint16_t *buf, uint32_t buf_len); inline static void set_callback(void(*function)(uint16_t *buf, uint32_t buf_len)); inline static bool adcReady; - inline static uint16_t dacBuf0[MAX_BUFFER_SIZE]; - inline static uint16_t dacBuf1[MAX_BUFFER_SIZE]; + inline static int16_t dacBuf0[MAX_BUFFER_SIZE]; + inline static int16_t dacBuf1[MAX_BUFFER_SIZE]; bool micOn; int pwrPin; int dinPin; int clkPin; int8_t gain; inline static uint32_t sampleCounter; -#endif - -#if defined ARDUINO_NRF52840_FEATHER +#elif defined (ARDUINO_ARCH_NRF52840) || defined (ARDUINO_ARCH_NRF52) || defined (ARDUINO_NRF52840_FEATHER) && !defined __MBED__ uint16_t dacBuf0[MAX_BUFFER_SIZE]; uint16_t dacBuf1[MAX_BUFFER_SIZE]; + static uint8_t aCtr; + static uint32_t aSize; + static uint16_t *buf0; + static uint16_t *buf1; + static void (*_onReceive)(uint16_t *buf, uint32_t buf_len); + static void adcCallback(uint16_t *buf, uint32_t buf_len); + void set_callback(void(*function)(uint16_t *buf, uint32_t buf_len)); + static bool adcReady; + bool micOn; + int pwrPin; + int dinPin; + int clkPin; + int8_t gain; + uint32_t sampleCounter; #endif private: diff --git a/src/NRF52840/AutoAnalogAudio.cpp b/src/NRF52840/AutoAnalogAudio.cpp index eb33c63..b06cb2b 100644 --- a/src/NRF52840/AutoAnalogAudio.cpp +++ b/src/NRF52840/AutoAnalogAudio.cpp @@ -24,15 +24,30 @@ #include #include +//#define USE_I2s + +#if !defined __MBED__ + #define myPDM NRF_PDM0 + uint16_t AutoAnalog::adcBuffer16[MAX_BUFFER_SIZE]; + bool AutoAnalog::adcReady; + uint32_t AutoAnalog::aSize; + uint8_t AutoAnalog::aCtr; + uint16_t *AutoAnalog::buf0; + uint16_t *AutoAnalog::buf1; + void (*AutoAnalog::_onReceive)(uint16_t *buf, uint32_t buf_len); +#endif + #if !defined ARDUINO_NRF52840_FEATHER #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 + #ifndef NRF_PDM_FREQ_1280K + #define NRF_PDM_FREQ_1280K (nrf_pdm_freq_t)(0x0A000000UL) ///< PDM_CLK= 1.280 MHz (32 MHz / 25) => Fs= 20000 Hz + #endif #define DEFAULT_PDM_GAIN 40 #endif - #define PIN_MCK 12//(13) + #define PIN_MCK 15//(13) #define PIN_SCK 13//(14) #define PIN_LRCK 14//(15) - #define PIN_SDOUT (28) + #define PIN_SDOUT (5) #define DEFAULT_PWM_PIN 5 /****************************************************************************/ /* Public Functions */ @@ -46,29 +61,29 @@ //int16_t sine_table[] = { 0, 0, 23170, 23170, 32767, 32767, 23170, 23170, 0, 0, -23170, -23170, -32768, -32768, -23170, -23170}; AutoAnalog::AutoAnalog(){ -#if !defined ARDUINO_NRF52840_FEATHER + adcReady = true; adcBitsPerSample = 8; -#endif + dacBitsPerSample = 8; autoAdjust = true; for(int i=0; iEVENTS_HFCLKSTARTED == 0) { + NRF_CLOCK->TASKS_HFCLKSTART = 1; + while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) { } + } + //Set default 16khz sample rate + NRF_PDM->RATIO = ((PDM_RATIO_RATIO_Ratio80 << PDM_RATIO_RATIO_Pos) & PDM_RATIO_RATIO_Msk); + nrf_pdm_clock_set(myPDM,NRF_PDM_FREQ_1280K); +//nrf_pdm_clock_set(NRF_PDM_FREQ_1032K); + //nrf_pdm_clock_set(NRF_PDM_FREQ_1067K); + // NRF_PDM_FREQ_1000K = PDM_PDMCLKCTRL_FREQ_1000K, ///< PDM_CLK = 1.000 MHz. + // NRF_PDM_FREQ_1032K = PDM_PDMCLKCTRL_FREQ_Default, ///< PDM_CLK = 1.032 MHz. + // NRF_PDM_FREQ_1067K = PDM_PDMCLKCTRL_FREQ_1067K ///< PDM_CLK = 1.067 MHz. + + //Set default channel mono + nrf_pdm_mode_set(myPDM,NRF_PDM_MODE_MONO, NRF_PDM_EDGE_LEFTFALLING); + if(gain == -1) { + gain = DEFAULT_PDM_GAIN; + } + nrf_pdm_gain_set(myPDM,gain, gain); + + pinMode(clkPin, OUTPUT); + digitalWrite(clkPin, LOW); + + pinMode(dinPin, INPUT); + + nrf_pdm_psel_connect(myPDM,digitalPinToPinName(clkPin), digitalPinToPinName(dinPin)); + + //Enable PDM interrupts and clear events + nrf_pdm_event_clear(myPDM,NRF_PDM_EVENT_STARTED); + nrf_pdm_event_clear(myPDM,NRF_PDM_EVENT_END); + nrf_pdm_event_clear(myPDM,NRF_PDM_EVENT_STOPPED); + nrf_pdm_int_enable(myPDM,NRF_PDM_INT_STARTED | NRF_PDM_INT_STOPPED); + + //Turn on the mic + if (pwrPin > -1) { + pinMode(pwrPin, OUTPUT); + digitalWrite(pwrPin, HIGH); + micOn=1; + }else{ + Serial.println(pwrPin); + } + + // set the PDM IRQ priority and enable + NVIC_SetPriority(PDM_IRQn, PDM_IRQ_PRIORITY); + NVIC_ClearPendingIRQ(PDM_IRQn); + NVIC_EnableIRQ(PDM_IRQn); + + // enable and trigger start task + nrf_pdm_enable(myPDM); + nrf_pdm_event_clear(myPDM,NRF_PDM_EVENT_STARTED); + nrf_pdm_task_trigger(myPDM,NRF_PDM_TASK_START); + } + #endif + + if(enDAC){ + #if defined USE_I2s // Enable transmission - /*NRF_I2S->CONFIG.TXEN = (I2S_CONFIG_TXEN_TXEN_ENABLE << I2S_CONFIG_TXEN_TXEN_Pos); + NRF_I2S->CONFIG.TXEN = (I2S_CONFIG_TXEN_TXEN_ENABLE << I2S_CONFIG_TXEN_TXEN_Pos); // Enable MCK generator NRF_I2S->CONFIG.MCKEN = (I2S_CONFIG_MCKEN_MCKEN_ENABLE << I2S_CONFIG_MCKEN_MCKEN_Pos); // MCKFREQ = 4 MHz - NRF_I2S->CONFIG.MCKFREQ = I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV31 << I2S_CONFIG_MCKFREQ_MCKFREQ_Pos; - //NRF_I2S->CONFIG.MCKFREQ = I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV63 << I2S_CONFIG_MCKFREQ_MCKFREQ_Pos; + //NRF_I2S->CONFIG.MCKFREQ = I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV31 << I2S_CONFIG_MCKFREQ_MCKFREQ_Pos; + NRF_I2S->CONFIG.MCKFREQ = I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV16 << I2S_CONFIG_MCKFREQ_MCKFREQ_Pos; // Ratio = 64 - NRF_I2S->CONFIG.RATIO = I2S_CONFIG_RATIO_RATIO_64X << I2S_CONFIG_RATIO_RATIO_Pos; + NRF_I2S->CONFIG.RATIO = I2S_CONFIG_RATIO_RATIO_256X << I2S_CONFIG_RATIO_RATIO_Pos; //NRF_I2S->CONFIG.RATIO = I2S_CONFIG_RATIO_RATIO_256X << I2S_CONFIG_RATIO_RATIO_Pos; // Master mode, 16Bit, left aligned @@ -167,23 +247,32 @@ void AutoAnalog::begin(bool enADC, bool enDAC){ NRF_I2S->CONFIG.CHANNELS = I2S_CONFIG_CHANNELS_CHANNELS_LEFT << I2S_CONFIG_CHANNELS_CHANNELS_Pos; // Configure pins - NRF_I2S->PSEL.MCK = (PIN_MCK << I2S_PSEL_MCK_PIN_Pos); - NRF_I2S->PSEL.SCK = (PIN_SCK << I2S_PSEL_SCK_PIN_Pos); - NRF_I2S->PSEL.LRCK = (PIN_LRCK << I2S_PSEL_LRCK_PIN_Pos); - NRF_I2S->PSEL.SDOUT = (PIN_SDOUT << I2S_PSEL_SDOUT_PIN_Pos); - - + //NRF_I2S->PSEL.MCK = (PIN_MCK << I2S_PSEL_MCK_PIN_Pos); + //NRF_I2S->PSEL.SCK = (PIN_SCK << I2S_PSEL_SCK_PIN_Pos); + //NRF_I2S->PSEL.LRCK = (PIN_LRCK << I2S_PSEL_LRCK_PIN_Pos); + //NRF_I2S->PSEL.SDOUT = (PIN_SDOUT << I2S_PSEL_SDOUT_PIN_Pos); + NRF_I2S->PSEL.MCK = (PIN_MCK << I2S_PSEL_MCK_PIN_Pos) | (I2S_PSEL_MCK_CONNECT_Connected << I2S_PSEL_MCK_CONNECT_Pos); + NRF_I2S->PSEL.SCK = (PIN_SCK << I2S_PSEL_SCK_PIN_Pos) | (I2S_PSEL_SCK_CONNECT_Connected << I2S_PSEL_SCK_CONNECT_Pos); + NRF_I2S->PSEL.LRCK = (PIN_LRCK << I2S_PSEL_LRCK_PIN_Pos) | (I2S_PSEL_LRCK_CONNECT_Connected << I2S_PSEL_LRCK_CONNECT_Pos); + NRF_I2S->PSEL.SDOUT = (PIN_SDOUT << I2S_PSEL_SDOUT_PIN_Pos) | (I2S_PSEL_SDOUT_CONNECT_Connected << I2S_PSEL_SDOUT_CONNECT_Pos); + + //NRF_I2S->INTENSET = I2S_INTEN_TXPTRUPD_Enabled << I2S_INTEN_TXPTRUPD_Pos; + //NVIC_EnableIRQ(I2S_IRQn); // Configure data pointer NRF_I2S->TXD.PTR = (uint32_t)dacBuf0; NRF_I2S->RXD.PTR = (uint32_t)dacBuf1; - NRF_I2S->RXTXD.MAXCNT = MAX_BUFFER_SIZE / sizeof(uint32_t); + NRF_I2S->RXTXD.MAXCNT = 16;// / sizeof(uint32_t); + + + //NRF_I2S->INTENSET = I2S_INTENSET_TXPTRUPD_Enabled << I2S_INTENSET_TXPTRUPD_Pos; + //NVIC_EnableIRQ(I2S_IRQn); //NRF_I2S->TXD.PTR = (uint32_t)&sine_table[0]; //NRF_I2S->RXTXD.MAXCNT = sizeof(sine_table) / sizeof(uint32_t); NRF_I2S->ENABLE = 1; // Start transmitting I2S data - NRF_I2S->TASKS_START = 1;*/ - + NRF_I2S->TASKS_START = 1; + #else pinMode(DEFAULT_PWM_PIN,OUTPUT); digitalWrite(DEFAULT_PWM_PIN,LOW); @@ -200,7 +289,7 @@ void AutoAnalog::begin(bool enADC, bool enDAC){ NRF_PWM0->SEQ[0].ENDDELAY = 0; NRF_PWM0->TASKS_SEQSTART[0] = 1; - + #endif //USE_I2s } @@ -211,12 +300,12 @@ void AutoAnalog::begin(bool enADC, bool enDAC){ void AutoAnalog::setSampleRate(uint32_t sampRate, bool stereo){ - NRF_PWM0->TASKS_STOP = 1; +/* NRF_PWM0->TASKS_STOP = 1; uint32_t timer = millis(); while(NRF_PWM0->EVENTS_STOPPED == 0){ if(millis() - timer > 1000){break;} } NRF_PWM0->COUNTERTOP = (((uint16_t)((16000000/sampRate) + 5)) << PWM_COUNTERTOP_COUNTERTOP_Pos); - NRF_PWM0->TASKS_SEQSTART[0] = 1; + NRF_PWM0->TASKS_SEQSTART[0] = 1;*/ } /****************************************************************************/ @@ -230,48 +319,72 @@ void AutoAnalog::triggerADC(){ /****************************************************************************/ void AutoAnalog::enableAdcChannel(uint8_t pinAx){ -#if !defined ARDUINO_NRF52840_FEATHER +#if defined __MBED__ nrf_pdm_enable(); +#else + nrf_pdm_enable(myPDM); #endif } /****************************************************************************/ void AutoAnalog::disableAdcChannel(uint8_t pinAx){ -#if !defined ARDUINO_NRF52840_FEATHER +#if defined __MBED__ nrf_pdm_disable(); +#else + nrf_pdm_disable(myPDM); #endif } /****************************************************************************/ void AutoAnalog::getADC(uint32_t samples){ - #if !defined ARDUINO_NRF52840_FEATHER +// #if defined __MBED__ while(!adcReady){__WFE();}; aSize = samples; adcReady = false; - #endif +// #endif } /****************************************************************************/ -bool dacBufCtr = 0; + +bool whichBuf = 0; void AutoAnalog::feedDAC(uint8_t dacChannel, uint32_t samples, bool startInterrupts){ - - /*uint32_t ctr = millis(); - while(!nrf_i2s_event_check(NRF_I2S,NRF_I2S_EVENT_TXPTRUPD));//{ if(millis() - ctr > 5){break;}}; - nrf_i2s_event_clear(NRF_I2S,NRF_I2S_EVENT_TXPTRUPD); + + #if defined USE_I2s + uint32_t ctr = millis(); + //while(!nrf_i2s_event_check(NRF_I2S,NRF_I2S_EVENT_TXPTRUPD));//{ if(millis() - ctr > 5){break;}}; + + + - for(uint32_t i=0; iEVENTS_TXPTRUPD == 0){} + NRF_I2S->EVENTS_TXPTRUPD = 0; + + if(whichBuf == 0){ + for(uint32_t i=0; iTXD.PTR = (uint32_t)dacBuf0; + }else{ + for(uint32_t i=0; iTXD.PTR = (uint32_t)dacBuf1; + } + whichBuf = !whichBuf; //memcpy(dacBuf0, dacBuffer16, samples * 2); + + //NRF_I2S->TXD.PTR = (uint32_t)&dacBuf0[0]; + //memcpy(&dacBuf[0], &dacBuffer16[0], samples); + uint32_t bytes = samples * 2; + NRF_I2S->RXTXD.MAXCNT = samples / sizeof(uint32_t); + //nrf_i2s_event_clear(NRF_I2S,NRF_I2S_EVENT_TXPTRUPD); - //NRF_I2S->TXD.PTR = (uint32_t)&dacBuf0[0]; - //memcpy(&dacBuf[0], &dacBuffer16[0], samples); - NRF_I2S->RXTXD.MAXCNT = 8;// * 2 / sizeof(uint32_t);*/ + #else uint32_t timer = millis(); while(NRF_PWM0->EVENTS_SEQEND[0] == 0){ if(millis() - timer > 1000){ Serial.println("return"); NRF_PWM0->TASKS_SEQSTART[0] = 1; return; } @@ -288,6 +401,7 @@ void AutoAnalog::feedDAC(uint8_t dacChannel, uint32_t samples, bool startInterru NRF_PWM0->SEQ[0].PTR = ((uint32_t)(&dacBuf0[0]) << PWM_SEQ_PTR_PTR_Pos); NRF_PWM0->SEQ[0].CNT = (samples << PWM_SEQ_CNT_CNT_Pos); NRF_PWM0->TASKS_SEQSTART[0] = 1; + #endif //USE_I2s } @@ -331,8 +445,11 @@ void AutoAnalog::dacSetup(void){ /****************************************************************************/ void AutoAnalog::disableDAC(bool withinTask){ - //NRF_I2S->ENABLE = 0; + #if defined USE_I2s + NRF_I2S->ENABLE = 0; + #else NRF_PWM0->TASKS_STOP = 1; + #endif } /****************************************************************************/ @@ -358,8 +475,13 @@ void AutoAnalog::tc2Setup (uint32_t sampRate) } +void I2S_IRQHandler_v(void){ + // AutoAnalog::testCounter++; +} + + /****************************************************************************/ -#if !defined ARDUINO_NRF52840_FEATHER +#if defined __MBED__ extern "C" { __attribute__((__used__)) void PDM_IRQHandler_v(void) { @@ -408,5 +530,56 @@ void AutoAnalog::adcCallback(uint16_t *buf, uint32_t buf_len){ adcReady = true; } -#endif //#if !defined ARDUINO_NRF52840_FEATHER +#elif !defined MBED +extern "C" { + __attribute__((__used__)) void PDM_IRQHandler(void) + { +if (nrf_pdm_event_check(myPDM,NRF_PDM_EVENT_STARTED)) { + nrf_pdm_event_clear(myPDM,NRF_PDM_EVENT_STARTED); + + // switch to fill + + if (AutoAnalog::aCtr) { + nrf_pdm_buffer_set(myPDM,(uint32_t*)(AutoAnalog::buf0), AutoAnalog::aSize); + if(AutoAnalog::_onReceive){ + NVIC_DisableIRQ(PDM_IRQn); + AutoAnalog::_onReceive(AutoAnalog::buf1, AutoAnalog::aSize); + NVIC_EnableIRQ(PDM_IRQn); + } + } else { + nrf_pdm_buffer_set(myPDM,(uint32_t*)(AutoAnalog::buf1), AutoAnalog::aSize); + if(AutoAnalog::_onReceive){ + NVIC_DisableIRQ(PDM_IRQn); + AutoAnalog::_onReceive(AutoAnalog::buf0, AutoAnalog::aSize); + NVIC_EnableIRQ(PDM_IRQn); + } + } + + // Flip to next buffer + AutoAnalog::aCtr = (AutoAnalog::aCtr + 1) % 2; + + + } else if (nrf_pdm_event_check(myPDM,NRF_PDM_EVENT_STOPPED)) { + nrf_pdm_event_clear(myPDM,NRF_PDM_EVENT_STOPPED); + } else if (nrf_pdm_event_check(myPDM,NRF_PDM_EVENT_END)) { + nrf_pdm_event_clear(myPDM,NRF_PDM_EVENT_END); + } + } +} + +void AutoAnalog::set_callback(void(*function)(uint16_t *buf, uint32_t buf_len)){ + _onReceive = function; +} + + + +void AutoAnalog::adcCallback(uint16_t *buf, uint32_t buf_len){ + + for(uint32_t i=0; i < buf_len; i++){ + adcBuffer16[i] = buf[i]; + } + + adcReady = true; +} +#endif #endif //#if defined (ARDUINO_ARCH_SAM)