text: //*****************************************************************************
  //
  //! @file pdm_fft.c
  //!
  //! @brief An example to show basic PDM operation.
  //!
  //! Purpose: This example enables the PDM interface to record audio signals from an
  //! external microphone. The required pin connections are:
  //!
  //! Printing takes place over the ITM at 1M Baud.
  //!
  //! GPIO 10 - PDM DATA
  //! GPIO 11 - PDM CLK
  //
  //*****************************************************************************
  
  //*****************************************************************************
  //
  // Copyright (c) 2019, Ambiq Micro
  // All rights reserved.
  // 
  // Redistribution and use in source and binary forms, with or without
  // modification, are permitted provided that the following conditions are met:
  // 
  // 1. Redistributions of source code must retain the above copyright notice,
  // this list of conditions and the following disclaimer.
  // 
  // 2. Redistributions in binary form must reproduce the above copyright
  // notice, this list of conditions and the following disclaimer in the
  // documentation and/or other materials provided with the distribution.
  // 
  // 3. Neither the name of the copyright holder nor the names of its
  // contributors may be used to endorse or promote products derived from this
  // software without specific prior written permission.
  // 
  // Third party software included in this distribution is subject to the
  // additional license terms as defined in the /docs/licenses directory.
  // 
  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
  // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  // POSSIBILITY OF SUCH DAMAGE.
  //
  // This is part of revision v2.2.0-7-g63f7c2ba1 of the AmbiqSuite Development Package.
  //
  //*****************************************************************************
  
  #define ARM_MATH_CM4
  #include <arm_math.h>
  
  #include "am_mcu_apollo.h"
  #include "am_bsp.h"
  #include "am_util.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.
  //
  //*****************************************************************************
  volatile bool g_bPDMDataReady = false;
  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;
  
  //*****************************************************************************
  //
  // PDM configuration information.
  //
  //*****************************************************************************
  void *PDMHandle;
  
  am_hal_pdm_config_t g_sPdmConfig =
  {
      .eClkDivider = AM_HAL_PDM_MCLKDIV_1,
      .eLeftGain = AM_HAL_PDM_GAIN_0DB,
      .eRightGain = AM_HAL_PDM_GAIN_0DB,
      .ui32DecimationRate = 64,
      .bHighPassEnable = 0,
      .ui32HighPassCutoff = 0xB,
      .ePDMClkSpeed = AM_HAL_PDM_CLK_6MHZ,
      .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 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.
      //
      am_hal_gpio_pincfg_t sPinCfg = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
  
      sPinCfg.uFuncSel = AM_HAL_PIN_11_PDMDATA;
      am_hal_gpio_pinconfig(11, sPinCfg);
  
      sPinCfg.uFuncSel = AM_HAL_PIN_12_PDMCLK;
      am_hal_gpio_pinconfig(12, sPinCfg);
  
      am_hal_gpio_state_write(14, AM_HAL_GPIO_OUTPUT_CLEAR);
      am_hal_gpio_pinconfig(14, g_AM_HAL_GPIO_OUTPUT);
  
      //
      // 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));
  
      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 (g_sPdmConfig.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 (g_sPdmConfig.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 * g_sPdmConfig.ui32DecimationRate));
  
      fFrequencyUnits = (float) g_ui32SampleFreq / (float) PDM_FFT_SIZE;
  
      am_util_stdio_printf("Settings:\n");
      am_util_stdio_printf("PDM Clock (Hz):         %12d\n", ui32PDMClk);
      am_util_stdio_printf("Decimation Rate:        %12d\n", g_sPdmConfig.ui32DecimationRate);
      am_util_stdio_printf("Effective Sample Freq.: %12d\n", g_ui32SampleFreq);
      am_util_stdio_printf("FFT Length:             %12d\n\n", PDM_FFT_SIZE);
      am_util_stdio_printf("FFT Resolution: %15.3f Hz\n", fFrequencyUnits);
  }
  
  //*****************************************************************************
  //
  // 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(PDMHandle);
      am_util_delay_ms(100);
      am_hal_pdm_fifo_flush(PDMHandle);
      am_hal_pdm_dma_start(PDMHandle, &sTransfer);
  }
  
  //*****************************************************************************
  //
  // PDM interrupt handler.
  //
  //*****************************************************************************
  void
  am_pdm0_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);
          g_bPDMDataReady = true;
      }
  }
  
  //*****************************************************************************
  //
  // 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)
          {
              am_util_stdio_printf("%d\n", pi16PDMData[i]);
          }
  
          g_fPDMTimeDomain[2 * i] = pi16PDMData[i] / 1.0;
          g_fPDMTimeDomain[2 * i + 1] = 0.0;
      }
  
      if (PRINT_PDM_DATA)
      {
          am_util_stdio_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++)
          {
              am_util_stdio_printf("%f\n", g_fPDMMagnitudes[i]);
          }
  
          am_util_stdio_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)
      {
          am_util_stdio_printf("Loudest frequency bin: %d\n", ui32MaxIndex);
      }
  
      am_util_stdio_printf("Loudest frequency: %d         \r", ui32LoudestFrequency);
  }
  
  //*****************************************************************************
  //
  // Main
  //
  //*****************************************************************************
  int
  main(void)
  {
      //
      // Perform the standard initialzation for clocks, cache settings, and
      // board-level low-power operation.
      //
      am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_SYSCLK_MAX, 0);
      am_hal_cachectrl_config(&am_hal_cachectrl_defaults);
      am_hal_cachectrl_enable();
      //am_bsp_low_power_init();
  
      //
      // Initialize the printf interface for ITM output
      //
      am_bsp_itm_printf_enable();
  
      //
      // Print the banner.
      //
      am_util_stdio_terminal_clear();
      am_util_stdio_printf("PDM FFT example.\n\n");
  
      //
      // Turn on the PDM, set it up for our chosen recording settings, and start
      // the first DMA transaction.
      //
      pdm_init();
      pdm_config_print();
      am_hal_pdm_fifo_flush(PDMHandle);
      pdm_data_get();
  
      //
      // Loop forever while sleeping.
      //
      while (1)
      {
          am_hal_interrupt_master_disable();
  
          if (g_bPDMDataReady)
          {
              g_bPDMDataReady = 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();
      }
  }
  