Comments (14)
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.
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.
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.
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.
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.
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.
@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.
please do a try-except
from adafruit_blinka.
fyi dont commit directly to this repo, have PRs only that i'll review. thanks :)
from adafruit_blinka.
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.
why not add an except for OSError?
from adafruit_blinka.
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.
ok got it
from adafruit_blinka.
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)
- New issue HOT 1
- Jetson Nano -- AttributeError: module 'adafruit_platformdetect.constants.chips' has no attribute 'JH71x0' HOT 2
- FT232H not being recognized in PowerShell HOT 4
- Issue with APDS9960 sensor on Raspberry Pi 3 Model B+ HOT 1
- Add board: BOARD=orangepi3-lts HOT 1
- ADD BANANAPI CM4 BOARD
- ADD BANANAPI CM4 BOARD SUPPORT
- DietPi - Error installing Adafruit-Blinka HOT 1
- any check scripts that should be performed before issuing a PR for adding a new board
- Adafruit-PlatformDetect version 3.66.0 was unable to identify the board HOT 3
- Cant find the board HOT 5
- Some microcontroller definitions may be incorrect.
- libgpiod_pin_1_x.py does not detect internal pulldowns HOT 1
- GT911 touch sensor support for Adafruit Blinka HOT 7
- adafruit-circuitpython-motor fails with "RuntimeError: Cannot determine SOC peripheral base address" on Raspberry Pi 5 HOT 12
- Adafruit-Adafruit-PlatformDetect version 3.69.0 was unable to identify the board HOT 13
- Adafruit-PlatformDetect version 3.69.0 was unable to identify the board HOT 2
- support for NXP i.MX 8M PLUS HOT 3
- pip try to install sysv-ipc on Windows HOT 26
- It is no longer possible to install from 8.44.0. HOT 4
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from adafruit_blinka.