Forum Rule: Always post complete source code & details to reproduce any issue!
Page 32 of 34 FirstFirst ... 22 30 31 32 33 34 LastLast
Results 776 to 800 of 835

Thread: LittleFS port to Teensy/SPIFlash

  1. #776
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    6,772
    @KurtE - @defragster -@PaulStoffregen

    Just pushed a change up to the repository for W25N01G NAND flash to support Bad Block Management. I brought it to the point of actually writing the Table. A little reluctant to do that until I can test the logic. May have to write a test sketch just for that purpose.

    Anyway what I did was I reserved 24 blocks on the N01 for BBM and then copied the lowlevelformat function and moded so it would erase the blocks as specified by the config structure the added the additional block erases for the reserved area. Probably have to do something with quickformat as well but for now its a start.

    Didn't add support for the M02 as it looks like it can have 2 BBM areas, 1 for each die - definitely adds alot more complication. Not sure want to keep supporting this one - just lazy but will figure it out. As for the N02 - there is no remapping identified to support BBM which is kind of strange - wondering if its a omission in the ref manual or not.

    Probably going to just post an issue on the LittleFS repository just for status for Paul to eventual take a look at and do his magic. But at least it works for the most part
    Last edited by mjs513; 01-09-2021 at 11:19 AM.

  2. #777
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    8,871
    Sounds good.

    Maybe time to do PR back into LittleFS?

  3. #778
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    6,772
    Quote Originally Posted by KurtE View Post
    Sounds good.

    Maybe time to do PR back into LittleFS?
    Took your advice and issued a PR (Updated for NAND/FeRAM.Fram and tweaks) for what we have so far.

  4. #779
    Junior Member
    Join Date
    Sep 2020
    Posts
    10
    Quote Originally Posted by mjs513 View Post
    First the W25Q16JV-DTR is a NOR Flash chip, yes I know that the datasheet is silent on it but if you do a searching you will find that Mouser and Winbond shows it as a NOR flash. Second if you don't include the "LittleFS.h" you won't be using that library in you sketch and you won't be using a FileSystem with that flash chip. You can continue to use SPIMemory in your sketch.

    Hope this answers your question?

    In your prior post you mentioned using SPI2, unfortunately you can't really use it for Serial Flash chips using the underside pins unless you are breaking the connections out to a castellated board or in some other manner. Those underside pins for the PSRAM and FLASH are really meant to be used for QSPI access.
    Hmm, I've been testing this for a couple of days using LFSintegrity-PlusNAND library. It seems to work well enough for my purposes, but after rereading your statement about SPI2 it started me thinking. I my case I have a custom carrier board with Teensy4.1 and prop-shield both incorporated (long header goes through my board with teensy on one side and propshield on the other). Carrier board has extra power supplies and interfaces to instruments for my boat and a raspberry pi 4b, displays, a few buttons, etc.

    I have a psram and a winbond nor flash on the back of the teensy.
    I did a test with both
    LittleFS_SPIFlash myfs;
    and then
    LittleFS_QSPIFlash myfs;

    Now I'm wondering if it was using the flash on the propshield in the first case, and the flash on the teensy board in the second case?
    Last edited by dpj; 01-10-2021 at 10:15 PM.

  5. #780
    Junior Member
    Join Date
    Sep 2020
    Posts
    10
    Well I just confirmed my own suspicion.
    It appears when I do
    LittleFS_SPIFlash myfs
    vs.
    LittleFS_QSPIFlash myfs;
    I am indeed talking to different flash chips, I think the first case is the one on the propshield. (I was wondering how I could access both when the latter class removed the chipselect argument from the begin() function.)

  6. #781
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    13,732
    Quote Originally Posted by dpj View Post
    Well I just confirmed my own suspicion.
    It appears when I do
    LittleFS_SPIFlash myfs
    vs.
    LittleFS_QSPIFlash myfs;
    I am indeed talking to different flash chips, I think the first case is the one on the propshield. (I was wondering how I could access both when the latter class removed the chipselect argument from the begin() function.)
    Look at the LFSintegrity sketch - it has #ifdef for all the known MEDIA types:
    Code:
    //#define TEST_RAM // can be PSRAM or smaller in processor RAM1 or RAM2
    //#define TEST_SPI // this is Propshield or other SPI based media - where the "FlashChipSelect" is passed
    //#define TEST_QSPI // this is T_4.1 underside QSPI FLASH
    //#define TEST_SPI_NAND // this is a NAND device SPI based media - where the "FlashChipSelect" is passed
    #define TEST_QSPI_NAND // this is T_4.1 underside QSPI NAND device FLASH
    //#define TEST_PROG // this uses PCB onboard FLASH - tested with T_4.x : it is currently erase on every code upload, but static across restarts
    //#define TEST_MRAM // this is a supported SPI MRAM/FRAM chip - where the "FlashChipSelect" is passed
    It only tests 1 at a time - where this code keys off those #define values and always presents "myfs" to the sketch.

    There are MTP samples on another thread where ALL available types are Mounted and shares over USB - that just takes using one instance of "myfs" for one and perhaps "myfs2" for the other - names of your choice replacing below for one:
    Code:
    #ifdef TEST_RAM
    LittleFS_RAM myfs;
    // RUNTIME :: extern "C" uint8_t external_psram_size;
    EXTMEM char buf[MYPSRAM * 1024 * 1024];	// USE DMAMEM for more memory than ITCM allows - or remove
    //DMAMEM char buf[490000];	// USE DMAMEM for more memory than ITCM allows - or remove
    char szDiskMem[] = "RAM_DISK";
    #elif defined(TEST_SPI)
    //const int FlashChipSelect = 10; // Arduino 101 built-in SPI Flash
    #define FORMATSPI
    //#define FORMATSPI2
    LittleFS_SPIFlash myfs;
    char szDiskMem[] = "SPI_DISK";
    #elif defined(TEST_MRAM)
    //const int FlashChipSelect = 10; // Arduino 101 built-in SPI Flash
    LittleFS_SPIFram myfs;
    char szDiskMem[] = "FRAM_DISK";
    #elif defined(TEST_PROG)
    LittleFS_Program myfs;
    char szDiskMem[] = "PRO_DISK";
    #elif defined(TEST_QSPI_NAND)
    char szDiskMem[] = "QSPI_NAND";
    LittleFS_QPINAND myfs;
    #elif defined(TEST_SPI_NAND)
    char szDiskMem[] = "SPI_NAND";
    LittleFS_SPINAND myfs;
    #else // TEST_QSPI
    LittleFS_QSPIFlash myfs;
    char szDiskMem[] = "QSPI_DISK";
    
    #endif

  7. #782
    Junior Member
    Join Date
    Sep 2020
    Posts
    10
    Thanks I guess I overlooked comments with those defines.

  8. #783
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    13,732
    Quote Originally Posted by dpj View Post
    Thanks I guess I overlooked comments with those defines.
    Quite welcome. The comments in bold are not in the code - added for help with clarification - easier to read than 700 preceding posts as it all developed.

  9. #784
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    6,772
    @KurtE = @defragster
    Just pushed an update to plusNAND to bring LittleFS even with what I have updated for NAND. In the READ_BCC test case I added a quick test of the addbblut function - I think I got it working correctly now but reluctant to turn it on yet with out some further use cases to abuse it - have to find that crazy data logger that @defragster was playing with

    I also updated the PR to add the same examples that are in LFSIntegriy as well as the update to addBBLUT - this was a result of me playing with it this morning.

  10. #785
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    13,732
    Good work @mjs513 - I typed on github asking about that ... but never hit post I saw today ... too many distractions ...

    Wish I had known about the 2Gb NAND digikey had with last order so I don't have one of any sort to QSPI test yet ...

  11. #786
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    6,772
    @defragster - @KurtE
    Decided to try to modify the MTP_logger example to work with the selected device for logging data. I tested SD, SPI, QSPI, and SPI NAND which all worked without an issue. However, when I tried QSPI NAND ran into a problem. It would see the device but couldn't read anything on the NAND especially the index.dat file. Not sure what I am doing wrong. Just to be clean you can only use 1 device at a time to log to in this version!

    Noticed a couple of other things I have to figure out:
    1. MTP Serial is throwing errors from SDFAT when I select it
    2. Have to figure out where to put the test for events - otherwise if i hit q it doesn't stop logging Think I am just tired at this point. Anyway if you want to play along:
    Code:
    #include "Arduino.h"
    
    #define NDAT 128L
    #define NCH_ACQ 1
    #define NCH_I2S 4
    #define NBUF_ACQ (NCH_ACQ*NDAT)
    #define NBUF_I2S (NCH_I2S*NDAT)
    #define N_ADC 2
    #define FRAME_I2S (NCH_I2S/N_ADC)
    
    #define I2S_CONFIG 1
    // CONFIG 1 (PURE RX)
    //PIN  36 BCLK
    //PIN  37 FS
    //PIN  13 RXD0
    //PIN  38 RXD1
    
    #include "SD.h"
    #include "MTP.h"
    
    #if defined(__IMXRT1062__)
      // following only while usb_mtp is not included in cores
      #if __has_include("usb_mtp.h")
        #include "usb_mtp.h"
      #else
        #include "usb1_mtp.h"
      #endif
    #endif
    
    #define USE_SD  0
    #define USE_LITTLEFS 1 // set to zero if no LtttleFS is existing or to be used
    
    #if USE_LITTLEFS==1
      #include "LittleFS.h"
      #include "LittleFS_NAND.h"
    #ifdef ARDUINO_TEENSY41
      #define USE_RAM 0     // T4.1 PSRAM (or RAM)
    #else
      #define USE_RAM 0     // T4.1 PSRAM (or RAM)
    #endif
      #define USE_SPI 0
      #define USE_QSPI 1
      #define USE_NAND 0
      #define USE_QSPI_NAND 0
      #define USE_FRAM 0
    #endif
    
    #if USE_EVENTS==1
      extern "C" int usb_init_events(void);
    #else
      int usb_init_events(void) {}
    #endif
    
    
    #if defined(__IMXRT1062__)
      // following only as long usb_mtp is not included in cores
      #if !__has_include("usb_mtp.h")
        #include "usb1_mtp.h"
      #endif
    #else
      #ifndef BUILTIN_SCCARD 
        #define BUILTIN_SDCARD 254
      #endif
      void usb_mtp_configure(void) {}
    #endif
    
    /****  Start device specific change area  ****/
    #if USE_SD==1
      // edit SPI to reflect your configuration (following is for T4.1)
      #define SD_MOSI 11
      #define SD_MISO 12
      #define SD_SCK  13
    
      #define stor_SPEED SD_SCK_MHZ(16)  // adjust to sd card 
    
    // SDClasses
      const char *sd_str[]={"sdio"}; // edit to reflect your configuration
      const int cs[] = {BUILTIN_SDCARD}; // edit to reflect your configuration
      const int nsd = sizeof(cs)/sizeof(int);
    
    SDClass storfs[nsd];
    #endif
    
    //LittleFS classes
    #if USE_LITTLEFS==1
      #include "LittleFS.h"
      #if USE_RAM == 1
        const char *stor_str[]={"RAM0"};     // edit to reflect your configuration
        const int stor_size[] = {2'000'000};
        const int stor_nsd = sizeof(stor_size)/sizeof(int);
        LittleFS_RAM storfs[stor_nsd]; // needs to be declared if LittleFS is used in storage.h
      #endif
      
      #if USE_SPI == 1
        const char *stor_str[]={"WINBOND"};     // edit to reflect your configuration
        const int stor_cs[] = {5}; // edit to reflect your configuration
        const int stor_nsd = sizeof(stor_cs)/sizeof(int);
        LittleFS_SPIFlash storfs[stor_nsd]; // needs to be declared if LittleFS is used in storage.h
      #endif
      
      #if USE_FRAM == 1
        const char *stor_str[]={"CYPRESS"};     // edit to reflect your configuration
        const int stor_cs[] = {10}; // edit to reflect your configuration
        const int stor_nsd = sizeof(stor_cs)/sizeof(int);
        LittleFS_SPIFlash storfs[stor_nsd]; // needs to be declared if LittleFS is used in storage.h
      #endif
    
      #if USE_NAND == 1
        const char *stor_str[]={"WINBOND1G"};     // edit to reflect your configuration
        const int stor_cs[] = {3}; // edit to reflect your configuration
        const int stor_nsd = sizeof(stor_cs)/sizeof(int);
        LittleFS_SPINAND storfs[stor_nsd]; // needs to be declared if LittleFS is used in storage.h
      #endif
    
      #if USE_QSPI_NAND == 1
        const char *stor_str[]={"WINBOND1G"};     // edit to reflect your configuration
        const int stor_nsd = 1;
        LittleFS_QPINAND storfs[stor_nsd]; // needs to be declared if LittleFS is used in storage.h
      #endif
      
      #if USE_QSPI == 1
        const char *stor_str[]={"QSPI0"};     // edit to reflect your configuration
        const int stor_nsd = 1;
        LittleFS_QSPIFlash storfs[stor_nsd]; // needs to be declared if LittleFS is used in storage.h
      #endif
     
    #endif
    
    
    MTPStorage_SD storage;
    MTPD       mtpd(&storage);
    
    
    void storage_configure()
    {
      #if USE_SD==1
        #if defined SD_SCK
          SPI.setMOSI(SD_MOSI);
          SPI.setMISO(SD_MISO);
          SPI.setSCK(SD_SCK);
        #endif
    
        for(int ii=0; ii<nsd; ii++)
        { if(cs[ii] == BUILTIN_SDCARD)
          {
            if(!storfs[ii].sdfs.begin(SdioConfig(FIFO_SDIO))) {Serial.println("No storage"); while(1);};
            storage.addFilesystem(storfs[ii], sd_str[ii]);
          }
          else if(cs[ii]<BUILTIN_SDCARD)
          {
            pinMode(cs[ii],OUTPUT); digitalWriteFast(cs[ii],HIGH);
            if(!storfs[ii].sdfs.begin(SdSpiConfig(cs[ii], SHARED_SPI, stor_SPEED))) {Serial.println("No storage"); while(1);}
            storage.addFilesystem(storfs[ii], sd_str[ii]);
          }
            uint64_t totalSize = storfs[ii].totalSize();
            uint64_t usedSize  = storfs[ii].usedSize();
            Serial.printf("Storage %d %d %s ",ii,cs[ii],sd_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
        }
        #endif
    
         #if USE_LITTLEFS==1
          #if USE_RAM == 1
            for(int ii=0; ii<stor_nsd;ii++)
            {
              { if(!ramfs[ii].begin(stor_size[ii])) { Serial.println("No storage"); while(1);}
                storage.addFilesystem(ramfs[ii], stor_str[ii]);
              }
              uint64_t totalSize = ramfs[ii].totalSize();
              uint64_t usedSize  = ramfs[ii].usedSize();
              Serial.printf("Storage %d %s ",ii,stor_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
            }
          #endif
    
          #if USE_SPI == 1
            for(int ii=0; ii<stor_nsd;ii++) {
              pinMode(stor_cs[ii],OUTPUT); digitalWriteFast(stor_cs[ii],HIGH);
              if(!storfs[ii].begin(stor_cs[ii], SPI)) {Serial.println("No storage"); while(1);}
              storage.addFilesystem(storfs[ii], stor_str[ii]);
              
            uint64_t totalSize = storfs[ii].totalSize();
            uint64_t usedSize  = storfs[ii].usedSize();
            Serial.printf("Storage %d %d %s ",ii,stor_cs[ii],stor_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
            }
          #endif
    
          #if USE_FRAM == 1
            for(int ii=0; ii<stor_nsd;ii++) {
              pinMode(stor_cs[ii],OUTPUT); digitalWriteFast(stor_cs[ii],HIGH);
              if(!storfs[ii].begin(stor_cs[ii], SPI)) {Serial.println("No storage"); while(1);}
              storage.addFilesystem(storfs[ii], stor_str[ii]);
              
            uint64_t totalSize = storfs[ii].totalSize();
            uint64_t usedSize  = storfs[ii].usedSize();
            Serial.printf("Storage %d %d %s ",ii,stor_cs[ii],stor_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
            }
          #endif
    
          #if USE_NAND == 1
            for(int ii=0; ii<stor_nsd;ii++) {
              pinMode(stor_cs[ii],OUTPUT); digitalWriteFast(stor_cs[ii],HIGH);
              if(!storfs[ii].begin(stor_cs[ii], SPI)) {Serial.println("No storage"); while(1);}
              storage.addFilesystem(storfs[ii], stor_str[ii]);
              
            uint64_t totalSize = storfs[ii].totalSize();
            uint64_t usedSize  = storfs[ii].usedSize();
            Serial.printf("Storage %d %d %s ",ii,stor_cs[ii],stor_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
            }
          #endif
    
          #if USE_QSPI_NAND == 1
           for(int ii=0; ii<stor_nsd;ii++) {
              if(!storfs[ii].begin()) {Serial.println("No storage"); while(1);}
              storage.addFilesystem(storfs[ii], stor_str[ii]);
         
              uint64_t totalSize = storfs[ii].totalSize();
              uint64_t usedSize  = storfs[ii].usedSize();
              Serial.printf("Storage %d %s ",ii,stor_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
            }
          #endif
    
          #if USE_QSPI == 1
           for(int ii=0; ii<stor_nsd;ii++) {
              if(!storfs[ii].begin()) {Serial.println("No storage"); while(1);}
              storage.addFilesystem(storfs[ii], stor_str[ii]);
         
              uint64_t totalSize = storfs[ii].totalSize();
              uint64_t usedSize  = storfs[ii].usedSize();
              Serial.printf("Storage %d %s ",ii,stor_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
            }
          #endif
          
        #endif
    }
    /****  End of device specific change area  ****/
    
    void logg(uint32_t del, const char *txt);
    
    int16_t state;
    int16_t do_logger(uint16_t store, int16_t state);
    int16_t do_menu(int16_t state);
    int16_t check_filing(int16_t state);
    
    void acq_init(int32_t fsamp);
    int16_t acq_check(int16_t state);
    
    void setup()
    { 
      Serial.begin(115200);
      #if defined(USB_MTPDISK_SERIAL) 
        while(!Serial); // comment if you do not want to wait for terminal
      #else
        //while(!Serial.available()); // comment if you do not want to wait for terminal (otherwise press any key to continue)
        while(!Serial.available() && millis() < 5000); // or third option to wait up to 5 seconds and then continue
      #endif
      Serial.println("MTP logger");
    
      #if !__has_include("usb_mtp.h")
        usb_mtp_configure();
      #endif
      storage_configure();
    
      acq_init(93750); // is fixed for this example, to be modified below
      state=-1;
    
      Serial.println("Setup done");
      Serial.println(" Enter s to start acquisition and q to stop acquisition");
      Serial.flush();
    }
    
    uint32_t loop_count=0;
    void loop()
    { loop_count++;
      state = do_menu(state);
      state = acq_check(state);
      state = check_filing(state);
      //
      if(state<0)
        mtpd.loop();
      else
        state=do_logger(0,state);
    
      if(state>=0) logg(1000,"loop");
      //asm("wfi"); // may wait forever on T4.x
    
    
    }
    
    /**************** Online logging *******************************/
    extern uint32_t loop_count, acq_count, acq_miss, maxDel;
    extern uint16_t maxCount;
    void logg(uint32_t del, const char *txt)
    { static uint32_t to;
      if(millis()-to > del)
      {
        Serial.printf("%s: %6d %4d %4d %4d %4d %d\n",
                txt,loop_count, acq_count, acq_miss,maxCount, maxDel,state); 
        loop_count=0;
        acq_count=0;
        acq_miss=0;
        maxCount=0;
        maxDel=0;
        to=millis();
      }
    }
    
    /*************************** Circular Buffer ********************/
    #if defined ARDUINO_TEENSY41
      #define HAVE_PSRAM 1
    #else
      #define HAVE_PSRAM 0
    #endif
    
      #if HAVE_PSRAM==1
        #define MAXBUF (1000) // 3000 kB   // < 5461 for 16 MByte PSRAM
    
    //    extern "C" uint8_t external_psram_size;
    //    uint8_t size = external_psram_size;
    //    uint32_t *memory_begin = (uint32_t *)(0x70000000);
    //    uint32_t *data_buffer = memory_begin;
    
        uint32_t *data_buffer = (uint32_t *)extmem_malloc(MAXBUF*128*sizeof(uint32_t));
      #else
        #if defined(ARDUINO_TEENSY41)
          #define MAXBUF (46)           // 138 kB
        #elif defined(ARDUINO_TEENSY40)
          #define MAXBUF (46)           // 138 kB
        #elif defined(__MK66FX1M0__)
          #define MAXBUF (46)           // 138 kB
        #elif defined(__MK20DX256__)
          #define MAXBUF (12)           // 36 kB
        #endif
        uint32_t data_buffer[MAXBUF*NBUF_ACQ];
      #endif
    
    static uint16_t front_ = 0, rear_ = 0;
    uint16_t getCount () { if(front_ >= rear_) return front_ - rear_; return front_+ MAXBUF -rear_; }
    uint16_t maxCount=0;
    
    void resetData(void) {  front_ = 0;  rear_ = 0; }
    
    uint16_t pushData(uint32_t * src)
    { 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;
      //
      uint16_t count;
      count = (front_ >= rear_) ? (front_ - rear_) : front_+ (MAXBUF -rear_) ;
      if(count>maxCount) maxCount=count;
      //
      return 1;
    }
    
    uint16_t pullData(uint32_t * dst, uint32_t ndbl)
    { uint16_t r = (rear_/ndbl) ;
      if(r == (front_/ndbl)) return 0;
      if(++r >= (MAXBUF/ndbl)) r=0;
      uint32_t *ptr= data_buffer + r*ndbl*NBUF_ACQ;
      memcpy(dst,ptr,ndbl*NBUF_ACQ*4);
      rear_ = r*ndbl;
      return 1;
    }
    
    /*************************** Filing *****************************/
    int16_t file_open(uint16_t store);
    int16_t file_writeHeader(void);
    int16_t file_writeData(void *diskBuffer, uint32_t ndbl);
    int16_t file_close(void);
    #define NDBL 1
    #define NBUF_DISK (NDBL*NBUF_ACQ)
    uint32_t diskBuffer[NBUF_DISK];
    uint32_t maxDel=0;
    
    int16_t do_logger(uint16_t store, int16_t state)
    { uint32_t to=millis();
      if(pullData(diskBuffer,NDBL))
      {
        if(state==0)
        { // acquisition is running, need to open file
          if(!file_open(store)) return -2;
          state=1;
        }
        if(state==1)
        { // file just opended, need to write header
          if(!file_writeHeader()) return -3;
          state=2;
          
        }
        if(state>=2)
        { // write data to disk
          if(!file_writeData(diskBuffer,NBUF_DISK*4)) return -4;
        }
      }
    
      if(state==3)
      { // close file, but continue acquisition
        if(!file_close()) return -5;
        state=0;
      }
    
      if(state==4)
      { // close file and stop acquisition
        if(!file_close()) return -6;
        state=-1;
      }
    
      uint32_t dt=millis()-to;
      if(dt>maxDel) maxDel=dt;
    
      return state;
    }
    
    /******************** Menu ***************************/
    void do_menu1(void);
    void do_menu2(void);
    void do_menu3(void);
    
    int16_t do_menu(int16_t state)
    { // check Serial input
      if(!Serial.available()) return state;
      char cc = Serial.read();
      switch(cc)
      {
        case 's': // start acquisition
          if(state>=0) return state;
          state=0;
          Serial.println("\nStart");
          break;
        case 'q': // stop acquisition
          if(state<0) return state;
          state=4;
          Serial.println("\nStop");
          break;
        case '?': // get parameters
          do_menu1();
          break;
        case '!': // set parameters
          if(state>=0) return state;
          do_menu2();
          break;
        case ':': // misc commands
          if(state>=0) return state;
          do_menu3();
          break;
        default:
          break;
      }
      return state;
    }
    
    /************ Basic File System Interface *************************/
    //#include "SD.h"
    //extern SDClass sdx[];
    static File mfile;
    
    char header[512];
    
    void makeHeader(char *header);
    int16_t makeFilename(char *filename);
    int16_t checkPath(uint16_t store, char *filename);
    
    int16_t file_open(uint16_t store)
    { char filename[80];
      if(!makeFilename(filename)) return 0;
      if(!checkPath(store, filename)) return 0;
      mfile = storfs[store].open(filename,FILE_WRITE_BEGIN);
      return !(!mfile);
    }
    
    int16_t file_writeHeader(void)
    { if(!mfile) return 0;
      makeHeader(header);
      size_t nb = mfile.write(header,512);
      return (nb==512);
    }
    
    int16_t file_writeData(void *diskBuffer, uint32_t nd)
    { if(!mfile) return 0;
      uint32_t nb = mfile.write(diskBuffer,nd);
      return (nb==nd);
    }
    
    int16_t file_close(void)
    { mfile.close();
      return (!mfile);
    }
    
    /*
     * Custom Implementation
     * 
     */
    /************************ some utilities modified from time.cpp ************************/
    // leap year calculator expects year argument as years offset from 1970
    #define LEAP_YEAR(Y) ( ((1970+(Y))>0) && !((1970+(Y))%4) && ( ((1970+(Y))%100) || !((1970+(Y))%400) ) )
    
    static  const uint8_t monthDays[]={31,28,31,30,31,30,31,31,30,31,30,31}; 
    
    void day2date(uint32_t dd, uint32_t *day, uint32_t *month, uint32_t *year)
    {
      uint32_t yy= 0;
      uint32_t days = 0;
      while((unsigned)(days += (LEAP_YEAR(yy) ? 366 : 365)) <= dd) {yy++;}
      
      days -= LEAP_YEAR(yy) ? 366 : 365;
      dd  -= days; // now it is days in this year, starting at 0
    
      uint32_t mm=0;
      uint32_t monthLength=0;
      for (mm=0; mm<12; mm++) 
      {
        monthLength = monthDays[mm];
        if ((mm==1) && (LEAP_YEAR(yy))) monthLength++;
        if (dd >= monthLength) { dd -= monthLength; } else { break; }
      }
    
      *month =mm + 1;   // jan is month 1  
      *day  = dd + 1;   // day of month
      *year = yy + 1970;
    }
    
    void date2day(uint32_t *dd, uint32_t day, uint32_t month, uint32_t year)
    {
      day -= 1;
      month -= 1;
      year -= 1970;
      uint32_t dx=0;
      for (uint32_t ii = 0; ii < year; ii++) { dx += LEAP_YEAR(ii)? 366: 365; } 
      for (uint32_t ii = 0; ii < month; ii++)
      {
        dx += monthDays[ii];
        if((ii==2) && (LEAP_YEAR(year))) dx++; // after feb check for leap year
      }
      *dd = dx + day;
    }
    
    /************* Menu implementation ******************************/
    void do_menu1(void)
    {  // get parameters
    }
    void do_menu2(void)
    {  // set parameters
    }
    void do_menu3(void)
    {  // misc commands
    }
    
    /****************** File Utilities *****************************/
    void makeHeader(char *header)
    {
      memset(header,0,512);
    }
    
    int16_t makeFilename(char *filename)
    {
      uint32_t tt = rtc_get();
      int hh,mm,ss;
      int dd;
      ss= tt % 60; tt /= 60;
      mm= tt % 60; tt /= 60;
      hh= tt % 24; tt /= 24;
      dd= tt;
      sprintf(filename,"/%d/%02d_%02d_%02d.raw",dd,hh,mm,ss);
      Serial.println(filename);
      return 1;
    }
    
    int16_t checkPath(uint16_t store, char *filename)
    {
      int ln=strlen(filename);
      int i1=-1;
      for(int ii=0;ii<ln;ii++) if(filename[ii]=='/') i1=ii;
      if(i1<0) return 1; // no path
      filename[i1]=0;
      if(!storfs[store].exists(filename))
      { Serial.println(filename); 
        if(!storfs[store].mkdir(filename)) return 0;
      }
    
      filename[i1]='/';
      return 1;
    }
    
    uint32_t t_on = 60;
    int16_t check_filing(int16_t state)
    {
      static uint32_t to;
      if(state==2)
      {
        uint32_t tt = rtc_get();
        uint32_t dt = tt % t_on;
        if(dt<to) state = 3;
        to = dt;
      }
      return state;
    }
    
    /****************** Data Acquisition *******************************************/
    #define DO_TEST 1
    #define DO_I2S 2
    #define DO_ACQ DO_TEST
    
    #if DO_ACQ==DO_TEST
      /****************** Intervall timer(dummy example) *****************************/
      #include "IntervalTimer.h"
    
      IntervalTimer t1;
    
      static uint32_t acq_buffer[NBUF_ACQ];
    
      uint32_t acq_period=1000;
      int16_t acq_state=-1;
      void acq_isr(void);
    
      void acq_init(int32_t fsamp)
      { acq_period=128'000'000/fsamp;
        acq_state=0;
      }
    
      void acq_start(void)
      { if(acq_state) return;
        resetData();
        t1.begin(acq_isr, acq_period);
        acq_state=1;
      }
    
      void acq_stop(void)
      { if(acq_state<=0) return;
        t1.end();
        acq_state=0;
      }
    
      uint32_t acq_count=0;
      uint32_t acq_miss=0;
    
      void acq_isr(void)
      { acq_count++;
        for(int jj=0;jj<NCH_ACQ;jj++)
        {
          for(int ii=0; ii<NBUF_ACQ;ii++)
          {
            acq_buffer[jj+ii*NCH_ACQ]=acq_count;
          }
        }
        if(!pushData(acq_buffer)) acq_miss++;
      }
    
      int16_t acq_check(int16_t state)
      { if(!state)
        { // start acquisition
          acq_start();
        }
        if(state>3)
        { // stop acquisition
          acq_stop();
        }
        return state;
      }
    #else
    // try I2S (not working yet)
      static uint32_t tdm_rx_buffer[2*NBUF_I2S];
      static uint32_t acq_rx_buffer[NBUF_ACQ];
      #define I2S_DMA_PRIO 6
    
      #include "DMAChannel.h"
      DMAChannel dma;
    
      void acq_isr(void);
    
      #if defined(__MK66FX1M0__)
      //Teensy 3.6
          #define MCLK_SRC  3
          #define MCLK_SCALE 1
    
        // set MCLK to 48 MHz or a integer fraction (MCLK_SCALE) of it 
        // MCLK_MULT is set to 1 or 2 to minimize jitter
        // this reduces the possibilities for sampling frequencies
    
        #if (F_PLL == 96000000) //PLL is 96 MHz for F_CPU==48 or F_CPU==96 MHz
            #define MCLK_MULT 1 
            #define MCLK_DIV  (2*MCLK_SCALE) 
            //  #define MCLK_DIV  (3*MCLK_SCALE) 
        #elif F_PLL == 120000000
            #define MCLK_MULT 2
            #define MCLK_DIV  (5*MCLK_SCALE)
        #elif F_PLL == 144000000
            #define MCLK_MULT 1
            #define MCLK_DIV  (3*MCLK_SCALE)
        #elif F_PLL == 192000000
            #define MCLK_MULT 1
            #define MCLK_DIV  (4*MCLK_SCALE)
        #elif F_PLL == 240000000
            #define MCLK_MULT 1
            #define MCLK_DIV  (5*MCLK_SCALE)
        #else
            #error "set F_CPU to (48, 96, 120, 144, 192, 240) MHz"
        #endif
    
    #define BIT_DIV 4
    
    
        /*
         * estimation of sampling frequency
        MCLK 98 MHz * 1 / 2 = 48 MHz 
      N_ADC =1
      case 0
        nch=8
        Bit clock: 93750*(8*32) = 93750*256 = 24 MHz ->(I2S_RCR2_DIV(0))
      case 1
        nch=4
        Bit clock: 93750*(4*32) = 93750*128 = 12 MHz ->(I2S_RCR2_DIV(1))
      case 2
        nch=2
        Bit clock: 93750*(2*32) = 93750*64 = 6 MHz ->(I2S_RCR2_DIV(3))
    
      N_ADC =2
      case 3
        nch=4
        Bit clock: 93750*((4/2)*32) = 93750*64 = 6 MHz ->(I2S_RCR2_DIV(3))
    
      bitclock = fs *( nch*32)/n_adc = f_pll*mckl_mult/mckl_div/(2*bit_div)
    
      fs=bitclock*((nch/n_adc)*32)
    
      bitclock= f_pll*mckl_mult/mckl_div/(2*bit_div)
      fs=f_pll*mckl_mult/mckl_div/(2*bit_div)/((nch/n_adc)*32)
    */
    
        const int32_t fsamp0=(((F_PLL*MCLK_MULT)/MCLK_DIV)/(2*BIT_DIV)/(NCH_I2S*32/N_ADC));
    
    
        void acq_init(int32_t fsamp)
        {
            Serial.printf("%d %d\n",fsamp,fsamp0);
            SIM_SCGC6 |= SIM_SCGC6_I2S;
            SIM_SCGC7 |= SIM_SCGC7_DMA;
            SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
    
    /*
    P23 PTC2   I2S0_TX_FS (6)
    P9  PTC3   I2S0_TX_BCLK (6)
    P13 PTC5                    I2S0_RXD0 (4)
    P11 PTC6   I2S0_MCLK (6)    I2S0_RX_BCLK (4)
    P12 PTC7                    I2S0_RX_FS (4)
    P35 PTC8                    I2S0_MCLK (4)
    P36 PTC9                    I2S0_RX_BCLK (4)
    P37 PTC10                   I2S0_RX_FS (4)
    P38 PTC11                   I2S0_RXD1 (4)
    */
            #if I2S_CONFIG==0
    //            CORE_PIN39_CONFIG = PORT_PCR_MUX(6);  //pin39, PTA17, I2S0_MCLK
    //            CORE_PIN11_CONFIG = PORT_PCR_MUX(4);  //pin11, PTC6,  I2S0_RX_BCLK
    //            CORE_PIN12_CONFIG = PORT_PCR_MUX(4);  //pin12, PTC7,  I2S0_RX_FS
    //            CORE_PIN13_CONFIG = PORT_PCR_MUX(4);  //pin13, PTC5,  I2S0_RXD0
            #elif I2S_CONFIG==1
                CORE_PIN35_CONFIG = PORT_PCR_MUX(4);   // PTC8,  I2S0_MCLK
                CORE_PIN36_CONFIG = PORT_PCR_MUX(4);   // PTC9,  I2S0_RX_BCLK
                CORE_PIN37_CONFIG = PORT_PCR_MUX(4);   // PTC10, I2S0_RX_FS 
            #elif I2S_CONFIG==2
    //            CORE_PIN35_CONFIG = PORT_PCR_MUX(4) | PORT_PCR_SRE | PORT_PCR_DSE;  //pin35, PTC8,   I2S0_MCLK (SLEW rate (SRE)?)
    //            CORE_PIN36_CONFIG = PORT_PCR_MUX(4);  //pin36, PTC9,   I2S0_RX_BCLK
    //            CORE_PIN37_CONFIG = PORT_PCR_MUX(4);  //pin37, PTC10,  I2S0_RX_FS
    //            CORE_PIN27_CONFIG = PORT_PCR_MUX(6);  //pin27, PTA15,  I2S0_RXD0
            #endif
    
            I2S0_RCSR=0;
    
            // enable MCLK output // MCLK = INP *((MULT)/(DIV))
            I2S0_MDR = I2S_MDR_FRACT((MCLK_MULT-1)) | I2S_MDR_DIVIDE((MCLK_DIV-1));
            while(I2S0_MCR & I2S_MCR_DUF);
            I2S0_MCR = I2S_MCR_MICS(MCLK_SRC) | I2S_MCR_MOE;
            
            I2S0_RMR=0; // enable receiver mask
            I2S0_RCR1 = I2S_RCR1_RFW(3); 
    
            I2S0_RCR2 = I2S_RCR2_SYNC(0) 
                        | I2S_RCR2_BCP 
                        | I2S_RCR2_BCD  // Bit clock in master mode
                        | I2S_RCR2_DIV((BIT_DIV-1)); // divides MCLK down to Bitclock (BIT_DIV)*2
                        
            I2S0_RCR4 = I2S_RCR4_FRSZ((FRAME_I2S-1)) 
                        | I2S_RCR4_FSE  // frame sync early
                        | I2S_RCR4_FSD  // Frame sync in master mode
                        | I2S_RCR4_MF;
            
            I2S0_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);
    
    
      dma.begin(true); // Allocate the DMA channel first
    
    #if N_ADC==1
              CORE_PIN13_CONFIG = PORT_PCR_MUX(4);  // PTC5,  I2S0_RXD0
    
              I2S0_RCR3 = I2S_RCR3_RCE;
    
              dma.TCD->SADDR = &I2S0_RDR0;
              dma.TCD->SOFF = 0;
              dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
              dma.TCD->NBYTES_MLNO = 4;
              dma.TCD->SLAST = 0;
    #elif N_ADC==2
              CORE_PIN13_CONFIG = PORT_PCR_MUX(4);  // PTC5,  I2S0_RXD0
              CORE_PIN38_CONFIG = PORT_PCR_MUX(4);  // PTC11, I2S0_RXD1
    
              I2S0_RCR3 = I2S_RCR3_RCE_2CH;
    
              dma.TCD->SADDR = &I2S0_RDR0;
              dma.TCD->SOFF = 4;
              dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
              dma.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_SMLOE |
                  DMA_TCD_NBYTES_MLOFFYES_MLOFF(-8) |
                  DMA_TCD_NBYTES_MLOFFYES_NBYTES(8);
              dma.TCD->SLAST = -8;
    #endif
              dma.TCD->DADDR = tdm_rx_buffer;
              dma.TCD->DOFF = 4;
              dma.TCD->CITER_ELINKNO = NBUF_I2S;
              dma.TCD->DLASTSGA = -sizeof(tdm_rx_buffer);
              dma.TCD->BITER_ELINKNO = NBUF_I2S;
              dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
              dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_RX);
              dma.enable();
    
              I2S0_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
              dma.attachInterrupt(acq_isr,I2S_DMA_PRIO*16);	
        }
    
        void acq_start(void)
        {
    
        }
        void acq_stop(void)
        {
            
        }
    
      #elif defined(__IMXRT1062__)
      //Teensy 4.x
    
        #define IMXRT_CACHE_ENABLED 2 // 0=disabled, 1=WT, 2= WB
    
        /************************* I2S *************************************************/
        void set_audioClock(int nfact, int32_t nmult, uint32_t ndiv, bool force) // sets PLL4
          {
              if (!force && (CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_ENABLE)) return;
    
              CCM_ANALOG_PLL_AUDIO = CCM_ANALOG_PLL_AUDIO_BYPASS | CCM_ANALOG_PLL_AUDIO_ENABLE
                          | CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(2) // 2: 1/4; 1: 1/2; 0: 1/1
                          | CCM_ANALOG_PLL_AUDIO_DIV_SELECT(nfact);
    
              CCM_ANALOG_PLL_AUDIO_NUM   = nmult & CCM_ANALOG_PLL_AUDIO_NUM_MASK;
              CCM_ANALOG_PLL_AUDIO_DENOM = ndiv & CCM_ANALOG_PLL_AUDIO_DENOM_MASK;
              
              CCM_ANALOG_PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_POWERDOWN;//Switch on PLL
              while (!(CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_LOCK)) {}; //Wait for pll-lock
              
              const int div_post_pll = 1; // other values: 2,4
              CCM_ANALOG_MISC2 &= ~(CCM_ANALOG_MISC2_DIV_MSB | CCM_ANALOG_MISC2_DIV_LSB);
              if(div_post_pll>1) CCM_ANALOG_MISC2 |= CCM_ANALOG_MISC2_DIV_LSB;
              if(div_post_pll>3) CCM_ANALOG_MISC2 |= CCM_ANALOG_MISC2_DIV_MSB;
              
              CCM_ANALOG_PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_BYPASS;   //Disable Bypass
          }
    
          void acq_init(int32_t fsamp)
          {
              CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);
    
              // if either transmitter or receiver is enabled, do nothing
              if (I2S1_RCSR & I2S_RCSR_RE) return;
              //PLL:
              int fs = fsamp;
              int ovr = FRAME_I2S*32;
              // PLL between 27*24 = 648MHz und 54*24=1296MHz
              int n1 = 4;                    //4; //SAI prescaler 4 => (n1*n2) = multiple of 4
              int n2 = 1 + (24000000 * 27) / (fs * ovr * n1);
              Serial.printf("fs=%d, n1=%d, n2=%d, %d (>27 && < 54)\r\n", 
                            fs, n1,n2,n1*n2*(fs/1000)*ovr/24000);
    
              double C = ((double)fs * ovr * n1 * n2) / 24000000;
              int c0 = C;
              int c2 = 10000;
              int c1 = C * c2 - (c0 * c2);
              set_audioClock(c0, c1, c2, true);
    
              // clear SAI1_CLK register locations
              CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI1_CLK_SEL_MASK))
                  | CCM_CSCMR1_SAI1_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4
    
              n1 = n1 / 2; //Double Speed for TDM
    
              CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK))
                  | CCM_CS1CDR_SAI1_CLK_PRED((n1-1)) // &0x07
                  | CCM_CS1CDR_SAI1_CLK_PODF((n2-1)); // &0x3f
    
              IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK))
                      | (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0));	//Select MCLK
    
              I2S1_RMR = 0;
              I2S1_RCR1 = I2S_RCR1_RFW(4);
              I2S1_RCR2 = I2S_RCR2_SYNC(0) | I2S_TCR2_BCP | I2S_RCR2_MSEL(1)
                  | I2S_RCR2_BCD | I2S_RCR2_DIV(0);
              
              I2S1_RCR4 = I2S_RCR4_FRSZ((FRAME_I2S-1)) | I2S_RCR4_SYWD(0) | I2S_RCR4_MF
                  | I2S_RCR4_FSE | I2S_RCR4_FSD;
              I2S1_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);
    
            	CORE_PIN23_CONFIG = 3;  //1:MCLK 
              CORE_PIN21_CONFIG = 3;  //1:RX_BCLK
              CORE_PIN20_CONFIG = 3;  //1:RX_SYNC
    #if N_ADC==1
              I2S1_RCR3 = I2S_RCR3_RCE;
              CORE_PIN8_CONFIG  = 3;  //RX_DATA0
              IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2;
    
              dma.TCD->SADDR = &I2S1_RDR0;
              dma.TCD->SOFF = 0;
              dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
              dma.TCD->NBYTES_MLNO = 4;
              dma.TCD->SLAST = 0;
    #elif N_ADC==2
              I2S1_RCR3 = I2S_RCR3_RCE_2CH;
              CORE_PIN8_CONFIG  = 3;  //RX_DATA0
              CORE_PIN6_CONFIG  = 3;  //RX_DATA1
              IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; // GPIO_B1_00_ALT3, pg 873
              IOMUXC_SAI1_RX_DATA1_SELECT_INPUT = 1; // GPIO_B0_10_ALT3, pg 873
    
              dma.TCD->SADDR = &I2S1_RDR0;
              dma.TCD->SOFF = 4;
              dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
              dma.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_SMLOE |
                  DMA_TCD_NBYTES_MLOFFYES_MLOFF(-8) |
                  DMA_TCD_NBYTES_MLOFFYES_NBYTES(8);
              dma.TCD->SLAST = -8;
    #endif
              dma.TCD->DADDR = tdm_rx_buffer;
              dma.TCD->DOFF = 4;
              dma.TCD->CITER_ELINKNO = NBUF_I2S;
              dma.TCD->DLASTSGA = -sizeof(tdm_rx_buffer);
              dma.TCD->BITER_ELINKNO = NBUF_I2S;
              dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
              dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX);
              dma.enable();
    
              I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
              dma.attachInterrupt(acq_isr,I2S_DMA_PRIO*16);	
          }
    
          void acq_start(void)
          {
    
          }
          void acq_stop(void)
          {
              
          }
      #endif
    
      uint32_t acq_count=0;
      uint32_t acq_miss=0;
    
        void acq_isr(void)
        {
            uint32_t daddr;
            uint32_t *src;
            acq_count++;
    
            daddr = (uint32_t)(dma.TCD->DADDR);
            dma.clearInterrupt();
    
            if (daddr < (uint32_t)tdm_rx_buffer + sizeof(tdm_rx_buffer) / 2) {
                // DMA is receiving to the first half of the buffer
                // need to remove data from the second half
                src = &tdm_rx_buffer[NBUF_I2S];
            } else {
                // DMA is receiving to the second half of the buffer
                // need to remove data from the first half
                src = &tdm_rx_buffer[0];
            }
    
            #if IMXRT_CACHE_ENABLED >=1
                arm_dcache_delete((void*)src, sizeof(tdm_rx_buffer) / 2);
            #endif
    
            for(int jj=0;jj<NCH_ACQ;jj++)
            {
              for(int ii=0; ii<NBUF_ACQ;ii++)
              {
                acq_rx_buffer[jj+ii*NCH_ACQ]=src[jj+ii*NCH_I2S];
              }
            }
    
            if(!pushData(acq_rx_buffer)) acq_miss++;
    
        }
    
      int16_t acq_check(int16_t state)
      { if(!state)
        { // start acquisition
          acq_start();
        }
        if(state>3)
        { // stop acquisition
          acq_stop();
        }
        return state;
      }
    #endif

  12. #787
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    6,772
    @KurtE - @defragster
    Still puzzling me about the problem with using the MTP logger. Wondering if there is some sort of issue with using DMA and/or I2S with QSPI NAND Flash? But funny thing is that it does work with SPI NAND? Didn't really dig into it - think going to try a simple logger just as a test. Any thoughts on if this is possible?

    It seems to be failing on mkdir and/or file open if I change to just write a file:
    Code:
    Logger
    Device ID: 0xEFAA21
    attempting to mount existing media
    Setup done
     Enter s to start acquisition and q to stop acquisition
    
    Start
    acquistion start
    loop: 20595404    0    0    0    1 0
    /17897/00_00_08.raw
    MAKING DIRECTORY
    /17897
    Below is a non-MTP version that I am using to test - figure would take one variable away:
    Code:
    #include "Arduino.h"
    
    #define NDAT 128L
    #define NCH_ACQ 1
    #define NCH_I2S 4
    #define NBUF_ACQ (NCH_ACQ*NDAT)
    #define NBUF_I2S (NCH_I2S*NDAT)
    #define N_ADC 2
    #define FRAME_I2S (NCH_I2S/N_ADC)
    
    #define I2S_CONFIG 1
    // CONFIG 1 (PURE RX)
    //PIN  36 BCLK
    //PIN  37 FS
    //PIN  13 RXD0
    //PIN  38 RXD1
    
    #include <LittleFS.h>
    #include <LittleFS_NAND.h>
    
    //#define TEST_SPI_NAND
    #define TEST_QSPI_NAND
    
    #define USE_W25N01 1
    //#define USE_W25N02 1
    //#define UES_W25M02 1
    
    // Set for SPI usage
    //const int FlashChipSelect = 10; // AUDIO BOARD
    #if defined(USE_W25N01)
      const int FlashChipSelect = 3; // PJRC Mem board 64MB on #5, #6 : NAND 1Gb on #3, 2GB on #4
    #elif defined(USE_W25N02)
      const int FlashChipSelect = 4; // PJRC Mem board 64MB on #5, #6 : NAND 1Gb on #3, 2GB on #4
    #else //W25M02
      const int FlashChipSelect = 6; // digital pin for flash chip CS pin
    #endif
    //const int FlashChipSelect = 5; // PJRC Mem board 64MB on #5, #6 : NAND 1Gb on #3, 2GB on #4
    //const int FlashChipSelect = 6; // digital pin for flash chip CS pin
    
    
    #if defined(TEST_QSPI_NAND)
    char szDiskMem[] = "QPI_NAND";
    LittleFS_QPINAND myNAND;
    #elif defined(TEST_SPI_NAND)
    char szDiskMem[] = "SPI_NAND";
    LittleFS_SPINAND myNAND;
    #endif
    
    
    
    void logg(uint32_t del, const char *txt);
    
    int16_t state;
    int16_t do_logger(uint16_t store, int16_t state);
    int16_t do_menu(int16_t state);
    int16_t check_filing(int16_t state);
    
    void acq_init(int32_t fsamp);
    int16_t acq_check(int16_t state);
    
    void setup()
    { 
      Serial.begin(115200);
      while(!Serial.available() && millis() < 5000); // or third option to wait up to 5 seconds and then continue
      Serial.println("Logger");
    
      #if defined(TEST_SPI_NAND)
        if (!myNAND.begin( FlashChipSelect )) {
      #else
        if (!myNAND.begin()) {
      #endif
        Serial.printf("Error starting %s\n", szDiskMem);
        while( 1 );
      }
    
    
      acq_init(93750); // is fixed for this example, to be modified below
      state=-1;
    
      Serial.println("Setup done");
      Serial.println(" Enter s to start acquisition and q to stop acquisition");
      Serial.flush();
    }
    
    uint32_t loop_count=0;
    void loop()
    { loop_count++;
      state = do_menu(state);
      state = acq_check(state);
      state = check_filing(state);
      state = do_logger(0,state);
    
      if(state>=0) logg(1000,"loop");
      //asm("wfi"); // may wait forever on T4.x
    
    
    }
    
    /**************** Online logging *******************************/
    extern uint32_t loop_count, acq_count, acq_miss, maxDel;
    extern uint16_t maxCount;
    void logg(uint32_t del, const char *txt)
    { static uint32_t to;
      if(millis()-to > del)
      {
        Serial.printf("%s: %6d %4d %4d %4d %4d %d\n",
                txt,loop_count, acq_count, acq_miss,maxCount, maxDel,state); 
        loop_count=0;
        acq_count=0;
        acq_miss=0;
        maxCount=0;
        maxDel=0;
        to=millis();
      }
    }
    
    /*************************** Circular Buffer ********************/
    #if defined ARDUINO_TEENSY41
      #define HAVE_PSRAM 1
    #else
      #define HAVE_PSRAM 0
    #endif
    
      #if HAVE_PSRAM==1
        #define MAXBUF (1000) // 3000 kB   // < 5461 for 16 MByte PSRAM
    
    //    extern "C" uint8_t external_psram_size;
    //    uint8_t size = external_psram_size;
    //    uint32_t *memory_begin = (uint32_t *)(0x70000000);
    //    uint32_t *data_buffer = memory_begin;
    
        uint32_t *data_buffer = (uint32_t *)extmem_malloc(MAXBUF*128*sizeof(uint32_t));
      #else
        #if defined(ARDUINO_TEENSY41)
          #define MAXBUF (46)           // 138 kB
        #elif defined(ARDUINO_TEENSY40)
          #define MAXBUF (46)           // 138 kB
        #elif defined(__MK66FX1M0__)
          #define MAXBUF (46)           // 138 kB
        #elif defined(__MK20DX256__)
          #define MAXBUF (12)           // 36 kB
        #endif
        uint32_t data_buffer[MAXBUF*NBUF_ACQ];
      #endif
    
    static uint16_t front_ = 0, rear_ = 0;
    uint16_t getCount () { if(front_ >= rear_) return front_ - rear_; return front_+ MAXBUF -rear_; }
    uint16_t maxCount=0;
    
    void resetData(void) {  front_ = 0;  rear_ = 0; }
    
    uint16_t pushData(uint32_t * src)
    { 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;
      //
      uint16_t count;
      count = (front_ >= rear_) ? (front_ - rear_) : front_+ (MAXBUF -rear_) ;
      if(count>maxCount) maxCount=count;
      //
      return 1;
    }
    
    uint16_t pullData(uint32_t * dst, uint32_t ndbl)
    { uint16_t r = (rear_/ndbl) ;
      if(r == (front_/ndbl)) return 0;
      if(++r >= (MAXBUF/ndbl)) r=0;
      uint32_t *ptr= data_buffer + r*ndbl*NBUF_ACQ;
      memcpy(dst,ptr,ndbl*NBUF_ACQ*4);
      rear_ = r*ndbl;
      return 1;
    }
    
    /*************************** Filing *****************************/
    int16_t file_open(uint16_t store);
    int16_t file_writeHeader(void);
    int16_t file_writeData(void *diskBuffer, uint32_t ndbl);
    int16_t file_close(void);
    #define NDBL 1
    #define NBUF_DISK (NDBL*NBUF_ACQ)
    uint32_t diskBuffer[NBUF_DISK];
    uint32_t maxDel=0;
    
    int16_t do_logger(uint16_t store, int16_t state)
    { uint32_t to=millis();
      if(pullData(diskBuffer,NDBL))
      {
        if(state==0)
        { // acquisition is running, need to open file
          if(!file_open(store)) 
            return -2;
          state=1;
          Serial.println("file_open");
        }
        if(state==1)
        { // file just opended, need to write header
          if(!file_writeHeader()) return -3;
          Serial.println("wrote header");
          state=2;
          
        }
        if(state>=2)
        { // write data to disk
          if(!file_writeData(diskBuffer,NBUF_DISK*4)) return -4;
          Serial.println("write data to disk");
        }
      }
    
      if(state==3)
      { // close file, but continue acquisition
        if(!file_close()) return -5;
        Serial.println("close file");
        state=0;
      }
    
      if(state==4)
      { // close file and stop acquisition
        if(!file_close()) return -6;
        Serial.println("file_close and stop");
        state=-1;
      }
    
      uint32_t dt=millis()-to;
      if(dt>maxDel) maxDel=dt;
    
      return state;
    }
    
    /******************** Menu ***************************/
    void do_menu1(void);
    void do_menu2(void);
    void do_menu3(void);
    
    int16_t do_menu(int16_t state)
    { // check Serial input
      if(!Serial.available()) return state;
      char cc = Serial.read();
      switch(cc)
      {
        case 's': // start acquisition
          if(state>=0) return state;
          state=0;
          Serial.println("\nStart");
          break;
        case 'q': // stop acquisition
          if(state<0) return state;
          state=4;
          Serial.println("\nStop");
          break;
        case '?': // get parameters
          do_menu1();
          break;
        case '!': // set parameters
          if(state>=0) return state;
          do_menu2();
          break;
        case ':': // misc commands
          if(state>=0) return state;
          do_menu3();
          break;
        default:
          break;
      }
      return state;
    }
    
    /************ Basic File System Interface *************************/
    //#include "SD.h"
    //extern SDClass sdx[];
    static File mfile;
    
    char header[512];
    
    void makeHeader(char *header);
    int16_t makeFilename(char *filename);
    int16_t checkPath(uint16_t store, char *filename);
    
    int16_t file_open(uint16_t store)
    { char filename[80];
      if(!makeFilename(filename)) {
        Serial.println("filename created");
        return 0;
      }
      if(!checkPath(store, filename)){
        Serial.println("check path fail");
        return 0;
      }
      Serial.println("Opening FIle");
      mfile = myNAND.open(filename,FILE_WRITE_BEGIN);
      Serial.printf("File Open: %d\n", mfile);
      return !(!mfile);
    }
    
    int16_t file_writeHeader(void)
    { if(!mfile) return 0;
      makeHeader(header);
      size_t nb = mfile.write(header,512);
      return (nb==512);
    }
    
    int16_t file_writeData(void *diskBuffer, uint32_t nd)
    { if(!mfile) return 0;
      uint32_t nb = mfile.write(diskBuffer,nd);
      return (nb==nd);
    }
    
    int16_t file_close(void)
    { mfile.close();
      return (!mfile);
    }
    
    /*
     * Custom Implementation
     * 
     */
    /************************ some utilities modified from time.cpp ************************/
    // leap year calculator expects year argument as years offset from 1970
    #define LEAP_YEAR(Y) ( ((1970+(Y))>0) && !((1970+(Y))%4) && ( ((1970+(Y))%100) || !((1970+(Y))%400) ) )
    
    static  const uint8_t monthDays[]={31,28,31,30,31,30,31,31,30,31,30,31}; 
    
    void day2date(uint32_t dd, uint32_t *day, uint32_t *month, uint32_t *year)
    {
      uint32_t yy= 0;
      uint32_t days = 0;
      while((unsigned)(days += (LEAP_YEAR(yy) ? 366 : 365)) <= dd) {yy++;}
      
      days -= LEAP_YEAR(yy) ? 366 : 365;
      dd  -= days; // now it is days in this year, starting at 0
    
      uint32_t mm=0;
      uint32_t monthLength=0;
      for (mm=0; mm<12; mm++) 
      {
        monthLength = monthDays[mm];
        if ((mm==1) && (LEAP_YEAR(yy))) monthLength++;
        if (dd >= monthLength) { dd -= monthLength; } else { break; }
      }
    
      *month =mm + 1;   // jan is month 1  
      *day  = dd + 1;   // day of month
      *year = yy + 1970;
    }
    
    void date2day(uint32_t *dd, uint32_t day, uint32_t month, uint32_t year)
    {
      day -= 1;
      month -= 1;
      year -= 1970;
      uint32_t dx=0;
      for (uint32_t ii = 0; ii < year; ii++) { dx += LEAP_YEAR(ii)? 366: 365; } 
      for (uint32_t ii = 0; ii < month; ii++)
      {
        dx += monthDays[ii];
        if((ii==2) && (LEAP_YEAR(year))) dx++; // after feb check for leap year
      }
      *dd = dx + day;
    }
    
    /************* Menu implementation ******************************/
    void do_menu1(void)
    {  // get parameters
    }
    void do_menu2(void)
    {  // set parameters
    }
    void do_menu3(void)
    {  // misc commands
    }
    
    /****************** File Utilities *****************************/
    void makeHeader(char *header)
    {
      memset(header,0,512);
    }
    
    int16_t makeFilename(char *filename)
    {
      uint32_t tt = rtc_get();
      int hh,mm,ss;
      int dd;
      ss= tt % 60; tt /= 60;
      mm= tt % 60; tt /= 60;
      hh= tt % 24; tt /= 24;
      dd= tt;
      sprintf(filename,"/%d/%02d_%02d_%02d.raw",dd,hh,mm,ss);
      Serial.println(filename);
      return 1;
    }
    
    int16_t checkPath(uint16_t store, char *filename)
    {
      int ln=strlen(filename);
      int i1=-1;
      for(int ii=0;ii<ln;ii++) if(filename[ii]=='/') i1=ii;
      if(i1<0) {
        Serial.println("NO PATH");
        return 1; // no path
      }
      filename[i1]=0;
      if(!myNAND.exists(filename))
      { 
        Serial.println("MAKING DIRECTORY");
        Serial.println(filename); 
        if(!myNAND.mkdir(filename)) 
        {
          Serial.println("directory fail");
          return 0;
        }
      }
      Serial.println("Check path OK");
      filename[i1]='/';
      return 1;
    }
    
    uint32_t t_on = 60;
    int16_t check_filing(int16_t state)
    {
      static uint32_t to;
      if(state==2)
      {
        uint32_t tt = rtc_get();
        uint32_t dt = tt % t_on;
        if(dt<to) state = 3;
        to = dt;
      }
      return state;
    }
    
    /****************** Data Acquisition *******************************************/
    #define DO_TEST 1
    #define DO_I2S 2
    #define DO_ACQ DO_TEST
    
    #if DO_ACQ==DO_TEST
      /****************** Intervall timer(dummy example) *****************************/
      #include "IntervalTimer.h"
    
      IntervalTimer t1;
    
      static uint32_t acq_buffer[NBUF_ACQ];
    
      uint32_t acq_period=1000;
      int16_t acq_state=-1;
      void acq_isr(void);
    
      void acq_init(int32_t fsamp)
      { acq_period=128'000'000/fsamp;
        acq_state=0;
      }
    
      void acq_start(void)
      { if(acq_state) return;
          Serial.println("acquistion start");
        resetData();
        t1.begin(acq_isr, acq_period);
        acq_state=1;
      }
    
      void acq_stop(void)
      { if(acq_state<=0) return;
        t1.end();
        acq_state=0;
      }
    
      uint32_t acq_count=0;
      uint32_t acq_miss=0;
    
      void acq_isr(void)
      { acq_count++;
        for(int jj=0;jj<NCH_ACQ;jj++)
        {
          for(int ii=0; ii<NBUF_ACQ;ii++)
          {
            acq_buffer[jj+ii*NCH_ACQ]=acq_count;
          }
        }
        if(!pushData(acq_buffer)) acq_miss++;
      }
    
      int16_t acq_check(int16_t state)
      { if(!state)
        { // start acquisition
          acq_start();
        }
        if(state>3)
        { // stop acquisition
          acq_stop();
        }
        return state;
      }
    #else
    // try I2S (not working yet)
      static uint32_t tdm_rx_buffer[2*NBUF_I2S];
      static uint32_t acq_rx_buffer[NBUF_ACQ];
      #define I2S_DMA_PRIO 6
    
      #include "DMAChannel.h"
      DMAChannel dma;
    
      void acq_isr(void);
    
      #if defined(__MK66FX1M0__)
      //Teensy 3.6
          #define MCLK_SRC  3
          #define MCLK_SCALE 1
    
        // set MCLK to 48 MHz or a integer fraction (MCLK_SCALE) of it 
        // MCLK_MULT is set to 1 or 2 to minimize jitter
        // this reduces the possibilities for sampling frequencies
    
        #if (F_PLL == 96000000) //PLL is 96 MHz for F_CPU==48 or F_CPU==96 MHz
            #define MCLK_MULT 1 
            #define MCLK_DIV  (2*MCLK_SCALE) 
            //  #define MCLK_DIV  (3*MCLK_SCALE) 
        #elif F_PLL == 120000000
            #define MCLK_MULT 2
            #define MCLK_DIV  (5*MCLK_SCALE)
        #elif F_PLL == 144000000
            #define MCLK_MULT 1
            #define MCLK_DIV  (3*MCLK_SCALE)
        #elif F_PLL == 192000000
            #define MCLK_MULT 1
            #define MCLK_DIV  (4*MCLK_SCALE)
        #elif F_PLL == 240000000
            #define MCLK_MULT 1
            #define MCLK_DIV  (5*MCLK_SCALE)
        #else
            #error "set F_CPU to (48, 96, 120, 144, 192, 240) MHz"
        #endif
    
    #define BIT_DIV 4
    
    
        /*
         * estimation of sampling frequency
        MCLK 98 MHz * 1 / 2 = 48 MHz 
      N_ADC =1
      case 0
        nch=8
        Bit clock: 93750*(8*32) = 93750*256 = 24 MHz ->(I2S_RCR2_DIV(0))
      case 1
        nch=4
        Bit clock: 93750*(4*32) = 93750*128 = 12 MHz ->(I2S_RCR2_DIV(1))
      case 2
        nch=2
        Bit clock: 93750*(2*32) = 93750*64 = 6 MHz ->(I2S_RCR2_DIV(3))
    
      N_ADC =2
      case 3
        nch=4
        Bit clock: 93750*((4/2)*32) = 93750*64 = 6 MHz ->(I2S_RCR2_DIV(3))
    
      bitclock = fs *( nch*32)/n_adc = f_pll*mckl_mult/mckl_div/(2*bit_div)
    
      fs=bitclock*((nch/n_adc)*32)
    
      bitclock= f_pll*mckl_mult/mckl_div/(2*bit_div)
      fs=f_pll*mckl_mult/mckl_div/(2*bit_div)/((nch/n_adc)*32)
    */
    
        const int32_t fsamp0=(((F_PLL*MCLK_MULT)/MCLK_DIV)/(2*BIT_DIV)/(NCH_I2S*32/N_ADC));
    
    
        void acq_init(int32_t fsamp)
        {
            Serial.printf("%d %d\n",fsamp,fsamp0);
            SIM_SCGC6 |= SIM_SCGC6_I2S;
            SIM_SCGC7 |= SIM_SCGC7_DMA;
            SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
    
    /*
    P23 PTC2   I2S0_TX_FS (6)
    P9  PTC3   I2S0_TX_BCLK (6)
    P13 PTC5                    I2S0_RXD0 (4)
    P11 PTC6   I2S0_MCLK (6)    I2S0_RX_BCLK (4)
    P12 PTC7                    I2S0_RX_FS (4)
    P35 PTC8                    I2S0_MCLK (4)
    P36 PTC9                    I2S0_RX_BCLK (4)
    P37 PTC10                   I2S0_RX_FS (4)
    P38 PTC11                   I2S0_RXD1 (4)
    */
            #if I2S_CONFIG==0
    //            CORE_PIN39_CONFIG = PORT_PCR_MUX(6);  //pin39, PTA17, I2S0_MCLK
    //            CORE_PIN11_CONFIG = PORT_PCR_MUX(4);  //pin11, PTC6,  I2S0_RX_BCLK
    //            CORE_PIN12_CONFIG = PORT_PCR_MUX(4);  //pin12, PTC7,  I2S0_RX_FS
    //            CORE_PIN13_CONFIG = PORT_PCR_MUX(4);  //pin13, PTC5,  I2S0_RXD0
            #elif I2S_CONFIG==1
                CORE_PIN35_CONFIG = PORT_PCR_MUX(4);   // PTC8,  I2S0_MCLK
                CORE_PIN36_CONFIG = PORT_PCR_MUX(4);   // PTC9,  I2S0_RX_BCLK
                CORE_PIN37_CONFIG = PORT_PCR_MUX(4);   // PTC10, I2S0_RX_FS 
            #elif I2S_CONFIG==2
    //            CORE_PIN35_CONFIG = PORT_PCR_MUX(4) | PORT_PCR_SRE | PORT_PCR_DSE;  //pin35, PTC8,   I2S0_MCLK (SLEW rate (SRE)?)
    //            CORE_PIN36_CONFIG = PORT_PCR_MUX(4);  //pin36, PTC9,   I2S0_RX_BCLK
    //            CORE_PIN37_CONFIG = PORT_PCR_MUX(4);  //pin37, PTC10,  I2S0_RX_FS
    //            CORE_PIN27_CONFIG = PORT_PCR_MUX(6);  //pin27, PTA15,  I2S0_RXD0
            #endif
    
            I2S0_RCSR=0;
    
            // enable MCLK output // MCLK = INP *((MULT)/(DIV))
            I2S0_MDR = I2S_MDR_FRACT((MCLK_MULT-1)) | I2S_MDR_DIVIDE((MCLK_DIV-1));
            while(I2S0_MCR & I2S_MCR_DUF);
            I2S0_MCR = I2S_MCR_MICS(MCLK_SRC) | I2S_MCR_MOE;
            
            I2S0_RMR=0; // enable receiver mask
            I2S0_RCR1 = I2S_RCR1_RFW(3); 
    
            I2S0_RCR2 = I2S_RCR2_SYNC(0) 
                        | I2S_RCR2_BCP 
                        | I2S_RCR2_BCD  // Bit clock in master mode
                        | I2S_RCR2_DIV((BIT_DIV-1)); // divides MCLK down to Bitclock (BIT_DIV)*2
                        
            I2S0_RCR4 = I2S_RCR4_FRSZ((FRAME_I2S-1)) 
                        | I2S_RCR4_FSE  // frame sync early
                        | I2S_RCR4_FSD  // Frame sync in master mode
                        | I2S_RCR4_MF;
            
            I2S0_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);
    
    
      dma.begin(true); // Allocate the DMA channel first
    
    #if N_ADC==1
              CORE_PIN13_CONFIG = PORT_PCR_MUX(4);  // PTC5,  I2S0_RXD0
    
              I2S0_RCR3 = I2S_RCR3_RCE;
    
              dma.TCD->SADDR = &I2S0_RDR0;
              dma.TCD->SOFF = 0;
              dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
              dma.TCD->NBYTES_MLNO = 4;
              dma.TCD->SLAST = 0;
    #elif N_ADC==2
              CORE_PIN13_CONFIG = PORT_PCR_MUX(4);  // PTC5,  I2S0_RXD0
              CORE_PIN38_CONFIG = PORT_PCR_MUX(4);  // PTC11, I2S0_RXD1
    
              I2S0_RCR3 = I2S_RCR3_RCE_2CH;
    
              dma.TCD->SADDR = &I2S0_RDR0;
              dma.TCD->SOFF = 4;
              dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
              dma.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_SMLOE |
                  DMA_TCD_NBYTES_MLOFFYES_MLOFF(-8) |
                  DMA_TCD_NBYTES_MLOFFYES_NBYTES(8);
              dma.TCD->SLAST = -8;
    #endif
              dma.TCD->DADDR = tdm_rx_buffer;
              dma.TCD->DOFF = 4;
              dma.TCD->CITER_ELINKNO = NBUF_I2S;
              dma.TCD->DLASTSGA = -sizeof(tdm_rx_buffer);
              dma.TCD->BITER_ELINKNO = NBUF_I2S;
              dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
              dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_RX);
              dma.enable();
    
              I2S0_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
              dma.attachInterrupt(acq_isr,I2S_DMA_PRIO*16);  
        }
    
        void acq_start(void)
        {
    
        }
        void acq_stop(void)
        {
            
        }
    
      #elif defined(__IMXRT1062__)
      //Teensy 4.x
    
        #define IMXRT_CACHE_ENABLED 2 // 0=disabled, 1=WT, 2= WB
    
        /************************* I2S *************************************************/
        void set_audioClock(int nfact, int32_t nmult, uint32_t ndiv, bool force) // sets PLL4
          {
              if (!force && (CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_ENABLE)) return;
    
              CCM_ANALOG_PLL_AUDIO = CCM_ANALOG_PLL_AUDIO_BYPASS | CCM_ANALOG_PLL_AUDIO_ENABLE
                          | CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(2) // 2: 1/4; 1: 1/2; 0: 1/1
                          | CCM_ANALOG_PLL_AUDIO_DIV_SELECT(nfact);
    
              CCM_ANALOG_PLL_AUDIO_NUM   = nmult & CCM_ANALOG_PLL_AUDIO_NUM_MASK;
              CCM_ANALOG_PLL_AUDIO_DENOM = ndiv & CCM_ANALOG_PLL_AUDIO_DENOM_MASK;
              
              CCM_ANALOG_PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_POWERDOWN;//Switch on PLL
              while (!(CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_LOCK)) {}; //Wait for pll-lock
              
              const int div_post_pll = 1; // other values: 2,4
              CCM_ANALOG_MISC2 &= ~(CCM_ANALOG_MISC2_DIV_MSB | CCM_ANALOG_MISC2_DIV_LSB);
              if(div_post_pll>1) CCM_ANALOG_MISC2 |= CCM_ANALOG_MISC2_DIV_LSB;
              if(div_post_pll>3) CCM_ANALOG_MISC2 |= CCM_ANALOG_MISC2_DIV_MSB;
              
              CCM_ANALOG_PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_BYPASS;   //Disable Bypass
          }
    
          void acq_init(int32_t fsamp)
          {
              CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);
    
              // if either transmitter or receiver is enabled, do nothing
              if (I2S1_RCSR & I2S_RCSR_RE) return;
              //PLL:
              int fs = fsamp;
              int ovr = FRAME_I2S*32;
              // PLL between 27*24 = 648MHz und 54*24=1296MHz
              int n1 = 4;                    //4; //SAI prescaler 4 => (n1*n2) = multiple of 4
              int n2 = 1 + (24000000 * 27) / (fs * ovr * n1);
              Serial.printf("fs=%d, n1=%d, n2=%d, %d (>27 && < 54)\r\n", 
                            fs, n1,n2,n1*n2*(fs/1000)*ovr/24000);
    
              double C = ((double)fs * ovr * n1 * n2) / 24000000;
              int c0 = C;
              int c2 = 10000;
              int c1 = C * c2 - (c0 * c2);
              set_audioClock(c0, c1, c2, true);
    
              // clear SAI1_CLK register locations
              CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI1_CLK_SEL_MASK))
                  | CCM_CSCMR1_SAI1_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4
    
              n1 = n1 / 2; //Double Speed for TDM
    
              CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK))
                  | CCM_CS1CDR_SAI1_CLK_PRED((n1-1)) // &0x07
                  | CCM_CS1CDR_SAI1_CLK_PODF((n2-1)); // &0x3f
    
              IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK))
                      | (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0));  //Select MCLK
    
              I2S1_RMR = 0;
              I2S1_RCR1 = I2S_RCR1_RFW(4);
              I2S1_RCR2 = I2S_RCR2_SYNC(0) | I2S_TCR2_BCP | I2S_RCR2_MSEL(1)
                  | I2S_RCR2_BCD | I2S_RCR2_DIV(0);
              
              I2S1_RCR4 = I2S_RCR4_FRSZ((FRAME_I2S-1)) | I2S_RCR4_SYWD(0) | I2S_RCR4_MF
                  | I2S_RCR4_FSE | I2S_RCR4_FSD;
              I2S1_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);
    
              CORE_PIN23_CONFIG = 3;  //1:MCLK 
              CORE_PIN21_CONFIG = 3;  //1:RX_BCLK
              CORE_PIN20_CONFIG = 3;  //1:RX_SYNC
    #if N_ADC==1
              I2S1_RCR3 = I2S_RCR3_RCE;
              CORE_PIN8_CONFIG  = 3;  //RX_DATA0
              IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2;
    
              dma.TCD->SADDR = &I2S1_RDR0;
              dma.TCD->SOFF = 0;
              dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
              dma.TCD->NBYTES_MLNO = 4;
              dma.TCD->SLAST = 0;
    #elif N_ADC==2
              I2S1_RCR3 = I2S_RCR3_RCE_2CH;
              CORE_PIN8_CONFIG  = 3;  //RX_DATA0
              CORE_PIN6_CONFIG  = 3;  //RX_DATA1
              IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; // GPIO_B1_00_ALT3, pg 873
              IOMUXC_SAI1_RX_DATA1_SELECT_INPUT = 1; // GPIO_B0_10_ALT3, pg 873
    
              dma.TCD->SADDR = &I2S1_RDR0;
              dma.TCD->SOFF = 4;
              dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
              dma.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_SMLOE |
                  DMA_TCD_NBYTES_MLOFFYES_MLOFF(-8) |
                  DMA_TCD_NBYTES_MLOFFYES_NBYTES(8);
              dma.TCD->SLAST = -8;
    #endif
              dma.TCD->DADDR = tdm_rx_buffer;
              dma.TCD->DOFF = 4;
              dma.TCD->CITER_ELINKNO = NBUF_I2S;
              dma.TCD->DLASTSGA = -sizeof(tdm_rx_buffer);
              dma.TCD->BITER_ELINKNO = NBUF_I2S;
              dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
              dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX);
              dma.enable();
    
              I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
              dma.attachInterrupt(acq_isr,I2S_DMA_PRIO*16); 
          }
    
          void acq_start(void)
          {
    
          }
          void acq_stop(void)
          {
              
          }
      #endif
    
      uint32_t acq_count=0;
      uint32_t acq_miss=0;
    
        void acq_isr(void)
        {
            uint32_t daddr;
            uint32_t *src;
            acq_count++;
    
            daddr = (uint32_t)(dma.TCD->DADDR);
            dma.clearInterrupt();
    
            if (daddr < (uint32_t)tdm_rx_buffer + sizeof(tdm_rx_buffer) / 2) {
                // DMA is receiving to the first half of the buffer
                // need to remove data from the second half
                src = &tdm_rx_buffer[NBUF_I2S];
            } else {
                // DMA is receiving to the second half of the buffer
                // need to remove data from the first half
                src = &tdm_rx_buffer[0];
            }
    
            #if IMXRT_CACHE_ENABLED >=1
                arm_dcache_delete((void*)src, sizeof(tdm_rx_buffer) / 2);
            #endif
    
            for(int jj=0;jj<NCH_ACQ;jj++)
            {
              for(int ii=0; ii<NBUF_ACQ;ii++)
              {
                acq_rx_buffer[jj+ii*NCH_ACQ]=src[jj+ii*NCH_I2S];
              }
            }
    
            if(!pushData(acq_rx_buffer)) acq_miss++;
    
        }
    
      int16_t acq_check(int16_t state)
      { if(!state)
        { // start acquisition
          acq_start();
        }
        if(state>3)
        { // stop acquisition
          acq_stop();
        }
        return state;
      }
    #endif

  13. #788
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    8,871
    Good morning,

    I will have to play later. Looking at larger files receive... More on other thread.
    I assume you have already tested that creating a directory works in a simple case.

    What Teensy is this supposed to run on? Why I am asking is the mention of using pins like:
    Code:
            #elif I2S_CONFIG==1
                CORE_PIN35_CONFIG = PORT_PCR_MUX(4);   // PTC8,  I2S0_MCLK
                CORE_PIN36_CONFIG = PORT_PCR_MUX(4);   // PTC9,  I2S0_RX_BCLK
                CORE_PIN37_CONFIG = PORT_PCR_MUX(4);   // PTC10, I2S0_RX_FS
    But looking at my T4.1 Excel document: I see:
    Code:
    35	B1_12    	IOMUXC_LPUART5_TX_SELECT_INPUT=1	CSI_PIXCLK	ENET_1588_EVENT0_IN	FLEXIO2_FLEXIO28	GPIO2_IO28	USDHC1_CD_B			FLEXIO3_FLEXIO28
    36	B1_02    	LCD_DATA14	XBAR1_INOUT16	LPSPI4_PCS2	SAI1_TX_BCLK	FLEXIO2_FLEXIO18	GPIO2_IO18	FLEXPWM2_PWMA03		ENET2_RDATA01	FLEXIO3_FLEXIO18
    37	B1_03    	LCD_DATA15	IOMUXC_XBAR1_IN17_SELECT_INPUT=0x3	LPSPI4_PCS1	SAI1_TX_SYNC	FLEXIO2_FLEXIO19	GPIO2_IO19	FLEXPWM2_PWMB03		ENET2_RX_EN	FLEXIO3_FLEXIO19
    That MUX(4) is for FlexIO on those pins...

  14. #789
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    6,772
    Morning - all mornings seem to be the same lately.

    I assume you have already tested that creating a directory works in a simple case.
    Yep - made sure of that. Right now I am in the process of putting together a basic logger using similar functions for creating files/directories and writing data.

    What Teensy is this supposed to run on?
    Been using it a T4.1 - works for everything that I tested so far except NAND QSPI.

    Kind of along the same lines as I was thinking, that is that is some sort of conflict, but didn't dig into it. Figure it might just be better to try a basic test case.

    Taking longer than expected since I have to make more coffee

  15. #790
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    6,772
    Ok just wrote a simple test case that seems to be working - probably do better on next update but wanted to see if this was going to work and it did - so probably a conflict as you identified:
    Code:
    Logger
    Device ID: 0xEFAA21
    attempting to mount existing media
    Disk Usuage:
    Bytes Used: 655360, Bytes Total:131596288
    /18639/09_18_28.raw
    Check path OK
    Opening FIle
    File Open: 536873028
    printDirectory
    --------------
    DIR	18639 / 
    	FILE	09_16_11.raw		51200
    	FILE	09_18_28.raw		51200
    
     0 dirs with 2 files of Size 102400 Bytes
    
     1 dirs with 0 files of Size 0 Bytes
    here is the sketch I used - its a dup of the key elements from the MTP Logger Sketch:
    Code:
    #include "Arduino.h"
    
    
    #include <LittleFS.h>
    #include <LittleFS_NAND.h>
    
    //#define TEST_SPI_NAND
    #define TEST_QSPI_NAND
    
    #define USE_W25N01 1
    //#define USE_W25N02 1
    //#define UES_W25M02 1
    
    // Set for SPI usage
    //const int FlashChipSelect = 10; // AUDIO BOARD
    #if defined(USE_W25N01)
      const int FlashChipSelect = 3; // PJRC Mem board 64MB on #5, #6 : NAND 1Gb on #3, 2GB on #4
    #elif defined(USE_W25N02)
      const int FlashChipSelect = 4; // PJRC Mem board 64MB on #5, #6 : NAND 1Gb on #3, 2GB on #4
    #else //W25M02
      const int FlashChipSelect = 6; // digital pin for flash chip CS pin
    #endif
    //const int FlashChipSelect = 5; // PJRC Mem board 64MB on #5, #6 : NAND 1Gb on #3, 2GB on #4
    //const int FlashChipSelect = 6; // digital pin for flash chip CS pin
    
    
    #if defined(TEST_QSPI_NAND)
    char szDiskMem[] = "QPI_NAND";
    LittleFS_QPINAND myNAND;
    #elif defined(TEST_SPI_NAND)
    char szDiskMem[] = "SPI_NAND";
    LittleFS_SPINAND myNAND;
    #endif
    
    File mfile;
    uint64_t fTot, totSize1;
    
    #define NDBL 1
    #define NBUF_DISK (NDBL*128)
    uint32_t diskBuffer[NBUF_DISK];
    uint32_t maxDel=0;
    
    
    void setup() {
      Serial.begin(115200);
      while(!Serial.available() && millis() < 5000); // or third option to wait up to 5 seconds and then continue
      Serial.println("Logger");
    
      #if defined(TEST_SPI_NAND)
        if (!myNAND.begin( FlashChipSelect )) {
      #else
        if (!myNAND.begin()) {
      #endif
        Serial.printf("Error starting %s\n", szDiskMem);
        while( 1 );
      }
      //myNAND.lowLevelFormat('.');
      Serial.printf("Disk Usuage:\n");
      Serial.printf("Bytes Used: %llu, Bytes Total:%llu\n", myNAND.usedSize(), myNAND.totalSize());
    
      file_open();
      memset(diskBuffer, 0x01, NBUF_DISK*4);
      for(int16_t j = 0; j < 100; j++)
        file_writeData(diskBuffer,NBUF_DISK*4);
      file_close();
      printDirectory();
    }
    
    void loop() {
    
    }
    
    int16_t makeFilename(char *filename)
    {
      uint32_t tt = rtc_get();
      int hh,mm,ss;
      int dd;
      ss= tt % 60; tt /= 60;
      mm= tt % 60; tt /= 60;
      hh= tt % 24; tt /= 24;
      dd= tt;
      sprintf(filename,"/%d/%02d_%02d_%02d.raw",dd,hh,mm,ss);
      Serial.println(filename);
      return 1;
    }
    
    int16_t checkPath(char *filename)
    {
      int ln=strlen(filename);
      int i1=-1;
      for(int ii=0;ii<ln;ii++) if(filename[ii]=='/') i1=ii;
      if(i1<0) {
        Serial.println("NO PATH");
        return 1; // no path
      }
      filename[i1]=0;
      if(!myNAND.exists(filename))
      { 
        Serial.println("MAKING DIRECTORY");
        Serial.println(filename); 
        if(!myNAND.mkdir(filename)) 
        {
          Serial.println("directory fail");
          return 0;
        }
      }
      Serial.println("Check path OK");
      filename[i1]='/';
      return 1;
    }
    /*************************** Filing *****************************/
    
    
    
    int16_t file_open()
    { char filename[80];
      if(!makeFilename(filename)) {
        Serial.println("filename created");
        return 0;
      }
      if(!checkPath(filename)){
        Serial.println("check path fail");
        return 0;
      }
      Serial.println("Opening FIle");
      mfile = myNAND.open(filename,FILE_WRITE_BEGIN);
      Serial.printf("File Open: %d\n", mfile);
      return !(!mfile);
    }
    
    int16_t file_writeData(void *diskBuffer, uint32_t nd)
    { if(!mfile) return 0;
      uint32_t nb = mfile.write(diskBuffer,nd);
      return (nb==nd);
    }
    
    int16_t file_close(void)
    { mfile.close();
      return (!mfile);
    }
    
    void printDirectory() {
    
      Serial.println("printDirectory\n--------------");
      printDirectory(myNAND.open("/"), 0);
      Serial.println();
    }
    
    
    void printDirectory(File dir, int numTabs) {
      //dir.whoami();
      uint64_t fSize = 0;
      uint32_t dCnt = 0, fCnt = 0;
      if ( 0 == dir ) {
        Serial.printf( "\t>>>\t>>>>> No Dir\n" );
        return;
      }
      while (true) {
        File entry =  dir.openNextFile();
        if (! entry) {
          // no more files
          Serial.printf("\n %u dirs with %u files of Size %u Bytes\n", dCnt, fCnt, fSize);
          fTot += fCnt;
          totSize1 += fSize;
          break;
        }
        for (uint8_t i = 0; i < numTabs; i++) {
          Serial.print('\t');
        }
    
        if (entry.isDirectory()) {
          Serial.print("DIR\t");
          dCnt++;
        } else {
          Serial.print("FILE\t");
          fCnt++;
          fSize += entry.size();
        }
        Serial.print(entry.name());
        if (entry.isDirectory()) {
          Serial.println(" / ");
          printDirectory(entry, numTabs + 1);
        } else {
          // files have sizes, directories do not
          Serial.print("\t\t");
          Serial.println(entry.size(), DEC);
        }
        entry.close();
        //Serial.flush();
      }
    }
    Now to figure out a good data pull to log . Not sure I want to hook up a GPS or not - maybe another sensor

  16. #791
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    6,772
    Ok - reworked the mtp-logger example to just read data from MPU-9250 and store it on any of our flash chips, now it works no problem with the NAND on QSPI. Must be that conflict @KurtE mentioned. If you want to play:
    Code:
    #include "Arduino.h"
    
    #include "SD.h"
    #include "MTP.h"
    
    //used for logger
    File mfile;
    uint64_t fTot, totSize1;
    int16_t state;
    
    uint32_t maxDel=0;
    char header[512];
    
    //used for MPU9250 and data structure
    
    #include "MPU9250.h"
    
    // an MPU9250 object with the MPU-9250 sensor on I2C bus 0 with address 0x68
    MPU9250 IMU(Wire,0x68);
    int status;
    
    //define a struct of various data types
    struct MYDATA_t {
      float ax, ay, az;
      float gx, gy, gz;
      float mx, my, mz;
      float temp;
    };
    
    //define a struct joining MYDATA_t to an array of bytes to be stored
    union MYDATA4I2C_t {
     MYDATA_t datastruct;
     uint8_t I2CPacket[sizeof(MYDATA_t)];
    };
    
    MYDATA4I2C_t mydata; //data to be written in memory
    MYDATA4I2C_t readdata; //data read from memory
    
    
    #if defined(__IMXRT1062__)
      // following only while usb_mtp is not included in cores
      #if __has_include("usb_mtp.h")
        #include "usb_mtp.h"
      #else
        #include "usb1_mtp.h"
      #endif
    #endif
    
    #define USE_EVENTS 1
    #define USE_SD  0
    #define USE_LITTLEFS 1 // set to zero if no LtttleFS is existing or to be used
    
    #if USE_LITTLEFS==1
    #ifdef ARDUINO_TEENSY41
      #define USE_RAM 0     // T4.1 PSRAM (or RAM)
    #else
      #define USE_RAM 0     // T4.1 PSRAM (or RAM)
    #endif
      #define USE_SPI 0
      #define USE_QSPI 0
      #define USE_NAND 0
      #define USE_QSPI_NAND 1
      #define USE_FRAM 0
    #endif
    
    #if USE_EVENTS==1
      extern "C" int usb_init_events(void);
    #else
      int usb_init_events(void) {}
    #endif
    
    
    #if defined(__IMXRT1062__)
      // following only as long usb_mtp is not included in cores
      #if !__has_include("usb_mtp.h")
        #include "usb1_mtp.h"
      #endif
    #else
      #ifndef BUILTIN_SCCARD 
        #define BUILTIN_SDCARD 254
      #endif
      void usb_mtp_configure(void) {}
    #endif
    
    /****  Start device specific change area  ****/
    #if USE_SD==1
      // edit SPI to reflect your configuration (following is for T4.1)
      #define SD_MOSI 11
      #define SD_MISO 12
      #define SD_SCK  13
    
      #define stor_SPEED SD_SCK_MHZ(16)  // adjust to sd card 
    
    // SDClasses
      const char *sd_str[]={"sdio"}; // edit to reflect your configuration
      const int cs[] = {BUILTIN_SDCARD}; // edit to reflect your configuration
      const int nsd = sizeof(cs)/sizeof(int);
    
    SDClass storfs[nsd];
    #endif
    
    //LittleFS classes
    #if USE_LITTLEFS==1
      #include "LittleFS.h"
      #include "LittleFS_NAND.h"
      
      #if USE_RAM == 1
        const char *stor_str[]={"RAM0"};     // edit to reflect your configuration
        const int stor_size[] = {2'000'000};
        const int stor_nsd = sizeof(stor_size)/sizeof(int);
        LittleFS_RAM storfs[stor_nsd]; // needs to be declared if LittleFS is used in storage.h
      #endif
      
      #if USE_SPI == 1
        const char *stor_str[]={"WINBOND"};     // edit to reflect your configuration
        const int stor_cs[] = {5}; // edit to reflect your configuration
        const int stor_nsd = sizeof(stor_cs)/sizeof(int);
        LittleFS_SPIFlash storfs[stor_nsd]; // needs to be declared if LittleFS is used in storage.h
      #endif
      
      #if USE_FRAM == 1
        const char *stor_str[]={"CYPRESS"};     // edit to reflect your configuration
        const int stor_cs[] = {10}; // edit to reflect your configuration
        const int stor_nsd = sizeof(stor_cs)/sizeof(int);
        LittleFS_SPIFlash storfs[stor_nsd]; // needs to be declared if LittleFS is used in storage.h
      #endif
    
      #if USE_NAND == 1
        const char *stor_str[]={"WINBOND1G"};     // edit to reflect your configuration
        const int stor_cs[] = {3}; // edit to reflect your configuration
        const int stor_nsd = sizeof(stor_cs)/sizeof(int);
        LittleFS_SPINAND storfs[stor_nsd]; // needs to be declared if LittleFS is used in storage.h
      #endif
    
      #if USE_QSPI_NAND == 1
        const char *stor_str[]={"WINBOND1G"};     // edit to reflect your configuration
        const int stor_nsd = 1;
        LittleFS_QPINAND storfs[stor_nsd]; // needs to be declared if LittleFS is used in storage.h
      #endif
      
      #if USE_QSPI == 1
        const char *stor_str[]={"QSPI0"};     // edit to reflect your configuration
        const int stor_nsd = 1;
        LittleFS_QSPIFlash storfs[stor_nsd]; // needs to be declared if LittleFS is used in storage.h
      #endif
     
    #endif
    
    
    MTPStorage_SD storage;
    MTPD       mtpd(&storage);
    
    
    void storage_configure()
    {
      #if USE_SD==1
        #if defined SD_SCK
          SPI.setMOSI(SD_MOSI);
          SPI.setMISO(SD_MISO);
          SPI.setSCK(SD_SCK);
        #endif
    
        for(int ii=0; ii<nsd; ii++)
        { if(cs[ii] == BUILTIN_SDCARD)
          {
            if(!storfs[ii].sdfs.begin(SdioConfig(FIFO_SDIO))) {Serial.println("No storage"); while(1);};
            storage.addFilesystem(storfs[ii], sd_str[ii]);
          }
          else if(cs[ii]<BUILTIN_SDCARD)
          {
            pinMode(cs[ii],OUTPUT); digitalWriteFast(cs[ii],HIGH);
            if(!storfs[ii].sdfs.begin(SdSpiConfig(cs[ii], SHARED_SPI, stor_SPEED))) {Serial.println("No storage"); while(1);}
            storage.addFilesystem(storfs[ii], sd_str[ii]);
          }
            uint64_t totalSize = storfs[ii].totalSize();
            uint64_t usedSize  = storfs[ii].usedSize();
            Serial.printf("Storage %d %d %s ",ii,cs[ii],sd_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
        }
        #endif
    
        #if USE_LITTLEFS==1
          #if USE_RAM == 1
            for(int ii=0; ii<stor_nsd;ii++)
            {
              { if(!ramfs[ii].begin(stor_size[ii])) { Serial.println("No storage"); while(1);}
                storage.addFilesystem(ramfs[ii], stor_str[ii]);
              }
              uint64_t totalSize = ramfs[ii].totalSize();
              uint64_t usedSize  = ramfs[ii].usedSize();
              Serial.printf("Storage %d %s ",ii,stor_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
            }
          #endif
    
          #if USE_SPI == 1
            for(int ii=0; ii<stor_nsd;ii++) {
              pinMode(stor_cs[ii],OUTPUT); digitalWriteFast(stor_cs[ii],HIGH);
              if(!storfs[ii].begin(stor_cs[ii], SPI)) {Serial.println("No storage"); while(1);}
              storage.addFilesystem(storfs[ii], stor_str[ii]);
              
            uint64_t totalSize = storfs[ii].totalSize();
            uint64_t usedSize  = storfs[ii].usedSize();
            Serial.printf("Storage %d %d %s ",ii,stor_cs[ii],stor_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
            }
          #endif
    
          #if USE_FRAM == 1
            for(int ii=0; ii<stor_nsd;ii++) {
              pinMode(stor_cs[ii],OUTPUT); digitalWriteFast(stor_cs[ii],HIGH);
              if(!storfs[ii].begin(stor_cs[ii], SPI)) {Serial.println("No storage"); while(1);}
              storage.addFilesystem(storfs[ii], stor_str[ii]);
              
            uint64_t totalSize = storfs[ii].totalSize();
            uint64_t usedSize  = storfs[ii].usedSize();
            Serial.printf("Storage %d %d %s ",ii,stor_cs[ii],stor_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
            }
          #endif
    
          #if USE_NAND == 1
            for(int ii=0; ii<stor_nsd;ii++) {
              pinMode(stor_cs[ii],OUTPUT); digitalWriteFast(stor_cs[ii],HIGH);
              if(!storfs[ii].begin(stor_cs[ii], SPI)) {Serial.println("No storage"); while(1);}
              storage.addFilesystem(storfs[ii], stor_str[ii]);
              
            uint64_t totalSize = storfs[ii].totalSize();
            uint64_t usedSize  = storfs[ii].usedSize();
            Serial.printf("Storage %d %d %s ",ii,stor_cs[ii],stor_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
            }
          #endif
    
          #if USE_QSPI_NAND == 1
           for(int ii=0; ii<stor_nsd;ii++) {
              if(!storfs[ii].begin()) {Serial.println("No storage"); while(1);}
              storage.addFilesystem(storfs[ii], stor_str[ii]);
         
              uint64_t totalSize = storfs[ii].totalSize();
              uint64_t usedSize  = storfs[ii].usedSize();
              Serial.printf("Storage %d %s ",ii,stor_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
            }
          #endif
    
          #if USE_QSPI == 1
           for(int ii=0; ii<stor_nsd;ii++) {
              if(!storfs[ii].begin()) {Serial.println("No storage"); while(1);}
              storage.addFilesystem(storfs[ii], stor_str[ii]);
         
              uint64_t totalSize = storfs[ii].totalSize();
              uint64_t usedSize  = storfs[ii].usedSize();
              Serial.printf("Storage %d %s ",ii,stor_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
            }
          #endif
          
        #endif
    }
    /****  End of device specific change area  ****/
    
    
    void setup()
    { 
      Serial.begin(115200);
      #if defined(USB_MTPDISK_SERIAL) 
        while(!Serial); // comment if you do not want to wait for terminal
      #else
        //while(!Serial.available()); // comment if you do not want to wait for terminal (otherwise press any key to continue)
        while(!Serial.available() && millis() < 5000); // or third option to wait up to 5 seconds and then continue
      #endif
      Serial.println("MTP logger");
    
      #if USE_EVENTS==1
        usb_init_events();
      #endif
    
      #if !__has_include("usb_mtp.h")
        usb_mtp_configure();
      #endif
      storage_configure();
    
     //Init 9250 i2c
      // start communication with IMU 
      status = IMU.begin();
      if (status < 0) {
        Serial.println("IMU initialization unsuccessful");
        Serial.println("Check IMU wiring or try cycling power");
        Serial.print("Status: ");
        Serial.println(status);
        while(1) {}
      }
    
    
      state=-1;
    
      Serial.println("Setup done");
      Serial.println(" Enter s to start acquisition and q to stop acquisition");
      Serial.flush();
    }
    
    
    uint32_t loop_count=0;
    void loop()
    { loop_count++;
      state = do_menu(state);
      state = check_filing(state);
      //
      if(state<0)
        mtpd.loop();
      else
        state=do_logger(0,state);
    
      if(state>=0) logg(1000,"loop");
      //asm("wfi"); // may wait forever on T4.x
    
    }
    
    
    
    /**************** Online logging *******************************/
    extern uint32_t loop_count, acq_count, acq_miss, maxDel;
    extern uint16_t maxCount;
    void logg(uint32_t del, const char *txt)
    { static uint32_t to;
      if(millis()-to > del)
      {
        Serial.printf("%s: %6d %4d %4d %4d %4d %d\n",
                txt,loop_count, state); 
        loop_count=0;
        to=millis();
      }
    }
    
    int16_t do_menu(int16_t state)
    { // check Serial input
      if(!Serial.available()) return state;
      char cc = Serial.read();
      switch(cc)
      {
        case 's': // start acquisition
          if(state>=0) return state;
          state=0;
          Serial.println("\nStart");
          break;
        case 'q': // stop acquisition
          if(state<0) return state;
          state=4;
          Serial.println("\nStop");
          break;
        case 'r':
          Serial.println("Reset");
          mtpd.send_DeviceResetEvent();
          break;
        case 'd':
          // first dump list of storages:
          uint32_t fsCount = storage.getFSCount();
          Serial.printf("\nDump Storage list(%u)\n", fsCount);
          for (uint32_t ii = 0; ii < fsCount; ii++) {
            Serial.printf("store:%u name:%s fs:%x\n", ii, storage.getStoreName(ii), (uint32_t)storage.getStoreFS(ii));
          }
          Serial.println("\nDump Index List");
          storage.dumpIndexList();
          break;
      default:
        break;
    
      }
    
      return state;
    }
    
    int16_t do_logger(uint16_t store, int16_t state)
    { uint32_t to=millis();
    
        if(state==0)
        { // acquisition is running, need to open file
          if(!file_open(store)) 
            return -2;
          state=1;
          //Serial.println("file_open");
        }
        if(state==1)
        { // file just opended, need to write header
          if(!file_writeHeader()) return -3;
          //Serial.println("wrote header");
          state=2;
          
        }
        if(state>=2)
        { // write data to disk
          if(!file_writeData()) return -4;
          //Serial.println("write data to disk");
        }
    
      if(state==3)
      { // close file, but continue acquisition
        if(!file_close()) return -5;
        //Serial.println("close file");
        state=0;
      }
    
      if(state==4)
      { // close file and stop acquisition
        if(!file_close()) return -6;
        //Serial.println("file_close and stop");
        state=-1;
      }
    
      uint32_t dt=millis()-to;
      if(dt>maxDel) maxDel=dt;
    
      return state;
    }
    
    uint32_t t_on = 60;
    int16_t check_filing(int16_t state)
    {
      static uint32_t to;
      if(state==2)
      {
        uint32_t tt = rtc_get();
        uint32_t dt = tt % t_on;
        if(dt<to) state = 3;
        to = dt;
      }
      return state;
    }
    
    
    void makeHeader(char *header)
    {
      memset(header,0,512);
    }
    
    int16_t file_writeHeader(void)
    { if(!mfile) return 0;
      makeHeader(header);
      size_t nb = mfile.write(header,512);
      return (nb==512);
    }
    
    
    int16_t makeFilename(char *filename)
    {
      uint32_t tt = rtc_get();
      int hh,mm,ss;
      int dd;
      ss= tt % 60; tt /= 60;
      mm= tt % 60; tt /= 60;
      hh= tt % 24; tt /= 24;
      dd= tt;
      sprintf(filename,"/%d/%02d_%02d_%02d.raw",dd,hh,mm,ss);
      Serial.println(filename);
      return 1;
    }
    
    int16_t checkPath(uint16_t store, char *filename)
    {
      int ln=strlen(filename);
      int i1=-1;
      for(int ii=0;ii<ln;ii++) if(filename[ii]=='/') i1=ii;
      if(i1<0) {
        //Serial.println("NO PATH");
        return 1; // no path
      }
      filename[i1]=0;
      if(!storfs[store].exists(filename))
      { 
        //Serial.println("MAKING DIRECTORY");
        Serial.println(filename); 
        if(!storfs[store].mkdir(filename)) 
        {
          Serial.println("directory fail");
          return 0;
        }
      }
      //Serial.println("Check path OK");
      filename[i1]='/';
      return 1;
    }
    
    /*************************** Filing *****************************/
    int16_t file_open(uint16_t store)
    { char filename[80];
      if(!makeFilename(filename)) {
        //Serial.println("filename created");
        return 0;
      }
      if(!checkPath(store, filename)){
        //Serial.println("check path fail");
        return 0;
      }
      //Serial.println("Opening FIle");
      mfile = storfs[store].open(filename,FILE_WRITE_BEGIN);
      Serial.printf("File Open: %d\n", mfile);
      return !(!mfile);
    }
    
    int16_t file_writeData()
    { if(!mfile) return 0;
      uint16_t arraySize = sizeof(MYDATA_t);
      uint8_t diskBuffer[arraySize];
      IMU.readSensor();
      mydata.datastruct.ax = IMU.getAccelX_mss();
      mydata.datastruct.ay = IMU.getAccelY_mss();
      mydata.datastruct.az = IMU.getAccelZ_mss();
      mydata.datastruct.gx = IMU.getGyroX_rads();
      mydata.datastruct.gy = IMU.getGyroY_rads();
      mydata.datastruct.gz = IMU.getGyroZ_rads();
      mydata.datastruct.mx = IMU.getMagX_uT();
      mydata.datastruct.my = IMU.getMagY_uT();
      mydata.datastruct.mz = IMU.getMagZ_uT();
      mydata.datastruct.temp = IMU.getTemperature_C();
        for(uint16_t j = 0; j < arraySize; j++) {
          diskBuffer[j]= mydata.I2CPacket[j];
        }
      uint32_t nb = mfile.write(diskBuffer,arraySize);
      return 1;
    }
    
    int16_t file_close(void)
    { mfile.close();
      return (!mfile);
    }

  17. #792
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    13,732
    Quote Originally Posted by KurtE View Post
    Good morning,

    I will have to play later. Looking at larger files receive... More on other thread.
    I assume you have already tested that creating a directory works in a simple case.

    What Teensy is this supposed to run on? Why I am asking is the mention of using pins like:
    Code:
            #elif I2S_CONFIG==1
                CORE_PIN35_CONFIG = PORT_PCR_MUX(4);   // PTC8,  I2S0_MCLK
                CORE_PIN36_CONFIG = PORT_PCR_MUX(4);   // PTC9,  I2S0_RX_BCLK
                CORE_PIN37_CONFIG = PORT_PCR_MUX(4);   // PTC10, I2S0_RX_FS
    But looking at my T4.1 Excel document: I see:
    Code:
    35	B1_12    	IOMUXC_LPUART5_TX_SELECT_INPUT=1	CSI_PIXCLK	ENET_1588_EVENT0_IN	FLEXIO2_FLEXIO28	GPIO2_IO28	USDHC1_CD_B			FLEXIO3_FLEXIO28
    36	B1_02    	LCD_DATA14	XBAR1_INOUT16	LPSPI4_PCS2	SAI1_TX_BCLK	FLEXIO2_FLEXIO18	GPIO2_IO18	FLEXPWM2_PWMA03		ENET2_RDATA01	FLEXIO3_FLEXIO18
    37	B1_03    	LCD_DATA15	IOMUXC_XBAR1_IN17_SELECT_INPUT=0x3	LPSPI4_PCS1	SAI1_TX_SYNC	FLEXIO2_FLEXIO19	GPIO2_IO19	FLEXPWM2_PWMB03		ENET2_RX_EN	FLEXIO3_FLEXIO19
    That MUX(4) is for FlexIO on those pins...
    Just to make sure at a glance what that is showing - the issue is using those pins 35,36,37 is in conflict because they have FLEXIO2 conflict with QSPI usage?

  18. #793
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    8,871
    @defragster - Sorry not really sure what is going on, just that when I saw stuff about configuring some pins for I2S where are not I2S pins, It made me suspicious that maybe some of the other settings like DMA and the like may not be setup to work on a T4.x...

  19. #794
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    13,732
    Quote Originally Posted by KurtE View Post
    @defragster - Sorry not really sure what is going on, just that when I saw stuff about configuring some pins for I2S where are not I2S pins, It made me suspicious that maybe some of the other settings like DMA and the like may not be setup to work on a T4.x...
    Okay, I had just glanced at the comments on the pin #'s (and saw i2s comments) before posting - then the 'red notes' on the FLEXIO mux set. So potential issue is not because the are mux'able to FLEXIO - but if the code tried enabling them as i2s when they are not - the side effects could be the problem.

  20. #795
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    6,772
    @defragster - @KurtE
    Pretty much what Kurt said - there is some sort of conflict but didn't spend much time tracking it down specifically as I just rewrote to get rid of I2S/DMA and use the MPU-9250 to get real data. Heres the greatest incarnation of the test sketch if you have a I2C MPU-9250 laying around. Pretty sure you have one left over from days gone by

    there are a few commands you can send:
    s - start data acquisition
    q - stop data acquisition
    r - read data file you just created on flash
    d - first dump list of storages
    x - reset.

    Code:
    #include "Arduino.h"
    
    #include "SD.h"
    #include "MTP.h"
    
    //used for logger
    File mfile;
    uint64_t fTot, totSize1;
    int16_t state;
    
    uint32_t maxDel=0;
    char header[512];
    char filename[80];
    
    //used for MPU9250 and data structure
    
    #include "MPU9250.h"
    
    // an MPU9250 object with the MPU-9250 sensor on I2C bus 0 with address 0x68
    MPU9250 IMU(Wire,0x68);
    int status;
    
    //define a struct of various data types
    struct MYDATA_t {
      float ax, ay, az;
      float gx, gy, gz;
      float mx, my, mz;
      float temp;
    };
    
    //define a struct joining MYDATA_t to an array of bytes to be stored
    union MYDATA4I2C_t {
     MYDATA_t datastruct;
     uint8_t I2CPacket[sizeof(MYDATA_t)];
    };
    
    MYDATA4I2C_t mydata; //data to be written in memory
    MYDATA4I2C_t readdata; //data read from memory
    
    
    #if defined(__IMXRT1062__)
      // following only while usb_mtp is not included in cores
      #if __has_include("usb_mtp.h")
        #include "usb_mtp.h"
      #else
        #include "usb1_mtp.h"
      #endif
    #endif
    
    #define USE_EVENTS 1
    #define USE_SD  0
    #define USE_LITTLEFS 1 // set to zero if no LtttleFS is existing or to be used
    
    #if USE_LITTLEFS==1
    #ifdef ARDUINO_TEENSY41
      #define USE_RAM 0     // T4.1 PSRAM (or RAM)
    #else
      #define USE_RAM 0     // T4.1 PSRAM (or RAM)
    #endif
      #define USE_SPI 0
      #define USE_QSPI 0
      #define USE_NAND 0
      #define USE_QSPI_NAND 1
      #define USE_FRAM 0
    #endif
    
    #if USE_EVENTS==1
      extern "C" int usb_init_events(void);
    #else
      int usb_init_events(void) {}
    #endif
    
    
    #if defined(__IMXRT1062__)
      // following only as long usb_mtp is not included in cores
      #if !__has_include("usb_mtp.h")
        #include "usb1_mtp.h"
      #endif
    #else
      #ifndef BUILTIN_SCCARD 
        #define BUILTIN_SDCARD 254
      #endif
      void usb_mtp_configure(void) {}
    #endif
    
    /****  Start device specific change area  ****/
    #if USE_SD==1
      // edit SPI to reflect your configuration (following is for T4.1)
      #define SD_MOSI 11
      #define SD_MISO 12
      #define SD_SCK  13
    
      #define stor_SPEED SD_SCK_MHZ(16)  // adjust to sd card 
    
    // SDClasses
      const char *sd_str[]={"sdio"}; // edit to reflect your configuration
      const int cs[] = {BUILTIN_SDCARD}; // edit to reflect your configuration
      const int nsd = sizeof(cs)/sizeof(int);
    
    SDClass storfs[nsd];
    #endif
    
    //LittleFS classes
    #if USE_LITTLEFS==1
      #include "LittleFS.h"
      #include "LittleFS_NAND.h"
      
      #if USE_RAM == 1
        const char *stor_str[]={"RAM0"};     // edit to reflect your configuration
        const int stor_size[] = {2'000'000};
        const int stor_nsd = sizeof(stor_size)/sizeof(int);
        LittleFS_RAM storfs[stor_nsd]; // needs to be declared if LittleFS is used in storage.h
      #endif
      
      #if USE_SPI == 1
        const char *stor_str[]={"WINBOND"};     // edit to reflect your configuration
        const int stor_cs[] = {5}; // edit to reflect your configuration
        const int stor_nsd = sizeof(stor_cs)/sizeof(int);
        LittleFS_SPIFlash storfs[stor_nsd]; // needs to be declared if LittleFS is used in storage.h
      #endif
      
      #if USE_FRAM == 1
        const char *stor_str[]={"CYPRESS"};     // edit to reflect your configuration
        const int stor_cs[] = {10}; // edit to reflect your configuration
        const int stor_nsd = sizeof(stor_cs)/sizeof(int);
        LittleFS_SPIFlash storfs[stor_nsd]; // needs to be declared if LittleFS is used in storage.h
      #endif
    
      #if USE_NAND == 1
        const char *stor_str[]={"WINBOND1G"};     // edit to reflect your configuration
        const int stor_cs[] = {3}; // edit to reflect your configuration
        const int stor_nsd = sizeof(stor_cs)/sizeof(int);
        LittleFS_SPINAND storfs[stor_nsd]; // needs to be declared if LittleFS is used in storage.h
      #endif
    
      #if USE_QSPI_NAND == 1
        const char *stor_str[]={"WINBOND1G"};     // edit to reflect your configuration
        const int stor_nsd = 1;
        LittleFS_QPINAND storfs[stor_nsd]; // needs to be declared if LittleFS is used in storage.h
      #endif
      
      #if USE_QSPI == 1
        const char *stor_str[]={"QSPI0"};     // edit to reflect your configuration
        const int stor_nsd = 1;
        LittleFS_QSPIFlash storfs[stor_nsd]; // needs to be declared if LittleFS is used in storage.h
      #endif
     
    #endif
    
    
    MTPStorage_SD storage;
    MTPD       mtpd(&storage);
    
    
    void storage_configure()
    {
      #if USE_SD==1
        #if defined SD_SCK
          SPI.setMOSI(SD_MOSI);
          SPI.setMISO(SD_MISO);
          SPI.setSCK(SD_SCK);
        #endif
    
        for(int ii=0; ii<nsd; ii++)
        { if(cs[ii] == BUILTIN_SDCARD)
          {
            if(!storfs[ii].sdfs.begin(SdioConfig(FIFO_SDIO))) {Serial.println("No storage"); while(1);};
            storage.addFilesystem(storfs[ii], sd_str[ii]);
          }
          else if(cs[ii]<BUILTIN_SDCARD)
          {
            pinMode(cs[ii],OUTPUT); digitalWriteFast(cs[ii],HIGH);
            if(!storfs[ii].sdfs.begin(SdSpiConfig(cs[ii], SHARED_SPI, stor_SPEED))) {Serial.println("No storage"); while(1);}
            storage.addFilesystem(storfs[ii], sd_str[ii]);
          }
            uint64_t totalSize = storfs[ii].totalSize();
            uint64_t usedSize  = storfs[ii].usedSize();
            Serial.printf("Storage %d %d %s ",ii,cs[ii],sd_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
        }
        #endif
    
        #if USE_LITTLEFS==1
          #if USE_RAM == 1
            for(int ii=0; ii<stor_nsd;ii++)
            {
              { if(!ramfs[ii].begin(stor_size[ii])) { Serial.println("No storage"); while(1);}
                storage.addFilesystem(ramfs[ii], stor_str[ii]);
              }
              uint64_t totalSize = ramfs[ii].totalSize();
              uint64_t usedSize  = ramfs[ii].usedSize();
              Serial.printf("Storage %d %s ",ii,stor_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
            }
          #endif
    
          #if USE_SPI == 1
            for(int ii=0; ii<stor_nsd;ii++) {
              pinMode(stor_cs[ii],OUTPUT); digitalWriteFast(stor_cs[ii],HIGH);
              if(!storfs[ii].begin(stor_cs[ii], SPI)) {Serial.println("No storage"); while(1);}
              storage.addFilesystem(storfs[ii], stor_str[ii]);
              
            uint64_t totalSize = storfs[ii].totalSize();
            uint64_t usedSize  = storfs[ii].usedSize();
            Serial.printf("Storage %d %d %s ",ii,stor_cs[ii],stor_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
            }
          #endif
    
          #if USE_FRAM == 1
            for(int ii=0; ii<stor_nsd;ii++) {
              pinMode(stor_cs[ii],OUTPUT); digitalWriteFast(stor_cs[ii],HIGH);
              if(!storfs[ii].begin(stor_cs[ii], SPI)) {Serial.println("No storage"); while(1);}
              storage.addFilesystem(storfs[ii], stor_str[ii]);
              
            uint64_t totalSize = storfs[ii].totalSize();
            uint64_t usedSize  = storfs[ii].usedSize();
            Serial.printf("Storage %d %d %s ",ii,stor_cs[ii],stor_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
            }
          #endif
    
          #if USE_NAND == 1
            for(int ii=0; ii<stor_nsd;ii++) {
              pinMode(stor_cs[ii],OUTPUT); digitalWriteFast(stor_cs[ii],HIGH);
              if(!storfs[ii].begin(stor_cs[ii], SPI)) {Serial.println("No storage"); while(1);}
              storage.addFilesystem(storfs[ii], stor_str[ii]);
              
            uint64_t totalSize = storfs[ii].totalSize();
            uint64_t usedSize  = storfs[ii].usedSize();
            Serial.printf("Storage %d %d %s ",ii,stor_cs[ii],stor_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
            }
          #endif
    
          #if USE_QSPI_NAND == 1
           for(int ii=0; ii<stor_nsd;ii++) {
              if(!storfs[ii].begin()) {Serial.println("No storage"); while(1);}
              storage.addFilesystem(storfs[ii], stor_str[ii]);
         
              uint64_t totalSize = storfs[ii].totalSize();
              uint64_t usedSize  = storfs[ii].usedSize();
              Serial.printf("Storage %d %s ",ii,stor_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
            }
          #endif
    
          #if USE_QSPI == 1
           for(int ii=0; ii<stor_nsd;ii++) {
              if(!storfs[ii].begin()) {Serial.println("No storage"); while(1);}
              storage.addFilesystem(storfs[ii], stor_str[ii]);
         
              uint64_t totalSize = storfs[ii].totalSize();
              uint64_t usedSize  = storfs[ii].usedSize();
              Serial.printf("Storage %d %s ",ii,stor_str[ii]); Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
            }
          #endif
          
        #endif
    }
    /****  End of device specific change area  ****/
    
    
    void setup()
    { 
      Serial.begin(115200);
      #if defined(USB_MTPDISK_SERIAL) 
        while(!Serial); // comment if you do not want to wait for terminal
      #else
        //while(!Serial.available()); // comment if you do not want to wait for terminal (otherwise press any key to continue)
        while(!Serial.available() && millis() < 5000); // or third option to wait up to 5 seconds and then continue
      #endif
      Serial.println("MTP logger");
    
      #if USE_EVENTS==1
        usb_init_events();
      #endif
    
      #if !__has_include("usb_mtp.h")
        usb_mtp_configure();
      #endif
      storage_configure();
    
     //Init 9250 i2c
      // start communication with IMU 
      status = IMU.begin();
      if (status < 0) {
        Serial.println("IMU initialization unsuccessful");
        Serial.println("Check IMU wiring or try cycling power");
        Serial.print("Status: ");
        Serial.println(status);
        while(1) {}
      }
    
    
      state=-1;
    
      Serial.println("Setup done");
      Serial.println(" Enter s to start acquisition and q to stop acquisition");
      Serial.flush();
    }
    
    
    uint32_t loop_count=0;
    void loop()
    { loop_count++;
      state = do_menu(state);
      state = check_filing(state);
      //
      if(state<0)
        mtpd.loop();
      else
        state=do_logger(0,state);
    
      if(state>=0) logg(1000,"loop");
      //asm("wfi"); // may wait forever on T4.x
    
    }
    
    
    
    /**************** Online logging *******************************/
    extern uint32_t loop_count, acq_count, acq_miss, maxDel;
    extern uint16_t maxCount;
    void logg(uint32_t del, const char *txt)
    { static uint32_t to;
      if(millis()-to > del)
      {
        Serial.printf("%s: %6d %d\n",
                txt,loop_count, state); 
        loop_count=0;
        to=millis();
      }
    }
    
    int16_t do_menu(int16_t state)
    { // check Serial input
      if(!Serial.available()) return state;
      char cc = Serial.read();
      if(cc == 's'){// start acquisition
          if(state>=0) return state;
          state=0;
          Serial.println("\nStart");
      }
      if(cc == 'q'){ // stop acquisition
          if(state<0) return state;
          state=4;
          Serial.println("\nStop");
      }
      if(cc == 'x'){
          Serial.println("Reset");
          mtpd.send_DeviceResetEvent();
      }
      if(cc == 'd'){
          // first dump list of storages:
          uint32_t fsCount = storage.getFSCount();
          Serial.printf("\nDump Storage list(%u)\n", fsCount);
          for (uint32_t ii = 0; ii < fsCount; ii++) {
            Serial.printf("store:%u name:%s fs:%x\n", ii, storage.getStoreName(ii), (uint32_t)storage.getStoreFS(ii));
          }
          Serial.println("\nDump Index List");
          storage.dumpIndexList();
      }
      if(cc == 'r'){
          Serial.println("r key hit");
          Serial.printf("FILE: %s\n", filename);
          read_data_file();
      }
    
      return state;
    }
    
    int16_t do_logger(uint16_t store, int16_t state)
    { uint32_t to=millis();
    
        if(state==0)
        { // acquisition is running, need to open file
          if(!file_open(store)) 
            return -2;
          state=1;
          //Serial.println("file_open");
        }
        if(state==1)
        { // file just opended, need to write header
          if(!file_writeHeader()) return -3;
          //Serial.println("wrote header");
          state=2;
          
        }
        if(state>=2)
        { // write data to disk
          if(!file_writeData()) return -4;
          //Serial.println("write data to disk");
        }
    
      if(state==3)
      { // close file, but continue acquisition
        if(!file_close()) return -5;
        //Serial.println("close file");
        state=0;
      }
    
      if(state==4)
      { // close file and stop acquisition
        if(!file_close()) return -6;
        //Serial.println("file_close and stop");
        state=-1;
      }
    
      uint32_t dt=millis()-to;
      if(dt>maxDel) maxDel=dt;
    
      return state;
    }
    
    uint32_t t_on = 60;
    int16_t check_filing(int16_t state)
    {
      static uint32_t to;
      if(state==2)
      {
        uint32_t tt = rtc_get();
        uint32_t dt = tt % t_on;
        if(dt<to) state = 3;
        to = dt;
      }
      return state;
    }
    
    
    void makeHeader(char *header)
    {
      memset(header,0,512);
    }
    
    int16_t file_writeHeader(void)
    { if(!mfile) return 0;
      makeHeader(header);
      //size_t nb = mfile.write(header,512);
      //return (nb==512);
      return 1;
    }
    
    
    int16_t makeFilename(char *filename)
    {
      uint32_t tt = rtc_get();
      int hh,mm,ss;
      int dd;
      ss= tt % 60; tt /= 60;
      mm= tt % 60; tt /= 60;
      hh= tt % 24; tt /= 24;
      dd= tt;
      sprintf(filename,"/%d/%02d_%02d_%02d.raw",dd,hh,mm,ss);
      Serial.println(filename);
      return 1;
    }
    
    int16_t checkPath(uint16_t store, char *filename)
    {
      int ln=strlen(filename);
      int i1=-1;
      for(int ii=0;ii<ln;ii++) if(filename[ii]=='/') i1=ii;
      if(i1<0) {
        //Serial.println("NO PATH");
        return 1; // no path
      }
      filename[i1]=0;
      if(!storfs[store].exists(filename))
      { 
        //Serial.println("MAKING DIRECTORY");
        Serial.println(filename); 
        if(!storfs[store].mkdir(filename)) 
        {
          Serial.println("directory fail");
          return 0;
        }
      }
      //Serial.println("Check path OK");
      filename[i1]='/';
      return 1;
    }
    
    
    /*************************** Filing *****************************/
    int16_t file_open(uint16_t store)
    { 
      if(!makeFilename(filename)) {
        //Serial.println("filename created");
        return 0;
      }
      if(!checkPath(store, filename)){
        //Serial.println("check path fail");
        return 0;
      }
      //Serial.println("Opening FIle");
      mfile = storfs[store].open(filename,FILE_WRITE_BEGIN);
      Serial.printf("File Open: %d\n", mfile);
      return !(!mfile);
    }
    
    int16_t file_writeData()
    { if(!mfile) return 0;
      uint16_t arraySize = sizeof(MYDATA_t);
      uint8_t diskBuffer[arraySize];
      IMU.readSensor();
      mydata.datastruct.ax = IMU.getAccelX_mss();
      mydata.datastruct.ay = IMU.getAccelY_mss();
      mydata.datastruct.az = IMU.getAccelZ_mss();
      mydata.datastruct.gx = IMU.getGyroX_rads();
      mydata.datastruct.gy = IMU.getGyroY_rads();
      mydata.datastruct.gz = IMU.getGyroZ_rads();
      mydata.datastruct.mx = IMU.getMagX_uT();
      mydata.datastruct.my = IMU.getMagY_uT();
      mydata.datastruct.mz = IMU.getMagZ_uT();
      mydata.datastruct.temp = IMU.getTemperature_C();
      for(uint16_t j = 0; j < arraySize; j++) {
        diskBuffer[j]= mydata.I2CPacket[j];
      }
      uint32_t nb = mfile.write(diskBuffer, arraySize);
      return 1;
    }
    
    int16_t file_close(void)
    { mfile.close();
      return (!mfile);
    }
    
    void read_data_file()
    {
        uint16_t arraySize = sizeof(MYDATA_t);
      //Serial.println("Opening FIle");
      mfile = storfs[0].open(filename,FILE_READ);
      Serial.printf("FILE: %s\n", filename);
      
      mfile.seek(0);  
      
      uint64_t file_pos = 0;
      while(file_pos <= mfile.size()) {
        mfile.read(readdata.I2CPacket, arraySize);
        Serial.print(readdata.datastruct.ax,4); Serial.print("\t");
        Serial.print(readdata.datastruct.ay,4); Serial.print("\t");
        Serial.print(readdata.datastruct.az,4); Serial.print("\t");
        Serial.print(readdata.datastruct.gx,4); Serial.print("\t");
        Serial.print(readdata.datastruct.gy,4); Serial.print("\t");
        Serial.print(readdata.datastruct.gz,4); Serial.print("\t");
        Serial.print(readdata.datastruct.mx,4); Serial.print("\t");
        Serial.print(readdata.datastruct.my,4); Serial.print("\t");
        Serial.print(readdata.datastruct.mz,4); Serial.print("\t");
        Serial.println(readdata.datastruct.temp);
        
        file_pos += arraySize;
      }
    }

  21. #796
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    6,772
    @defragster - @KurtE - @Paul
    Just did a few more tests using the MPT-logger sketch just as a sanity check and see if I got any ECC errors well everything worked. The below is a table of chips tested and whether they Passed - data was written and read back.

    Code:
    w25M02
    SPI - Passed
    QSPI - Passed
    
    W25N01
    SPI - Passed
    QSPI - Passed
    
    W25N02
    SPI - Passed
    QSPI - Not tested
    
    w25Q128JV
    SPI - Passed
    QSPI - Passed
    
    W25Q64JV
    SPI - Passed
    QSPI - Passed
    
    Cypress 1 MByte
    SPI - Pass

  22. #797
    Senior Member
    Join Date
    Jul 2014
    Posts
    3,120
    Quote Originally Posted by mjs513 View Post
    there are a few commands you can send:
    s - start data acquisition
    q - stop data acquisition
    r - read data file you just created on flash
    d - first dump list of storages
    x - reset.
    ]
    I only wanted to clarify the "d - first dump list of storages" option
    this prints out the relevant info of the index file, mtp uses to access the files.
    It is only useful during development and requires understanding of the index file structure , so consider this as a debug tool.

  23. #798
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    6,772
    @WMXZ
    I have been using @KurtE's branch of MTP for testing so I left the 'd' in to test events - as a debug tool as you said.

  24. #799
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    6,772
    @PaulStoffregen - @defragster - @KurtE

    In regards to BBM on the W25N02 I contacted Winbond to see if it was an error in the datasheet for the N02. Well, I got back an answer today and basically its not supported:
    Answer 2 : Hi Michael,
    The W25N02KV datasheet is correct and does not have BBM. NAND devices are page access where BBM is not as critical and relies on the controller to handle bad blocks.
    Regards,
    Robin
    (2021-01-14 02:12:49)
    So not sure what do with the N02 at this point.

  25. #800
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    6,772
    Ok just pushed the last update to plusNAND as well @Paul 's LittleFS library.

    I added support for Bad Block Management (BBM) for the M02. As I said before its not turned on (didn't send the command). Reason: Not 100% sure I implemented correctly and since I am not getting any ECC errors no way for me to confirm (at least to my satisfaction). If anyone wants to take a look and double check please feel free.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •