Welcome to the Pycom forum Forum
    • \[\[global:header.categories\]\]
    • \[\[global:header.recent\]\]
    • \[\[global:header.tags\]\]
    • \[\[global:header.popular\]\]
    • Login

    Explore Pybytes | Official Documentation | Report a Firmware Bug/Issue | GitHub

    SPI bug?

    Scheduled Pinned Locked Moved Firmware
    lopybug
    11 Posts 6 Posters 2.2k Views 6 Watching
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • E Offline
      Eric24
      last edited by

      Additional info...

      I'm pretty well convinced that none of the SPI functions that take a 'write' argument pay any attention to that argument--no matter what, only 0x55 is sent during the operation. So I tried rewriting my SPI driver to use only the write_readinto function, passing in a write buffer full of 0xFF. This worked as expected (and made my driver work as well--unfortunately, it makes for some pretty gross code to use only write_readinto).

      1 Reply Last reply Reply Quote 0
      • T Offline
        throwaway
        last edited by throwaway

        Came across someone's post from Nov, 2018 and it looks like you're right.

        See https://github.com/pycom/pycom-micropython-sigfox/blob/master/esp32/mods/machspi.c#L402 and https://github.com/pycom/pycom-micropython-sigfox/blob/master/esp32/mods/machspi.c#L190. The former actually considers the write argument but it's completely ignored in pybspi_transfer(). Instead the code uses 0x55555555 because there's a NULL TX buffer in the call that pyb_spi_read() makes.

        STATIC mp_obj_t pyb_spi_read(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
            static const mp_arg_t allowed_args[] = {
                { MP_QSTR_nbytes,    MP_ARG_REQUIRED | MP_ARG_OBJ, },
                { MP_QSTR_write,     MP_ARG_KW_ONLY  | MP_ARG_INT, {.u_int = 0x00} },
            };
        
            // parse args
            mach_spi_obj_t *self = pos_args[0];
            mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
            mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(args), allowed_args, args);
        
            // get the buffer to receive into
            vstr_t vstr;
            pyb_buf_get_for_recv(args[0].u_obj, &vstr);
        
            // just receive
            uint32_t write = args[1].u_int;
            pybspi_transfer(self, NULL, vstr.buf, vstr.len, &write);
        
            // return the received data
            return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
        }
        

        and:

        STATIC void pybspi_transfer (mach_spi_obj_t *self, const char *txdata, char *rxdata, uint32_t len, uint32_t *txchar) {
            if (!self->baudrate) {
                nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, mpexception_os_request_not_possible));
            }
            // send and receive the data
            for (int i = 0; i < len; i += self->wlen) {
                uint32_t _rxdata = 0;
                uint32_t _txdata;
                if (txdata) {
                    memcpy(&_txdata, &txdata[i], self->wlen);
                } else {
                    _txdata = 0x55555555;
                }
                spi_data_t spidata = {.cmd = 0, .cmdLen = 0, .addr = NULL, .addrLen = 0,
                                        .txData = &_txdata, .txDataLen = self->wlen,
                                        .rxData = &_rxdata, .rxDataLen = self->wlen};
        
                spi_master_send_recv_data(self->spi_num, &spidata);
                if (rxdata) {
                    memcpy(&rxdata[i], &_rxdata, self->wlen);
                }
            }
        }
        

        The upcoming v1.20 RC might have a different implementation that writes out whatever appears in the input buffer before updating it with whatever comes in -- although I'm not sure when "soft" SPI is used. See here:
        https://github.com/pycom/pycom-micropython-sigfox/blob/release-candidate/drivers/bus/softspi.c

        This might also be relevant:
        https://github.com/pycom/pycom-micropython-sigfox/blob/release-candidate/extmod/machine_spi.c

        robert-hhR 1 Reply Last reply Reply Quote 0
        • robert-hhR Offline
          robert-hh @throwaway
          last edited by robert-hh

          @throwaway Thanks for pointing at the interesting places. It looks like not all cases of spi_transfer had been considered. Maybe it should be:

                  if (txdata) {
                      memcpy(&_txdata, &txdata[i], self->wlen);
                  } else {
                      if (txchar) {
                          _txdata = *txchar;
                      } else {
                          _txdata = 0x55555555; // Just some dummy value
                      }
                  }
          

          Edit: Tested the change; it seems to work.
          Edit2: There is still a glitch: when setting bits=32, bit 31 of the word at write=xxxx must be 0, so the largest value is 0x7fffffff.
          Edit3: OK. Using negative numbers you can set bit31. Conversion of a hex pattern could be done with struct.unpack, like:
          spi.read(4, write=struct.unpack("<i", b'\x80\x80\x80\x80')[0])

          1 Reply Last reply Reply Quote 0
          • P Offline
            Paul Thornton
            last edited by

            Good diagnosis. Ill talk to the team and make sure they are aware of this and it get's looked into.

            1 Reply Last reply Reply Quote 0
            • P Offline
              PaulM
              last edited by

              This was the simple way I worked around it, until fixed in native API:

              def spiread(spiObj, nbytes, write=0x00):
                      wb = bytearray()
                      wb.extend(bytearray([write]*nbytes))
                      rb = bytearray(len(wb))
                      spiObj.write_readinto(wb,rb)
                      return rb
              
              T 1 Reply Last reply Reply Quote 0
              • T Offline
                throwaway @PaulM
                last edited by

                @PaulM Neat!

                I noticed that @robert-hh's patch accidently snuck into this PR: https://github.com/pycom/pycom-micropython-sigfox/pull/241/files

                robert-hhR 1 Reply Last reply Reply Quote 0
                • robert-hhR Offline
                  robert-hh @throwaway
                  last edited by robert-hh

                  @throwaway Actually, to commits to make it complete. The commit text mentions the analysis done by you, folks.
                  Accidentally, since it should have been a separate PR, but git merged it all together.

                  1 Reply Last reply Reply Quote 0
                  • P Offline
                    Paul Thornton
                    last edited by Paul Thornton

                    Just to follow up on this.

                    @robert-hh has done some wonderfull work. His fix has been merged into the next RC candidate. Thanks again @robert-hh, @paulM and anyone else who's been involved.

                    Edit: Additional thanks to @throwaway for his work in debugging this,

                    robert-hhR 1 Reply Last reply Reply Quote 0
                    • robert-hhR Offline
                      robert-hh @Paul Thornton
                      last edited by

                      @paul-thornton It should be mentioned that this was based on @throwaway's analysis of the place of failure. So kudos to him.

                      1 Reply Last reply Reply Quote 0
                      • N Offline
                        noviniumJeef
                        last edited by

                        Is there a fix to this? Im running the latest 1.18.2.r2 and im having this exact issue only the write_readinto() work around doesnt seem to work.

                        1 Reply Last reply Reply Quote 0

                        Hello! It looks like you're interested in this conversation, but you don't have an account yet.

                        Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

                        With your input, this post could be even better 💗

                        Register Login
                        • 1 / 1
                        • First post
                          Last post

                        Pycom on Twitter

                        Pycom