Giter Club home page Giter Club logo

Comments (14)

pdp7 avatar pdp7 commented on July 21, 2024

I have added debug output to Adafruit_Blinka, Adafruit_CircuitPython_BusDevice, and py_spidev:

debian@beaglebone:~/Adafruit_CircuitPython_BME280$ sudo strace -f -o /tmp/strace.out python3 examples/bme280_simpletest.py 
[sudo] password for debian: 
spidev_module: SpiDev_init()
Adafruit_CircuitPython_BusDevice.__init__
Adafruit_CircuitPython_BusDevice.__init__: set self.chip_select = chip_select: P9_17
Adafruit_CircuitPython_BusDevice.__init__: set self.chip_select.value = True
Adafruit_CircuitPython_BusDevice.__init__: return
Adafruit_CircuitPython_BusDevice.__enter__
Adafruit_CircuitPython_BusDevice.__enter__: self.spi.configure()
Adafruit_CircuitPython_BusDevice.__enter__: set self.chip_select.value = False
Adafruit_CircuitPython_BusDevice.__enter__: return self.spi
generic_linux/spi.py: call self._spi.open(self._port, 0)
spidev_module: SpiDev_open(): enter
spidev_module: SpiDev_open(): call open(path, O_RDWR, 0)
spidev_module: SpiDev_open(): ioctl(self->fd, SPI_IOC_RD_MODE, &tmp8)
spidev_module: SpiDev_open(): ioctl(self->fd, SPI_IOC_RD_BITS_PER_WORD, &tmp8)
spidev_module: SpiDev_open(): ioctl(self->fd, SPI_IOC_RD_MAX_SPEED_HZ, &tmp32)
spidev_module: SpiDev_open(): return Py_None
generic_linux/spi.py: return from self._spi.open(self._port, 0)
generic_linux/spi.py: try: self._spi.no_cs
spidev_module: SpiDev_set_no_cs(): self->mode=0x1
spidev_module: SpiDev_set_no_cs(): SPI_NO_CS=0x40
spidev_module: SpiDev_set_no_cs(): set SPI_NO_CS bit
spidev_module: SpiDev_set_no_cs(): tmp=0x41
spidev_module: SpiDev_set_no_cs(): call __spidev_set_mode()
spidev_module: __spidev_set_mode(): fd=4
spidev_module: __spidev_set_mode(): mode=0x41
spidev_module: __spidev_set_mode(): call ioctl(fd, SPI_IOC_WR_MODE=0x40016b01, &mode)
spidev_module: __spidev_set_mode(): return -1
spidev_module: SpiDev_set_no_cs(): ret=-1
Traceback (most recent call last):
  File "examples/bme280_simpletest.py", line 17, in <module>
    bme280 = adafruit_bme280.Adafruit_BME280_SPI(spi, bme_cs)
  File "/usr/local/lib/python3.5/dist-packages/adafruit_bme280.py", line 469, in __init__
    super().__init__()
  File "/usr/local/lib/python3.5/dist-packages/adafruit_bme280.py", line 120, in __init__
    chip_id = self._read_byte(_BME280_REGISTER_CHIPID)
  File "/usr/local/lib/python3.5/dist-packages/adafruit_bme280.py", line 428, in _read_byte
    return self._read_register(register, 1)[0]
  File "/usr/local/lib/python3.5/dist-packages/adafruit_bme280.py", line 474, in _read_register
    spi.write(bytearray([register]))  #pylint: disable=no-member
  File "/usr/local/lib/python3.5/dist-packages/Adafruit_Blinka-1.2.9.dev23+g1d32b1f.d20190410-py3.5.egg/busio.py", line 129, in write
    return self._spi.write(buf, start, end)
  File "/usr/local/lib/python3.5/dist-packages/Adafruit_Blinka-1.2.9.dev23+g1d32b1f.d20190410-py3.5.egg/adafruit_blinka/microcontroller/generic_linux/spi.py", line 44, in write
    self._spi.no_cs = True  # this doesn't work but try anyways
OSError: [Errno 22] Invalid argument

The strace output shows that ioctl() call fails:

ioctl(4, SPI_IOC_WR_MODE, 0xbe81bb97) = -1 EINVAL (Invalid argument)

