I am working with SPH0645LM4H-B MEMS microphone with I2S output. I successfully set up the SPI on dsPIC33CK256MP503 to the point, that it outputs the I2S clock at a rate 2MHZ, and WS control signal. The microphone seems to return proper data:
My issue is with getting Interrupt for each received I2S word, so I can process the data. I suspect that my setting the SPI is not correct, but cannot figure it out.
Within my MPLAB X IDE v5.35, initially I used the MCC to generate the code, then I wrote some comments. Here is my SPI initialization:
/**
* To set up the SPIx module for Audio mode:
*
* 1. Clear the SPIxBUFL and SPIxBUFH registers.
*
* 2. If using interrupts:
* a) Clear the interrupt flag bits in the respective IFSx register.
* b) Set the interrupt enable bits in the respective IECx register.
* c) Write the SPIxIP bits in the respective IPCx register to set the interrupt priority.
*
* 3. Write the desired settings to the SPIxCON1L, SPIxCON1H and SPIxCON2L registers with AUDEN (SPIxCON1H[15]) = 1.
*
* 4. Clear the SPIROV bit (SPIxSTATL[6]).
*
* 5. Enable SPIx operation by setting the SPIEN bit (SPIxCON1L[15]).
*
* 6. Write the data to be transmitted to the SPIxBUFL and SPIxBUFH registers.
* Transmission (and reception) will start as soon as data are written to the SPIxBUFL and SPIxBUFH registers.
*/
void SPI1_Initialize (void)
{
/*
* AUDEN enabled;
* FRMEN disabled;
* AUDMOD I2S;
* FRMSYPW One clock wide;
* AUDMONO mono;
* FRMCNT 0;
* MSSEN disabled;
* FRMPOL disabled;
* IGNROV disabled;
* SPISGNEXT not sign-extended;
* FRMSYNC disabled;
* URDTEN disabled;
* IGNTUR disabled;
*/
SPI1CON1H = 0x8800;
// WLENGTH 0;
SPI1CON2L = 0x00;
// SPIROV disabled; FRMERR disabled;
SPI1STATL = 0x00;
// SPI1BRGL 0;
SPI1BRGL = 0x00;
/*
* SPITBFEN disabled;
* SPITUREN disabled;
* FRMERREN disabled;
* SRMTEN disabled;
* SPIRBEN disabled;
* BUSYEN disabled;
* SPITBEN disabled;
* SPIROVEN disabled;
* SPIRBFEN disabled;
*/
SPI1IMSKL = 0x00;
// RXMSK 0; TXWIEN disabled; TXMSK 0; RXWIEN disabled;
SPI1IMSKH = 0x00;
// SPI1URDTL 0;
SPI1URDTL = 0x00;
// SPI1URDTH 0;
SPI1URDTH = 0x00;
/*
* THIS MUST BE LAST, because the above initialization requires the SPIEN=0 for their access
*
* SPIEN enabled;
* DISSDO: SDOx pin is not used by the module; pin is controlled by the port function;
* MCLKEN FOSC/2;
* CKP Idle:High, Active:Low;
* SSEN disabled;
* MSTEN Master;
* MODE32 enabled; | these two together select I2S: 24-bit data, 32-bit FIFO, 32-bit channel/64-bit frame
* MODE16 enabled; |
* SMP Middle;
* DISSCK CONTROLLED BY THE MODULE;
* SPIFE Frame Sync pulse precedes;
* CKE Idle to Active;
* SPISIDL disabled;
* ENHBUF disabled;
* DISSDI: SDIx pin is controlled by the module;
*/
SPI1CON1L = 0x1C60;
}
I was hoping to get interrupt after each single sampe received, but it acts strangely. I do get into interrupt routine, but code doesn't get to the middle of the routine, or it gets there but only 5 times or so.
Here is my Interrupt priority table:
void INTERRUPT_Initialize (void)
{
// SPIRXI: SPI1 RX
// Priority: 4 ??
IPC2bits.SPI1RXIP = 4;
// UEVTI: UART1 Event
// Priority: 1
IPC47bits.U1EVTIP = 1;
// UTXI: UART1 TX
// Priority: 1
IPC3bits.U1TXIP = 1;
// UEI: UART1 Error
// Priority: 1
IPC12bits.U1EIP = 1;
// URXI: UART1 RX
// Priority: 1
IPC2bits.U1RXIP = 1;
// TI: Timer 1
// Priority: 1
IPC0bits.T1IP = 1;
}
And finally, here is my test interrupt routine:
void __attribute__ ( ( interrupt, no_auto_psv ) ) _SPI1RXInterrupt ( void )
{
IFS0bits.SPI1RXIF = 0;
while ( SPI1STATLbits.SPIRBE == true) // Automatically cleared in hardware when SPIx transfers data from SPIxRXSR to SPIxRXB.
{
}
/*
* Received data looks like 32 bit, but only top 18 bits are valid from microphone.
* By reading the H word only, we loose only the bottom 2 bit resolution, making the audio sample 16-bit wide
*/
*(audio.tail_ptr) = SPI1BUFL;
*(audio.tail_ptr+2) = SPI1BUFH;
//*(audio.tail_ptr) = 0x64;//SPI1BUFL;
audio.i++;
if (audio.i >= 40)
{
audio.i++;
}
// Will the increment not result in a wrap and not result in a pure collision?
// This is most often condition so check first
if ( ( audio.tail_ptr != (audio.buffer + AUDIO_BUFFER_SIZE-2)) &&
((audio.tail_ptr+2) != audio.head_ptr) )
{
audio.tail_ptr++;
}
else if ( (audio.tail_ptr == (audio.buffer + AUDIO_BUFFER_SIZE-2)) &&
(audio.head_ptr != audio.buffer) )
{
// Pure wrap no collision
audio.tail_ptr = audio.buffer;
}
else // must be collision
{
audio.overflowed = true;
}
SPI1BUFL = 0;
SPI1BUFH = 0;
}