External RAM more the the psram for teensy 4.1

Eyal

Member
Hello everyone,:D

I have a system that I need to record about 10 4-byte variables at a pace of 2K[hz] in ~25 minutes duration.

Because of the fast rate of the system, I don't have enough time to wait for the SD card to save the variables because it takes much more than just 500u sec.

(500u sec is the time per cycle of the system).

My plan was to allocate as much memory as I need and then save that to the SD card once the system finished operating.

That means that it needs at least 10x4x2000x60x25 bytes = 120 MegaByte of RAM storage.

The external PSRAM memory chips that you can add to teensy 4.1 are 16 MegaByte which is not enough in my case.

Is there an option to connect external Ram with SPI in order to reach the ~120MegaByte ? if so, do you know what IC chip I can use and where to get it from?
also if you have a code example for using external IC RAM besides the PSRAM that you can add to teensy 4.1 that will be great.

In case that can't be done, I had another idea that I would like to hear your opinions about it. The idea is to try to use Teensy thread library in order to save to the SD card in parallel once one ram chip is full, and in another thread simultaneously to continue reading variables to the ram.
but that idea sounds to me a bit more complex, so I prefer the idea of using more ram, and when the program stops writing all the data to the SD card.

any opinion, information, and advice are welcome:D
 
The audio library Recorder example is able to continuously write a 16 bit, 44.1 kHz audio stream to a file on the SD card. That's 88200 bytes/sec sustained speed, which proves your requirement of 80000 bytes/sec can be done if managed well.
 
wow thank you very much, I'll see the tutorial and try to implement this in my system.
I thought initially that writing to the SD card is the speed limiting factor.

Have a great day :)
 
To explain a bit more, you'll need to acquire your 2 kHz data using an interrupt or DMA or both. The audio library does this with DMA into a circular buffer and an interrupt which copies 128 sample blocks into a pool of buffers.

You'll also need to have a fairly large buffer in RAM for the rare times SD writes spend longer than normal. The audio library does this by a queue interface which allows the main program to read the raw audio data, where additional data which arrives keeps using up more buffers when/if your program takes longer to use it.

In an ideal world of infinite programming hours in every day, we'd have a SD library with non-blocking API, so you could begin a write to the card and later get a function called when it completes, or call a function to check whether the write is still in progress. But with SD & SdFat, writes are blocking. So to get sustained speed, you need interrupts or DMA to collect the incoming data into a fairly large buffer, so you don't lose data while the SD library is waiting on the card to complete writing.

The other minor point is write size. 4096 bytes gives better performance, but pretty much any multiple of 512 will work. The Recorder example uses 512 because it was written years ago before we switched the underlying SD library access to SdFat. With the old SD library (in Teensyduino 1.53 and earlier) everything was done as 512 bytes. For today's libraries, you can get better performance if you give the SD library writes of 4096 bytes. You'll probably want a large buffer of 40K or more, so your program can handle the rare cases where a SD card needs to do media management and is "busy" for a good fraction of a second.
 
On a T4.1 with 16MB ERAM buffer and 33 MHz SPI clock, you can easily record 2.75 MB/s continuously to microSD (about 75% SPI duty circle)
From time to time some buffer will be needed and used when microSD card is 'non-cooperative'
Trick is:
have Acquisition use DMA at elevated priority, write to microSD card in loop() (lowest priority), and as Paul said, use large write buffers (e.g. 32*512 bytes)
 
Thank you both :D That was very insightful.

My project is a quadcopter flight controller. The data acquisition is happening at constant times and then it needs to use this data in the control algorithm to send data to the motors and keep the quadcopter in the air.

So my data acquisition is not happening in interrupts and I use the time between samples to calculate the data I need for the control algorithm, communication, and sending signals to the motors.

When looking into the audio recorder example I notice you used (readBuffer(), memcpy function to get the data from the buffer and check if the 2 paks of 256Bytes are ready) But I didn't find the file that the function was writing in, the library is a bit complex for me. It's not as easy as looking for audio.h and audio.c files. Can you direct me to where I should look to find this function in order to understand better the code?

