From 0c8c9ad976879f7c90f9915a60845ccb0cdb337d Mon Sep 17 00:00:00 2001 From: manuel Date: Wed, 25 Dec 2013 13:25:16 +0100 Subject: initial commit --- python/native/bridge.c | 720 ++++++++++++++++++++++++++++++++++++++++++++++++ python/native/cpuinfo.c | 65 +++++ python/native/cpuinfo.h | 23 ++ python/native/gpio.c | 329 ++++++++++++++++++++++ python/native/gpio.h | 73 +++++ 5 files changed, 1210 insertions(+) create mode 100644 python/native/bridge.c create mode 100644 python/native/cpuinfo.c create mode 100644 python/native/cpuinfo.h create mode 100644 python/native/gpio.c create mode 100644 python/native/gpio.h (limited to 'python/native') diff --git a/python/native/bridge.c b/python/native/bridge.c new file mode 100644 index 0000000..8b2b8da --- /dev/null +++ b/python/native/bridge.c @@ -0,0 +1,720 @@ +/* +Copyright (c) 2012 Ben Croston / 2012-2013 Eric PTAK + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "Python.h" +#include "gpio.h" +#include "cpuinfo.h" + +static PyObject *_SetupException; +static PyObject *_InvalidDirectionException; +static PyObject *_InvalidChannelException; +static PyObject *_InvalidPullException; + +static PyObject *_gpioCount; + + +static PyObject *_low; +static PyObject *_high; + +static PyObject *_in; +static PyObject *_out; +static PyObject *_alt0; +static PyObject *_alt1; +static PyObject *_alt2; +static PyObject *_alt3; +static PyObject *_alt4; +static PyObject *_alt5; +static PyObject *_pwm; + +static PyObject *_pud_off; +static PyObject *_pud_up; +static PyObject *_pud_down; + +static PyObject *_board_revision; + +static char* FUNCTIONS[] = {"IN", "OUT", "ALT5", "ALT4", "ALT0", "ALT1", "ALT2", "ALT3", "PWM"}; +static char* PWM_MODES[] = {"none", "ratio", "angle"}; + +static int module_state = -1; + +// setup function run on import of the RPi.GPIO module +static int module_setup(void) +{ + if (module_state == SETUP_OK) { + return SETUP_OK; + } + + module_state = setup(); + if (module_state == SETUP_DEVMEM_FAIL) + { + PyErr_SetString(_SetupException, "No access to /dev/mem. Try running as root!"); + } else if (module_state == SETUP_MALLOC_FAIL) { + PyErr_NoMemory(); + } else if (module_state == SETUP_MMAP_FAIL) { + PyErr_SetString(_SetupException, "Mmap failed on module import"); + } + + return module_state; +} + +// python function getFunction(channel) +static PyObject *py_get_function(PyObject *self, PyObject *args) +{ + if (module_setup() != SETUP_OK) { + return NULL; + } + + int channel, f; + + if (!PyArg_ParseTuple(args, "i", &channel)) + return NULL; + + if (channel < 0 || channel >= GPIO_COUNT) + { + PyErr_SetString(_InvalidChannelException, "The GPIO channel is invalid"); + return NULL; + } + + f = get_function(channel); + return Py_BuildValue("i", f); +} + +// python function getFunctionString(channel) +static PyObject *py_get_function_string(PyObject *self, PyObject *args) +{ + if (module_setup() != SETUP_OK) { + return NULL; + } + + int channel, f; + char *str; + + if (!PyArg_ParseTuple(args, "i", &channel)) + return NULL; + + if (channel < 0 || channel >= GPIO_COUNT) + { + PyErr_SetString(_InvalidChannelException, "The GPIO channel is invalid"); + return NULL; + } + + f = get_function(channel); + str = FUNCTIONS[f]; + return Py_BuildValue("s", str); +} + +// python function setFunction(channel, direction, pull_up_down=PUD_OFF) +static PyObject *py_set_function(PyObject *self, PyObject *args, PyObject *kwargs) +{ + if (module_setup() != SETUP_OK) { + return NULL; + } + + int channel, function; + int pud = PUD_OFF; + static char *kwlist[] = {"channel", "function", "pull_up_down", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii|i", kwlist, &channel, &function, &pud)) + return NULL; + + if (function != IN && function != OUT && function != PWM) + { + PyErr_SetString(_InvalidDirectionException, "Invalid function"); + return NULL; + } + + if (function == OUT || function == PWM) + pud = PUD_OFF; + + if (pud != PUD_OFF && pud != PUD_DOWN && pud != PUD_UP) + { + PyErr_SetString(_InvalidPullException, "Invalid value for pull_up_down - should be either PUD_OFF, PUD_UP or PUD_DOWN"); + return NULL; + } + + if (channel < 0 || channel >= GPIO_COUNT) + { + PyErr_SetString(_InvalidChannelException, "The GPIO channel is invalid"); + return NULL; + } + + set_function(channel, function, pud); + + Py_INCREF(Py_None); + return Py_None; +} + +// python function value = input(channel) +static PyObject *py_input(PyObject *self, PyObject *args) +{ + if (module_setup() != SETUP_OK) { + return NULL; + } + + int channel; + + if (!PyArg_ParseTuple(args, "i", &channel)) + return NULL; + + if (channel < 0 || channel >= GPIO_COUNT) + { + PyErr_SetString(_InvalidChannelException, "The GPIO channel is invalid"); + return NULL; + } + + if (input(channel)) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +// python function output(channel, value) +static PyObject *py_output(PyObject *self, PyObject *args, PyObject *kwargs) +{ + if (module_setup() != SETUP_OK) { + return NULL; + } + + int channel, value; + static char *kwlist[] = {"channel", "value", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &channel, &value)) + return NULL; + + if (channel < 0 || channel >= GPIO_COUNT) + { + PyErr_SetString(_InvalidChannelException, "The GPIO channel is invalid"); + return NULL; + } + + if (get_function(channel) != OUT) + { + PyErr_SetString(_InvalidDirectionException, "The GPIO channel is not an OUTPUT"); + return NULL; + } + + output(channel, value); + + Py_INCREF(Py_None); + return Py_None; +} + +// python function outputSequence(channel, period, sequence) +static PyObject *py_output_sequence(PyObject *self, PyObject *args, PyObject *kwargs) +{ + if (module_setup() != SETUP_OK) { + return NULL; + } + + int channel, period; + char* sequence; + static char *kwlist[] = {"channel", "period", "sequence", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iis", kwlist, &channel, &period, &sequence)) + return NULL; + + if (channel < 0 || channel >= GPIO_COUNT) + { + PyErr_SetString(_InvalidChannelException, "The GPIO channel is invalid"); + return NULL; + } + + if (get_function(channel) != OUT) + { + PyErr_SetString(_InvalidDirectionException, "The GPIO channel is not an OUTPUT"); + return NULL; + } + + outputSequence(channel, period, sequence); + + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject *py_pulseMilli(PyObject *self, PyObject *args, PyObject *kwargs) +{ + if (module_setup() != SETUP_OK) { + return NULL; + } + + int channel, function, up, down; + static char *kwlist[] = {"channel", "up", "down", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iii", kwlist, &channel, &up, &down)) + return NULL; + + if (channel < 0 || channel >= GPIO_COUNT) + { + PyErr_SetString(_InvalidChannelException, "The GPIO channel is invalid"); + return NULL; + } + + function = get_function(channel); + if ((function != OUT) && (function != PWM)) + { + PyErr_SetString(_InvalidDirectionException, "The GPIO channel is not an OUTPUT or PWM"); + return NULL; + } + + pulseMilli(channel, up, down); + + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject *py_pulseMilliRatio(PyObject *self, PyObject *args, PyObject *kwargs) +{ + if (module_setup() != SETUP_OK) { + return NULL; + } + + int channel, function, width; + float ratio; + static char *kwlist[] = {"channel", "width", "ratio", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iif", kwlist, &channel, &width, &ratio)) + return NULL; + + if (channel < 0 || channel >= GPIO_COUNT) + { + PyErr_SetString(_InvalidChannelException, "The GPIO channel is invalid"); + return NULL; + } + + function = get_function(channel); + if ((function != OUT) && (function != PWM)) + { + PyErr_SetString(_InvalidDirectionException, "The GPIO channel is not an OUTPUT or PWM"); + return NULL; + } + + pulseMilliRatio(channel, width, ratio); + + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject *py_pulseMicro(PyObject *self, PyObject *args, PyObject *kwargs) +{ + if (module_setup() != SETUP_OK) { + return NULL; + } + + int channel, function, up, down; + static char *kwlist[] = {"channel", "up", "down", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iii", kwlist, &channel, &up, &down)) + return NULL; + + if (channel < 0 || channel >= GPIO_COUNT) + { + PyErr_SetString(_InvalidChannelException, "The GPIO channel is invalid"); + return NULL; + } + + function = get_function(channel); + if ((function != OUT) && (function != PWM)) + { + PyErr_SetString(_InvalidDirectionException, "The GPIO channel is not an OUTPUT or PWM"); + return NULL; + } + + pulseMicro(channel, up, down); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *py_pulseMicroRatio(PyObject *self, PyObject *args, PyObject *kwargs) +{ + if (module_setup() != SETUP_OK) { + return NULL; + } + + int channel, function, width; + float ratio; + static char *kwlist[] = {"channel", "width", "ratio", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iif", kwlist, &channel, &width, &ratio)) + return NULL; + + if (channel < 0 || channel >= GPIO_COUNT) + { + PyErr_SetString(_InvalidChannelException, "The GPIO channel is invalid"); + return NULL; + } + + function = get_function(channel); + if ((function != OUT) && (function != PWM)) + { + PyErr_SetString(_InvalidDirectionException, "The GPIO channel is not an OUTPUT or PWM"); + return NULL; + } + + pulseMicroRatio(channel, width, ratio); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *py_pulseAngle(PyObject *self, PyObject *args, PyObject *kwargs) +{ + if (module_setup() != SETUP_OK) { + return NULL; + } + + int channel, function; + float angle; + static char *kwlist[] = {"channel", "angle", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "if", kwlist, &channel, &angle)) + return NULL; + + if (channel < 0 || channel >= GPIO_COUNT) + { + PyErr_SetString(_InvalidChannelException, "The GPIO channel is invalid"); + return NULL; + } + + function = get_function(channel); + if ((function != OUT) && (function != PWM)) + { + PyErr_SetString(_InvalidDirectionException, "The GPIO channel is not an OUTPUT or PWM"); + return NULL; + } + + pulseAngle(channel, angle); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *py_pulseRatio(PyObject *self, PyObject *args, PyObject *kwargs) +{ + if (module_setup() != SETUP_OK) { + return NULL; + } + + int channel, function; + float ratio; + static char *kwlist[] = {"channel", "ratio", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "if", kwlist, &channel, &ratio)) + return NULL; + + if (channel < 0 || channel >= GPIO_COUNT) + { + PyErr_SetString(_InvalidChannelException, "The GPIO channel is invalid"); + return NULL; + } + + function = get_function(channel); + if ((function != OUT) && (function != PWM)) + { + PyErr_SetString(_InvalidDirectionException, "The GPIO channel is not an OUTPUT or PWM"); + return NULL; + } + + pulseRatio(channel, ratio); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *py_pulse(PyObject *self, PyObject *args) +{ + if (module_setup() != SETUP_OK) { + return NULL; + } + + int channel; + + if (!PyArg_ParseTuple(args, "i", &channel)) + return NULL; + + if (channel < 0 || channel >= GPIO_COUNT) + { + PyErr_SetString(_InvalidChannelException, "The GPIO channel is invalid"); + return NULL; + } + + pulseRatio(channel, 0.5); + return Py_None; +} + +static PyObject *py_getPulse(PyObject *self, PyObject *args) +{ + if (module_setup() != SETUP_OK) { + return NULL; + } + + int channel; + char str[256]; + struct pulse *p; + + if (!PyArg_ParseTuple(args, "i", &channel)) + return NULL; + + if (channel < 0 || channel >= GPIO_COUNT) + { + PyErr_SetString(_InvalidChannelException, "The GPIO channel is invalid"); + return NULL; + } + + p = getPulse(channel); + + sprintf(str, "%s:%.2f", PWM_MODES[p->type], p->value); +#if PY_MAJOR_VERSION > 2 + return PyUnicode_FromString(str); +#else + return PyString_FromString(str); +#endif +} + +static PyObject *py_enablePWM(PyObject *self, PyObject *args) +{ + if (module_setup() != SETUP_OK) { + return NULL; + } + + int channel; + + if (!PyArg_ParseTuple(args, "i", &channel)) + return NULL; + + if (channel < 0 || channel >= GPIO_COUNT) + { + PyErr_SetString(_InvalidChannelException, "The GPIO channel is invalid"); + return NULL; + } + + enablePWM(channel); + return Py_None; +} + +static PyObject *py_disablePWM(PyObject *self, PyObject *args) +{ + if (module_setup() != SETUP_OK) { + return NULL; + } + + int channel; + + if (!PyArg_ParseTuple(args, "i", &channel)) + return NULL; + + if (channel < 0 || channel >= GPIO_COUNT) + { + PyErr_SetString(_InvalidChannelException, "The GPIO channel is invalid"); + return NULL; + } + + disablePWM(channel); + return Py_None; +} + + + +static PyObject *py_isPWMEnabled(PyObject *self, PyObject *args) +{ + if (module_setup() != SETUP_OK) { + return NULL; + } + + int channel; + + if (!PyArg_ParseTuple(args, "i", &channel)) + return NULL; + + if (channel < 0 || channel >= GPIO_COUNT) + { + PyErr_SetString(_InvalidChannelException, "The GPIO channel is invalid"); + return NULL; + } + + if (isPWMEnabled(channel)) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +PyMethodDef python_methods[] = { + {"getFunction", py_get_function, METH_VARARGS, "Return the current GPIO setup (IN, OUT, ALT0)"}, + {"getSetup", py_get_function, METH_VARARGS, "Return the current GPIO setup (IN, OUT, ALT0)"}, + + {"getFunctionString", py_get_function_string, METH_VARARGS, "Return the current GPIO setup (IN, OUT, ALT0) as string"}, + {"getSetupString", py_get_function_string, METH_VARARGS, "Return the current GPIO setup (IN, OUT, ALT0) as string"}, + + {"setFunction", (PyCFunction)py_set_function, METH_VARARGS | METH_KEYWORDS, "Setup the GPIO channel, direction and (optional) pull/up down control\nchannel - BCM GPIO number\ndirection - IN or OUT\n[pull_up_down] - PUD_OFF (default), PUD_UP or PUD_DOWN"}, + {"setup", (PyCFunction)py_set_function, METH_VARARGS | METH_KEYWORDS, "Setup the GPIO channel, direction and (optional) pull/up down control\nchannel - BCM GPIO number\ndirection - IN or OUT\n[pull_up_down] - PUD_OFF (default), PUD_UP or PUD_DOWN"}, + + {"input", py_input, METH_VARARGS, "Input from a GPIO channel - Deprecated, use digitalRead instead"}, + {"digitalRead", py_input, METH_VARARGS, "Read a GPIO channel"}, + + {"output", (PyCFunction)py_output, METH_VARARGS | METH_KEYWORDS, "Output to a GPIO channel - Deprecated, use digitalWrite instead"}, + {"digitalWrite", (PyCFunction)py_output, METH_VARARGS | METH_KEYWORDS, "Write to a GPIO channel"}, + + {"outputSequence", (PyCFunction)py_output_sequence, METH_VARARGS | METH_KEYWORDS, "Output a sequence to a GPIO channel"}, + + {"getPulse", py_getPulse, METH_VARARGS, "Read current PWM output"}, + {"pwmRead", py_getPulse, METH_VARARGS, "Read current PWM output"}, + + {"pulseMilli", (PyCFunction)py_pulseMilli, METH_VARARGS | METH_KEYWORDS, "Output a PWM to a GPIO channel using milliseconds for both HIGH and LOW state widths"}, + {"pulseMilliRatio", (PyCFunction)py_pulseMilliRatio, METH_VARARGS | METH_KEYWORDS, "Output a PWM to a GPIO channel using millisecond for the total width and a ratio (duty cycle) for the HIGH state width"}, + {"pulseMicro", (PyCFunction)py_pulseMicro, METH_VARARGS | METH_KEYWORDS, "Output a PWM pulse to a GPIO channel using microseconds for both HIGH and LOW state widths"}, + {"pulseMicroRatio", (PyCFunction)py_pulseMicroRatio, METH_VARARGS | METH_KEYWORDS, "Output a PWM to a GPIO channel using microseconds for the total width and a ratio (duty cycle) for the HIGH state width"}, + + {"pulseAngle", (PyCFunction)py_pulseAngle, METH_VARARGS | METH_KEYWORDS, "Output a PWM to a GPIO channel using an angle - Deprecated, use pwmWriteAngle instead"}, + {"pwmWriteAngle", (PyCFunction)py_pulseAngle, METH_VARARGS | METH_KEYWORDS, "Output a PWM to a GPIO channel using an angle"}, + + {"pulseRatio", (PyCFunction)py_pulseRatio, METH_VARARGS | METH_KEYWORDS, "Output a PWM to a GPIO channel using a ratio (duty cycle) with the default 50Hz signal - Deprecated, use pwmWrite instead"}, + {"pwmWrite", (PyCFunction)py_pulseRatio, METH_VARARGS | METH_KEYWORDS, "Output a PWM to a GPIO channel using a ratio (duty cycle) with the default 50Hz signal"}, + + {"pulse", py_pulse, METH_VARARGS, "Output a PWM to a GPIO channel using a 50% ratio (duty cycle) with the default 50Hz signal"}, + + {"enablePWM", py_enablePWM, METH_VARARGS, "Enable software PWM loop for a GPIO channel"}, + {"disablePWM", py_disablePWM, METH_VARARGS, "Disable software PWM loop of a GPIO channel"}, + {"isPWMEnabled", py_isPWMEnabled, METH_VARARGS, "Returns software PWM state"}, + + {NULL, NULL, 0, NULL} +}; + +#if PY_MAJOR_VERSION > 2 +static struct PyModuleDef python_module = { + PyModuleDef_HEAD_INIT, + "_webiopi.GPIO", /* name of module */ + NULL, /* module documentation, may be NULL */ + -1, /* size of per-interpreter state of the module, + or -1 if the module keeps state in global variables. */ + python_methods +}; +#endif + +#if PY_MAJOR_VERSION > 2 +PyMODINIT_FUNC PyInit_GPIO(void) +#else +PyMODINIT_FUNC initGPIO(void) +#endif +{ + PyObject *module = NULL; + int revision = -1; + +#if PY_MAJOR_VERSION > 2 + if ((module = PyModule_Create(&python_module)) == NULL) + goto exit; +#else + if ((module = Py_InitModule("_webiopi.GPIO", python_methods)) == NULL) + goto exit; +#endif + + _SetupException = PyErr_NewException("_webiopi.GPIO.SetupException", NULL, NULL); + PyModule_AddObject(module, "SetupException", _SetupException); + + _InvalidDirectionException = PyErr_NewException("_webiopi.GPIO.InvalidDirectionException", NULL, NULL); + PyModule_AddObject(module, "InvalidDirectionException", _InvalidDirectionException); + + _InvalidChannelException = PyErr_NewException("_webiopi.GPIO.InvalidChannelException", NULL, NULL); + PyModule_AddObject(module, "InvalidChannelException", _InvalidChannelException); + + _InvalidPullException = PyErr_NewException("_webiopi.GPIO.InvalidPullException", NULL, NULL); + PyModule_AddObject(module, "InvalidPullException", _InvalidPullException); + + _gpioCount = Py_BuildValue("i", GPIO_COUNT); + PyModule_AddObject(module, "GPIO_COUNT", _gpioCount); + + _low = Py_BuildValue("i", LOW); + PyModule_AddObject(module, "LOW", _low); + + _high = Py_BuildValue("i", HIGH); + PyModule_AddObject(module, "HIGH", _high); + + _in = Py_BuildValue("i", IN); + PyModule_AddObject(module, "IN", _in); + + _out = Py_BuildValue("i", OUT); + PyModule_AddObject(module, "OUT", _out); + + _alt0 = Py_BuildValue("i", ALT0); + PyModule_AddObject(module, "ALT0", _alt0); + + _alt1 = Py_BuildValue("i", ALT1); + PyModule_AddObject(module, "ALT1", _alt1); + + _alt2 = Py_BuildValue("i", ALT2); + PyModule_AddObject(module, "ALT2", _alt2); + + _alt3 = Py_BuildValue("i", ALT3); + PyModule_AddObject(module, "ALT3", _alt3); + + _alt4 = Py_BuildValue("i", ALT4); + PyModule_AddObject(module, "ALT4", _alt4); + + _alt5 = Py_BuildValue("i", ALT5); + PyModule_AddObject(module, "ALT5", _alt5); + + _pwm = Py_BuildValue("i", PWM); + PyModule_AddObject(module, "PWM", _pwm); + + _pud_off = Py_BuildValue("i", PUD_OFF); + PyModule_AddObject(module, "PUD_OFF", _pud_off); + + _pud_up = Py_BuildValue("i", PUD_UP); + PyModule_AddObject(module, "PUD_UP", _pud_up); + + _pud_down = Py_BuildValue("i", PUD_DOWN); + PyModule_AddObject(module, "PUD_DOWN", _pud_down); + + // detect board revision and set up accordingly + revision = get_rpi_revision(); + if (revision == -1) + { + PyErr_SetString(_SetupException, "This module can only be run on a Raspberry Pi!"); +#if PY_MAJOR_VERSION > 2 + return NULL; +#else + return; +#endif + } + + _board_revision = Py_BuildValue("i", revision); + PyModule_AddObject(module, "BOARD_REVISION", _board_revision); + + if (Py_AtExit(cleanup) != 0) + { + cleanup(); +#if PY_MAJOR_VERSION > 2 + return NULL; +#else + return; +#endif + } + +exit: +#if PY_MAJOR_VERSION > 2 + return module; +#else + return; +#endif +} diff --git a/python/native/cpuinfo.c b/python/native/cpuinfo.c new file mode 100644 index 0000000..a69d97e --- /dev/null +++ b/python/native/cpuinfo.c @@ -0,0 +1,65 @@ +/* +Copyright (c) 2012 Ben Croston + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include +#include +#include "cpuinfo.h" + +char *get_cpuinfo_revision(char *revision) +{ + FILE *fp; + char buffer[1024]; + char hardware[1024]; + int rpi_found = 0; + + if ((fp = fopen("/proc/cpuinfo", "r")) == NULL) + return 0; + + while(!feof(fp)) { + fgets(buffer, sizeof(buffer) , fp); + sscanf(buffer, "Hardware : %s", hardware); + if (strcmp(hardware, "BCM2708") == 0) + rpi_found = 1; + sscanf(buffer, "Revision : %s", revision); + } + fclose(fp); + + if (!rpi_found) + revision = NULL; + return revision; +} + +int get_rpi_revision(void) +{ + char revision[1024] = {'\0'}; + + if (get_cpuinfo_revision(revision) == NULL) + return -1; + + if ((strcmp(revision, "0002") == 0) || + (strcmp(revision, "1000002") == 0 ) || + (strcmp(revision, "0003") == 0) || + (strcmp(revision, "1000003") == 0 )) + return 1; + else // assume rev 2 (0004 0005 0006 1000004 1000005 1000006) + return 2; +} diff --git a/python/native/cpuinfo.h b/python/native/cpuinfo.h new file mode 100644 index 0000000..e84ea7d --- /dev/null +++ b/python/native/cpuinfo.h @@ -0,0 +1,23 @@ +/* +Copyright (c) 2012 Ben Croston + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +int get_rpi_revision(void); diff --git a/python/native/gpio.c b/python/native/gpio.c new file mode 100644 index 0000000..3950b16 --- /dev/null +++ b/python/native/gpio.c @@ -0,0 +1,329 @@ +/* +Copyright (c) 2012 Ben Croston / 2012-2013 Eric PTAK + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "gpio.h" + +#define BCM2708_PERI_BASE 0x20000000 +#define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) +#define FSEL_OFFSET 0 // 0x0000 +#define SET_OFFSET 7 // 0x001c / 4 +#define CLR_OFFSET 10 // 0x0028 / 4 +#define PINLEVEL_OFFSET 13 // 0x0034 / 4 +#define EVENT_DETECT_OFFSET 16 // 0x0040 / 4 +#define RISING_ED_OFFSET 19 // 0x004c / 4 +#define FALLING_ED_OFFSET 22 // 0x0058 / 4 +#define HIGH_DETECT_OFFSET 25 // 0x0064 / 4 +#define LOW_DETECT_OFFSET 28 // 0x0070 / 4 +#define PULLUPDN_OFFSET 37 // 0x0094 / 4 +#define PULLUPDNCLK_OFFSET 38 // 0x0098 / 4 + +#define PAGE_SIZE (4*1024) +#define BLOCK_SIZE (4*1024) + +static volatile uint32_t *gpio_map; + +struct tspair { + struct timespec up; + struct timespec down; +}; + +static struct pulse gpio_pulses[GPIO_COUNT]; +static struct tspair gpio_tspairs[GPIO_COUNT]; +static pthread_t *gpio_threads[GPIO_COUNT]; + +void short_wait(void) +{ + int i; + + for (i=0; i<150; i++) // wait 150 cycles + { + asm volatile("nop"); + } +} + +int setup(void) +{ + int mem_fd; + uint8_t *gpio_mem; + + if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) + { + return SETUP_DEVMEM_FAIL; + } + + if ((gpio_mem = malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) + return SETUP_MALLOC_FAIL; + + if ((uint32_t)gpio_mem % PAGE_SIZE) + gpio_mem += PAGE_SIZE - ((uint32_t)gpio_mem % PAGE_SIZE); + + gpio_map = (uint32_t *)mmap( (caddr_t)gpio_mem, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, mem_fd, GPIO_BASE); + + if ((uint32_t)gpio_map < 0) + return SETUP_MMAP_FAIL; + + return SETUP_OK; +} + +void set_pullupdn(int gpio, int pud) +{ + int clk_offset = PULLUPDNCLK_OFFSET + (gpio/32); + int shift = (gpio%32); + + if (pud == PUD_DOWN) + *(gpio_map+PULLUPDN_OFFSET) = (*(gpio_map+PULLUPDN_OFFSET) & ~3) | PUD_DOWN; + else if (pud == PUD_UP) + *(gpio_map+PULLUPDN_OFFSET) = (*(gpio_map+PULLUPDN_OFFSET) & ~3) | PUD_UP; + else // pud == PUD_OFF + *(gpio_map+PULLUPDN_OFFSET) &= ~3; + + short_wait(); + *(gpio_map+clk_offset) = 1 << shift; + short_wait(); + *(gpio_map+PULLUPDN_OFFSET) &= ~3; + *(gpio_map+clk_offset) = 0; +} + +//updated Eric PTAK - trouch.com +void set_function(int gpio, int function, int pud) +{ + if (function == PWM) { + function = OUT; + enablePWM(gpio); + } + else { + disablePWM(gpio); + } + + int offset = FSEL_OFFSET + (gpio/10); + int shift = (gpio%10)*3; + + set_pullupdn(gpio, pud); + *(gpio_map+offset) = (*(gpio_map+offset) & ~(7<>= shift; + value &= 7; + if ((value == OUT) && isPWMEnabled(gpio)) { + value = PWM; + } + return value; // 0=input, 1=output, 4=alt0 +} + +//updated Eric PTAK - trouch.com +int input(int gpio) +{ + int offset, value, mask; + + offset = PINLEVEL_OFFSET + (gpio/32); + mask = (1 << gpio%32); + value = *(gpio_map+offset) & mask; + return value; +} + +void output(int gpio, int value) +{ + int offset, shift; + + if (value) // value == HIGH + offset = SET_OFFSET + (gpio/32); + else // value == LOW + offset = CLR_OFFSET + (gpio/32); + + shift = (gpio%32); + + *(gpio_map+offset) = 1 << shift; +} + +//added Eric PTAK - trouch.com +void outputSequence(int gpio, int period, char* sequence) { + int i, value; + struct timespec ts; + ts.tv_sec = period/1000; + ts.tv_nsec = (period%1000) * 1000000; + + for (i=0; sequence[i] != '\0'; i++) { + if (sequence[i] == '1') { + value = 1; + } + else { + value = 0; + } + output(gpio, value); + nanosleep(&ts, NULL); + } +} + +void resetPWM(int gpio) { + gpio_pulses[gpio].type = 0; + gpio_pulses[gpio].value = 0; + + gpio_tspairs[gpio].up.tv_sec = 0; + gpio_tspairs[gpio].up.tv_nsec = 0; + gpio_tspairs[gpio].down.tv_sec = 0; + gpio_tspairs[gpio].down.tv_nsec = 0; +} + +//added Eric PTAK - trouch.com +void pulseTS(int gpio, struct timespec *up, struct timespec *down) { + if ((up->tv_sec > 0) || (up->tv_nsec > 0)) { + output(gpio, 1); + nanosleep(up, NULL); + } + + if ((down->tv_sec > 0) || (down->tv_nsec > 0)) { + output(gpio, 0); + nanosleep(down, NULL); + } +} + +//added Eric PTAK - trouch.com +void pulseOrSaveTS(int gpio, struct timespec *up, struct timespec *down) { + if (gpio_threads[gpio] != NULL) { + memcpy(&gpio_tspairs[gpio].up, up, sizeof(struct timespec)); + memcpy(&gpio_tspairs[gpio].down, down, sizeof(struct timespec)); + } + else { + pulseTS(gpio, up, down); + } +} + +//added Eric PTAK - trouch.com +void pulseMilli(int gpio, int up, int down) { + struct timespec tsUP, tsDOWN; + + tsUP.tv_sec = up/1000; + tsUP.tv_nsec = (up%1000) * 1000000; + + tsDOWN.tv_sec = down/1000; + tsDOWN.tv_nsec = (down%1000) * 1000000; + pulseOrSaveTS(gpio, &tsUP, &tsDOWN); +} + +//added Eric PTAK - trouch.com +void pulseMilliRatio(int gpio, int width, float ratio) { + int up = ratio*width; + int down = width - up; + pulseMilli(gpio, up, down); +} + +//added Eric PTAK - trouch.com +void pulseMicro(int gpio, int up, int down) { + struct timespec tsUP, tsDOWN; + + tsUP.tv_sec = 0; + tsUP.tv_nsec = up * 1000; + + tsDOWN.tv_sec = 0; + tsDOWN.tv_nsec = down * 1000; + pulseOrSaveTS(gpio, &tsUP, &tsDOWN); +} + +//added Eric PTAK - trouch.com +void pulseMicroRatio(int gpio, int width, float ratio) { + int up = ratio*width; + int down = width - up; + pulseMicro(gpio, up, down); +} + +//added Eric PTAK - trouch.com +void pulseAngle(int gpio, float angle) { + gpio_pulses[gpio].type = ANGLE; + gpio_pulses[gpio].value = angle; + int up = 1520 + (angle*400)/45; + int down = 20000-up; + pulseMicro(gpio, up, down); +} + +//added Eric PTAK - trouch.com +void pulseRatio(int gpio, float ratio) { + gpio_pulses[gpio].type = RATIO; + gpio_pulses[gpio].value = ratio; + int up = ratio * 20000; + int down = 20000 - up; + pulseMicro(gpio, up, down); +} + +struct pulse* getPulse(int gpio) { + return &gpio_pulses[gpio]; +} + +//added Eric PTAK - trouch.com +void* pwmLoop(void* data) { + int gpio = (int)data; + + while (1) { + pulseTS(gpio, &gpio_tspairs[gpio].up, &gpio_tspairs[gpio].down); + } +} + +//added Eric PTAK - trouch.com +void enablePWM(int gpio) { + pthread_t *thread = gpio_threads[gpio]; + if (thread != NULL) { + return; + } + + resetPWM(gpio); + + thread = (pthread_t*) malloc(sizeof(pthread_t)); + pthread_create(thread, NULL, pwmLoop, (void*)gpio); + gpio_threads[gpio] = thread; +} + +//added Eric PTAK - trouch.com +void disablePWM(int gpio) { + pthread_t *thread = gpio_threads[gpio]; + if (thread == NULL) { + return; + } + + pthread_cancel(*thread); + gpio_threads[gpio] = NULL; + output(gpio, 0); + resetPWM(gpio); +} + +//added Eric PTAK - trouch.com +int isPWMEnabled(int gpio) { + return gpio_threads[gpio] != NULL; +} + + +void cleanup(void) +{ + // fixme - set all gpios back to input + munmap((caddr_t)gpio_map, BLOCK_SIZE); +} diff --git a/python/native/gpio.h b/python/native/gpio.h new file mode 100644 index 0000000..cb0147c --- /dev/null +++ b/python/native/gpio.h @@ -0,0 +1,73 @@ +/* +Copyright (c) 2012 Ben Croston / 2012-2013 Eric PTAK + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#define SETUP_OK 0 +#define SETUP_DEVMEM_FAIL 1 +#define SETUP_MALLOC_FAIL 2 +#define SETUP_MMAP_FAIL 3 + +#define GPIO_COUNT 54 + +#define IN 0 +#define OUT 1 +#define ALT5 2 +#define ALT4 3 +#define ALT0 4 +#define ALT1 5 +#define ALT2 6 +#define ALT3 7 +#define PWM 8 + +#define LOW 0 +#define HIGH 1 + +#define PUD_OFF 0 +#define PUD_DOWN 1 +#define PUD_UP 2 + +#define RATIO 1 +#define ANGLE 2 + +struct pulse { + int type; + float value; +}; + +int setup(void); +int get_function(int gpio); +void set_function(int gpio, int function, int pud); +int input(int gpio); +void output(int gpio, int value); +void outputSequence(int gpio, int period, char* sequence); +struct pulse* getPulse(int gpio); +void pulseMilli(int gpio, int up, int down); +void pulseMilliRatio(int gpio, int width, float ratio); +void pulseMicro(int gpio, int up, int down); +void pulseMicroRatio(int gpio, int width, float ratio); +void pulseAngle(int gpio, float angle); +void pulseRatio(int gpio, float ratio); +void enablePWM(int gpio); +void disablePWM(int gpio); +int isPWMEnabled(int gpio); + +void cleanup(void); + -- cgit v1.2.3