RuntimeError with Camera as SPI Slave combined with sensor.snapshot()?

Hello there again!

We are using SPI to communicate with another microcontroller. The Camera is the spi-slave and the microcontroller is the spi-master.We can’t use the camera as SPI Master because we can’t use the microcontroller as SPI Slave, since its pins for that use are already used elsewhere. We are telling the microcontoller via another Pin (P4) whether the camera has some calculation results to deliver.
So, after the SPI-interrupt was executed and data were sent, this P4 is set to 0 and when the main loop has some results, it sets P4 to 1, microcontroller starts communication, P4 gets set to 0, main loop sets it to 1… and so on.

We are having problems with a runtime exception with no error message:

Uncaught exception in ExtInt interrupt handler line 12
RuntimeError:

Since we might have some problems with our heap (see here: http://forums.openmv.io/viewtopic.php?f=3&t=338) I checked out whether the error occured if we skip the “heap-corrupting”-functions. And it didn’t. But then I added a simple sensor.snapshot() in the main loop and it showed the same error as before.
For debugging I currently have a counter in the main loop, and the error occures not always, and data are successfully sent and printed to serial terminal, but then suddenly it stops with that RuntimeError.

Then I added following instruction before sensor.snapshot() in the main loop:

if not pyb.Pin.board.P4.value(): sensor.snapshot()

So, as long as there can be a spi communication running or being started, there will be no snapshots done.
And the error didn’t appear anymore.

Why does the camera show this behaviour?

Here is the code that is executed in the spi-interrupt:

    def _csIntHandler(self, line):
        try:
            #self._spi.send_recv(send=self._spiSendData,recv=self._spiRecData, timeout=100)
            #self._spi.send(self._spitestdata, timeout=1000)
             self._spi.send_recv(send=self._spiSendData,recv=self._spiRecData, timeout=100)
             self._intPin.value(0)

        except OSError as err:
            print("error os :" + str(err))
            pass
        except NameError as err:
            print("error name")
            pass

        if self._spiRecData[0] == self._startByte and self._spiRecData[5] == self._endByte:

            self._recValid = 1
            self._recBnoAngle = 0
            self._recBnoAngle = self._spiRecData[1] & 0xFF
            self._recBnoAngle |= (self._spiRecData[2] << 8)  & 0xFF00
            self._recGoalSide = self._spiRecData[4] & 1
            self._recReset = (self._spiRecData[4] & (1 << 1)) >> 1
            self._recLifted = (self._spiRecData[4] & (1 << 2)) >> 2
            self._recOutside = (self._spiRecData[4] & (1 << 3)) >> 3
        else:
            self._recValid = 0

Hi, please see the pixy emulation script with spi for how to use the OpenMV Cam as a spi slave.

That said, using the camera as a SPI slave is… Hard. Really hard. This is because the OpenMV Cam will not always be ready to handle spi data request when you try to access it.

If you want to work with the OpenMV Cam as a slave you should use two sideband lines in addition to the clock and two data lines.

First, the master should raise one sideband line high letting the camera know it was to do spi stuff. Then the camera should raise another line high letting the master know it’s ready to do spi stuff. The camera should raise the line to the master high after disabling interrupts. It should immediately then start the spi receive method waiting for a command.

Next the master device will see the line high and he can then clock out a command to the camera. After which he can do some more spi stuff.

To end the syncing, the master should drop his request line, the the camera will drop it’s response line… Then the master knows that camera is back to doing stuff.

Use async serial if you can avoid spi. Otherwise it’s nightmare. I2C is slightly easier than spi but still harder to get to work than async serial.

The reason for I2C and SPI being bad is because they don’t have hardware to buffer data for you. The OpenMV Cam has to be actively servicing the interface for each packet… Which might not be possible if it’s doing USB stuff or etc. In master mode this isn’t a problem, but, in slave mode it is.

Anyway, the whole point of the above syncing method I wrote is to make sure the OpenMV Cam is in a no interrupt mode to handle spi traffic before the master starts sending data. If you don’t do this you get bit/byte alignment issues.

Finally, there’s no benefit to doing spi transactions in an interrupt handler. You should in general avoid using those if possible because you can’t allocate memory in an interrupt.