Created
July 26, 2019 02:50
-
-
Save nseidle/8dcbc1ba3cf2de63f977ac93e1d82f57 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Author: Nathan Seidle | |
Created: July 24, 2019 | |
License: MIT. See SparkFun Arduino Apollo3 Project for more information | |
This example demonstrates how to use the PDM microphone on Artemis boards. | |
*/ | |
#define ARM_MATH_CM4 | |
#include <arm_math.h> | |
//***************************************************************************** | |
// | |
// Example parameters. | |
// | |
//***************************************************************************** | |
#define PDM_FFT_SIZE 4096 | |
#define PDM_FFT_BYTES (PDM_FFT_SIZE * 2) | |
#define PRINT_PDM_DATA 0 | |
#define PRINT_FFT_DATA 0 | |
//***************************************************************************** | |
// | |
// Global variables. | |
// | |
//***************************************************************************** | |
uint32_t g_ui32PDMDataBuffer[PDM_FFT_SIZE]; | |
float g_fPDMTimeDomain[PDM_FFT_SIZE * 2]; | |
float g_fPDMFrequencyDomain[PDM_FFT_SIZE * 2]; | |
float g_fPDMMagnitudes[PDM_FFT_SIZE * 2]; | |
uint32_t g_ui32SampleFreq; | |
#include <AP3_PDM.H> | |
AP3_PDM myPDM; | |
//extern "C" void am_pdm_isr(void) | |
//{ | |
// uint32_t ui32Status; | |
// | |
// //Serial.println("P"); | |
// | |
// // | |
// // Read the interrupt status. | |
// // | |
// am_hal_pdm_interrupt_status_get(myPDM._PDMhandle, &ui32Status, true); | |
// am_hal_pdm_interrupt_clear(myPDM._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(myPDM._PDMhandle); | |
// myPDM._PDMdataReady = true; | |
// } | |
//} | |
void setup() | |
{ | |
Serial.begin(9600); | |
Serial.println("SparkFun PDM Example"); | |
// | |
// Turn on the PDM, set it up for our chosen recording settings, and start | |
// the first DMA transaction. | |
// | |
//pdm_init(); | |
if(myPDM.begin(22, 23) == false) //Data, clock - 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); | |
} | |
Serial.println("PDM Initialized"); | |
pdm_config_print(); | |
am_hal_pdm_fifo_flush(myPDM._PDMhandle); | |
pdm_data_get(); | |
} | |
void loop() | |
{ | |
am_hal_interrupt_master_disable(); | |
if (myPDM.available()) | |
{ | |
myPDM._PDMdataReady = false; | |
pcm_fft_print(); | |
while (PRINT_PDM_DATA || PRINT_FFT_DATA); | |
// | |
// Start converting the next set of PCM samples. | |
// | |
pdm_data_get(); | |
} | |
// | |
// Go to Deep Sleep. | |
// | |
am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP); | |
am_hal_interrupt_master_enable(); | |
} | |
//***************************************************************************** | |
// | |
// Start a transaction to get some number of bytes from the PDM interface. | |
// | |
//***************************************************************************** | |
void | |
pdm_data_get(void) | |
{ | |
// | |
// Configure DMA and target address. | |
// | |
am_hal_pdm_transfer_t sTransfer; | |
sTransfer.ui32TargetAddr = (uint32_t ) g_ui32PDMDataBuffer; | |
sTransfer.ui32TotalCount = PDM_FFT_BYTES; | |
// | |
// Start the data transfer. | |
// | |
am_hal_pdm_enable(myPDM._PDMhandle); | |
am_util_delay_ms(100); | |
am_hal_pdm_fifo_flush(myPDM._PDMhandle); | |
am_hal_pdm_dma_start(myPDM._PDMhandle, &sTransfer); | |
} | |
//***************************************************************************** | |
// | |
// Analyze and print frequency data. | |
// | |
//***************************************************************************** | |
void | |
pcm_fft_print(void) | |
{ | |
float fMaxValue; | |
uint32_t ui32MaxIndex; | |
int16_t *pi16PDMData = (int16_t *) g_ui32PDMDataBuffer; | |
uint32_t ui32LoudestFrequency; | |
// | |
// Convert the PDM samples to floats, and arrange them in the format | |
// required by the FFT function. | |
// | |
for (uint32_t i = 0; i < PDM_FFT_SIZE; i++) | |
{ | |
if (PRINT_PDM_DATA) | |
{ | |
Serial.printf("%d\n", pi16PDMData[i]); | |
} | |
g_fPDMTimeDomain[2 * i] = pi16PDMData[i] / 1.0; | |
g_fPDMTimeDomain[2 * i + 1] = 0.0; | |
} | |
if (PRINT_PDM_DATA) | |
{ | |
Serial.printf("END\n"); | |
} | |
// | |
// Perform the FFT. | |
// | |
arm_cfft_radix4_instance_f32 S; | |
arm_cfft_radix4_init_f32(&S, PDM_FFT_SIZE, 0, 1); | |
arm_cfft_radix4_f32(&S, g_fPDMTimeDomain); | |
arm_cmplx_mag_f32(g_fPDMTimeDomain, g_fPDMMagnitudes, PDM_FFT_SIZE); | |
if (PRINT_FFT_DATA) | |
{ | |
for (uint32_t i = 0; i < PDM_FFT_SIZE / 2; i++) | |
{ | |
Serial.printf("%f\n", g_fPDMMagnitudes[i]); | |
} | |
Serial.printf("END\n"); | |
} | |
// | |
// Find the frequency bin with the largest magnitude. | |
// | |
arm_max_f32(g_fPDMMagnitudes, PDM_FFT_SIZE / 2, &fMaxValue, &ui32MaxIndex); | |
ui32LoudestFrequency = (g_ui32SampleFreq * ui32MaxIndex) / PDM_FFT_SIZE; | |
if (PRINT_FFT_DATA) | |
{ | |
Serial.printf("Loudest frequency bin: %d\n", ui32MaxIndex); | |
} | |
Serial.printf("Loudest frequency: %d \n", ui32LoudestFrequency); | |
} | |
//***************************************************************************** | |
// | |
// PDM initialization. | |
// | |
//***************************************************************************** | |
//void pdm_init(void) { | |
// | |
// // | |
// // Initialize, power-up, and configure the PDM. | |
// // | |
// am_hal_pdm_initialize(0, &PDMHandle); | |
// am_hal_pdm_power_control(PDMHandle, AM_HAL_PDM_POWER_ON, false); | |
// am_hal_pdm_configure(PDMHandle, &g_sPdmConfig); | |
// am_hal_pdm_enable(PDMHandle); | |
// | |
// // | |
// // Configure the necessary pins. | |
// // | |
// | |
// //Configure pins as AM_HAL_GPIO_PIN_POWERSW_NONE, AM_HAL_GPIO_PIN_PULLUP_NONE, etc. | |
// am_hal_gpio_pincfg_t sPinCfg = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; | |
// | |
// sPinCfg.uFuncSel = AM_HAL_PIN_36_PDMDATA; | |
// am_hal_gpio_pinconfig(36, sPinCfg); | |
// | |
// sPinCfg.uFuncSel = AM_HAL_PIN_37_PDMCLK; | |
// am_hal_gpio_pinconfig(37, sPinCfg); | |
// | |
// // | |
// // Configure and enable PDM interrupts (set up to trigger on DMA | |
// // 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(); | |
// NVIC_EnableIRQ(PDM_IRQn); | |
//} | |
//***************************************************************************** | |
// | |
// Print PDM configuration data. | |
// | |
//***************************************************************************** | |
void | |
pdm_config_print(void) | |
{ | |
uint32_t ui32PDMClk; | |
uint32_t ui32MClkDiv; | |
float fFrequencyUnits; | |
// | |
// Read the config structure to figure out what our internal clock is set | |
// to. | |
// | |
switch (myPDM._PDMconfig.eClkDivider) | |
{ | |
case AM_HAL_PDM_MCLKDIV_4: ui32MClkDiv = 4; break; | |
case AM_HAL_PDM_MCLKDIV_3: ui32MClkDiv = 3; break; | |
case AM_HAL_PDM_MCLKDIV_2: ui32MClkDiv = 2; break; | |
case AM_HAL_PDM_MCLKDIV_1: ui32MClkDiv = 1; break; | |
default: | |
ui32MClkDiv = 0; | |
} | |
switch (myPDM._PDMconfig.ePDMClkSpeed) | |
{ | |
case AM_HAL_PDM_CLK_12MHZ: ui32PDMClk = 12000000; break; | |
case AM_HAL_PDM_CLK_6MHZ: ui32PDMClk = 6000000; break; | |
case AM_HAL_PDM_CLK_3MHZ: ui32PDMClk = 3000000; break; | |
case AM_HAL_PDM_CLK_1_5MHZ: ui32PDMClk = 1500000; break; | |
case AM_HAL_PDM_CLK_750KHZ: ui32PDMClk = 750000; break; | |
case AM_HAL_PDM_CLK_375KHZ: ui32PDMClk = 375000; break; | |
case AM_HAL_PDM_CLK_187KHZ: ui32PDMClk = 187000; break; | |
default: | |
ui32PDMClk = 0; | |
} | |
// | |
// Record the effective sample frequency. We'll need it later to print the | |
// loudest frequency from the sample. | |
// | |
g_ui32SampleFreq = (ui32PDMClk / | |
(ui32MClkDiv * 2 * myPDM._PDMconfig.ui32DecimationRate)); | |
fFrequencyUnits = (float) g_ui32SampleFreq / (float) PDM_FFT_SIZE; | |
Serial.printf("Settings:\n"); | |
Serial.printf("PDM Clock (Hz): %12d\n", ui32PDMClk); | |
Serial.printf("Decimation Rate: %12d\n", myPDM._PDMconfig.ui32DecimationRate); | |
Serial.printf("Effective Sample Freq.: %12d\n", g_ui32SampleFreq); | |
Serial.printf("FFT Length: %12d\n\n", PDM_FFT_SIZE); | |
Serial.printf("FFT Resolution: %15.3f Hz\n", fFrequencyUnits); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment