Skip to content

SPITarget unexpected MOSI read results #10504

@anecdata

Description

@anecdata

CircuitPython version and board name

# main (raw SPI, or SPIDevice):
Adafruit CircuitPython 9.2.8 on 2025-05-28; Adafruit Feather M4 Express with samd51j19
Adafruit CircuitPython 10.0.0-beta.0 on 2025-07-15; Adafruit Feather M4 Express with samd51j19
Adafruit CircuitPython 10.0.0-beta.0 on 2025-07-15; Adafruit Feather RP2040 with rp2040

# secondary (SPITarget):
Adafruit CircuitPython 9.2.8 on 2025-05-28; Adafruit Feather M4 Express with samd51j19
Adafruit CircuitPython 10.0.0-beta.0 on 2025-07-15; Adafruit Feather M4 Express with samd51j19

Code/REPL

# SPITarget secondary code from docs example, with the addition of only the final print line:
# https://docs.circuitpython.org/en/latest/shared-bindings/spitarget/index.html


import time  ####
import board
import analogio
from spitarget import SPITarget

ain0 = analogio.AnalogIn(board.A0)
ain1 = analogio.AnalogIn(board.A1)
selected_channel = ain0

def map_adc_channel(index):
    return ain0 if (index == 0) else ain1

mosi_buffer = bytearray(2)
miso_buffer = bytearray(2)
with SPITarget(sck=board.D12, mosi=board.D13, miso=board.D11, ss=board.D10) as device:
    while True:
        # Convert analog signal to array of bytes
        reading = selected_channel.value
        miso_buffer[0] = (reading >> 8) & 0xFF
        miso_buffer[1] = (reading) & 0xFF
        # Send array of bytes over SPI to main
        device.load_packet(mosi_buffer, miso_buffer)
        while not device.wait_transfer(timeout=-1):
            pass
        # Handle command from main, which sets the ADC channel
        selected_channel = map_adc_channel((mosi_buffer[0] << 8) | mosi_buffer[1])

        print(f"{time.monotonic():.03f} {mosi_buffer=} ADC={int.from_bytes(miso_buffer, 'big')}")  ####


# SPI main code taken from docs example, and made into looping code:

import board
import digitalio
import busio
import time

## setup
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
cs = digitalio.DigitalInOut(board.D5)  ####
cs.direction = digitalio.Direction.OUTPUT
cs.value = True

while True:  ####
    while not spi.try_lock():  ####
        pass  ####

    ## ADC command: read from A0
    cs.value = False
    mosi_buffer = bytearray([0, 0])  #### <---------------- crux of the issue
    spi.write(mosi_buffer)  ####
    cs.value = True
    print(f"{time.monotonic():.03f} {mosi_buffer=}", end=" ")  ####

    # wait for ADC to read a value
    time.sleep(0.01)  ####

    ## get two-byte output from ADC
    adc_result = bytearray(2)
    cs.value = False
    spi.readinto(adc_result, write_value=1)
    cs.value = True
    print(f"ADC={int.from_bytes(adc_result, 'big')}")  #### list(adc_result)

    spi.unlock()  ####
    time.sleep(3)  ####


# (my changes to lines in the docs code are marked with "####")

Behavior

The key is to vary the values in mosi_buffer in the main.

SPITarget secondary print output when mosi_buffer is filled with [0, 0] in the main, as in the example:

931.938 mosi_buffer=bytearray(b'\x00\x00') ADC=29927
931.948 mosi_buffer=bytearray(b'\x01\x01') ADC=21445
934.948 mosi_buffer=bytearray(b'\x00\x00') ADC=22437
934.958 mosi_buffer=bytearray(b'\x01\x01') ADC=16804
937.958 mosi_buffer=bytearray(b'\x00\x00') ADC=29735
937.969 mosi_buffer=bytearray(b'\x01\x01') ADC=20837

(Since the main reads and writes are separate, each triggers a device.wait_transfer in the secondary - I assume this is correct behavior, but I don't know where the bytearray(b'\x01\x01') is origniating. Only every other reading is received at the main (when the main does the readinto).

But with other values written from main to secondary (e.g., [5, 5] or [65, 90] or [0b01010101, 0b00110011]), output is unpredictable and doesn't reflect the data written:

1133.767 mosi_buffer=bytearray(b'\x01\x01') ADC=29255
1136.778 mosi_buffer=bytearray(b'\x01\x01') ADC=23397
1139.789 mosi_buffer=bytearray(b'\x01\x01') ADC=23461
1142.800 mosi_buffer=bytearray(b'\x01\x01') ADC=28743
1145.810 mosi_buffer=bytearray(b'\x01\x01') ADC=26886
1148.820 mosi_buffer=bytearray(b'\x01\x01') ADC=22597

(note that in this case, the doubled-triggered device.wait_transfer doesn't seem to be happening)

Description

This may be a support issue (asked on Discord but no replies), but I've tried many variations and haven't been able to get sensible reads on the secondary when values much different than [0, 0] are written from the main.

Tried:

  • with and without SPIDevice on the main (not supported with SPITarget)
  • with a buffer size of 1 byte, 2 bytes, or more, on both sides
  • all combinations of polarity and phase
  • slower SPI baudrates
  • separate write and readinto vs. write_readinto, on the main

Additional information

With this behavior, I don't see how it's possible to implement SPITarget to allow addressing multiple registers and / or accepting more varied data (data values, data length).

(FYI: feature was added in #9385)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      pFad - Phonifier reborn

      Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

      Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


      Alternative Proxies:

      Alternative Proxy

      pFad Proxy

      pFad v3 Proxy

      pFad v4 Proxy