Skip to content

Instantly share code, notes, and snippets.

@nseidle
Last active June 2, 2022 09:23
Show Gist options
  • Save nseidle/636e23bdac566cc6fe6d703116983568 to your computer and use it in GitHub Desktop.
Save nseidle/636e23bdac566cc6fe6d703116983568 to your computer and use it in GitHub Desktop.
PDM Audio harvester to serial
/* 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 sektch 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.
*/
#include "am_bsp.h"
// ----------------
// Global Variables
// ----------------
#define BUFFSIZE 512
uint32_t PDMDataBuffer[BUFFSIZE];
int16_t *pi16Buffer = (int16_t *) PDMDataBuffer;
volatile int16_t outBuffer1[BUFFSIZE];
volatile int16_t outBuffer2[BUFFSIZE];
volatile int buff1New = false;
volatile int buff2New = false;
bool newData = false;
am_hal_pdm_transfer_t sTransfer;
// -----------------
// 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,
};
// -----------------
// PDM Configuration
// -----------------
void pdm_init(void) {
// Initialize, power-up and configure PDM
am_hal_pdm_initialize(0, &PDMHandle);
am_hal_pdm_power_control(PDMHandle, AM_HAL_PDM_POWER_ON, false);
am_hal_pdm_configure(PDMHandle, &newConfig);
am_hal_pdm_enable(PDMHandle);
// Configure PDM pins
am_hal_gpio_pinconfig(AM_BSP_PDM_DATA, g_AM_BSP_PDM_DATA);
am_hal_gpio_pinconfig(AM_BSP_PDM_CLOCK, g_AM_BSP_PDM_CLOCK);
// Configure PDM interrupts - set 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));
// Enable PDM interrupt
NVIC_EnableIRQ(PDM_IRQn);
}
// ----------------------------
// Get Data from PDM Microphone
// ----------------------------
void pdm_data_start(void) {
// Configure DMA and target address.
sTransfer.ui32TargetAddr = (uint32_t ) PDMDataBuffer;
sTransfer.ui32TotalCount = BUFFSIZE * 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);
}
void pdm_data_stop(void) {
am_hal_pdm_disable(PDMHandle);
}
// -----------------------------
// PDM Interrupt Service Routine
// -----------------------------
extern "C" void am_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 DMA transaction completes, move to Queue & Start next conversion
if (ui32Status & AM_HAL_PDM_INT_DCMP) {
// for (int i = 0; i < BUFFSIZE; i++) {
// AudioQueue.push(pi16Buffer[i]);
// }
//Store in the first available buffer
if (buff1New == false)
{
for (int i = 0; i < BUFFSIZE; i++) {
outBuffer1[i] = pi16Buffer[i];
}
buff1New = true;
}
else if (buff2New == false)
{
for (int i = 0; i < BUFFSIZE; i++) {
outBuffer2[i] = pi16Buffer[i];
}
buff2New = true;
}
else
{
//Used for debugging
Serial.println("Over flow!");
}
newData = true;
// Start next conversion
am_hal_pdm_dma_start(PDMHandle, &sTransfer);
}
}
void setup() {
Serial.begin(500000);
delay(10);
pdm_init();
pdm_data_start();
}
void loop() {
processFrame();
}
void processFrame() {
//Grab up to 512 bytes from circular buffer and print them
// if (AudioQueue.isEmpty() == false) {
// uint16_t tempBuff[512];
// int counter = 0;
// while (counter < 512)
// {
// if (AudioQueue.isEmpty())
// {
// Serial.write((uint8_t*)tempBuff, counter);
// delay(2);
// break;
// }
// tempBuff[counter++] = AudioQueue.pop();
// }
// }
if (newData == true)
{
if (buff1New == true)
{
Serial.write((uint8_t*)outBuffer1, sizeof(outBuffer1));
buff1New = false;
}
if (buff2New == true)
{
Serial.write((uint8_t*)outBuffer2, sizeof(outBuffer2));
buff2New = false;
}
newData = false;
}
}
@sayederfanarefin
Copy link

May I have a reference to the sample python script, that visualizes, and coverts the raw data to a WAV file?

@nseidle
Copy link
Author

nseidle commented Jul 2, 2020

@sayederfanarefin
Copy link

Thank you so much! I have another query. I am trying to interface a PDM mic for ultrasonic sound. Can this provide higher clock for the PDM mic? It requires above 2MHz clock speed for ultrasonic mode. Also, If I want to capture 40k Hz sound, I need to sample it to at least 80k. Is it possible? Please let me know. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment