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