|
| 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", ¶m, &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", ¶m)) |
| 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