BPIO2 - how to override BPIO outputs when using SPI?

EDIT: TLDR, the second parameter is also a bitmask. See summary.

I’m trying to play with the BPIO2 interface from Python, to help prototype a safer method of reading JEDEC IDs, and using the result to detect if the NAND chip requires delays between command bytes and device response data.

I started with the spi_example.py file, ripped out the status check option, and 99% of the spi_read_jedec_id() function. I then disabled pullups by setting the BPIOSPI.configure() option accordingly.

My goal has been to set BPIO2 as high (/HOLD on the plank), I try the following:

if True:
    print(f"Setting BPIO2 as output/high")
    BPIO2_MASK = 0b00000100
    spi.set_io_direction(BPIO2_MASK, 1)
    spi.set_io_value(BPIO2_MASK, 1)
else:
    print(f"Setting BPIO3..0 as HHLH")
    BPIO_HIGH_MASK = 0b00001101
    spi.set_io_direction(BPIO_HIGH_MASK, 1)
    spi.set_io_value(BPIO_HIGH_MASK, 1)
    BPIO_LOW_MASK  = 0b00000010
    spi.set_io_direction(BPIO_LOW_MASK, 1)
    spi.set_io_value(BPIO_LOW_MASK, 0)

The attempt to set only BPIO as output/High do not appear to actually do so (verified with multimeter). Oddly, the code within the else clause appears to set BPIO0 as output/High … but neither BPIO3 nor BPIO2 are set as output/High.

If I am using the API wrong, what’s the correct way to do this?

If the above should work, then … what’s needed for a good bug report?

spi_test.txt (2.7 KB)

Update:
I confirmed (via manual inspection of client.show_status()) that:

  • The If True path behavior is as noted above.
  • The Else path behavior is as noted above.

Seems like there is quite likely at least one bug, either in my use of the APIs, or in their implementation.

Anyone play around with this and get it to work?

2 Likes
spi.set_io_direction(0b1111, 0b1111)  # Set BPIO0...3 output
spi.set_io_value(0b1111, 0b0000)      # Set outputs low

This works on my end.

  • The first byte is the mask (which pins we want to change)
  • The second byte is the state to configure each pin.

Here is the example.

    BPIO_HIGH_MASK = 0b00001101
    spi.set_io_direction(BPIO_HIGH_MASK, 1)
    spi.set_io_value(BPIO_HIGH_MASK, 1)
    BPIO_LOW_MASK  = 0b00000010
    spi.set_io_direction(BPIO_LOW_MASK, 1)
    spi.set_io_value(BPIO_LOW_MASK, 0)

So in your example we can simplify that to:

MASK = 0b1111
VALUE = 0b1101
spi.set_io_direction(MASK, MASK) # all four to output
spi.set_io_value(MASK, VALUE) #set value
[BPIO] Flatbuffer data read complete, total length: 66
[BPIO] Packet Type: 2
[Config Request] IO0 set to output
[Config Request] IO1 set to output
[Config Request] IO2 set to output
[Config Request] IO3 set to output
[Send Packet] Length 36
[Send Packet] COBS encoded buffer length: 38
[BPIO] COBS delimiter found, stopping read
[BPIO] Flatbuffer data read complete, total length: 66
[BPIO] Packet Type: 2
[Config Request] IO0 set to low
[Config Request] IO1 set to low
[Config Request] IO2 set to low
[Config Request] IO3 set to low

You can add "bpio_debug_enable":1 to your BPCONFIG.JSON file to see detailed BPIO2 debugging info in the terminal.

:tada: Aha! Thank you Ian … I think I see it now.

My issue was how I interpreted the second paramter to send to spi.set_io_value(). I had incorrectly understood that parameter to be a simple True/False value that is used to set all the masked pins to H/L, respectively.

I now understand that the first parameter simply indicates which pins are to be set, and the value is also a bitmask indicating the values of the pins (ignored when not in the bitmask).

My code was thus executing correctly...

In the if True branch, the mask limited the effect of the value to bitmask 0b100, but the value sent was 0b001. Thus, it set BPIO2 low.

Similarly, the else branch had a mask of 0b1101 in the first call, with value 0b0001, and thus was setting BPIO0 high, and BPIO2 and BPIO3 low. The second call (0b0010 / 0b0001 ) was setting BPIO1 low.

if True:
    print(f"Setting BPIO2 as output/high")
    BPIO2_MASK = 0b00000100
    spi.set_io_direction(BPIO2_MASK, 1)
    spi.set_io_value(BPIO2_MASK, 1)
else:
    print(f"Setting BPIO3..0 as HHLH")
    BPIO_HIGH_MASK = 0b00001101
    spi.set_io_direction(BPIO_HIGH_MASK, 1)
    spi.set_io_value(BPIO_HIGH_MASK, 1)
    BPIO_LOW_MASK  = 0b00000010
    spi.set_io_direction(BPIO_LOW_MASK, 1)
    spi.set_io_value(BPIO_LOW_MASK, 0)

I’ll have to think on this; maybe I can find a way to clarify the parameter names. (in a backwards-compatible manner, of course!)

Or maybe add a debug output warning when a bit is NOT included in the mask, but IS set in the value? (only the low ten bits, of course, and adjust for negative values to check for cleared bits not in the mask.) Yes, it could be noisy, but it could also highlight errors in the caller’s code (always good).

Either way, I think this will unblock me. Thank you again!

1 Like