There is no "dead" time between samples every loop is 500usec and I can use approximately ~150u sec of that time for adding the data to a buffer or to saving to the SD card somehow.
Can the audio library still be a solution in that case?
 
So my data acquisition is not happening in interrupts and I use the time between samples to calculate the data I need for the control algorithm, communication, and sending signals to the motors.
IMO, you should redesign your approach to use interrupts even if that happens 2000 times per second. this way you can do your 150 us processing at interrupt level, accumulate your data at a write queue and safely write data to disk.
IMO, never call adc from loop() but always from an timed interrupt.
 
So basically making my entire flight controller control algorithm run on one big interrupt and write to the SD card from the loop?

Another question that I want to make sure that I understood correctly if you could answer me:

- when I want to save to the SD I should do it in multiples of 512Byte. So if I want to create a CSV file with 10 variables
I should use the print command in such a way that the string I'm saving is 512Bytes so 512 characters for each time I want to save the buffered data to the SD?

for example:
str_To_save = string(variable1) + "," + string(variable2) + "," + .... string(variable10) + "\n" + string(variable1) + ... ;

I should make sure the size of str_To_save is 512Bytes or multiple of 512Bytes and then use file.println(str_To_save, 512) ?
 
Data collection on ISR() or DMA at elevated priority - should be fast and low overhead - with nothing blocking.
loop() writing data to SD will have intermittent blocking of SD writes of 100ms or more as the SD writes work with that hardware

And seems the flight controller code will be on a normal or lesser priority interrupt and it will have to work in small bites and regularly exit so loop() doesn't stall SD writes, and return to work in progress on the next interrupt, perhaps what @WMXZ meant by 2,000/second.

As far as saving data avoiding strings and using index+= sprintf( &buff[index], "... ", this, that ); then monitoring and file.printing from buff in blocks and overhead as needed to print 512B groups, and carry over the remainder to second buffer or back to buff[index=0];index=remainder;
 
what do you mean by:
low overhead

so before running the loop I declare the buffer like that:

Code:
char  buffer[512]; // to make sure my buffer size is exactly 512.

And by monitoring you mean to check when the index reaches almost 512 so I would write to the SD?

what did you mean by
overhead as needed to print 512B groups

Did you mean that if my next data line will be eventually more than 512 to skip the slots I didn't fill and move the next data to the next buffer for the next SD printing?

Thank you:)

EDIT:

I had something vaguely like this in mind, what do you think? (Initialy index set to zero)
Code:
loop()
{
   check_length = index;
   check_legnth += sprintf(&buffer[index], "%f,%f,%f,%f\n",123.456,123.456,123.456,123.456);
 
     if(check_legnth > 512)
     {
         buffer[512] = '\0';
         file.print(buffer);
         index = check_length = 0;
         index +=  sprintf(&buffer[index], "%f,%f,%f,%f\n",123.456,123.456,123.456,123.456);
     }
     else
     index = check_legnth;
}
 
Last edited:
As a proof of concept I compiled the following
Code:
    /**
     * @brief Circular Buffer definitions
     * 
     */
    #define NBUF_ACQ 10
    #define MAXBUF 1280
    EXTMEM uint32_t data_buffer[MAXBUF*NBUF_ACQ];

    /**
     * @brief Data storage class
     * 
     */
    class Data
    {
        public:
            Data(uint32_t * buffer) 
            { /**
             * @brief Constructor
             * @param buffer is pointer to data store
             * 
             */
                data_buffer=buffer; front_=rear_=0;
            }

            uint16_t push(uint32_t * src)
            { 
                /** 
                 * @brief push data to storage
                 * @param src is pointer to data block
                 */
                uint16_t f =front_ + 1;
                if(f >= MAXBUF) f=0;
                if(f == rear_) return 0;

                uint32_t *ptr= data_buffer+f*NBUF_ACQ;
                memcpy(ptr,src,NBUF_ACQ*4);
                front_ = f;
                return 1;
            }

            uint16_t pull(uint32_t * dst, uint32_t ndbl)
            {   
                /** 
                 * @brief pull data from storage
                 * @param dst is pointer to data blocks
                 * @param ndbl is number of data blocks
                 */
                uint16_t r = (rear_/ndbl) ;
                if(r == (front_/ndbl)) return 0;

                uint32_t *ptr= data_buffer + r*ndbl*NBUF_ACQ;
                memcpy(dst,ptr,ndbl*NBUF_ACQ*4);
                if(++r >= (MAXBUF/ndbl)) r=0;
                rear_ = r*ndbl;
                return 1;
            }
            uint16_t getCount () 
            {  
                /**
                 * @brief get number of data blocks in storage
                 * 
                 */
                if(front_ >= rear_) return front_ - rear_; return front_+ MAXBUF -rear_; 
            }

    private:    
        uint16_t front_, rear_;
        uint32_t *data_buffer;

    };

    Data rawData(data_buffer);

