Very Strange Situation

09 Apr 2015

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);
}
09 Apr 2015

Got it! If it's not the hardware, and it's not the software, it must be some sort of initial state.

The DMA_TCD0_CSR register has different initial values on each PCB, making only one of them work. The solution was initializing the register to 0.

Now the question is, is this normal behavior?