Skip to content

Commit 2624d10

Browse files
committed
add spi, fork from SPI-Py written by Louis Thiery, add auto pin mode set
1 parent 7429845 commit 2624d10

1 file changed

Lines changed: 249 additions & 0 deletions

File tree

‎pcduino/spi.c‎

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
/* SPI testing utility (see copyright beow)
2+
* adapted for use in Python
3+
* by Louis Thiery
4+
* Lots more flexibility and cleanup by Connor Wolf (imaginaryindustries.com)
5+
*
6+
* compile for Python using: "python setup.py build"
7+
* compiled module will be in "./build/lib.linux-armv6l-2.7/spi.so"
8+
*
9+
* SPI testing utility (using spidev driver)
10+
*
11+
* Copyright (c) 2007 MontaVista Software, Inc.
12+
* Copyright (c) 2007 Anton Vorontsov <[email protected]>
13+
*
14+
* This program is free software; you can redistribute it and/or modify
15+
* it under the terms of the GNU General Public License as published by
16+
* the Free Software Foundation; either version 2 of the License.
17+
*
18+
*/
19+
20+
#include <Python.h>
21+
#include <stdint.h>
22+
#include <unistd.h>
23+
#include <stdio.h>
24+
#include <stdlib.h>
25+
#include <getopt.h>
26+
#include <fcntl.h>
27+
#include <sys/ioctl.h>
28+
#include <linux/types.h>
29+
#include <linux/spi/spidev.h>
30+
31+
static void pabort(const char *s)
32+
{
33+
perror(s);
34+
abort();
35+
}
36+
37+
static const char *device = "/dev/spidev0.0";
38+
static uint8_t mode;
39+
static uint8_t bits = 8;
40+
static uint32_t speed = 500000;
41+
static uint16_t delay;
42+
43+
int ret = 0;
44+
int fd;
45+
46+
47+
static PyObject* openSPI(PyObject *self, PyObject *args, PyObject *kwargs)
48+
{
49+
50+
static char* kwlist[] = {"device", "mode", "bits", "speed", "delay", NULL};
51+
52+
// 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
53+
// stuffed into an int.
54+
// 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
55+
//
56+
57+
58+
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|siiii:keywords", kwlist, &device, &mode, &bits, &speed, &delay))
59+
return NULL;
60+
// It's not clearly documented, but it seems that PyArg_ParseTupleAndKeywords basically only modifies the values passed to it if the
61+
// keyword pertaining to that value is passed to the function. As such, the defaults specified by the variable definition are used
62+
// unless you pass a kwd argument.
63+
// 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
64+
// being stuffed into the avasilable space. For example, passing a bits-per-word of 500 gets truncated to 244. Unfortunately, the
65+
// PyArg_ParseTupleAndKeywords function only seems to support ints of 32 bits.
66+
67+
PyErr_Clear();
68+
69+
// printf("Mode: %i, Bits: %i, Speed: %i, Delay: %i\n", mode, bits, speed, delay);
70+
71+
72+
73+
fd = open(device, O_RDWR);
74+
if (fd < 0)
75+
pabort("can't open device");
76+
77+
/*
78+
* Setup SPI mode
79+
*/
80+
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
81+
if (ret == -1)
82+
pabort("can't set spi mode");
83+
84+
ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
85+
if (ret == -1)
86+
pabort("can't get spi mode");
87+
88+
/*
89+
* bits per word
90+
*/
91+
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
92+
if (ret == -1)
93+
pabort("can't set bits per word");
94+
95+
ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
96+
if (ret == -1)
97+
pabort("can't get bits per word");
98+
99+
/*
100+
* max speed hz
101+
*/
102+
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
103+
if (ret == -1)
104+
pabort("can't set max speed hz");
105+
106+
ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
107+
if (ret == -1)
108+
pabort("can't get max speed hz");
109+
110+
// Stuff the various initilization parameters into a dict, and return that.
111+
// Note that the returned values may not be completely real. It seems that, at least for the speed value,
112+
// the hardware only has several possible settings (250000, 500000, 1000000, etc...) Strangely enough, the
113+
// ioctl for setting the speed *returns the speed you specify*. However, the hardware seems to default to the
114+
// closest avalable value *below* the specified rate. (i.e. you will never get a speed faster then you spec),
115+
// but you may get a slower value.
116+
117+
//It would probably be a good idea to bin-down the passed arguement to the available values, and return
118+
// that.
119+
120+
PyObject* retDict;
121+
retDict = PyDict_New();
122+
123+
#if PY_MAJOR_VERSION >= 3
124+
PyDict_SetItem(retDict, PyBytes_FromString("mode"), PyLong_FromLong((long)mode));
125+
PyDict_SetItem(retDict, PyBytes_FromString("bits"), PyLong_FromLong((long)bits));
126+
PyDict_SetItem(retDict, PyBytes_FromString("speed"), PyLong_FromLong((long)speed));
127+
PyDict_SetItem(retDict, PyBytes_FromString("delay"), PyLong_FromLong((long)delay));
128+
#else
129+
PyDict_SetItem(retDict, PyString_FromString("mode"), PyInt_FromLong((long)mode));
130+
PyDict_SetItem(retDict, PyString_FromString("bits"), PyInt_FromLong((long)bits));
131+
PyDict_SetItem(retDict, PyString_FromString("speed"), PyInt_FromLong((long)speed));
132+
PyDict_SetItem(retDict, PyString_FromString("delay"), PyInt_FromLong((long)delay));
133+
#endif
134+
135+
136+
return retDict;
137+
}
138+
139+
140+
141+
static PyObject* transfer(PyObject* self, PyObject* arg)
142+
{
143+
PyObject* transferTuple;
144+
145+
if(!PyArg_ParseTuple(arg, "O", &transferTuple)) // "O" - Gets non-NULL borrowed reference to Python argument.
146+
return NULL; // As far as I can tell, it's mostly just copying arg[0] into transferTuple
147+
// and making sure at least one arg has been passed (I think)
148+
149+
if(!PyTuple_Check(transferTuple)) // The only argument we support is a single tuple.
150+
pabort("Only accepts a single tuple as an argument\n");
151+
152+
153+
uint32_t tupleSize = PyTuple_Size(transferTuple);
154+
155+
uint8_t tx[tupleSize];
156+
uint8_t rx[tupleSize];
157+
PyObject* tempItem;
158+
159+
uint16_t i=0;
160+
161+
while(i < tupleSize)
162+
{
163+
tempItem = PyTuple_GetItem(transferTuple, i); //
164+
#if PY_MAJOR_VERSION >= 3
165+
if(!PyLong_Check(tempItem))
166+
#else
167+
if(!PyInt_Check(tempItem))
168+
#endif
169+
{
170+
pabort("non-integer contained in tuple\n");
171+
}
172+
#if PY_MAJOR_VERSION >= 3
173+
tx[i] = (uint8_t)PyLong_AsSsize_t(tempItem);
174+
#else
175+
tx[i] = (uint8_t)PyInt_AsSsize_t(tempItem);
176+
#endif
177+
178+
i++;
179+
180+
}
181+
182+
struct spi_ioc_transfer tr = {
183+
.tx_buf = (unsigned long)tx,
184+
.rx_buf = (unsigned long)rx,
185+
.len = tupleSize,
186+
.delay_usecs = delay,
187+
.speed_hz = speed,
188+
.bits_per_word = bits,
189+
.cs_change = 1,
190+
};
191+
192+
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
193+
if (ret < 1)
194+
pabort("can't send spi message");
195+
196+
transferTuple = PyTuple_New(tupleSize);
197+
for(i=0;i<tupleSize;i++)
198+
PyTuple_SetItem(transferTuple, i, Py_BuildValue("i",rx[i]));
199+
200+
return transferTuple;
201+
}
202+
203+
204+
static PyObject* closeSPI(PyObject* self,PyObject* args)
205+
{
206+
close(fd);
207+
Py_RETURN_NONE;
208+
}
209+
210+
static PyMethodDef SpiMethods[] =
211+
{
212+
{"openSPI", (PyCFunction)openSPI, METH_VARARGS | METH_KEYWORDS, "Open SPI Port."},
213+
{"transfer", (PyCFunction)transfer, METH_VARARGS, "Transfer data."},
214+
{"closeSPI", (PyCFunction)closeSPI, METH_NOARGS, "Close SPI port."},
215+
{NULL, NULL, 0, NULL}
216+
};
217+
218+
#if PY_MAJOR_VERSION >= 3
219+
static struct PyModuleDef moduledef = {
220+
PyModuleDef_HEAD_INIT,
221+
"spi", /* m_name */
222+
"spi library", /* m_doc */
223+
-1, /* m_size */
224+
SpiMethods, /* m_methods */
225+
NULL, /* m_reload */
226+
NULL, /* m_traverse */
227+
NULL, /* m_clear */
228+
NULL, /* m_free */
229+
};
230+
#endif
231+
232+
PyMODINIT_FUNC
233+
234+
#if PY_MAJOR_VERSION >= 3
235+
PyInit_spi(void)
236+
#else
237+
initspi(void)
238+
#endif
239+
{
240+
#if PY_MAJOR_VERSION >= 3
241+
PyObject *module = PyModule_Create(&moduledef);
242+
#else
243+
(void) Py_InitModule("spi", SpiMethods);
244+
#endif
245+
246+
#if PY_MAJOR_VERSION >= 3
247+
return module;
248+
#endif
249+
}

0 commit comments

Comments
 (0)