//  uint16_t getCount () { return rawData.getCount(); }
//  uint16_t pushData(uint32_t * src){ return rawData.push(src);}
//  uint16_t pullData(uint32_t * dst, uint32_t ndbl) {return rawData.pull(dst,ndbl);}
  
  /****************** Intervall timer(dummy example) *****************************/
  #include "IntervalTimer.h"

  IntervalTimer t1;

  /**/
  #include <SD.h>
  File file;
  char fname[40];
  int32_t tmp[10];
  int32_t diskBuffer[1280]; //10*512 bytes 

  /****/
  uint32_t acqCount=0;
  uint32_t acqMiss=0;
  void acq_isr(void)
  { acqCount++;
    for(int ii=0; ii<10; ii++) tmp[ii]=analogRead(ii);
    // simulate some processing
    delayMicroseconds(150);
    // copy to disk buffer
    if(!rawData.push(tmp)) acqMiss++;
  }

void setup() {
  // put your setup code here, to run once:
  while(!Serial);
   t1.begin(acq_isr, 500);
  SD.begin(BUILTIN_SDCARD);
}

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

  static uint32_t t0;
  static uint32_t diskCount=0;
  if(rawData.pull(diskBuffer,128)) // get 128*NBUF_ACQ samples from data store
  {
    diskCount++;
    if(!file)
    {
      uint32_t tt = rtc_get(); tt%=(24*3600);
      sprintf(fname,"D_%02d%02d%02d.bin",tt/3600,(tt%3600)/60,tt%60);
      Serial.println(fname);
      file=SD.open(fname,FILE_WRITE_BEGIN);
    }
    if(file)
    {
      int nret=file.write(diskBuffer,128*10*4);
      uint32_t tt=rtc_get();
      static uint32_t t2;
      uint32_t dt=tt %60;
      if(dt<t2)
      {
        Serial.println("close");
        file.close();
      }
      t2=dt;
    }
  }

  
  if( millis()>t0+1000)
  {
    Serial.print(loopCount); Serial.print(' '); 
    Serial.print(acqCount); Serial.print(' '); 
    Serial.print(rawData.getCount()); Serial.print(' '); 
    Serial.print(acqMiss); Serial.print(' '); 
    Serial.println(diskCount);
    loopCount=0;
    acqMiss=0;
    acqCount=0;
    diskCount=0;
    t0=millis();
  }
}

Note, I prefer to write data as binary to file and not as formatted text file. Makes programming much easier.
 
Wow, Thank you very much WMXZ I learned a lot from your code and I appreciate the time you took in order to help me here.:)
I'll try to implement these principles in my code.

I have a few questions if you could answer please:
- Did you choose 128 because after 128 times of sampling 10 variables we get 5120byte which is a multiple of 512byte?

- How can you read later the bin file if I want to use these readings with teensy? so basically doing it somehow in reverse.

- Can I transfer the data of the bin file when the quadcopter has landed and we don't have the timing restrictions inside teensy to be saved as a CSV format?
So when I take out the SD card I'll have a bin file and a CSV file of the same data?

I'm just familiar with CSV files, and when I file.write to a CSV file I know I can just open briefly Excel or notepad to check if the data was saved correctly. But after I tested some short code to write data as binary to bin file I couldn't see the data later.

