attachInterrupt/detachInterrupt interfering with SPI reads

Snowman256

New member
I’m trying to interface with a TPC6240 analog-to-digital converter (link), which has a combined DOUT/RDY pin for SPI communication with my Teensy 4.1 microcontroller. I can poll the DOUT/RDY pin and get good data back (i.e., send a command specifying the data register’s address every 100us, and receive four bytes back with the ADC’s data register and status). However, as soon as I try to attach an interrupt to the DOUT/RDY pin, the Teensy’s SPI library only reads zeros on the MISO pin (even though my logic analyzer is telling me that data is being send, exactly like when I poll the ADC). The weird thing, in my mind, is that even if I detach the interrupt right after (see the code snippet below), I also only read zeros on the MISO pin. Does anyone have ideas/leads on how the SPI library interacts with interrupts?

Code:
SPI.usingInterrupt(digitalPinToInterrupt(12));
attachInterrupt(digitalPinToInterrupt(12), saveVoltage, FALLING);
detachInterrupt(digitalPinToInterrupt(12));

With attachInterrupt and detachInterrupt both commented out:
15:56:45.197 -> Sent 0x44 --> Received 0x80 0xAC 0x47 0x00
15:56:45.197 -> Sent 0x44 --> Received 0x80 0xAC 0x47 0x00
15:56:45.197 -> Sent 0x44 --> Received 0x80 0xAC 0x47 0x00
15:56:45.197 -> Sent 0x44 --> Received 0x80 0xAC 0x47 0x00
15:56:45.197 -> Sent 0x44 --> Received 0x80 0xAC 0x47 0x00
15:56:45.197 -> Sent 0x44 --> Received 0x80 0xAC 0x47 0x00
15:56:45.197 -> Sent 0x44 --> Received 0x80 0xAC 0x47 0x00

With attachInterrupt and detachInterrupt left in (or if only attachInterrupt is left in, pointing to an empty function):
16:02:35.420 -> Sent 0x44 --> Received 0x00 0x00 0x00 0x00
16:02:35.420 -> Sent 0x44 --> Received 0x00 0x00 0x00 0x00
16:02:35.420 -> Sent 0x44 --> Received 0x00 0x00 0x00 0x00
16:02:35.420 -> Sent 0x44 --> Received 0x00 0x00 0x00 0x00
16:02:35.420 -> Sent 0x44 --> Received 0x00 0x00 0x00 0x00
16:02:35.420 -> Sent 0x44 --> Received 0x00 0x00 0x00 0x00
16:02:35.420 -> Sent 0x44 --> Received 0x00 0x00 0x00 0x00

Regardless of whether the lines are left in/out, I get signals like this on a logic analyzer. The only difference appears to be whether my Teensy microcontroller can actually read the data.
Screenshot 2025-06-10 160920.png



I've also included some stripped down code below, if it helps (it references another library I wrote that actually calls the SPI commands, so I included the main parts of that code below as well). Feel free to let me know if more details are needed!

Code:
#include "tpc6240.h"

uint8_t csPin1 = 7;
TPC6240 adc = TPC6240(csPin1);

void setup() {
  Serial.begin(115200);
  SPI.begin();
  SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE3));
  digitalWrite(csPin1, LOW);

  uint16_t id = adc.ReadID();
  Serial.printf("ID: 0x%04X\r\n", id);  // should return 0x6240

  // Set up ADC
  adc.writeADCMode(true, false, false, true);  // enable_ref2_5V, enable_PGA_rail_detection, single_channel, continuous_conversion
  adc.writeInterfaceMode(false, true);  // continuous_read, append_status_to_dataread
  adc.writeCH(0, true, 0, BOARD2ADC_CHANNEL::CH0);  // channelNum, enable, configNum, AINNumPlus
  adc.writeSetupConfig(0, false, true, true, true);  // configNum, _unipolar, buffer_ref_input, enable_pga, use_internal_ref_voltage

  // The lines in question that confuse me
  SPI.usingInterrupt(digitalPinToInterrupt(12));
  attachInterrupt(digitalPinToInterrupt(12), saveVoltage, FALLING);
  detachInterrupt(digitalPinToInterrupt(12));
  SPI.endTransaction();
}

uint32_t ReadCode = 0;
int32_t ADC_Code = 0;
uint8_t status = 0;

void loop() {
    ReadCode = adc.readDataRegister();  // included below
    delayMicroseconds(10);
}

void saveVoltage() {}

uint32_t TPC6240::readDataRegister() {
  uint32_t ADC_Value;
  uint8_t regAddr = RegisterMap::DATA | (1<<6);    //0bx1xxxxxx
  regAddr = regAddr & (~(1<<7)); //0b0xxxxxxx
 
  Serial.printf("Sent 0x%02X --> Received ", ReadAddr);
  SPI.beginTransaction(ConnectionSettings);
  SPI_ReadWriteByte(ReadAddr);
  for (uint16_t i = 0; i < NumByteToRead; i++) {
      pBuffer[i]=SPI_ReadWriteByte(0x00);
      Serial.printf("0x%02X ", pBuffer[i]);
  }
  Serial.println();
  SPI.endTransaction();
  ADC_Value = (SPI_ReadBuff[0] << 24) | (SPI_ReadBuff[1] << 16) | (SPI_ReadBuff[2] << 8) |
  return ADC_Value;
}
 
Last edited:
I think adding an interrupt to the pin changes it to being controlled by the GPIO module, not the SPI. Simply connect the signal to two pins, one for SPI and one for interrupts and you should be good.
 
I think adding an interrupt to the pin changes it to being controlled by the GPIO module, not the SPI. Simply connect the signal to two pins, one for SPI and one for interrupts and you should be good.
Thanks so much, that worked like a charm! I thought that was what the SPI.usingInterrupt(digitalPinToInterrupt(12)); was meant to handle, but in hindsight I guess that command's meant only for when you're triggering interrupts on non-SPI pins to avoid an interrupt from triggering mid-read?

At first glance, SPI.cpp's trigger() function doesn't include any sort of interrupt/trigger mechanisms, but maybe that's buried in the IMXRT_LPSPI_t implementation.

C++:
do {
    if ((port().RSR & LPSPI_RSR_RXEMPTY) == 0)  {
        uint8_t b = port().RDR;  // Read any pending RX bytes in
        if (p_read) *p_read++ = b;
        count_read--;
    }
} while ((port().SR & LPSPI_SR_TDF) == 0) ;
 
Back
Top