diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ca70f0f..f6ddded 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,8 @@ +0.3.4 +--- +* Pull Up/Pull Down resistor setting now available for the R8 GPIO. +* Some general cleanup + 0.3.3 ---- * Added Debug printing for all the capabilities with the toggle_debug() function diff --git a/README.rst b/README.rst index 4de4a1b..6debf27 100644 --- a/README.rst +++ b/README.rst @@ -191,6 +191,15 @@ Inputs work similarly to outputs.:: import CHIP_IO.GPIO as GPIO GPIO.setup("CSID0", GPIO.IN) +Other options when setting up pins:: + + # Specify pull up/pull down settings on a pin + GPIO.setup("CSID0", GPIO.IN, pull_up_down=GPIO.PUD_UP) + # Specify initial value for an output + GPIO.setup("CSID0", GPIO.OUT, initial=1) + +Pull Up/Down values are only for pins that are provided by the R8, the XIO are not capable of this. The allowable values are: PUD_OFF, PUD_UP, and PUD_DOWN. + Polling inputs:: if GPIO.input("CSID0"): diff --git a/setup.py b/setup.py index e88d62c..8779fc0 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,7 @@ classifiers = ['Development Status :: 3 - Alpha', 'Topic :: System :: Hardware'] setup(name = 'CHIP_IO', - version = '0.3.3', + version = '0.3.4', author = 'Robert Wolterman', author_email = 'robert.wolterman@gmail.com', description = 'A module to control CHIP IO channels', diff --git a/source/common.c b/source/common.c index f0c8ee2..4557bb3 100644 --- a/source/common.c +++ b/source/common.c @@ -249,6 +249,22 @@ int gpio_number(pins_t *pin) return gpio_num; } /* gpio_number */ +int gpio_pud_capable(pins_t *pin) +{ + int capable = -1; + + switch (pin->base_method) { + case BASE_METHOD_AS_IS: + capable = 1; + break; + + case BASE_METHOD_XIO: + capable = 0; + break; + } + + return capable; +} int lookup_gpio_by_key(const char *key) { @@ -283,6 +299,39 @@ int lookup_gpio_by_altname(const char *altname) return -1; } +int lookup_pud_capable_by_key(const char *key) +{ + pins_t *p; + for (p = pins_info; p->key != NULL; ++p) { + if (strcmp(p->key, key) == 0) { + return gpio_pud_capable(p); + } + } + return -1; +} + +int lookup_pud_capable_by_name(const char *name) +{ + pins_t *p; + for (p = pins_info; p->name != NULL; ++p) { + if (strcmp(p->name, name) == 0) { + return gpio_pud_capable(p); + } + } + return -1; +} + +int lookup_pud_capable_by_altname(const char *altname) +{ + pins_t *p; + for (p = pins_info; p->altname != NULL; ++p) { + if (strcmp(p->altname, altname) == 0) { + return gpio_pud_capable(p); + } + } + return -1; +} + int lookup_ain_by_key(const char *key) { pins_t *p; @@ -396,17 +445,29 @@ int get_gpio_number(const char *key, int *gpio) return status; } -int compute_port_pin(const char *key, int *port, int *pin) +int compute_port_pin(const char *key, int gpio, int *port, int *pin) { - int gpio; - int rtn; - - rtn = get_gpio_number(key, &gpio); - - // Method from: - //https://bbs.nextthing.co/t/chippy-gonzales-fast-gpio/14056/6?u=xtacocorex - *port = gpio / 32; - *pin = gpio % 32; + int capable = 0; + int rtn = -1; + + capable = lookup_pud_capable_by_key(key); + if (capable < 0) { + capable = lookup_pud_capable_by_name(key); + if (capable < 0) { + capable = lookup_gpio_by_altname(key); + if (capable < 0) { + capable = 0; // default to false + } + } + } + + if (capable) { + // Method from: + // https://bbs.nextthing.co/t/chippy-gonzales-fast-gpio/14056/6?u=xtacocorex + *port = gpio / 32; + *pin = gpio % 32; + rtn = 0; + } return rtn; } diff --git a/source/common.h b/source/common.h index 4aacd91..2f4f695 100644 --- a/source/common.h +++ b/source/common.h @@ -92,9 +92,13 @@ int DEBUG; int get_xio_base(void); int gpio_number(pins_t *pin); +int gpio_pud_capable(pins_t *pin); int lookup_gpio_by_key(const char *key); int lookup_gpio_by_name(const char *name); int lookup_gpio_by_altname(const char *altname); +int lookup_pud_capable_by_key(const char *key); +int lookup_pud_capable_by_name(const char *name); +int lookup_pud_capable_by_altname(const char *altname); int lookup_ain_by_key(const char *key); int lookup_ain_by_name(const char *name); int copy_key_by_key(const char *input_key, char *key); @@ -113,4 +117,4 @@ void clear_error_msg(void); char *get_error_msg(void); void add_error_msg(char *msg); void toggle_debug(void); -int compute_port_pin(const char *key, int *port, int *pin); +int compute_port_pin(const char *key, int gpio, int *port, int *pin); diff --git a/source/constants.c b/source/constants.c index 29b5d1f..684cbbb 100644 --- a/source/constants.c +++ b/source/constants.c @@ -88,6 +88,6 @@ void define_constants(PyObject *module) module_debug = Py_BuildValue("i", DEBUG ? Py_True: Py_False); PyModule_AddObject(module, "DEBUG", module_debug); - version = Py_BuildValue("s", "0.3.3"); + version = Py_BuildValue("s", "0.3.4"); PyModule_AddObject(module, "VERSION", version); } diff --git a/source/event_gpio.c b/source/event_gpio.c index 7fe4665..c54e481 100644 --- a/source/event_gpio.c +++ b/source/event_gpio.c @@ -38,17 +38,23 @@ SOFTWARE. #include #include +#include #include #include #include #include #include #include +#include +#include #include "event_gpio.h" #include "common.h" const char *stredge[4] = {"none", "rising", "falling", "both"}; +// Memory Map for PUD +uint8_t *memmap; + // file descriptors struct fdx { @@ -85,6 +91,63 @@ dyn_int_array_t *event_occurred = NULL; int thread_running = 0; int epfd = -1; +// Thanks to WereCatf and Chippy-Gonzales for the Memory Mapping code/help +int map_pio_memory() +{ + if (DEBUG) + printf(" ** map_pio_memory: opening /dev/mem **\n"); + int fd = open("/dev/mem", O_RDWR|O_SYNC); + if(fd < 0) { + char err[256]; + snprintf(err, sizeof(err), "map_pio_memory: could not open /dev/mem (%s)", strerror(errno)); + add_error_msg(err); + return -1; + } + // uint32_t addr = 0x01c20800 & ~(getpagesize() - 1); + //Requires memmap to be on pagesize-boundary + if (DEBUG) + printf(" ** map_pio_memory: mapping memory **\n"); + memmap = (uint8_t *)mmap(NULL, getpagesize()*2, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0x01C20000); + if(memmap == NULL) { + char err[256]; + snprintf(err, sizeof(err), "map_pio_memory: mmap failed (%s)", strerror(errno)); + add_error_msg(err); + return -1; + } + close(fd); + + //Set memmap to point to PIO-registers + if (DEBUG) + printf(" ** map_pio_memory: moving to pio registers **\n"); + memmap=memmap+0x800; + + return 0; +} + +int gpio_get_pud(int port, int pin) +{ + if (DEBUG) + printf(" ** gpio_get_pud: port %d, pin %d **\n", port, pin); + volatile uint32_t *pioMem32, *configRegister; + pioMem32=(uint32_t *)(memmap+port*0x24+0x1c); //0x1c == pull-register + configRegister=pioMem32+(pin >> 4); + return *configRegister >> ((pin & 15) * 2) & 3; +} + +int gpio_set_pud(int port, int pin, uint8_t value) +{ + if (DEBUG) + printf(" ** gpio_set_pud: port %d, pin %d, value %d **\n", port, pin, value); + value &= 3; + volatile uint32_t *pioMem32, *configRegister; + uint32_t mask; + pioMem32=(uint32_t *)(memmap+port*0x24+0x1c); //0x1c == pull-register + configRegister=pioMem32+(pin >> 4); + mask = ~(3 << ((pin & 15) * 2)); + *configRegister &= mask; + *configRegister |= value << ((pin & 15) * 2); + return 0; +} int gpio_export(int gpio) { @@ -119,7 +182,7 @@ int gpio_export(int gpio) // add to list if (DEBUG) - printf(" ** gpio_export: creating data struct **\n"); + printf(" ** gpio_export: creating data struct **\n"); new_gpio = malloc(sizeof(struct gpio_exp)); ASSRT(new_gpio != NULL); new_gpio->gpio = gpio; diff --git a/source/event_gpio.h b/source/event_gpio.h index 7c03db0..bfadb14 100644 --- a/source/event_gpio.h +++ b/source/event_gpio.h @@ -36,6 +36,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include + #define NO_EDGE 0 #define RISING_EDGE 1 #define FALLING_EDGE 2 @@ -54,6 +56,12 @@ SOFTWARE. #define PUD_DOWN 1 #define PUD_UP 2 +extern uint8_t *memmap; + +int map_pio_memory(void); +int gpio_get_pud(int port, int pin); +int gpio_set_pud(int port, int pin, uint8_t value); + int gpio_export(int gpio); int gpio_unexport(int gpio); void exports_cleanup(void); diff --git a/source/py_gpio.c b/source/py_gpio.c index 83e58aa..6598986 100644 --- a/source/py_gpio.c +++ b/source/py_gpio.c @@ -58,8 +58,29 @@ struct py_callback }; static struct py_callback *py_callbacks = NULL; +// python function toggle_debug() +static PyObject *py_toggle_debug(PyObject *self, PyObject *args) +{ + // toggle debug printing + toggle_debug(); + + Py_RETURN_NONE; +} + static int init_module(void) { + clear_error_msg(); + + if (map_pio_memory() < 0) { + char err[2000]; + snprintf(err, sizeof(err), "init_module error (%s)", get_error_msg()); + PyErr_SetString(PyExc_RuntimeError, err); + return 0; + } + + // If we make it here, we're good to go + if (DEBUG) + printf(" ** init_module: setup complete **\n"); module_setup = 1; return 0; @@ -93,7 +114,7 @@ static PyObject *py_cleanup(PyObject *self, PyObject *args, PyObject *kwargs) } // The !channel fixes issues #50 - if (!channel || strcmp(channel, "") == 0) { + if (channel == NULL || strcmp(channel, "\0") == 0) { event_cleanup(); } else { if (get_gpio_number(channel, &gpio) < 0) { @@ -111,68 +132,91 @@ static PyObject *py_cleanup(PyObject *self, PyObject *args, PyObject *kwargs) // python function setup(channel, direction, pull_up_down=PUD_OFF, initial=None) static PyObject *py_setup_channel(PyObject *self, PyObject *args, PyObject *kwargs) { - int gpio; - char *channel; - int direction; - int pud = PUD_OFF; - int initial = 0; - static char *kwlist[] = {"channel", "direction", "pull_up_down", "initial", NULL}; - - clear_error_msg(); - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "si|ii", kwlist, &channel, &direction, &pud, &initial)) - return NULL; - - if (!module_setup) { - init_module(); - } - - if (direction != INPUT && direction != OUTPUT) - { - PyErr_SetString(PyExc_ValueError, "An invalid direction was passed to setup()"); - return NULL; - } - - if (direction == OUTPUT) - pud = PUD_OFF; - - if (pud != PUD_OFF && pud != PUD_DOWN && pud != PUD_UP) - { - PyErr_SetString(PyExc_ValueError, "Invalid value for pull_up_down - should be either PUD_OFF, PUD_UP or PUD_DOWN"); - return NULL; - } - - if (get_gpio_number(channel, &gpio) < 0) { - char err[2000]; - snprintf(err, sizeof(err), "Invalid channel %s. (%s)", channel, get_error_msg()); - PyErr_SetString(PyExc_ValueError, err); - return NULL; - } - - if (gpio_export(gpio) < 0) { - char err[2000]; - snprintf(err, sizeof(err), "Error setting up channel %s, maybe already exported? (%s)", channel, get_error_msg()); - PyErr_SetString(PyExc_RuntimeError, err); - return NULL; - } - if (gpio_set_direction(gpio, direction) < 0) { - char err[2000]; - snprintf(err, sizeof(err), "Error setting direction %d on channel %s. (%s)", direction, channel, get_error_msg()); - PyErr_SetString(PyExc_RuntimeError, err); - return NULL; - } - if (direction == OUTPUT) { - if (gpio_set_value(gpio, initial) < 0) { + int gpio; + char *channel; + int direction; + int pud = PUD_OFF; + int initial = 0; + static char *kwlist[] = {"channel", "direction", "pull_up_down", "initial", NULL}; + + clear_error_msg(); + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "si|ii", kwlist, &channel, &direction, &pud, &initial)) + return NULL; + + if (!module_setup) { + init_module(); + } + + if (direction != INPUT && direction != OUTPUT) + { + PyErr_SetString(PyExc_ValueError, "An invalid direction was passed to setup()"); + return NULL; + } + + // Force pud to be off if we're configured for output + if (direction == OUTPUT) { + pud = PUD_OFF; + } + + if (pud != PUD_OFF && pud != PUD_DOWN && pud != PUD_UP) + { + PyErr_SetString(PyExc_ValueError, "Invalid value for pull_up_down - should be either PUD_OFF, PUD_UP or PUD_DOWN"); + return NULL; + } + + if (get_gpio_number(channel, &gpio) < 0) { + char err[2000]; + snprintf(err, sizeof(err), "Invalid channel %s. (%s)", channel, get_error_msg()); + PyErr_SetString(PyExc_ValueError, err); + return NULL; + } + + if (gpio_export(gpio) < 0) { + char err[2000]; + snprintf(err, sizeof(err), "Error setting up channel %s, maybe already exported? (%s)", channel, get_error_msg()); + PyErr_SetString(PyExc_RuntimeError, err); + return NULL; + } + if (gpio_set_direction(gpio, direction) < 0) { + char err[2000]; + snprintf(err, sizeof(err), "Error setting direction %d on channel %s. (%s)", direction, channel, get_error_msg()); + PyErr_SetString(PyExc_RuntimeError, err); + return NULL; + } + + // Pull Up/Down + int port, pin; + if (compute_port_pin(channel, gpio, &port, &pin) < 0) { + char err[2000]; + snprintf(err, sizeof(err), "Pull Up/Down setting not capable for %s. (%s)", channel, get_error_msg()); + PyErr_SetString(PyExc_ValueError, err); + return NULL; + } else { + // Set the PUD + gpio_set_pud(port, pin, pud); + // Check it was set properly + int pudr = gpio_get_pud(port, pin); + if (pudr != pud) { + char err[2000]; + snprintf(err, sizeof(err), "Error setting pull up down %d on channel %s", pud, channel); + PyErr_SetString(PyExc_RuntimeError, err); + return NULL; + } + } + + if (direction == OUTPUT) { + if (gpio_set_value(gpio, initial) < 0) { char err[2000]; snprintf(err, sizeof(err), "Error setting initial value %d on channel %s. (%s)", initial, channel, get_error_msg()); PyErr_SetString(PyExc_RuntimeError, err); return NULL; - } - } - - remember_gpio_direction(gpio, direction); - - Py_RETURN_NONE; + } + } + + remember_gpio_direction(gpio, direction); + + Py_RETURN_NONE; } /* py_setup_channel */ @@ -831,6 +875,7 @@ PyMethodDef gpio_methods[] = { {"selftest", py_selftest, METH_VARARGS, "Internal unit tests"}, {"direction", (PyCFunction)py_set_direction, METH_VARARGS | METH_KEYWORDS, "Change direction of gpio channel. Either INPUT or OUTPUT\n" }, {"setmode", (PyCFunction)py_setmode, METH_VARARGS, "Dummy function that does nothing but maintain compatibility with RPi.GPIO\n" }, + {"toggle_debug", py_toggle_debug, METH_VARARGS, "Toggles the enabling/disabling of Debug print output"}, {NULL, NULL, 0, NULL} }; diff --git a/source/py_softpwm.c b/source/py_softpwm.c index 14e87b6..15566f7 100644 --- a/source/py_softpwm.c +++ b/source/py_softpwm.c @@ -34,6 +34,15 @@ SOFTWARE. #include "common.h" #include "c_softpwm.h" +// python function toggle_debug() +static PyObject *py_toggle_debug(PyObject *self, PyObject *args) +{ + // toggle debug printing + toggle_debug(); + + Py_RETURN_NONE; +} + // python function cleanup(channel=None) static PyObject *py_cleanup(PyObject *self, PyObject *args) { @@ -48,8 +57,9 @@ static PyObject *py_cleanup(PyObject *self, PyObject *args) return NULL; // The !channel fixes issue #50 - if (!channel || strcmp(channel, "") == 0) { + if (channel == NULL || strcmp(channel, "\0") == 0) { softpwm_cleanup(); + return NULL; } else { if (!get_key(channel, key)) { PyErr_SetString(PyExc_ValueError, "Invalid SOFTPWM key or name."); @@ -195,11 +205,12 @@ static PyObject *py_set_frequency(PyObject *self, PyObject *args, PyObject *kwar static const char moduledocstring[] = "Software PWM functionality of a CHIP using Python"; PyMethodDef pwm_methods[] = { - { "start", (PyCFunction)py_start_channel, METH_VARARGS | METH_KEYWORDS, "Set up and start the PWM channel. channel can be in the form of 'XIO-P0', or 'U14_13'"}, - { "stop", (PyCFunction)py_stop_channel, METH_VARARGS | METH_KEYWORDS, "Stop the PWM channel. channel can be in the form of 'XIO-P0', or 'U14_13'"}, - { "set_duty_cycle", (PyCFunction)py_set_duty_cycle, METH_VARARGS, "Change the duty cycle\ndutycycle - between 0.0 and 100.0" }, - { "set_frequency", (PyCFunction)py_set_frequency, METH_VARARGS, "Change the frequency\nfrequency - frequency in Hz (freq > 0.0)" }, - { "cleanup", (PyCFunction)py_cleanup, METH_VARARGS, "Clean up by resetting all GPIO channels that have been used by this program to INPUT with no pullup/pulldown and no event detection"}, + {"start", (PyCFunction)py_start_channel, METH_VARARGS | METH_KEYWORDS, "Set up and start the PWM channel. channel can be in the form of 'XIO-P0', or 'U14_13'"}, + {"stop", (PyCFunction)py_stop_channel, METH_VARARGS | METH_KEYWORDS, "Stop the PWM channel. channel can be in the form of 'XIO-P0', or 'U14_13'"}, + {"set_duty_cycle", (PyCFunction)py_set_duty_cycle, METH_VARARGS, "Change the duty cycle\ndutycycle - between 0.0 and 100.0" }, + {"set_frequency", (PyCFunction)py_set_frequency, METH_VARARGS, "Change the frequency\nfrequency - frequency in Hz (freq > 0.0)" }, + {"cleanup", (PyCFunction)py_cleanup, METH_VARARGS, "Clean up by resetting all GPIO channels that have been used by this program to INPUT with no pullup/pulldown and no event detection"}, + {"toggle_debug", py_toggle_debug, METH_VARARGS, "Toggles the enabling/disabling of Debug print output"}, {NULL, NULL, 0, NULL} };