Again thank you very much for your time, I'm new here and the community is really nice with great people.:)
 
Last edited:
I have a few questions if you could answer please:
- Did you choose 128 because after 128 times of sampling 10 variables we get 5120byte which is a multiple of 512byte?
Yes that is the idea, you want size to be a multiple of 10 and a multiple of 512 (128 *4)

- How can you read later the bin file if I want to use these readings with teensy? so basically doing it somehow in reverse.

- Can I transfer the data of the bin file when the quadcopter has landed and we don't have the timing restrictions inside teensy to be saved as a CSV format?
So when I take out the SD card I'll have a bin file and a CSV file of the same data?

I'm just familiar with CSV files, and when I file.write to a CSV file I know I can just open briefly Excel or notepad to check if the data was saved correctly. But after I tested some short code to write data as binary to bin file I couldn't see the data later.

If you have the possibility, I would take the disk out from teensy ad plug into PC.
Alternatively you could use MTP to transfer file from Teensy to PC.
I would do this only when everything else is working, and you have limitation to access the Teensy.
But one step at time

To get your data into Excel, you can write a program that convert binary data to csv, or you edit and run following macro

Code:
Sub ReadBin()
    Dim fName As String: fName = "D:\D_183700.bin"
    
    Dim intFileNum As Byte
    Dim intCellRow As Long
    Dim intCellCol As Integer
    Dim intVal As Long
    
    intFileNum = FreeFile
    intCellRow = 0
    '
    Open fName For Binary Access Read As intFileNum
    
    Do While Not EOF(intFileNum)
        intCellRow = intCellRow + 1
        For intCellCol = 1 To 10
            Get intFileNum, , intVal
            Cells(intCellRow, intCellCol) = intVal
        Next
    Loop
    '
    Close intFileNum
End Sub

reading the whole file (120000 lines) needs some time, but it worked
For checking only, I would read only 10 seconds (20000 lines)
 
If you have the possibility, I would take the disk out from teensy ad plug into PC.

Yes, that's what I was planning on doing, just taking the data out by unplugging the uSD card and using it inside the computer.

To get your data into Excel, you can write a program that convert binary data to csv, or you edit and run following macro
thank you I'll try that.

I might try to do also the conversion to the CSV file inside teensy. Maybe trying to read blocks of 10 varialles from the bin file I saved, and then file.print() them to a different file with CSV format, so eventually I'll have one bin file and one CSV file of the same data.
 
I might try to do also the conversion to the CSV file inside teensy. Maybe trying to read blocks of 10 varialles from the bin file I saved, and then file.print() them to a different file with CSV format, so eventually I'll have one bin file and one CSV file of the same data.

this way you may need a more complex program:
- acquire data and control vehicle.
- convert data.
of course, you can download two different programs, 1 for acquisition and one for converting.
but then you could run the conversion program directly on PC

while slower than writing binary data, you could try to write directly csv data
however, efficient writing to microSD card asks for predictable size.
for example, you have 10 integer variables up to 16 bit (+/- ~32768) so you use
as print format
Code:
fmt="%6d,%6d,%6d,%6d,%6d,%6d,%6d,%6d,%6d,%6d\n";
which results to 70 bytes instead of 40 binary bytes

your disk buffer would then be 70*512 * 35840 bytes
you should adapt circular buffer to cover multiple of disk buffers

Will test 2night, when have access to T4.1
 
while slower than writing binary data, you could try to write directly csv data
however, efficient writing to microSD card asks for predictable size.

So because I can have a random number like 153 which is 3 Byte (when printing as a string) but also can have a random number like 31770 which is 5 Byte we need to make sure we save all values with the same number of digits so we get a predictable variable size for storing the data in multiple of 512.

your disk buffer would then be 70*512 * 35840 bytes
you should adapt circular buffer to cover multiple of disk buffers

Ok so basically adopt the same code we used before for writing in binary, but doing some changes to make sure that we print data as a string with the size of multiples of 512.

