Search code examples
interruptaudio-streamingspimicrochip

dsPIC33 SPI AUDIO interrupt not firing


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).


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.