Files
controller-wisetopdataserver/python/biopro/ext/bpsspi.c
T
2021-12-20 14:52:55 +08:00

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;
}