What I thought about trying to do is to save the data in binary while the quadcopter is flying.
And after the quadcopter has landed (and we don't have the control system timing restrictions) then read the data we saved as binary from the SD card.
Reading 40 bytes at a time and printing it to a different file in a CSV format. So eventually we will have a bin file and a CSV file.
 
@Eyal: p#9 indicated the sprinf() to large buffers could work.

With 2 buffers of say 512B*10+256 bytes: sprintf into buf1 until it holds over 5120 bytes.
- copy the bytes beyond 5120 to buf2
- write 5120 bytes of buf1 to SD
- while that writes, do sprintf into buf2 until it holds over 5120 bytes
> swap buffers and repeat

Indeed, writing just the binary values would give better size control and smaller output to write to SD, but a similar use of two buffers would be needed to allow SD writing from one while the other was being collected.
And yes, reading the binary data and creating the CSV offline/after the copter is on the ground would be a good and easy task for the Teensy. Buffering could make that more time efficient given it would be reading one binary file while writing out the growing CSV file, but that wouldn't be critical.
 
Here is the version that writes directly to csv files

Code:
    /**
     * @brief Circular Buffer definitions
     * 
     */
    #define NBUF_ACQ 70
    #define MAXBUF (10*512)
    EXTMEM char data_buffer[MAXBUF*NBUF_ACQ];

    /**
     * @brief Data storage class
     * 
     */
    class Data
    {
        public:
            Data(void * buffer) 
            { /**
             * @brief Constructor
             * @param buffer is pointer to data store
             * 
             */
                data_buffer=buffer; front_=rear_=0;
            }

            uint16_t push(void * src)
            { 
                /** 
                 * @brief push data to storage
                 * @param src is pointer to data block
                 */
                uint16_t f =front_ + 1;
                if(f >= MAXBUF) f=0;
                if(f == rear_) return 0;

                void *ptr= data_buffer+f*NBUF_ACQ;
                memcpy(ptr,src,NBUF_ACQ);
                front_ = f;
                return 1;
            }

            uint16_t pull(void * dst, uint32_t ndbl)
            {   
                /** 
                 * @brief pull data from storage
                 * @param dst is pointer to data blocks
                 * @param ndbl is number of data blocks
                 */
                uint16_t r = (rear_/ndbl) ;
                if(r == (front_/ndbl)) return 0;

                void *ptr= data_buffer + r*ndbl*NBUF_ACQ;
                memcpy(dst,ptr,ndbl*NBUF_ACQ);
                if(++r >= (MAXBUF/ndbl)) r=0;
                rear_ = r*ndbl;
                return 1;
            }
            uint16_t getCount () 
            {  
                /**
                 * @brief get number of data blocks in storage
                 * 
                 */
                if(front_ >= rear_) return front_ - rear_; return front_+ MAXBUF -rear_; 
            }

    private:    
        uint16_t front_, rear_;
        void *data_buffer;

    };

    Data rawData(data_buffer);

//  uint16_t getCount () { return rawData.getCount(); }
//  uint16_t pushData(char * src){ return rawData.push(src);}
//  uint16_t pullData(char * dst, uint32_t ndbl) {return rawData.pull(dst,ndbl);}
  
  /****************** Intervall timer(dummy example) *****************************/
  #include "IntervalTimer.h"
  IntervalTimer t1;

  /**/
  #include <SD.h>
  File file;
  char fname[40];
  int32_t tmp[10];
  char tmpBuf[80];
  char diskBuffer[512*70]; 

  /****/
  uint32_t acqCount=0;
  uint32_t acqMiss=0;
  void acq_isr(void)
  { acqCount++;
    for(int ii=0; ii<10; ii++) tmp[ii]=analogRead(ii);
    // simulate some processing
    delayMicroseconds(150);
    // format 
    for(int ii=0;ii<10;ii++) sprintf(tmpBuf+7*ii,"%6d,",tmp[ii]&0xFFFF); tmpBuf[69]='\n';
    // copy to disk buffer
    if(!rawData.push(tmpBuf)) acqMiss++;
  }

void setup() {
  // put your setup code here, to run once:
  while(!Serial);
  //
  t1.begin(acq_isr, 500);
  SD.begin(BUILTIN_SDCARD);
}

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

  static uint32_t t0;
  static uint32_t diskCount=0;
  if(rawData.pull(diskBuffer,512))
  {
    diskCount++;
    if(!file)
    {
      uint32_t tt = rtc_get(); tt%=(24*3600);
      sprintf(fname,"D_%02d%02d%02d.csv",tt/3600,(tt%3600)/60,tt%60);
      Serial.println(fname);
      file=SD.open(fname,FILE_WRITE_BEGIN);
      if(!file) {Serial.print(fname); Serial.println(" not opened"); while(1);}
    }
    if(file)
    {
      uint32_t tm1=micros();
      int nret=file.write(diskBuffer,512*NBUF_ACQ);
      tm1=micros()-tm1;
      if(tm1>tm) tm=tm1;
      //
      uint32_t tt=rtc_get();
      static uint32_t t2;
      uint32_t dt=tt %60;
      if(dt<t2)
      {
        Serial.println("close");
        file.close();
      }
      t2=dt;
    }
  }

  
  if( millis()>t0+1000)
  {
    Serial.print(loopCount); Serial.print(' '); 
    Serial.print(acqCount); Serial.print(' '); 
    Serial.print(rawData.getCount()); Serial.print(' '); 
    Serial.print(acqMiss); Serial.print(' '); 
    Serial.print(tm); Serial.print(' '); 
    Serial.println(diskCount);
    loopCount=0;
    acqMiss=0;
    acqCount=0;
    diskCount=0;
    tm=0;
    t0=millis();
  }
}
 
