text: #include "dma_rec.h"
  
  enum {ADC_BUF_LEN = BUF_SIZE};    // Size of one of the DMA double buffers
  static const int debug_pin = LED_BUILTIN; // Toggles each DAC ISR (if DEBUG is set to 1)
  
  // DMAC descriptor structure
  typedef struct {
    uint16_t btctrl;
    uint16_t btcnt;
    uint32_t srcaddr;
    uint32_t dstaddr;
    uint32_t descaddr;
  } dmacdescriptor ;
  
  // Globals - DMA and ADC
  volatile uint8_t recording = 0;
  volatile static bool record_ready = false;
  
  uint16_t adc_buf_0[ADC_BUF_LEN];    // ADC results array 0
  uint16_t adc_buf_1[ADC_BUF_LEN];    // ADC results array 1
  volatile dmacdescriptor wrb[DMAC_CH_NUM] __attribute__ ((aligned (16)));          // Write-back DMAC descriptors
  dmacdescriptor descriptor_section[DMAC_CH_NUM] __attribute__ ((aligned (16)));    // DMAC channel descriptors
  dmacdescriptor descriptor __attribute__ ((aligned (16)));                         // Place holder descriptor
  
  int16_t audio_buffer[BUF_SIZE];
  bool buf_ready = false;
  
  //High pass butterworth filter order=1 alpha1=0.0125 
  FilterBuHp::FilterBuHp()
  {
    v[0]=0.0;
  }
  
  float FilterBuHp::step(float x)
  {
    v[0] = v[1];
    v[1] = (9.621952458291035404e-1f * x) + (0.92439049165820696974f * v[0]);
    return (v[1] - v[0]);
  }
  
  FilterBuHp filter;
  
  
  /*******************************************************************************
   * Interrupt Service Routines (ISRs)
   */
  
  /**
   * @brief      Copy sample data in selected buf and signal ready when buffer is full
   *
   * @param[in]  *buf  Pointer to source buffer
   * @param[in]  buf_len  Number of samples to copy from buffer
   */
  static void audio_rec_callback(uint16_t *buf, uint32_t buf_len) {
  
    // Copy samples from DMA buffer to inference buffer
    if (recording) {
      for (uint32_t i = 0; i < buf_len; i++) {
    
        // Convert 12-bit unsigned ADC value to 16-bit PCM (signed) audio value
        audio_buffer[i++] = filter.step((int16_t)(buf[i] - 1024) * 16);
  
        buf_ready = true;
       
      }
    }
  }
  
  /**
   * Interrupt Service Routine (ISR) for DMAC 1
   */
  void DMAC_1_Handler() {
  
    static uint8_t count = 0;
  
    // Check if DMAC channel 1 has been suspended (SUSP)
    if (DMAC->Channel[1].CHINTFLAG.bit.SUSP) {
  
       // Debug: make pin high before copying buffer
  #ifdef DEBUG
      digitalWrite(debug_pin, HIGH);
  #endif
  
      // Restart DMAC on channel 1 and clear SUSP interrupt flag
      DMAC->Channel[1].CHCTRLB.reg = DMAC_CHCTRLB_CMD_RESUME;
      DMAC->Channel[1].CHINTFLAG.bit.SUSP = 1;
  
      // See which buffer has filled up, and dump results into large buffer
      if (count) {
        audio_rec_callback(adc_buf_0, ADC_BUF_LEN);
      } else {
        audio_rec_callback(adc_buf_1, ADC_BUF_LEN);
      }
  
      // Flip to next buffer
      count = (count + 1) % 2;
  
      // Debug: make pin low after copying buffer
  #ifdef DEBUG
      digitalWrite(debug_pin, LOW);
  #endif
    }
  }
  
  /*******************************************************************************
   * Functions
   */
  // This is all based on MartinL's work from these two posts:
  // https://forum.arduino.cc/index.php?topic=685347.0
  // and https://forum.arduino.cc/index.php?topic=709104.0
  void config_dma_adc() {
    
    // Configure DMA to sample from ADC at a regular interval (triggered by timer/counter)
    DMAC->BASEADDR.reg = (uint32_t)descriptor_section;                          // Specify the location of the descriptors
    DMAC->WRBADDR.reg = (uint32_t)wrb;                                          // Specify the location of the write back descriptors
    DMAC->CTRL.reg = DMAC_CTRL_DMAENABLE | DMAC_CTRL_LVLEN(0xf);                // Enable the DMAC peripheral
    DMAC->Channel[1].CHCTRLA.reg = DMAC_CHCTRLA_TRIGSRC(TC5_DMAC_ID_OVF) |      // Set DMAC to trigger on TC5 timer overflow
                                   DMAC_CHCTRLA_TRIGACT_BURST;                  // DMAC burst transfer
                                   
    descriptor.descaddr = (uint32_t)&descriptor_section[1];                     // Set up a circular descriptor
    descriptor.srcaddr = (uint32_t)&ADC1->RESULT.reg;                           // Take the result from the ADC0 RESULT register
    descriptor.dstaddr = (uint32_t)adc_buf_0 + sizeof(uint16_t) * ADC_BUF_LEN;  // Place it in the adc_buf_0 array
    descriptor.btcnt = ADC_BUF_LEN;                                             // Beat count
    descriptor.btctrl = DMAC_BTCTRL_BEATSIZE_HWORD |                            // Beat size is HWORD (16-bits)
                        DMAC_BTCTRL_DSTINC |                                    // Increment the destination address
                        DMAC_BTCTRL_VALID |                                     // Descriptor is valid
                        DMAC_BTCTRL_BLOCKACT_SUSPEND;                           // Suspend DMAC channel 0 after block transfer
    memcpy(&descriptor_section[0], &descriptor, sizeof(descriptor));            // Copy the descriptor to the descriptor section
    
    descriptor.descaddr = (uint32_t)&descriptor_section[0];                     // Set up a circular descriptor
    descriptor.srcaddr = (uint32_t)&ADC1->RESULT.reg;                           // Take the result from the ADC0 RESULT register
    descriptor.dstaddr = (uint32_t)adc_buf_1 + sizeof(uint16_t) * ADC_BUF_LEN;  // Place it in the adc_buf_1 array
    descriptor.btcnt = ADC_BUF_LEN;                                             // Beat count
    descriptor.btctrl = DMAC_BTCTRL_BEATSIZE_HWORD |                            // Beat size is HWORD (16-bits)
                        DMAC_BTCTRL_DSTINC |                                    // Increment the destination address
                        DMAC_BTCTRL_VALID |                                     // Descriptor is valid
                        DMAC_BTCTRL_BLOCKACT_SUSPEND;                           // Suspend DMAC channel 0 after block transfer
    memcpy(&descriptor_section[1], &descriptor, sizeof(descriptor));            // Copy the descriptor to the descriptor section
  
    // Configure NVIC
    NVIC_SetPriority(DMAC_1_IRQn, 0);    // Set the Nested Vector Interrupt Controller (NVIC) priority for DMAC1 to 0 (highest)
    NVIC_EnableIRQ(DMAC_1_IRQn);         // Connect DMAC1 to Nested Vector Interrupt Controller (NVIC)
  
    // Activate the suspend (SUSP) interrupt on DMAC channel 1
    DMAC->Channel[1].CHINTENSET.reg = DMAC_CHINTENSET_SUSP;
  
    // Configure ADC
    ADC1->INPUTCTRL.bit.MUXPOS = ADC_INPUTCTRL_MUXPOS_AIN12_Val; // Set the analog input to ADC0/AIN2 (PB08 - A4 on Metro M4)
    while(ADC1->SYNCBUSY.bit.INPUTCTRL);                // Wait for synchronization
    ADC1->SAMPCTRL.bit.SAMPLEN = 0x00;                  // Set max Sampling Time Length to half divided ADC clock pulse (2.66us)
    while(ADC1->SYNCBUSY.bit.SAMPCTRL);                 // Wait for synchronization 
    ADC1->CTRLA.reg = ADC_CTRLA_PRESCALER_DIV128;       // Divide Clock ADC GCLK by 128 (48MHz/128 = 375kHz)
    ADC1->CTRLB.reg = ADC_CTRLB_RESSEL_12BIT |          // Set ADC resolution to 12 bits
                      ADC_CTRLB_FREERUN;                // Set ADC to free run mode       
    while(ADC1->SYNCBUSY.bit.CTRLB);                    // Wait for synchronization
    ADC1->CTRLA.bit.ENABLE = 1;                         // Enable the ADC
    while(ADC1->SYNCBUSY.bit.ENABLE);                   // Wait for synchronization
    ADC1->SWTRIG.bit.START = 1;                         // Initiate a software trigger to start an ADC conversion
    while(ADC1->SYNCBUSY.bit.SWTRIG);                   // Wait for synchronization
  
    // Enable DMA channel 1
    DMAC->Channel[1].CHCTRLA.bit.ENABLE = 1;
  
    // Configure Timer/Counter 5
    GCLK->PCHCTRL[TC5_GCLK_ID].reg = GCLK_PCHCTRL_CHEN |        // Enable perhipheral channel for TC5
                                     GCLK_PCHCTRL_GEN_GCLK1;    // Connect generic clock 0 at 48MHz
     
    TC5->COUNT16.WAVE.reg = TC_WAVE_WAVEGEN_MFRQ;               // Set TC5 to Match Frequency (MFRQ) mode
    TC5->COUNT16.CC[0].reg = 3000 - 1;                          // Set the trigger to 16 kHz: (4Mhz / 16000) - 1
    while (TC5->COUNT16.SYNCBUSY.bit.CC0);                      // Wait for synchronization
  
    // Start Timer/Counter 5
    TC5->COUNT16.CTRLA.bit.ENABLE = 1;                          // Enable the TC5 timer
    while (TC5->COUNT16.SYNCBUSY.bit.ENABLE);                   // Wait for synchronization
  }
  