From 4af43b90f7e9cdf228258d019d44e63c1e9415a4 Mon Sep 17 00:00:00 2001 From: Chris Hager Date: Fri, 22 Feb 2013 23:02:36 +0100 Subject: [PATCH] c_gpio cleanup: pep7, added documentation, removed experimental event methods --- .gitignore | 4 +- source/c_gpio/c_gpio.c | 184 +++------- source/c_gpio/c_gpio.h | 49 --- source/c_gpio/py_gpio.c | 794 +++++++++++++++++----------------------- 4 files changed, 402 insertions(+), 629 deletions(-) delete mode 100644 source/c_gpio/c_gpio.h diff --git a/.gitignore b/.gitignore index 6574013..60ab825 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,5 @@ *.py[cod] -# C extensions -*.so - # Packages *.egg *.egg-info @@ -33,5 +30,6 @@ nosetests.xml .mr.developer.cfg .project .pydevproject +.cproject documentation/build/ diff --git a/source/c_gpio/c_gpio.c b/source/c_gpio/c_gpio.c index 7ef5a28..080cd5b 100644 --- a/source/c_gpio/c_gpio.c +++ b/source/c_gpio/c_gpio.c @@ -1,30 +1,15 @@ /* -Copyright (c) 2012-2013 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. -*/ +Based on c_gpio.c by Ben Croston. +Changes: +- PEP7 cleanup +- Added some documentation +- Removed experimental event detection methods +*/ #include #include #include #include -#include "c_gpio.h" #define BCM2708_PERI_BASE 0x20000000 #define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) @@ -43,27 +28,42 @@ SOFTWARE. #define PAGE_SIZE (4*1024) #define BLOCK_SIZE (4*1024) +#define SETUP_OK 0 +#define SETUP_DEVMEM_FAIL 1 +#define SETUP_MALLOC_FAIL 2 +#define SETUP_MMAP_FAIL 3 + +#define INPUT 1 // is really 0 for control register! +#define OUTPUT 0 // is really 1 for control register! +#define ALT0 4 + +#define HIGH 1 +#define LOW 0 + +#define PUD_OFF 0 +#define PUD_DOWN 1 +#define PUD_UP 2 + static volatile uint32_t *gpio_map; -void short_wait(void) +// `short_wait` waits 150 cycles +void +short_wait(void) { - int i; - - for (i=0; i<150; i++) // wait 150 cycles - { - asm volatile("nop"); + for (int i=0; i<150; i++) { + asm volatile("nop"); } } -int setup(void) +// `setup` is run when GPIO is imported in Python +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; @@ -79,95 +79,20 @@ int setup(void) return SETUP_OK; } -void clear_event_detect(int gpio) -{ - int offset = EVENT_DETECT_OFFSET + (gpio/32); - int shift = (gpio%32); - - *(gpio_map+offset) |= (1 << shift); - short_wait(); - *(gpio_map+offset) = 0; -} - -int event_detected(int gpio) -{ - int offset, value, bit; - - offset = EVENT_DETECT_OFFSET + (gpio/32); - bit = (1 << (gpio%32)); - value = *(gpio_map+offset) & bit; - if (value) - { - clear_event_detect(gpio); - } - return value; -} - -void set_rising_event(int gpio, int enable) -{ - int offset = RISING_ED_OFFSET + (gpio/32); - int shift = (gpio%32); - - if (enable) - *(gpio_map+offset) |= 1 << shift; - else - *(gpio_map+offset) &= ~(1 << shift); - clear_event_detect(gpio); -} - -void set_falling_event(int gpio, int enable) -{ - int offset = FALLING_ED_OFFSET + (gpio/32); - int shift = (gpio%32); - - if (enable) - { - *(gpio_map+offset) |= (1 << shift); - *(gpio_map+offset) = (1 << shift); - } else { - *(gpio_map+offset) &= ~(1 << shift); - } - clear_event_detect(gpio); -} - -void set_high_event(int gpio, int enable) -{ - int offset = HIGH_DETECT_OFFSET + (gpio/32); - int shift = (gpio%32); - - if (enable) - { - *(gpio_map+offset) |= (1 << shift); - } else { - *(gpio_map+offset) &= ~(1 << shift); - } - clear_event_detect(gpio); -} - -void set_low_event(int gpio, int enable) -{ - int offset = LOW_DETECT_OFFSET + (gpio/32); - int shift = (gpio%32); - - if (enable) - *(gpio_map+offset) |= 1 << shift; - else - *(gpio_map+offset) &= ~(1 << shift); - clear_event_detect(gpio); -} - -void set_pullupdn(int gpio, int pud) +// Sets a pullup or -down resistor on a GPIO +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(); @@ -175,7 +100,10 @@ void set_pullupdn(int gpio, int pud) *(gpio_map+clk_offset) = 0; } -void setup_gpio(int gpio, int direction, int pud) +// Sets a GPIO to either output or input (input can have an optional pullup +// or -down resistor). +void +setup_gpio(int gpio, int direction, int pud) { int offset = FSEL_OFFSET + (gpio/10); int shift = (gpio%10)*3; @@ -187,42 +115,44 @@ void setup_gpio(int gpio, int direction, int pud) *(gpio_map+offset) = (*(gpio_map+offset) & ~(7< -int gpio_function(int gpio) +int +gpio_function(int gpio) { - int offset = FSEL_OFFSET + (gpio/10); - int shift = (gpio%10)*3; - int value = *(gpio_map+offset); - value >>= shift; - value &= 7; - return value; // 0=input, 1=output, 4=alt0 + int offset = FSEL_OFFSET + (gpio/10); + int shift = (gpio%10)*3; + int value = *(gpio_map+offset); + value >>= shift; + value &= 7; + return value; } -void output_gpio(int gpio, int value) +// Sets a GPIO output to 1 or 0 +void +output_gpio(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; + *(gpio_map+offset) = 1 << gpio % 32; } -int input_gpio(int gpio) +// Returns the value of a GPIO input (1 or 0) +int +input_gpio(int gpio) { int offset, value, mask; - offset = PINLEVEL_OFFSET + (gpio/32); mask = (1 << gpio%32); value = *(gpio_map+offset) & mask; return value; } -void cleanup(void) +void +cleanup(void) { // fixme - set all gpios back to input munmap((caddr_t)gpio_map, BLOCK_SIZE); diff --git a/source/c_gpio/c_gpio.h b/source/c_gpio/c_gpio.h deleted file mode 100644 index 259f64a..0000000 --- a/source/c_gpio/c_gpio.h +++ /dev/null @@ -1,49 +0,0 @@ -/* -Copyright (c) 2012-2013 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 setup(void); -void setup_gpio(int gpio, int direction, int pud); -int gpio_function(int gpio); -void output_gpio(int gpio, int value); -int input_gpio(int gpio); -void set_rising_event(int gpio, int enable); -void set_falling_event(int gpio, int enable); -void set_high_event(int gpio, int enable); -void set_low_event(int gpio, int enable); -int event_detected(int gpio); -void cleanup(void); - -#define SETUP_OK 0 -#define SETUP_DEVMEM_FAIL 1 -#define SETUP_MALLOC_FAIL 2 -#define SETUP_MMAP_FAIL 3 - -#define INPUT 1 // is really 0 for control register! -#define OUTPUT 0 // is really 1 for control register! -#define ALT0 4 - -#define HIGH 1 -#define LOW 0 - -#define PUD_OFF 0 -#define PUD_DOWN 1 -#define PUD_UP 2 diff --git a/source/c_gpio/py_gpio.c b/source/c_gpio/py_gpio.c index bb7bb69..0761fe5 100644 --- a/source/c_gpio/py_gpio.c +++ b/source/c_gpio/py_gpio.c @@ -43,102 +43,94 @@ static PyObject *pud_down; static PyObject *rpi_revision; static PyObject *version; -static int gpio_direction[54]; +// Conversion from board_pin_id to gpio_id +// eg. gpio_id = *(*pin_to_gpio_rev2 + board_pin_id); static const int pin_to_gpio_rev1[27] = {-1, -1, -1, 0, -1, 1, -1, 4, 14, -1, 15, 17, 18, 21, -1, 22, 23, -1, 24, 10, -1, 9, 25, 11, 8, -1, 7}; static const int pin_to_gpio_rev2[27] = {-1, -1, -1, 2, -1, 3, -1, 4, 14, -1, 15, 17, 18, 27, -1, 22, 23, -1, 24, 10, -1, 9, 25, 11, 8, -1, 7}; static const int (*pin_to_gpio)[27]; + +// Flag whether to show warnings static int gpio_warnings = 1; +// Internal map of directions (in/out) per gpio to prevent user mistakes. +static int gpio_direction[54]; + +// GPIO Modes #define MODE_UNKNOWN -1 #define BOARD 10 #define BCM 11 static int gpio_mode = MODE_UNKNOWN; -// setup function run on import of the GPIO module -static int module_setup(void) +// module_setup is run on import of the GPIO module and calls the setup() method in c_gpio.c +static int +module_setup(void) { - int i, result; - -// printf("Setup module (mmap)\n"); - for (i=0; i<54; i++) - gpio_direction[i] = -1; - - result = setup(); - if (result == SETUP_DEVMEM_FAIL) - { - PyErr_SetString(PyExc_RuntimeError, "No access to /dev/mem. Try running as root!"); - return SETUP_DEVMEM_FAIL; - } else if (result == SETUP_MALLOC_FAIL) { - PyErr_NoMemory(); - return SETUP_MALLOC_FAIL; - } else if (result == SETUP_MMAP_FAIL) { - PyErr_SetString(PyExc_RuntimeError, "Mmap failed on module import"); - return SETUP_MALLOC_FAIL; - } else { // result == SETUP_OK - return SETUP_OK; - } + int result; + // printf("Setup module (mmap)\n"); + + // Set all gpios to input in internal direction (not the system) + for (int i=0; i<54; i++) + gpio_direction[i] = -1; + + result = setup(); + if (result == SETUP_DEVMEM_FAIL) { + PyErr_SetString(PyExc_RuntimeError, "No access to /dev/mem. Try running as root!"); + return SETUP_DEVMEM_FAIL; + } else if (result == SETUP_MALLOC_FAIL) { + PyErr_NoMemory(); + return SETUP_MALLOC_FAIL; + } else if (result == SETUP_MMAP_FAIL) { + PyErr_SetString(PyExc_RuntimeError, "Mmap failed on module import"); + return SETUP_MALLOC_FAIL; + } else { + // result == SETUP_OK + return SETUP_OK; + } } -// python function cleanup() -static PyObject *py_cleanup(PyObject *self, PyObject *args) +// Python function cleanup() +// Sets everything back to input +static PyObject* +py_cleanup(PyObject *self, PyObject *args) { - int i; - - // clear event registers - for (i=0; i<54; i++) - if (gpio_direction[i] == INPUT) - { -// printf("GPIO %d - clear events\n", i); - set_rising_event(i, 0); - set_falling_event(i, 0); - set_high_event(i, 0); - set_low_event(i, 0); - } - - // set everything back to input - for (i=0; i<54; i++) - if (gpio_direction[i] != -1) - { -// printf("GPIO %d --> INPUT\n", i); + for (int i=0; i<54; i++) { + if (gpio_direction[i] != -1) { + // printf("GPIO %d --> INPUT\n", i); setup_gpio(i, INPUT, PUD_OFF); gpio_direction[i] = -1; } - - Py_INCREF(Py_None); - return Py_None; + } + + Py_INCREF(Py_None); + return Py_None; } -static int verify_input(int channel, int *gpio) +static int +verify_input(int channel, int *gpio) { - if (gpio_mode != BOARD && gpio_mode != BCM) - { + if (gpio_mode != BOARD && gpio_mode != BCM) { PyErr_SetString(ModeNotSetException, "Please set pin numbering mode using GPIO.setmode(GPIO.BOARD) or GPIO.setmode(GPIO.BCM)"); return 0; } - if ( (gpio_mode == BCM && (channel < 0 || channel > 53)) - || (gpio_mode == BOARD && (channel < 1 || channel > 26)) ) - { + if ( (gpio_mode == BCM && (channel < 0 || channel > 53)) || + (gpio_mode == BOARD && (channel < 1 || channel > 26)) ) { PyErr_SetString(InvalidChannelException, "The channel sent is invalid on a Raspberry Pi"); return 0; } - if (gpio_mode == BOARD) - { + if (gpio_mode == BOARD) { *gpio = *(*pin_to_gpio+channel); - if (*gpio == -1) - { + if (*gpio == -1) { PyErr_SetString(InvalidChannelException, "The channel sent is invalid on a Raspberry Pi"); return 0; } - } - else // gpio_mode == BCM - { + } else { + // gpio_mode == BCM *gpio = channel; } - if ((gpio_direction[*gpio] != INPUT) && (gpio_direction[*gpio] != OUTPUT)) - { + if ((gpio_direction[*gpio] != INPUT) && (gpio_direction[*gpio] != OUTPUT)) { PyErr_SetString(WrongDirectionException, "GPIO channel has not been set up"); return 0; } @@ -146,372 +138,276 @@ static int verify_input(int channel, int *gpio) } // python function setup(channel, direction, pull_up_down=PUD_OFF, initial=None) -static PyObject *py_setup_channel(PyObject *self, PyObject *args, PyObject *kwargs) +static PyObject* +py_setup_channel(PyObject *self, PyObject *args, PyObject *kwargs) { - int gpio, channel, direction; - int pud = PUD_OFF; - int initial = -1; - static char *kwlist[] = {"channel", "direction", "pull_up_down", "initial", NULL}; - int func; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii|ii", kwlist, &channel, &direction, &pud, &initial)) - return NULL; - - if (direction != INPUT && direction != OUTPUT) - { - PyErr_SetString(InvalidDirectionException, "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(InvalidPullException, "Invalid value for pull_up_down - should be either PUD_OFF, PUD_UP or PUD_DOWN"); - return NULL; - } - - if (gpio_mode != BOARD && gpio_mode != BCM) - { - PyErr_SetString(ModeNotSetException, "Please set mode using GPIO.setmode(GPIO.BOARD) or GPIO.setmode(GPIO.BCM)"); - return NULL; - } - - if ( (gpio_mode == BCM && (channel < 0 || channel > 53)) - || (gpio_mode == BOARD && (channel < 1 || channel > 26)) ) - { - PyErr_SetString(InvalidChannelException, "The channel sent is invalid on a Raspberry Pi"); - return NULL; - } - - if (gpio_mode == BOARD) - { - gpio = *(*pin_to_gpio+channel); - if (gpio == -1) - { - PyErr_SetString(InvalidChannelException, "The channel sent is invalid on a Raspberry Pi"); - return NULL; - } - } - else // gpio_mode == BCM - { - gpio = channel; - } - - func = gpio_function(gpio); - if (gpio_warnings && // warnings enabled and - ((func != 0 && func != 1) || // (already one of the alt functions or - (gpio_direction[gpio] == -1 && func == 1))) // already an output not set from this program) - { - PyErr_WarnEx(NULL, "This channel is already in use, continuing anyway. Use GPIO.setwarnings(False) to disable warnings.", 1); - } - -// printf("Setup GPIO %d direction %d pud %d\n", gpio, direction, pud); - set_rising_event(gpio, 0); - set_falling_event(gpio, 0); - set_high_event(gpio, 0); - set_low_event(gpio, 0); - if (direction == OUTPUT && (initial == LOW || initial == HIGH)) - { -// printf("Writing intial value %d\n",initial); - output_gpio(gpio, initial); - } - setup_gpio(gpio, direction, pud); - gpio_direction[gpio] = direction; - - Py_INCREF(Py_None); - return Py_None; -} + int gpio, channel, direction; + int pud = PUD_OFF; + int initial = -1; + static char *kwlist[] = {"channel", "direction", "pull_up_down", "initial", NULL}; + int func; -// python function output(channel, value) -static PyObject *py_output_gpio(PyObject *self, PyObject *args) -{ - int gpio, channel, value; - - if (!PyArg_ParseTuple(args, "ii", &channel, &value)) - return NULL; - - if (gpio_mode != BOARD && gpio_mode != BCM) - { - PyErr_SetString(ModeNotSetException, "Please set mode using GPIO.setmode(GPIO.BOARD) or GPIO.setmode(GPIO.BCM)"); - return NULL; - } - - if ( (gpio_mode == BCM && (channel < 0 || channel > 53)) - || (gpio_mode == BOARD && (channel < 1 || channel > 26)) ) - { - PyErr_SetString(InvalidChannelException, "The channel sent is invalid on a Raspberry Pi"); - return NULL; - } - - if (gpio_mode == BOARD) - { - gpio = *(*pin_to_gpio+channel); - if (gpio == -1) - { - PyErr_SetString(InvalidChannelException, "The channel sent is invalid on a Raspberry Pi"); - return NULL; - } - } - else // gpio_mode == BCM - { - gpio = channel; - } - - if (gpio_direction[gpio] != OUTPUT) - { - PyErr_SetString(WrongDirectionException, "The GPIO channel has not been set up as an OUTPUT"); - return NULL; - } - -// printf("Output GPIO %d value %d\n", gpio, value); - output_gpio(gpio, value); - - Py_INCREF(Py_None); - return Py_None; -} + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii|ii", kwlist, &channel, &direction, &pud, &initial)) + return NULL; + if (direction != INPUT && direction != OUTPUT) { + PyErr_SetString(InvalidDirectionException, "An invalid direction was passed to setup()"); + return NULL; + } -// python function output(channel, value) without direction check -static PyObject *py_forceoutput_gpio(PyObject *self, PyObject *args) -{ - int gpio, channel, value; - - if (!PyArg_ParseTuple(args, "ii", &channel, &value)) - return NULL; - - if (gpio_mode != BOARD && gpio_mode != BCM) - { - PyErr_SetString(ModeNotSetException, "Please set mode using GPIO.setmode(GPIO.BOARD) or GPIO.setmode(GPIO.BCM)"); - return NULL; - } - - if ( (gpio_mode == BCM && (channel < 0 || channel > 53)) - || (gpio_mode == BOARD && (channel < 1 || channel > 26)) ) - { - PyErr_SetString(InvalidChannelException, "The channel sent is invalid on a Raspberry Pi"); - return NULL; - } - - if (gpio_mode == BOARD) - { - gpio = *(*pin_to_gpio+channel); - if (gpio == -1) - { - PyErr_SetString(InvalidChannelException, "The channel sent is invalid on a Raspberry Pi"); - return NULL; - } - } - else // gpio_mode == BCM - { - gpio = channel; - } - -// printf("Output GPIO %d value %d\n", gpio, value); - output_gpio(gpio, value); - - Py_INCREF(Py_None); - return Py_None; -} + if (direction == OUTPUT) + pud = PUD_OFF; -// python function value = input(channel) -static PyObject *py_input_gpio(PyObject *self, PyObject *args) -{ - int gpio, channel; + 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 (!PyArg_ParseTuple(args, "i", &channel)) - return NULL; + if (gpio_mode != BOARD && gpio_mode != BCM) { + PyErr_SetString(ModeNotSetException, "Please set mode using GPIO.setmode(GPIO.BOARD) or GPIO.setmode(GPIO.BCM)"); + return NULL; + } - if (!verify_input(channel, &gpio)) + if ( (gpio_mode == BCM && (channel < 0 || channel > 53)) + || (gpio_mode == BOARD && (channel < 1 || channel > 26)) ) + { + PyErr_SetString(InvalidChannelException, "The channel sent is invalid on a Raspberry Pi"); return NULL; - - // printf("Input GPIO %d\n", gpio); - if (input_gpio(gpio)) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; -} + } -// python function value = input(channel) without direction check -static PyObject *py_forceinput_gpio(PyObject *self, PyObject *args) -{ - int gpio; + if (gpio_mode == BOARD) + { + gpio = *(*pin_to_gpio+channel); + if (gpio == -1) + { + PyErr_SetString(InvalidChannelException, "The channel sent is invalid on a Raspberry Pi"); + return NULL; + } + } + else // gpio_mode == BCM + { + gpio = channel; + } + + func = gpio_function(gpio); + if (gpio_warnings && // warnings enabled and + ((func != 0 && func != 1) || // (already one of the alt functions or + (gpio_direction[gpio] == -1 && func == 1))) // already an output not set from this program) + { + PyErr_WarnEx(NULL, "This channel is already in use, continuing anyway. Use GPIO.setwarnings(False) to disable warnings.", 1); + } - if (!PyArg_ParseTuple(args, "i", &gpio)) - return NULL; +// printf("Setup GPIO %d direction %d pud %d\n", gpio, direction, pud); + if (direction == OUTPUT && (initial == LOW || initial == HIGH)) + { +// printf("Writing intial value %d\n",initial); + output_gpio(gpio, initial); + } + setup_gpio(gpio, direction, pud); + gpio_direction[gpio] = direction; - //printf("Input GPIO %d\n", gpio); - if (input_gpio(gpio)) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; + Py_INCREF(Py_None); + return Py_None; } -// python function setmode(mode) -static PyObject *setmode(PyObject *self, PyObject *args) +// python function output(channel, value) +static PyObject* +py_output_gpio(PyObject *self, PyObject *args) { - if (!PyArg_ParseTuple(args, "i", &gpio_mode)) - return NULL; + int gpio, channel, value; - if (gpio_mode != BOARD && gpio_mode != BCM) - { - PyErr_SetString(InvalidModeException, "An invalid mode was passed to setmode()"); - return NULL; - } + if (!PyArg_ParseTuple(args, "ii", &channel, &value)) + return NULL; - Py_INCREF(Py_None); - return Py_None; -} + if (gpio_mode != BOARD && gpio_mode != BCM) + { + PyErr_SetString(ModeNotSetException, "Please set mode using GPIO.setmode(GPIO.BOARD) or GPIO.setmode(GPIO.BCM)"); + return NULL; + } -// python function set_rising_event(channel, enable=True) -static PyObject *py_set_rising_event(PyObject *self, PyObject *args, PyObject *kwargs) -{ - int channel, gpio; - int enable = 1; - static char *kwlist[] = {"channel", "enable", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|i", kwlist, &channel, &enable)) + if ( (gpio_mode == BCM && (channel < 0 || channel > 53)) + || (gpio_mode == BOARD && (channel < 1 || channel > 26)) ) + { + PyErr_SetString(InvalidChannelException, "The channel sent is invalid on a Raspberry Pi"); return NULL; + } + + if (gpio_mode == BOARD) + { + gpio = *(*pin_to_gpio+channel); + if (gpio == -1) + { + PyErr_SetString(InvalidChannelException, "The channel sent is invalid on a Raspberry Pi"); + return NULL; + } + } + else // gpio_mode == BCM + { + gpio = channel; + } - if (!verify_input(channel, &gpio)) + if (gpio_direction[gpio] != OUTPUT) + { + PyErr_SetString(WrongDirectionException, "The GPIO channel has not been set up as an OUTPUT"); return NULL; - - set_rising_event(gpio, enable); - + } + +// printf("Output GPIO %d value %d\n", gpio, value); + output_gpio(gpio, value); + Py_INCREF(Py_None); return Py_None; } -// python function set_falling_event(channel, enable=True) -static PyObject *py_set_falling_event(PyObject *self, PyObject *args, PyObject *kwargs) + +// python function output(channel, value) without direction check +static PyObject* +py_forceoutput_gpio(PyObject *self, PyObject *args) { - int channel, gpio; - int enable = 1; - static char *kwlist[] = {"channel", "enable", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|i", kwlist, &channel, &enable)) + int gpio, channel, value; + + if (!PyArg_ParseTuple(args, "ii", &channel, &value)) return NULL; - if (!verify_input(channel, &gpio)) + if (gpio_mode != BOARD && gpio_mode != BCM) + { + PyErr_SetString(ModeNotSetException, "Please set mode using GPIO.setmode(GPIO.BOARD) or GPIO.setmode(GPIO.BCM)"); + return NULL; + } + + if ( (gpio_mode == BCM && (channel < 0 || channel > 53)) + || (gpio_mode == BOARD && (channel < 1 || channel > 26)) ) + { + PyErr_SetString(InvalidChannelException, "The channel sent is invalid on a Raspberry Pi"); return NULL; + } + + if (gpio_mode == BOARD) + { + gpio = *(*pin_to_gpio+channel); + if (gpio == -1) + { + PyErr_SetString(InvalidChannelException, "The channel sent is invalid on a Raspberry Pi"); + return NULL; + } + } + else // gpio_mode == BCM + { + gpio = channel; + } + +// printf("Output GPIO %d value %d\n", gpio, value); + output_gpio(gpio, value); - set_falling_event(gpio, enable); - Py_INCREF(Py_None); return Py_None; } -// python function set_high_event(channel, enable=True) -static PyObject *py_set_high_event(PyObject *self, PyObject *args, PyObject *kwargs) +// python function value = input(channel) +static PyObject* +py_input_gpio(PyObject *self, PyObject *args) { - int channel, gpio; - int enable = 1; - static char *kwlist[] = {"channel", "enable", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|i", kwlist, &channel, &enable)) + int gpio, channel; + + if (!PyArg_ParseTuple(args, "i", &channel)) return NULL; - if (!verify_input(channel, &gpio)) + if (!verify_input(channel, &gpio)) + return NULL; + + // printf("Input GPIO %d\n", gpio); + if (input_gpio(gpio)) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +// python function value = input(channel) without direction check +static PyObject* +py_forceinput_gpio(PyObject *self, PyObject *args) +{ + int gpio; + + if (!PyArg_ParseTuple(args, "i", &gpio)) return NULL; - set_high_event(gpio, enable); - - Py_INCREF(Py_None); - return Py_None; + //printf("Input GPIO %d\n", gpio); + if (input_gpio(gpio)) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; } -// python function set_low_event(channel, enable=True) -static PyObject *py_set_low_event(PyObject *self, PyObject *args, PyObject *kwargs) +// python function setmode(mode) +static PyObject* +setmode(PyObject *self, PyObject *args) { - int channel, gpio; - int enable = 1; - static char *kwlist[] = {"channel", "enable", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|i", kwlist, &channel, &enable)) + if (!PyArg_ParseTuple(args, "i", &gpio_mode)) return NULL; - if (!verify_input(channel, &gpio)) + if (gpio_mode != BOARD && gpio_mode != BCM) + { + PyErr_SetString(InvalidModeException, "An invalid mode was passed to setmode()"); return NULL; + } - set_low_event(gpio, enable); - Py_INCREF(Py_None); return Py_None; } -// python function value = event_detected(channel) -static PyObject *py_event_detected(PyObject *self, PyObject *args) +// python function value = gpio_function(gpio) +static PyObject* +py_gpio_function(PyObject *self, PyObject *args) { - int gpio, channel; + int gpio, f; + PyObject *func; - if (!PyArg_ParseTuple(args, "i", &channel)) - return NULL; - - if (!verify_input(channel, &gpio)) + if (!PyArg_ParseTuple(args, "i", &gpio)) return NULL; - - // printf("Detect event GPIO %d\n", gpio); - if (event_detected(gpio)) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; -} -// python function value = gpio_function(gpio) -static PyObject *py_gpio_function(PyObject *self, PyObject *args) -{ - int gpio, f; - PyObject *func; - - if (!PyArg_ParseTuple(args, "i", &gpio)) - return NULL; - - f = gpio_function(gpio); - switch (f) - { - case 0 : f = INPUT; break; - case 1 : f = OUTPUT; break; - } - func = Py_BuildValue("i", f); - return func; + f = gpio_function(gpio); + switch (f) + { + case 0 : f = INPUT; break; + case 1 : f = OUTPUT; break; + } + func = Py_BuildValue("i", f); + return func; } // python function setwarnings(state) -static PyObject *py_setwarnings(PyObject *self, PyObject *args) +static PyObject* +py_setwarnings(PyObject *self, PyObject *args) { - if (!PyArg_ParseTuple(args, "i", &gpio_warnings)) - return NULL; - Py_INCREF(Py_None); - return Py_None; + if (!PyArg_ParseTuple(args, "i", &gpio_warnings)) + return NULL; + Py_INCREF(Py_None); + return Py_None; } PyMethodDef rpi_gpio_methods[] = { - {"setup", (PyCFunction)py_setup_channel, METH_VARARGS | METH_KEYWORDS, "Set up the GPIO channel, direction and (optional) pull/up down control\nchannel - Either: RPi board pin number (not BCM GPIO 00..nn number). Pins start from 1\n or : BCM GPIO number\ndirection - INPUT or OUTPUT\n[pull_up_down] - PUD_OFF (default), PUD_UP or PUD_DOWN\n[initial] - Initial value for an output channel"}, - {"cleanup", 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"}, - {"output", py_output_gpio, METH_VARARGS, "Output to a GPIO channel"}, - {"forceoutput", py_forceoutput_gpio, METH_VARARGS, "Force output to a GPIO channel,\nignoring whether it has been set up before."}, - {"input", py_input_gpio, METH_VARARGS, "Input from a GPIO channel"}, - {"forceinput", py_forceinput_gpio, METH_VARARGS, "Force read input from a GPIO channel,\nignoring whether it was set up before."}, - {"setmode", setmode, METH_VARARGS, "Set up numbering mode to use for channels.\nBOARD - Use Raspberry Pi board numbers\nBCM - Use Broadcom GPIO 00..nn numbers"}, - {"set_rising_event", (PyCFunction)py_set_rising_event, METH_VARARGS | METH_KEYWORDS, "EXPERIMENTAL. Set rising edge detection\nchannel - Either: RPi board pin number (not BCM GPIO 00..nn number). Pins start from 1\n or : BCM GPIO number\n[enable] - True (default) or False"}, - {"set_falling_event", (PyCFunction)py_set_falling_event, METH_VARARGS | METH_KEYWORDS, "EXPERIMENTAL. Set falling edge detection\nchannel - Either: RPi board pin number (not BCM GPIO 00..nn number). Pins start from 1\n or : BCM GPIO number\n[enable] - True (default) or False"}, - {"set_high_event", (PyCFunction)py_set_high_event, METH_VARARGS | METH_KEYWORDS, "EXPERIMENTAL. Set high detection\nchannel - Either: RPi board pin number (not BCM GPIO 00..nn number). Pins start from 1\n or : BCM GPIO number\n[enable] - True (default) or False"}, - {"set_low_event", (PyCFunction)py_set_low_event, METH_VARARGS | METH_KEYWORDS, "EXPERIMENTAL. Set low detection\nchannel - Either: RPi board pin number (not BCM GPIO 00..nn number). Pins start from 1\n or : BCM GPIO number\n[enable] - True (default) or False"}, - {"event_detected", py_event_detected, METH_VARARGS, "EXPERIMENTAL. Returns True if an event has occured"}, - {"gpio_function", py_gpio_function, METH_VARARGS, "Return the current GPIO function (IN, OUT, ALT0)"}, - {"setwarnings", py_setwarnings, METH_VARARGS, "Enable or disable warning messages"}, - {NULL, NULL, 0, NULL} + {"setup", (PyCFunction)py_setup_channel, METH_VARARGS | METH_KEYWORDS, "Set up the GPIO channel, direction and (optional) pull/up down control\nchannel - Either: RPi board pin number (not BCM GPIO 00..nn number). Pins start from 1\n or : BCM GPIO number\ndirection - INPUT or OUTPUT\n[pull_up_down] - PUD_OFF (default), PUD_UP or PUD_DOWN\n[initial] - Initial value for an output channel"}, + {"cleanup", 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"}, + {"output", py_output_gpio, METH_VARARGS, "Output to a GPIO channel"}, + {"input", py_input_gpio, METH_VARARGS, "Input from a GPIO channel"}, + {"setmode", setmode, METH_VARARGS, "Set up numbering mode to use for channels.\nBOARD - Use Raspberry Pi board numbers\nBCM - Use Broadcom GPIO 00..nn numbers"}, + {"gpio_function", py_gpio_function, METH_VARARGS, "Return the current GPIO function (IN, OUT, ALT0)"}, + {"setwarnings", py_setwarnings, METH_VARARGS, "Enable or disable warning messages"}, + + // New methods in RPIO + {"forceoutput", py_forceoutput_gpio, METH_VARARGS, "Force output to a GPIO channel,\nignoring whether it has been set up before."}, + {"forceinput", py_forceinput_gpio, METH_VARARGS, "Force read input from a GPIO channel,\nignoring whether it was set up before."}, + {NULL, NULL, 0, NULL} }; #if PY_MAJOR_VERSION > 2 static struct PyModuleDef rpigpiomodule = { - PyModuleDef_HEAD_INIT, - "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. */ - rpi_gpio_methods + PyModuleDef_HEAD_INIT, + "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. */ + rpi_gpio_methods }; #endif @@ -521,110 +417,108 @@ PyMODINIT_FUNC PyInit_GPIO(void) PyMODINIT_FUNC initGPIO(void) #endif { - PyObject *module = NULL; - int revision = -1; + PyObject *module = NULL; + int revision = -1; #if PY_MAJOR_VERSION > 2 - if ((module = PyModule_Create(&rpigpiomodule)) == NULL) - goto exit; + if ((module = PyModule_Create(&rpigpiomodule)) == NULL) + goto exit; #else - if ((module = Py_InitModule("GPIO", rpi_gpio_methods)) == NULL) - goto exit; + if ((module = Py_InitModule("GPIO", rpi_gpio_methods)) == NULL) + goto exit; #endif - WrongDirectionException = PyErr_NewException("GPIO.WrongDirectionException", NULL, NULL); - PyModule_AddObject(module, "WrongDirectionException", WrongDirectionException); - - InvalidModeException = PyErr_NewException("GPIO.InvalidModeException", NULL, NULL); - PyModule_AddObject(module, "InvalidModeException", InvalidModeException); - - InvalidDirectionException = PyErr_NewException("GPIO.InvalidDirectionException", NULL, NULL); - PyModule_AddObject(module, "InvalidDirectionException", InvalidDirectionException); - - InvalidChannelException = PyErr_NewException("GPIO.InvalidChannelException", NULL, NULL); - PyModule_AddObject(module, "InvalidChannelException", InvalidChannelException); - - InvalidPullException = PyErr_NewException("GPIO.InvalidPullException", NULL, NULL); - PyModule_AddObject(module, "InvalidPullException", InvalidPullException); - - ModeNotSetException = PyErr_NewException("GPIO.ModeNotSetException", NULL, NULL); - PyModule_AddObject(module, "ModeNotSetException", ModeNotSetException); - - high = Py_BuildValue("i", HIGH); - PyModule_AddObject(module, "HIGH", high); - - low = Py_BuildValue("i", LOW); - PyModule_AddObject(module, "LOW", low); - - output = Py_BuildValue("i", OUTPUT); - PyModule_AddObject(module, "OUT", output); - - input = Py_BuildValue("i", INPUT); - PyModule_AddObject(module, "IN", input); - - alt0 = Py_BuildValue("i", ALT0); - PyModule_AddObject(module, "ALT0", alt0); - - board = Py_BuildValue("i", BOARD); - PyModule_AddObject(module, "BOARD", board); - - bcm = Py_BuildValue("i", BCM); - PyModule_AddObject(module, "BCM", bcm); - - 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(PyExc_SystemError, "This module can only be run on a Raspberry Pi!"); + WrongDirectionException = PyErr_NewException("GPIO.WrongDirectionException", NULL, NULL); + PyModule_AddObject(module, "WrongDirectionException", WrongDirectionException); + + InvalidModeException = PyErr_NewException("GPIO.InvalidModeException", NULL, NULL); + PyModule_AddObject(module, "InvalidModeException", InvalidModeException); + + InvalidDirectionException = PyErr_NewException("GPIO.InvalidDirectionException", NULL, NULL); + PyModule_AddObject(module, "InvalidDirectionException", InvalidDirectionException); + + InvalidChannelException = PyErr_NewException("GPIO.InvalidChannelException", NULL, NULL); + PyModule_AddObject(module, "InvalidChannelException", InvalidChannelException); + + InvalidPullException = PyErr_NewException("GPIO.InvalidPullException", NULL, NULL); + PyModule_AddObject(module, "InvalidPullException", InvalidPullException); + + ModeNotSetException = PyErr_NewException("GPIO.ModeNotSetException", NULL, NULL); + PyModule_AddObject(module, "ModeNotSetException", ModeNotSetException); + + high = Py_BuildValue("i", HIGH); + PyModule_AddObject(module, "HIGH", high); + + low = Py_BuildValue("i", LOW); + PyModule_AddObject(module, "LOW", low); + + output = Py_BuildValue("i", OUTPUT); + PyModule_AddObject(module, "OUT", output); + + input = Py_BuildValue("i", INPUT); + PyModule_AddObject(module, "IN", input); + + alt0 = Py_BuildValue("i", ALT0); + PyModule_AddObject(module, "ALT0", alt0); + + board = Py_BuildValue("i", BOARD); + PyModule_AddObject(module, "BOARD", board); + + bcm = Py_BuildValue("i", BCM); + PyModule_AddObject(module, "BCM", bcm); + + 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(PyExc_SystemError, "This module can only be run on a Raspberry Pi!"); #if PY_MAJOR_VERSION > 2 - return NULL; + return NULL; #else - return; + return; #endif - } else if (revision == 1) { - pin_to_gpio = &pin_to_gpio_rev1; - } else { // assume revision 2 - pin_to_gpio = &pin_to_gpio_rev2; - } - rpi_revision = Py_BuildValue("i", revision); - PyModule_AddObject(module, "RPI_REVISION", rpi_revision); - - version = Py_BuildValue("s", "0.4.2a"); - PyModule_AddObject(module, "VERSION", version); - - // set up mmaped areas - if (module_setup() != SETUP_OK ) - { + } else if (revision == 1) { + pin_to_gpio = &pin_to_gpio_rev1; + } else { // assume revision 2 + pin_to_gpio = &pin_to_gpio_rev2; + } + rpi_revision = Py_BuildValue("i", revision); + PyModule_AddObject(module, "RPI_REVISION", rpi_revision); + + version = Py_BuildValue("s", "0.4.2a"); + PyModule_AddObject(module, "VERSION", version); + + // set up mmaped areas + if (module_setup() != SETUP_OK ) { #if PY_MAJOR_VERSION > 2 - return NULL; + return NULL; #else - return; + return; #endif - } - - if (Py_AtExit(cleanup) != 0) - { - cleanup(); + } + + if (Py_AtExit(cleanup) != 0) { + cleanup(); #if PY_MAJOR_VERSION > 2 - return NULL; + return NULL; #else - return; + return; #endif - } + } exit: #if PY_MAJOR_VERSION > 2 - return module; + return module; #else - return; + return; #endif }