I could not resist to adapt to Teensy 4.0 with AudioCard
Code:
    /**
     * @brief Circular Buffer definitions
     * 
     */
    #define NBUF_ACQ 70
    #if defined(ARDUINO_TEENSY41)
      #define MAXBUF (10*512)
      EXTMEM char data_buffer[MAXBUF*NBUF_ACQ];
      //
    #elif defined(ARDUINO_TEENSY40)
      #define MAXBUF (6*512)
      char data_buffer[MAXBUF*NBUF_ACQ];
    #endif

    /**
     * @brief Data storage class
     * 
     */
    class Data
    {
        public:
            Data(void * buffer) 
            { /**
             * @brief Constructor
             * @param buffer is pointer to data store
             * 
             */
                data_buffer= (char *)buffer; front_=rear_=0;
            }

            uint16_t push(void * src)
            { 
                /** 
                 * @brief push data to storage
                 * @param src is pointer to data block
                 */
                uint16_t f =front_ + 1;
                if(f >= MAXBUF) f=0;
                if(f == rear_) return 0;

                void *ptr= data_buffer+f*NBUF_ACQ;
                memcpy(ptr,src,NBUF_ACQ);
                front_ = f;
                return 1;
            }

            uint16_t pull(void * dst, uint32_t ndbl)
            {   
                /** 
                 * @brief pull data from storage
                 * @param dst is pointer to data blocks
                 * @param ndbl is number of data blocks
                 */
                uint16_t r = (rear_/ndbl) ;
                if(r == (front_/ndbl)) return 0;

                void *ptr= data_buffer + r*ndbl*NBUF_ACQ;
                memcpy(dst,ptr,ndbl*NBUF_ACQ);
                if(++r >= (MAXBUF/ndbl)) r=0;
                rear_ = r*ndbl;
                return 1;
            }
            uint16_t getCount () 
            {  
                /**
                 * @brief get number of data blocks in storage
                 * 
                 */
                if(front_ >= rear_) return front_ - rear_; return front_+ MAXBUF -rear_; 
            }

    private:    
        uint16_t front_, rear_;
        char *data_buffer;

    };

    Data rawData(data_buffer);