from adafruit_blinka.

pdp7 avatar pdp7 commented on July 21, 2024

How does Adafruit_CircuitPython_BME280 expect chip select (CS) to be handled?
https://github.com/adafruit/Adafruit_CircuitPython_BME280/blob/master/adafruit_bme280.py#L468

class Adafruit_BME280_SPI(Adafruit_BME280):
    """Driver for BME280 connected over SPI"""
    def __init__(self, spi, cs, baudrate=100000):
        import adafruit_bus_device.spi_device as spi_device
        self._spi = spi_device.SPIDevice(spi, cs, baudrate=baudrate)
        super().__init__()

adafruit_bus_device handles setting the CS pin:
https://github.com/adafruit/Adafruit_CircuitPython_BusDevice/blob/master/adafruit_bus_device/spi_device.py#L85

The SPI implementation in Adafruit_Blinka does NOT handle CS
https://github.com/adafruit/Adafruit_Blinka/blob/master/src/adafruit_blinka/microcontroller/generic_linux/spi.py

    def write(self, buf, start=0, end=None):
        if not buf:
            return
        if end is None:
            end = len(buf)
        try:
            self._spi.open(self._port, 0)
            try:
              self._spi.no_cs = True  # this doesn't work but try anyways
            except AttributeError:
              pass
            self._spi.max_speed_hz = self.baudrate
            self._spi.mode = self.mode
            self._spi.bits_per_word = self.bits
            self._spi.writebytes([x for x in buf[start:end]])
            self._spi.close()
        except FileNotFoundError as not_found:
            print("Could not open SPI device - check if SPI is enabled in kernel!")
            raise

from adafruit_blinka.

pdp7 avatar pdp7 commented on July 21, 2024

This line in adafruit_blinka/microcontroller/generic_linux/spi.py:

self._spi.no_cs = True  # this doesn't work but try anyways

causes the error:

  File "/usr/local/lib/python3.5/dist-packages/Adafruit_Blinka-1.2.9.dev23+g1d32b1f.d20190410-py3.5.egg/adafruit_blinka/microcontroller/generic_linux/spi.py", line 44, in write
    self._spi.no_cs = True  # this doesn't work but try anyways
OSError: [Errno 22] Invalid argument

debug output:

generic_linux/spi.py: return from self._spi.open(self._port, 0)
generic_linux/spi.py: try: self._spi.no_cs
spidev_module: SpiDev_set_no_cs(): self->mode=0x1
spidev_module: SpiDev_set_no_cs(): SPI_NO_CS=0x40
spidev_module: SpiDev_set_no_cs(): set SPI_NO_CS bit
spidev_module: SpiDev_set_no_cs(): tmp=0x41
spidev_module: SpiDev_set_no_cs(): call __spidev_set_mode()
spidev_module: __spidev_set_mode(): fd=4
spidev_module: __spidev_set_mode(): mode=0x41
spidev_module: __spidev_set_mode(): call ioctl(fd, SPI_IOC_WR_MODE=0x40016b01, &mode)
spidev_module: __spidev_set_mode(): return -1
spidev_module: SpiDev_set_no_cs(): ret=-1

What is happening in self._spi.no_cs = True?

It invokes this code in spidev_module.c:

	{"no_cs", (getter)SpiDev_get_no_cs, (setter)SpiDev_set_no_cs,
			"disable chip select\n"},

which calls:

SpiDev_set_no_cs(SpiDevObject *self, PyObject *val, void *closure)

which calls:

static int __spidev_set_mode( int fd, __u8 mode)

this is where the ioctl() that fails is called:

	if (ioctl(fd, SPI_IOC_WR_MODE, &mode) == -1) {

This ioctl() is trying to set the SPI mode with the NO_CS bit set.

from adafruit_blinka.

pdp7 avatar pdp7 commented on July 21, 2024

How does the Linux kernel handle SPI_NO_CS?

Here are the references to SPI_NO_CS in Linux kernel source code:
https://elixir.bootlin.com/linux/v4.19/ident/SPI_NO_CS

It is defined in:
https://elixir.bootlin.com/linux/v4.19/source/include/linux/spi/spi.h#L160

#define	SPI_NO_CS	0x40			/* 1 dev/bus, no chipselect */

The Raspberry Pi (BCM2835 processor) seems to support SPI_NO_CS:
https://elixir.bootlin.com/linux/v4.19/source/drivers/spi/spi-bcm2835.c#L79

#define BCM2835_SPI_MODE_BITS	(SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \
				| SPI_NO_CS | SPI_3WIRE)

