Hello everyone,
I want to read out some information from a MEMS Microphone with I2S. I Bought myself a Teensy 3.2 and a MEMS microphone from adafruit (https://learn.adafruit.com/adafruit-i2s-mems-microphone-breakout/). I connected the Pins (3V to +3.3V, GND to GND, BCLK to Pin 9, Dout to Pin 13, LRCL to Pin 23 and SEL ist connected to nothing) and with the Audio Library everything works. The Point is that i want to have less Code as possible and my first goal was to write out every single Sample which has been recorded (so i can see everything in the Arduino IDE Plotter). The full code is under this post.
To do that i looked into the Audio Library and extracted some parts of it. I do not fully understand what every line makes and I don't know where i can research that. Do you have any tips where i could find help about this theme (mainly it is the function config_i2s).
After i copied some parts of the code the program was executing on the Teensy but every second sample was a zero. I think this was because I only used one channel (?) (only used one micro so i got mono sound) and therefore the second channel was left empty. Is there a possibility that I2S is only sending the information of the mono channel or would it be easier just to increase the Pointer by 2 everytime he readout a sample?
I want to read out some information from a MEMS Microphone with I2S. I Bought myself a Teensy 3.2 and a MEMS microphone from adafruit (https://learn.adafruit.com/adafruit-i2s-mems-microphone-breakout/). I connected the Pins (3V to +3.3V, GND to GND, BCLK to Pin 9, Dout to Pin 13, LRCL to Pin 23 and SEL ist connected to nothing) and with the Audio Library everything works. The Point is that i want to have less Code as possible and my first goal was to write out every single Sample which has been recorded (so i can see everything in the Arduino IDE Plotter). The full code is under this post.
To do that i looked into the Audio Library and extracted some parts of it. I do not fully understand what every line makes and I don't know where i can research that. Do you have any tips where i could find help about this theme (mainly it is the function config_i2s).
After i copied some parts of the code the program was executing on the Teensy but every second sample was a zero. I think this was because I only used one channel (?) (only used one micro so i got mono sound) and therefore the second channel was left empty. Is there a possibility that I2S is only sending the information of the mono channel or would it be easier just to increase the Pointer by 2 everytime he readout a sample?
Code:
#include <DMAChannel.h>
#ifndef AUDIO_BLOCK_SAMPLES
#if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)
#define AUDIO_BLOCK_SAMPLES 128
#elif defined(__MKL26Z64__)
#define AUDIO_BLOCK_SAMPLES 64
#endif
#endif
typedef struct audio_block_struct {
uint8_t ref_count;
uint8_t reserved1;
uint16_t memory_pool_index;
int16_t data[AUDIO_BLOCK_SAMPLES];
} audio_block_t; // I am going to short this out in the future versions because i only need the audiodata and therefore a struct is not necessary
static DMAChannel dma;
DMAMEM static uint32_t i2s_rx_buffer[AUDIO_BLOCK_SAMPLES];
audio_block_t audiodata;
static audio_block_t *mainblock = &audiodata;
static uint16_t block_offset;
#if F_CPU == 96000000 || F_CPU == 48000000 || F_CPU == 24000000
// PLL is at 96 MHz in these modes
#define MCLK_MULT 2
#define MCLK_DIV 17
#elif F_CPU == 72000000
#define MCLK_MULT 8
#define MCLK_DIV 51
#elif F_CPU == 120000000
#define MCLK_MULT 8
#define MCLK_DIV 85
#elif F_CPU == 144000000
#define MCLK_MULT 4
#define MCLK_DIV 51
#elif F_CPU == 168000000
#define MCLK_MULT 8
#define MCLK_DIV 119
#elif F_CPU == 180000000
#define MCLK_MULT 16
#define MCLK_DIV 255
#define MCLK_SRC 0
#elif F_CPU == 192000000
#define MCLK_MULT 1
#define MCLK_DIV 17
#elif F_CPU == 216000000
#define MCLK_MULT 8
#define MCLK_DIV 153
#define MCLK_SRC 0
#elif F_CPU == 240000000
#define MCLK_MULT 4
#define MCLK_DIV 85
#elif F_CPU == 16000000
#define MCLK_MULT 12
#define MCLK_DIV 17
#else
#error "This CPU Clock Speed is not supported by the Audio library";
#endif
#ifndef MCLK_SRC
#if F_CPU >= 20000000
#define MCLK_SRC 3 // the PLL
#else
#define MCLK_SRC 0 // system clock
#endif
#endif
void config_i2s(void) // what exactly is happening here? Is there any documentation about this?/ what do I have to change to have a mono channel?
{
SIM_SCGC6 |= SIM_SCGC6_I2S; // System Clock Gating Control Register 6 -> I2S Clock Gate Control
SIM_SCGC7 |= SIM_SCGC7_DMA; // System Clock Gating Control Register 7 -> DMA Clock Gate Control
SIM_SCGC6 |= SIM_SCGC6_DMAMUX; // System Clock Gating Control Register 6 -> DMA Mux Clock Gate Control
// if either transmitter or receiver is enabled, do nothing
if (I2S0_TCSR & I2S_TCSR_TE) return; // SAI Transmit Control Register ; Transmitter Enable
if (I2S0_RCSR & I2S_RCSR_RE) return;// SAI Receive Control Register ; Receiver Enable
// enable MCLK output
I2S0_MCR = I2S_MCR_MICS(MCLK_SRC) | I2S_MCR_MOE; // SAI (Serial audio Interface) MCLK Control Register -> MCLK Input Clock Select | MCLK Output Enable
while (I2S0_MCR & I2S_MCR_DUF); // SAI MCLK Control Register & Divider Update Flag
I2S0_MDR = I2S_MDR_FRACT((MCLK_MULT - 1)) | I2S_MDR_DIVIDE((MCLK_DIV - 1)); // SAI MCLK Divide Register = MCLK Fraction | MCLK Divide
// configure transmitter
I2S0_TMR = 0; // SAI Transmit Mask Register
I2S0_TCR1 = I2S_TCR1_TFW(1); // SAI Transmit Configuration 1 Register = Transmit FIFO watermark (watermark at half fifo size)
I2S0_TCR2 = I2S_TCR2_SYNC(0) | I2S_TCR2_BCP | I2S_TCR2_MSEL(1) // SAI Transmit Configuration 2 Register = async | Bit clock polarity | MCLK select, 0=bus clock, 1=I2S0_MCLK
| I2S_TCR2_BCD | I2S_TCR2_DIV(1); // Bit clock direction | Bit clock divide by (DIV+1)*2
I2S0_TCR3 = I2S_TCR3_TCE; // SAI Transmit Configuration 3 Register = transmit channel enable
I2S0_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(31) | I2S_TCR4_MF // SAI Transmit Configuration 4 Register = Frame Size | Sync Width | MSB First
| I2S_TCR4_FSE | I2S_TCR4_FSP | I2S_TCR4_FSD; //Frame Sync Early | Frame Sync Polarity | Frame Sync Direction
I2S0_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31); // SAI Transmit Configuration 5 Register = Word N Width | Word 0 Width | First Bit Shifted
// configure receiver (sync'd to transmitter clocks)
I2S0_RMR = 0; // SAI Receive Mask Register
I2S0_RCR1 = I2S_RCR1_RFW(1); // SAI Receive Configuration 1 Register = Receive FIFO watermark
I2S0_RCR2 = I2S_RCR2_SYNC(1) | I2S_TCR2_BCP | I2S_RCR2_MSEL(1) // SAI Receive Configuration 2 Register | async with receiver | Bit clock polarity|MCLK select, 0=bus clock, 1=I2S0_MCLK
| I2S_RCR2_BCD | I2S_RCR2_DIV(1); // Bit clock direction | Bit clock divide by (DIV+1)*2
I2S0_RCR3 = I2S_RCR3_RCE; // SAI Receive Configuration 3 Register | receive channel enable
I2S0_RCR4 = I2S_RCR4_FRSZ(1) | I2S_RCR4_SYWD(31) | I2S_RCR4_MF // SAI Receive Configuration 4 Register | Frame Size | Sync Width | MSB First
| I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD; // Frame Sync Early | Frame Sync Polarity | Frame Sync Direction
I2S0_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31); // SAI Receive Configuration 5 Register | Word N Width | Word 0 Width |
// configure pin mux for 3 clock signals
CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK)
CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK
CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK
}
void isr() // inherited from the input_i2s.cpp
{
uint32_t daddr, offset;
const int16_t *src, *end;
int16_t *dest;
audio_block_t *block;
//digitalWriteFast(3, HIGH);
#if defined(KINETISK)
daddr = (uint32_t)(dma.TCD->DADDR);
#endif
dma.clearInterrupt(); // Set Clear Interrupt Request Register
if (daddr < (uint32_t)i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2)
{
// DMA is receiving to the first half of the buffer
// need to remove data from the second half
src = (int16_t *)&i2s_rx_buffer[AUDIO_BLOCK_SAMPLES / 2];
end = (int16_t *)&i2s_rx_buffer[AUDIO_BLOCK_SAMPLES];
}
else
{
// DMA is receiving to the second half of the buffer
// need to remove data from the first half
src = (int16_t *)&i2s_rx_buffer[0];
end = (int16_t *)&i2s_rx_buffer[AUDIO_BLOCK_SAMPLES / 2];
}
block = mainblock;
if (block != NULL)
{
offset = block_offset;
if (offset <= AUDIO_BLOCK_SAMPLES / 2)
{
dest = &(block->data[offset]);
do
{
Serial.println(*src);
*dest++ = *src++; // Or better Change here to *dest++ = *src
// src += 2
} while (src < end);
}
}
}
void setup()
{
Serial.begin(9600);
Serial.print("Number of Samples:");
Serial.println(AUDIO_BLOCK_SAMPLES);
dma.begin(true); // Allocate the DMA channel first
config_i2s();
CORE_PIN13_CONFIG = PORT_PCR_MUX(4);
#if defined(KINETISK)
dma.TCD->SADDR = (void *)((uint32_t)&I2S0_RDR0 + 2); // What is happening here?
dma.TCD->SOFF = 0;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
dma.TCD->NBYTES_MLNO = 2;
dma.TCD->SLAST = 0;
dma.TCD->DADDR = i2s_rx_buffer;
dma.TCD->DOFF = 2;
dma.TCD->CITER_ELINKNO = sizeof(i2s_rx_buffer) / 2;
dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer);
dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
#endif
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_RX);
dma.enable();
I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; //SAI Receive Control Register
I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX
dma.attachInterrupt(isr);
}
void loop()
{
// put your main code here, to run repeatedly:
}