//  uint16_t getCount () { return rawData.getCount(); }
//  uint16_t pushData(char * src){ return rawData.push(src);}
//  uint16_t pullData(char * dst, uint32_t ndbl) {return rawData.pull(dst,ndbl);}
  
  /****************** Intervall timer(dummy example) *****************************/
  #include "IntervalTimer.h"
  IntervalTimer t1;

  /**/
  #include <SD.h>
  #if defined(ARDUINO_TEENSY40)
    #include <SPI.h>
  #endif

  File file;
  char fname[40];
  int32_t tmp[10];
  char tmpBuf[80];
  char diskBuffer[512*70]; 

  /****/
  uint32_t acqCount=0;
  uint32_t acqMiss=0;
  void acq_isr(void)
  { acqCount++;
    for(int ii=0; ii<10; ii++) tmp[ii]=analogRead(ii);
    // simulate some processing
    delayMicroseconds(150);
    // format 
    for(int ii=0;ii<10;ii++) sprintf(tmpBuf+7*ii,"%6ld,",tmp[ii]&0xFFFF); tmpBuf[69]='\n';
    // copy to disk buffer
    if(!rawData.push(tmpBuf)) acqMiss++;
  }

void setup() {
  // put your setup code here, to run once:
  while(!Serial);
  //
  Serial.print("Initializing SD card...");
  
  #if defined ARDUINO_TEENSY41
    const int chipSelect = BUILTIN_SDCARD;
    Serial.print(" (SDIO) ");
    #define sdConfig SdioConfig(FIFO_SDIO)
  #else
    const int chipSelect = 10;
    #define sdConfig SdSpiConfig(chipSelect, DEDICATED_SPI, SD_SCK_MHZ(50))
    pinMode(chipSelect,OUTPUT); digitalWriteFast(chipSelect,HIGH);

    Serial.print(" (SPI) ");
    SPI.setMOSI(11);  // Audio shield has MOSI on pin 11
    SPI.setMISO(12);  // Audio shield has MISO on pin 12
    SPI.setSCK(13);  // Audio shield has SCK on pin 13    // for T4.x
    //SPI.setSCK(14);  // Audio shield has SCK on pin 14   // for T3.x
  #endif
  
  // see if the card is present and can be initialized (if required repeat up to 10 times):
  int ii;
  for ( ii=1; ii<10 && !SD.sdfs.begin(sdConfig); ii++) { Serial.print('.'); delay(100); }
  
  if(ii==10)
  { Serial.println("Card failed, or not present");
    while(1) continue;  // No SD card, so don't do anything more - stay stuck here
  }
  
  FsDateTime::setCallback(SDClass::dateTime);
  Serial.println("card initialized.");

  // start timer
  t1.begin(acq_isr, 400);
}

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

  static uint32_t t0;
  static uint32_t diskCount=0;
  if(rawData.pull(diskBuffer,512))
  {
    diskCount++;
    if(!file)
    {
      uint32_t tt = rtc_get(); tt%=(24*3600);
      sprintf(fname,"D_%02lu%02lu%02lu.csv",tt/3600,(tt%3600)/60,tt%60);
      Serial.println(fname);
      file=SD.open(fname,FILE_WRITE_BEGIN);
      if(!file) {Serial.print(fname); Serial.println(" not opened"); while(1);}
    }
    if(file)
    {
      uint32_t tm1=micros();
      int nret=file.write(diskBuffer,512*NBUF_ACQ);
      tm1=micros()-tm1;
      if(tm1>tm) tm=tm1;
      //
      uint32_t tt=rtc_get();
      static uint32_t t2;
      uint32_t dt=tt %60;
      if(dt<t2)
      {
        Serial.println("close");
        file.close();
      }
      t2=dt;
    }
  }

  
  if( millis()>t0+1000)
  {
    Serial.print(loopCount); Serial.print(' '); 
    Serial.print(acqCount); Serial.print(' '); 
    Serial.print(rawData.getCount()); Serial.print(' '); 
    Serial.print(acqMiss); Serial.print(' '); 
    Serial.print(tm); Serial.print(' '); 
    Serial.println(diskCount);
    loopCount=0;
    acqMiss=0;
    acqCount=0;
    diskCount=0;
    tm=0;
    t0=millis();
  }
}
To go to the limit I could increase sampling rate to 2500 samples/s

Edit: The audio card was only used to access SPI microSD card, not to use the SGTL5000.
 
Last edited:
Thank you everyone! I'll try the things you offered. I'm just taking a few days break from the project to study for my semester finals exams.
 
Back
Top