from adafruit_blinka.

pdp7 avatar pdp7 commented on July 21, 2024

SPI driver for BeagleBone does not support for SPI_NO_CS
The BeagleBone has TI Sitara AM3358 SoC. The support in the Linux kernel is often referred to as OMAP given the previous TI SoC family.

SPI on the AM3358 is called McSPI and is implemented in:
https://elixir.bootlin.com/linux/v4.14/source/drivers/spi/spi-omap2-mcspi.c

The allowed mode bits are:
https://elixir.bootlin.com/linux/v4.14/source/drivers/spi/spi-omap2-mcspi.c#L1351

master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;

The allowed mode bits for the SPI controller (spi->controller->mode_bits) are checked in:
https://elixir.bootlin.com/linux/v4.19/source/drivers/spi/spi.c#L2787

int spi_setup(struct spi_device *spi)
{
       /*snip*/
	bad_bits = spi->mode & ~spi->controller->mode_bits;
	ugly_bits = bad_bits &
		    (SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD);
	if (ugly_bits) {
		dev_warn(&spi->dev,
			 "setup: ignoring unsupported mode bits %x\n",
			 ugly_bits);
		spi->mode &= ~ugly_bits;
		bad_bits &= ~ugly_bits;
	}
	if (bad_bits) {
		dev_err(&spi->dev, "setup: unsupported mode bits %x\n",
			bad_bits);
		return -EINVAL;
	}

In the case of the BeagleBone, the omap2-mcspi driver does not support SPI_NO_CS so spi_setup() returns -EINVAL which translates to -22. This is why Python has the exception: OSError: [Errno 22] Invalid argument

I recompiled the kernel on the BeagleBone with some print statements which confirms that setting SPI_NO_CS (0x40) causes bad_bits to be non-zero and spi_setup() to return -EINVAL:

[  129.472021] DEBUG: spidev.c: SPI_IOC_WR_MODE
[  129.476873] DEBUG: spidev.c: SPI_IOC_WR_MODE: save=0x1
[  129.484114] DEBUG: spidev.c: SPI_IOC_WR_MODE: tmp=0x41
[  129.490318] DEBUG: spidev.c: SPI_IOC_WR_MODE: SPI_MODE_MASK=0xfff
[  129.497454] DEBUG: spidev.c: SPI_IOC_WR_MODE: ~SPI_MODE_MASK=0xfffff000
[  129.505120] DEBUG: spidev.c: SPI_IOC_WR_MODE: (tmp&~SPI_MODE_MASK)=0x0
[  129.512642] DEBUG: spi.c: spi_setup(): spi->mode=0x41
[  129.518549] DEBUG: spi.c: spi_setup(): spi->controller->mode_bits=0x7
[  129.526020] DEBUG: spi.c: spi_setup(): ~spi->controller->mode_bits=0xfffffff8
[  129.534085] DEBUG: spi.c: spi_setup(): bad_bits=0x40
[  129.539908] DEBUG: spi.c: spi_setup(): ugly_bits=0x0
[  129.545715] DEBUG: spi.c: spi_setup(): unsupported mode bits, return -EINVAL

from adafruit_blinka.

ladyada avatar ladyada commented on July 21, 2024

hihi yeah so there are situations where you want to use the SPI bus w/o a CS pin (dotstars for example) so what we do is we have one 'dummy' CS pin that we always toggle but never connect to. Then we use any plain gpio pin for CS. this lets us have any CS pin, and any config of SPI, at the loss of a GPIO

from adafruit_blinka.

pdp7 avatar pdp7 commented on July 21, 2024

@ladyada thanks, yes, I can see why it is useful to decouple CS from the hardware SPI peripheral.

As a test, I just commented out all instances of self._spi.no_cs = True in src/adafruit_blinka/microcontroller/generic_linux/spi.py and the BME280 example runs OK on the BeagleBone:

debian@beaglebone:~/Adafruit_Blinka$ sudo python3 ~/Adafruit_CircuitPython_BME280/examples/bme280_simpletest.py 2>/dev/null |grep -v generic_linux |grep -v Adafruit_CircuitPython

Temperature: 23.7 C
Humidity: 39.7 %
Pressure: 1020.0 hPa
Altitude = -56.05 meters

This is because spidev no longer tries to set the SPI_NO_CS mode bit which is unsupported by the Linux driver (omap2_mcspi) for BeagleBone's SPI controller (AM3358 McSPI).

I think a work around maybe to add logic to skip setting no CS if AM3358 based board. I'll try that out and followup.

from adafruit_blinka.

ladyada avatar ladyada commented on July 21, 2024

please do a try-except

from adafruit_blinka.

ladyada avatar ladyada commented on July 21, 2024

fyi dont commit directly to this repo, have PRs only that i'll review. thanks :)

from adafruit_blinka.

pdp7 avatar pdp7 commented on July 21, 2024

OK, no problem. I will create PR when I have a working code.

For src/adafruit_blinka/microcontroller/generic_linux/spi.py,
I am thinking of replacing instances of:

            try:
              self._spi.no_cs = True  # this doesn't work but try anyways
            except AttributeError:
              pass

with a call to to a new method set_no_cs():

    def set_no_cs(self):
        print("generic_linux/spi.py: set_no_cs(): enter")
        if detector.chip.AM33XX:
            print("generic_linux/spi.py: set_no_cs(): detected AM33XX, SKIP setting no_cs")
        else:
            try:
                print("generic_linux/spi.py: set_no_cs(): self._spi.no_cs = True")
                self._spi.no_cs = True  # this doesn't work but try anyways
            except AttributeError:
                pass

I just tested this out and the BME280 works OK on the BeagleBone. However, I think that calling detector.chip.AM33XX in set_no_cs() may be inefficient, so it might need to move to init().

from adafruit_blinka.

ladyada avatar ladyada commented on July 21, 2024

why not add an except for OSError?

from adafruit_blinka.

pdp7 avatar pdp7 commented on July 21, 2024

OSError can occur for any failed ioctl(). I think a better approach is avoid trying to set SPI_NO_CS mode bit when the chip is AM3358 (as this will always fail due to the Linux driver not supporting it).

from adafruit_blinka.

ladyada avatar ladyada commented on July 21, 2024

ok got it

from adafruit_blinka.

pdp7 avatar pdp7 commented on July 21, 2024

Tested OK with BME280 on PocketBeagle.

Make sure pins are configured for SPI0 plus P1.6 as GPIO for CS:

config-pin p1.6  gpio     # CS
config-pin p1.8  spi_sclk # SPI0 CLK
config-pin p1.10 spi     # SPI0 MISO
config-pin p1.12 spi     # SPI0 MOSI

Adafruit_CircuitPython_BME280/examples/bme280_simpletest.py:

import board
import busio
import adafruit_bme280
import digitalio

spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
bme_cs = digitalio.DigitalInOut(board.P1_6)
bme280 = adafruit_bme280.Adafruit_BME280_SPI(spi, bme_cs)

print("\nTemperature: %0.1f C" % bme280.temperature)
debian@beaglebone:~$ python3 bme280_simpletest.py

Temperature: 22.0 C

system version info:

debian@beaglebone:~$ cat /etc/dogtag 
BeagleBoard.org Debian Image 2019-03-03
debian@beaglebone:~$ uname -r
4.14.78-bone17
debian@beaglebone:~$ cat /etc/debian_version 
9.8
debian@beaglebone:~$ ls -ltar /dev/spidev*
crw-rw---- 1 root spi 153, 0 Apr 12 17:41 /dev/spidev0.0
crw-rw---- 1 root spi 153, 1 Apr 12 17:42 /dev/spidev1.0
crw-rw---- 1 root spi 153, 2 Apr 12 17:42 /dev/spidev1.1

from adafruit_blinka.

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.