SPI-Py
SPI-Py copied to clipboard
Accessing two SPI devices
As long as I'm only accessing ONE SPI device everything is fine, but when accessing TWO (in my case: MFRC522.py for two RFID-Readers) only one of them works. Looking at your code I think the problem is that
int fd;
is declared globally?!
If anyone is interested - here my version in which openSPI returns the file descriptor which then must be given to transfer and closeSPI. A little bit of a hack but works like a charm:
/* SPI testing utility (see copyright beow)
* adapted for use in Python
* by Louis Thiery
* Lots more flexibility and cleanup by Connor Wolf (imaginaryindustries.com)
*
* compile for Python using: "python setup.py build"
* compiled module will be in "./build/lib.linux-armv6l-2.7/spi.so"
*
* SPI testing utility (using spidev driver)
*
* Copyright (c) 2007 MontaVista Software, Inc.
* Copyright (c) 2007 Anton Vorontsov <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*
*/
#include <Python.h>
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
static void pabort(const char *s)
{
perror(s);
abort();
}
static uint8_t mode;
static uint8_t bits = 8;
static uint32_t speed = 500000;
static uint16_t delay;
static PyObject* openSPI(PyObject *self, PyObject *args, PyObject *kwargs) {
int ret = 0;
int fd;
static const char *device = "/dev/spidev0.0";
static char* kwlist[] = {"device", "mode", "bits", "speed", "delay", NULL};
// Adding some sort of mode parsing would probably be a nice idea for the future, so you don't have to specify it as a bitfield
// stuffed into an int.
// For the moment the default mode ("0"), will probably work for 99% of people who need a SPI interface, so I'm not working on that
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|siiii:keywords", kwlist, &device, &mode, &bits, &speed, &delay))
return NULL;
// It's not clearly documented, but it seems that PyArg_ParseTupleAndKeywords basically only modifies the values passed to it if the
// keyword pertaining to that value is passed to the function. As such, the defaults specified by the variable definition are used
// unless you pass a kwd argument.
// Note that there isn't any proper bounds-checking, so if you pass a value that exceeds the variable size, it's just truncated before
// being stuffed into the avasilable space. For example, passing a bits-per-word of 500 gets truncated to 244. Unfortunately, the
// PyArg_ParseTupleAndKeywords function only seems to support ints of 32 bits.
PyErr_Clear();
// printf("*** SPI.C openSPI: Mode: %i, Bits: %i, Speed: %i, Delay: %i\n", mode, bits, speed, delay);
fd = open(device, O_RDWR);
if (fd < 0)
pabort("can't open device");
// printf("*** SPI.C openSPI: fd: %i\n", fd);
/*
* Setup SPI mode
*/
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
if (ret == -1)
pabort("can't set spi mode");
ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
if (ret == -1)
pabort("can't get spi mode");
/*
* bits per word
*/
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
if (ret == -1)
pabort("can't set bits per word");
ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
if (ret == -1)
pabort("can't get bits per word");
/*
* max speed hz
*/
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -1)
pabort("can't set max speed hz");
ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
if (ret == -1)
pabort("can't get max speed hz");
// Stuff the various initilization parameters into a dict, and return that.
// Note that the returned values may not be completely real. It seems that, at least for the speed value,
// the hardware only has several possible settings (250000, 500000, 1000000, etc...) Strangely enough, the
// ioctl for setting the speed *returns the speed you specify*. However, the hardware seems to default to the
// closest avalable value *below* the specified rate. (i.e. you will never get a speed faster then you spec),
// but you may get a slower value.
//It would probably be a good idea to bin-down the passed arguement to the available values, and return
// that.
PyObject* retDict;
retDict = PyDict_New();
#if PY_MAJOR_VERSION >= 3
PyDict_SetItem(retDict, PyBytes_FromString("fd"), PyLong_FromLong((long)fd));
PyDict_SetItem(retDict, PyBytes_FromString("mode"), PyLong_FromLong((long)mode));
PyDict_SetItem(retDict, PyBytes_FromString("bits"), PyLong_FromLong((long)bits));
PyDict_SetItem(retDict, PyBytes_FromString("speed"), PyLong_FromLong((long)speed));
PyDict_SetItem(retDict, PyBytes_FromString("delay"), PyLong_FromLong((long)delay));
#else
PyDict_SetItem(retDict, PyString_FromString("fd"), PyInt_FromLong((long)fd));
PyDict_SetItem(retDict, PyString_FromString("mode"), PyInt_FromLong((long)mode));
PyDict_SetItem(retDict, PyString_FromString("bits"), PyInt_FromLong((long)bits));
PyDict_SetItem(retDict, PyString_FromString("speed"), PyInt_FromLong((long)speed));
PyDict_SetItem(retDict, PyString_FromString("delay"), PyInt_FromLong((long)delay));
#endif
return retDict;
}
static PyObject* transfer(PyObject* self, PyObject* arg) {
int ret = 0;
int fd;
PyObject* transferTuple;
// "O" - Gets non-NULL borrowed reference to Python argument.
// As far as I can tell, it's mostly just copying arg[0] into transferTuple
// and making sure at least one arg has been passed (I think)
if(!PyArg_ParseTuple(arg, "iO", &fd, &transferTuple))
return NULL;
// printf("*** SPI.C transfer: fd: %i\n", fd);
// The only argument we support is a single tuple.
if(!PyTuple_Check(transferTuple))
pabort("Only accepts a single tuple as an argument\n");
uint32_t tupleSize = PyTuple_Size(transferTuple);
uint8_t tx[tupleSize];
uint8_t rx[tupleSize];
PyObject* tempItem;
uint16_t i=0;
while(i < tupleSize)
{
tempItem = PyTuple_GetItem(transferTuple, i); //
#if PY_MAJOR_VERSION >= 3
if(!PyLong_Check(tempItem))
#else
if(!PyInt_Check(tempItem))
#endif
{
pabort("non-integer contained in tuple\n");
}
#if PY_MAJOR_VERSION >= 3
tx[i] = (uint8_t)PyLong_AsSsize_t(tempItem);
#else
tx[i] = (uint8_t)PyInt_AsSsize_t(tempItem);
#endif
i++;
}
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = tupleSize,
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
.cs_change = 1,
};
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1)
pabort("can't send spi message");
transferTuple = PyTuple_New(tupleSize);
for (i = 0; i < tupleSize; i++)
PyTuple_SetItem(transferTuple, i, Py_BuildValue("i",rx[i]));
return transferTuple;
}
static PyObject* closeSPI (PyObject* self, PyObject* arg) {
int fd;
if(!PyArg_ParseTuple(arg, "i", &fd))
return NULL;
// printf ("*** SPI.C closeSPI: fd: %i\n", fd);
PyErr_Clear();
close (fd);
Py_RETURN_NONE;
}
static PyMethodDef SpiMethods[] = {
{"openSPI", (PyCFunction)openSPI, METH_VARARGS | METH_KEYWORDS, "Open SPI Port."},
{"transfer", (PyCFunction)transfer, METH_VARARGS, "Transfer data."},
{"closeSPI", (PyCFunction)closeSPI, METH_VARARGS, "Close SPI port."},
{NULL, NULL, 0, NULL}
};
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"spi", /* m_name */
"spi library", /* m_doc */
-1, /* m_size */
SpiMethods, /* m_methods */
NULL, /* m_reload */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
};
#endif
PyMODINIT_FUNC
#if PY_MAJOR_VERSION >= 3
PyInit_spi(void)
#else
initspi(void)
#endif
{
#if PY_MAJOR_VERSION >= 3
PyObject *module = PyModule_Create(&moduledef);
#else
(void) Py_InitModule("spi", SpiMethods);
#endif
;
#if PY_MAJOR_VERSION >= 3
return module;
#endif
}
do you know how to make a pull request out of this? that way i can look at the diffs more easily and pull it in potentially?
Puh, I'm not very experienced with github.... (: I tried my best, but since I have no write access to the project github forced my to make a fork which is here: https://github.com/msedv/SPI-Py
So I'm just starting to look into this but I want to use the RC522 and a touchscreen display (https://www.adafruit.com/products/2441). The touch screen display uses GPIO25 already. I know that's a general purpose I/O pin so I should be able to change the connection for the RC522's rst pin to another one of the general purpose I/O pins. I am just unsure where in the code to do this or if there's some reason I am not aware of that i cannot. Any help?
Hi, I try to use my RPi3 and the RC522 and a touchscreen display aswell so i need to put my RC522 on the auxiliary SPI1 right ? How can I do it ? The actual wires are :
SS --> PIN26 CE1 (BCM7)
SCLK --> PIN40 (BCM21)
MOSI --> PIN38 (BCM20)
MISO --> PIN35 (BCM19)
IRQ --> not connected
GND --> GND
RST --> PIN37 (BCM26)
VCC --> 3.3v
using this as the reference layout : http://pinout.xyz/ My question is how to change the code in order to get this RC522 to work on SP1 ?