There is an SPIM unexpected interrupt issue which I have replicated in a totally bare-bones piece of code using no Nordic library drivers. In my bare-bones SPI code I get 2 interrupts instead of the expected 1 at the end of every SPI transaction when only the EVENTS_END interrupt is enabled. This is checked by having a volatile counter which never resets; the count should match the expected number of SPI interrupts but in fact shows double (2x) the expected number. I suspect this to be an SPI errata, but have been too busy to chase it down.
With the same setup and function code, the following handler results in double interrupts for every transaction:
volatile uint32_t mSpiInterruptCounter = 0UL; uint32_t mSpiCorrectResponseCounter = 0UL; void SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler(void) { mSpiInterruptCounter++; NRF_SPIM0->EVENTS_END = 0; }
Modifying the interrupt handler to this code removes the problem, keep in mind EVENTS_ENDRX and EVENTS_ENDTX are not enabled::
void SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler(void) { mSpiInterruptCounter++; if (NRF_SPIM0->EVENTS_END) NRF_SPIM0->EVENTS_END = 0; if (NRF_SPIM0->EVENTS_ENDRX) NRF_SPIM0->EVENTS_ENDRX = 0; if (NRF_SPIM0->EVENTS_ENDTX) NRF_SPIM0->EVENTS_ENDTX = 0; }
The other odd thing is that either clearing ENDRX or clearing ENDTX fixes the issue; both do not need to be cleared.
This is the initialisation and repeated transfer code in both the above cases:
#define LIS2DH12_WHO_AM_I 0x0F // Read as 0x33 #define I_AM_LIS3DH 0x33 uint8_t spiXBuf[] = {0x80|LIS2DH12_WHO_AM_I, 0xFF}; uint8_t spiRXBuf[sizeof(spiTXBuf)] = {0,0}; static const uint8_t mRxTxBufLength = sizeof(spiTXBuf); static void Test4WireSPI(void) { NRF_GPIO->PIN_CNF[CSPIN] = 0x301; // output, high drive high and low H0H1 NRF_GPIO->PIN_CNF[SCKPIN] = 0x301; // output, high drive high and low H0H1 NRF_GPIO->PIN_CNF[MOSIPIN] = 1; // output, standard drive S0S1 NRF_GPIO->PIN_CNF[MISOPIN] = 0; // input pin, input buffer connected, no pull, S0S1, sense disabled NRF_GPIO->OUTSET = 1 << CSPIN; // deactivate by setting chip select high NRF_SPIM0->PSEL.SCK = SCKPIN; NRF_SPIM0->PSEL.MOSI = MOSIPIN; NRF_SPIM0->PSEL.MISO = MISOPIN; NRF_SPIM0->CONFIG = 0; // CPOL 0 -- clock polarity active high, CPHA 1 -- sample on trailing clock edge, send Msb first NRF_SPIM0->FREQUENCY = 0x80000000UL; // 8 Mbps NRF_SPIM0->ORC =0; // Unused Tx bytes, set all low NRF_SPIM0->EVENTS_ENDTX = 0; NRF_SPIM0->EVENTS_ENDRX = 0; // Disable all interrupts NRF_SPIM0->INTENCLR = 0xFFFFFFFFUL; // Enable selected interrupts NRF_SPIM0->INTENSET = 0x040; // END //NRF_SPIM0->INTENSET = 0x010; // END_RX - not enabled! //NRF_SPIM0->INTENSET = 0x100; // END_TX - not enabled! // Set interrupt priority and enable interrupt NVIC_SetPriority(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn, 6); NVIC_ClearPendingIRQ(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn); NVIC_EnableIRQ(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn); NRF_SPIM0->ENABLE = 7; // enable SPI mSpiInterruptCounter = 0UL; // Test some arbitrary number of SPI command requests for (uint32_t i = 0; i<40; i++) { // Clear response byte spiRXBuf[1] == '?'; NRF_SPIM0->TXD.PTR = (uint32_t)spiTXBuf; NRF_SPIM0->TXD.MAXCNT = mRxTxBufLength; NRF_SPIM0->RXD.PTR = (uint32_t)spiRXBuf; NRF_SPIM0->RXD.MAXCNT = mRxTxBufLength; NRF_GPIO->OUTCLR = 1 << CSPIN; // drive cs low to initiate spi comm NRF_SPIM0->TASKS_START = 1; while(!NRF_SPIM0->EVENTS_ENDTX); //last byte transmitted while(!NRF_SPIM0->EVENTS_ENDRX); //last byte received NRF_SPIM0->TASKS_STOP = 1; NRF_GPIO->OUTSET = 1 << CSPIN; // drive cs high to end spi comm // Ensure transfer has completed and SPI stopped while(!NRF_SPIM0->EVENTS_STOPPED); // Check result if (spiRXBuf[1] == I_AM_LIS3DH) mSpiCorrectResponseCounter++; } if (mSpiInterruptCounter == mSpiCorrectResponseCounter) { // test pass } }
I will post this code in the bare-bones Git repo as I'm sure others will find it useful. Meanwhile I would like to not have the extra instructions in the interrupt handler in the interests of reducing cpu cycles