Open MV SPI bus speed

Hi,

I’m trying to use the SPI bus to communicate with a raspeberry PI but i’m having some issues with the communication. The main issue is that the camera gets disconnected from the IDE after a running the script from some time.

I want know if there are specifics speeds the openMV supports. The IDE itself allows me to set any customize speed without showing any errors, but i don’t know if this has an effect when the openMV actually runs the script.

The openMV CAM model i’m using is H7 R2 which in the product page says that has an SPI bus that can run up to 80Mbs.

Hi, you can pretty much do any speed you’d like (that’s a divisor of the system clock). That’s not the issue.

Can you reduce your code to a minimum that causes the error? It disconnecting while doing SPI implies a firmware bug.

Also, sometimes when it disconnects i get this error: OSError: [Errno 5] EIO

What does this error means?

The code i’m running in the openMV is really simple (see attached). And the code running on the raspberry pi which is acting as the master, is also very simple (see attached).

simple_sp_master.py (586 Bytes)

simple_spi_slave.py (290 Bytes)

Which line hits that? EIO can only be returned if the SPI peripheral fails to init. Otherwise the error should be 1/2/3.

Can you capture the line of code where the issue is?

Regarding Comms.

We made an interface library for this. Please use it… or atleast pull examples from it:

Everyone likes to roll their own comms and this is very hard. I don’t not recommend it unless you are a very good programmer and have a protocol debugger like a saleae logic analyzer. If you do not have these tools it’s foolhardy.

Anyway, please pull from the script above code to configure your SPI interface.

Unfortunately, I don’t have the Raspberry Pi code setup for SPI with the interface library. That’s a todo. This would make your life really easy.

Error

The error is on the spi.send line.

Can you post all the code?

The code is the one i attached before.

Hi, yes, as mentioned before. You need to use the SPI bus like how the RPC library does it.

class spi_slave():
    def __init__(self, cs_pin="P3", clk_polarity=1, clk_phase=0, spi_bus=2):  # private
        self.__pin = pyb.Pin(cs_pin, pyb.Pin.IN)
        self.__polarity = clk_polarity
        self.__clk_phase = clk_phase
        self.__spi = pyb.SPI(spi_bus)
        self._get_short_timeout = 10

    def get_bytes(self, buff, timeout_ms):
        start = pyb.millis()
        while self.__pin.value():
            if pyb.elapsed_millis(start) >= self._get_short_timeout:
                return None
        self.__spi.init(pyb.SPI.SLAVE, polarity=self.__polarity, phase=self.__clk_phase)
        try:
            self.__spi.send_recv(buff, buff, timeout=timeout_ms)  # SPI.recv() is broken.
        except OSError:
            buff = None
        self.__spi.deinit()
        return buff

    def put_bytes(self, data, timeout_ms):
        start = pyb.millis()
        while self.__pin.value():
            if pyb.elapsed_millis(start) >= self._put_short_timeout:
                return
        self.__spi.init(pyb.SPI.SLAVE, polarity=self.__polarity, phase=self.__clk_phase)
        try:
            self.__spi.send(data, timeout=timeout_ms)
        except OSError:
            pass
        self.__spi.deinit()

See the init and deinit around SPI send. It is required per transaction. The hardware driver can lockup pretty easily. Note, we are enabling DMA on SPI. So, when you send or receive it will pack data at the speed without any spaces between bytes. You can easily move 80 Mb/s.

Yes, I tested your code and got the same error. Just use the class template above. It will work for you.

Here’s master mode btw:

class spi_master():
    @micropython.viper
    def _same(self, data, size: int) -> bool:  # private
        if not size:
            return False
        d = ptr8(data)
        old = d[0]
        for i in range(1, size):
            new = d[i]
            if new != old:
                return False
            old = new
        return True

    def __init__(
        self, cs_pin="P3", freq=1000000, clk_polarity=1, clk_phase=0, spi_bus=2
    ):  # private
        self.__pin = pyb.Pin(cs_pin, pyb.Pin.OUT_PP)
        self.__freq = freq
        self.__polarity = clk_polarity
        self.__clk_phase = clk_phase
        self.__spi = pyb.SPI(spi_bus)
        self._get_short_timeout = 10

    def get_bytes(self, buff, timeout_ms):
        self.__pin.value(False)
        pyb.udelay(100)  # Give slave time to get ready.
        self.__spi.init(
            pyb.SPI.MASTER, self.__freq, polarity=self.__polarity, phase=self.__clk_phase
        )
        try:
            self.__spi.send_recv(buff, buff, timeout=timeout_ms)  # SPI.recv() is broken.
        except OSError:
            buff = None
        self.__spi.deinit()
        self.__pin.value(True)
        if buff is None or self._same(buff, len(buff)):
            pyb.delay(self._get_short_timeout)
        return buff

    def put_bytes(self, data, timeout_ms):
        self.__pin.value(False)
        pyb.udelay(100)  # Give slave time to get ready.
        self.__spi.init(
            pyb.SPI.MASTER, self.__freq, polarity=self.__polarity, phase=self.__clk_phase
        )
        try:
            self.__spi.send(data, timeout=timeout_ms)
        except OSError:
            pass
        self.__spi.deinit()

Note… some of the issues with things being broken actually go down to the HAL from ST. I debugged this a few years back. But, like when it mentions that SPI.recv() is broken it’s like that it actually can’t work in DMA mode.

Again though…

You’d save yourself some time if you just write the SPI layer for the RPC library for the RPi. Would love a PR for that. Then you could just use the RPC library directly which has already debugged most of these issues.

I don’t know if I will be able to get to doing the RPi SPI/I2C/CAN driver for the RPC library this year.