I have a very strange situation happening. I am trying to build a simple PCB with an onboard I2S Microphone using the K22F. I use DMA.
I have built two PCBs that are identical. The components on the PCBs are working and can do printf, switch LED, etc... However, only one of them can obtain data from the I2S microphone!!! You would say the microphone isn't working in the PCB that doesn't work, but I know for a fact the microphone is not the issue. This is confirmed by an oscilloscope and another successful program that doesn't use DMA and can read data from the microphone and output it.
I have done some tests and it seems that in the PCB that is not working the program only enters the DMA0_IRQHandler exactly 1 time, in the PCB that works it access it every time new data is available. My program essentially sets up I2S, sets up DMA, and then uses the ARM DSP library to do some FFTs, nothing more. The I2S set-up seems to be working since I use that in the successful non-DMA program in the faulty unit. Here is a pic to prove it, the red PCB works, the Green and White doesn't:
/media/uploads/edumana/wp_20150408_001.jpg
How can this be possible? Identical hardware, identical software, yet not the same behavior. Is it possible that when soldering something heated up too much and failed? Hard to believe since the faulty PCB seems to be able to do about everything but obtain data from the microphone.
(Edit: The SGE and SBE error flags are present in the DMA_ES register, maybe this is the issue?)
Here is all of my code:
#include "mbed.h"
#include <stdio.h>
#include <ctype.h>
#include <arm_math.h>
Serial pc(USBTX, USBRX);
DigitalOut led(PTD4);
DigitalOut CHIPEN(PTC11);
DigitalOut LR(PTC10);
int times = 0;
const int SAMPLE_RATE_HZ = 9000;
const int FFT_SIZE = 2048;
int32_t BUFFER_A[FFT_SIZE];
int32_t BUFFER_B[FFT_SIZE];
int32_t *DMA_BUFF;
int32_t *READ_BUFF;
float samples[FFT_SIZE*2];
float magnitudes[FFT_SIZE*2];
bool SAMPLING_DONE = 0;
bool USING_BUFFER_A = 1;
void pr_16(volatile uint16_t *reg);
void pr(volatile uint32_t *reg);
void START_I2S();
void START_DMA();
void RX_CALL();
void checkRequest();
void sendData(int dataType);
extern "C" void DMA0_IRQHandler();
int main(){
pc.baud(230400);
START_I2S();
START_DMA();
led = 1;
while(1){
pc.printf("%i\r\n",times);
if(SAMPLING_DONE){
times++;
unsigned short j = FFT_SIZE;
while(j--){
samples[j] = (float) READ_BUFF[j]/100000; //100000
}
arm_rfft_fast_instance_f32 fft_inst;
arm_rfft_fast_init_f32(&fft_inst, FFT_SIZE);
arm_rfft_fast_f32(&fft_inst, samples, magnitudes, 0);
arm_cmplx_mag_squared_f32(magnitudes, magnitudes, FFT_SIZE);
SAMPLING_DONE = 0;
//checkRequest();
}
}
}
//I2S Init
void START_I2S(){
CHIPEN = 1;
LR = 0;
//Pins Init
SIM_SCGC5 |= 0x42B82; //Enables PORTC
PORTC_PCR5 |= 0x400; //PTC5 --> I2S0_RXD0 -> SD
PORTC_PCR6 |= 0x400; //PTC6 --> I2S0_RC_BCLK -> SCK
PORTC_PCR7 |= 0x400; //PTC7 --> I2S0_RX_FS -> WS
//Clock Init
SIM_SCGC6 |= 0x8000; //Turn on I2S Clock Gate
SIM_SOPT2 |= 0x30000; //I2S Clocking Sourced from 48MHz IRC48MCLK
I2S0_MCR |= 0x43000000; //Output Enable. Mux IRC48MCLK into Fractional Divider
I2S0_RCR2 |= 0x700000D; //MCLK MSEL to BCLK. Div -> 13, 9k Sample
I2S0_MDR |= 0x1F07C; //Set I2S Master Clock to 12.2880MHz
SIM_SCGC6 |= 0x8000; //Re-Enable I2S Module
//Receive Init
I2S0_RCSR = 0x0; //Disable Receive as per Datasheet
I2S0_RCR1 |= 0x1; //Sets FIFO Watermark to 1
I2S0_RCR3 |= 0x10000; //Enable Receive Data Channel
I2S0_RCR4 |= 0x10011F1B; //Internal FS. Early FS. 32 SYWD. FRSZ 2. FCONT
I2S0_RCR5 |= 0x1F1F1F00; //32 Bits per Word. 32 Bits in First Word. Shifted 32.
I2S0_RMR |= 0x2; //Mask 2nd Word (One Channel Only)
I2S0_RCSR |= 0x80000001; //Enable & Request DMA
}
//DMA Init
void START_DMA(){
SIM_SCGC6 |= 0x2; //DMAMUX Module Turn On
SIM_SCGC7 |= 0x2; //DMA Module Turn On
DMAMUX_CHCFG0 = 0x8C; //I2S Source: Slot 12 & Activate
DMA_TCD0_CSR &= 0xBF; //Inactive
NVIC_EnableIRQ(DMA0_IRQn); //Enable IRQ DMA Channel 0
DMA_TCD0_CSR |= 0x2; //Interrupt Major Iteration
DMA_CR = 0x0; //Disable
DMA_TCD0_SADDR = (uint32_t) &I2S0_RDR0; //Data Source
DMA_TCD0_DADDR = (uint32_t) BUFFER_A; //Destination
DMA_TCD0_SOFF = 0; //No Source Offset
DMA_TCD0_SLAST = 0; //Nothing Added to Source Address after Major Loop
DMA_TCD0_DLASTSGA = -(FFT_SIZE*4); //Value Added to Destination Address after Major Loop
DMA_TCD0_DOFF = 4; //4 Byte Destination Offset
DMA_TCD0_NBYTES_MLNO = 4; //4 Bytes Transfered in Minor Loop
DMA_TCD0_BITER_ELINKNO = FFT_SIZE; //1024 Bins
DMA_TCD0_CITER_ELINKNO = FFT_SIZE; //1024 Bins
DMA_TCD0_ATTR = 0x202; //32-Bit Transfer Size
DMA_SERQ = 0x0; //Channel 0 Enable
DMA_TCD0_CSR |= 0x41; //Enable!
}
//RX Interrupt Callback
void RX_CALL(){
if(USING_BUFFER_A){
USING_BUFFER_A = 0;
DMA_BUFF = BUFFER_B;
READ_BUFF = BUFFER_A;
}
else{
USING_BUFFER_A = 1;
DMA_BUFF = BUFFER_A;
READ_BUFF = BUFFER_B;
}
DMA_TCD0_DADDR = (uint32_t) DMA_BUFF;
}
//DMA 0 Interrupt Handler
extern "C" void DMA0_IRQHandler(){
DMA_CINT = 0;
SAMPLING_DONE = 1;
RX_CALL();
return;
}
//Debugging tool, it prints register [address]--> [contents]
//Pass the "address of" a register, i.e: pr(&PORTA_PCR1)
void pr_16(volatile uint16_t *reg) {
pc.printf("%x --> %u \r\n", reg, *reg);
}
//32-Bit
void pr(volatile uint32_t *reg) {
pc.printf("%x --> %x \r\n", reg, *reg);
}
/* MATLAB Serial Com.
* 1. Sends Magnitudes
* 2. Sends Samples
* 3. Sends FFT Size
* 4. Sends Sample Rate
*/
void sendData(int dataType) {
if (dataType == 1) {
for (int i = 1; i < (FFT_SIZE/2) + 1; ++i) {
pc.printf("%f\n", magnitudes[i]);
}
}
else if (dataType == 2) {
for (int i = 0; i < FFT_SIZE; ++i) {
pc.printf("%f\n", samples[i]);
}
}
else if (dataType == 3) {
pc.printf("%d\n", FFT_SIZE);
}
else if (dataType == 4) {
pc.printf("%d\n", SAMPLE_RATE_HZ);
}
}
//Checks If MATLAB needs Data
void checkRequest(){
int number;
pc.scanf("%d", &number);
sendData(number);
}
I have a very strange situation happening. I am trying to build a simple PCB with an onboard I2S Microphone using the K22F. I use DMA.
I have built two PCBs that are identical. The components on the PCBs are working and can do printf, switch LED, etc... However, only one of them can obtain data from the I2S microphone!!! You would say the microphone isn't working in the PCB that doesn't work, but I know for a fact the microphone is not the issue. This is confirmed by an oscilloscope and another successful program that doesn't use DMA and can read data from the microphone and output it.
I have done some tests and it seems that in the PCB that is not working the program only enters the DMA0_IRQHandler exactly 1 time, in the PCB that works it access it every time new data is available. My program essentially sets up I2S, sets up DMA, and then uses the ARM DSP library to do some FFTs, nothing more. The I2S set-up seems to be working since I use that in the successful non-DMA program in the faulty unit. Here is a pic to prove it, the red PCB works, the Green and White doesn't:
/media/uploads/edumana/wp_20150408_001.jpg
How can this be possible? Identical hardware, identical software, yet not the same behavior. Is it possible that when soldering something heated up too much and failed? Hard to believe since the faulty PCB seems to be able to do about everything but obtain data from the microphone.
(Edit: The SGE and SBE error flags are present in the DMA_ES register, maybe this is the issue?)
Here is all of my code: