Sounds good.
Maybe time to do PR back into LittleFS?
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.
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.)
//#define TEST_RAM [B]// can be PSRAM or smaller in processor RAM1 or RAM2[/B]
//#define TEST_SPI [B]// this is Propshield or other SPI based media - where the "FlashChipSelect" is passed[/B]
//#define TEST_QSPI [B]// this is T_4.1 underside QSPI FLASH[/B]
//#define TEST_SPI_NAND [B]// this is a NAND device SPI based media - where the "FlashChipSelect" is passed[/B]
#define TEST_QSPI_NAND [B]// this is T_4.1 underside QSPI NAND device FLASH[/B]
//#define TEST_PROG [B]// this uses PCB onboard FLASH - tested with T_4.x : it is currently erase on every code upload, but static across restarts[/B]
//#define TEST_MRAM [B]// this is a supported SPI MRAM/FRAM chip - where the "FlashChipSelect" is passed[/B]
#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
Thanks I guess I overlooked comments with those defines.
#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
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
#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
#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
35 B1_12 IOMUXC_LPUART5_TX_SELECT_INPUT=1 CSI_PIXCLK ENET_1588_EVENT0_IN [COLOR="#FF0000"]FLEXIO2_FLEXIO28 [/COLOR]GPIO2_IO28 USDHC1_CD_B FLEXIO3_FLEXIO28
36 B1_02 LCD_DATA14 XBAR1_INOUT16 LPSPI4_PCS2 SAI1_TX_BCLK [COLOR="#FF0000"]FLEXIO2_FLEXIO18 [/COLOR]GPIO2_IO18 FLEXPWM2_PWMA03 ENET2_RDATA01 FLEXIO3_FLEXIO18
37 B1_03 LCD_DATA15 IOMUXC_XBAR1_IN17_SELECT_INPUT=0x3 LPSPI4_PCS1 SAI1_TX_SYNC [COLOR="#FF0000"]FLEXIO2_FLEXIO19 [/COLOR]GPIO2_IO19 FLEXPWM2_PWMB03 ENET2_RX_EN FLEXIO3_FLEXIO19
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.I assume you have already tested that creating a directory works in a simple case.
Been using it a T4.1 - works for everything that I tested so far except NAND QSPI.What Teensy is this supposed to run on?
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
#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();
}
}
#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);
}
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:
But looking at my T4.1 Excel document: I see: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
That MUX(4) is for FlexIO on those pins...Code:35 B1_12 IOMUXC_LPUART5_TX_SELECT_INPUT=1 CSI_PIXCLK ENET_1588_EVENT0_IN [COLOR="#FF0000"]FLEXIO2_FLEXIO28 [/COLOR]GPIO2_IO28 USDHC1_CD_B FLEXIO3_FLEXIO28 36 B1_02 LCD_DATA14 XBAR1_INOUT16 LPSPI4_PCS2 SAI1_TX_BCLK [COLOR="#FF0000"]FLEXIO2_FLEXIO18 [/COLOR]GPIO2_IO18 FLEXPWM2_PWMA03 ENET2_RDATA01 FLEXIO3_FLEXIO18 37 B1_03 LCD_DATA15 IOMUXC_XBAR1_IN17_SELECT_INPUT=0x3 LPSPI4_PCS1 SAI1_TX_SYNC [COLOR="#FF0000"]FLEXIO2_FLEXIO19 [/COLOR]GPIO2_IO19 FLEXPWM2_PWMB03 ENET2_RX_EN FLEXIO3_FLEXIO19
@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...
#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;
}
}
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
I only wanted to clarify the "d - first dump list of storages" optionthere 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.
]
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)