417 lines
11 KiB
C
417 lines
11 KiB
C
|
|
#include "Python.h"
|
|
#include "structmember.h"
|
|
|
|
#ifdef _RASPBERRY_PI3_
|
|
#include <wiringPi.h>
|
|
#else
|
|
#include "wiringPi.h"
|
|
#endif
|
|
|
|
#define SPI_SPEED_US 100
|
|
|
|
// class Bpsspi
|
|
|
|
typedef struct {
|
|
PyObject_HEAD;
|
|
|
|
unsigned int cpol;
|
|
unsigned int cpha;
|
|
|
|
unsigned int clk;
|
|
unsigned int mosi;
|
|
int miso;
|
|
unsigned int cs_sz;
|
|
unsigned int *cs;
|
|
|
|
} Bpsspi;
|
|
|
|
// Bpsspi __init__
|
|
static PyObject *Bpsspi_new(PyTypeObject *type, PyObject *args, PyObject *keywords);
|
|
static int Bpsspi_init(Bpsspi *self, PyObject *args, PyObject *kwargs);
|
|
static void Bpsspi_dealloc(Bpsspi *self);
|
|
|
|
// member
|
|
static PyMemberDef BpsspiMembers[] = { //
|
|
{"cpol", T_UINT, offsetof(Bpsspi, cpol), READONLY, "polarity"},
|
|
{"cpha", T_UINT, offsetof(Bpsspi, cpha), READONLY, "phase"},
|
|
{"clk", T_UINT, offsetof(Bpsspi, clk), READONLY, "clock pin"},
|
|
{"mosi", T_UINT, offsetof(Bpsspi, mosi), READONLY, "mosi pin"},
|
|
{"miso", T_INT, offsetof(Bpsspi, miso), READONLY, "miso pin"},
|
|
{"cs_sz", T_UINT, offsetof(Bpsspi, cs_sz), READONLY, "chip selection pins number"},
|
|
{NULL}};
|
|
|
|
// methods
|
|
|
|
static PyObject *Bpsspi_reset(Bpsspi *self);
|
|
static PyObject *Bpsspi_set_cs(Bpsspi *self, PyObject *args, PyObject *kwargs);
|
|
static PyObject *Bpsspi_send_byte_transmit(Bpsspi *self, PyObject *args, PyObject *kwargs);
|
|
static PyObject *Bpsspi_send_byte_exchange(Bpsspi *self, PyObject *args, PyObject *kwargs);
|
|
|
|
static PyMethodDef BpsspiMethods[] = { //
|
|
{"reset", (PyCFunction)Bpsspi_reset, METH_NOARGS, "Bpsspi.reset"},
|
|
|
|
// set_cs(self, cs:int, output:bool)
|
|
{"set_cs", (PyCFunction)Bpsspi_set_cs, METH_VARARGS | METH_KEYWORDS, "Bpsspi.set_cs"},
|
|
|
|
// send_byte_transmit(self, data:bytes)
|
|
{"send_byte_transmit", (PyCFunction)Bpsspi_send_byte_transmit, METH_VARARGS | METH_KEYWORDS, "Bpsspi.send_byte_transmit"},
|
|
|
|
// send_byte_exchange(self, data:bytes) -> List[int]
|
|
{"send_byte_exchange", (PyCFunction)Bpsspi_send_byte_exchange, METH_VARARGS | METH_KEYWORDS, "Bpsspi.send_byte_exchange"},
|
|
{NULL}};
|
|
|
|
static PyTypeObject BpsspiType = {
|
|
PyVarObject_HEAD_INIT(NULL, 0) //,
|
|
.tp_name = "Bpsspi",
|
|
.tp_doc = "BPS spi",
|
|
.tp_basicsize = sizeof(Bpsspi),
|
|
.tp_itemsize = 0,
|
|
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
|
|
.tp_new = Bpsspi_new,
|
|
.tp_init = (initproc)Bpsspi_init,
|
|
.tp_dealloc = (destructor)Bpsspi_dealloc,
|
|
.tp_members = BpsspiMembers,
|
|
.tp_methods = BpsspiMethods,
|
|
};
|
|
|
|
// function implement
|
|
static PyObject *Bpsspi_new(PyTypeObject *type, PyObject *args, PyObject *keywords) {
|
|
Bpsspi *self = (Bpsspi *)type->tp_alloc(type, 0);
|
|
|
|
if (self != NULL) {
|
|
self->cpol = 0;
|
|
self->cpha = 0;
|
|
self->clk = 0;
|
|
self->mosi = 0;
|
|
self->miso = -1;
|
|
self->cs_sz = 0;
|
|
self->cs = NULL;
|
|
}
|
|
|
|
return (PyObject *)self;
|
|
}
|
|
|
|
static int Bpsspi_init_cs(Bpsspi *self) {
|
|
if (self->cs != NULL) {
|
|
PyMem_Free(self->cs);
|
|
self->cs = NULL;
|
|
}
|
|
|
|
self->cs = (unsigned int *)PyMem_Malloc(self->cs_sz * sizeof(unsigned int));
|
|
if (self->cs == NULL) {
|
|
PyErr_SetString(PyExc_RuntimeError, "PyMem_Malloc cs fail");
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int Bpsspi_init(Bpsspi *self, PyObject *args, PyObject *kwargs) {
|
|
static char *keywords[] = {"clk", "mosi", "miso", "cs", "cpol", "cpha", NULL};
|
|
|
|
unsigned int clk;
|
|
unsigned int mosi;
|
|
PyObject * miso = NULL;
|
|
PyObject * cs = NULL;
|
|
int cpol = 0;
|
|
int cpha = 0;
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "II|OOpp", keywords, &clk, &mosi, &miso, &cs, &cpol, &cpha)) return -1;
|
|
|
|
self->clk = clk;
|
|
self->mosi = mosi;
|
|
self->cpol = cpol > 0;
|
|
self->cpha = cpha > 0;
|
|
|
|
// miso
|
|
if (miso == NULL || miso == Py_None) {
|
|
self->miso = -1;
|
|
|
|
} else if (PyLong_Check(miso)) {
|
|
int miso_val = (int)PyLong_AsLong(miso);
|
|
|
|
if (miso_val < 0) {
|
|
PyErr_Format(PyExc_ValueError, "negative miso pin %d", miso_val);
|
|
return -1;
|
|
}
|
|
|
|
self->miso = miso_val;
|
|
|
|
} else {
|
|
PyErr_SetString(PyExc_ValueError, "illegal miso type");
|
|
return -1;
|
|
}
|
|
|
|
// cs
|
|
if (cs == NULL || cs == Py_None) {
|
|
self->cs_sz = 0;
|
|
self->cs = NULL;
|
|
|
|
} else if (PyLong_Check(cs)) {
|
|
self->cs_sz = 1;
|
|
Bpsspi_init_cs(self);
|
|
|
|
int cs_val = (int)PyLong_AsLong(cs);
|
|
|
|
if (cs_val < 0) {
|
|
PyErr_Format(PyExc_ValueError, "negative cs pin %d", cs_val);
|
|
return -1;
|
|
}
|
|
|
|
*self->cs = cs_val;
|
|
|
|
} else if (PyTuple_Check(cs)) {
|
|
self->cs_sz = PyTuple_GET_SIZE(cs);
|
|
Bpsspi_init_cs(self);
|
|
|
|
for (unsigned int i = 0; i < self->cs_sz; i++) {
|
|
PyObject *cs_ref = PyTuple_GetItem(cs, i);
|
|
|
|
if (cs_ref == NULL) return -1;
|
|
|
|
int cs_val = (int)PyLong_AsLong(cs_ref);
|
|
|
|
if (cs_val == -1 && PyErr_Occurred()) return -1;
|
|
|
|
if (cs_val < 0) {
|
|
PyErr_Format(PyExc_ValueError, "negative cs pin %d", cs_val);
|
|
return -1;
|
|
}
|
|
|
|
*(self->cs + i) = cs_val;
|
|
}
|
|
|
|
} else {
|
|
PyErr_SetString(PyExc_ValueError, "illegal cs type");
|
|
return -1;
|
|
}
|
|
|
|
// reset
|
|
Bpsspi_reset(self);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void Bpsspi_dealloc(Bpsspi *self) {
|
|
if (self->cs != NULL) {
|
|
PyMem_Free(self->cs);
|
|
self->cs = NULL;
|
|
}
|
|
|
|
Py_TYPE(self)->tp_free((PyObject *)self);
|
|
}
|
|
|
|
static PyObject *Bpsspi_reset(Bpsspi *self) {
|
|
pinMode(self->clk, OUTPUT);
|
|
digitalWrite(self->clk, (self->cpol) ? HIGH : LOW);
|
|
|
|
pinMode(self->mosi, OUTPUT);
|
|
digitalWrite(self->mosi, LOW);
|
|
|
|
if (self->miso >= 0) {
|
|
pinMode(self->miso, INPUT);
|
|
pullUpDnControl(self->miso, LOW);
|
|
}
|
|
if (self->cs_sz > 0) {
|
|
for (unsigned int i = 0; i < self->cs_sz; i++) {
|
|
pinMode(*(self->cs + i), OUTPUT);
|
|
digitalWrite(*(self->cs + i), HIGH);
|
|
}
|
|
}
|
|
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
static int Bpsspi_set_cs0(Bpsspi *self, unsigned int cs, int output) {
|
|
if (self->cs_sz == 0) return 0;
|
|
|
|
for (unsigned int i = 0; i < self->cs_sz; i++) {
|
|
if (cs == *(self->cs + i)) {
|
|
digitalWrite(*(self->cs + i), output);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static PyObject *Bpsspi_set_cs(Bpsspi *self, PyObject *args, PyObject *kwargs) {
|
|
static char *keywords[] = {"cs", "output", NULL};
|
|
|
|
unsigned int cs;
|
|
int hl = 0;
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ip", keywords, &cs, &hl)) return NULL;
|
|
|
|
if (self->cs_sz == 0) {
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
Bpsspi_set_cs0(self, cs, hl ? HIGH : LOW);
|
|
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
static PyObject *Bpsspi_send_byte_transmit(Bpsspi *self, PyObject *args, PyObject *kwargs) {
|
|
static char *keywords[] = {"data", NULL};
|
|
|
|
PyObject *data_ref;
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", keywords, &data_ref)) return NULL;
|
|
|
|
if (!PyBytes_Check(data_ref)) {
|
|
PyErr_SetString(PyExc_ValueError, "data not bytes");
|
|
return NULL;
|
|
}
|
|
|
|
Py_ssize_t data_sz = PyBytes_GET_SIZE(data_ref);
|
|
char * data = PyBytes_AS_STRING(data_ref);
|
|
|
|
Py_BEGIN_ALLOW_THREADS;
|
|
|
|
int clk_0 = (self->cpol) ? HIGH : LOW;
|
|
int clk_1 = (self->cpol) ? LOW : HIGH;
|
|
|
|
digitalWrite(self->clk, clk_0);
|
|
|
|
for (unsigned int i = 0; i < data_sz; i++) {
|
|
char d = data[i];
|
|
|
|
for (int j = 7; j >= 0; j--) {
|
|
int v = (d & (1 << j)) > 0;
|
|
|
|
if (self->cpha) {
|
|
// change data
|
|
digitalWrite(self->mosi, (v) ? HIGH : LOW);
|
|
digitalWrite(self->clk, clk_1);
|
|
delayMicroseconds(SPI_SPEED_US);
|
|
|
|
// sample data
|
|
digitalWrite(self->clk, clk_0);
|
|
} else {
|
|
// sample data
|
|
digitalWrite(self->clk, clk_1);
|
|
delayMicroseconds(SPI_SPEED_US);
|
|
|
|
// change data
|
|
digitalWrite(self->mosi, (v) ? HIGH : LOW);
|
|
digitalWrite(self->clk, clk_0);
|
|
}
|
|
|
|
delayMicroseconds(SPI_SPEED_US);
|
|
}
|
|
}
|
|
|
|
digitalWrite(self->clk, clk_0);
|
|
|
|
Py_END_ALLOW_THREADS;
|
|
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
// XXX not tested
|
|
static PyObject *Bpsspi_send_byte_exchange(Bpsspi *self, PyObject *args, PyObject *kwargs) {
|
|
if (self->miso < 0) {
|
|
PyErr_SetString(PyExc_RuntimeError, "miso pin not set");
|
|
}
|
|
|
|
static char *keywords[] = {"data", NULL};
|
|
|
|
PyObject *data_ref;
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", keywords, &data_ref)) return NULL;
|
|
|
|
if (!PyBytes_Check(data_ref)) {
|
|
PyErr_SetString(PyExc_ValueError, "data not bytes");
|
|
return NULL;
|
|
}
|
|
|
|
Py_ssize_t data_sz = PyBytes_GET_SIZE(data_ref);
|
|
char * data = PyBytes_AS_STRING(data_ref);
|
|
|
|
PyObject *ret = PyList_New(data_sz);
|
|
if (ret == NULL) return NULL;
|
|
|
|
Py_BEGIN_ALLOW_THREADS
|
|
|
|
int clk_0 = (self->cpol) ? HIGH : LOW;
|
|
int clk_1 = (self->cpol) ? LOW : HIGH;
|
|
|
|
digitalWrite(self->clk, clk_0);
|
|
|
|
for (unsigned int i = 0; i < data_sz; i++) {
|
|
char d = data[i];
|
|
int r = 0;
|
|
|
|
for (int j = 7; j >= 0; j--) {
|
|
int v = (d & (1 << j)) > 0;
|
|
|
|
if (self->cpha) {
|
|
digitalWrite(self->clk, clk_1);
|
|
delayMicroseconds(SPI_SPEED_US);
|
|
|
|
digitalWrite(self->mosi, (v) ? HIGH : LOW);
|
|
digitalWrite(self->clk, clk_0);
|
|
delayMicroseconds(SPI_SPEED_US);
|
|
|
|
if (digitalRead(self->miso)) {
|
|
r |= (1 << j);
|
|
}
|
|
|
|
} else {
|
|
digitalWrite(self->mosi, (v) ? HIGH : LOW);
|
|
digitalWrite(self->clk, clk_1);
|
|
delayMicroseconds(SPI_SPEED_US);
|
|
|
|
if (digitalRead(self->miso)) {
|
|
r |= (1 << j);
|
|
}
|
|
|
|
digitalWrite(self->clk, clk_0);
|
|
delayMicroseconds(SPI_SPEED_US);
|
|
}
|
|
}
|
|
|
|
PyList_SET_ITEM(ret, i, PyLong_FromLong(r));
|
|
}
|
|
|
|
digitalWrite(self->clk, clk_0);
|
|
|
|
Py_END_ALLOW_THREADS
|
|
|
|
return ret;
|
|
}
|
|
|
|
// module init
|
|
|
|
static PyMethodDef moduleMethods[] = { //
|
|
{NULL, NULL, 0, NULL}};
|
|
|
|
static struct PyModuleDef BpsspiModule = { //
|
|
PyModuleDef_HEAD_INIT,
|
|
// module name
|
|
"bpsspi",
|
|
// module doc
|
|
NULL,
|
|
-1,
|
|
moduleMethods};
|
|
|
|
// module export
|
|
|
|
PyMODINIT_FUNC PyInit_bpsspi(void) {
|
|
wiringPiSetupPhys();
|
|
|
|
PyObject *module;
|
|
|
|
module = PyModule_Create(&BpsspiModule);
|
|
if (module == NULL) return NULL;
|
|
|
|
if (PyType_Ready(&BpsspiType) < 0) return NULL;
|
|
|
|
Py_INCREF(&BpsspiType);
|
|
PyModule_AddObject(module, "Bpsspi", (PyObject *)&BpsspiType);
|
|
|
|
return module;
|
|
}
|