Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
branch: master
209 lines (160 sloc) 6.036 kb
/* 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 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.
PyErr_Clear();
// 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, 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); //
if(!PyInt_Check(tempItem))
{
pabort("non-integer contained in tuple\n");
}
tx[i] = (uint8_t)PyInt_AsSsize_t(tempItem);
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* args)
{
close(fd);
Py_RETURN_NONE;
}
static PyMethodDef SpiMethods[] =
{
{"openSPI", openSPI, METH_KEYWORDS, "Open SPI Port."},
{"transfer", transfer, METH_VARARGS, "Transfer data."},
{"closeSPI", closeSPI, METH_NOARGS, "Close SPI port."},
{NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC
initspi(void)
{
(void) Py_InitModule("spi", SpiMethods);
}
Jump to Line
Something went wrong with that request. Please try again.