Switch branches/tags
Nothing to show
Find file
2c6cf5c Sep 6, 2016
@lthiery @fake-name @pelwell @blark @franckinux
250 lines (199 sloc) 7.09 KB
/* SPI testing utility (see copyright beow)
* adapted for use in Python
* by Louis Thiery
* Lots more flexibility and cleanup by Connor Wolf (
* compile for Python using: "python build"
* compiled module will be in "./build/lib.linux-armv6l-2.7/"
* SPI testing utility (using spidev driver)
* Copyright (c) 2007 MontaVista Software, Inc.
* Copyright (c) 2007 Anton Vorontsov <>
* 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)
static const char *device = "/dev/spidev0.0";
static uint8_t mode;
static uint8_t bits = 8;
static uint32_t speed = 500000;
static uint16_t delay;
int ret = 0;
int fd;
static PyObject* openSPI(PyObject *self, PyObject *args, PyObject *kwargs)
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.
// printf("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");
* 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();
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));
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));
return retDict;
static PyObject* transfer(PyObject* self, PyObject* arg)
PyObject* transferTuple;
if(!PyArg_ParseTuple(arg, "O", &transferTuple)) // "O" - Gets non-NULL borrowed reference to Python argument.
return NULL; // 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(!PyTuple_Check(transferTuple)) // The only argument we support is a single tuple.
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); //
pabort("non-integer contained in tuple\n");
tx[i] = (uint8_t)PyLong_AsSsize_t(tempItem);
tx[i] = (uint8_t)PyInt_AsSsize_t(tempItem);
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 = 0,
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1)
pabort("can't send spi message");
transferTuple = PyTuple_New(tupleSize);
PyTuple_SetItem(transferTuple, i, Py_BuildValue("i",rx[i]));
return transferTuple;
static PyObject* closeSPI(PyObject* self,PyObject* args)
static PyMethodDef SpiMethods[] =
{"openSPI", (PyCFunction)openSPI, METH_VARARGS | METH_KEYWORDS, "Open SPI Port."},
{"transfer", (PyCFunction)transfer, METH_VARARGS, "Transfer data."},
{"closeSPI", (PyCFunction)closeSPI, METH_NOARGS, "Close SPI port."},
static struct PyModuleDef moduledef = {
"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 */
PyObject *module = PyModule_Create(&moduledef);
(void) Py_InitModule("spi", SpiMethods);
return module;