Skip to content

Commit c6c5f66

Browse files
committed
add more future for spi
1 parent 4a781ae commit c6c5f66

1 file changed

Lines changed: 291 additions & 0 deletions

File tree

‎pcduino/spi_multi.c‎

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

0 commit comments

Comments
 (0)