I have setup SPI2 on a dsPIC33CK64MP505 to receive Left adjusted audio as a slave. If I run the code by polling the SPI buffer, I get data in and all is dandy. However I need the synchronization of left and right channel so I'm trying to get the SPI2 RX interrupt to fire on received data. The Audio format is 24bit left adjusted so SPI2BUFH contains the 16bit MSB while the SPI2BUFL contains the last 8bit LSB.
I have configured it like this:
RPINR23bits.SS2R = 38; // RB6 LRCK
RPINR22bits.SCK2R = 39; // RB7 BCLK
RPINR22bits.SDI2R = 40; // RB8 ADCDAT (SDI)
IEC7bits.SPI2IE = 0;
SPI2CON1Lbits.SPIEN = 0;
SPI2CON1H = 0;
while(!SPI2STATLbits.SPIRBE) { (void) SPI2BUFL; (void) SPI2BUFH; }
SPI2STATLbits.SPIROV = 0; // Clear receive overflow
SPI2CON1Hbits.AUDMOD = 1; // Left justified
SPI2CON1Hbits.AUDEN = 1;
SPI2CON1Lbits.MSTEN = 0;
SPI2CON1Lbits.CKP = 0;
SPI2CON1Hbits.FRMPOL = 1;
SPI2CON1Lbits.MODE = 3; // 24bit over 32bit register
IPC7bits.SPI2RXIP = 4;
//SPI2IMSKHbits.RXMSK = 1; // Tried this as well
//SPI2IMSKHbits.RXWIEN = 1; // ...didn't work either
SPI2IMSKLbits.SPIRBFEN = 1;
IEC7bits.SPI2IE = 1;
//IFS7bits.SPI2IF = 0; // As a test, if I set this to 1,
// I enter the ISR instant.
SPI2CON1Lbits.ENHBUF = 1;
SPI2CON1Lbits.SPIEN = 1;
The interrupt routine is as follow:
void __attribute__((interrupt, no_auto_psv)) _SPI2Interrupt(void) {
IFS7bits.SPI2IF = 0;
while(!SPI2STATLbits.SPIRBE) { // Clear buffer for test.
(void)(uint16_t)(SPI2BUFH);
(void)(uint8_t)(SPI2BUFL);
}
SPI2STATLbits.SPIROV = 0; // Clear receive overflow
}
So, with an error free audio stream the interrupt never fire. If I set the SPI2IF flag at initialization it fires immediately, so the ISR is correct. I get in data when polling, so we know the pins are correctly configured. But for some reason it doesn't fire on the buffer-full event (I'm of course not polling when trying the interrupt solution).
I found out at the documentation was unclear and maybe even incomplete (no surprise Microchip). I had enabled SPI2IE instead of SPI2RXIE (also missing in the IO View together with the SPI2RXIF). The documentation only states, set up interrupts accordingly.
Also the enhanced SPI buffer mode MUST be used. If not, I got somewhat random values in the buffer until they stabilized and always return a fixed value.
The format in the buffer is not mentioned to be right aligned (it converts the audio format so LSB are in the buffer's LSB).
So my setup for Left adjusted 24bit audio became this:
RPINR23bits.SS2R = 38; // RB6 LRCK
RPINR22bits.SCK2R = 39; // RB7 BCLK
RPINR22bits.SDI2R = 40; // RB8 ADCDAT (SDI)
// Reset and clean-up
IEC7bits.SPI2IE = 0;
IEC1bits.SPI2RXIE = 0;
SPI2CON1Lbits.SPIEN = 0;
SPI2CON1H = 0;
while(!SPI2STATLbits.SPIRBE) { (void)SPI2BUFL; (void)SPI2BUFH; }
// Setup Left adjusted 24bit Audio
SPI2STATLbits.SPIROV = 0; // Clear receive overflow
SPI2CON1Hbits.AUDMOD = 1; // Left justified
SPI2CON1Hbits.AUDEN = 1; // Enable audio protocol
SPI2CON1Lbits.MSTEN = 0; // Listen in slave mode
SPI2CON1Lbits.CKP = 0; // Clock polarity for audio
SPI2CON1Hbits.FRMPOL = 1; // Set audio frame polarity
SPI2CON1Lbits.MODE = 3; // 24bit over 32bit register
SPI2CON1Hbits.IGNROV = 1; // Ignore buffer overrun
SPI2CON1Hbits.IGNTUR = 1; // Ignore buffer underrun
SPI2CON1Lbits.ENHBUF = 1; // Enable enhanced buffer mode (A MUST!)
SPI2IMSKLbits.SPIRBFEN = 1; // Rx Full interrupt Enable
IPC7bits.SPI2RXIP = 4; // Set interrupt priority
IFS1bits.SPI2RXIF = 0; // Clear Rx Interrupt flag
IEC1bits.SPI2RXIE = 1; // Enable Rx Interrupt
SPI2CON1Lbits.SPIEN = 1; // Enable SPI module
And of course another interrupt vector needs to be setup.
static uint32_t Levels[2] = { 0,0 }; // Right, Left channel
void __attribute__((interrupt, no_auto_psv)) _SPI2RXInterrupt(void) {
IFS1bits.SPI2RXIF = 0;
bool n = PORTBbits.RB6; // Get Left/Right channel
while(!SPI2STATLbits.SPIRBE) {
Levels[n] = SPI2BUFL;
Levels[n] += ((uint32_t)SPI2BUFH<<16);
}
}
This did the trick. Note that even if the audio format is left adjusted, it is right adjusted in the buffer. Take notice that the 24bit data isn't sign extended. You can easily do this by checking bit 23.
// Sign extend from 24bit to 32bit.
if(Levels[n]&0x00800000) Levels[n] |= 0xFF000000;
However, there is a bit in the SPI to sign extend, but I didn't get it to work. Hope this will help others trying to deal with Microchip documentation.