microPAM software#

The microPAM software differs from the software presented for the Teensy 4.1 version

  • No use of Teensy audio library

  • complete 32 bit data storage

  • RP2040 specific implementation of the I2S interface

  • Use of dedicated real-time-clock for RP2040

  • use of SPI for disk access

The software is work-in-progress and will be maintained on WMXZ-EU/microPAM in subdirectory microPAM_V2.

The software can run on on both hardware systems: the microPAM-T4 and the micrPAM-pico.

Also, the V2 software is developed from scratch with the rp2040 MCU (symbol TARGET_RP2040) as primary processor and the Teensy 4.1 (symbol __IMXRT1062__ ) as a alternative processor. Consequently, the main functionality may differ from the dedicated mocroPAM-T4 software.

Arduino environment#

to facilitate the development of microPAM for the RP-2040 MCU, the *.ino file is maintained containing setup() and loop() functions.

microPAM_V2.ino#

The main software that is here in microPAM_V2.ino contains

Preparations#

#include "Arduino.h"
#if defined(__IMXRT1062__)
  #include <CrashReport.h>
#endif

#if defined(TARGET_RP2040)
  #include "pico/stdlib.h"
#endif

#include "src/mConfig.h"
#include "src/mQueue.h"
#include "src/mAcq.h"
#include "src/mRTC.h"
#include "src/mCompress.h"
#include "src/mFiling.h"
#include "src/menu.h"

#if defined(__IMXRT1062__) && defined(AUDIO_INTERFACE)
  #include "AudioStream.h"
  #include "usb_audio.h"
  #include "src/mAudioTrigger.h"
  #include "src/mAudioIF.h"

  AudioTrigger trigger;
  AudioIF         acqIF(FSAMP);
  AudioOutputUSB  usb; 
  AudioConnection patchCord1(acqIF, 0, usb, 0);
  AudioConnection patchCord2(acqIF, 1, usb, 1);
#endif

In case of use of Teensy and selection of “Serial + Midi + Audio” in “Tools > USB type” the AUDIO_INTERFACE symbol is declared and data are streamed to USB audio. This allows to listen to the microphone from PC or to use program like Audacity to visualize microphone data

Setup function#

/***************************************************************************/
volatile int ready=0;
void setup1();
//
void setup() 
{
  // put your setup code here, to run once:
  #if defined(TARGET_RP2040)
    set_sys_clock_khz(48000, true);
  #endif

  Serial.begin(115200);
  // wait for a minute to allow USB-Serial connection
  while(millis()<60000) if(Serial) break;

  // Teensy has a crash report
  #if defined(__IMXRT1062__)
    if(CrashReport) Serial.print(CrashReport);
    #if defined(AUDIO_INTERFACE)
      AudioMemory(8);
    #endif
  #endif

  rtc_setup();
  Serial.println("rtc_setup() done");

  datetime_t t;
  if(!rtc_get_datetime(&t)) Serial.println("failing get_datetime");
  Serial.printf("RTC-main: %4d-%02d-%02d %02d:%02d:%02d",
                      t.year,t.month,t.day,t.hour,t.min,t.sec); Serial.println();

  Serial.println("checking rtc_get()");
  time2date(rtc_get(), &t);
  Serial.printf("Now-sec: %4d-%02d-%02d %02d:%02d:%02d",
                      t.year,t.month,t.day,t.hour,t.min,t.sec); Serial.println();
  Serial.print("Week Day (may not be correct)"); Serial.println(t.dotw);

  Serial.println("filing_init");
  filing_init();
  Serial.println("Setup done");
  ready=1;

  // in case of single core teensy 4.1 start acquisition
  #if defined(__IMXRT1062__)
    setup1();
  #endif
}

The setup is special as the RP2040 is a dual core MCU and the acquisition functionality is delegated to the 2nd core. In case of Teensy, this 2nd setup1() is callend at the end of the regular setup().

Loop function#

void loop() 
{
  // put your main code here, to run repeatedly:
  static uint32_t loopCount=0;
  loopCount++;
  static int16_t monitor=0;

  // obtain some statistics on Queue usage
  static uint16_t mxb=0;
  uint16_t nb = getDataCount();
  if(nb>mxb) mxb=nb;

  static volatile int16_t status=CLOSED;
  // basic menu to start and stop archiving  
  if(Serial.available())
  {
    char ch=Serial.read();
    if(ch=='s') status=CLOSED;
    if(ch=='e') status=MUSTSTOP;
    if(ch=='m') monitor=1-monitor;
    if(ch==':') menu1(); // returns only when menu1 gets not handled character
    if(ch=='?') menu2(); // returns only when menu2 gets not handled character
    if(ch=='!') menu3(); // returns only when menu3 gets not handled character
  }

  // save data (filing will be handled inside saveData)
  status=saveData(status);  

//  if(status<0) return;
  // once a second provide some information to User
  static uint32_t t0=0;
  uint32_t t1;
  if((t1=millis())>(t0+1000))
  { datetime_t t;
    rtc_get_datetime(&t);

    if(monitor)
    {
      Serial.printf("\n%4d-%02d-%02d %02d:%02d:%02d %d",
                    t.year,t.month,t.day,t.hour,t.min,t.sec,t.dotw); Serial.print(" : ");

      Serial.print(loopCount); Serial.print(" ");
      Serial.print(procCount); Serial.print(" ");
      Serial.print(procMiss); Serial.print(" ");
      Serial.printf("%3d",mxb); Serial.print("  ");
      Serial.printf("%4d",acqbias); Serial.print(" ");
      Serial.print(disk_count); Serial.print("  ; ");

      #if PROC_MODE==0
        for(int ii=0; ii<8;ii++){ Serial.printf("%8X ",logBuffer[ii]);}
      #else
        for(int ii=0; ii<MB;ii++){ Serial.printf("%2d ",proc_stat[ii]);}
        Serial.printf("%2d",max_stat);

        for(int ii=0; ii<MB;ii++){ proc_stat[ii]=0;}
        max_stat=0;
      #endif
    }

    loopCount=0;
    procCount=0;
    procMiss=0;
    mxb=0;
    disk_count=0;

    t0=t1;
  }
}

Second core#

// rp2040 has dial core. let acq run on its own core
void setup1()
{ while(!ready) {delay(1);} // wait for setup() to finish
  i2s_setup();
  dma_setup();

  Serial.println("Setup1 done");
}

void loop1(){}  // nothing to be done here

As mentioned above, the aquisition is started within the second core of RP2040, or in case of Teensy at the end of promary setup.