diff --git a/libraries/I2S/examples/I2S_input_D51/I2S_input_D51.ino b/libraries/I2S/examples/I2S_input_D51/I2S_input_D51.ino new file mode 100644 index 000000000..ff51f4cee --- /dev/null +++ b/libraries/I2S/examples/I2S_input_D51/I2S_input_D51.ino @@ -0,0 +1,116 @@ +/* + This example reads audio data from an Invensense's ICS43432 I2S microphone + breakout board, and prints out the samples to the Serial console. The + Serial Plotter built into the Arduino IDE can be used to plot the audio + data (Tools -> Serial Plotter) + + Circuit: + Arduino/Genuino Zero, MKRZero or MKR1000 board + ICS43432: + GND connected GND + 3.3V connected 3.3V (Zero) or VCC (MKR1000, MKRZero) + WS connected to pin 0 (Zero) or pin 3 (MKR1000, MKRZero) + CLK connected to pin 1 (Zero) or pin 2 (MKR1000, MKRZero) + SD connected to pin 9 (Zero) or pin A6 (MKR1000, MKRZero) + + created 17 November 2016 + by Sandeep Mistry +*/ + + +// Used by Adafruit on +// It eventually hangs, so it's recommend to use "ArduinoSound" Library on SAMD21G is discussed here: +// Dictaphone project - MKRZero hangs indefinitely: https://forum.arduino.cc/index.php?topic=455963.0 +// i2s mic returns 0 or -1: https://forums.adafruit.com/viewtopic.php?f=19&t=115089 +// This sketch is NOT compatible with SAMD51J + +#include +//#define Serial SerialUSB +uint8_t _buffer[I2S_BUFFER_SIZE]; + +void setup() { + //I2S = I2SClass(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SDI, PIN_I2S_SCK, PIN_I2S_FS); + // Open serial communications and wait for port to open: + // A baud rate of 115200 is used instead of 9600 for a faster data rate + // on non-native USB ports + Serial.begin(115200); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + Serial.println("I2S_input_D51"); + // start I2S at 8 kHz with 32-bits per sample + if (!I2S.begin(I2S_PHILIPS_MODE, 48000, 32)) { // 48,000,000 / sampleRate must be a multiple of 64 + Serial.println("Failed to initialize I2S!"); + while (1); // do nothing + } + + I2S.read(_buffer, I2S_BUFFER_SIZE); +} + +void loop() +{ + + int32_t sampleLeft, sampleRight = 0; + +// Scan & Print samples using I2S DMA double buffer technique + while (!I2S.available()); + int bytes = I2S.read(_buffer, I2S_BUFFER_SIZE); + //Serial.println("bytes: " + String(bytes)); // must be 512 bytes + + for (int i = 0; i < bytes; i += 8) + { + // Get Samples + memcpy(&sampleLeft, &_buffer[i], 4); + memcpy(&sampleRight, &_buffer[i+4], 4); + + // convert to 24 bits + //sampleLeft >>= 8; + //sampleLeft &= 0x00ffffff; + //sampleRight >>= 8; + //sampleRight &= 0x00ffffff; + + // Get samples another way + //sampleLeft = 0; + //sampleLeft |= ((uint32_t)_buffer[i]) << 16; // MSB + //sampleLeft |= ((uint32_t)_buffer[i+1]) << 8; + //sampleLeft |= ((uint32_t)_buffer[i+2]) << 0; + //sampleLeft |= ((uint32_t)_buffer[i+3] << 24); // LSB, commented for 24 bit resolution + + //sampleRight = 0; + //sampleRight |= ((uint32_t)_buffer[i+4]) << 16; // MSB + //sampleRight |= ((uint32_t)_buffer[i+5]) << 8; + //sampleRight |= ((uint32_t)_buffer[i+6]) << 0; + //sampleRight |= ((uint32_t)_buffer[i+7] << 24); // LSB + + // print samples + Serial.print(sampleLeft); + Serial.print(","); + Serial.println(sampleRight); + } + +// Scan using one read + //sampleLeft = I2S.read(); Serial.println(sampleLeft); + //sampleRight = I2S.read(); Serial.println(sampleRight); + + +/* +//Scan using Adafruit Zero I2S Read + I2S.read(&sampleLeft, &sampleRight); + + Serial.print(sampleLeft); // sampleLeft + Serial.print(","); + Serial.println(sampleRight); // sampleRight + */ + + /* + I2S.read(_buffer, I2S_BUFFER_SIZE); + for (int i=0; i +#include + +#define Serial Serial + +#undef AUDIO_SAMPLE_RATE +#undef AUDIO_BITS_PER_SAMPLE + +#define AUDIO_SAMPLE_RATE 48000 +#define AUDIO_BITS_PER_SAMPLE 16 + + +const String fileName = "MyRECORD.WAV"; +//write wav +unsigned long ChunkSize = 0L; +unsigned long Subchunk1Size = 16; +unsigned int AudioFormat = 1; +unsigned int numChannels = 2; //1 +unsigned long sampleRate = AUDIO_SAMPLE_RATE; +unsigned int bitsPerSample = AUDIO_BITS_PER_SAMPLE; // 16 +unsigned long byteRate = sampleRate*numChannels*(bitsPerSample/8);// samplerate x channels x (bitspersample / 8) +unsigned int blockAlign = numChannels*bitsPerSample/8; +unsigned long Subchunk2Size = 0L; +unsigned long recByteSaved = 0L; +unsigned long NumSamples = 0L; +byte byte1, byte2, byte3, byte4; + +int mode = 0; // 0=stopped, 1=recording, 2=playing +File frec; +long msecs; //elapsedMillis + +// Use these with the Teensy 3.5 & 3.6 SD card +#define SDCARD_CS_PIN 10 +volatile int available = 0; +volatile int read = 0; +uint8_t buffer[I2S_BUFFER_SIZE]; + +#define WRITE_BUFFER_SIZE 512 +byte bufferToWrite[WRITE_BUFFER_SIZE]; +int lenOfBufferToWrite = 0; +long startTime = 0; + +void setup() +{ + Serial.begin(115200); + while(!Serial); + + Serial.println("metroM4RecordWAV.ino"); + while (!(SD.begin(SDCARD_CS_PIN))) { + // stop here, but print a message repetitively + Serial.println("Unable to access the SD card"); + delay(500); + } + + I2S.onReceive(onI2SReceive); + + //int sampleRateForI2S = 31250; // 48,000,000 / sampleRate must be a multiple of 64 + while (!I2S.begin(I2S_PHILIPS_MODE, AUDIO_SAMPLE_RATE, 32)) + { + Serial.println("Failed to initialize I2S!"); + delay(500); + } + +/* + startRecording(); + stopRecording(); + while(true); + */ +} + + +void loop() { + // send data only when you receive data: + if (Serial.available() > 0) { + // read the incoming byte: + byte incomingByte = Serial.read(); + // Respond to button presses + if ( incomingByte == '1' ) { + Serial.println("Record Button Press"); + if (mode == 2) stopPlaying(); + if (mode == 0) startRecording(); + } + if ( incomingByte == '2' ) { + Serial.println("Stop Button Press"); + if (mode == 1) stopRecording(); + if (mode == 2) stopPlaying(); + } + if ( incomingByte == '3' ) { + Serial.println("Play Button Press"); + if (mode == 1) stopRecording(); + if (mode == 0) startPlaying(); + } + } + if (mode == 1) { + continueRecording(); + } + +} + +void onI2SReceive() +{ + // This function will run at a frequency of (sampleRate / 64) + // At 31.25khz, this is every 1962.5 microseconds so make sure any processing here takes less + // time than that about 1.1798, 1817, 1768 + //Serial.println("onI2SReceive called after " + String((micros()-startTime)) + " milliseconds"); + read = I2S.read(buffer, I2S_BUFFER_SIZE); // Will actually read 256 bytes + available = 1; + //startTime = micros(); +} + +void startRecording() { + Serial.println("startRecording"); + if (SD.exists(fileName)) + SD.remove(fileName); + + frec = SD.open(fileName, O_CREAT | O_TRUNC | O_RDWR); // if opened as FILE_WRITE cannot seek to another position + if (frec) { + I2S.read(); // Needed to start the interrupt handler + //queue1.begin(); // teensy doble buffer + mode = 1; + recByteSaved = 0L; + } + startTime = millis(); +} + + + + +void continueRecording() { + + if (available)//(I2S.available() >= 2) + { + /* +// Technique from @MohitB + for (int i = 0; i < 32; i++)//32, 512/8/2 = 32 + { + // buffer[0] to buffer[3] are zeros (it is the other channel), so we skip those + // buffer[4] is always zero + // buffer[5], [6] and [7] are the interesting data, on 24 bits (6 of which are always zeros) + // Etc + // We only want the 256 first values, hence we stope at 8 * 31 + 7 = 255 + // More info on robsgreen's great post: https://forums.adafruit.com/viewtopic.php?f=19&t=115089&start=15#wrap + memcpy(&bufferToWrite[lenOfBufferToWrite + 3 * i], &buffer[(8 * i) + 5], 3); //3 + } + lenOfBufferToWrite += 96; //96 + + if (lenOfBufferToWrite == 2016) + { + // Write on SD card by batches of 2048 bits for better performance + frec.write(bufferToWrite, 2016); + lenOfBufferToWrite = 0; + recByteSaved += 2016; + //Serial.println(recByteSaved); + } + read = 0; + */ +// NekuNeko technique 1 + // write all 512 bytes to the SD card + frec.write(buffer, read); + recByteSaved += read; + + /* +// NekuNeko technique 2 + for (int i=0; i 0) { + int bytes = I2S.read(buffer, I2S_BUFFER_SIZE); + frec.write((byte*)queue1.readBuffer(), 256); + queue1.freeBuffer(); + recByteSaved += 256; + }*/ + + +/* +// Nekuneko technique 2 + if (lenOfBufferToWrite > 0) + { // swap MSB & LSB + for (int i=0; i<=lenOfBufferToWrite-2; i+=2) + { + uint8_t aux = bufferToWrite[i]; + bufferToWrite[i] = bufferToWrite[i+1]; + bufferToWrite[i+1] = aux; + } + + frec.write(bufferToWrite, lenOfBufferToWrite); + recByteSaved += lenOfBufferToWrite; + lenOfBufferToWrite = 0; + } + */ + Serial.println("Recording has taken about: " + String((millis()-startTime)/1000.0) + " seconds"); + writeOutHeader(); + frec.close(); + } + mode = 0; +} + + +void startPlaying() { + Serial.println("startPlaying"); + //audioSD.play("RECORD.WAV"); // teensy + mode = 2; + +} + + +void stopPlaying() { + Serial.println("stopPlaying"); + //if (mode == 2) audioSD.stop(); // teensy + mode = 0; +} +void writeOutHeader() { // update WAV header with final filesize/datasize + +// NumSamples = (recByteSaved*8)/bitsPerSample/numChannels; +// Subchunk2Size = NumSamples*numChannels*bitsPerSample/8; // number of samples x number of channels x number of bytes per sample + Subchunk2Size = recByteSaved; + ChunkSize = Subchunk2Size + 36; + frec.seek(0); + delay(10); + frec.write("RIFF"); + byte1 = ChunkSize & 0xff; + byte2 = (ChunkSize >> 8) & 0xff; + byte3 = (ChunkSize >> 16) & 0xff; + byte4 = (ChunkSize >> 24) & 0xff; + frec.write(byte1); frec.write(byte2); frec.write(byte3); frec.write(byte4); + frec.write("WAVE"); + frec.write("fmt "); + byte1 = Subchunk1Size & 0xff; + byte2 = (Subchunk1Size >> 8) & 0xff; + byte3 = (Subchunk1Size >> 16) & 0xff; + byte4 = (Subchunk1Size >> 24) & 0xff; + frec.write(byte1); frec.write(byte2); frec.write(byte3); frec.write(byte4); + byte1 = AudioFormat & 0xff; + byte2 = (AudioFormat >> 8) & 0xff; + frec.write(byte1); frec.write(byte2); + byte1 = numChannels & 0xff; + byte2 = (numChannels >> 8) & 0xff; + frec.write(byte1); frec.write(byte2); + byte1 = sampleRate & 0xff; + byte2 = (sampleRate >> 8) & 0xff; + byte3 = (sampleRate >> 16) & 0xff; + byte4 = (sampleRate >> 24) & 0xff; + frec.write(byte1); frec.write(byte2); frec.write(byte3); frec.write(byte4); + byte1 = byteRate & 0xff; + byte2 = (byteRate >> 8) & 0xff; + byte3 = (byteRate >> 16) & 0xff; + byte4 = (byteRate >> 24) & 0xff; + frec.write(byte1); frec.write(byte2); frec.write(byte3); frec.write(byte4); + byte1 = blockAlign & 0xff; + byte2 = (blockAlign >> 8) & 0xff; + frec.write(byte1); frec.write(byte2); + byte1 = bitsPerSample & 0xff; + byte2 = (bitsPerSample >> 8) & 0xff; + frec.write(byte1); frec.write(byte2); + frec.write("data"); + byte1 = Subchunk2Size & 0xff; + byte2 = (Subchunk2Size >> 8) & 0xff; + byte3 = (Subchunk2Size >> 16) & 0xff; + byte4 = (Subchunk2Size >> 24) & 0xff; + frec.write(byte1); frec.write(byte2); frec.write(byte3); frec.write(byte4); + frec.close(); + Serial.println("header written"); + Serial.print("Subchunk2: "); + Serial.println(Subchunk2Size); +} diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 4c8ab68e8..6d808041e 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -39,6 +39,7 @@ static I2SDevice_SAMD21G18x i2sd(*I2S); int I2SClass::_beginCount = 0; + I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, uint8_t sckPin, uint8_t fsPin) : _deviceIndex(deviceIndex), _clockGenerator(clockGenerator), @@ -54,6 +55,7 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u _onTransmit(NULL), _onReceive(NULL) { + } int I2SClass::begin(int mode, long sampleRate, int bitsPerSample) @@ -74,6 +76,7 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc return 0; } + i2sd.disable(); switch (mode) { case I2S_PHILIPS_MODE: case I2S_RIGHT_JUSTIFIED_MODE: @@ -99,7 +102,6 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc // try to allocate a DMA channel DMA.begin(); - _dmaChannel = DMA.allocateChannel(); if (_dmaChannel < 0) { @@ -121,10 +123,10 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc _beginCount++; - if (driveClock) { - // set up clock - enableClock(sampleRate * 2 * bitsPerSample); - + if (driveClock) + { + // set up clock + enableClock(sampleRate * 2 * _bitsPerSample); // this library assumes 2 channels, stereo audio i2sd.setSerialClockSelectMasterClockDiv(_deviceIndex); i2sd.setFrameSyncSelectSerialClockDiv(_deviceIndex); } else { @@ -145,9 +147,15 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc i2sd.setSlotSize(_deviceIndex, bitsPerSample); i2sd.setDataSize(_deviceIndex, bitsPerSample); +#if defined(__SAMD51__) + pinPeripheral(_sckPin, PIO_I2S); + pinPeripheral(_fsPin, PIO_I2S); +#else // SAMD21 pinPeripheral(_sckPin, PIO_COM); - pinPeripheral(_fsPin, PIO_COM); + pinPeripheral(_fsPin, PIO_COM); +#endif + // Serial.println ("mode: " + String(mode)); if (mode == I2S_RIGHT_JUSTIFIED_MODE) { i2sd.setSlotAdjustedRight(_deviceIndex); } else { @@ -156,14 +164,25 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc i2sd.setClockUnit(_deviceIndex); +#if defined(__SAMD51__) + pinPeripheral(_sdPin, PIO_I2S); +#else // SAMD21 pinPeripheral(_sdPin, PIO_COM); - +#endif +/* +// Test what pins are being used + Serial.println("_sdPin: " + String(_sdPin)); + Serial.println("_sckPin: " + String(_sckPin)); + Serial.println("_fsPin: " + String(_fsPin)); + while(true); +*/ // done configure enable i2sd.enable(); _doubleBuffer.reset(); return 1; + } void I2SClass::end() @@ -205,6 +224,8 @@ int I2SClass::available() enableReceiver(); } +// printRegisters(); + uint8_t enableInterrupts = ((__get_PRIMASK() & 0x1) == 0); size_t avail; @@ -240,7 +261,6 @@ union i2s_sample_t { int I2SClass::read() { i2s_sample_t sample; - sample.b32 = 0; read(&sample, _bitsPerSample / 8); @@ -320,13 +340,25 @@ int I2SClass::availableForWrite() return space; } +// New, just for testing, blocking read +void I2SClass::read(int32_t *left, int32_t *right) +{ + if (_state != I2S_STATE_RECEIVER) + enableReceiver(); + i2sd.read(left, right); +} +// int I2SClass::read(void* buffer, size_t size) { if (_state != I2S_STATE_RECEIVER) { enableReceiver(); } +// Testing registers if they match AdafruitZeroI2S configuration +// i2sd.printRegisters(); +// while(true); + uint8_t enableInterrupts = ((__get_PRIMASK() & 0x1) == 0); // disable interrupts, @@ -349,7 +381,7 @@ int I2SClass::read(void* buffer, size_t size) __enable_irq(); } - return read; + return size; } size_t I2SClass::write(int sample) @@ -415,63 +447,112 @@ void I2SClass::onReceive(void(*function)(void)) _onReceive = function; } -void I2SClass::enableClock(int divider) +void I2SClass::enableClock(int divider/*, int mck_mult*/) { +#if defined(__SAMD51__) + + // divider = sampleRate * numChannels * _bitsPerSample (44100 * 2 * 32) + uint32_t mckFreq = (divider / 2 / _bitsPerSample) * 256; // (fs_freq * mck_mult), assumes 2 channels and mck_mult = 256 + uint32_t sckFreq = divider; // (fs_freq * I2S_NUM_SLOTS * bitsPerSample) + + uint32_t gclkval = GCLK_PCHCTRL_GEN_GCLK1_Val; + uint32_t gclkFreq = VARIANT_GCLK1_FREQ; + + uint8_t mckoutdiv = min( (gclkFreq/mckFreq) - 1, 63); + uint8_t mckdiv = min( (gclkFreq/sckFreq) - 1, 63 ); + + // divider it too big, use 120Mhz oscillator instead? + if (((VARIANT_GCLK1_FREQ/mckFreq) - 1) > 63) { + gclkval = GCLK_PCHCTRL_GEN_GCLK4_Val; + gclkFreq = 12000000; // 120Mhz + } +/* + Serial.println ("divider: " + String(divider)); + Serial.println ("mckoutdiv: " + String(mckoutdiv)); + Serial.println ("mckdiv: " + String(mckdiv)); + */ + //while (GCLK->SYNCBUSY.reg); + + // if this is not configured I2S won't work at all. + GCLK->PCHCTRL[I2S_GCLK_ID_0].reg = gclkval | GCLK_PCHCTRL_CHEN; + GCLK->PCHCTRL[I2S_GCLK_ID_1].reg = gclkval | GCLK_PCHCTRL_CHEN; + + i2sd.setMasterClockOutputDivisionFactor(_deviceIndex, mckoutdiv); + i2sd.setMasterClockDivisionFactor(_deviceIndex, mckdiv); + i2sd.setMasterClockEnable(_deviceIndex); + i2sd.setFrameSyncPulse(_deviceIndex, I2S_CLKCTRL_FSWIDTH_HALF_Val); + +#else // SAMD21 int div = SystemCoreClock / divider; - int src = GCLK_GENCTRL_SRC_DFLL48M_Val; + int src = GCLK_GENCTRL_SRC_DFLL48M_Val; if (div > 255) { // divider is too big, use 8 MHz oscillator instead div = 8000000 / divider; - src = GCLK_GENCTRL_SRC_OSC8M_Val; + src = GCLK_GENCTRL_SRC_OSC8M_Val; } // configure the clock divider - while (GCLK->STATUS.bit.SYNCBUSY); - GCLK->GENDIV.bit.ID = _clockGenerator; - GCLK->GENDIV.bit.DIV = div; + while (GCLK->STATUS.bit.SYNCBUSY); + GCLK->GENDIV.bit.ID = _clockGenerator; + GCLK->GENDIV.bit.DIV = div; // use the DFLL as the source - while (GCLK->STATUS.bit.SYNCBUSY); - GCLK->GENCTRL.bit.ID = _clockGenerator; - GCLK->GENCTRL.bit.SRC = src; - GCLK->GENCTRL.bit.IDC = 1; - GCLK->GENCTRL.bit.GENEN = 1; + while (GCLK->STATUS.bit.SYNCBUSY); + GCLK->GENCTRL.bit.ID = _clockGenerator; + GCLK->GENCTRL.bit.SRC = src; + GCLK->GENCTRL.bit.IDC = 1; + GCLK->GENCTRL.bit.GENEN = 1; // enable - while (GCLK->STATUS.bit.SYNCBUSY); - GCLK->CLKCTRL.bit.ID = i2sd.glckId(_deviceIndex); - GCLK->CLKCTRL.bit.GEN = _clockGenerator; - GCLK->CLKCTRL.bit.CLKEN = 1; + while (GCLK->STATUS.bit.SYNCBUSY); + GCLK->CLKCTRL.bit.ID = i2sd.glckId(_deviceIndex); + GCLK->CLKCTRL.bit.GEN = _clockGenerator; + GCLK->CLKCTRL.bit.CLKEN = 1; - while (GCLK->STATUS.bit.SYNCBUSY); + while (GCLK->STATUS.bit.SYNCBUSY); +#endif } void I2SClass::disableClock() { - while (GCLK->STATUS.bit.SYNCBUSY); - GCLK->GENCTRL.bit.ID = _clockGenerator; - GCLK->GENCTRL.bit.SRC = GCLK_GENCTRL_SRC_DFLL48M_Val; - GCLK->GENCTRL.bit.IDC = 1; - GCLK->GENCTRL.bit.GENEN = 0; - - while (GCLK->STATUS.bit.SYNCBUSY); - GCLK->CLKCTRL.bit.ID = i2sd.glckId(_deviceIndex); - GCLK->CLKCTRL.bit.GEN = _clockGenerator; - GCLK->CLKCTRL.bit.CLKEN = 0; - - while (GCLK->STATUS.bit.SYNCBUSY); +#if defined(__SAMD51__) + // Need to be tested if works + //GCLK->PCHCTRL[I2S_GCLK_ID_0].reg = GCLK_PCHCTRL_RESETVALUE; + //GCLK->PCHCTRL[I2S_GCLK_ID_1].reg = GCLK_PCHCTRL_RESETVALUE; +#else // SAMD21 + while (GCLK->STATUS.bit.SYNCBUSY); + GCLK->GENCTRL.bit.ID = _clockGenerator; + GCLK->GENCTRL.bit.SRC = GCLK_GENCTRL_SRC_DFLL48M_Val; + GCLK->GENCTRL.bit.IDC = 1; + GCLK->GENCTRL.bit.GENEN = 0; + + while (GCLK->STATUS.bit.SYNCBUSY); + GCLK->CLKCTRL.bit.ID = i2sd.glckId(_deviceIndex); + GCLK->CLKCTRL.bit.GEN = _clockGenerator; + GCLK->CLKCTRL.bit.CLKEN = 0; + + while (GCLK->STATUS.bit.SYNCBUSY); +#endif } void I2SClass::enableTransmitter() { +#if defined (__SAMD51__) + i2sd.setTxMode(_deviceIndex, _bitsPerSample); +#else i2sd.setTxMode(_deviceIndex); +#endif i2sd.enableClockUnit(_deviceIndex); i2sd.enableSerializer(_deviceIndex); DMA.incSrc(_dmaChannel); DMA.onTransferComplete(_dmaChannel, I2SClass::onDmaTransferComplete); +#if defined (__SAMD51__) + DMA.setTriggerSource(_dmaChannel, i2sd.dmaTriggerSource(_deviceIndex, true)); +#else DMA.setTriggerSource(_dmaChannel, i2sd.dmaTriggerSource(_deviceIndex)); +#endif DMA.setTransferWidth(_dmaChannel, _bitsPerSample); _state = I2S_STATE_TRANSMITTER; @@ -479,13 +560,21 @@ void I2SClass::enableTransmitter() void I2SClass::enableReceiver() { +#if defined (__SAMD51__) + i2sd.setRxMode(_deviceIndex, _bitsPerSample); +#else i2sd.setRxMode(_deviceIndex); +#endif i2sd.enableClockUnit(_deviceIndex); i2sd.enableSerializer(_deviceIndex); DMA.incDst(_dmaChannel); DMA.onTransferComplete(_dmaChannel, I2SClass::onDmaTransferComplete); +#if defined (__SAMD51__) + DMA.setTriggerSource(_dmaChannel, i2sd.dmaTriggerSource(_deviceIndex, false)); +#else DMA.setTriggerSource(_dmaChannel, i2sd.dmaTriggerSource(_deviceIndex)); +#endif DMA.setTransferWidth(_dmaChannel, _bitsPerSample); _state = I2S_STATE_RECEIVER; @@ -542,6 +631,18 @@ void I2SClass::onDmaTransferComplete(int channel) #endif } -#if I2S_INTERFACES_COUNT > 0 -I2SClass I2S(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SCK, PIN_I2S_FS); -#endif + +#if defined(__SAMD51__) + // If you want to use this class as output on SAMD51, you can redefine this on arduino's setup function as: + // I2S = I2SClass(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SDO, PIN_I2S_SCK, PIN_I2S_FS); + #if I2S_INTERFACES_COUNT > 0 + I2SClass I2S(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SDI, PIN_I2S_SCK, PIN_I2S_FS); + #endif + +#else + + #if I2S_INTERFACES_COUNT > 0 + I2SClass I2S(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SCK, PIN_I2S_FS); + #endif + +#endif \ No newline at end of file diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 855f26b4c..b995ca09e 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -52,7 +52,8 @@ class I2SClass : public Stream virtual size_t write(const uint8_t *buffer, size_t size); virtual int availableForWrite(); - + + void read(int32_t *left, int32_t *right); // this function is NOT available on Standard Arduino's I2S API, just for testing int read(void* buffer, size_t size); size_t write(int); diff --git a/libraries/I2S/src/utility/SAMD51_I2SDevice.h b/libraries/I2S/src/utility/SAMD51_I2SDevice.h index ba0320afe..0eb228306 100644 --- a/libraries/I2S/src/utility/SAMD51_I2SDevice.h +++ b/libraries/I2S/src/utility/SAMD51_I2SDevice.h @@ -22,6 +22,7 @@ class I2SDevice_SAMD51 { public: + I2SDevice_SAMD51(I2s& _i2s) : i2s(_i2s) { @@ -31,6 +32,7 @@ class I2SDevice_SAMD51 { inline void reset() { while(i2s.SYNCBUSY.bit.SWRST); i2s.CTRLA.bit.SWRST = 1; + //while(i2s.SYNCBUSY.bit.SWRST || i2s.SYNCBUSY.bit.ENABLE); //wait for sync } inline void disable() { @@ -46,6 +48,110 @@ class I2SDevice_SAMD51 { inline int glckId(int index) { return (index == 0) ? I2S_GCLK_ID_0 : I2S_GCLK_ID_1; } +// --- New for SAMD51, taken from Adafrui Zero I2S Library ---- + inline void setMasterClockOutputDivisionFactor(int index, uint8_t mckoutdiv) { + // printRegisters(); + // Serial.println ("index: " + String(index)); + // Serial.println ("mckoutdiv: " + String(mckoutdiv)); + while(i2s.SYNCBUSY.reg); // wait for sync + i2s.CLKCTRL[index].reg |= I2S_CLKCTRL_MCKOUTDIV(mckoutdiv); //(I2S_CLKCTRL_MCKOUTDIV_Msk & ((mckoutdiv) << I2S_CLKCTRL_MCKOUTDIV_Pos)); + // Serial.println ("mckoutdiv: " + String(i2s.CLKCTRL[index].bit.MCKOUTDIV)); + } + + inline void setMasterClockDivisionFactor(int index, uint8_t mckdiv) { + while(i2s.SYNCBUSY.reg); // wait for sync + i2s.CLKCTRL[index].reg |= I2S_CLKCTRL_MCKDIV(mckdiv); + } + + inline void setMasterClockEnable(int index) { + while(i2s.SYNCBUSY.reg); // wait for sync + i2s.CLKCTRL[index].reg |= I2S_CLKCTRL_MCKEN; + } + + inline void setFrameSyncPulse(int index, uint8_t fswidth) { // possible values are: I2S_CLKCTRL_FSWIDTH_SLOT_Val, I2S_CLKCTRL_FSWIDTH_HALF_Val, I2S_CLKCTRL_FSWIDTH_BIT_Val, I2S_CLKCTRL_FSWIDTH_BURST_Val + while(i2s.SYNCBUSY.reg); // wait for sync + i2s.CLKCTRL[index].reg |= I2S_CLKCTRL_FSWIDTH(fswidth); + } + + // added bitsPerSample parameter + inline void setTxMode(int index, int bitsPerSample) { + + // Previously used here: + //i2s.RXCTRL.bit.SERMODE = 0x01; + //i2s.TXCTRL.reg &= ~(0x03); //TODO: why is this not in CMSIS... <- I think Atmel's datasheet has an errata, it has no sense to set this serializer as RX + //i2s.TXCTRL.reg |= 0x01; + + uint8_t wordSize = 0; + switch (bitsPerSample) + { + case 8: + wordSize = I2S_TXCTRL_DATASIZE_8_Val; + break; + case 16: + wordSize = I2S_TXCTRL_DATASIZE_16_Val; + break; + case 24: + wordSize = I2S_TXCTRL_DATASIZE_24_Val; + break; + case 32: + wordSize = I2S_TXCTRL_DATASIZE_32_Val; + break; + } + + i2s.TXCTRL.reg &= I2S_TXCTRL_RESETVALUE; + i2s.TXCTRL.reg = I2S_TXCTRL_DMA_SINGLE | + I2S_TXCTRL_MONO_STEREO | + I2S_TXCTRL_BITREV_MSBIT | + I2S_TXCTRL_EXTEND_ZERO | + I2S_TXCTRL_WORDADJ_RIGHT | + I2S_TXCTRL_DATASIZE(wordSize) | + I2S_TXCTRL_TXSAME_ZERO | + I2S_TXCTRL_TXDEFAULT_ZERO; + + // printRegisters(); + // while(true); + } + + // added bitsPerSample parameter + inline void setRxMode(int index, int bitsPerSample) { + + //i2s.RXCTRL.bit.SERMODE = 0x00; + //i2s.TXCTRL.reg &= ~(0x03); //TODO: why is this not in CMSIS... + //i2s.TXCTRL.reg |= 0x00; + + uint8_t wordSize = 0; + switch (bitsPerSample) + { + case 8: + wordSize = I2S_RXCTRL_DATASIZE_8_Val; + break; + case 16: + wordSize = I2S_RXCTRL_DATASIZE_16_Val; + break; + case 24: + wordSize = I2S_RXCTRL_DATASIZE_24_Val; + break; + case 32: + wordSize = I2S_RXCTRL_DATASIZE_32_Val; + break; + } + + //while(i2s.SYNCBUSY.reg); // wait for sync + i2s.RXCTRL.reg &= I2S_RXCTRL_RESETVALUE; + i2s.RXCTRL.reg = I2S_RXCTRL_DMA_SINGLE | //I2S_RXCTRL_DMA_MULTIPLE | <- IT HANGS + I2S_RXCTRL_MONO_STEREO | + I2S_RXCTRL_BITREV_MSBIT | + I2S_RXCTRL_EXTEND_ZERO | // ZERO fills with ONEs and ONE fills with ZEROs, CMSIS errata? + I2S_RXCTRL_WORDADJ_RIGHT | + I2S_RXCTRL_DATASIZE(wordSize) | + I2S_RXCTRL_SLOTADJ_RIGHT | + I2S_RXCTRL_CLKSEL_CLK0 | + I2S_RXCTRL_SERMODE_RX; + + // printRegisters(); + // while(true); +} +// end new----- inline void setSerialClockSelectMasterClockDiv(int index) { i2s.CLKCTRL[index].bit.SCKSEL = I2S_CLKCTRL_SCKSEL_MCKDIV_Val; @@ -132,7 +238,7 @@ class I2SDevice_SAMD51 { inline void setClockUnit(int index) { i2s.RXCTRL.bit.CLKSEL = (index == 0) ? I2S_RXCTRL_CLKSEL_CLK0_Val : I2S_RXCTRL_CLKSEL_CLK1_Val; } - +/* inline void setTxMode(int index) { i2s.RXCTRL.bit.SERMODE = 0x01; i2s.TXCTRL.reg &= ~(0x03); //TODO: why is this not in CMSIS... @@ -144,7 +250,7 @@ class I2SDevice_SAMD51 { i2s.TXCTRL.reg &= ~(0x03); //TODO: why is this not in CMSIS... i2s.TXCTRL.reg |= 0x00; } - +*/ inline void enableClockUnit(int index) { if (index == 0) { while(i2s.SYNCBUSY.bit.CKEN0); @@ -165,17 +271,28 @@ class I2SDevice_SAMD51 { } } + // this should be explitted or filtered with "bool txrx" inline void enableSerializer(int index) { i2s.CTRLA.bit.RXEN = 1; i2s.CTRLA.bit.TXEN = 1; } + // this should be explitted or filtered with "bool txrx" inline void disableSerializer(int index) { i2s.CTRLA.bit.RXEN = 0; - i2s.CTRLA.bit.TXEN = 0; + i2s.CTRLA.bit.TXEN = 0; } - inline int dmaTriggerSource(int index) { + // this is also new, dmaTriggerSource also depends on serializer furthermore clock source index + // rx = 0, tx = 1 + inline int dmaTriggerSource(int index, bool txrx = 0) { + if (txrx) { + return (index == 0) ? I2S_DMAC_ID_TX_0 : I2S_DMAC_ID_TX_1; + } else { + return (index == 0) ? I2S_DMAC_ID_RX_0 : I2S_DMAC_ID_RX_1; + } + + // D21 /* if (i2s.SERCTRL[index].bit.SERMODE == I2S_SERCTRL_SERMODE_TX_Val) { return (index == 0) ? I2S_DMAC_ID_TX_0 : I2S_DMAC_ID_TX_1; @@ -206,10 +323,30 @@ class I2SDevice_SAMD51 { inline int rxReady(int index) { return (index == 0) ? i2s.INTFLAG.bit.RXRDY0 :i2s.INTFLAG.bit.RXRDY1; } - +// for blocking read --- + inline void read(int32_t *left, int32_t *right) { + // printRegisters (); + while( (!i2s.INTFLAG.bit.RXRDY0) || i2s.SYNCBUSY.bit.RXDATA ); + *left = i2s.RXDATA.reg; + + while( (!i2s.INTFLAG.bit.RXRDY0) || i2s.SYNCBUSY.bit.RXDATA ); + *right = i2s.RXDATA.reg; +/* + if (i2s.INTFLAG.bit.RXOR0) { + Serial.println("overrun"); + i2s.INTENCLR.bit.RXOR0 = 0b1; + } +*/ + } +// --- inline int32_t readData(int index) { - while(i2s.SYNCBUSY.bit.RXDATA) - return i2s.RXDATA.bit.DATA; + + //printRegisters (); + //while( (!i2s.INTFLAG.bit.RXRDY0) || i2s.SYNCBUSY.bit.RXDATA ); + //return i2s.RXDATA.reg; + + while(i2s.SYNCBUSY.bit.RXDATA); + return i2s.RXDATA.bit.DATA; } inline void clearRxReady(int index) { @@ -224,6 +361,230 @@ class I2SDevice_SAMD51 { return (void*)&i2s.RXDATA.reg; } + + // BOOOM, This is my arduino's embeded debugger xD + inline void printRegisters() + { + // MCLK_APBAMASK + Serial.println ("SAMD51_MCLK Main Clock (APBA Mask)"); + Serial.println ("(32-bit) MCLK->APBAMASK: 0x" + String(MCLK->APBAMASK.reg, HEX) + "\t, 0b" + String(MCLK->APBAMASK.reg, BIN)); + Serial.println ("\t [31:16] Reserved"); + Serial.println ("\t [15] TC1_: 0x" + String(MCLK->APBAMASK.bit.TC1_, HEX) + "\t, 0b" + String(MCLK->APBAMASK.bit.TC1_, BIN) + "\t TC1 APB Clock Enable"); + Serial.println ("\t [14] TC0_: 0x" + String(MCLK->APBAMASK.bit.TC0_, HEX) + "\t, 0b" + String(MCLK->APBAMASK.bit.TC0_, BIN) + "\t TC0 APB Clock Enable"); + Serial.println ("\t [13] SERCOM1_: 0x" + String(MCLK->APBAMASK.bit.SERCOM1_, HEX) + "\t, 0b" + String(MCLK->APBAMASK.bit.SERCOM1_, BIN) + "\t SERCOM1 APB Clock Enable"); + Serial.println ("\t [12] SERCOM0_: 0x" + String(MCLK->APBAMASK.bit.SERCOM0_, HEX) + "\t, 0b" + String(MCLK->APBAMASK.bit.SERCOM0_, BIN) + "\t SERCOM0 APB Clock Enable"); + Serial.println ("\t [11] FREQM_: 0x" + String(MCLK->APBAMASK.bit.FREQM_, HEX) + "\t, 0b" + String(MCLK->APBAMASK.bit.FREQM_, BIN) + "\t FREQM APB Clock Enable"); + Serial.println ("\t [10] EIC_: 0x" + String(MCLK->APBAMASK.bit.EIC_, HEX) + "\t, 0b" + String(MCLK->APBAMASK.bit.EIC_, BIN) + "\t EIC APB Clock Enable"); + Serial.println ("\t [9] RTC_: 0x" + String(MCLK->APBAMASK.bit.RTC_, HEX) + "\t, 0b" + String(MCLK->APBAMASK.bit.RTC_, BIN) + "\t RTC APB Clock Enable"); + Serial.println ("\t [8] WDT_: 0x" + String(MCLK->APBAMASK.bit.WDT_, HEX) + "\t, 0b" + String(MCLK->APBAMASK.bit.WDT_, BIN) + "\t WDT APB Clock Enable"); + Serial.println ("\t [7] GCLK_: 0x" + String(MCLK->APBAMASK.bit.GCLK_, HEX) + "\t, 0b" + String(MCLK->APBAMASK.bit.GCLK_, BIN) + "\t GCLK APB Clock Enable"); + Serial.println ("\t [6] SUPC_: 0x" + String(MCLK->APBAMASK.bit.SUPC_, HEX) + "\t, 0b" + String(MCLK->APBAMASK.bit.SUPC_, BIN) + "\t SUPC APB Clock Enable"); + Serial.println ("\t [5] OSC32KCTRL_: 0x" + String(MCLK->APBAMASK.bit.OSC32KCTRL_, HEX) + "\t, 0b" + String(MCLK->APBAMASK.bit.OSC32KCTRL_, BIN) + "\t OSC32KCTRL APB Clock Enable"); + Serial.println ("\t [4] OSCCTRL_: 0x" + String(MCLK->APBAMASK.bit.OSCCTRL_, HEX) + "\t, 0b" + String(MCLK->APBAMASK.bit.OSCCTRL_, BIN) + "\t OSCCTRL APB Clock Enable"); + Serial.println ("\t [3] RSTC_: 0x" + String(MCLK->APBAMASK.bit.RSTC_, HEX) + "\t, 0b" + String(MCLK->APBAMASK.bit.RSTC_, BIN) + "\t RSTC APB Clock Enable"); + Serial.println ("\t [2] MCLK_: 0x" + String(MCLK->APBAMASK.bit.MCLK_, HEX) + "\t, 0b" + String(MCLK->APBAMASK.bit.MCLK_, BIN) + "\t MCLK APB Clock Enable"); + Serial.println ("\t [1] PM_: 0x" + String(MCLK->APBAMASK.bit.PM_, HEX) + "\t, 0b" + String(MCLK->APBAMASK.bit.PM_, BIN) + "\t PM APB Clock Enable"); + Serial.println ("\t [0] PAC_: 0x" + String(MCLK->APBAMASK.bit.PAC_, HEX) + "\t, 0b" + String(MCLK->APBAMASK.bit.PAC_, BIN) + "\t PAC APB Clock Enable"); + Serial.println (); + + // GCLK_CTRLA + Serial.println ("SAMD51_GCLK Generic Clock Generator (Control)"); + Serial.println ("(8-bit) GCLK->CTRLA: 0x" + String(GCLK->CTRLA.reg, HEX) + "\t, 0b" + String(GCLK->CTRLA.reg, BIN)); + Serial.println ("\t [7:1] Reserved"); + Serial.println ("\t [0] SWRST: 0x" + String(GCLK->CTRLA.bit.SWRST, HEX) + "\t, 0b" + String(GCLK->CTRLA.bit.SWRST, BIN) + "\t Software Reset "); + Serial.println (); + + // GCLK_PCHCTRL[I2S_GCLK_ID_0] + Serial.println ("SAMD51_GCLK Generic Clock Generator (Peripheral Clock Control)"); + Serial.println ("(32-bit) GCLK->PCHCTRL[I2S_GCLK_ID_0]: 0x" + String(GCLK->PCHCTRL[I2S_GCLK_ID_0].reg, HEX) + "\t, 0b" + String(GCLK->PCHCTRL[I2S_GCLK_ID_0].reg, BIN)); + Serial.println ("\t [31:8] Reserved"); + Serial.println ("\t [7] WRTLOCK: 0x" + String(GCLK->PCHCTRL[I2S_GCLK_ID_0].bit.WRTLOCK, HEX) + "\t, 0b" + String(GCLK->PCHCTRL[I2S_GCLK_ID_0].bit.WRTLOCK, BIN) + "\t Write Lock"); + Serial.println ("\t [6] CHEN: 0x" + String(GCLK->PCHCTRL[I2S_GCLK_ID_0].bit.CHEN, HEX) + "\t, 0b" + String(GCLK->PCHCTRL[I2S_GCLK_ID_0].bit.CHEN, BIN) + "\t Channel Enable"); + Serial.println ("\t [5:4] Reserved"); + Serial.println ("\t [3:0] GEN: 0x" + String(GCLK->PCHCTRL[I2S_GCLK_ID_0].bit.GEN, HEX) + "\t, 0b" + String(GCLK->PCHCTRL[I2S_GCLK_ID_0].bit.GEN, BIN) + "\t Generic Clock Generator"); + Serial.println (); + + // GCLK_PCHCTRL[I2S_GCLK_ID_1] + Serial.println ("SAMD51_GCLK Generic Clock Generator (Peripheral Clock Control)"); + Serial.println ("(32-bit) GCLK->PCHCTRL[I2S_GCLK_ID_1]: 0x" + String(GCLK->PCHCTRL[I2S_GCLK_ID_1].reg, HEX) + "\t, 0b" + String(GCLK->PCHCTRL[I2S_GCLK_ID_1].reg, BIN)); + Serial.println ("\t [31:8] Reserved"); + Serial.println ("\t [7] WRTLOCK: 0x" + String(GCLK->PCHCTRL[I2S_GCLK_ID_1].bit.WRTLOCK, HEX) + "\t, 0b" + String(GCLK->PCHCTRL[I2S_GCLK_ID_1].bit.WRTLOCK, BIN) + "\t Write Lock"); + Serial.println ("\t [6] CHEN: 0x" + String(GCLK->PCHCTRL[I2S_GCLK_ID_1].bit.CHEN, HEX) + "\t, 0b" + String(GCLK->PCHCTRL[I2S_GCLK_ID_1].bit.CHEN, BIN) + "\t Channel Enable"); + Serial.println ("\t [5:4] Reserved"); + Serial.println ("\t [3:0] GEN: 0x" + String(GCLK->PCHCTRL[I2S_GCLK_ID_1].bit.GEN, HEX) + "\t, 0b" + String(GCLK->PCHCTRL[I2S_GCLK_ID_1].bit.GEN, BIN) + "\t Generic Clock Generator"); + Serial.println (); + + // I2S_CTRLA + Serial.println ("SAMD51_I2S Inter-IC Sound Interface (Control A)"); + Serial.println ("(8-bit) I2S->CTRLA: 0x" + String(I2S->CTRLA.reg, HEX) + "\t, 0b" + String(I2S->CTRLA.reg, BIN)); + Serial.println ("\t [7:6] Reserved"); + Serial.println ("\t [5] RXEN: 0x" + String(I2S->CTRLA.bit.RXEN, HEX) + "\t, 0b" + String(I2S->CTRLA.bit.RXEN, BIN) + "\t Rx Serializer Enable "); + Serial.println ("\t [4] TXEN: 0x" + String(I2S->CTRLA.bit.TXEN, HEX) + "\t, 0b" + String(I2S->CTRLA.bit.TXEN, BIN) + "\t Tx Serializer Enable"); + Serial.println ("\t [3] CKEN1: 0x" + String(I2S->CTRLA.bit.CKEN1, HEX) + "\t, 0b" + String(I2S->CTRLA.bit.CKEN1, BIN) + "\t Clock Unit 1 Enable"); + Serial.println ("\t [2] CKEN0: 0x" + String(I2S->CTRLA.bit.CKEN0, HEX) + "\t, 0b" + String(I2S->CTRLA.bit.CKEN0, BIN) + "\t Clock Unit 0 Enable"); + Serial.println ("\t [1] ENABLE: 0x" + String(I2S->CTRLA.bit.ENABLE, HEX) + "\t, 0b" + String(I2S->CTRLA.bit.ENABLE, BIN) + "\t Enable"); + Serial.println ("\t [0] SWRST: 0x" + String(I2S->CTRLA.bit.SWRST, HEX) + "\t, 0b" + String(I2S->CTRLA.bit.SWRST, BIN) + "\t Software Reset"); + Serial.println (); + + // I2S_CLKCTRL0 + Serial.println ("SAMD51_I2S Inter-IC Sound Interface (Clock Unit 0 Control)"); + Serial.println ("(32-bit) I2S->CLKCTRL[0]: 0x" + String(I2S->CLKCTRL[0].reg, HEX) + "\t, 0b" + String(I2S->CLKCTRL[0].reg, BIN)); + Serial.println ("\t [31:30] Reserved"); + Serial.println ("\t [29:24] MCKOUTDIV: 0x" + String(I2S->CLKCTRL[0].bit.MCKOUTDIV, HEX) + "\t, 0b" + String(I2S->CLKCTRL[0].bit.MCKOUTDIV, BIN) + "\t Master Clock Output Division Factor"); + Serial.println ("\t [23:22] Reserved"); + Serial.println ("\t [21:16] MCKDIV: 0x" + String(I2S->CLKCTRL[0].bit.MCKDIV, HEX) + "\t, 0b" + String(I2S->CLKCTRL[0].bit.MCKDIV, BIN) + "\t Master Clock Division Factor"); + Serial.println ("\t [15] MCKOUTINV: 0x" + String(I2S->CLKCTRL[0].bit.MCKOUTINV, HEX) + "\t, 0b" + String(I2S->CLKCTRL[0].bit.MCKOUTINV, BIN) + "\t Master Clock Output Invert"); + Serial.println ("\t [14] MCKEN: 0x" + String(I2S->CLKCTRL[0].bit.MCKEN, HEX) + "\t, 0b" + String(I2S->CLKCTRL[0].bit.MCKEN, BIN) + "\t Master Clock Enable"); + Serial.println ("\t [13] MCKSEL: 0x" + String(I2S->CLKCTRL[0].bit.MCKSEL, HEX) + "\t, 0b" + String(I2S->CLKCTRL[0].bit.MCKSEL, BIN) + "\t Master Clock Select "); + Serial.println ("\t [12] SCKOUTINV: 0x" + String(I2S->CLKCTRL[0].bit.SCKOUTINV, HEX) + "\t, 0b" + String(I2S->CLKCTRL[0].bit.SCKOUTINV, BIN) + "\t Serial Clock Output Invert"); + Serial.println ("\t [11] SCKSEL: 0x" + String(I2S->CLKCTRL[0].bit.SCKSEL, HEX) + "\t, 0b" + String(I2S->CLKCTRL[0].bit.SCKSEL, BIN) + "\t Serial Clock Select"); + Serial.println ("\t [10] FSOUTINV: 0x" + String(I2S->CLKCTRL[0].bit.FSOUTINV, HEX) + "\t, 0b" + String(I2S->CLKCTRL[0].bit.FSOUTINV, BIN) + "\t Frame Sync Output Invert"); + Serial.println ("\t [9] FSINV: 0x" + String(I2S->CLKCTRL[0].bit.FSINV, HEX) + "\t, 0b" + String(I2S->CLKCTRL[0].bit.FSINV, BIN) + "\t Frame Sync Invert"); + Serial.println ("\t [8] FSSEL: 0x" + String(I2S->CLKCTRL[0].bit.FSSEL, HEX) + "\t, 0b" + String(I2S->CLKCTRL[0].bit.FSSEL, BIN) + "\t Frame Sync Select"); + Serial.println ("\t [7] BITDELAY: 0x" + String(I2S->CLKCTRL[0].bit.BITDELAY, HEX) + "\t, 0b" + String(I2S->CLKCTRL[0].bit.BITDELAY, BIN) + "\t Data Delay from Frame Sync"); + Serial.println ("\t [6:5] FSWIDTH: 0x" + String(I2S->CLKCTRL[0].bit.FSWIDTH, HEX) + "\t, 0b" + String(I2S->CLKCTRL[0].bit.FSWIDTH, BIN) + "\t Frame Sync Width"); + Serial.println ("\t [4:2] NBSLOTS: 0x" + String(I2S->CLKCTRL[0].bit.NBSLOTS, HEX) + "\t, 0b" + String(I2S->CLKCTRL[0].bit.NBSLOTS, BIN) + "\t Number of Slots in Frame"); + Serial.println ("\t [1:0] SLOTSIZE: 0x" + String(I2S->CLKCTRL[0].bit.SLOTSIZE, HEX) + "\t, 0b" + String(I2S->CLKCTRL[0].bit.SLOTSIZE, BIN) + "\t Slot Size"); + Serial.println (); + + // I2S_CLKCTRL1 + Serial.println ("SAMD51_I2S Inter-IC Sound Interface (Clock Unit 1 Control)"); + Serial.println ("(32-bit) I2S->CLKCTRL[1]: 0x" + String(I2S->CLKCTRL[1].reg, HEX) + "\t, 0b" + String(I2S->CLKCTRL[1].reg, BIN)); + Serial.println ("\t [31:30] Reserved"); + Serial.println ("\t [29:24] MCKOUTDIV: 0x" + String(I2S->CLKCTRL[1].bit.MCKOUTDIV, HEX) + "\t, 0b" + String(I2S->CLKCTRL[1].bit.MCKOUTDIV, BIN) + "\t Master Clock Output Division Factor"); + Serial.println ("\t [23:22] Reserved"); + Serial.println ("\t [21:16] MCKDIV: 0x" + String(I2S->CLKCTRL[1].bit.MCKDIV, HEX) + "\t, 0b" + String(I2S->CLKCTRL[1].bit.MCKDIV, BIN) + "\t Master Clock Division Factor"); + Serial.println ("\t [15] MCKOUTINV: 0x" + String(I2S->CLKCTRL[1].bit.MCKOUTINV, HEX) + "\t, 0b" + String(I2S->CLKCTRL[1].bit.MCKOUTINV, BIN) + "\t Master Clock Output Invert"); + Serial.println ("\t [14] MCKEN: 0x" + String(I2S->CLKCTRL[1].bit.MCKEN, HEX) + "\t, 0b" + String(I2S->CLKCTRL[1].bit.MCKEN, BIN) + "\t Master Clock Enable"); + Serial.println ("\t [13] MCKSEL: 0x" + String(I2S->CLKCTRL[1].bit.MCKSEL, HEX) + "\t, 0b" + String(I2S->CLKCTRL[1].bit.MCKSEL, BIN) + "\t Master Clock Select "); + Serial.println ("\t [12] SCKOUTINV: 0x" + String(I2S->CLKCTRL[1].bit.SCKOUTINV, HEX) + "\t, 0b" + String(I2S->CLKCTRL[1].bit.SCKOUTINV, BIN) + "\t Serial Clock Output Invert"); + Serial.println ("\t [11] SCKSEL: 0x" + String(I2S->CLKCTRL[1].bit.SCKSEL, HEX) + "\t, 0b" + String(I2S->CLKCTRL[1].bit.SCKSEL, BIN) + "\t Serial Clock Select"); + Serial.println ("\t [10] FSOUTINV: 0x" + String(I2S->CLKCTRL[1].bit.FSOUTINV, HEX) + "\t, 0b" + String(I2S->CLKCTRL[1].bit.FSOUTINV, BIN) + "\t Frame Sync Output Invert"); + Serial.println ("\t [9] FSINV: 0x" + String(I2S->CLKCTRL[1].bit.FSINV, HEX) + "\t, 0b" + String(I2S->CLKCTRL[1].bit.FSINV, BIN) + "\t Frame Sync Invert"); + Serial.println ("\t [8] FSSEL: 0x" + String(I2S->CLKCTRL[1].bit.FSSEL, HEX) + "\t, 0b" + String(I2S->CLKCTRL[1].bit.FSSEL, BIN) + "\t Frame Sync Select"); + Serial.println ("\t [7] BITDELAY: 0x" + String(I2S->CLKCTRL[1].bit.BITDELAY, HEX) + "\t, 0b" + String(I2S->CLKCTRL[1].bit.BITDELAY, BIN) + "\t Data Delay from Frame Sync"); + Serial.println ("\t [6:5] FSWIDTH: 0x" + String(I2S->CLKCTRL[1].bit.FSWIDTH, HEX) + "\t, 0b" + String(I2S->CLKCTRL[1].bit.FSWIDTH, BIN) + "\t Frame Sync Width"); + Serial.println ("\t [4:2] NBSLOTS: 0x" + String(I2S->CLKCTRL[1].bit.NBSLOTS, HEX) + "\t, 0b" + String(I2S->CLKCTRL[1].bit.NBSLOTS, BIN) + "\t Number of Slots in Frame"); + Serial.println ("\t [1:0] SLOTSIZE: 0x" + String(I2S->CLKCTRL[1].bit.SLOTSIZE, HEX) + "\t, 0b" + String(I2S->CLKCTRL[1].bit.SLOTSIZE, BIN) + "\t Slot Size"); + Serial.println (); + + // I2S_INTENCLR + Serial.println ("SAMD51_I2S Inter-IC Sound Interface (Interrupt Enable Clear)"); + Serial.println ("(16-bit) I2S->INTENCLR: 0x" + String(I2S->INTENCLR.reg, HEX) + "\t, 0b" + String(I2S->INTENCLR.reg, BIN)); + Serial.println ("\t [15:14] Reserved"); + Serial.println ("\t [13] TXUR1: 0x" + String(I2S->INTENCLR.bit.TXUR1, HEX) + "\t, 0b" + String(I2S->INTENCLR.bit.TXUR1, BIN) + "\t Transmit Underrun 1 Interrupt"); + Serial.println ("\t [12] TXUR0: 0x" + String(I2S->INTENCLR.bit.TXUR0, HEX) + "\t, 0b" + String(I2S->INTENCLR.bit.TXUR0, BIN) + "\t Transmit Underrun 0 Interrupt"); + Serial.println ("\t [11:10] Reserved"); + Serial.println ("\t [9] TXRDY1: 0x" + String(I2S->INTENCLR.bit.TXRDY1, HEX) + "\t, 0b" + String(I2S->INTENCLR.bit.TXRDY1, BIN) + "\t Transmit Ready 1 Interrupt Enable"); + Serial.println ("\t [8] TXRDY0: 0x" + String(I2S->INTENCLR.bit.TXRDY0, HEX) + "\t, 0b" + String(I2S->INTENCLR.bit.TXRDY0, BIN) + "\t Transmit Ready 0 Interrupt Enable"); + Serial.println ("\t [7:6] Reserved"); + Serial.println ("\t [5] RXOR1: 0x" + String(I2S->INTENCLR.bit.RXOR1, HEX) + "\t, 0b" + String(I2S->INTENCLR.bit.RXOR1, BIN) + "\t Receive Overrun 1 Interrupt Enable"); + Serial.println ("\t [4] RXOR0: 0x" + String(I2S->INTENCLR.bit.RXOR0, HEX) + "\t, 0b" + String(I2S->INTENCLR.bit.RXOR0, BIN) + "\t Receive Overrun 0 Interrupt Enable"); + Serial.println ("\t [3:2] Reserved"); + Serial.println ("\t [1] RXRDY1: 0x" + String(I2S->INTENCLR.bit.RXRDY1, HEX) + "\t, 0b" + String(I2S->INTENCLR.bit.RXRDY1, BIN) + "\t Receive Ready 1 Interrupt Enable"); + Serial.println ("\t [0] RXRDY0: 0x" + String(I2S->INTENCLR.bit.RXRDY0, HEX) + "\t, 0b" + String(I2S->INTENCLR.bit.RXRDY0, BIN) + "\t Receive Ready 0 Interrupt Enable"); + Serial.println (); + + // I2S_INTENSET + Serial.println ("SAMD51_I2S Inter-IC Sound Interface (Interrupt Enable Set)"); + Serial.println ("(16-bit) I2S->INTENSET: 0x" + String(I2S->INTENSET.reg, HEX) + "\t, 0b" + String(I2S->INTENSET.reg, BIN)); + Serial.println ("\t [15:14] Reserved"); + Serial.println ("\t [13] TXUR1: 0x" + String(I2S->INTENSET.bit.TXUR1, HEX) + "\t, 0b" + String(I2S->INTENSET.bit.TXUR1, BIN) + "\t Transmit Underrun 1 Interrupt Enable"); + Serial.println ("\t [12] TXUR0: 0x" + String(I2S->INTENSET.bit.TXUR0, HEX) + "\t, 0b" + String(I2S->INTENSET.bit.TXUR0, BIN) + "\t Transmit Underrun 0 Interrupt Enable"); + Serial.println ("\t [11:10] Reserved"); + Serial.println ("\t [9] TXRDY1: 0x" + String(I2S->INTENSET.bit.TXRDY1, HEX) + "\t, 0b" + String(I2S->INTENSET.bit.TXRDY1, BIN) + "\t Transmit Ready 1 Interrupt Enable"); + Serial.println ("\t [8] TXRDY0: 0x" + String(I2S->INTENSET.bit.TXRDY0, HEX) + "\t, 0b" + String(I2S->INTENSET.bit.TXRDY0, BIN) + "\t Transmit Ready 0 Interrupt Enable"); + Serial.println ("\t [7:6] Reserved"); + Serial.println ("\t [5] RXOR1: 0x" + String(I2S->INTENSET.bit.RXOR1, HEX) + "\t, 0b" + String(I2S->INTENSET.bit.RXOR1, BIN) + "\t Receive Overrun 1 Interrupt Enable"); + Serial.println ("\t [4] RXOR0: 0x" + String(I2S->INTENSET.bit.RXOR0, HEX) + "\t, 0b" + String(I2S->INTENSET.bit.RXOR0, BIN) + "\t Receive Overrun 0 Interrupt Enable"); + Serial.println ("\t [3:2] Reserved"); + Serial.println ("\t [1] RXRDY1: 0x" + String(I2S->INTENSET.bit.RXRDY1, HEX) + "\t, 0b" + String(I2S->INTENSET.bit.RXRDY1, BIN) + "\t Receive Ready 1 Interrupt Enable"); + Serial.println ("\t [0] RXRDY0: 0x" + String(I2S->INTENSET.bit.RXRDY0, HEX) + "\t, 0b" + String(I2S->INTENSET.bit.RXRDY0, BIN) + "\t Receive Ready 0 Interrupt Enable"); + Serial.println (); + + // I2S_INTFLAG + Serial.println ("SAMD51_I2S Inter-IC Sound Interface (Interrupt Flag Status and Clear)"); + Serial.println ("(16-bit) I2S->INTFLAG: 0x" + String(I2S->INTFLAG.reg, HEX) + "\t, 0b" + String(I2S->INTFLAG.reg, BIN)); + Serial.println ("\t [15:14] Reserved"); + Serial.println ("\t [13] TXUR1: 0x" + String(I2S->INTFLAG.bit.TXUR1, HEX) + "\t, 0b" + String(I2S->INTFLAG.bit.TXUR1, BIN) + "\t Transmit Underrun 1"); + Serial.println ("\t [12] TXUR0: 0x" + String(I2S->INTFLAG.bit.TXUR0, HEX) + "\t, 0b" + String(I2S->INTFLAG.bit.TXUR0, BIN) + "\t Transmit Underrun 0"); + Serial.println ("\t [11:10] Reserved"); + Serial.println ("\t [9] TXRDY1: 0x" + String(I2S->INTFLAG.bit.TXRDY1, HEX) + "\t, 0b" + String(I2S->INTFLAG.bit.TXRDY1, BIN) + "\t Transmit Ready 1"); + Serial.println ("\t [8] TXRDY0: 0x" + String(I2S->INTFLAG.bit.TXRDY0, HEX) + "\t, 0b" + String(I2S->INTFLAG.bit.TXRDY0, BIN) + "\t Transmit Ready 0"); + Serial.println ("\t [7:6] Reserved"); + Serial.println ("\t [5] RXOR1: 0x" + String(I2S->INTFLAG.bit.RXOR1, HEX) + "\t, 0b" + String(I2S->INTFLAG.bit.RXOR1, BIN) + "\t Receive Overrun 1"); + Serial.println ("\t [4] RXOR0: 0x" + String(I2S->INTFLAG.bit.RXOR0, HEX) + "\t, 0b" + String(I2S->INTFLAG.bit.RXOR0, BIN) + "\t Receive Overrun 0"); + Serial.println ("\t [3:2] Reserved"); + Serial.println ("\t [1] RXRDY1: 0x" + String(I2S->INTFLAG.bit.RXRDY1, HEX) + "\t, 0b" + String(I2S->INTFLAG.bit.RXRDY1, BIN) + "\t Receive Ready 1"); + Serial.println ("\t [0] RXRDY0: 0x" + String(I2S->INTFLAG.bit.RXRDY0, HEX) + "\t, 0b" + String(I2S->INTFLAG.bit.RXRDY0, BIN) + "\t Receive Ready 0"); + Serial.println (); + + // I2S_SYNCBUSY + Serial.println ("SAMD51_I2S Inter-IC Sound Interface (Synchronization Status)"); + Serial.println ("(16-bit) I2S->SYNCBUSY: 0x" + String(I2S->SYNCBUSY.reg, HEX) + "\t, 0b" + String(I2S->SYNCBUSY.reg, BIN)); + Serial.println ("\t [15:10] Reserved"); + Serial.println ("\t [9] RXDATA: 0x" + String(I2S->SYNCBUSY.bit.RXDATA, HEX) + "\t, 0b" + String(I2S->SYNCBUSY.bit.RXDATA, BIN) + "\t Rx Data Synchronization Status"); + Serial.println ("\t [8] TXDATA: 0x" + String(I2S->SYNCBUSY.bit.TXDATA, HEX) + "\t, 0b" + String(I2S->SYNCBUSY.bit.TXDATA, BIN) + "\t Tx Data Synchronization Status"); + Serial.println ("\t [7:6] Reserved"); + Serial.println ("\t [5] RXEN: 0x" + String(I2S->SYNCBUSY.bit.RXEN, HEX) + "\t, 0b" + String(I2S->SYNCBUSY.bit.RXEN, BIN) + "\t Rx Serializer Enable Synchronization Status"); + Serial.println ("\t [4] TXEN: 0x" + String(I2S->SYNCBUSY.bit.TXEN, HEX) + "\t, 0b" + String(I2S->SYNCBUSY.bit.TXEN, BIN) + "\t Tx Serializer Enable Synchronization Status"); + Serial.println ("\t [3] CKEN1: 0x" + String(I2S->SYNCBUSY.bit.CKEN1, HEX) + "\t, 0b" + String(I2S->SYNCBUSY.bit.CKEN1, BIN) + "\t Clock Unit 1 Enable Synchronization Status"); + Serial.println ("\t [2] CKEN0: 0x" + String(I2S->SYNCBUSY.bit.CKEN0, HEX) + "\t, 0b" + String(I2S->SYNCBUSY.bit.CKEN0, BIN) + "\t Clock Unit 0 Enable Synchronization Status"); + Serial.println ("\t [1] ENABLE: 0x" + String(I2S->SYNCBUSY.bit.ENABLE, HEX) + "\t, 0b" + String(I2S->SYNCBUSY.bit.ENABLE, BIN) + "\t Enable Synchronization Status"); + Serial.println ("\t [0] SWRST: 0x" + String(I2S->SYNCBUSY.bit.SWRST, HEX) + "\t, 0b" + String(I2S->SYNCBUSY.bit.SWRST, BIN) + "\t Software Reset Synchronization Status"); + Serial.println (); + + // I2S_TXCTRL + Serial.println ("SAMD51_I2S Inter-IC Sound Interface (Tx Serializer Control)"); + Serial.println ("(32-bit) I2S->TXCTRL: 0x" + String(I2S->TXCTRL.reg, HEX) + "\t, 0b" + String(I2S->TXCTRL.reg, BIN)); + Serial.println ("\t [31:26] Reserved"); + Serial.println ("\t [25] DMA: 0x" + String(I2S->TXCTRL.bit.DMA, HEX) + "\t, 0b" + String(I2S->TXCTRL.bit.DMA, BIN) + "\t Single or Multiple DMA Channels"); + Serial.println ("\t [24] MONO: 0x" + String(I2S->TXCTRL.bit.MONO, HEX) + "\t, 0b" + String(I2S->TXCTRL.bit.MONO, BIN) + "\t Mono Mode"); + Serial.println ("\t [23:16] SLOTDIS: 0x" + String(I2S->TXCTRL.vec.SLOTDIS, HEX) + "\t, 0b" + String(I2S->TXCTRL.vec.SLOTDIS, BIN) + "\t Slot 0:7 Disabled for this Serializer"); + Serial.println ("\t [15] BITREV: 0x" + String(I2S->TXCTRL.bit.BITREV, HEX) + "\t, 0b" + String(I2S->TXCTRL.bit.BITREV, BIN) + "\t Data Formatting Bit Reverse"); + Serial.println ("\t [14:13] EXTEND: 0x" + String(I2S->TXCTRL.bit.EXTEND, HEX) + "\t, 0b" + String(I2S->TXCTRL.bit.EXTEND, BIN) + "\t Data Formatting Bit Extension"); + Serial.println ("\t [12] WORDADJ: 0x" + String(I2S->TXCTRL.bit.WORDADJ, HEX) + "\t, 0b" + String(I2S->TXCTRL.bit.WORDADJ, BIN) + "\t Data Word Formatting Adjust"); + Serial.println ("\t [11] Reserved"); + Serial.println ("\t [10:8] DATASIZE: 0x" + String(I2S->TXCTRL.bit.DATASIZE, HEX) + "\t, 0b" + String(I2S->TXCTRL.bit.DATASIZE, BIN) + "\t Data Word Size"); + Serial.println ("\t [7] SLOTADJ: 0x" + String(I2S->TXCTRL.bit.SLOTADJ, HEX) + "\t, 0b" + String(I2S->TXCTRL.bit.SLOTADJ, BIN) + "\t Data Slot Formatting Adjust"); + Serial.println ("\t [6] Reserved"); + Serial.println ("\t [5] Reserved (Datasheet errata says: CLKSEL)"); + Serial.println ("\t [4] TXSAME: 0x" + String(I2S->TXCTRL.bit.TXSAME, HEX) + "\t, 0b" + String(I2S->TXCTRL.bit.TXSAME, BIN) + "\t Transmit Data when Underrun"); + Serial.println ("\t [3:2] TXDEFAULT: 0x" + String(I2S->TXCTRL.bit.TXDEFAULT, HEX) + "\t, 0b" + String(I2S->TXCTRL.bit.TXDEFAULT, BIN) + "\t Line Default Line when Slot Disabled"); + Serial.println ("\t [1:0] Reserved (Datasheet errata says: SERMODE)"); + Serial.println (); + + // I2S_RXCTRL + Serial.println ("SAMD51_I2S Inter-IC Sound Interface (Rx Serializer Control)"); + Serial.println ("(32-bit) I2S->RXCTRL: 0x" + String(I2S->RXCTRL.reg, HEX) + "\t, 0b" + String(I2S->RXCTRL.reg, BIN)); + Serial.println ("\t [31:27] Reserved"); + Serial.println ("\t [25] RXLOOP: 0x" + String(I2S->RXCTRL.bit.RXLOOP, HEX) + "\t, 0b" + String(I2S->RXCTRL.bit.RXLOOP, BIN) + "\t Loop-back Test Mode"); + Serial.println ("\t [25] DMA: 0x" + String(I2S->RXCTRL.bit.DMA, HEX) + "\t, 0b" + String(I2S->RXCTRL.bit.DMA, BIN) + "\t Single or Multiple DMA Channels"); + Serial.println ("\t [24] MONO: 0x" + String(I2S->RXCTRL.bit.MONO, HEX) + "\t, 0b" + String(I2S->RXCTRL.bit.MONO, BIN) + "\t Mono Mode"); + Serial.println ("\t [23:16] SLOTDIS: 0x" + String(I2S->RXCTRL.vec.SLOTDIS, HEX) + "\t, 0b" + String(I2S->RXCTRL.vec.SLOTDIS, BIN) + "\t Slot 0:7 Disabled for this Serializer"); + Serial.println ("\t [15] BITREV: 0x" + String(I2S->RXCTRL.bit.BITREV, HEX) + "\t, 0b" + String(I2S->RXCTRL.bit.BITREV, BIN) + "\t Data Formatting Bit Reverse"); + Serial.println ("\t [14:13] EXTEND: 0x" + String(I2S->RXCTRL.bit.EXTEND, HEX) + "\t, 0b" + String(I2S->RXCTRL.bit.EXTEND, BIN) + "\t Data Formatting Bit Extension"); + Serial.println ("\t [12] WORDADJ: 0x" + String(I2S->RXCTRL.bit.WORDADJ, HEX) + "\t, 0b" + String(I2S->RXCTRL.bit.WORDADJ, BIN) + "\t Data Word Formatting Adjust"); + Serial.println ("\t [11] Reserved"); + Serial.println ("\t [10:8] DATASIZE: 0x" + String(I2S->RXCTRL.bit.DATASIZE, HEX) + "\t, 0b" + String(I2S->RXCTRL.bit.DATASIZE, BIN) + "\t Data Word Size"); + Serial.println ("\t [7] SLOTADJ: 0x" + String(I2S->RXCTRL.bit.SLOTADJ, HEX) + "\t, 0b" + String(I2S->RXCTRL.bit.SLOTADJ, BIN) + "\t Data Slot Formatting Adjust"); + Serial.println ("\t [6] Reserved"); + Serial.println ("\t [5] CLKSEL: 0x" + String(I2S->RXCTRL.bit.CLKSEL, HEX) + "\t, 0b" + String(I2S->RXCTRL.bit.CLKSEL, BIN) + "\t Clock Unit Selection"); + Serial.println ("\t [4:2] Reserved"); + Serial.println ("\t [1:0] SERMODE: 0x" + String(I2S->RXCTRL.bit.SERMODE, HEX) + "\t, 0b" + String(I2S->RXCTRL.bit.SERMODE, BIN) + "\t Serializer Mode"); + Serial.println (); + + // I2S_TXDATA + Serial.println ("SAMD51_I2S Inter-IC Sound Interface (Tx Data)"); + Serial.println ("(32-bit) I2S->TXDATA: 0x" + String(I2S->TXDATA.reg, HEX) + "\t, 0b" + String(I2S->TXDATA.reg, BIN) + "\t "); + Serial.println (); + + // I2S_RXDATA + Serial.println ("SAMD51_I2S Inter-IC Sound Interface (Rx Data)"); + Serial.println ("(32-bit) I2S->RXDATA: 0x" + String(I2S->RXDATA.reg, HEX) + "\t, 0b" + String(I2S->RXDATA.reg, BIN) + "\t "); + Serial.println (); + } + private: volatile I2s &i2s; };