Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accessing two SPI devices #11

Open
msedv opened this issue Oct 9, 2015 · 5 comments
Open

Accessing two SPI devices #11

msedv opened this issue Oct 9, 2015 · 5 comments

Comments

@msedv
Copy link
Contributor

msedv commented Oct 9, 2015

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?!

@msedv
Copy link
Contributor Author

msedv commented Oct 9, 2015

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 <avorontsov@ru.mvista.com>
 *
 * 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
}

@lthiery
Copy link
Owner

lthiery commented Oct 10, 2015

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?

@msedv
Copy link
Contributor Author

msedv commented Oct 10, 2015

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

@boconn7782
Copy link

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?

@magneticlab-ch
Copy link

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 ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants