SPI MISO data not being returned

I’m attempting to communicate with an SPI device via the following code:

        SpiClockConfiguration spiConfig = new SpiClockConfiguration(750, SpiClockConfiguration.Mode.Mode3);
        ISpiBus spiBus = Device.CreateSpiBus(Device.Pins.SCK, Device.Pins.MOSI, Device.Pins.MISO, spiConfig);

        IDigitalOutputPort spiPeriphChipSelect = Device.CreateDigitalOutputPort(Device.Pins.D02);
        ISpiPeripheral spiPeriph = new SpiPeripheral(spiBus, spiPeriphChipSelect);

        Int32 getVersion = 0b00000000000000000000000001110011;

        var buffer1 = BitConverter.GetBytes(getVersion);
        var buffer2 = BitConverter.GetBytes(0);

        Span<Byte> sBuffer1 = buffer1;
        Span<Byte> sBuffer2 = buffer2;

        while (true)
        {
            spiPeriph.Exchange(sBuffer1, sBuffer2);

            Console.WriteLine("Sent: {0:X}", BitConverter.ToInt32(buffer1, 0));
            Console.WriteLine("Received: {0:X}", BitConverter.ToInt32(buffer2, 0));

            Thread.Sleep(1000);
        }

I expect data to be received, however “Received: 0” is only ever emitted. So I attached a logic analyzer…

…and can see that MISO data is available: 15429101.

Thus I have 2 questions:

  1. Why is the MISO data not being reported?
  2. Why is there an additional 32 bits of data being sent after the 1st 32 bits of data that I’ve specified via ‘spiPeriph.Exchange’?

Hearing nothing, I was seeing if I could work around this issue; which just raises more questions…

If I replace spiPeriph.Exchange(sBuffer1, sBuffer2) with the deprecated functions:

            spiPeriph.WriteBytes(buffer1);
            buffer2 = spiPeriph.ReadBytes(4);

The logic trace for WriteBytes is as expected:

However, “buffer2 = spiPeriph.ReadBytes(4);” results in an added SPI bus request:

I’m not aware of any other way of obtaining the MISO data for the original write request. Thus, it would appear that the SPI implementation as of Meadow B5.1 is busted. The additional data being written on the SPI bus appears to stem from the firmware’s ‘read’ functionality; which does not appear to be correctly implemented.

Thoughts?

Exchange is doing what the old WriteRead did - it’s doing a bus write, followed by a read. To read, the master has to run the clock, so it’s not directly giving you the data that was clocked in during the write. Basically it’s a half-duplex function, not a full-duplex. The capability to get the data during the write I know exists, I just can’t recall offhand and am not on a machine with the code, so I can take a look tomorrow. What peripheral are you using? I’d like for us to add it to our test stable if we can.

Oh man, you’d make my week if you could point me to a supported way within Meadow to full-duplex communicate via SPI. I’m not finding it via the ‘SpiPerhipheral’ class & related interfaces.

I’m working to implement functionality to interact with Trinamic motion controllers / drivers via SPI:

etc…

Looks like if you call Exchange() directly on the SpiBus rather than on the peripheral, it’ll do a full-duplex exchange.

It’s funny, we were working on these APIs recently, and we had a conversation where we decided not to expose a full-duplex exchange on the IByteCommunications interface because almost zero peripherals support it. Now we need to revisit! :smiley:

We’ll discuss and figure out the right path forward to support a full-duplex exchange API set that’s consistent between bus and peripheral. Thanks for bringing this to our attention.

I can confirm, calling Exchange() directly on the SpiBus does work here…thank you folks, really appreciate it!

No problem. I think in a near beta we’ll reconfigure those APIs a little bit.