Giter VIP home page Giter VIP logo

Comments (5)

Gadgetoid avatar Gadgetoid commented on August 27, 2024 2

And, finally, the reason why this throws an error at all is that lsbfirst = True may be unsupported on the Raspberry Pi. If you need to reverse your bit order, you'll have to do it in software.

from py-spidev.

Gadgetoid avatar Gadgetoid commented on August 27, 2024

I'm a little late to the party, but I've just duplicated your test script and am getting exactly the same results.

Suffice to say, I'm a little baffled- BCM 23 has nothing to do with SPI. However, lsbfirst is implemented with setters/getters so it has side-effects.

Here's the setter:

static int
SpiDev_set_lsbfirst(SpiDevObject *self, PyObject *val, void *closure)
{
    uint8_t tmp;

    if (val == NULL) {
        PyErr_SetString(PyExc_TypeError,
            "Cannot delete attribute");
        return -1;
    }
    else if (!PyBool_Check(val)) {
        PyErr_SetString(PyExc_TypeError,
            "The lsbfirst attribute must be boolean");
        return -1;
    }

    if (val == Py_True)
        tmp = self->mode | SPI_LSB_FIRST;
    else
        tmp = self->mode & ~SPI_LSB_FIRST;

    __spidev_set_mode(self->fd, tmp);

    self->mode = tmp;
    return 0;
}

which calls:

static int __spidev_set_mode( int fd, __u8 mode) {
    __u8 test;
    if (ioctl(fd, SPI_IOC_WR_MODE, &mode) == -1) {
        PyErr_SetFromErrno(PyExc_IOError);
        return -1;
    }
    if (ioctl(fd, SPI_IOC_RD_MODE, &test) == -1) {
        PyErr_SetFromErrno(PyExc_IOError);
        return -1;
    }
    if (test != mode) {
        return -1;
    }
    return 0;
}

But why the SPI_IOC_WR_MODE or SPI_IOC_RD_MODE ioctls called on the SPI device should have any effect on GPIO is beyond me. This might be an issue to raise against RPi.GPIO too.

from py-spidev.

Gadgetoid avatar Gadgetoid commented on August 27, 2024

Okay, this is a good one!

Here's a puzzler for you. Modify your original code example to:

import time

import RPi.GPIO as GPIO
import spidev

spi = spidev.SpiDev()
spi.open(0, 0)
spi.lsbfirst = True

# Initialize GPIO23 as an output.
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)

try:
    GPIO.setup(23, GPIO.OUT)
except IOError:
    pass

GPIO.setup(23, GPIO.OUT)

# Set GPIO23 high and then low.
print 'High'
GPIO.output(23, GPIO.HIGH)
time.sleep(1)

print 'Low'
GPIO.output(23, GPIO.LOW)
time.sleep(1)

print 'Done!'

And see if you can figure out why that works :D

from py-spidev.

Gadgetoid avatar Gadgetoid commented on August 27, 2024

Welp! Down the rabbit hole I went. The issue is this;

  1. lsbfirst = True triggers set_lsbfirst
  2. This calls __spidev_set_mode
  3. __spidev_set_mode calls ioctl which fails with [Errno 22] Invalid Argument and passes this to Python with PyErr_SetFromErrno
  4. Back to set_lsbfirst which somehow exits without the previously set PyErr being raised
  5. Now we have an error buried in Python which hasn't been raised...
  6. Along comes RPi.GPIO... for some reason GPIO.setwarnings and GPIO.setmode ignore this buried error
  7. But now GPIO.setup(23, GPIO.OUT) digs up the error set by __spidev_set_mode and raises it... WHY!? I don't even...

Note about point 6. setwarnings bails with Py_RETURN_NONE so why it takes until setup for the error to be raised is anyone's guess. This stuff is bonkers!

I've rewritten a substantial portion of the code to check the return value of __spidev_set_mode and use Py_RETURN_NONE which seems to trigger error handling- whereas return NULL or return 0 do not.

This is odd, because according to https://docs.python.org/3/extending/extending.html#intermezzo-errors-and-exceptions return NULL should cause Python to choke and raise the exception, whereas Py_RETURN_NONE should do.. ?

With the setter and getter functions defined as static PyObject *. return NULL not only doesn't raise an exception set by PyErr_SetFromErrno but leaves that error latent in the interpreter:

Exceptions are stored in a static global variable inside the interpreter

For the next method that uses Py_RETURN_NONE to inadvertently raise.

D'oh =/

I've pushed these "fixes" (I'm not 100% sure what I'm doing is correct) into a new branch on my fork for some testing and peer overview before I submit a PR:

Gadgetoid@53b9695

from py-spidev.

Gadgetoid avatar Gadgetoid commented on August 27, 2024

After bashing my head against this problem for some time, I determined I'd made a mistake in changing the setters from static int to PyObject * in my original tweaks.

Despite the overwhelming majority of documentation pointing toward returning Py_NONE ( or a value ) upon success, and NULL upon error and using the PyObject type for functions that communicate with Python, a setter is a special little snowflake which, presumably, is called by some internal glue logic and must return 0 upon success, and -1 upon error.

This is alluded to only in its definition: typedef int (*setter)(PyObject *, PyObject *, void *); and I couldn't find any good examples or documentation for setters anywhere- it must be too generic a search term, and my search-fu has failed me.

Anyway, with my latest commit I have reverted my fudge and now only check __spidev_set_mode for a -1 return code, and bubble this up accordingly:

    if (__spidev_set_mode(self->fd, tmp) == -1){
        return -1;;
    }

As simple as that.

https://github.com/Gadgetoid/py-spidev/tree/ErrorHandling

from py-spidev.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.