diff --git a/libraries/PDM/examples/Example1_MicrophoneOutput/Example1_MicrophoneOutput.ino b/libraries/PDM/examples/Example1_MicrophoneOutput/Example1_MicrophoneOutput.ino index 4a5c73a..5b39656 100644 --- a/libraries/PDM/examples/Example1_MicrophoneOutput/Example1_MicrophoneOutput.ino +++ b/libraries/PDM/examples/Example1_MicrophoneOutput/Example1_MicrophoneOutput.ino @@ -8,7 +8,7 @@ //Global variables needed for PDM library #define pdmDataBufferSize 4096 //Default is array of 4096 * 32bit -uint32_t pdmDataBuffer[pdmDataBufferSize]; +uint16_t pdmDataBuffer[pdmDataBufferSize]; //Global variables needed for the FFT in this sketch float g_fPDMTimeDomain[pdmDataBufferSize * 2]; @@ -21,7 +21,7 @@ uint32_t sampleFreq; #define PRINT_FFT_DATA 0 #include //Include PDM library included with the Aruino_Apollo3 core -AP3_PDM myPDM; //Create instance of PDM class +AP3_PDM myPDM; //Create instance of PDM class //Math library needed for FFT #define ARM_MATH_CM4 @@ -29,39 +29,31 @@ AP3_PDM myPDM; //Create instance of PDM class void setup() { - Serial.begin(9600); + Serial.begin(115200); Serial.println("SparkFun PDM Example"); - if (myPDM.begin() == false) // Turn on PDM with default settings + if (myPDM.begin() == false) // Turn on PDM with default settings, start interrupts { Serial.println("PDM Init failed. Are you sure these pins are PDM capable?"); - while (1); + while (1) + ; } Serial.println("PDM Initialized"); printPDMConfig(); - - myPDM.getData(pdmDataBuffer, pdmDataBufferSize); //This clears the current PDM FIFO and starts DMA } void loop() { - noInterrupts(); - if (myPDM.available()) { - printLoudest(); - - while (PRINT_PDM_DATA || PRINT_FFT_DATA); - - // Start converting the next set of PCM samples. myPDM.getData(pdmDataBuffer, pdmDataBufferSize); + + printLoudest(); } // Go to Deep Sleep until the PDM ISR or other ISR wakes us. am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP); - - interrupts(); } //***************************************************************************** @@ -73,7 +65,7 @@ void printLoudest(void) { float fMaxValue; uint32_t ui32MaxIndex; - int16_t *pi16PDMData = (int16_t *) pdmDataBuffer; + int16_t *pi16PDMData = (int16_t *)pdmDataBuffer; uint32_t ui32LoudestFrequency; // @@ -146,27 +138,49 @@ void printPDMConfig(void) // switch (myPDM.getClockDivider()) { - case AM_HAL_PDM_MCLKDIV_4: MClkDiv = 4; break; - case AM_HAL_PDM_MCLKDIV_3: MClkDiv = 3; break; - case AM_HAL_PDM_MCLKDIV_2: MClkDiv = 2; break; - case AM_HAL_PDM_MCLKDIV_1: MClkDiv = 1; break; - - default: - MClkDiv = 0; + case AM_HAL_PDM_MCLKDIV_4: + MClkDiv = 4; + break; + case AM_HAL_PDM_MCLKDIV_3: + MClkDiv = 3; + break; + case AM_HAL_PDM_MCLKDIV_2: + MClkDiv = 2; + break; + case AM_HAL_PDM_MCLKDIV_1: + MClkDiv = 1; + break; + + default: + MClkDiv = 0; } switch (myPDM.getClockSpeed()) { - case AM_HAL_PDM_CLK_12MHZ: PDMClk = 12000000; break; - case AM_HAL_PDM_CLK_6MHZ: PDMClk = 6000000; break; - case AM_HAL_PDM_CLK_3MHZ: PDMClk = 3000000; break; - case AM_HAL_PDM_CLK_1_5MHZ: PDMClk = 1500000; break; - case AM_HAL_PDM_CLK_750KHZ: PDMClk = 750000; break; - case AM_HAL_PDM_CLK_375KHZ: PDMClk = 375000; break; - case AM_HAL_PDM_CLK_187KHZ: PDMClk = 187000; break; - - default: - PDMClk = 0; + case AM_HAL_PDM_CLK_12MHZ: + PDMClk = 12000000; + break; + case AM_HAL_PDM_CLK_6MHZ: + PDMClk = 6000000; + break; + case AM_HAL_PDM_CLK_3MHZ: + PDMClk = 3000000; + break; + case AM_HAL_PDM_CLK_1_5MHZ: + PDMClk = 1500000; + break; + case AM_HAL_PDM_CLK_750KHZ: + PDMClk = 750000; + break; + case AM_HAL_PDM_CLK_375KHZ: + PDMClk = 375000; + break; + case AM_HAL_PDM_CLK_187KHZ: + PDMClk = 187000; + break; + + default: + PDMClk = 0; } // @@ -175,7 +189,7 @@ void printPDMConfig(void) // sampleFreq = (PDMClk / (MClkDiv * 2 * myPDM.getDecimationRate())); - frequencyUnits = (float) sampleFreq / (float) pdmDataBufferSize; + frequencyUnits = (float)sampleFreq / (float)pdmDataBufferSize; Serial.printf("Settings:\n"); Serial.printf("PDM Clock (Hz): %12d\n", PDMClk); diff --git a/libraries/PDM/examples/Example2_ConfigureMic/Example2_ConfigureMic.ino b/libraries/PDM/examples/Example2_ConfigureMic/Example2_ConfigureMic.ino index a896db8..4c91ca5 100644 --- a/libraries/PDM/examples/Example2_ConfigureMic/Example2_ConfigureMic.ino +++ b/libraries/PDM/examples/Example2_ConfigureMic/Example2_ConfigureMic.ino @@ -7,7 +7,7 @@ //Global variables needed for PDM library #define pdmDataBufferSize 4096 //Default is array of 4096 * 32bit -uint32_t pdmDataBuffer[pdmDataBufferSize]; +uint16_t pdmDataBuffer[pdmDataBufferSize]; //Global variables needed for the FFT in this sketch float g_fPDMTimeDomain[pdmDataBufferSize * 2]; @@ -20,7 +20,7 @@ uint32_t sampleFreq; #define PRINT_FFT_DATA 0 #include //Include PDM library included with the Aruino_Apollo3 core -AP3_PDM myPDM; //Create instance of PDM class +AP3_PDM myPDM; //Create instance of PDM class //Math library needed for FFT #define ARM_MATH_CM4 @@ -28,7 +28,7 @@ AP3_PDM myPDM; //Create instance of PDM class void setup() { - Serial.begin(9600); + Serial.begin(115200); Serial.println("SparkFun PDM Example"); // The variant files for Artemis carrier boards have Mic data and clock pins defined @@ -37,7 +37,8 @@ void setup() if (myPDM.begin(22, 23) == false) //Data, clock on Artemis Nano - These are the pin names from variant file, not pad names { Serial.println("PDM Init failed. Are you sure these pins are PDM capable?"); - while (1); + while (1) + ; } Serial.println("PDM Initialized"); @@ -52,28 +53,19 @@ void setup() //The equivalent getGain(), getClockSpeed(), etc are available printPDMConfig(); - - myPDM.getData(pdmDataBuffer, pdmDataBufferSize); //This clears the current PDM FIFO and starts DMA } void loop() { - noInterrupts(); - if (myPDM.available()) { - printLoudest(); - - while (PRINT_PDM_DATA || PRINT_FFT_DATA); - - // Start converting the next set of PCM samples. myPDM.getData(pdmDataBuffer, pdmDataBufferSize); + + printLoudest(); } // Go to Deep Sleep until the PDM ISR or other ISR wakes us. am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP); - - interrupts(); } //***************************************************************************** @@ -85,7 +77,7 @@ void printLoudest(void) { float fMaxValue; uint32_t ui32MaxIndex; - int16_t *pi16PDMData = (int16_t *) pdmDataBuffer; + int16_t *pi16PDMData = (int16_t *)pdmDataBuffer; uint32_t ui32LoudestFrequency; // @@ -158,27 +150,49 @@ void printPDMConfig(void) // switch (myPDM.getClockDivider()) { - case AM_HAL_PDM_MCLKDIV_4: MClkDiv = 4; break; - case AM_HAL_PDM_MCLKDIV_3: MClkDiv = 3; break; - case AM_HAL_PDM_MCLKDIV_2: MClkDiv = 2; break; - case AM_HAL_PDM_MCLKDIV_1: MClkDiv = 1; break; - - default: - MClkDiv = 0; + case AM_HAL_PDM_MCLKDIV_4: + MClkDiv = 4; + break; + case AM_HAL_PDM_MCLKDIV_3: + MClkDiv = 3; + break; + case AM_HAL_PDM_MCLKDIV_2: + MClkDiv = 2; + break; + case AM_HAL_PDM_MCLKDIV_1: + MClkDiv = 1; + break; + + default: + MClkDiv = 0; } switch (myPDM.getClockSpeed()) { - case AM_HAL_PDM_CLK_12MHZ: PDMClk = 12000000; break; - case AM_HAL_PDM_CLK_6MHZ: PDMClk = 6000000; break; - case AM_HAL_PDM_CLK_3MHZ: PDMClk = 3000000; break; - case AM_HAL_PDM_CLK_1_5MHZ: PDMClk = 1500000; break; - case AM_HAL_PDM_CLK_750KHZ: PDMClk = 750000; break; - case AM_HAL_PDM_CLK_375KHZ: PDMClk = 375000; break; - case AM_HAL_PDM_CLK_187KHZ: PDMClk = 187000; break; - - default: - PDMClk = 0; + case AM_HAL_PDM_CLK_12MHZ: + PDMClk = 12000000; + break; + case AM_HAL_PDM_CLK_6MHZ: + PDMClk = 6000000; + break; + case AM_HAL_PDM_CLK_3MHZ: + PDMClk = 3000000; + break; + case AM_HAL_PDM_CLK_1_5MHZ: + PDMClk = 1500000; + break; + case AM_HAL_PDM_CLK_750KHZ: + PDMClk = 750000; + break; + case AM_HAL_PDM_CLK_375KHZ: + PDMClk = 375000; + break; + case AM_HAL_PDM_CLK_187KHZ: + PDMClk = 187000; + break; + + default: + PDMClk = 0; } // @@ -187,7 +201,7 @@ void printPDMConfig(void) // sampleFreq = (PDMClk / (MClkDiv * 2 * myPDM.getDecimationRate())); - frequencyUnits = (float) sampleFreq / (float) pdmDataBufferSize; + frequencyUnits = (float)sampleFreq / (float)pdmDataBufferSize; Serial.printf("Settings:\n"); Serial.printf("PDM Clock (Hz): %12d\n", PDMClk); diff --git a/libraries/PDM/examples/Example3_FullConfigure/Example3_FullConfigure.ino b/libraries/PDM/examples/Example3_FullConfigure/Example3_FullConfigure.ino index 12fe99e..53fa8ef 100644 --- a/libraries/PDM/examples/Example3_FullConfigure/Example3_FullConfigure.ino +++ b/libraries/PDM/examples/Example3_FullConfigure/Example3_FullConfigure.ino @@ -9,7 +9,7 @@ //Global variables needed for PDM library #define pdmDataBufferSize 4096 //Default is array of 4096 * 32bit -uint32_t pdmDataBuffer[pdmDataBufferSize]; +uint16_t pdmDataBuffer[pdmDataBufferSize]; //Global variables needed for the FFT in this sketch float g_fPDMTimeDomain[pdmDataBufferSize * 2]; @@ -22,7 +22,7 @@ uint32_t sampleFreq; #define PRINT_FFT_DATA 0 #include //Include PDM library included with the Aruino_Apollo3 core -AP3_PDM myPDM; //Create instance of PDM class +AP3_PDM myPDM; //Create instance of PDM class //Math library needed for FFT #define ARM_MATH_CM4 @@ -50,42 +50,34 @@ const am_hal_pdm_config_t newConfig = { void setup() { - Serial.begin(9600); + Serial.begin(115200); Serial.println("SparkFun PDM Example"); // Turn on the PDM with default settings if (myPDM.begin() == false) //Use Data, clock defines from variant file { Serial.println("PDM Init failed. Are you sure these pins are PDM capable?"); - while (1); + while (1) + ; } Serial.println("PDM Initialized"); myPDM.updateConfig(newConfig); //Send config struct - + printPDMConfig(); - - myPDM.getData(pdmDataBuffer, pdmDataBufferSize); //This clears the current PDM FIFO and starts DMA } void loop() { - noInterrupts(); - if (myPDM.available()) { - printLoudest(); - - while (PRINT_PDM_DATA || PRINT_FFT_DATA); - - // Start converting the next set of PCM samples. myPDM.getData(pdmDataBuffer, pdmDataBufferSize); + + printLoudest(); } // Go to Deep Sleep until the PDM ISR or other ISR wakes us. am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP); - - interrupts(); } //***************************************************************************** @@ -97,7 +89,7 @@ void printLoudest(void) { float fMaxValue; uint32_t ui32MaxIndex; - int16_t *pi16PDMData = (int16_t *) pdmDataBuffer; + int16_t *pi16PDMData = (int16_t *)pdmDataBuffer; uint32_t ui32LoudestFrequency; // @@ -170,27 +162,49 @@ void printPDMConfig(void) // switch (myPDM.getClockDivider()) { - case AM_HAL_PDM_MCLKDIV_4: MClkDiv = 4; break; - case AM_HAL_PDM_MCLKDIV_3: MClkDiv = 3; break; - case AM_HAL_PDM_MCLKDIV_2: MClkDiv = 2; break; - case AM_HAL_PDM_MCLKDIV_1: MClkDiv = 1; break; - - default: - MClkDiv = 0; + case AM_HAL_PDM_MCLKDIV_4: + MClkDiv = 4; + break; + case AM_HAL_PDM_MCLKDIV_3: + MClkDiv = 3; + break; + case AM_HAL_PDM_MCLKDIV_2: + MClkDiv = 2; + break; + case AM_HAL_PDM_MCLKDIV_1: + MClkDiv = 1; + break; + + default: + MClkDiv = 0; } switch (myPDM.getClockSpeed()) { - case AM_HAL_PDM_CLK_12MHZ: PDMClk = 12000000; break; - case AM_HAL_PDM_CLK_6MHZ: PDMClk = 6000000; break; - case AM_HAL_PDM_CLK_3MHZ: PDMClk = 3000000; break; - case AM_HAL_PDM_CLK_1_5MHZ: PDMClk = 1500000; break; - case AM_HAL_PDM_CLK_750KHZ: PDMClk = 750000; break; - case AM_HAL_PDM_CLK_375KHZ: PDMClk = 375000; break; - case AM_HAL_PDM_CLK_187KHZ: PDMClk = 187000; break; - - default: - PDMClk = 0; + case AM_HAL_PDM_CLK_12MHZ: + PDMClk = 12000000; + break; + case AM_HAL_PDM_CLK_6MHZ: + PDMClk = 6000000; + break; + case AM_HAL_PDM_CLK_3MHZ: + PDMClk = 3000000; + break; + case AM_HAL_PDM_CLK_1_5MHZ: + PDMClk = 1500000; + break; + case AM_HAL_PDM_CLK_750KHZ: + PDMClk = 750000; + break; + case AM_HAL_PDM_CLK_375KHZ: + PDMClk = 375000; + break; + case AM_HAL_PDM_CLK_187KHZ: + PDMClk = 187000; + break; + + default: + PDMClk = 0; } // @@ -199,7 +213,7 @@ void printPDMConfig(void) // sampleFreq = (PDMClk / (MClkDiv * 2 * myPDM.getDecimationRate())); - frequencyUnits = (float) sampleFreq / (float) pdmDataBufferSize; + frequencyUnits = (float)sampleFreq / (float)pdmDataBufferSize; Serial.printf("Settings:\n"); Serial.printf("PDM Clock (Hz): %12d\n", PDMClk); diff --git a/libraries/PDM/examples/Example4_RecordToWav/ConvertToWav.py b/libraries/PDM/examples/Example4_RecordToWav/ConvertToWav.py new file mode 100644 index 0000000..37e9335 --- /dev/null +++ b/libraries/PDM/examples/Example4_RecordToWav/ConvertToWav.py @@ -0,0 +1,132 @@ +#!/usr/bin/python +from __future__ import division + +""" +Author: Justice Amoh +Date: 11/01/2019 +Description: Python script to stream audio from Artemis Apollo3 PDM microphone +""" +import sys +import serial +import numpy as np +import matplotlib.pyplot as plt + +from serial.tools import list_ports +from time import sleep +from scipy.io import wavfile +from datetime import datetime + + +# Controls +do_plot = True +do_save = True +wavname = 'recording_%s.wav'%(datetime.now().strftime("%m%d_%H%M")) +runtime = 50#100 # runtime in frames, sec/10 + +# Find Artemis Serial Port +ports = list_ports.comports() +try: + sPort = [p[0] for p in ports if 'cu.wchusbserial' in p[0]][0] +except Exception as e: + print 'Cannot find serial port!' + sys.exit(3) + +# Serial Config +ser = serial.Serial(sPort,115200) +ser.reset_input_buffer() +ser.reset_output_buffer() + + +# Audio Format & Datatype +dtype = np.int16 # Data type to read data +typelen = np.dtype(dtype).itemsize # Length of data type +maxval = 32768. # 2**15 # For 16bit signed + +# Plot Parameters +delay = .00001 # Use 1us pauses - as in matlab +fsamp = 16000 # Sampling rate +nframes = 10 # No. of frames to read at a time +buflen = fsamp//10 # Buffer length +bufsize = buflen*typelen # Resulting number of bytes to read +window = fsamp*10 # window of signal to plot at a time in samples + + +# Variables +x = [0]*window +t = np.arange(window)/fsamp # [x/fsamp for x in range(10)] + +#--------------- +# Plot & Figures +#--------------- +plt.ion() +plt.show() + + +# Configure Figure +with plt.style.context(('dark_background')): + fig,axs = plt.subplots(1,1,figsize=(7,2.5)) + + lw, = axs.plot(t,x,'r') + axs.set_xlim(0,window/fsamp) + axs.grid(which='major', alpha=0.2) + axs.set_ylim(-1,1) + axs.set_xlabel('Time (s)') + axs.set_ylabel('Amplitude') + axs.set_title('Streaming Audio') + plt.tight_layout() + plt.pause(0.001) + + + +# Start Transmission +ser.write('START') # Send Start command +sleep(1) + +for i in range(runtime): + buf = ser.read(bufsize) # Read audio data + buf = np.frombuffer(buf,dtype=dtype) # Convert to int16 + buf = buf/maxval # convert to float + x.extend(buf) # Append to waveform array + + # Update Plot lines + lw.set_ydata(x[-window:]) + + plt.pause(0.001) + sleep(delay) + + +# Stop Streaming +ser.write('STOP') +sleep(0.5) +ser.reset_input_buffer() +ser.reset_output_buffer() +ser.close() + +# Remove initial zeros +x = x[window:] + + +# Helper Functions +def plotAll(): + t = np.arange(len(x))/fsamp + with plt.style.context(('dark_background')): + fig,axs = plt.subplots(1,1,figsize=(7,2.5)) + lw, = axs.plot(t,x,'r') + axs.grid(which='major', alpha=0.2) + axs.set_xlim(0,t[-1]) + plt.tight_layout() + return + +# Plot All +if do_plot: + plt.close(fig) + plotAll() + +# Save Recorded Audio +if do_save: + wavfile.write(wavname,fsamp,np.array(x)) + print "Recording saved to file: %s"%wavname + + + + diff --git a/libraries/PDM/examples/Example4_RecordToWav/ConvertToWav3.py b/libraries/PDM/examples/Example4_RecordToWav/ConvertToWav3.py new file mode 100644 index 0000000..d6d7fab --- /dev/null +++ b/libraries/PDM/examples/Example4_RecordToWav/ConvertToWav3.py @@ -0,0 +1,130 @@ +#!/usr/bin/python + + +""" +Author: Justice Amoh +Date: 11/01/2019 +Description: Python script to stream audio from Artemis Apollo3 PDM microphone +""" +import sys +import serial +import numpy as np +import matplotlib.pyplot as plt + +from serial.tools import list_ports +from time import sleep +from scipy.io import wavfile +from datetime import datetime + + +# Controls +do_plot = True +do_save = True +wavname = 'recording_%s.wav' % (datetime.now().strftime("%m%d_%H%M")) +runtime = 50 # 100 # runtime in frames, sec/10 + +# Auto port assignment for Mac/Linux machines +# try: +# sPort = [p[0] for p in ports if 'cu.wchusbserial' in p[0]][0] +# except Exception as e: +# print('Cannot find serial port!') +# sys.exit(3) + +# Manual port assignment for windows machines +sPort = "COM4" + +# Serial Config +ser = serial.Serial(sPort, 500000) +ser.reset_input_buffer() +ser.reset_output_buffer() + + +# Audio Format & Datatype +dtype = np.int16 # Data type to read data +typelen = np.dtype(dtype).itemsize # Length of data type +maxval = 32768. # 2**15 # For 16bit signed + +# Plot Parameters +delay = .00001 # Use 1us pauses - as in matlab +fsamp = 16000 # Sampling rate +nframes = 10 # No. of frames to read at a time +buflen = fsamp//10 # Buffer length +bufsize = buflen*typelen # Resulting number of bytes to read +window = fsamp*10 # window of signal to plot at a time in samples + + +# Variables +x = [0]*window +t = np.arange(window)/fsamp # [x/fsamp for x in range(10)] + +# --------------- +# Plot & Figures +# --------------- +plt.ion() +plt.show() + + +# Configure Figure +with plt.style.context(('dark_background')): + fig, axs = plt.subplots(1, 1, figsize=(7, 2.5)) + + lw, = axs.plot(t, x, 'r') + axs.set_xlim(0, window/fsamp) + axs.grid(which='major', alpha=0.2) + axs.set_ylim(-1, 1) + axs.set_xlabel('Time (s)') + axs.set_ylabel('Amplitude') + axs.set_title('Streaming Audio') + plt.tight_layout() + plt.pause(0.001) + + +# Start Transmission +ser.write('START'.encode()) # Send Start command +sleep(1) + +for i in range(runtime): + buf = ser.read(bufsize) # Read audio data + buf = np.frombuffer(buf, dtype=dtype) # Convert to int16 + buf = buf/maxval # convert to float + x.extend(buf) # Append to waveform array + + # Update Plot lines + lw.set_ydata(x[-window:]) + + plt.pause(0.001) + sleep(delay) + + +# Stop Streaming +ser.write('STOP'.encode()) +sleep(0.5) +ser.reset_input_buffer() +ser.reset_output_buffer() +ser.close() + +# Remove initial zeros +x = x[window:] + + +# Helper Functions +def plotAll(): + t = np.arange(len(x))/fsamp + with plt.style.context(('dark_background')): + fig, axs = plt.subplots(1, 1, figsize=(7, 2.5)) + lw, = axs.plot(t, x, 'r') + axs.grid(which='major', alpha=0.2) + axs.set_xlim(0, t[-1]) + plt.tight_layout() + return + + +# Plot All +if do_plot: + plt.close(fig) + plotAll() + +# Save Recorded Audio +if do_save: + wavfile.write(wavname, fsamp, np.array(x)) + print("Recording saved to file: %s" % wavname) diff --git a/libraries/PDM/examples/Example4_RecordToWav/CorruptOutput.wav b/libraries/PDM/examples/Example4_RecordToWav/CorruptOutput.wav new file mode 100644 index 0000000..99e4a03 Binary files /dev/null and b/libraries/PDM/examples/Example4_RecordToWav/CorruptOutput.wav differ diff --git a/libraries/PDM/examples/Example4_RecordToWav/Example4_RecordToWav.ino b/libraries/PDM/examples/Example4_RecordToWav/Example4_RecordToWav.ino new file mode 100644 index 0000000..09085fe --- /dev/null +++ b/libraries/PDM/examples/Example4_RecordToWav/Example4_RecordToWav.ino @@ -0,0 +1,70 @@ +/* Author: @justiceamoh and Nathan Seidle + Created: November 19th, 2019 + License: MIT. See SparkFun Arduino Apollo3 Project for more information + + This example demonstrates how to read audio data and output + it to a WAV file. This sketch outputs raw serial; an accompanying + python script visualizes and coverts the raw data to a WAV file. + + Note: Audio samples are generated fast enough that we need to output + serial at 500kbps. + + The PDM hardware is setup to take a sample every 64us (15.625kHz sample rate) + The PDM library uses DMA to transfer 4096 bytes every 262ms and stores the + data between two internal buffers. So check available() often and call getData + to prevent buffer overruns. +*/ + +#include //Include PDM library included with the Aruino_Apollo3 core +AP3_PDM myPDM; //Create instance of PDM class with our buffer + +#define pdmDataSize 4096 //Library requires array be 4096 +uint16_t pdmData[pdmDataSize]; + +// ----------------- +// PDM Configuration +// ----------------- +void *PDMHandle = NULL; +am_hal_pdm_config_t newConfig = { + .eClkDivider = AM_HAL_PDM_MCLKDIV_1, + .eLeftGain = AM_HAL_PDM_GAIN_0DB, + .eRightGain = AM_HAL_PDM_GAIN_P90DB, //Found empirically + .ui32DecimationRate = 48, // OSR = 1500/16 = 96 = 2*SINCRATE --> SINC_RATE = 48 + .bHighPassEnable = 0, + .ui32HighPassCutoff = 0xB, + .ePDMClkSpeed = AM_HAL_PDM_CLK_1_5MHZ, + .bInvertI2SBCLK = 0, + .ePDMClkSource = AM_HAL_PDM_INTERNAL_CLK, + .bPDMSampleDelay = 0, + .bDataPacking = 1, + .ePCMChannels = AM_HAL_PDM_CHANNEL_RIGHT, + .ui32GainChangeDelay = 1, + .bI2SEnable = 0, + .bSoftMute = 0, + .bLRSwap = 0, +}; + +void setup() +{ + Serial.begin(500000); + delay(10); + + if (myPDM.begin() == false) // Turn on PDM with default settings, start interrupts + { + Serial.println("PDM Init failed. Are you sure these pins are PDM capable?"); + while (1) + ; + } + myPDM.updateConfig(newConfig); //Send config struct +} + +void loop() +{ + if (myPDM.available()) + { + myPDM.getData(pdmData, pdmDataSize); + + //Print data to serial port + Serial.write((uint8_t *)pdmData, sizeof(pdmData)); + } +} \ No newline at end of file diff --git a/libraries/PDM/examples/Example4_RecordToWav/ExampleOutput.wav b/libraries/PDM/examples/Example4_RecordToWav/ExampleOutput.wav new file mode 100644 index 0000000..478f0d4 Binary files /dev/null and b/libraries/PDM/examples/Example4_RecordToWav/ExampleOutput.wav differ diff --git a/libraries/PDM/examples/Example4_RecordToWav/readme.md b/libraries/PDM/examples/Example4_RecordToWav/readme.md new file mode 100644 index 0000000..de29276 --- /dev/null +++ b/libraries/PDM/examples/Example4_RecordToWav/readme.md @@ -0,0 +1,11 @@ +Author: @justiceamoh and Nathan Seidle +Created: November 19th, 2019 +License: MIT. See SparkFun Arduino Apollo3 Project for more information + +This example demonstrates how to read audio data and output it to a WAV file. A sketch reads PDM data and outputs raw serial, and the ConverToWav python script (python versions 2 and 3 are both available) visualizes and coverts the serial data to a WAV file. + +Gotchas: + +* You will need to modify the python script to match the COM port used on your computer. For Windows based machines find the +* You may need to use VLC to play the WAV clips. The bitrate is 256kbps which is higher than some audio players can handle. +* Audio samples are generated fast enough that we need to output serial at 500kbps. \ No newline at end of file diff --git a/libraries/PDM/src/PDM.cpp b/libraries/PDM/src/PDM.cpp index 451ea62..f9d147d 100644 --- a/libraries/PDM/src/PDM.cpp +++ b/libraries/PDM/src/PDM.cpp @@ -23,6 +23,15 @@ SOFTWARE. AP3_PDM *ap3_pdm_handle = 0; +// AP3_PDM::AP3_PDM(uint16_t *userBuffer, uint32_t bufferSize) +// { +// _userBuffer = userBuffer; +// _userBufferSize = bufferSize; + +// _readHead = 0; +// _writeHead = 0; +// } + bool AP3_PDM::begin(ap3_gpio_pin_t pinPDMData, ap3_gpio_pin_t pinPDMClock) { _PDMhandle = NULL; @@ -39,7 +48,20 @@ bool AP3_PDM::begin(ap3_gpio_pin_t pinPDMData, ap3_gpio_pin_t pinPDMClock) bool AP3_PDM::available(void) { - return (_PDMdataReady); + // if (_readHead != _writeHead) + if (buff1New || buff2New) + return (true); + return (false); +} + +bool AP3_PDM::isOverrun(void) +{ + if (_overrun == true) + { + _overrun = false; + return (true); + } + return (false); } ap3_err_t AP3_PDM::_begin(void) @@ -106,12 +128,22 @@ ap3_err_t AP3_PDM::_begin(void) // completion). // am_hal_pdm_interrupt_enable(_PDMhandle, (AM_HAL_PDM_INT_DERR | AM_HAL_PDM_INT_DCMP | AM_HAL_PDM_INT_UNDFL | AM_HAL_PDM_INT_OVF)); - am_hal_interrupt_master_enable(); + //am_hal_interrupt_master_enable(); NVIC_EnableIRQ(PDM_IRQn); // Register the class into the local list ap3_pdm_handle = this; + // Configure DMA and set target address of internal buffer. + sTransfer.ui32TargetAddr = (uint32_t)_pdmDataBuffer; + sTransfer.ui32TotalCount = _pdmBufferSize * 2; + + // Start the data transfer. + am_hal_pdm_enable(_PDMhandle); + am_util_delay_ms(100); + am_hal_pdm_fifo_flush(_PDMhandle); + am_hal_pdm_dma_start(_PDMhandle, &sTransfer); + return retval; } @@ -197,7 +229,11 @@ uint32_t AP3_PDM::getDecimationRate() //Send a given configuration struct to PDM bool AP3_PDM::updateConfig(am_hal_pdm_config_t newConfiguration) { - ap3_err_t retval = (ap3_err_t)am_hal_pdm_configure(_PDMhandle, &newConfiguration); + _PDMconfig = newConfiguration; + ap3_err_t retval = (ap3_err_t)am_hal_pdm_configure(_PDMhandle, &_PDMconfig); + + am_hal_pdm_enable(_PDMhandle); //Reenable after changes + if (retval != AP3_OK) { return false; @@ -250,51 +286,110 @@ ap3_err_t ap3_pdm_pad_funcsel(ap3_pdm_pad_type_e type, ap3_gpio_pad_t pad, uint8 //***************************************************************************** // -// Start a transaction to get some number of bytes from the PDM interface. +// Read PDM data from internal buffer +// Returns number of bytes read. // //***************************************************************************** -void AP3_PDM::getData(uint32_t *PDMDataBuffer, uint32_t bufferSize) +uint32_t AP3_PDM::getData(uint16_t *externalBuffer, uint32_t externalBufferSize) { - // - // Configure DMA and target address. - // - am_hal_pdm_transfer_t sTransfer; - sTransfer.ui32TargetAddr = (uint32_t)PDMDataBuffer; - sTransfer.ui32TotalCount = bufferSize * 2; //PDM_FFT_BYTES; + if (externalBufferSize > _pdmBufferSize) + externalBufferSize = _pdmBufferSize; - // - // Start the data transfer. - // - am_hal_pdm_enable(_PDMhandle); - am_util_delay_ms(100); - am_hal_pdm_fifo_flush(_PDMhandle); - am_hal_pdm_dma_start(_PDMhandle, &sTransfer); - - _PDMdataReady = false; + //Move data from internal buffers to external caller + if (buff1New == true) + { + for (int x = 0; x < externalBufferSize; x++) + { + externalBuffer[x] = outBuffer1[x]; + } + buff1New = false; + } + else if (buff2New == true) + { + for (int x = 0; x < externalBufferSize; x++) + { + externalBuffer[x] = outBuffer2[x]; + } + buff2New = false; + } + // for (int x = 0; x < externalBufferSize; x++) + // { + // externalBuffer[x] = _userBuffer[_readHead]; + // _readHead++; //Advance the read head + // _readHead %= _userBufferSize; //Wrap if necessary + // } + + return (externalBufferSize); } inline void AP3_PDM::pdm_isr(void) { uint32_t ui32Status; - // // Read the interrupt status. - // am_hal_pdm_interrupt_status_get(_PDMhandle, &ui32Status, true); am_hal_pdm_interrupt_clear(_PDMhandle, ui32Status); - // - // Once our DMA transaction completes, we will disable the PDM and send a - // flag back down to the main routine. Disabling the PDM is only necessary - // because this example only implemented a single buffer for storing FFT - // data. More complex programs could use a system of multiple buffers to - // allow the CPU to run the FFT in one buffer while the DMA pulls PCM data - // into another buffer. - // if (ui32Status & AM_HAL_PDM_INT_DCMP) { - am_hal_pdm_disable(_PDMhandle); - _PDMdataReady = true; + uint32_t tempReadAmt = _pdmBufferSize; + + // if (_writeHead + _pdmBufferSize > _userBufferSize) + // { + // //Goes past the end of our buffer, adjust the amout to read so we hit end of buffer + // tempReadAmt = _userBufferSize - _writeHead; //16384 - 16000 = 384 + // } + + // int i; + // for (i = 0; i < tempReadAmt; i++) + // { + // _userBuffer[_writeHead + i] = _pdmDataBuffer[i]; + // } + + // _writeHead += tempReadAmt; //Advance the head + // _writeHead %= _userBufferSize; //Wrap the head + + // if (tempReadAmt < _pdmBufferSize) + // { + // //Finish the read where i had left off + // for (; i < _pdmBufferSize; i++) + // { + // _userBuffer[i - tempReadAmt] = _pdmDataBuffer[i]; + // } + + // _writeHead += _pdmBufferSize - tempReadAmt; + // } + //Check for overflow + //if (_writeHead + pdmBufferSize + + //Store in the first available buffer + if (buff1New == false) + { + for (int i = 0; i < _pdmBufferSize; i++) + { + outBuffer1[i] = pi16Buffer[i]; + } + buff1New = true; + } + else if (buff2New == false) + { + for (int i = 0; i < _pdmBufferSize; i++) + { + outBuffer2[i] = pi16Buffer[i]; + } + buff2New = true; + } + else + { + _overrun = true; + //Used for debugging + Serial.println("\n\rOver flow!"); + while (1) + ; + } + + //Start next conversion + am_hal_pdm_dma_start(_PDMhandle, &sTransfer); } } diff --git a/libraries/PDM/src/PDM.h b/libraries/PDM/src/PDM.h index 586bbe6..fe9d0d8 100644 --- a/libraries/PDM/src/PDM.h +++ b/libraries/PDM/src/PDM.h @@ -95,8 +95,11 @@ const am_hal_pdm_config_t ap3_pdm_config_default = { class AP3_PDM { public: + //AP3_PDM(uint16_t *userBuffer, uint32_t bufferSize); + bool begin(ap3_gpio_pin_t pinPDMData = MIC_DATA, ap3_gpio_pin_t pinPDMClock = MIC_CLOCK); - bool available(void); //Goes true once an interrupt has occured + bool available(void); //Goes true if circular buffer is not empty + bool isOverrun(void); //Goes true if head crosses tail bool setClockSpeed(am_hal_pdm_clkspd_e clockSpeed); am_hal_pdm_clkspd_e getClockSpeed(); @@ -114,7 +117,7 @@ class AP3_PDM bool updateConfig(am_hal_pdm_config_t newConfiguration); - void getData(uint32_t *PDMDataBuffer, uint32_t bufferSize); + uint32_t getData(uint16_t *externalBuffer, uint32_t bufferSize); void pdm_isr(void); @@ -126,7 +129,25 @@ class AP3_PDM ap3_err_t _begin(void); - volatile bool _PDMdataReady = false; + //volatile bool _PDMdataReady = false; + + am_hal_pdm_transfer_t sTransfer; + + // uint32_t _userBufferSize = 0; + // uint16_t *_userBuffer; + + // volatile uint32_t _writeHead = 0; + // volatile uint32_t _readHead = 0; + volatile bool _overrun = false; + +#define _pdmBufferSize 4096 //Default is array of 4096 * 32bit + volatile uint32_t _pdmDataBuffer[_pdmBufferSize]; + int16_t *pi16Buffer = (int16_t *)_pdmDataBuffer; + + volatile int16_t outBuffer1[_pdmBufferSize]; + volatile int16_t outBuffer2[_pdmBufferSize]; + volatile int buff1New = false; + volatile int buff2New = false; }; #endif //_PDM_H_ \ No newline at end of file