This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MSP432P401R: Unexpected behaviour waiting on SPI interrupt flag

Part Number: MSP432P401R

I noticed a weird behaviour with library function

uint_fast8_t SPI_getInterruptStatus(uint32_t moduleInstance,  uint16_t mask)

In breif, used to check whether UCxTXBUF is ready for a new byte during a multi-byte transmission, if previously sent byte MSB was '1' it sets SPI SIMO pin high.

I replaced

while (!SPI_getInterruptStatus(DevHandle->SensorSpiModule, SENSE_SPI_TRANSMIT_INTERRUPT));

with the following instruction

while(!(UCTXIFG & SENSE_SPI_INTERRUPT_FLAG_REGISTER));

circumventing the issue, that actually was negatively affecting communication with my slave SPI sensor.

Is that the expected behaviour for SPI_getInterruptStatus anyway?

  • That function (the source is right there in \TI) just reads the USCI IFG register then AND-s it with the mask you supply.

    What you're describing is an effect where reading the IFG alters the (Tx) data stream, which seems improbable. It's not just faith in the hardware -- I've seen a fair amount of code which uses this technique, and no one has reported an effect like this.

    Is there any chance your driverlib source has been corrupted? (I did that once.) How sure are you that "DevHandle->SensorSpiModule" is correct?

    Is there more context here?

  • Dear Bruce,

    I was quite surprised as well. Unfortunately I did not save any oscilloscope trace. Anyway, I was configuring a SPI sensor which requires sequences of uint16_t values to be sent from master (MCU) to slave (sensor).

    All I can say is that it was impossible for me to read back data from a specific register, and yes, even more weird: other registers worked.

    I am configuring registers in sequence, repeating the following write-read procedure for each one:

    1. Send the register address as a uint8_t value with MSB == '0'
    2. Send the configuration byte
    3. Send the register address as a uint8_t value with MSB == '1'
    4. Send dummy byte
    5. Retrieve value

    Carefully analysing bus traffic with an oscilloscope I noticed that MOSI wire was always set high between the address byte and the dummy one, apparently with no reason. It was not being sampled at clock rising edge anyway, so in theory it shoudn't have been a problem. This didn't happen for the write routine, before sending the configuration byte. Changing the READ busy-wait method solved everything.

    I will try to reproduce that as soon as I can, maybe by library is messed-up, or I inadvertently made some other change in the code and now it works regardless of how I implemented the busy wait.

  • Dear Bruce,

    you can have a look at the oscilloscope traces below:

       Yellow -> chipselect

       blue -> MOSI

       pink -> clock

       green -> MISO

    You were right: I was polling on the receive flag indeed,

    while (!SPI_getInterruptStatus(DevHandle->SensorSpiModule, SENSE_SPI_TRANSMIT_INTERRUPT));

    works just as expected:

    I was programming the SPI controller to clear receive interrupt flag before sending a byte, and wait for it once the byte was sent. In the end, according to the SPI protocol you can assume slave has completely received a byte once you get one back, right? Well, placing the following code into the readout routine

        // Transmit address byte
        SPI_transmitData(DevHandle->SensorSpiModule, (uint_fast8_t)(Address|(1<<7)));
        // Wait for RX buffer to be ready
        while (!SPI_getInterruptStatus(DevHandle->SensorSpiModule, SENSE_SPI_RECEIVE_INTERRUPT));
        // Clear interrupt flag
        SPI_clearInterruptFlag(DevHandle->SensorSpiModule, SENSE_SPI_RECEIVE_INTERRUPT);
        // Transmit dummy-byte
        SPI_transmitData(DevHandle->SensorSpiModule, 0x00);

    gets to the following traces:

    It didn't seem to be likely that the microcontroller sets MOSI with no reason while resetting or polling an interrupt flag register. So, the last screenshot (3) has been acquired disconnecting sensor SDI from the MCU. The unexpected MOSI signal comes from the microcontroller. Yet, being reset before dummy byte is sent, MOSI glitch should not affect the readout procedure, isn't it?

  • I did some poking with a Launchpad and it does seem that MOSI idles based on the MSbit of the previous byte -- not what I would have guessed, but I don't know how it works inside.

    The difference between the two traces is that in the first you keep the "pipeline" full, so it never idles, and in the second you (appropriately) let the pipeline empty, which results in a pause where MOSI can idle. That is, reading (spinning) on the RXIFG doesn't cause MOSI to go high, it just (indirectly) makes it visible.

    We might see different things if we squint at it, but it looks to me as though MOSI goes low "just in time" in both cases. But even if we suppose it didn't, it would be an unusual device which cares what the contents of the "dummy" byte are.

    Which leaves the mystery of why setting register 0x20 doesn't seem to stick in the second case. I don't know the answer. Can you tell me what sensor this is?

    As a workaround: What result do you get if you run the pipeline (first trace) but wait for completion using SPI_isBusy()? I.e., let it overrun, and just pick out the final byte (which is the one you want). [This won't work on a Black Launchpad, but you should be fine with any other platform.]
  • Dear Bruce,

    sorry for the late answer, but I have been waiting to collect some more details before replying.

    First of all: thanks for the workaround hint, it is an interesting way to know when data is ready to be fetched.

    The sensor is a LIS3DSH from STM. I got in contact with their tech assistance and they provided me an example project to run on my spare STM32eval. Well, according to the oscilloscope traces, the way they manage the SPI bus looks quite like the one I had implemented at first on MSP432. Master SDO retains address LSB (last bit sent) before the effective data byte is transferred (not MSB), but read/write operations succeed in any case.

    Could it be that after idling the SPI peripheral wakes up with MISO pin set as output, compromising sensor output signal?

    I attach a pair of acquisitions from STM environment:

    WRITE:

    READ:

  • The notion of the master drive MISO (low) also seems rather low probability, since if it did that pretty much nothing would work. What I referred to as "idling" is not at all unusual. For short transactions I rarely try to pack the pipeline full. And if I had ever seen MOSI go high between bytes I probably wouldn't have paid any attention.

    The LIS3DH data sheet also gives no indication that it cares at all what the "dummy" byte value is, so even if the master were a bit late in transitioning MOSI the slave wouldn't notice.

    I still find it interesting that you only see this effect on register 0x20 ("other registers worked"), and so far only with value 0x02 (if I'm reading right). It seems that it should be legal to set only Yen in CTRL_REG1, but the specification is a bit "sparse" in places.

    Unfortunately, I don't have an LIS3DH here, so all I can do is guess.
  • Dear Bruce,

    I made some additional tests and tried to hazard a guess regarding issue root causes.

    You can have a look at the following link for further information.

    https://community.st.com/s/question/0D50X00009kHRHISA4/failure-reading-from-lis3dsh-spi-interface

  • Giovanni,
    Like Bruce I still think it's suspicious that you are observing an issue only on a single register and not on communication in general. You might try observing the transaction with a logic analyzer to more clearly see what data is being transmitted.
    -Bob L.
  • Hi Bob,

    nice to hear from you again!

    Did you have a look at the link in my previous post? Please let me know what you think about it, it's being viewed by many, but nobody replies..

  • Just to clutter the HOLD TIME computation out, I tried using SPI_isBusy(), as below:

            // Transmit data byte
            SPI_transmitData(SensorInUse->ModuleInstance, (uint_fast8_t)SpiTxByte);
            // Wait for TX buffer to be ready
            while (!SPI_getInterruptStatus(SensorInUse->ModuleInstance, SPI_TRANSMIT_INTERRUPT_FLAG));
            // Wait Spi to be ready
            while(SPI_isBusy(SensorInUse->ModuleInstance));
            // Set CS
            GPIO_setOutputHighOnPin(SensorInUse->CsPort, SensorInUse->CsPin);

    But ChipSel goes high before transmission ends.

    (P.S. Waiting also for TX buffer to be ready could be redundant, I know)

  • Typo Alert! (No, not yours.) I looked at this multiple times and still didn't see it. From driverlib.c:

    >bool EUSCI_B_SPI_isBusy(uint32_t baseAddress)
    >{
    > //Return the bus busy status.
    > return BITBAND_PERI(EUSCI_B_CMSIS(baseAddress)->rSTATW.r, UCBBUSY_OFS);
    >}

    That should be UCBUSY_OFS (=0 i.e. (1<<0)) not UCBBUSY_OFS (=4, i.e. (1 << 4)). That's testing for I2C busy, not SPI busy.

    This code is in the ROM, so I don't expect a quick fix. As a workaround, I suggest:

    > while(SensorInUse->ModuleInstance->STATW & EUSCI_B_STATW_SPI_BUSY);
  • I implemented your suggestion.

        // Lower CS
        GPIO_setOutputLowOnPin(SensorInUse->CsPort, SensorInUse->CsPin);
    
        // Wait setup time
        while(--Timeout);
    
        // Wait for TX buffer to be ready
        while (!SPI_getInterruptStatus(SensorInUse->SerialPeripheral, SPI_TRANSMIT_INTERRUPT_FLAG));
        // Transmit address byte
        SPI_transmitData(SensorInUse->SerialPeripheral, (uint_fast8_t)(SpiActiveAddress|(1<<7)));
        // Wait for TX buffer to be ready
        while (!SPI_getInterruptStatus(SensorInUse->SerialPeripheral, SPI_TRANSMIT_INTERRUPT_FLAG));
        // Transmit dummy-byte
        SPI_transmitData(SensorInUse->SerialPeripheral, 0x00);
        // Wait for spi module to be ready
        while(SensorInUse->SpiModuleAddress->STATW & EUSCI_B_STATW_SPI_BUSY);
    
        // Set CS
        GPIO_setOutputHighOnPin(SensorInUse->CsPort, SensorInUse->CsPin);

    It seems to work, hooray!

  • Thanks for driving this to ground. I will submit a bug accordingly.

    Regards,
    Chris

**Attention** This is a public forum