Skip to content

Instantly share code, notes, and snippets.

@nseidle
Created July 26, 2019 02:50
Show Gist options
  • Save nseidle/8dcbc1ba3cf2de63f977ac93e1d82f57 to your computer and use it in GitHub Desktop.
Save nseidle/8dcbc1ba3cf2de63f977ac93e1d82f57 to your computer and use it in GitHub Desktop.
/* 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