├── CHANGELOG.txt ├── INSTALL.txt ├── LICENCE.txt ├── PKG-INFO ├── README.md ├── README.txt ├── RPi ├── GPIO │ └── __init__.py ├── I2C │ └── __init__.py ├── SPI │ └── __init__.py └── __init__.py ├── SPI └── __init__.py ├── create_gpio_user_permissions.py ├── setup.py ├── source ├── c_gpio.c ├── c_gpio.h ├── common.c ├── common.h ├── constants.c ├── constants.h ├── cpuinfo.c ├── cpuinfo.h ├── event_gpio.c ├── event_gpio.h ├── i2c │ ├── __init__.py │ ├── i2c.c │ ├── i2c_lib.c │ └── i2c_lib.h ├── py_gpio.c ├── py_pwm.c ├── py_pwm.h ├── soft_pwm.c ├── soft_pwm.h └── spi │ ├── spi.c │ ├── spi_lib.c │ └── spi_lib.h └── test └── test.py /CHANGELOG.txt: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | 4 | 0.6.2 5 | ----- 6 | - Rewrote Debian packaging mechanism 7 | - RPI_INFO reports Pi 3 8 | - Changed module layout - moved C components to RPi._GPIO 9 | 10 | 0.6.1 11 | ----- 12 | - Update RPI_INFO to detect more board types 13 | - Issue 118 - add_event_detect sometimes gives runtime error with unpriv user 14 | - Issue 120 - setmode() remembers invalid mode 15 | 16 | 0.6.0a3 17 | ------- 18 | - Now uses /dev/gpiomem if available to avoid being run as root 19 | - Fix warnings with pull up/down on pins 3/5 20 | - Correct base address on Pi 2 when devicetree is disabled 21 | - caddr_t error on compile (Issue 109) 22 | - Error on invalid parameters to setup() (issue 93) 23 | - Add timeout parameter to wait_for_edge() (issue 91) 24 | 25 | 0.5.11 26 | ------ 27 | - Fix - pins > 26 missing when using BOARD mode 28 | - Add getmode() 29 | - Raise exception when a mix of modes is used 30 | - GPIO.cleanaup() unsets the current pin mode 31 | 32 | 0.5.10 33 | ------ 34 | - Issue 95 - support RPi 2 boards 35 | - Introduce RPI_INFO 36 | - Deprecate RPI_REVISION 37 | - Issue 97 - fixed docstring for setup() 38 | 39 | 0.5.9 40 | ----- 41 | - Issue 87 - warn about pull up/down on i2c pins 42 | - Issue 86/75 - wait_for_edge() bugfix 43 | - Issue 84 - recognise RPi properly when using a custom kernel 44 | - Issue 90 - cleanup() on a list/tuple of channels 45 | 46 | 0.5.8 47 | ----- 48 | - Allow lists/tuples of channels in GPIO.setup() 49 | - GPIO.output() now allows lists/tuples of values 50 | - GPIO.wait_for_edge() bug fixes (issue 78) 51 | 52 | 0.5.7 53 | ----- 54 | - Issue 67 - speed up repeated calls to GPIO.wait_for_event() 55 | - Added bouncetime keyword to GPIO.wait_for_event() 56 | - Added extra edge/interrupt unit tests 57 | - GPIO.wait_for_event() can now be mixed with GPIO.add_event_detect() 58 | - Improved cleanups of events 59 | - Issue 69 resolved 60 | 61 | 0.5.6 62 | ----- 63 | - Issue 68 - support for RPi Model B+ 64 | - Fix gpio_function() 65 | 66 | 0.5.5 67 | ----- 68 | - Issue 52 - 'unallocate' a channel 69 | - Issue 35 - use switchbounce with GPIO.event_detected() 70 | - Refactored events code 71 | - Rewrote tests to use unittest mechanism and new test board with loopbacks 72 | - Fixed adding events after a GPIO.cleanup() 73 | - Issue 64 - misleading /dev/mem permissions error 74 | - Issue 59 - name collision with PWM constant and class 75 | 76 | 0.5.4 77 | ----- 78 | - Changed release status (from alpha to full release) 79 | - Warn when GPIO.cleanup() used with nothing to clean up (issue 44) 80 | - Avoid collisions in constants (e.g. HIGH / RISING / PUD_DOWN) 81 | - Accept BOARD numbers in gpio_function (issue 34) 82 | - More return values for gpio_function (INPUT, OUTPUT, SPI, I2C, PWM, SERIAL, UNKNOWN) 83 | - Tidy up docstrings 84 | - Fix /dev/mem access error with gpio_function 85 | 86 | 0.5.3a 87 | ------ 88 | - Allow pydoc for non-root users (issue 27) 89 | - Fix add_event_detect error when run as daemon (issue 32) 90 | - Simplified exception types 91 | - Changed from distribute to pip 92 | 93 | 0.5.2a 94 | ------ 95 | - Added software PWM (experimental) 96 | - Added switch bounce handling to event callbacks 97 | - Added channel number parameter to event callbacks (issue 31) 98 | - Internal refactoring and code tidy up 99 | 100 | 0.5.1a 101 | ------ 102 | - Fixed callbacks for multiple GPIOs (issue 28) 103 | 104 | 0.5.0a 105 | ------ 106 | - Added new edge detection events (interrupt handling) 107 | - Added add_event_detect() 108 | - Added remove_event_detect() 109 | - Added add_event_callback() 110 | - Added wait_for_edge() 111 | - Removed old experimental event functions 112 | - Removed set_rising_event() 113 | - Removed set_falling_event() 114 | - Removed set_high_event() 115 | - Removed set_low_event() 116 | - Changed event_detected() for new edge detection functionality 117 | - input() now returns 0/LOW == False or 1/HIGH == True (integers) instead of False or True (booleans). 118 | - Fix error on repeated import (issue 3) 119 | - Change SetupException to a RuntimeError so it can be caught on import (issue 25, Chris Hager ) 120 | - Improved docstrings of functions 121 | 122 | 0.4.2a 123 | ------ 124 | - Fix for installing on Arch Linux (Python 3.3) (issue 20) 125 | - Initial value when setting a channel as an output (issue 19) 126 | 127 | 0.4.1a 128 | ------ 129 | - Added VERSION 130 | - Permit input() of channels set as outputs (Eric Ptak ) 131 | 132 | 0.4.0a 133 | ------ 134 | - Added support for Revision 2 boards 135 | - Added RPI_REVISION 136 | - Added cleanup() function and removed automatic reset functionality on program exit 137 | - Added get_function() to read existing GPIO channel functionality (suggestion from Eric Ptak ) 138 | - Added set_rising_event() 139 | - Added set_falling_event() 140 | - Added set_high_event() 141 | - Added set_low_event() 142 | - Added event_detected() 143 | - Added test/test.py 144 | - Converted debian to armhf 145 | - Fixed C function short_wait() (thanks to Thibault Porteboeuf ) 146 | 147 | 0.3.1a 148 | ------ 149 | - Fixed critical bug with swapped high/low state on outputs 150 | - Added pull-up / pull-down setup functionality for inputs 151 | 152 | 0.3.0a 153 | ------ 154 | - Rewritten as a C extension 155 | - Now uses /dev/mem and SoC registers instead of /sys/class/gpio 156 | - Faster! 157 | - Make call to GPIO.setmode() mandatory 158 | - Added GPIO.HIGH and GPIO.LOW constants 159 | 160 | 0.2.0 161 | ----- 162 | - Changed status from alpha to beta 163 | - Added setmode() to be able to use BCM GPIO 00.nn channel numbers 164 | - Renamed InvalidPinException to InvalidChannelException 165 | 166 | 0.1.0 167 | ------ 168 | - Fixed direction bug 169 | - Added MANIFEST.in (to include missing file) 170 | - Changed GPIO channel number to pin number 171 | - Tested and working! 172 | 173 | 0.0.3a 174 | ------ 175 | - Added GPIO table 176 | - Refactored 177 | - Fixed a few critical bugs 178 | - Still completely untested! 179 | 180 | 0.0.2a 181 | ------ 182 | - Internal refactoring. Still completely untested! 183 | 184 | 0.0.1a 185 | ------ 186 | - First version. Completely untested until I can get hold of a Raspberry Pi! 187 | 188 | -------------------------------------------------------------------------------- /INSTALL.txt: -------------------------------------------------------------------------------- 1 | If you are using Raspbian, all you need to do to install the latest release is: 2 | $ sudo apt-get update 3 | $ sudo apt-get install python-rpi.gpio python3-rpi.gpio 4 | 5 | ------------ 6 | 7 | If you are not using Raspbian, it is recommended that you install this module using pip: 8 | $ sudo pip install RPi.GPIO 9 | 10 | ------------ 11 | 12 | If you want to build your own version from this downloaded copy, make 13 | sure that you have the Python development source installed first! 14 | 15 | On Raspbian: 16 | $ sudo apt-get install python-dev python3-dev 17 | 18 | To install the module: 19 | 20 | $ sudo python setup.py install 21 | or 22 | $ sudo python3 setup.py install 23 | -------------------------------------------------------------------------------- /LICENCE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2014 Ben Croston 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /PKG-INFO: -------------------------------------------------------------------------------- 1 | Metadata-Version: 1.1 2 | Name: RPi.GPIO 3 | Version: 0.6.2 4 | Summary: A module to control Raspberry Pi GPIO channels 5 | Home-page: http://sourceforge.net/projects/raspberry-gpio-python/ 6 | Author: Ben Croston 7 | Author-email: ben@croston.org 8 | License: MIT 9 | Description: This package provides a class to control the GPIO on a Raspberry Pi. 10 | 11 | Note that this module is unsuitable for real-time or timing critical applications. This is because you 12 | can not predict when Python will be busy garbage collecting. It also runs under the Linux kernel which 13 | is not suitable for real time applications - it is multitasking O/S and another process may be given 14 | priority over the CPU, causing jitter in your program. If you are after true real-time performance and 15 | predictability, buy yourself an Arduino http://www.arduino.cc ! 16 | 17 | Note that the current release does not support SPI, I2C, hardware PWM or serial functionality on the RPi yet. 18 | This is planned for the near future - watch this space! One-wire functionality is also planned. 19 | 20 | Although hardware PWM is not available yet, software PWM is available to use on all channels. 21 | 22 | For examples and documentation, visit http://sourceforge.net/p/raspberry-gpio-python/wiki/Home/ 23 | 24 | Change Log 25 | ========== 26 | 27 | 0.6.2 28 | ----- 29 | - Rewrote Debian packaging mechanism 30 | - RPI_INFO reports Pi 3 31 | - Changed module layout - moved C components to RPi._GPIO 32 | 33 | 0.6.1 34 | ----- 35 | - Update RPI_INFO to detect more board types 36 | - Issue 118 - add_event_detect sometimes gives runtime error with unpriv user 37 | - Issue 120 - setmode() remembers invalid mode 38 | 39 | 0.6.0a3 40 | ------- 41 | - Now uses /dev/gpiomem if available to avoid being run as root 42 | - Fix warnings with pull up/down on pins 3/5 43 | - Correct base address on Pi 2 when devicetree is disabled 44 | - caddr_t error on compile (Issue 109) 45 | - Error on invalid parameters to setup() (issue 93) 46 | - Add timeout parameter to wait_for_edge() (issue 91) 47 | 48 | 0.5.11 49 | ------ 50 | - Fix - pins > 26 missing when using BOARD mode 51 | - Add getmode() 52 | - Raise exception when a mix of modes is used 53 | - GPIO.cleanaup() unsets the current pin mode 54 | 55 | 0.5.10 56 | ------ 57 | - Issue 95 - support RPi 2 boards 58 | - Introduce RPI_INFO 59 | - Deprecate RPI_REVISION 60 | - Issue 97 - fixed docstring for setup() 61 | 62 | 0.5.9 63 | ----- 64 | - Issue 87 - warn about pull up/down on i2c pins 65 | - Issue 86/75 - wait_for_edge() bugfix 66 | - Issue 84 - recognise RPi properly when using a custom kernel 67 | - Issue 90 - cleanup() on a list/tuple of channels 68 | 69 | 0.5.8 70 | ----- 71 | - Allow lists/tuples of channels in GPIO.setup() 72 | - GPIO.output() now allows lists/tuples of values 73 | - GPIO.wait_for_edge() bug fixes (issue 78) 74 | 75 | 0.5.7 76 | ----- 77 | - Issue 67 - speed up repeated calls to GPIO.wait_for_event() 78 | - Added bouncetime keyword to GPIO.wait_for_event() 79 | - Added extra edge/interrupt unit tests 80 | - GPIO.wait_for_event() can now be mixed with GPIO.add_event_detect() 81 | - Improved cleanups of events 82 | - Issue 69 resolved 83 | 84 | 0.5.6 85 | ----- 86 | - Issue 68 - support for RPi Model B+ 87 | - Fix gpio_function() 88 | 89 | 0.5.5 90 | ----- 91 | - Issue 52 - 'unallocate' a channel 92 | - Issue 35 - use switchbounce with GPIO.event_detected() 93 | - Refactored events code 94 | - Rewrote tests to use unittest mechanism and new test board with loopbacks 95 | - Fixed adding events after a GPIO.cleanup() 96 | - Issue 64 - misleading /dev/mem permissions error 97 | - Issue 59 - name collision with PWM constant and class 98 | 99 | 0.5.4 100 | ----- 101 | - Changed release status (from alpha to full release) 102 | - Warn when GPIO.cleanup() used with nothing to clean up (issue 44) 103 | - Avoid collisions in constants (e.g. HIGH / RISING / PUD_DOWN) 104 | - Accept BOARD numbers in gpio_function (issue 34) 105 | - More return values for gpio_function (INPUT, OUTPUT, SPI, I2C, PWM, SERIAL, UNKNOWN) 106 | - Tidy up docstrings 107 | - Fix /dev/mem access error with gpio_function 108 | 109 | 0.5.3a 110 | ------ 111 | - Allow pydoc for non-root users (issue 27) 112 | - Fix add_event_detect error when run as daemon (issue 32) 113 | - Simplified exception types 114 | - Changed from distribute to pip 115 | 116 | 0.5.2a 117 | ------ 118 | - Added software PWM (experimental) 119 | - Added switch bounce handling to event callbacks 120 | - Added channel number parameter to event callbacks (issue 31) 121 | - Internal refactoring and code tidy up 122 | 123 | 0.5.1a 124 | ------ 125 | - Fixed callbacks for multiple GPIOs (issue 28) 126 | 127 | 0.5.0a 128 | ------ 129 | - Added new edge detection events (interrupt handling) 130 | - Added add_event_detect() 131 | - Added remove_event_detect() 132 | - Added add_event_callback() 133 | - Added wait_for_edge() 134 | - Removed old experimental event functions 135 | - Removed set_rising_event() 136 | - Removed set_falling_event() 137 | - Removed set_high_event() 138 | - Removed set_low_event() 139 | - Changed event_detected() for new edge detection functionality 140 | - input() now returns 0/LOW == False or 1/HIGH == True (integers) instead of False or True (booleans). 141 | - Fix error on repeated import (issue 3) 142 | - Change SetupException to a RuntimeError so it can be caught on import (issue 25, Chris Hager ) 143 | - Improved docstrings of functions 144 | 145 | 0.4.2a 146 | ------ 147 | - Fix for installing on Arch Linux (Python 3.3) (issue 20) 148 | - Initial value when setting a channel as an output (issue 19) 149 | 150 | 0.4.1a 151 | ------ 152 | - Added VERSION 153 | - Permit input() of channels set as outputs (Eric Ptak ) 154 | 155 | 0.4.0a 156 | ------ 157 | - Added support for Revision 2 boards 158 | - Added RPI_REVISION 159 | - Added cleanup() function and removed automatic reset functionality on program exit 160 | - Added get_function() to read existing GPIO channel functionality (suggestion from Eric Ptak ) 161 | - Added set_rising_event() 162 | - Added set_falling_event() 163 | - Added set_high_event() 164 | - Added set_low_event() 165 | - Added event_detected() 166 | - Added test/test.py 167 | - Converted debian to armhf 168 | - Fixed C function short_wait() (thanks to Thibault Porteboeuf ) 169 | 170 | 0.3.1a 171 | ------ 172 | - Fixed critical bug with swapped high/low state on outputs 173 | - Added pull-up / pull-down setup functionality for inputs 174 | 175 | 0.3.0a 176 | ------ 177 | - Rewritten as a C extension 178 | - Now uses /dev/mem and SoC registers instead of /sys/class/gpio 179 | - Faster! 180 | - Make call to GPIO.setmode() mandatory 181 | - Added GPIO.HIGH and GPIO.LOW constants 182 | 183 | 0.2.0 184 | ----- 185 | - Changed status from alpha to beta 186 | - Added setmode() to be able to use BCM GPIO 00.nn channel numbers 187 | - Renamed InvalidPinException to InvalidChannelException 188 | 189 | 0.1.0 190 | ------ 191 | - Fixed direction bug 192 | - Added MANIFEST.in (to include missing file) 193 | - Changed GPIO channel number to pin number 194 | - Tested and working! 195 | 196 | 0.0.3a 197 | ------ 198 | - Added GPIO table 199 | - Refactored 200 | - Fixed a few critical bugs 201 | - Still completely untested! 202 | 203 | 0.0.2a 204 | ------ 205 | - Internal refactoring. Still completely untested! 206 | 207 | 0.0.1a 208 | ------ 209 | - First version. Completely untested until I can get hold of a Raspberry Pi! 210 | 211 | 212 | Keywords: Raspberry Pi GPIO 213 | Platform: UNKNOWN 214 | Classifier: Development Status :: 5 - Production/Stable 215 | Classifier: Operating System :: POSIX :: Linux 216 | Classifier: License :: OSI Approved :: MIT License 217 | Classifier: Intended Audience :: Developers 218 | Classifier: Programming Language :: Python :: 2.7 219 | Classifier: Programming Language :: Python :: 3 220 | Classifier: Topic :: Software Development 221 | Classifier: Topic :: Home Automation 222 | Classifier: Topic :: System :: Hardware 223 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RPi.GPIO-PineA64 2 | 3 | This is a RPi.GPIO module for Pine A64/A64+ board. 4 | 5 | - GPIO input and output 6 | - GPIO interrupts(callbacks when events occur on input gpios) Not Implemented yet!!! 7 | - Software PWM 8 | 9 | Install this package by executing: 10 | ```` 11 | sudo python setup.py install 12 | ```` 13 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | This package provides a class to control the GPIO on a Raspberry Pi. 2 | 3 | Note that this module is unsuitable for real-time or timing critical applications. This is because you 4 | can not predict when Python will be busy garbage collecting. It also runs under the Linux kernel which 5 | is not suitable for real time applications - it is multitasking O/S and another process may be given 6 | priority over the CPU, causing jitter in your program. If you are after true real-time performance and 7 | predictability, buy yourself an Arduino http://www.arduino.cc ! 8 | 9 | Note that the current release does not support SPI, I2C, hardware PWM or serial functionality on the RPi yet. 10 | This is planned for the near future - watch this space! One-wire functionality is also planned. 11 | 12 | Although hardware PWM is not available yet, software PWM is available to use on all channels. 13 | 14 | For examples and documentation, visit http://sourceforge.net/p/raspberry-gpio-python/wiki/Home/ 15 | 16 | -------------------------------------------------------------------------------- /RPi/GPIO/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2012-2016 Ben Croston 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | """ 22 | 23 | from RPi._GPIO import * 24 | 25 | VERSION = '0.6.2' 26 | -------------------------------------------------------------------------------- /RPi/I2C/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | """ 3 | 4 | from RPi._I2C import * 5 | 6 | VERSION = '0.6.2' 7 | -------------------------------------------------------------------------------- /RPi/SPI/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | """ 3 | 4 | from RPi._SPI import * 5 | 6 | VERSION = '0.6.2' 7 | -------------------------------------------------------------------------------- /RPi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swkim01/RPi.GPIO-PineA64/50769acbee82e394c3646ae030f5bf0767906ac9/RPi/__init__.py -------------------------------------------------------------------------------- /SPI/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | """ 3 | 4 | from RPi._SPI import * 5 | 6 | VERSION = '0.6.2' 7 | -------------------------------------------------------------------------------- /create_gpio_user_permissions.py: -------------------------------------------------------------------------------- 1 | import grp 2 | import subprocess 3 | 4 | def ensure_gpiogroup(): 5 | try: 6 | grp.getgrnam('gpio') 7 | except KeyError: 8 | print('GPIO group does not exist - creating...') 9 | subprocess.call(['groupadd', '-f', '-r', 'gpio']) 10 | subprocess.call(['adduser', 'pi', 'gpio']) 11 | # in future, also for groups: 12 | # spi 13 | # i2c 14 | add_udev_rules() 15 | 16 | def add_udev_rules(): 17 | with open('/etc/udev/rules.d/99-gpio.rules','w') as f: 18 | f.write("""SUBSYSTEM=="bcm2835-gpiomem", KERNEL=="gpiomem", GROUP="gpio", MODE="0660" 19 | SUBSYSTEM=="gpio", KERNEL=="gpiochip*", ACTION=="add", PROGRAM="/bin/sh -c 'chown root:gpio /sys/class/gpio/export /sys/class/gpio/unexport ; chmod 220 /sys/class/gpio/export /sys/class/gpio/unexport'" 20 | SUBSYSTEM=="gpio", KERNEL=="gpio*", ACTION=="add", PROGRAM="/bin/sh -c 'chown root:gpio /sys%p/active_low /sys%p/direction /sys%p/edge /sys%p/value ; chmod 660 /sys%p/active_low /sys%p/direction /sys%p/edge /sys%p/value'" 21 | """) 22 | 23 | if __name__ == '__main__': 24 | ensure_gpiogroup() 25 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2012-2016 Ben Croston 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | """ 22 | 23 | from distutils.core import setup, Extension 24 | 25 | classifiers = ['Development Status :: 5 - Production/Stable', 26 | 'Operating System :: POSIX :: Linux', 27 | 'License :: OSI Approved :: MIT License', 28 | 'Intended Audience :: Developers', 29 | 'Programming Language :: Python :: 2.7', 30 | 'Programming Language :: Python :: 3', 31 | 'Topic :: Software Development', 32 | 'Topic :: Home Automation', 33 | 'Topic :: System :: Hardware'] 34 | 35 | setup(name = 'RPi.GPIO-PineA64', 36 | version = '0.1.0', 37 | author = 'Seong-Woo Kim', 38 | author_email = 'swkim01@gmail.com', 39 | description = 'A module to control Pine A64/A64+ GPIO channels', 40 | long_description = open('README.txt').read() + open('CHANGELOG.txt').read(), 41 | license = 'MIT', 42 | keywords = 'Raspberry Pi GPIO', 43 | url = 'http://sourceforge.net/projects/raspberry-gpio-python/', 44 | classifiers = classifiers, 45 | packages = ['RPi','RPi.GPIO', 'RPi.I2C', 'RPi.SPI'], 46 | ext_modules = [Extension('RPi._GPIO', ['source/py_gpio.c', 'source/c_gpio.c', 'source/cpuinfo.c', 'source/event_gpio.c', 'source/soft_pwm.c', 'source/py_pwm.c', 'source/common.c', 'source/constants.c']), 47 | Extension('RPi._I2C', ['source/i2c/i2c.c', 'source/i2c/i2c_lib.c']), 48 | Extension('RPi._SPI', ['source/spi/spi.c', 'source/spi/spi_lib.c'])]) 49 | -------------------------------------------------------------------------------- /source/c_gpio.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015 Ben Croston 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include "c_gpio.h" 30 | 31 | #define BCM2708_PERI_BASE_DEFAULT 0x20000000 32 | #define BCM2709_PERI_BASE_DEFAULT 0x3f000000 33 | #define GPIO_BASE_OFFSET 0x200000 34 | #define FSEL_OFFSET 0 // 0x0000 35 | #define SET_OFFSET 7 // 0x001c / 4 36 | #define CLR_OFFSET 10 // 0x0028 / 4 37 | #define PINLEVEL_OFFSET 13 // 0x0034 / 4 38 | #define EVENT_DETECT_OFFSET 16 // 0x0040 / 4 39 | #define RISING_ED_OFFSET 19 // 0x004c / 4 40 | #define FALLING_ED_OFFSET 22 // 0x0058 / 4 41 | #define HIGH_DETECT_OFFSET 25 // 0x0064 / 4 42 | #define LOW_DETECT_OFFSET 28 // 0x0070 / 4 43 | #define PULLUPDN_OFFSET 37 // 0x0094 / 4 44 | #define PULLUPDNCLK_OFFSET 38 // 0x0098 / 4 45 | 46 | #define PAGE_SIZE (4*1024) 47 | #define BLOCK_SIZE (4*1024) 48 | 49 | // 50 | // For Pine A64/A64+ Board 51 | // 52 | #define PINEA64_GPIO_MASK (0xFFFFFF80) 53 | #define SUNXI_GPIO_BASE 0x01C20000 54 | #define SUNXI_GPIO_REG_OFFSET 0x800 55 | #define PINEA64_GPIO_BASE (SUNXI_GPIO_BASE + SUNXI_GPIO_REG_OFFSET) 56 | #define SUNXI_CFG_OFFSET 0x00 57 | #define SUNXI_DATA_OFFSET 0x10 58 | #define SUNXI_PUD_OFFSET 0x1C 59 | #define SUNXI_BANK_SIZE 0x24 60 | 61 | #define MAP_SIZE (4096*2) 62 | #define MAP_MASK (MAP_SIZE - 1) 63 | 64 | typedef struct sunxi_gpio { 65 | unsigned int CFG[4]; 66 | unsigned int DAT; 67 | unsigned int DRV[2]; 68 | unsigned int PULL[2]; 69 | } sunxi_gpio_t; 70 | 71 | /* gpio interrupt control */ 72 | typedef struct sunxi_gpio_int { 73 | unsigned int CFG[3]; 74 | unsigned int CTL; 75 | unsigned int STA; 76 | unsigned int DEB; 77 | } sunxi_gpio_int_t; 78 | 79 | typedef struct sunxi_gpio_reg { 80 | struct sunxi_gpio gpio_bank[9]; 81 | unsigned char res[0xbc]; 82 | struct sunxi_gpio_int gpio_int; 83 | } sunxi_gpio_reg_t; 84 | 85 | #define GPIO_BANK(pin) ((pin) >> 5) 86 | #define GPIO_NUM(pin) ((pin) & 0x1F) 87 | 88 | #define GPIO_CFG_INDEX(pin) (((pin) & 0x1F) >> 3) 89 | #define GPIO_CFG_OFFSET(pin) ((((pin) & 0x1F) & 0x7) << 2) 90 | 91 | #define GPIO_PUL_INDEX(pin) (((pin) & 0x1F )>> 4) 92 | #define GPIO_PUL_OFFSET(pin) (((pin) & 0x0F) << 1) 93 | 94 | extern int pinea64_found; 95 | static volatile uint32_t *pio_map; 96 | // end of Pine A64/A64+ 97 | 98 | static volatile uint32_t *gpio_map; 99 | 100 | void short_wait(void) 101 | { 102 | int i; 103 | 104 | for (i=0; i<150; i++) { // wait 150 cycles 105 | asm volatile("nop"); 106 | } 107 | } 108 | 109 | int setup(void) 110 | { 111 | int mem_fd; 112 | uint8_t *gpio_mem; 113 | uint32_t peri_base; 114 | uint32_t gpio_base; 115 | unsigned char buf[4]; 116 | FILE *fp; 117 | char buffer[1024]; 118 | char hardware[1024]; 119 | int found = 0; 120 | 121 | if ( !pinea64_found ) { 122 | // try /dev/gpiomem first - this does not require root privs 123 | if ((mem_fd = open("/dev/gpiomem", O_RDWR|O_SYNC)) > 0) 124 | { 125 | gpio_map = (uint32_t *)mmap(NULL, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, 0); 126 | if ((uint32_t)gpio_map < 0) { 127 | return SETUP_MMAP_FAIL; 128 | } else { 129 | return SETUP_OK; 130 | } 131 | } 132 | 133 | // revert to /dev/mem method - requires root 134 | 135 | // determine peri_base 136 | if ((fp = fopen("/proc/device-tree/soc/ranges", "rb")) != NULL) { 137 | // get peri base from device tree 138 | fseek(fp, 4, SEEK_SET); 139 | if (fread(buf, 1, sizeof buf, fp) == sizeof buf) { 140 | peri_base = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3] << 0; 141 | } 142 | fclose(fp); 143 | } else { 144 | // guess peri base based on /proc/cpuinfo hardware field 145 | if ((fp = fopen("/proc/cpuinfo", "r")) == NULL) 146 | return SETUP_CPUINFO_FAIL; 147 | 148 | while(!feof(fp) && !found) { 149 | fgets(buffer, sizeof(buffer), fp); 150 | sscanf(buffer, "Hardware : %s", hardware); 151 | if (strcmp(hardware, "BCM2708") == 0 || strcmp(hardware, "BCM2835") == 0) { 152 | // pi 1 hardware 153 | peri_base = BCM2708_PERI_BASE_DEFAULT; 154 | found = 1; 155 | } else if (strcmp(hardware, "BCM2709") == 0 || strcmp(hardware, "BCM2836") == 0) { 156 | // pi 2 hardware 157 | peri_base = BCM2709_PERI_BASE_DEFAULT; 158 | found = 1; 159 | } 160 | } 161 | fclose(fp); 162 | if (!found) 163 | return SETUP_NOT_RPI_FAIL; 164 | } 165 | 166 | gpio_base = peri_base + GPIO_BASE_OFFSET; 167 | } 168 | 169 | // mmap the GPIO memory registers 170 | if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) 171 | return SETUP_DEVMEM_FAIL; 172 | 173 | if ((gpio_mem = malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) 174 | return SETUP_MALLOC_FAIL; 175 | 176 | if ((uint32_t)gpio_mem % PAGE_SIZE) 177 | gpio_mem += PAGE_SIZE - ((uint32_t)gpio_mem % PAGE_SIZE); 178 | 179 | if ( pinea64_found ) { 180 | gpio_map = (uint32_t *)mmap( (caddr_t)gpio_mem, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, mem_fd, SUNXI_GPIO_BASE); 181 | pio_map = gpio_map + (SUNXI_GPIO_REG_OFFSET>>2); 182 | } else { 183 | gpio_map = (uint32_t *)mmap( (void *)gpio_mem, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, mem_fd, gpio_base); 184 | } 185 | 186 | if ((uint32_t)gpio_map < 0) 187 | return SETUP_MMAP_FAIL; 188 | 189 | return SETUP_OK; 190 | } 191 | 192 | void clear_event_detect(int gpio) 193 | { 194 | if ( !pinea64_found ) { 195 | int offset = EVENT_DETECT_OFFSET + (gpio/32); 196 | int shift = (gpio%32); 197 | 198 | *(gpio_map+offset) |= (1 << shift); 199 | short_wait(); 200 | *(gpio_map+offset) = 0; 201 | } 202 | } 203 | 204 | int eventdetected(int gpio) 205 | { 206 | if ( !pinea64_found ) { 207 | int offset, value, bit; 208 | 209 | offset = EVENT_DETECT_OFFSET + (gpio/32); 210 | bit = (1 << (gpio%32)); 211 | value = *(gpio_map+offset) & bit; 212 | if (value) 213 | clear_event_detect(gpio); 214 | return value; 215 | } 216 | } 217 | 218 | void set_rising_event(int gpio, int enable) 219 | { 220 | if ( !pinea64_found ) { 221 | int offset = RISING_ED_OFFSET + (gpio/32); 222 | int shift = (gpio%32); 223 | 224 | if (enable) 225 | *(gpio_map+offset) |= 1 << shift; 226 | else 227 | *(gpio_map+offset) &= ~(1 << shift); 228 | clear_event_detect(gpio); 229 | } 230 | } 231 | 232 | void set_falling_event(int gpio, int enable) 233 | { 234 | if ( !pinea64_found ) { 235 | int offset = FALLING_ED_OFFSET + (gpio/32); 236 | int shift = (gpio%32); 237 | 238 | if (enable) { 239 | *(gpio_map+offset) |= (1 << shift); 240 | *(gpio_map+offset) = (1 << shift); 241 | } else { 242 | *(gpio_map+offset) &= ~(1 << shift); 243 | } 244 | clear_event_detect(gpio); 245 | } 246 | } 247 | 248 | void set_high_event(int gpio, int enable) 249 | { 250 | if ( !pinea64_found ) { 251 | int offset = HIGH_DETECT_OFFSET + (gpio/32); 252 | int shift = (gpio%32); 253 | 254 | if (enable) 255 | *(gpio_map+offset) |= (1 << shift); 256 | else 257 | *(gpio_map+offset) &= ~(1 << shift); 258 | clear_event_detect(gpio); 259 | } 260 | } 261 | 262 | void set_low_event(int gpio, int enable) 263 | { 264 | if ( !pinea64_found ) { 265 | int offset = LOW_DETECT_OFFSET + (gpio/32); 266 | int shift = (gpio%32); 267 | 268 | if (enable) 269 | *(gpio_map+offset) |= 1 << shift; 270 | else 271 | *(gpio_map+offset) &= ~(1 << shift); 272 | clear_event_detect(gpio); 273 | } 274 | } 275 | 276 | uint32_t sunxi_readl(volatile uint32_t *addr) 277 | { 278 | uint32_t val = 0; 279 | uint32_t mmap_base = (uint32_t)addr & (~MAP_MASK); 280 | uint32_t mmap_seek = ((uint32_t)addr - mmap_base) >> 2; 281 | val = *(gpio_map + mmap_seek); 282 | return val; 283 | } 284 | 285 | void sunxi_writel(volatile uint32_t *addr, uint32_t val) 286 | { 287 | uint32_t mmap_base = (uint32_t)addr & (~MAP_MASK); 288 | uint32_t mmap_seek =( (uint32_t)addr - mmap_base) >> 2; 289 | *(gpio_map + mmap_seek) = val; 290 | } 291 | 292 | void set_pullupdn(int gpio, int pud) 293 | { 294 | if ( pinea64_found ) { 295 | uint32_t regval = 0; 296 | int bank = GPIO_BANK(gpio); //gpio >> 5 297 | int index = GPIO_PUL_INDEX(gpio); // (gpio & 0x1f) >> 4 298 | int offset = GPIO_PUL_OFFSET(gpio); // (gpio) & 0x0F) << 1 299 | 300 | sunxi_gpio_t *pio = &((sunxi_gpio_reg_t *) pio_map)->gpio_bank[bank]; 301 | 302 | regval = *(&pio->PULL[0] + index); 303 | regval &= ~(3 << offset); 304 | regval |= pud << offset; 305 | *(&pio->PULL[0] + index) = regval; 306 | } else { 307 | int clk_offset = PULLUPDNCLK_OFFSET + (gpio/32); 308 | int shift = (gpio%32); 309 | 310 | if (pud == PUD_DOWN) 311 | *(gpio_map+PULLUPDN_OFFSET) = (*(gpio_map+PULLUPDN_OFFSET) & ~3) | PUD_DOWN; 312 | else if (pud == PUD_UP) 313 | *(gpio_map+PULLUPDN_OFFSET) = (*(gpio_map+PULLUPDN_OFFSET) & ~3) | PUD_UP; 314 | else // pud == PUD_OFF 315 | *(gpio_map+PULLUPDN_OFFSET) &= ~3; 316 | 317 | short_wait(); 318 | *(gpio_map+clk_offset) = 1 << shift; 319 | short_wait(); 320 | *(gpio_map+PULLUPDN_OFFSET) &= ~3; 321 | *(gpio_map+clk_offset) = 0; 322 | } 323 | } 324 | 325 | void setup_gpio(int gpio, int direction, int pud) 326 | { 327 | if ( pinea64_found ) { 328 | uint32_t regval = 0; 329 | int bank = GPIO_BANK(gpio); //gpio >> 5 330 | int index = GPIO_CFG_INDEX(gpio); // (gpio & 0x1F) >> 3 331 | int offset = GPIO_CFG_OFFSET(gpio); // ((gpio & 0x1F) & 0x7) << 2 332 | 333 | sunxi_gpio_t *pio = &((sunxi_gpio_reg_t *) pio_map)->gpio_bank[bank]; 334 | 335 | set_pullupdn(gpio, pud); 336 | 337 | regval = *(&pio->CFG[0] + index); 338 | regval &= ~(0x7 << offset); // 0xf? 339 | if (INPUT == direction) { 340 | *(&pio->CFG[0] + index) = regval; 341 | } else if (OUTPUT == direction) { 342 | regval |= (1 << offset); 343 | *(&pio->CFG[0] + index) = regval; 344 | } else { 345 | printf("line:%dgpio number error\n",__LINE__); 346 | } 347 | } else { 348 | int offset = FSEL_OFFSET + (gpio/10); 349 | int shift = (gpio%10)*3; 350 | 351 | set_pullupdn(gpio, pud); 352 | if (direction == OUTPUT) 353 | *(gpio_map+offset) = (*(gpio_map+offset) & ~(7< 360 | int gpio_function(int gpio) 361 | { 362 | if ( pinea64_found ) { 363 | uint32_t regval = 0; 364 | int bank = GPIO_BANK(gpio); //gpio >> 5 365 | int index = GPIO_CFG_INDEX(gpio); // (gpio & 0x1F) >> 3 366 | int offset = GPIO_CFG_OFFSET(gpio); // ((gpio & 0x1F) & 0x7) << 2 367 | 368 | sunxi_gpio_t *pio = &((sunxi_gpio_reg_t *) pio_map)->gpio_bank[bank]; 369 | 370 | regval = *(&pio->CFG[0] + index); 371 | regval >>= offset; 372 | regval &= 7; 373 | return regval; // 0=input, 1=output, 4=alt0 374 | } else { 375 | int offset = FSEL_OFFSET + (gpio/10); 376 | int shift = (gpio%10)*3; 377 | int value = *(gpio_map+offset); 378 | value >>= shift; 379 | value &= 7; 380 | return value; // 0=input, 1=output, 4=alt0 381 | } 382 | } 383 | 384 | void output_gpio(int gpio, int value) 385 | { 386 | if ( pinea64_found ) { 387 | int bank = GPIO_BANK(gpio); //gpio >> 5 388 | int num = GPIO_NUM(gpio); // gpio & 0x1F 389 | 390 | sunxi_gpio_t *pio = &((sunxi_gpio_reg_t *) pio_map)->gpio_bank[bank]; 391 | 392 | if (value == 0) 393 | *(&pio->DAT) &= ~(1 << num); 394 | else 395 | *(&pio->DAT) |= (1 << num); 396 | } else { 397 | int offset, shift; 398 | 399 | if (value) // value == HIGH 400 | offset = SET_OFFSET + (gpio/32); 401 | else // value == LOW 402 | offset = CLR_OFFSET + (gpio/32); 403 | 404 | shift = (gpio%32); 405 | 406 | *(gpio_map+offset) = 1 << shift; 407 | } 408 | } 409 | 410 | int input_gpio(int gpio) 411 | { 412 | if ( pinea64_found ) { 413 | uint32_t regval = 0; 414 | int bank = GPIO_BANK(gpio); //gpio >> 5 415 | int num = GPIO_NUM(gpio); // gpio & 0x1F 416 | 417 | sunxi_gpio_t *pio = &((sunxi_gpio_reg_t *) pio_map)->gpio_bank[bank]; 418 | 419 | regval = *(&pio->DAT); 420 | regval = regval >> num; 421 | regval &= 1; 422 | return regval; 423 | } else { 424 | int offset, value, mask; 425 | 426 | offset = PINLEVEL_OFFSET + (gpio/32); 427 | mask = (1 << gpio%32); 428 | value = *(gpio_map+offset) & mask; 429 | return value; 430 | } 431 | } 432 | 433 | void cleanup(void) 434 | { 435 | munmap((void *)gpio_map, BLOCK_SIZE); 436 | } 437 | -------------------------------------------------------------------------------- /source/c_gpio.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015 Ben Croston 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | int setup(void); 24 | void setup_gpio(int gpio, int direction, int pud); 25 | int gpio_function(int gpio); 26 | void output_gpio(int gpio, int value); 27 | int input_gpio(int gpio); 28 | void set_rising_event(int gpio, int enable); 29 | void set_falling_event(int gpio, int enable); 30 | void set_high_event(int gpio, int enable); 31 | void set_low_event(int gpio, int enable); 32 | int eventdetected(int gpio); 33 | void cleanup(void); 34 | 35 | #define SETUP_OK 0 36 | #define SETUP_DEVMEM_FAIL 1 37 | #define SETUP_MALLOC_FAIL 2 38 | #define SETUP_MMAP_FAIL 3 39 | #define SETUP_CPUINFO_FAIL 4 40 | #define SETUP_NOT_RPI_FAIL 5 41 | 42 | #define INPUT 1 // is really 0 for control register! 43 | #define OUTPUT 0 // is really 1 for control register! 44 | #define ALT0 4 45 | 46 | #define HIGH 1 47 | #define LOW 0 48 | 49 | #define PUD_OFF 0 50 | #define PUD_UP 1 51 | #define PUD_DOWN 2 52 | -------------------------------------------------------------------------------- /source/common.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013-2014 Ben Croston 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #include "Python.h" 24 | #include "c_gpio.h" 25 | #include "common.h" 26 | 27 | int gpio_mode = MODE_UNKNOWN; 28 | const int pin_to_gpio_rev1[41] = {-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, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; 29 | const int pin_to_gpio_rev2[41] = {-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, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; 30 | const int pin_to_gpio_rev3[41] = {-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, -1, -1, 5, -1, 6, 12, 13, -1, 19, 16, 26, 20, -1, 21 }; 31 | const int (*pin_to_gpio)[41]; 32 | int gpio_direction[54]; 33 | rpi_info rpiinfo; 34 | int setup_error = 0; 35 | int module_setup = 0; 36 | 37 | // 38 | // For Pine A64/A64+ Board 39 | // 40 | // pinToGpio: 41 | // 42 | const int pinToGpioPineA64[41] = { // BCM ModE 43 | -1, -1, 227, 226, 362, 229, 230, 231, // 0..7 44 | 67, 65, 64, 66, 68, 69, 32, 33, // 8..15 45 | 70, 71, 72, 73, 74, 75, 76, 77, // 16..23 46 | 78, 79, 80, 233, -1, -1, -1, -1, // 24..31 47 | // Padding: 48 | -1, -1, -1, -1, -1, -1, -1, -1, // ... 40 49 | }; 50 | 51 | // 52 | // physToGpio: 53 | // 54 | const int physToGpioPineA64[41] = // BOARD MODE 55 | { 56 | -1, // 0 57 | -1, -1, // 1, 2 58 | 227, -1, 59 | 226, -1, 60 | 362, 32, 61 | -1, 33, 62 | 71, 72, 63 | 233, -1, 64 | 76, 77, 65 | -1, 78, 66 | 64, -1, 67 | 65, 79, 68 | 66, 67, 69 | -1, 231, // 25, 26 70 | 71 | 361, 360, 72 | 229, -1, 73 | 230, 68, 74 | 69, -1, 75 | 73, 70, 76 | 80, 74, 77 | -1, 75, // 39, 40 78 | } ; 79 | // end of Pine A64/A64+ 80 | 81 | int check_gpio_priv(void) 82 | { 83 | // check module has been imported cleanly 84 | if (setup_error) 85 | { 86 | PyErr_SetString(PyExc_RuntimeError, "Module not imported correctly!"); 87 | return 1; 88 | } 89 | 90 | // check mmap setup has worked 91 | if (!module_setup) 92 | { 93 | PyErr_SetString(PyExc_RuntimeError, "No access to /dev/mem. Try running as root!"); 94 | return 2; 95 | } 96 | return 0; 97 | } 98 | 99 | int get_gpio_number(int channel, unsigned int *gpio, unsigned int *bcm_gpio) 100 | { 101 | // check setmode() has been run 102 | if (gpio_mode != BOARD && gpio_mode != BCM) 103 | { 104 | PyErr_SetString(PyExc_RuntimeError, "Please set pin numbering mode using GPIO.setmode(GPIO.BOARD) or GPIO.setmode(GPIO.BCM)"); 105 | return 3; 106 | } 107 | 108 | // check channel number is in range 109 | if ( (gpio_mode == BCM && (channel < 0 || channel > 53)) 110 | || (gpio_mode == BOARD && (channel < 1 || channel > 26) && rpiinfo.p1_revision < 3) 111 | || (gpio_mode == BOARD && (channel < 1 || channel > 40) && rpiinfo.p1_revision >= 3) ) 112 | { 113 | PyErr_SetString(PyExc_ValueError, "The channel sent is invalid on a Raspberry Pi"); 114 | return 4; 115 | } 116 | 117 | // convert channel to gpio 118 | if (gpio_mode == BOARD) 119 | { 120 | if (*(*pin_to_gpio+channel) == -1) 121 | { 122 | PyErr_SetString(PyExc_ValueError, "The channel sent is invalid on a Raspberry Pi"); 123 | return 5; 124 | } else { 125 | *gpio = *(*pin_to_gpio+channel); 126 | *bcm_gpio = *(pin_to_gpio_rev3+channel); 127 | } 128 | } 129 | else // gpio_mode == BCM 130 | { 131 | *gpio = *(pinToGpioPineA64 + channel); 132 | *bcm_gpio = channel; 133 | } 134 | 135 | return 0; 136 | } 137 | -------------------------------------------------------------------------------- /source/common.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013-2015 Ben Croston 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #include "cpuinfo.h" 24 | 25 | #define MODE_UNKNOWN -1 26 | #define BOARD 10 27 | #define BCM 11 28 | #define SERIAL 40 29 | #define SPI 41 30 | #define I2C 42 31 | #define PWM 43 32 | 33 | extern int gpio_mode; 34 | extern const int pin_to_gpio_rev1[41]; 35 | extern const int pin_to_gpio_rev2[41]; 36 | extern const int pin_to_gpio_rev3[41]; 37 | extern const int (*pin_to_gpio)[41]; 38 | extern const int pinToGpioPineA64[41]; 39 | extern const int physToGpioPineA64[41]; 40 | extern int gpio_direction[54]; 41 | extern rpi_info rpiinfo; 42 | extern int setup_error; 43 | extern int module_setup; 44 | int check_gpio_priv(void); 45 | int get_gpio_number(int channel, unsigned int *gpio, unsigned int *bcm_gpio); 46 | -------------------------------------------------------------------------------- /source/constants.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013-2016 Ben Croston 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #include "Python.h" 24 | #include "constants.h" 25 | #include "common.h" 26 | #include "c_gpio.h" 27 | #include "event_gpio.h" 28 | 29 | PyObject *high; 30 | PyObject *low; 31 | PyObject *input; 32 | PyObject *output; 33 | PyObject *pwm; 34 | PyObject *serial; 35 | PyObject *i2c; 36 | PyObject *spi; 37 | PyObject *unknown; 38 | PyObject *board; 39 | PyObject *bcm; 40 | PyObject *pud_off; 41 | PyObject *pud_up; 42 | PyObject *pud_down; 43 | PyObject *rising_edge; 44 | PyObject *falling_edge; 45 | PyObject *both_edge; 46 | 47 | void define_constants(PyObject *module) 48 | { 49 | high = Py_BuildValue("i", HIGH); 50 | PyModule_AddObject(module, "HIGH", high); 51 | 52 | low = Py_BuildValue("i", LOW); 53 | PyModule_AddObject(module, "LOW", low); 54 | 55 | output = Py_BuildValue("i", OUTPUT); 56 | PyModule_AddObject(module, "OUT", output); 57 | 58 | input = Py_BuildValue("i", INPUT); 59 | PyModule_AddObject(module, "IN", input); 60 | 61 | pwm = Py_BuildValue("i", PWM); 62 | PyModule_AddObject(module, "HARD_PWM", pwm); 63 | 64 | serial = Py_BuildValue("i", SERIAL); 65 | PyModule_AddObject(module, "SERIAL", serial); 66 | 67 | i2c = Py_BuildValue("i", I2C); 68 | PyModule_AddObject(module, "I2C", i2c); 69 | 70 | spi = Py_BuildValue("i", SPI); 71 | PyModule_AddObject(module, "SPI", spi); 72 | 73 | unknown = Py_BuildValue("i", MODE_UNKNOWN); 74 | PyModule_AddObject(module, "UNKNOWN", unknown); 75 | 76 | board = Py_BuildValue("i", BOARD); 77 | PyModule_AddObject(module, "BOARD", board); 78 | 79 | bcm = Py_BuildValue("i", BCM); 80 | PyModule_AddObject(module, "BCM", bcm); 81 | 82 | pud_off = Py_BuildValue("i", PUD_OFF + PY_PUD_CONST_OFFSET); 83 | PyModule_AddObject(module, "PUD_OFF", pud_off); 84 | 85 | pud_up = Py_BuildValue("i", PUD_UP + PY_PUD_CONST_OFFSET); 86 | PyModule_AddObject(module, "PUD_UP", pud_up); 87 | 88 | pud_down = Py_BuildValue("i", PUD_DOWN + PY_PUD_CONST_OFFSET); 89 | PyModule_AddObject(module, "PUD_DOWN", pud_down); 90 | 91 | rising_edge = Py_BuildValue("i", RISING_EDGE + PY_EVENT_CONST_OFFSET); 92 | PyModule_AddObject(module, "RISING", rising_edge); 93 | 94 | falling_edge = Py_BuildValue("i", FALLING_EDGE + PY_EVENT_CONST_OFFSET); 95 | PyModule_AddObject(module, "FALLING", falling_edge); 96 | 97 | both_edge = Py_BuildValue("i", BOTH_EDGE + PY_EVENT_CONST_OFFSET); 98 | PyModule_AddObject(module, "BOTH", both_edge); 99 | } 100 | -------------------------------------------------------------------------------- /source/constants.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Ben Croston 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #define PY_PUD_CONST_OFFSET 20 24 | #define PY_EVENT_CONST_OFFSET 30 25 | 26 | extern PyObject *high; 27 | extern PyObject *low; 28 | extern PyObject *input; 29 | extern PyObject *output; 30 | extern PyObject *pwm; 31 | extern PyObject *serial; 32 | extern PyObject *i2c; 33 | extern PyObject *spi; 34 | extern PyObject *unknown; 35 | extern PyObject *board; 36 | extern PyObject *bcm; 37 | extern PyObject *pud_off; 38 | extern PyObject *pud_up; 39 | extern PyObject *pud_down; 40 | extern PyObject *rising_edge; 41 | extern PyObject *falling_edge; 42 | extern PyObject *both_edge; 43 | 44 | void define_constants(PyObject *module); 45 | -------------------------------------------------------------------------------- /source/cpuinfo.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2016 Ben Croston 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include "cpuinfo.h" 27 | 28 | int pinea64_found = 0; 29 | 30 | int get_rpi_info(rpi_info *info) 31 | { 32 | FILE *fp; 33 | char buffer[1024]; 34 | char hardware[1024]; 35 | char revision[1024]; 36 | char *rev; 37 | int found = 0; 38 | int len; 39 | 40 | // if ((fp = fopen("/proc/cpuinfo", "r")) == NULL) 41 | // return -1; 42 | // while(!feof(fp)) { 43 | // fgets(buffer, sizeof(buffer), fp); 44 | // sscanf(buffer, "Hardware : %s", hardware); 45 | // if (strcmp(hardware, "BCM2708") == 0 || 46 | // strcmp(hardware, "BCM2709") == 0 || 47 | // strcmp(hardware, "BCM2835") == 0 || 48 | // strcmp(hardware, "BCM2836") == 0 || 49 | // strcmp(hardware, "BCM2837") == 0 ) { 50 | // found = 1; 51 | // sscanf(buffer, "Revision : %s", revision); 52 | // } else if (strcmp(hardware, "sun50iw1p1") == 0 || 53 | // strcmp(hardware, "Pine64") == 0 || 54 | // strcmp(hardware, "Pine64+") == 0 ) { 55 | pinea64_found = 1; 56 | found = 1; 57 | sprintf(hardware, "Pine64"); 58 | sprintf(revision, "000b"); 59 | // } else 60 | // sscanf(buffer, "Revision : %s", revision); 61 | // } 62 | // fclose(fp); 63 | 64 | if (!found) 65 | return -1; 66 | 67 | if ((len = strlen(revision)) == 0) 68 | return -1; 69 | 70 | if (len >= 6 && strtol((char[]){revision[len-6],0}, NULL, 16) & 8) { 71 | // new scheme 72 | //info->rev = revision[len-1]-'0'; 73 | strcpy(info->revision, revision); 74 | switch (revision[len-2]) { 75 | case '0': info->type = "Model A"; info->p1_revision = 2; break; 76 | case '1': info->type = "Model B"; info->p1_revision = 2; break; 77 | case '2': info->type = "Model A+"; info->p1_revision = 3; break; 78 | case '3': info->type = "Model B+"; info->p1_revision = 3; break; 79 | case '4': info->type = "Pi 2 Model B"; info->p1_revision = 3; break; 80 | case '5': info->type = "Alpha"; info->p1_revision = 3; break; 81 | case '6': info->type = "Compute"; info->p1_revision = 0; break; 82 | case '8': info->type = "Pi 3 Model B"; info->p1_revision = 3; break; 83 | case '9': info->type = "Zero"; info->p1_revision = 3; break; 84 | default : info->type = "Unknown"; info->p1_revision = 3; break; 85 | } 86 | switch (revision[len-4]) { 87 | case '0': info->processor = "BCM2835"; break; 88 | case '1': info->processor = "BCM2836"; break; 89 | case '2': info->processor = "BCM2837"; break; 90 | default : info->processor = "Unknown"; break; 91 | } 92 | switch (revision[len-5]) { 93 | case '0': info->manufacturer = "Sony"; break; 94 | case '1': info->manufacturer = "Egoman"; break; 95 | case '2': info->manufacturer = "Embest"; break; 96 | case '4': info->manufacturer = "Embest"; break; 97 | default : info->manufacturer = "Unknown"; break; 98 | } 99 | switch (strtol((char[]){revision[len-6],0}, NULL, 16) & 7) { 100 | case 0: info->ram = "256M"; break; 101 | case 1: info->ram = "512M"; break; 102 | case 2: info->ram = "1024M"; break; 103 | default: info->ram = "Unknown"; break; 104 | } 105 | } else { 106 | // old scheme 107 | info->ram = "Unknown"; 108 | info->manufacturer = "Unknown"; 109 | info->processor = "Unknown"; 110 | info->type = "Unknown"; 111 | strcpy(info->revision, revision); 112 | 113 | // get last four characters (ignore preceeding 1000 for overvolt) 114 | if (len > 4) 115 | rev = (char *)&revision+len-4; 116 | else 117 | rev = revision; 118 | 119 | if ((strcmp(rev, "0002") == 0) || 120 | (strcmp(rev, "0003") == 0)) { 121 | info->type = "Model B"; 122 | info->p1_revision = 1; 123 | info->ram = "256M"; 124 | info->processor = "BCM2835"; 125 | } else if (strcmp(rev, "0004") == 0) { 126 | info->type = "Model B"; 127 | info->p1_revision = 2; 128 | info->ram = "256M"; 129 | info->manufacturer = "Sony"; 130 | info->processor = "BCM2835"; 131 | } else if (strcmp(rev, "0005") == 0) { 132 | info->type = "Model B"; 133 | info->p1_revision = 2; 134 | info->ram = "256M"; 135 | info->manufacturer = "Qisda"; 136 | info->processor = "BCM2835"; 137 | } else if (strcmp(rev, "0006") == 0) { 138 | info->type = "Model B"; 139 | info->p1_revision = 2; 140 | info->ram = "256M"; 141 | info->manufacturer = "Egoman"; 142 | info->processor = "BCM2835"; 143 | } else if (strcmp(rev, "0007") == 0) { 144 | info->type = "Model A"; 145 | info->p1_revision = 2; 146 | info->ram = "256M"; 147 | info->manufacturer = "Egoman"; 148 | info->processor = "BCM2835"; 149 | } else if (strcmp(rev, "0008") == 0) { 150 | info->type = "Model A"; 151 | info->p1_revision = 2; 152 | info->ram = "256M"; 153 | info->manufacturer = "Sony"; 154 | info->processor = "BCM2835"; 155 | } else if (strcmp(rev, "0009") == 0) { 156 | info->type = "Model A"; 157 | info->p1_revision = 2; 158 | info->ram = "256M"; 159 | info->manufacturer = "Qisda"; 160 | info->processor = "BCM2835"; 161 | } else if (strcmp(rev, "000d") == 0) { 162 | info->type = "Model B"; 163 | info->p1_revision = 2; 164 | info->ram = "512M"; 165 | info->manufacturer = "Egoman"; 166 | info->processor = "BCM2835"; 167 | } else if (strcmp(rev, "000e") == 0) { 168 | info->type = "Model B"; 169 | info->p1_revision = 2; 170 | info->ram = "512M"; 171 | info->manufacturer = "Sony"; 172 | info->processor = "BCM2835"; 173 | } else if (strcmp(rev, "000f") == 0) { 174 | info->type = "Model B"; 175 | info->p1_revision = 2; 176 | info->ram = "512M"; 177 | info->manufacturer = "Qisda"; 178 | info->processor = "BCM2835"; 179 | } else if ((strcmp(rev, "0011") == 0) || 180 | (strcmp(rev, "0014") == 0)) { 181 | info->type = "Compute Module"; 182 | info->p1_revision = 0; 183 | info->ram = "512M"; 184 | info->processor = "BCM2835"; 185 | } else if (strcmp(rev, "0012") == 0) { 186 | info->type = "Model A+"; 187 | info->p1_revision = 3; 188 | info->ram = "256M"; 189 | info->processor = "BCM2835"; 190 | } else if ((strcmp(rev, "0010") == 0) || 191 | (strcmp(rev, "0013") == 0)) { 192 | info->type = "Model B+"; 193 | info->p1_revision = 3; 194 | info->ram = "512M"; 195 | info->processor = "BCM2835"; 196 | } else if (strcmp(rev, "000b") == 0) { 197 | info->type = "Pine A64+"; 198 | info->p1_revision = 4; 199 | info->ram = "1024M"; 200 | info->processor = "Allwinner A64"; 201 | } else { // don't know - assume revision 3 p1 connector 202 | info->p1_revision = 3; 203 | } 204 | } 205 | return 0; 206 | } 207 | 208 | /* 209 | 210 | 32 bits 211 | NEW 23: will be 1 for the new scheme, 0 for the old scheme 212 | MEMSIZE 20: 0=256M 1=512M 2=1G 213 | MANUFACTURER 16: 0=SONY 1=EGOMAN 214 | PROCESSOR 12: 0=2835 1=2836 215 | TYPE 04: 0=MODELA 1=MODELB 2=MODELA+ 3=MODELB+ 4=Pi2 MODEL B 5=ALPHA 6=CM 216 | REV 00: 0=REV0 1=REV1 2=REV2 217 | 218 | pi2 = 1<<23 | 2<<20 | 1<<12 | 4<<4 = 0xa01040 219 | 220 | -------------------- 221 | 222 | SRRR MMMM PPPP TTTT TTTT VVVV 223 | 224 | S scheme (0=old, 1=new) 225 | R RAM (0=256, 1=512, 2=1024) 226 | M manufacturer (0='SONY',1='EGOMAN',2='EMBEST',3='UNKNOWN',4='EMBEST') 227 | P processor (0=2835, 1=2836 2=2837) 228 | T type (0='A', 1='B', 2='A+', 3='B+', 4='Pi 2 B', 5='Alpha', 6='Compute Module') 229 | V revision (0-15) 230 | 231 | */ 232 | -------------------------------------------------------------------------------- /source/cpuinfo.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015 Ben Croston 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #ifndef CPUINFO_H 24 | #define CPUINFO_H 25 | typedef struct 26 | { 27 | int p1_revision; 28 | char *ram; 29 | char *manufacturer; 30 | char *processor; 31 | char *type; 32 | char revision[1024]; 33 | } rpi_info; 34 | #endif /* CPUINFO_H */ 35 | 36 | int get_rpi_info(rpi_info *info); 37 | -------------------------------------------------------------------------------- /source/event_gpio.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013-2016 Ben Croston 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include "event_gpio.h" 32 | 33 | const char *stredge[4] = {"none", "rising", "falling", "both"}; 34 | 35 | struct gpios 36 | { 37 | unsigned int gpio; 38 | int value_fd; 39 | int exported; 40 | int edge; 41 | int initial_thread; 42 | int initial_wait; 43 | int thread_added; 44 | int bouncetime; 45 | unsigned long long lastcall; 46 | struct gpios *next; 47 | }; 48 | struct gpios *gpio_list = NULL; 49 | 50 | // event callbacks 51 | struct callback 52 | { 53 | unsigned int gpio; 54 | void (*func)(unsigned int gpio); 55 | struct callback *next; 56 | }; 57 | struct callback *callbacks = NULL; 58 | 59 | static pthread_t threads; 60 | int event_occurred[54] = { 0 }; 61 | int thread_running = 0; 62 | int epfd_thread = -1; 63 | int epfd_blocking = -1; 64 | 65 | /************* /sys/class/gpio functions ************/ 66 | int gpio_export(unsigned int gpio) 67 | { 68 | int fd, len; 69 | char str_gpio[4]; 70 | 71 | if ((fd = open("/sys/class/gpio/export", O_WRONLY)) < 0) 72 | return -1; 73 | 74 | len = snprintf(str_gpio, sizeof(str_gpio), "%d", gpio); 75 | write(fd, str_gpio, len); 76 | close(fd); 77 | 78 | return 0; 79 | } 80 | 81 | int gpio_unexport(unsigned int gpio) 82 | { 83 | int fd, len; 84 | char str_gpio[4]; 85 | 86 | if ((fd = open("/sys/class/gpio/unexport", O_WRONLY)) < 0) 87 | return -1; 88 | 89 | len = snprintf(str_gpio, sizeof(str_gpio), "%d", gpio); 90 | write(fd, str_gpio, len); 91 | close(fd); 92 | 93 | return 0; 94 | } 95 | 96 | int gpio_set_direction(unsigned int gpio, unsigned int in_flag) 97 | { 98 | int retry; 99 | struct timespec delay; 100 | int fd; 101 | char filename[34]; 102 | 103 | snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d/direction", gpio); 104 | 105 | // retry waiting for udev to set correct file permissions 106 | delay.tv_sec = 0; 107 | delay.tv_nsec = 10000000L; // 10ms 108 | for (retry=0; retry<100; retry++) { 109 | if ((fd = open(filename, O_WRONLY)) >= 0) 110 | break; 111 | nanosleep(&delay, NULL); 112 | } 113 | if (retry >= 100) 114 | return -1; 115 | 116 | if (in_flag) 117 | write(fd, "in", 3); 118 | else 119 | write(fd, "out", 4); 120 | 121 | close(fd); 122 | return 0; 123 | } 124 | 125 | int gpio_set_edge(unsigned int gpio, unsigned int edge) 126 | { 127 | int fd; 128 | char filename[29]; 129 | 130 | snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d/edge", gpio); 131 | 132 | if ((fd = open(filename, O_WRONLY)) < 0) 133 | return -1; 134 | 135 | write(fd, stredge[edge], strlen(stredge[edge]) + 1); 136 | close(fd); 137 | return 0; 138 | } 139 | 140 | int open_value_file(unsigned int gpio) 141 | { 142 | int fd; 143 | char filename[30]; 144 | 145 | // create file descriptor of value file 146 | snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d/value", gpio); 147 | if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) < 0) 148 | return -1; 149 | return fd; 150 | } 151 | 152 | /********* gpio list functions **********/ 153 | struct gpios *get_gpio(unsigned int gpio) 154 | { 155 | struct gpios *g = gpio_list; 156 | while (g != NULL) { 157 | if (g->gpio == gpio) 158 | return g; 159 | g = g->next; 160 | } 161 | return NULL; 162 | } 163 | 164 | struct gpios *get_gpio_from_value_fd(int fd) 165 | { 166 | struct gpios *g = gpio_list; 167 | while (g != NULL) { 168 | if (g->value_fd == fd) 169 | return g; 170 | g = g->next; 171 | } 172 | return NULL; 173 | } 174 | 175 | struct gpios *new_gpio(unsigned int gpio) 176 | { 177 | struct gpios *new_gpio; 178 | 179 | new_gpio = malloc(sizeof(struct gpios)); 180 | if (new_gpio == 0) { 181 | return NULL; // out of memory 182 | } 183 | 184 | new_gpio->gpio = gpio; 185 | if (gpio_export(gpio) != 0) { 186 | free(new_gpio); 187 | return NULL; 188 | } 189 | new_gpio->exported = 1; 190 | 191 | if (gpio_set_direction(gpio,1) != 0) { // 1==input 192 | free(new_gpio); 193 | return NULL; 194 | } 195 | 196 | if ((new_gpio->value_fd = open_value_file(gpio)) == -1) { 197 | gpio_unexport(gpio); 198 | free(new_gpio); 199 | return NULL; 200 | } 201 | 202 | new_gpio->initial_thread = 1; 203 | new_gpio->initial_wait = 1; 204 | new_gpio->bouncetime = -666; 205 | new_gpio->lastcall = 0; 206 | new_gpio->thread_added = 0; 207 | 208 | if (gpio_list == NULL) { 209 | new_gpio->next = NULL; 210 | } else { 211 | new_gpio->next = gpio_list; 212 | } 213 | gpio_list = new_gpio; 214 | return new_gpio; 215 | } 216 | 217 | void delete_gpio(unsigned int gpio) 218 | { 219 | struct gpios *g = gpio_list; 220 | struct gpios *temp; 221 | struct gpios *prev = NULL; 222 | 223 | while (g != NULL) { 224 | if (g->gpio == gpio) { 225 | if (prev == NULL) 226 | gpio_list = g->next; 227 | else 228 | prev->next = g->next; 229 | temp = g; 230 | g = g->next; 231 | free(temp); 232 | return; 233 | } else { 234 | prev = g; 235 | g = g->next; 236 | } 237 | } 238 | } 239 | 240 | int gpio_event_added(unsigned int gpio) 241 | { 242 | struct gpios *g = gpio_list; 243 | while (g != NULL) { 244 | if (g->gpio == gpio) 245 | return g->edge; 246 | g = g->next; 247 | } 248 | return 0; 249 | } 250 | 251 | /******* callback list functions ********/ 252 | int add_edge_callback(unsigned int gpio, void (*func)(unsigned int gpio)) 253 | { 254 | struct callback *cb = callbacks; 255 | struct callback *new_cb; 256 | 257 | new_cb = malloc(sizeof(struct callback)); 258 | if (new_cb == 0) 259 | return -1; // out of memory 260 | 261 | new_cb->gpio = gpio; 262 | new_cb->func = func; 263 | new_cb->next = NULL; 264 | 265 | if (callbacks == NULL) { 266 | // start new list 267 | callbacks = new_cb; 268 | } else { 269 | // add to end of list 270 | while (cb->next != NULL) 271 | cb = cb->next; 272 | cb->next = new_cb; 273 | } 274 | return 0; 275 | } 276 | 277 | int callback_exists(unsigned int gpio) 278 | { 279 | struct callback *cb = callbacks; 280 | while (cb != NULL) { 281 | if (cb->gpio == gpio) 282 | return 1; 283 | cb = cb->next; 284 | } 285 | return 0; 286 | } 287 | 288 | void run_callbacks(unsigned int gpio) 289 | { 290 | struct callback *cb = callbacks; 291 | while (cb != NULL) 292 | { 293 | if (cb->gpio == gpio) 294 | cb->func(cb->gpio); 295 | cb = cb->next; 296 | } 297 | } 298 | 299 | void remove_callbacks(unsigned int gpio) 300 | { 301 | struct callback *cb = callbacks; 302 | struct callback *temp; 303 | struct callback *prev = NULL; 304 | 305 | while (cb != NULL) 306 | { 307 | if (cb->gpio == gpio) 308 | { 309 | if (prev == NULL) 310 | callbacks = cb->next; 311 | else 312 | prev->next = cb->next; 313 | temp = cb; 314 | cb = cb->next; 315 | free(temp); 316 | } else { 317 | prev = cb; 318 | cb = cb->next; 319 | } 320 | } 321 | } 322 | 323 | void *poll_thread(void *threadarg) 324 | { 325 | struct epoll_event events; 326 | char buf; 327 | struct timeval tv_timenow; 328 | unsigned long long timenow; 329 | struct gpios *g; 330 | int n; 331 | 332 | thread_running = 1; 333 | while (thread_running) { 334 | if ((n = epoll_wait(epfd_thread, &events, 1, -1)) == -1) { 335 | thread_running = 0; 336 | pthread_exit(NULL); 337 | } 338 | if (n > 0) { 339 | lseek(events.data.fd, 0, SEEK_SET); 340 | if (read(events.data.fd, &buf, 1) != 1) { 341 | thread_running = 0; 342 | pthread_exit(NULL); 343 | } 344 | g = get_gpio_from_value_fd(events.data.fd); 345 | if (g->initial_thread) { // ignore first epoll trigger 346 | g->initial_thread = 0; 347 | } else { 348 | gettimeofday(&tv_timenow, NULL); 349 | timenow = tv_timenow.tv_sec*1E6 + tv_timenow.tv_usec; 350 | if (g->bouncetime == -666 || timenow - g->lastcall > g->bouncetime*1000 || g->lastcall == 0 || g->lastcall > timenow) { 351 | g->lastcall = timenow; 352 | event_occurred[g->gpio] = 1; 353 | run_callbacks(g->gpio); 354 | } 355 | } 356 | } 357 | } 358 | thread_running = 0; 359 | pthread_exit(NULL); 360 | } 361 | 362 | void remove_edge_detect(unsigned int gpio) 363 | { 364 | struct epoll_event ev; 365 | struct gpios *g = get_gpio(gpio); 366 | 367 | if (g == NULL) 368 | return; 369 | 370 | // delete epoll of fd 371 | 372 | ev.events = EPOLLIN | EPOLLET | EPOLLPRI; 373 | ev.data.fd = g->value_fd; 374 | epoll_ctl(epfd_thread, EPOLL_CTL_DEL, g->value_fd, &ev); 375 | 376 | // delete callbacks for gpio 377 | remove_callbacks(gpio); 378 | 379 | // btc fixme - check return result?? 380 | gpio_set_edge(gpio, NO_EDGE); 381 | g->edge = NO_EDGE; 382 | 383 | if (g->value_fd != -1) 384 | close(g->value_fd); 385 | 386 | // btc fixme - check return result?? 387 | gpio_unexport(gpio); 388 | event_occurred[gpio] = 0; 389 | 390 | delete_gpio(gpio); 391 | } 392 | 393 | int event_detected(unsigned int gpio) 394 | { 395 | if (event_occurred[gpio]) { 396 | event_occurred[gpio] = 0; 397 | return 1; 398 | } else { 399 | return 0; 400 | } 401 | } 402 | 403 | void event_cleanup(unsigned int gpio) 404 | // gpio of -666 means clean every channel used 405 | { 406 | struct gpios *g = gpio_list; 407 | struct gpios *temp = NULL; 408 | 409 | while (g != NULL) { 410 | if ((gpio == -666) || (g->gpio == gpio)) 411 | temp = g->next; 412 | remove_edge_detect(g->gpio); 413 | g = temp; 414 | } 415 | if (gpio_list == NULL) 416 | if (epfd_blocking != -1) 417 | close(epfd_blocking); 418 | epfd_blocking = -1; 419 | if (epfd_thread != -1) 420 | close(epfd_thread); 421 | epfd_thread = -1; 422 | thread_running = 0; 423 | } 424 | 425 | void event_cleanup_all(void) 426 | { 427 | event_cleanup(-666); 428 | } 429 | 430 | int add_edge_detect(unsigned int gpio, unsigned int edge, int bouncetime) 431 | // return values: 432 | // 0 - Success 433 | // 1 - Edge detection already added 434 | // 2 - Other error 435 | { 436 | struct epoll_event ev; 437 | long t = 0; 438 | struct gpios *g; 439 | int i = -1; 440 | 441 | i = gpio_event_added(gpio); 442 | if (i == 0) { // event not already added 443 | if ((g = new_gpio(gpio)) == NULL) 444 | return 2; 445 | 446 | gpio_set_edge(gpio, edge); 447 | g->edge = edge; 448 | g->bouncetime = bouncetime; 449 | } else if (i == edge) { // get existing event 450 | g = get_gpio(gpio); 451 | if ((bouncetime != -666 && g->bouncetime != bouncetime) || // different event bouncetime used 452 | (g->thread_added)) // event already added 453 | return 1; 454 | } else { 455 | return 1; 456 | } 457 | 458 | // create epfd_thread if not already open 459 | if ((epfd_thread == -1) && ((epfd_thread = epoll_create(1)) == -1)) 460 | return 2; 461 | 462 | // add to epoll fd 463 | ev.events = EPOLLIN | EPOLLET | EPOLLPRI; 464 | ev.data.fd = g->value_fd; 465 | if (epoll_ctl(epfd_thread, EPOLL_CTL_ADD, g->value_fd, &ev) == -1) { 466 | remove_edge_detect(gpio); 467 | return 2; 468 | } 469 | g->thread_added = 1; 470 | 471 | // start poll thread if it is not already running 472 | if (!thread_running) { 473 | if (pthread_create(&threads, NULL, poll_thread, (void *)t) != 0) { 474 | remove_edge_detect(gpio); 475 | return 2; 476 | } 477 | } 478 | return 0; 479 | } 480 | 481 | int blocking_wait_for_edge(unsigned int gpio, unsigned int edge, int bouncetime, int timeout) 482 | // return values: 483 | // 1 - Success (edge detected) 484 | // 0 - Timeout 485 | // -1 - Edge detection already added 486 | // -2 - Other error 487 | { 488 | int n, ed; 489 | struct epoll_event events, ev; 490 | char buf; 491 | struct gpios *g = NULL; 492 | struct timeval tv_timenow; 493 | unsigned long long timenow; 494 | int finished = 0; 495 | int initial_edge = 1; 496 | 497 | if (callback_exists(gpio)) 498 | return -1; 499 | 500 | // add gpio if it has not been added already 501 | ed = gpio_event_added(gpio); 502 | if (ed == edge) { // get existing record 503 | g = get_gpio(gpio); 504 | if (g->bouncetime != -666 && g->bouncetime != bouncetime) { 505 | return -1; 506 | } 507 | } else if (ed == NO_EDGE) { // not found so add event 508 | if ((g = new_gpio(gpio)) == NULL) { 509 | return -2; 510 | } 511 | gpio_set_edge(gpio, edge); 512 | g->edge = edge; 513 | g->bouncetime = bouncetime; 514 | } else { // ed != edge - event for a different edge 515 | g = get_gpio(gpio); 516 | gpio_set_edge(gpio, edge); 517 | g->edge = edge; 518 | g->bouncetime = bouncetime; 519 | g->initial_wait = 1; 520 | } 521 | 522 | // create epfd_blocking if not already open 523 | if ((epfd_blocking == -1) && ((epfd_blocking = epoll_create(1)) == -1)) { 524 | return -2; 525 | } 526 | 527 | // add to epoll fd 528 | ev.events = EPOLLIN | EPOLLET | EPOLLPRI; 529 | ev.data.fd = g->value_fd; 530 | if (epoll_ctl(epfd_blocking, EPOLL_CTL_ADD, g->value_fd, &ev) == -1) { 531 | return -2; 532 | } 533 | 534 | // wait for edge 535 | while (!finished) { 536 | if ((n = epoll_wait(epfd_blocking, &events, 1, timeout)) == -1) { 537 | epoll_ctl(epfd_blocking, EPOLL_CTL_DEL, g->value_fd, &ev); 538 | return -2; 539 | } 540 | if (initial_edge) { // first time triggers with current state, so ignore 541 | initial_edge = 0; 542 | } else { 543 | gettimeofday(&tv_timenow, NULL); 544 | timenow = tv_timenow.tv_sec*1E6 + tv_timenow.tv_usec; 545 | if (g->bouncetime == -666 || timenow - g->lastcall > g->bouncetime*1000 || g->lastcall == 0 || g->lastcall > timenow) { 546 | g->lastcall = timenow; 547 | finished = 1; 548 | } 549 | } 550 | } 551 | 552 | // check event was valid 553 | if (n > 0) { 554 | lseek(events.data.fd, 0, SEEK_SET); 555 | if ((read(events.data.fd, &buf, 1) != 1) || (events.data.fd != g->value_fd)) { 556 | epoll_ctl(epfd_blocking, EPOLL_CTL_DEL, g->value_fd, &ev); 557 | return -2; 558 | } 559 | } 560 | 561 | epoll_ctl(epfd_blocking, EPOLL_CTL_DEL, g->value_fd, &ev); 562 | if (n == 0) { 563 | return 0; // timeout 564 | } else { 565 | return 1; // edge found 566 | } 567 | } 568 | -------------------------------------------------------------------------------- /source/event_gpio.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013-2015 Ben Croston 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #define NO_EDGE 0 24 | #define RISING_EDGE 1 25 | #define FALLING_EDGE 2 26 | #define BOTH_EDGE 3 27 | 28 | int add_edge_detect(unsigned int gpio, unsigned int edge, int bouncetime); 29 | void remove_edge_detect(unsigned int gpio); 30 | int add_edge_callback(unsigned int gpio, void (*func)(unsigned int gpio)); 31 | int event_detected(unsigned int gpio); 32 | int gpio_event_added(unsigned int gpio); 33 | int event_initialise(void); 34 | void event_cleanup(unsigned int gpio); 35 | void event_cleanup_all(void); 36 | int blocking_wait_for_edge(unsigned int gpio, unsigned int edge, int bouncetime, int timeout); 37 | -------------------------------------------------------------------------------- /source/i2c/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'stefan' 2 | -------------------------------------------------------------------------------- /source/i2c/i2c.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * This file is part of pyA20. 4 | * i2c.c is python I2C extension. 5 | * 6 | * Copyright (c) 2014 Stefan Mavrodiev @ OLIMEX LTD, 7 | * 8 | * pyA20 is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 | * MA 02110-1301, USA. 22 | */ 23 | 24 | #include "Python.h" 25 | 26 | #if PY_MAJOR_VERSION >= 3 27 | #define PyInt_FromLong PyLong_FromLong 28 | #define PyInt_AsLong PyLong_AsLong 29 | #endif 30 | 31 | #include "i2c_lib.h" 32 | 33 | /* Define some global variables */ 34 | char bus[30]; 35 | int address; 36 | int fd; 37 | 38 | /** 39 | * Initialize module by giving bus name. 40 | * 41 | * @param self 42 | * @param args string with the name of the i2c bus 43 | * @return none 44 | */ 45 | static PyObject* py_init(PyObject* self, PyObject* args) { 46 | 47 | char *temp; 48 | 49 | /* Parse arguments */ 50 | if (!PyArg_ParseTuple(args, "s", &temp)) { 51 | return NULL; 52 | } 53 | 54 | /* Copy name to the global variable */ 55 | strcpy(bus, temp); 56 | 57 | Py_RETURN_NONE; 58 | } 59 | 60 | /** 61 | * Read n bytes from i2c-bus 62 | * 63 | * @param self 64 | * @param args number of bytes to read 65 | * @return tuple containing the read bytes 66 | */ 67 | static PyObject* py_read(PyObject* self, PyObject* args) { 68 | 69 | int bytes; 70 | int i; 71 | 72 | PyObject *pylist = 0; 73 | PyObject *item = 0; 74 | 75 | /* Parse arguments */ 76 | if (!PyArg_ParseTuple(args, "i", &bytes)) { 77 | return NULL; 78 | } 79 | 80 | /* If bytes are <= 0 this could cause segmentation fault */ 81 | if (bytes <= 0) { 82 | PyErr_SetString(PyExc_ValueError, "i2c read: invalid number of bytes to read"); 83 | return NULL; 84 | } 85 | 86 | /* Allocate memory for the temp buffer */ 87 | uint8_t *read_buffer = (uint8_t *) malloc(bytes * sizeof (uint8_t)); 88 | 89 | 90 | /* Read bytes and check return status */ 91 | if (i2c_read(fd, read_buffer, bytes) < 0) { 92 | Py_DECREF(pylist); 93 | // Py_DECREF(item); 94 | free(read_buffer); 95 | return PyErr_SetFromErrno(PyExc_IOError); 96 | } 97 | 98 | /* Make new list with size n bytes */ 99 | pylist = PyList_New(bytes); 100 | 101 | /* Fill list with data from read_buffer */ 102 | if (pylist != NULL) { 103 | for (i = 0; i < bytes; i++) { 104 | item = PyInt_FromLong(read_buffer[i]); 105 | PyList_SET_ITEM(pylist, i, item); 106 | } 107 | } 108 | 109 | /* Cleanup */ 110 | free(read_buffer); 111 | // Py_DECREF(item); 112 | 113 | /* Return list */ 114 | return pylist; 115 | } 116 | 117 | /** 118 | * Open I2C device 119 | * 120 | * @param self 121 | * @param args 122 | * @return 123 | */ 124 | static PyObject* py_open(PyObject* self, PyObject* args) { 125 | 126 | /* Parse arguments */ 127 | if (!PyArg_ParseTuple(args, "i", &address)) { 128 | return NULL; 129 | } 130 | 131 | /* Open slave device*/ 132 | fd = i2c_open(bus, address); 133 | if (fd < 0) { 134 | return PyErr_SetFromErrno(PyExc_IOError); 135 | } 136 | 137 | Py_RETURN_NONE; 138 | } 139 | 140 | /** 141 | * Write bytes to I2C slave device 142 | * 143 | * @param self 144 | * @param args bytes to send 145 | * @return none 146 | */ 147 | static PyObject* py_write(PyObject* self, PyObject* args) { 148 | 149 | PyObject *list = 0; 150 | PyObject *item = 0; 151 | 152 | /* Parse arguments */ 153 | if (!PyArg_ParseTuple(args, "O!", &PyList_Type, &list)) { 154 | return NULL; 155 | } 156 | 157 | /* Get length of the list */ 158 | uint8_t n = PyList_Size(list); 159 | 160 | 161 | /* Allocate memory */ 162 | uint8_t *buffer = (uint8_t*)malloc(n * sizeof(uint8_t)); 163 | 164 | /* Populate output buffer */ 165 | uint8_t i; 166 | for (i = 0; i < n; i++) { 167 | item = PyList_GetItem(list, i); 168 | buffer[i] = (unsigned char) PyInt_AsLong(item); 169 | } 170 | 171 | /* Send data */ 172 | if (i2c_send(fd, buffer, n) < 0) { 173 | return PyErr_SetFromErrno(PyExc_IOError); 174 | } 175 | 176 | /* Do cleanup */ 177 | free(buffer); 178 | Py_DECREF(list); 179 | // Py_DECREF(item); 180 | 181 | Py_RETURN_NONE; 182 | } 183 | 184 | /** 185 | * Close communication with I2C slave device 186 | * 187 | * @param self 188 | * @param args none 189 | * @return none 190 | */ 191 | static PyObject* py_close(PyObject* self, PyObject *args) { 192 | 193 | /* Close file descriptor */ 194 | if (i2c_close(fd) < 0) { 195 | return PyErr_SetFromErrno(PyExc_IOError); 196 | } 197 | Py_RETURN_NONE; 198 | } 199 | 200 | static PyMethodDef module_methods[] = { 201 | {"init", py_init, METH_VARARGS, "Initialize module"}, 202 | {"open", py_open, METH_VARARGS, "Open file descriptor"}, 203 | {"close", py_close, METH_NOARGS, "Close file descriptor"}, 204 | {"read", py_read, METH_VARARGS, "Read n bytes from I2C bus"}, 205 | {"write", py_write, METH_VARARGS, "Write n bytes to I2C bus"}, 206 | {NULL, NULL, 0, NULL} 207 | }; 208 | 209 | #if PY_MAJOR_VERSION >= 3 210 | static struct PyModuleDef module_def = { 211 | PyModuleDef_HEAD_INIT, 212 | "RPi._I2C", 213 | NULL, 214 | -1, 215 | module_methods 216 | }; 217 | #endif 218 | 219 | PyMODINIT_FUNC 220 | #if PY_MAJOR_VERSION >= 3 221 | PyInit__I2C(void) { 222 | #else 223 | init_I2C(void) { 224 | #endif 225 | 226 | PyObject* module = NULL; 227 | 228 | #if PY_MAJOR_VERSION >= 3 229 | module = PyModule_Create(&module_def); 230 | #else 231 | module = Py_InitModule("RPi._I2C", module_methods); 232 | #endif 233 | 234 | 235 | if (module == NULL) 236 | #if PY_MAJOR_VERSION >= 3 237 | return NULL; 238 | #else 239 | return; 240 | #endif 241 | 242 | #if PY_MAJOR_VERSION >= 3 243 | return module; 244 | #else 245 | return; 246 | #endif 247 | 248 | } 249 | 250 | 251 | 252 | -------------------------------------------------------------------------------- /source/i2c/i2c_lib.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * This file is part of pyA20. 4 | * i2c_lib.c is python I2C extension. 5 | * 6 | * Copyright (c) 2014 Stefan Mavrodiev @ OLIMEX LTD, 7 | * 8 | * pyA20 is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 | * MA 02110-1301, USA. 22 | */ 23 | 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | 33 | #include 34 | #include 35 | 36 | int i2c_open(char *device, uint8_t address) { 37 | int fd; 38 | int ret; 39 | 40 | fd = open(device, O_RDWR); 41 | if (fd < 0) 42 | return fd; 43 | 44 | ret = ioctl(fd, I2C_SLAVE_FORCE, address); 45 | if (ret < 0) 46 | return ret; 47 | 48 | return fd; 49 | 50 | } 51 | 52 | int i2c_close(int fd) { 53 | return (close(fd)); 54 | } 55 | 56 | int i2c_send(int fd, uint8_t *buffer, uint8_t num_bytes) { 57 | return (write(fd, buffer, num_bytes)); 58 | } 59 | 60 | int i2c_read(int fd, uint8_t *buffer, uint8_t num_bytes) { 61 | return (read(fd, buffer, num_bytes)); 62 | } 63 | -------------------------------------------------------------------------------- /source/i2c/i2c_lib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * This file is part of pyA20. 4 | * i2c_lib.h is python I2C extension. 5 | * 6 | * Copyright (c) 2014 Stefan Mavrodiev @ OLIMEX LTD, 7 | * 8 | * pyA20 is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 | * MA 02110-1301, USA. 22 | */ 23 | 24 | #ifndef _I2C_LIB_H 25 | #define _I2C_LIB_H 26 | 27 | extern int i2c_open(char *device, uint8_t address); 28 | extern int i2c_close(int fd); 29 | extern int i2c_send(int fd, uint8_t *buffer, uint8_t num_bytes); 30 | extern int i2c_read(int fd, uint8_t *buffer, uint8_t num_bytes); 31 | 32 | #endif -------------------------------------------------------------------------------- /source/py_gpio.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2016 Ben Croston 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #include "Python.h" 24 | #include "c_gpio.h" 25 | #include "event_gpio.h" 26 | #include "py_pwm.h" 27 | #include "cpuinfo.h" 28 | #include "constants.h" 29 | #include "common.h" 30 | 31 | static PyObject *rpi_revision; // deprecated 32 | static PyObject *board_info; 33 | static int gpio_warnings = 1; 34 | 35 | struct py_callback 36 | { 37 | unsigned int gpio; 38 | PyObject *py_cb; 39 | struct py_callback *next; 40 | }; 41 | static struct py_callback *py_callbacks = NULL; 42 | 43 | static int mmap_gpio_mem(void) 44 | { 45 | int result; 46 | 47 | if (module_setup) 48 | return 0; 49 | 50 | result = setup(); 51 | if (result == SETUP_DEVMEM_FAIL) 52 | { 53 | PyErr_SetString(PyExc_RuntimeError, "No access to /dev/mem. Try running as root!"); 54 | return 1; 55 | } else if (result == SETUP_MALLOC_FAIL) { 56 | PyErr_NoMemory(); 57 | return 2; 58 | } else if (result == SETUP_MMAP_FAIL) { 59 | PyErr_SetString(PyExc_RuntimeError, "Mmap of GPIO registers failed"); 60 | return 3; 61 | } else if (result == SETUP_CPUINFO_FAIL) { 62 | PyErr_SetString(PyExc_RuntimeError, "Unable to open /proc/cpuinfo"); 63 | return 4; 64 | } else if (result == SETUP_NOT_RPI_FAIL) { 65 | PyErr_SetString(PyExc_RuntimeError, "Not running on a RPi!"); 66 | return 5; 67 | } else { // result == SETUP_OK 68 | module_setup = 1; 69 | return 0; 70 | } 71 | } 72 | 73 | // python function cleanup(channel=None) 74 | static PyObject *py_cleanup(PyObject *self, PyObject *args, PyObject *kwargs) 75 | { 76 | int i; 77 | int chancount = -666; 78 | int found = 0; 79 | int channel = -666; 80 | unsigned int gpio; 81 | PyObject *chanlist = NULL; 82 | PyObject *chantuple = NULL; 83 | PyObject *tempobj; 84 | static char *kwlist[] = {"channel", NULL}; 85 | unsigned int bcm_gpio; 86 | 87 | void cleanup_one(void) 88 | { 89 | // clean up any /sys/class exports 90 | event_cleanup(gpio); 91 | 92 | // set everything back to input 93 | if (gpio_direction[bcm_gpio] != -1) { 94 | setup_gpio(gpio, INPUT, PUD_OFF); 95 | gpio_direction[bcm_gpio] = -1; 96 | found = 1; 97 | } 98 | } 99 | 100 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O", kwlist, &chanlist)) 101 | return NULL; 102 | 103 | if (chanlist == NULL) { // channel kwarg not set 104 | // do nothing 105 | #if PY_MAJOR_VERSION > 2 106 | } else if (PyLong_Check(chanlist)) { 107 | channel = (int)PyLong_AsLong(chanlist); 108 | #else 109 | } else if (PyInt_Check(chanlist)) { 110 | channel = (int)PyInt_AsLong(chanlist); 111 | #endif 112 | if (PyErr_Occurred()) 113 | return NULL; 114 | chanlist = NULL; 115 | } else if (PyList_Check(chanlist)) { 116 | chancount = PyList_Size(chanlist); 117 | } else if (PyTuple_Check(chanlist)) { 118 | chantuple = chanlist; 119 | chanlist = NULL; 120 | chancount = PyTuple_Size(chantuple); 121 | } else { 122 | // raise exception 123 | PyErr_SetString(PyExc_ValueError, "Channel must be an integer or list/tuple of integers"); 124 | return NULL; 125 | } 126 | 127 | if (module_setup && !setup_error) { 128 | if (channel == -666 && chancount == -666) { // channel not set - cleanup everything 129 | // clean up any /sys/class exports 130 | event_cleanup_all(); 131 | 132 | // set everything back to input 133 | //for (i=0; i<54; i++) { 134 | for (i=0; i<41; i++) { 135 | if (gpio_direction[i] != -1) { 136 | //setup_gpio(i, INPUT, PUD_OFF); 137 | setup_gpio(*(pinToGpioPineA64+i), INPUT, PUD_OFF); 138 | gpio_direction[i] = -1; 139 | found = 1; 140 | } 141 | } 142 | gpio_mode = MODE_UNKNOWN; 143 | } else if (channel != -666) { // channel was an int indicating single channel 144 | if (get_gpio_number(channel, &gpio, &bcm_gpio)) 145 | return NULL; 146 | cleanup_one(); 147 | } else { // channel was a list/tuple 148 | for (i=0; i 2 160 | if (PyLong_Check(tempobj)) { 161 | channel = (int)PyLong_AsLong(tempobj); 162 | #else 163 | if (PyInt_Check(tempobj)) { 164 | channel = (int)PyInt_AsLong(tempobj); 165 | #endif 166 | if (PyErr_Occurred()) 167 | return NULL; 168 | } else { 169 | PyErr_SetString(PyExc_ValueError, "Channel must be an integer"); 170 | return NULL; 171 | } 172 | 173 | if (get_gpio_number(channel, &gpio, &bcm_gpio)) 174 | return NULL; 175 | cleanup_one(); 176 | } 177 | } 178 | } 179 | 180 | // check if any channels set up - if not warn about misuse of GPIO.cleanup() 181 | if (!found && gpio_warnings) { 182 | PyErr_WarnEx(NULL, "No channels have been set up yet - nothing to clean up! Try cleaning up at the end of your program instead!", 1); 183 | } 184 | 185 | Py_RETURN_NONE; 186 | } 187 | 188 | // python function setup(channel(s), direction, pull_up_down=PUD_OFF, initial=None) 189 | static PyObject *py_setup_channel(PyObject *self, PyObject *args, PyObject *kwargs) 190 | { 191 | unsigned int gpio; 192 | int channel = -1; 193 | int direction; 194 | int i, chancount; 195 | PyObject *chanlist = NULL; 196 | PyObject *chantuple = NULL; 197 | PyObject *tempobj; 198 | int pud = PUD_OFF + PY_PUD_CONST_OFFSET; 199 | int initial = -1; 200 | static char *kwlist[] = {"channel", "direction", "pull_up_down", "initial", NULL}; 201 | int func; 202 | unsigned int bcm_gpio; 203 | 204 | int setup_one(void) { 205 | if (get_gpio_number(channel, &gpio, &bcm_gpio)) 206 | return 0; 207 | 208 | func = gpio_function(gpio); 209 | if (gpio_warnings && // warnings enabled and 210 | ((func != 0 && func != 1) || // (already one of the alt functions or 211 | (gpio_direction[bcm_gpio] == -1 && func == 1))) // already an output not set from this program) 212 | { 213 | PyErr_WarnEx(NULL, "This channel is already in use, continuing anyway. Use GPIO.setwarnings(False) to disable warnings.", 1); 214 | } 215 | 216 | // warn about pull/up down on i2c channels 217 | if (gpio_warnings) { 218 | if (rpiinfo.p1_revision == 0) { // compute module - do nothing 219 | } else if ((rpiinfo.p1_revision == 1 && (gpio == 0 || gpio == 1)) || 220 | (gpio == 2 || gpio == 3)) { 221 | if (pud == PUD_UP || pud == PUD_DOWN) 222 | PyErr_WarnEx(NULL, "A physical pull up resistor is fitted on this channel!", 1); 223 | } 224 | } 225 | 226 | if (direction == OUTPUT && (initial == LOW || initial == HIGH)) { 227 | output_gpio(gpio, initial); 228 | } 229 | setup_gpio(gpio, direction, pud); 230 | gpio_direction[bcm_gpio] = direction; 231 | return 1; 232 | } 233 | 234 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Oi|ii", kwlist, &chanlist, &direction, &pud, &initial)) 235 | return NULL; 236 | 237 | #if PY_MAJOR_VERSION > 2 238 | if (PyLong_Check(chanlist)) { 239 | channel = (int)PyLong_AsLong(chanlist); 240 | #else 241 | if (PyInt_Check(chanlist)) { 242 | channel = (int)PyInt_AsLong(chanlist); 243 | #endif 244 | if (PyErr_Occurred()) 245 | return NULL; 246 | chanlist = NULL; 247 | } else if (PyList_Check(chanlist)) { 248 | // do nothing 249 | } else if (PyTuple_Check(chanlist)) { 250 | chantuple = chanlist; 251 | chanlist = NULL; 252 | } else { 253 | // raise exception 254 | PyErr_SetString(PyExc_ValueError, "Channel must be an integer or list/tuple of integers"); 255 | return NULL; 256 | } 257 | 258 | // check module has been imported cleanly 259 | if (setup_error) 260 | { 261 | PyErr_SetString(PyExc_RuntimeError, "Module not imported correctly!"); 262 | return NULL; 263 | } 264 | 265 | if (mmap_gpio_mem()) 266 | return NULL; 267 | 268 | if (direction != INPUT && direction != OUTPUT) { 269 | PyErr_SetString(PyExc_ValueError, "An invalid direction was passed to setup()"); 270 | return 0; 271 | } 272 | 273 | if (direction == OUTPUT && pud != PUD_OFF + PY_PUD_CONST_OFFSET) { 274 | PyErr_SetString(PyExc_ValueError, "pull_up_down parameter is not valid for outputs"); 275 | return 0; 276 | } 277 | 278 | if (direction == INPUT && initial != -1) { 279 | PyErr_SetString(PyExc_ValueError, "initial parameter is not valid for inputs"); 280 | return 0; 281 | } 282 | 283 | if (direction == OUTPUT) 284 | pud = PUD_OFF + PY_PUD_CONST_OFFSET; 285 | 286 | pud -= PY_PUD_CONST_OFFSET; 287 | if (pud != PUD_OFF && pud != PUD_DOWN && pud != PUD_UP) { 288 | PyErr_SetString(PyExc_ValueError, "Invalid value for pull_up_down - should be either PUD_OFF, PUD_UP or PUD_DOWN"); 289 | return NULL; 290 | } 291 | 292 | if (chanlist) { 293 | chancount = PyList_Size(chanlist); 294 | } else if (chantuple) { 295 | chancount = PyTuple_Size(chantuple); 296 | } else { 297 | if (!setup_one()) 298 | return NULL; 299 | Py_RETURN_NONE; 300 | } 301 | 302 | for (i=0; i 2 314 | if (PyLong_Check(tempobj)) { 315 | channel = (int)PyLong_AsLong(tempobj); 316 | #else 317 | if (PyInt_Check(tempobj)) { 318 | channel = (int)PyInt_AsLong(tempobj); 319 | #endif 320 | if (PyErr_Occurred()) 321 | return NULL; 322 | } else { 323 | PyErr_SetString(PyExc_ValueError, "Channel must be an integer"); 324 | return NULL; 325 | } 326 | 327 | if (!setup_one()) 328 | return NULL; 329 | } 330 | 331 | Py_RETURN_NONE; 332 | } 333 | 334 | // python function output(channel(s), value(s)) 335 | static PyObject *py_output_gpio(PyObject *self, PyObject *args) 336 | { 337 | unsigned int gpio; 338 | int channel = -1; 339 | int value = -1; 340 | int i; 341 | PyObject *chanlist = NULL; 342 | PyObject *valuelist = NULL; 343 | PyObject *chantuple = NULL; 344 | PyObject *valuetuple = NULL; 345 | PyObject *tempobj = NULL; 346 | int chancount = -1; 347 | int valuecount = -1; 348 | unsigned int bcm_gpio; 349 | 350 | int output(void) { 351 | if (get_gpio_number(channel, &gpio, &bcm_gpio)) 352 | return 0; 353 | 354 | if (gpio_direction[bcm_gpio] != OUTPUT) 355 | { 356 | PyErr_SetString(PyExc_RuntimeError, "The GPIO channel has not been set up as an OUTPUT"); 357 | return 0; 358 | } 359 | 360 | if (check_gpio_priv()) 361 | return 0; 362 | 363 | output_gpio(gpio, value); 364 | return 1; 365 | } 366 | 367 | if (!PyArg_ParseTuple(args, "OO", &chanlist, &valuelist)) 368 | return NULL; 369 | 370 | #if PY_MAJOR_VERSION >= 3 371 | if (PyLong_Check(chanlist)) { 372 | channel = (int)PyLong_AsLong(chanlist); 373 | #else 374 | if (PyInt_Check(chanlist)) { 375 | channel = (int)PyInt_AsLong(chanlist); 376 | #endif 377 | if (PyErr_Occurred()) 378 | return NULL; 379 | chanlist = NULL; 380 | } else if (PyList_Check(chanlist)) { 381 | // do nothing 382 | } else if (PyTuple_Check(chanlist)) { 383 | chantuple = chanlist; 384 | chanlist = NULL; 385 | } else { 386 | PyErr_SetString(PyExc_ValueError, "Channel must be an integer or list/tuple of integers"); 387 | return NULL; 388 | } 389 | 390 | #if PY_MAJOR_VERSION >= 3 391 | if (PyLong_Check(valuelist)) { 392 | value = (int)PyLong_AsLong(valuelist); 393 | #else 394 | if (PyInt_Check(valuelist)) { 395 | value = (int)PyInt_AsLong(valuelist); 396 | #endif 397 | if (PyErr_Occurred()) 398 | return NULL; 399 | valuelist = NULL; 400 | } else if (PyList_Check(valuelist)) { 401 | // do nothing 402 | } else if (PyTuple_Check(valuelist)) { 403 | valuetuple = valuelist; 404 | valuelist = NULL; 405 | } else { 406 | PyErr_SetString(PyExc_ValueError, "Value must be an integer/boolean or a list/tuple of integers/booleans"); 407 | return NULL; 408 | } 409 | 410 | if (chanlist) 411 | chancount = PyList_Size(chanlist); 412 | if (chantuple) 413 | chancount = PyTuple_Size(chantuple); 414 | if (valuelist) 415 | valuecount = PyList_Size(valuelist); 416 | if (valuetuple) 417 | valuecount = PyTuple_Size(valuetuple); 418 | if ((chancount != -1 && chancount != valuecount && valuecount != -1) || (chancount == -1 && valuecount != -1)) { 419 | PyErr_SetString(PyExc_RuntimeError, "Number of channels != number of values"); 420 | return NULL; 421 | } 422 | 423 | if (chancount == -1) { 424 | if (!output()) 425 | return NULL; 426 | Py_RETURN_NONE; 427 | } 428 | 429 | for (i=0; i= 3 442 | if (PyLong_Check(tempobj)) { 443 | channel = (int)PyLong_AsLong(tempobj); 444 | #else 445 | if (PyInt_Check(tempobj)) { 446 | channel = (int)PyInt_AsLong(tempobj); 447 | #endif 448 | if (PyErr_Occurred()) 449 | return NULL; 450 | } else { 451 | PyErr_SetString(PyExc_ValueError, "Channel must be an integer"); 452 | return NULL; 453 | } 454 | 455 | // get value 456 | if (valuecount > 0) { 457 | if (valuelist) { 458 | if ((tempobj = PyList_GetItem(valuelist, i)) == NULL) { 459 | return NULL; 460 | } 461 | } else { // assume valuetuple 462 | if ((tempobj = PyTuple_GetItem(valuetuple, i)) == NULL) { 463 | return NULL; 464 | } 465 | } 466 | #if PY_MAJOR_VERSION >= 3 467 | if (PyLong_Check(tempobj)) { 468 | value = (int)PyLong_AsLong(tempobj); 469 | #else 470 | if (PyInt_Check(tempobj)) { 471 | value = (int)PyInt_AsLong(tempobj); 472 | #endif 473 | if (PyErr_Occurred()) 474 | return NULL; 475 | } else { 476 | PyErr_SetString(PyExc_ValueError, "Value must be an integer or boolean"); 477 | return NULL; 478 | } 479 | } 480 | if (!output()) 481 | return NULL; 482 | } 483 | 484 | Py_RETURN_NONE; 485 | } 486 | 487 | // python function value = input(channel) 488 | static PyObject *py_input_gpio(PyObject *self, PyObject *args) 489 | { 490 | unsigned int gpio; 491 | int channel; 492 | PyObject *value; 493 | unsigned int bcm_gpio; 494 | 495 | if (!PyArg_ParseTuple(args, "i", &channel)) 496 | return NULL; 497 | 498 | if (get_gpio_number(channel, &gpio, &bcm_gpio)) 499 | return NULL; 500 | 501 | // check channel is set up as an input or output 502 | if (gpio_direction[bcm_gpio] != INPUT && gpio_direction[bcm_gpio] != OUTPUT) 503 | { 504 | PyErr_SetString(PyExc_RuntimeError, "You must setup() the GPIO channel first"); 505 | return NULL; 506 | } 507 | 508 | if (check_gpio_priv()) 509 | return NULL; 510 | 511 | if (input_gpio(gpio)) { 512 | value = Py_BuildValue("i", HIGH); 513 | } else { 514 | value = Py_BuildValue("i", LOW); 515 | } 516 | return value; 517 | } 518 | 519 | // python function setmode(mode) 520 | static PyObject *py_setmode(PyObject *self, PyObject *args) 521 | { 522 | int new_mode; 523 | 524 | if (!PyArg_ParseTuple(args, "i", &new_mode)) 525 | return NULL; 526 | 527 | if (gpio_mode != MODE_UNKNOWN && new_mode != gpio_mode) 528 | { 529 | PyErr_SetString(PyExc_ValueError, "A different mode has already been set!"); 530 | return NULL; 531 | } 532 | 533 | if (setup_error) 534 | { 535 | PyErr_SetString(PyExc_RuntimeError, "Module not imported correctly!"); 536 | return NULL; 537 | } 538 | 539 | if (new_mode != BOARD && new_mode != BCM) 540 | { 541 | PyErr_SetString(PyExc_ValueError, "An invalid mode was passed to setmode()"); 542 | return NULL; 543 | } 544 | 545 | if (rpiinfo.p1_revision == 0 && new_mode == BOARD) 546 | { 547 | PyErr_SetString(PyExc_RuntimeError, "BOARD numbering system not applicable on compute module"); 548 | return NULL; 549 | } 550 | 551 | gpio_mode = new_mode; 552 | Py_RETURN_NONE; 553 | } 554 | 555 | // python function getmode() 556 | static PyObject *py_getmode(PyObject *self, PyObject *args) 557 | { 558 | PyObject *value; 559 | 560 | if (setup_error) 561 | { 562 | PyErr_SetString(PyExc_RuntimeError, "Module not imported correctly!"); 563 | return NULL; 564 | } 565 | 566 | if (gpio_mode == MODE_UNKNOWN) 567 | Py_RETURN_NONE; 568 | 569 | value = Py_BuildValue("i", gpio_mode); 570 | return value; 571 | } 572 | 573 | static unsigned int chan_from_gpio(unsigned int gpio) 574 | { 575 | int chan; 576 | int chans; 577 | 578 | if (gpio_mode == BCM) { 579 | for (chan=1; chan<41; chan++) 580 | if (*(pinToGpioPineA64+chan) == gpio) 581 | return chan; 582 | return -1; 583 | //return gpio; 584 | } 585 | if (rpiinfo.p1_revision == 0) // not applicable for compute module 586 | return -1; 587 | else if (rpiinfo.p1_revision == 1 || rpiinfo.p1_revision == 2) 588 | chans = 26; 589 | else 590 | chans = 40; 591 | for (chan=1; chan<=chans; chan++) 592 | if (*(*pin_to_gpio+chan) == gpio) 593 | return chan; 594 | return -1; 595 | } 596 | 597 | static void run_py_callbacks(unsigned int gpio) 598 | { 599 | PyObject *result; 600 | PyGILState_STATE gstate; 601 | struct py_callback *cb = py_callbacks; 602 | 603 | while (cb != NULL) 604 | { 605 | if (cb->gpio == gpio) { 606 | // run callback 607 | gstate = PyGILState_Ensure(); 608 | result = PyObject_CallFunction(cb->py_cb, "i", chan_from_gpio(gpio)); 609 | if (result == NULL && PyErr_Occurred()){ 610 | PyErr_Print(); 611 | PyErr_Clear(); 612 | } 613 | Py_XDECREF(result); 614 | PyGILState_Release(gstate); 615 | } 616 | cb = cb->next; 617 | } 618 | } 619 | 620 | static int add_py_callback(unsigned int gpio, PyObject *cb_func) 621 | { 622 | struct py_callback *new_py_cb; 623 | struct py_callback *cb = py_callbacks; 624 | 625 | // add callback to py_callbacks list 626 | new_py_cb = malloc(sizeof(struct py_callback)); 627 | if (new_py_cb == 0) 628 | { 629 | PyErr_NoMemory(); 630 | return -1; 631 | } 632 | new_py_cb->py_cb = cb_func; 633 | Py_XINCREF(cb_func); // Add a reference to new callback 634 | new_py_cb->gpio = gpio; 635 | new_py_cb->next = NULL; 636 | if (py_callbacks == NULL) { 637 | py_callbacks = new_py_cb; 638 | } else { 639 | // add to end of list 640 | while (cb->next != NULL) 641 | cb = cb->next; 642 | cb->next = new_py_cb; 643 | } 644 | add_edge_callback(gpio, run_py_callbacks); 645 | return 0; 646 | } 647 | 648 | // python function add_event_callback(gpio, callback) 649 | static PyObject *py_add_event_callback(PyObject *self, PyObject *args, PyObject *kwargs) 650 | { 651 | unsigned int gpio; 652 | int channel; 653 | PyObject *cb_func; 654 | char *kwlist[] = {"gpio", "callback", NULL}; 655 | unsigned int bcm_gpio; 656 | 657 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iO|i", kwlist, &channel, &cb_func)) 658 | return NULL; 659 | 660 | if (!PyCallable_Check(cb_func)) 661 | { 662 | PyErr_SetString(PyExc_TypeError, "Parameter must be callable"); 663 | return NULL; 664 | } 665 | 666 | if (get_gpio_number(channel, &gpio, &bcm_gpio)) 667 | return NULL; 668 | 669 | // check channel is set up as an input 670 | if (gpio_direction[bcm_gpio] != INPUT) 671 | { 672 | PyErr_SetString(PyExc_RuntimeError, "You must setup() the GPIO channel as an input first"); 673 | return NULL; 674 | } 675 | 676 | if (!gpio_event_added(gpio)) 677 | { 678 | PyErr_SetString(PyExc_RuntimeError, "Add event detection using add_event_detect first before adding a callback"); 679 | return NULL; 680 | } 681 | 682 | if (add_py_callback(gpio, cb_func) != 0) 683 | return NULL; 684 | 685 | Py_RETURN_NONE; 686 | } 687 | 688 | // python function add_event_detect(gpio, edge, callback=None, bouncetime=None) 689 | static PyObject *py_add_event_detect(PyObject *self, PyObject *args, PyObject *kwargs) 690 | { 691 | unsigned int gpio; 692 | int channel, edge, result; 693 | int bouncetime = -666; 694 | PyObject *cb_func = NULL; 695 | char *kwlist[] = {"gpio", "edge", "callback", "bouncetime", NULL}; 696 | unsigned int bcm_gpio; 697 | 698 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii|Oi", kwlist, &channel, &edge, &cb_func, &bouncetime)) 699 | return NULL; 700 | 701 | if (cb_func != NULL && !PyCallable_Check(cb_func)) 702 | { 703 | PyErr_SetString(PyExc_TypeError, "Parameter must be callable"); 704 | return NULL; 705 | } 706 | 707 | if (get_gpio_number(channel, &gpio, &bcm_gpio)) 708 | return NULL; 709 | 710 | // check channel is set up as an input 711 | if (gpio_direction[bcm_gpio] != INPUT) 712 | { 713 | PyErr_SetString(PyExc_RuntimeError, "You must setup() the GPIO channel as an input first"); 714 | return NULL; 715 | } 716 | 717 | // is edge valid value 718 | edge -= PY_EVENT_CONST_OFFSET; 719 | if (edge != RISING_EDGE && edge != FALLING_EDGE && edge != BOTH_EDGE) 720 | { 721 | PyErr_SetString(PyExc_ValueError, "The edge must be set to RISING, FALLING or BOTH"); 722 | return NULL; 723 | } 724 | 725 | if (bouncetime <= 0 && bouncetime != -666) 726 | { 727 | PyErr_SetString(PyExc_ValueError, "Bouncetime must be greater than 0"); 728 | return NULL; 729 | } 730 | 731 | if (check_gpio_priv()) 732 | return NULL; 733 | 734 | if ((result = add_edge_detect(gpio, edge, bouncetime)) != 0) // starts a thread 735 | { 736 | if (result == 1) 737 | { 738 | PyErr_SetString(PyExc_RuntimeError, "Conflicting edge detection already enabled for this GPIO channel"); 739 | return NULL; 740 | } else { 741 | PyErr_SetString(PyExc_RuntimeError, "Failed to add edge detection"); 742 | return NULL; 743 | } 744 | } 745 | 746 | if (cb_func != NULL) 747 | if (add_py_callback(gpio, cb_func) != 0) 748 | return NULL; 749 | 750 | Py_RETURN_NONE; 751 | } 752 | 753 | // python function remove_event_detect(gpio) 754 | static PyObject *py_remove_event_detect(PyObject *self, PyObject *args) 755 | { 756 | unsigned int gpio; 757 | int channel; 758 | struct py_callback *cb = py_callbacks; 759 | struct py_callback *temp; 760 | struct py_callback *prev = NULL; 761 | unsigned int bcm_gpio; 762 | 763 | if (!PyArg_ParseTuple(args, "i", &channel)) 764 | return NULL; 765 | 766 | if (get_gpio_number(channel, &gpio, &bcm_gpio)) 767 | return NULL; 768 | 769 | // remove all python callbacks for gpio 770 | while (cb != NULL) 771 | { 772 | if (cb->gpio == gpio) 773 | { 774 | Py_XDECREF(cb->py_cb); 775 | if (prev == NULL) 776 | py_callbacks = cb->next; 777 | else 778 | prev->next = cb->next; 779 | temp = cb; 780 | cb = cb->next; 781 | free(temp); 782 | } else { 783 | prev = cb; 784 | cb = cb->next; 785 | } 786 | } 787 | 788 | if (check_gpio_priv()) 789 | return NULL; 790 | 791 | remove_edge_detect(gpio); 792 | 793 | Py_RETURN_NONE; 794 | } 795 | 796 | // python function value = event_detected(channel) 797 | static PyObject *py_event_detected(PyObject *self, PyObject *args) 798 | { 799 | unsigned int gpio; 800 | int channel; 801 | unsigned int bcm_gpio; 802 | 803 | if (!PyArg_ParseTuple(args, "i", &channel)) 804 | return NULL; 805 | 806 | if (get_gpio_number(channel, &gpio, &bcm_gpio)) 807 | return NULL; 808 | 809 | if (event_detected(gpio)) 810 | Py_RETURN_TRUE; 811 | else 812 | Py_RETURN_FALSE; 813 | } 814 | 815 | // python function channel = wait_for_edge(channel, edge, bouncetime=None, timeout=None) 816 | static PyObject *py_wait_for_edge(PyObject *self, PyObject *args, PyObject *kwargs) 817 | { 818 | unsigned int gpio; 819 | int channel, edge, result; 820 | int bouncetime = -666; // None 821 | int timeout = -1; // None 822 | unsigned int bcm_gpio; 823 | 824 | static char *kwlist[] = {"channel", "edge", "bouncetime", "timeout", NULL}; 825 | 826 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii|ii", kwlist, &channel, &edge, &bouncetime, &timeout)) 827 | return NULL; 828 | 829 | if (get_gpio_number(channel, &gpio, &bcm_gpio)) 830 | return NULL; 831 | 832 | // check channel is setup as an input 833 | if (gpio_direction[bcm_gpio] != INPUT) 834 | { 835 | PyErr_SetString(PyExc_RuntimeError, "You must setup() the GPIO channel as an input first"); 836 | return NULL; 837 | } 838 | 839 | // is edge a valid value? 840 | edge -= PY_EVENT_CONST_OFFSET; 841 | if (edge != RISING_EDGE && edge != FALLING_EDGE && edge != BOTH_EDGE) 842 | { 843 | PyErr_SetString(PyExc_ValueError, "The edge must be set to RISING, FALLING or BOTH"); 844 | return NULL; 845 | } 846 | 847 | if (bouncetime <= 0 && bouncetime != -666) 848 | { 849 | PyErr_SetString(PyExc_ValueError, "Bouncetime must be greater than 0"); 850 | return NULL; 851 | } 852 | 853 | if (timeout <= 0 && timeout != -1) 854 | { 855 | PyErr_SetString(PyExc_ValueError, "Timeout must be greater than 0"); 856 | return NULL; 857 | } 858 | 859 | if (check_gpio_priv()) 860 | return NULL; 861 | 862 | Py_BEGIN_ALLOW_THREADS // disable GIL 863 | result = blocking_wait_for_edge(gpio, edge, bouncetime, timeout); 864 | Py_END_ALLOW_THREADS // enable GIL 865 | 866 | if (result == 0) { 867 | Py_RETURN_NONE; 868 | } else if (result == -1) { 869 | PyErr_SetString(PyExc_RuntimeError, "Conflicting edge detection events already exist for this GPIO channel"); 870 | return NULL; 871 | } else if (result == -2) { 872 | PyErr_SetString(PyExc_RuntimeError, "Error waiting for edge"); 873 | return NULL; 874 | } else { 875 | return Py_BuildValue("i", channel); 876 | } 877 | 878 | } 879 | 880 | // python function value = gpio_function(channel) 881 | static PyObject *py_gpio_function(PyObject *self, PyObject *args) 882 | { 883 | unsigned int gpio; 884 | int channel; 885 | int f; 886 | PyObject *func; 887 | unsigned int bcm_gpio; 888 | 889 | if (!PyArg_ParseTuple(args, "i", &channel)) 890 | return NULL; 891 | 892 | if (get_gpio_number(channel, &gpio, &bcm_gpio)) 893 | return NULL; 894 | 895 | if (mmap_gpio_mem()) 896 | return NULL; 897 | 898 | f = gpio_function(gpio); 899 | switch (f) 900 | { 901 | case 0 : f = INPUT; break; 902 | case 1 : f = OUTPUT; break; 903 | 904 | // ALT 0 905 | case 4 : switch (gpio) 906 | { 907 | case 0 : 908 | case 1 : 909 | case 2 : 910 | case 3 : f = I2C; break; 911 | 912 | case 7 : 913 | case 8 : 914 | case 9 : 915 | case 10 : 916 | case 11 : f = SPI; break; 917 | 918 | case 12 : 919 | case 13 : f = PWM; break; 920 | 921 | case 14 : 922 | case 15 : f = SERIAL; break; 923 | 924 | case 28 : 925 | case 29 : f = I2C; break; 926 | 927 | default : f = MODE_UNKNOWN; break; 928 | } 929 | break; 930 | 931 | // ALT 5 932 | case 2 : if (gpio == 18 || gpio == 19) f = PWM; else f = MODE_UNKNOWN; 933 | break; 934 | 935 | // ALT 4 936 | case 3 : switch (gpio) 937 | 938 | { 939 | case 16 : 940 | case 17 : 941 | case 18 : 942 | case 19 : 943 | case 20 : 944 | case 21 : f = SPI; break; 945 | default : f = MODE_UNKNOWN; break; 946 | } 947 | break; 948 | 949 | default : f = MODE_UNKNOWN; break; 950 | 951 | } 952 | func = Py_BuildValue("i", f); 953 | return func; 954 | } 955 | 956 | // python function setwarnings(state) 957 | static PyObject *py_setwarnings(PyObject *self, PyObject *args) 958 | { 959 | if (!PyArg_ParseTuple(args, "i", &gpio_warnings)) 960 | return NULL; 961 | 962 | if (setup_error) 963 | { 964 | PyErr_SetString(PyExc_RuntimeError, "Module not imported correctly!"); 965 | return NULL; 966 | } 967 | 968 | Py_RETURN_NONE; 969 | } 970 | 971 | static const char moduledocstring[] = "GPIO functionality of a Raspberry Pi using Python"; 972 | 973 | PyMethodDef rpi_gpio_methods[] = { 974 | {"setup", (PyCFunction)py_setup_channel, METH_VARARGS | METH_KEYWORDS, "Set up a GPIO channel or list of channels with a direction and (optional) pull/up down control\nchannel - either board pin number or BCM number depending on which mode is set.\ndirection - IN or OUT\n[pull_up_down] - PUD_OFF (default), PUD_UP or PUD_DOWN\n[initial] - Initial value for an output channel"}, 975 | {"cleanup", (PyCFunction)py_cleanup, METH_VARARGS | METH_KEYWORDS, "Clean up by resetting all GPIO channels that have been used by this program to INPUT with no pullup/pulldown and no event detection\n[channel] - individual channel or list/tuple of channels to clean up. Default - clean every channel that has been used."}, 976 | {"output", py_output_gpio, METH_VARARGS, "Output to a GPIO channel or list of channels\nchannel - either board pin number or BCM number depending on which mode is set.\nvalue - 0/1 or False/True or LOW/HIGH"}, 977 | {"input", py_input_gpio, METH_VARARGS, "Input from a GPIO channel. Returns HIGH=1=True or LOW=0=False\nchannel - either board pin number or BCM number depending on which mode is set."}, 978 | {"setmode", py_setmode, METH_VARARGS, "Set up numbering mode to use for channels.\nBOARD - Use Raspberry Pi board numbers\nBCM - Use Broadcom GPIO 00..nn numbers"}, 979 | {"getmode", py_getmode, METH_VARARGS, "Get numbering mode used for channel numbers.\nReturns BOARD, BCM or None"}, 980 | {"add_event_detect", (PyCFunction)py_add_event_detect, METH_VARARGS | METH_KEYWORDS, "Enable edge detection events for a particular GPIO channel.\nchannel - either board pin number or BCM number depending on which mode is set.\nedge - RISING, FALLING or BOTH\n[callback] - A callback function for the event (optional)\n[bouncetime] - Switch bounce timeout in ms for callback"}, 981 | {"remove_event_detect", py_remove_event_detect, METH_VARARGS, "Remove edge detection for a particular GPIO channel\nchannel - either board pin number or BCM number depending on which mode is set."}, 982 | {"event_detected", py_event_detected, METH_VARARGS, "Returns True if an edge has occured on a given GPIO. You need to enable edge detection using add_event_detect() first.\nchannel - either board pin number or BCM number depending on which mode is set."}, 983 | {"add_event_callback", (PyCFunction)py_add_event_callback, METH_VARARGS | METH_KEYWORDS, "Add a callback for an event already defined using add_event_detect()\nchannel - either board pin number or BCM number depending on which mode is set.\ncallback - a callback function"}, 984 | {"wait_for_edge", (PyCFunction)py_wait_for_edge, METH_VARARGS | METH_KEYWORDS, "Wait for an edge. Returns the channel number or None on timeout.\nchannel - either board pin number or BCM number depending on which mode is set.\nedge - RISING, FALLING or BOTH\n[bouncetime] - time allowed between calls to allow for switchbounce\n[timeout] - timeout in ms"}, 985 | {"gpio_function", py_gpio_function, METH_VARARGS, "Return the current GPIO function (IN, OUT, PWM, SERIAL, I2C, SPI)\nchannel - either board pin number or BCM number depending on which mode is set."}, 986 | {"setwarnings", py_setwarnings, METH_VARARGS, "Enable or disable warning messages"}, 987 | {NULL, NULL, 0, NULL} 988 | }; 989 | 990 | #if PY_MAJOR_VERSION > 2 991 | static struct PyModuleDef rpigpiomodule = { 992 | PyModuleDef_HEAD_INIT, 993 | "RPi._GPIO", // name of module 994 | moduledocstring, // module documentation, may be NULL 995 | -1, // size of per-interpreter state of the module, or -1 if the module keeps state in global variables. 996 | rpi_gpio_methods 997 | }; 998 | #endif 999 | 1000 | #if PY_MAJOR_VERSION > 2 1001 | PyMODINIT_FUNC PyInit__GPIO(void) 1002 | #else 1003 | PyMODINIT_FUNC init_GPIO(void) 1004 | #endif 1005 | { 1006 | int i; 1007 | PyObject *module = NULL; 1008 | 1009 | #if PY_MAJOR_VERSION > 2 1010 | if ((module = PyModule_Create(&rpigpiomodule)) == NULL) 1011 | return NULL; 1012 | #else 1013 | if ((module = Py_InitModule3("RPi._GPIO", rpi_gpio_methods, moduledocstring)) == NULL) 1014 | return; 1015 | #endif 1016 | 1017 | define_constants(module); 1018 | 1019 | for (i=0; i<54; i++) 1020 | gpio_direction[i] = -1; 1021 | 1022 | // detect board revision and set up accordingly 1023 | if (get_rpi_info(&rpiinfo)) 1024 | { 1025 | PyErr_SetString(PyExc_RuntimeError, "This module can only be run on a Raspberry Pi!"); 1026 | setup_error = 1; 1027 | #if PY_MAJOR_VERSION > 2 1028 | return NULL; 1029 | #else 1030 | return; 1031 | #endif 1032 | } 1033 | board_info = Py_BuildValue("{sissssssssss}", 1034 | "P1_REVISION",rpiinfo.p1_revision, 1035 | "REVISION",&rpiinfo.revision, 1036 | "TYPE",rpiinfo.type, 1037 | "MANUFACTURER",rpiinfo.manufacturer, 1038 | "PROCESSOR",rpiinfo.processor, 1039 | "RAM",rpiinfo.ram); 1040 | PyModule_AddObject(module, "RPI_INFO", board_info); 1041 | 1042 | if (rpiinfo.p1_revision == 1) { 1043 | pin_to_gpio = &pin_to_gpio_rev1; 1044 | } else if (rpiinfo.p1_revision == 2) { 1045 | pin_to_gpio = &pin_to_gpio_rev2; 1046 | } else if (rpiinfo.p1_revision == 3) { // assume model B+ or A+ or 2B 1047 | pin_to_gpio = &pin_to_gpio_rev3; 1048 | } else { // assume model Pine A64/A64+ 1049 | pin_to_gpio = &physToGpioPineA64; 1050 | } 1051 | 1052 | rpi_revision = Py_BuildValue("i", rpiinfo.p1_revision); // deprecated 1053 | PyModule_AddObject(module, "RPI_REVISION", rpi_revision); // deprecated 1054 | 1055 | // Add PWM class 1056 | if (PWM_init_PWMType() == NULL) 1057 | #if PY_MAJOR_VERSION > 2 1058 | return NULL; 1059 | #else 1060 | return; 1061 | #endif 1062 | Py_INCREF(&PWMType); 1063 | PyModule_AddObject(module, "PWM", (PyObject*)&PWMType); 1064 | 1065 | if (!PyEval_ThreadsInitialized()) 1066 | PyEval_InitThreads(); 1067 | 1068 | // register exit functions - last declared is called first 1069 | if (Py_AtExit(cleanup) != 0) 1070 | { 1071 | setup_error = 1; 1072 | cleanup(); 1073 | #if PY_MAJOR_VERSION > 2 1074 | return NULL; 1075 | #else 1076 | return; 1077 | #endif 1078 | } 1079 | 1080 | if (Py_AtExit(event_cleanup_all) != 0) 1081 | { 1082 | setup_error = 1; 1083 | cleanup(); 1084 | #if PY_MAJOR_VERSION > 2 1085 | return NULL; 1086 | #else 1087 | return; 1088 | #endif 1089 | } 1090 | 1091 | #if PY_MAJOR_VERSION > 2 1092 | return module; 1093 | #else 1094 | return; 1095 | #endif 1096 | } 1097 | -------------------------------------------------------------------------------- /source/py_pwm.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Ben Croston 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #include "Python.h" 24 | #include "soft_pwm.h" 25 | #include "py_pwm.h" 26 | #include "common.h" 27 | #include "c_gpio.h" 28 | 29 | typedef struct 30 | { 31 | PyObject_HEAD 32 | unsigned int gpio; 33 | float freq; 34 | float dutycycle; 35 | } PWMObject; 36 | 37 | // python method PWM.__init__(self, channel, frequency) 38 | static int PWM_init(PWMObject *self, PyObject *args, PyObject *kwds) 39 | { 40 | int channel; 41 | float frequency; 42 | unsigned int real_gpio; 43 | 44 | if (!PyArg_ParseTuple(args, "if", &channel, &frequency)) 45 | return -1; 46 | 47 | // convert channel to gpio 48 | if (get_gpio_number(channel, &real_gpio, &(self->gpio))) 49 | return -1; 50 | 51 | // ensure channel set as output 52 | if (gpio_direction[self->gpio] != OUTPUT) 53 | { 54 | PyErr_SetString(PyExc_RuntimeError, "You must setup() the GPIO channel as an output first"); 55 | return -1; 56 | } 57 | 58 | if (frequency <= 0.0) 59 | { 60 | PyErr_SetString(PyExc_ValueError, "frequency must be greater than 0.0"); 61 | return -1; 62 | } 63 | 64 | self->freq = frequency; 65 | 66 | pwm_set_frequency(self->gpio, self->freq); 67 | return 0; 68 | } 69 | 70 | // python method PWM.start(self, dutycycle) 71 | static PyObject *PWM_start(PWMObject *self, PyObject *args) 72 | { 73 | float dutycycle; 74 | 75 | if (!PyArg_ParseTuple(args, "f", &dutycycle)) 76 | return NULL; 77 | 78 | if (dutycycle < 0.0 || dutycycle > 100.0) 79 | { 80 | PyErr_SetString(PyExc_ValueError, "dutycycle must have a value from 0.0 to 100.0"); 81 | return NULL; 82 | } 83 | 84 | self->dutycycle = dutycycle; 85 | pwm_set_duty_cycle(self->gpio, self->dutycycle); 86 | pwm_start(self->gpio); 87 | Py_RETURN_NONE; 88 | } 89 | 90 | // python method PWM.ChangeDutyCycle(self, dutycycle) 91 | static PyObject *PWM_ChangeDutyCycle(PWMObject *self, PyObject *args) 92 | { 93 | float dutycycle = 0.0; 94 | if (!PyArg_ParseTuple(args, "f", &dutycycle)) 95 | return NULL; 96 | 97 | if (dutycycle < 0.0 || dutycycle > 100.0) 98 | { 99 | PyErr_SetString(PyExc_ValueError, "dutycycle must have a value from 0.0 to 100.0"); 100 | return NULL; 101 | } 102 | 103 | self->dutycycle = dutycycle; 104 | pwm_set_duty_cycle(self->gpio, self->dutycycle); 105 | Py_RETURN_NONE; 106 | } 107 | 108 | // python method PWM. ChangeFrequency(self, frequency) 109 | static PyObject *PWM_ChangeFrequency(PWMObject *self, PyObject *args) 110 | { 111 | float frequency = 1.0; 112 | 113 | if (!PyArg_ParseTuple(args, "f", &frequency)) 114 | return NULL; 115 | 116 | if (frequency <= 0.0) 117 | { 118 | PyErr_SetString(PyExc_ValueError, "frequency must be greater than 0.0"); 119 | return NULL; 120 | } 121 | 122 | self->freq = frequency; 123 | 124 | pwm_set_frequency(self->gpio, self->freq); 125 | Py_RETURN_NONE; 126 | } 127 | 128 | // python function PWM.stop(self) 129 | static PyObject *PWM_stop(PWMObject *self, PyObject *args) 130 | { 131 | pwm_stop(self->gpio); 132 | Py_RETURN_NONE; 133 | } 134 | 135 | // deallocation method 136 | static void PWM_dealloc(PWMObject *self) 137 | { 138 | pwm_stop(self->gpio); 139 | Py_TYPE(self)->tp_free((PyObject*)self); 140 | } 141 | 142 | static PyMethodDef 143 | PWM_methods[] = { 144 | { "start", (PyCFunction)PWM_start, METH_VARARGS, "Start software PWM\ndutycycle - the duty cycle (0.0 to 100.0)" }, 145 | { "ChangeDutyCycle", (PyCFunction)PWM_ChangeDutyCycle, METH_VARARGS, "Change the duty cycle\ndutycycle - between 0.0 and 100.0" }, 146 | { "ChangeFrequency", (PyCFunction)PWM_ChangeFrequency, METH_VARARGS, "Change the frequency\nfrequency - frequency in Hz (freq > 1.0)" }, 147 | { "stop", (PyCFunction)PWM_stop, METH_VARARGS, "Stop software PWM" }, 148 | { NULL } 149 | }; 150 | 151 | PyTypeObject PWMType = { 152 | PyVarObject_HEAD_INIT(NULL,0) 153 | "RPi.GPIO.PWM", // tp_name 154 | sizeof(PWMObject), // tp_basicsize 155 | 0, // tp_itemsize 156 | (destructor)PWM_dealloc, // tp_dealloc 157 | 0, // tp_print 158 | 0, // tp_getattr 159 | 0, // tp_setattr 160 | 0, // tp_compare 161 | 0, // tp_repr 162 | 0, // tp_as_number 163 | 0, // tp_as_sequence 164 | 0, // tp_as_mapping 165 | 0, // tp_hash 166 | 0, // tp_call 167 | 0, // tp_str 168 | 0, // tp_getattro 169 | 0, // tp_setattro 170 | 0, // tp_as_buffer 171 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flag 172 | "Pulse Width Modulation class", // tp_doc 173 | 0, // tp_traverse 174 | 0, // tp_clear 175 | 0, // tp_richcompare 176 | 0, // tp_weaklistoffset 177 | 0, // tp_iter 178 | 0, // tp_iternext 179 | PWM_methods, // tp_methods 180 | 0, // tp_members 181 | 0, // tp_getset 182 | 0, // tp_base 183 | 0, // tp_dict 184 | 0, // tp_descr_get 185 | 0, // tp_descr_set 186 | 0, // tp_dictoffset 187 | (initproc)PWM_init, // tp_init 188 | 0, // tp_alloc 189 | 0, // tp_new 190 | }; 191 | 192 | PyTypeObject *PWM_init_PWMType(void) 193 | { 194 | // Fill in some slots in the type, and make it ready 195 | PWMType.tp_new = PyType_GenericNew; 196 | if (PyType_Ready(&PWMType) < 0) 197 | return NULL; 198 | 199 | return &PWMType; 200 | } 201 | -------------------------------------------------------------------------------- /source/py_pwm.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Ben Croston 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | extern PyTypeObject PWMType; 24 | PyTypeObject *PWM_init_PWMType(void); 25 | -------------------------------------------------------------------------------- /source/soft_pwm.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Ben Croston 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include "c_gpio.h" 27 | #include "common.h" 28 | #include "soft_pwm.h" 29 | static pthread_t threads; 30 | 31 | struct pwm 32 | { 33 | unsigned int gpio; 34 | float freq; 35 | float dutycycle; 36 | float basetime; 37 | float slicetime; 38 | struct timespec req_on, req_off; 39 | int running; 40 | struct pwm *next; 41 | }; 42 | struct pwm *pwm_list = NULL; 43 | 44 | extern int pinea64_found; 45 | 46 | void remove_pwm(unsigned int gpio) 47 | { 48 | struct pwm *p = pwm_list; 49 | struct pwm *prev = NULL; 50 | struct pwm *temp; 51 | 52 | while (p != NULL) 53 | { 54 | if (p->gpio == gpio) 55 | { 56 | if (prev == NULL) 57 | pwm_list = p->next; 58 | else 59 | prev->next = p->next; 60 | temp = p; 61 | p = p->next; 62 | free(temp); 63 | } else { 64 | prev = p; 65 | p = p->next; 66 | } 67 | } 68 | } 69 | 70 | void calculate_times(struct pwm *p) 71 | { 72 | long long usec; 73 | 74 | usec = (long long)(p->dutycycle * p->slicetime * 1000.0); 75 | p->req_on.tv_sec = (int)(usec / 1000000LL); 76 | usec -= (long long)p->req_on.tv_sec * 1000000LL; 77 | p->req_on.tv_nsec = (long)usec * 1000L; 78 | 79 | usec = (long long)((100.0-p->dutycycle) * p->slicetime * 1000.0); 80 | p->req_off.tv_sec = (int)(usec / 1000000LL); 81 | usec -= (long long)p->req_off.tv_sec * 1000000LL; 82 | p->req_off.tv_nsec = (long)usec * 1000L; 83 | } 84 | 85 | void full_sleep(struct timespec *req) 86 | { 87 | struct timespec rem = {0}; 88 | 89 | if (nanosleep(req,&rem) == -1) 90 | full_sleep(&rem); 91 | } 92 | 93 | void *pwm_thread(void *threadarg) 94 | { 95 | struct pwm *p = (struct pwm *)threadarg; 96 | 97 | while (p->running) 98 | { 99 | 100 | if (p->dutycycle > 0.0) 101 | { 102 | if (pinea64_found) 103 | output_gpio(*(pinToGpioPineA64 + p->gpio), 1); 104 | else 105 | output_gpio(p->gpio, 1); 106 | full_sleep(&p->req_on); 107 | } 108 | 109 | if (p->dutycycle < 100.0) 110 | { 111 | if (pinea64_found) 112 | output_gpio(*(pinToGpioPineA64 + p->gpio), 0); 113 | else 114 | output_gpio(p->gpio, 0); 115 | full_sleep(&p->req_off); 116 | } 117 | } 118 | 119 | // clean up 120 | if (pinea64_found) 121 | output_gpio(*(pinToGpioPineA64 + p->gpio), 0); 122 | else 123 | output_gpio(p->gpio, 0); 124 | remove_pwm(p->gpio); 125 | pthread_exit(NULL); 126 | } 127 | 128 | struct pwm *add_new_pwm(unsigned int gpio) 129 | { 130 | struct pwm *new_pwm; 131 | 132 | new_pwm = malloc(sizeof(struct pwm)); 133 | new_pwm->gpio = gpio; 134 | new_pwm->running = 0; 135 | new_pwm->next = NULL; 136 | // default to 1 kHz frequency, dutycycle 0.0 137 | new_pwm->freq = 1000.0; 138 | new_pwm->dutycycle = 0.0; 139 | new_pwm->basetime = 1.0; // 1 ms 140 | new_pwm->slicetime = 0.01; // 0.01 ms 141 | calculate_times(new_pwm); 142 | return new_pwm; 143 | } 144 | 145 | struct pwm *find_pwm(unsigned int gpio) 146 | { 147 | struct pwm *p = pwm_list; 148 | 149 | if (pwm_list == NULL) 150 | { 151 | pwm_list = add_new_pwm(gpio); 152 | return pwm_list; 153 | } 154 | 155 | while (p != NULL) 156 | { 157 | if (p->gpio == gpio) 158 | return p; 159 | if (p->next == NULL) 160 | { 161 | p->next = add_new_pwm(gpio); 162 | return p->next; 163 | } 164 | p = p->next; 165 | } 166 | return NULL; 167 | } 168 | 169 | void pwm_set_duty_cycle(unsigned int gpio, float dutycycle) 170 | { 171 | struct pwm *p; 172 | 173 | if (dutycycle < 0.0 || dutycycle > 100.0) 174 | { 175 | // btc fixme - error 176 | return; 177 | } 178 | 179 | if ((p = find_pwm(gpio)) != NULL) 180 | { 181 | p->dutycycle = dutycycle; 182 | calculate_times(p); 183 | } 184 | } 185 | 186 | void pwm_set_frequency(unsigned int gpio, float freq) 187 | { 188 | struct pwm *p; 189 | 190 | if (freq <= 0.0) // to avoid divide by zero 191 | { 192 | // btc fixme - error 193 | return; 194 | } 195 | 196 | if ((p = find_pwm(gpio)) != NULL) 197 | { 198 | p->basetime = 1000.0 / freq; // calculated in ms 199 | p->slicetime = p->basetime / 100.0; 200 | calculate_times(p); 201 | } 202 | } 203 | 204 | void pwm_start(unsigned int gpio) 205 | { 206 | struct pwm *p; 207 | 208 | if (((p = find_pwm(gpio)) == NULL) || p->running) 209 | return; 210 | 211 | p->running = 1; 212 | if (pthread_create(&threads, NULL, pwm_thread, (void *)p) != 0) 213 | { 214 | // btc fixme - error 215 | p->running = 0; 216 | return; 217 | } 218 | } 219 | 220 | void pwm_stop(unsigned int gpio) 221 | { 222 | struct pwm *p; 223 | 224 | if ((p = find_pwm(gpio)) != NULL) 225 | p->running = 0; 226 | } 227 | -------------------------------------------------------------------------------- /source/soft_pwm.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Ben Croston 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | /* Software PWM using threads */ 24 | 25 | void pwm_set_duty_cycle(unsigned int gpio, float dutycycle); 26 | void pwm_set_frequency(unsigned int gpio, float freq); 27 | void pwm_start(unsigned int gpio); 28 | void pwm_stop(unsigned int gpio); 29 | -------------------------------------------------------------------------------- /source/spi/spi.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * This file is part of pyA20. 4 | * spi.c is python SPI extension. 5 | * 6 | * Copyright (c) 2014 Stefan Mavrodiev @ OLIMEX LTD, 7 | * 8 | * pyA20 is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 | * MA 02110-1301, USA. 22 | */ 23 | 24 | #include "Python.h" 25 | 26 | #if PY_MAJOR_VERSION >= 3 27 | #define PyInt_FromLong PyLong_FromLong 28 | #define PyInt_AsLong PyLong_AsLong 29 | #endif 30 | 31 | 32 | #include "spi_lib.h" 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | int fd; 41 | 42 | #ifdef __DEBUG 43 | #define debug(format, args...) printf(format, args); 44 | #else 45 | #define debug(...) ((void)0) 46 | #endif 47 | 48 | 49 | /** 50 | * Read n bytes from slave device 51 | * 52 | * @param self 53 | * @param args number of bytes 54 | * @return tuple with read elements 55 | */ 56 | static PyObject* py_read(PyObject *self, PyObject* args){ 57 | 58 | PyObject *rx_list = 0; 59 | PyObject *item = 0; 60 | uint8_t rx_len = 0; 61 | 62 | /* Parse arguments */ 63 | if(!PyArg_ParseTuple(args, "i", &rx_len)){ 64 | return NULL; 65 | } 66 | 67 | /* Alocate memmory for buffer*/ 68 | uint8_t *rx_buffer = (uint8_t*)malloc(rx_len * sizeof(uint8_t)); 69 | memset(rx_buffer, 0, sizeof(uint8_t)*rx_len); 70 | 71 | /* Read from device*/ 72 | if(spi_read(fd, rx_buffer, rx_len) < 0){ 73 | return PyErr_SetFromErrno(PyExc_IOError); 74 | } 75 | 76 | /* Make list */ 77 | rx_list = PyList_New(rx_len); 78 | 79 | /* Populate list */ 80 | uint8_t i; 81 | for(i = 0; i < rx_len; i++){ 82 | item = PyInt_FromLong(rx_buffer[i]); 83 | PyList_SET_ITEM(rx_list, i, item); 84 | } 85 | 86 | /* Do cleanup*/ 87 | free(rx_buffer); 88 | //Py_DECREF(item); 89 | 90 | /* Return list */ 91 | return rx_list; 92 | } 93 | 94 | /** 95 | * Write bytes to slave device 96 | * 97 | * @param self 98 | * @param args Tuple with data to write 99 | * @return none 100 | */ 101 | static PyObject* py_write(PyObject *self, PyObject* args){ 102 | 103 | PyObject *tx_list; 104 | PyObject *item = 0; 105 | uint8_t tx_len = 0; 106 | 107 | /* Parse arguments */ 108 | if(!PyArg_ParseTuple(args, "O!", &PyList_Type, &tx_list)){ 109 | return NULL; 110 | } 111 | 112 | /* Get length of list */ 113 | tx_len = PyList_Size(tx_list); 114 | 115 | /* Allocate memory for output buffer */ 116 | uint8_t *tx_buffer = (uint8_t *)malloc(tx_len * sizeof(uint8_t)); 117 | memset(tx_buffer, 0, sizeof(uint8_t)); 118 | 119 | /* Populate output buffer */ 120 | int i; 121 | for(i = 0; i < tx_len; i++){ 122 | item = PyList_GetItem(tx_list, i); 123 | tx_buffer[i] = (uint8_t)PyInt_AsLong(item); 124 | #ifdef __DEBUG 125 | printf("tx[%d]=%02X\n", i, tx_buffer[i]); 126 | #endif 127 | } 128 | 129 | /* Send data */ 130 | uint8_t *ptr = tx_buffer; 131 | while (tx_len > 63) { 132 | if(spi_write(fd, ptr, 64) < 0){ 133 | return PyErr_SetFromErrno(PyExc_IOError); 134 | } 135 | ptr += 64; 136 | tx_len -= 64; 137 | } 138 | if (tx_len > 0) { 139 | if(spi_write(fd, ptr, tx_len) < 0){ 140 | return PyErr_SetFromErrno(PyExc_IOError); 141 | } 142 | } 143 | 144 | /* Do cleanup */ 145 | free(tx_buffer); 146 | 147 | //Py_DECREF(item); 148 | // Py_DECREF(tx_list); 149 | 150 | Py_RETURN_NONE; 151 | } 152 | 153 | 154 | /** 155 | * Do transfer of data to slave device 156 | * 157 | * @param self 158 | * @param args tuple of data to read and 159 | * @return list with read data 160 | */ 161 | static PyObject* py_xfer(PyObject* self, PyObject* args){ 162 | 163 | PyObject *tx_list = 0; 164 | PyObject *rx_list = 0; 165 | PyObject *item = 0; 166 | 167 | uint tx_len = 0; 168 | uint rx_len = 0; 169 | 170 | /* Parse arguments */ 171 | if(!PyArg_ParseTuple(args, "O!i", &PyList_Type, &tx_list, &rx_len)){ 172 | return NULL; 173 | } 174 | 175 | /* Get length of output data */ 176 | tx_len = PyList_Size(tx_list); 177 | 178 | /* Allocate memory for sending */ 179 | uint8_t* tx_buffer; 180 | uint8_t* rx_buffer; 181 | 182 | tx_buffer = (uint8_t *)malloc(tx_len * sizeof(uint8_t)); 183 | rx_buffer = (uint8_t *)malloc(rx_len * sizeof(uint8_t)); 184 | 185 | memset(rx_buffer, 0, sizeof(uint8_t)*rx_len); 186 | 187 | /* Populate both output buffer */ 188 | int i; 189 | for(i = 0; i < tx_len; i++){ 190 | item = PyList_GetItem(tx_list, i); 191 | tx_buffer[i] = (uint8_t)PyInt_AsLong(item); 192 | } 193 | 194 | /* Do the transaction */ 195 | if(spi_xfer(fd, tx_buffer, tx_len, rx_buffer, rx_len) < 0){ 196 | return PyErr_SetFromErrno(PyExc_IOError); 197 | } 198 | 199 | /* Make new list*/ 200 | rx_list = PyList_New(rx_len); 201 | 202 | /* Populate the new list */ 203 | for(i = 0; i < rx_len; i++){ 204 | item = PyInt_FromLong(rx_buffer[i]); 205 | PyList_SET_ITEM(rx_list, i, item); 206 | } 207 | 208 | /* Do cleanup */ 209 | free(tx_buffer); 210 | free(rx_buffer); 211 | 212 | // Py_DECREF(tx_list); 213 | // Py_DECREF(item); 214 | 215 | return rx_list; 216 | } 217 | 218 | /** 219 | * Open SPI device with given configuration 220 | * 221 | * @param self 222 | * @param args 223 | * @param kwargs 224 | * @return none 225 | */ 226 | static PyObject* py_open(PyObject* self, PyObject* args, PyObject* kwargs){ 227 | 228 | int ret; 229 | char *device; 230 | spi_config_t config = {0}; 231 | 232 | /* Set default values */ 233 | config.mode = 0; 234 | config.bits_per_word = 8; 235 | config.speed = 100000; 236 | config.delay = 0; 237 | 238 | /* Define keywords */ 239 | static char *kwlist [] = { 240 | "device", "mode", "bits_per_word", "speed", "delay", NULL 241 | }; 242 | 243 | /* Parse arguments */ 244 | if(!PyArg_ParseTupleAndKeywords( 245 | args, kwargs, "s|iiii", kwlist, 246 | &device, 247 | &config.mode, 248 | &config.bits_per_word, 249 | &config.speed, 250 | &config.delay)){ 251 | return NULL; 252 | } 253 | 254 | /* Open the device */ 255 | ret = spi_open(device, config); 256 | 257 | if(ret < 0 ){ 258 | return PyErr_SetFromErrno(PyExc_IOError); 259 | }else{ 260 | fd = ret; 261 | } 262 | 263 | Py_RETURN_NONE; 264 | } 265 | 266 | /** 267 | * Close file descriptor for SPI bus 268 | * 269 | * @param self 270 | * @param args 271 | * @return none 272 | */ 273 | static PyObject* py_close(PyObject* self, PyObject* args){ 274 | 275 | int ret; 276 | 277 | ret = spi_close(fd); 278 | if(ret < 0){ 279 | return PyErr_SetFromErrno(PyExc_IOError); 280 | }else{ 281 | fd = 0; 282 | } 283 | 284 | Py_RETURN_NONE; 285 | } 286 | PyMethodDef module_methods[] = { 287 | {"open", (PyCFunction)py_open, METH_VARARGS | METH_KEYWORDS, "Open file dssescriptor"}, 288 | {"xfer", py_xfer, METH_VARARGS, "Transfer data"}, 289 | {"write", py_write, METH_VARARGS, "Write data"}, 290 | {"read", py_read, METH_VARARGS, "Read data"}, 291 | {"close", py_close, METH_NOARGS, "Close file descriptor"}, 292 | {NULL, NULL, 0, NULL} 293 | }; 294 | 295 | #if PY_MAJOR_VERSION >= 3 296 | static struct PyModuleDef module_def = { 297 | PyModuleDef_HEAD_INIT, 298 | "RPi._SPI", 299 | NULL, 300 | -1, 301 | module_methods 302 | }; 303 | #endif 304 | 305 | PyMODINIT_FUNC 306 | #if PY_MAJOR_VERSION >= 3 307 | PyInit__SPI(void) { 308 | #else 309 | init_SPI(void) { 310 | #endif 311 | #pragma GCC diagnostic ignored "-Wunused-but-set-variable" 312 | PyObject* module = NULL; 313 | 314 | 315 | #if PY_MAJOR_VERSION >= 3 316 | module = PyModule_Create(&module_def); 317 | #else 318 | module = Py_InitModule("RPi._SPI", module_methods); 319 | #endif 320 | 321 | 322 | #if PY_MAJOR_VERSION >= 3 323 | return module; 324 | #else 325 | return; 326 | #endif 327 | 328 | } 329 | -------------------------------------------------------------------------------- /source/spi/spi_lib.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * This file is part of pyA20. 4 | * sppi_lib.c is python SPI extension. 5 | * 6 | * Copyright (c) 2014 Stefan Mavrodiev @ OLIMEX LTD, 7 | * 8 | * pyA20 is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 | * MA 02110-1301, USA. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | 35 | #include "spi_lib.h" 36 | 37 | int spi_open(char *device, spi_config_t config) { 38 | int fd; 39 | 40 | /* Open block device */ 41 | fd = open(device, O_RDWR); 42 | if (fd < 0) { 43 | return fd; 44 | } 45 | 46 | /* Set SPI_POL and SPI_PHA */ 47 | if (ioctl(fd, SPI_IOC_WR_MODE, &config.mode) < 0) { 48 | return -1; 49 | } 50 | if (ioctl(fd, SPI_IOC_RD_MODE, &config.mode) < 0) { 51 | return -1; 52 | } 53 | 54 | /* Set bits per word*/ 55 | if (ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &config.bits_per_word) < 0) { 56 | return -1; 57 | } 58 | if (ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &config.bits_per_word) < 0) { 59 | return -1; 60 | } 61 | 62 | /* Set SPI speed*/ 63 | if (ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &config.speed) < 0) { 64 | return -1; 65 | } 66 | if (ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &config.speed) < 0) { 67 | return -1; 68 | } 69 | 70 | /* Return file descriptor */ 71 | return fd; 72 | } 73 | 74 | int spi_close(int fd) { 75 | return close(fd); 76 | } 77 | 78 | int spi_xfer(int fd, uint8_t *tx_buffer, uint8_t tx_len, uint8_t *rx_buffer, uint8_t rx_len){ 79 | struct spi_ioc_transfer spi_message[1]; 80 | memset(spi_message, 0, sizeof(spi_message)); 81 | 82 | spi_message[0].rx_buf = (unsigned long)rx_buffer; 83 | spi_message[0].tx_buf = (unsigned long)tx_buffer; 84 | spi_message[0].len = tx_len; 85 | 86 | return ioctl(fd, SPI_IOC_MESSAGE(1), spi_message); 87 | } 88 | 89 | int spi_read(int fd, uint8_t *rx_buffer, uint8_t rx_len){ 90 | struct spi_ioc_transfer spi_message[1]; 91 | memset(spi_message, 0, sizeof(spi_message)); 92 | 93 | spi_message[0].rx_buf = (unsigned long)rx_buffer; 94 | spi_message[0].len = rx_len; 95 | 96 | return ioctl(fd, SPI_IOC_MESSAGE(1), spi_message); 97 | } 98 | 99 | int spi_write(int fd, uint8_t *tx_buffer, uint8_t tx_len){ 100 | struct spi_ioc_transfer spi_message[1]; 101 | memset(spi_message, 0, sizeof(spi_message)); 102 | 103 | spi_message[0].tx_buf = (unsigned long)tx_buffer; 104 | spi_message[0].len = tx_len; 105 | 106 | return ioctl(fd, SPI_IOC_MESSAGE(1), spi_message); 107 | } 108 | -------------------------------------------------------------------------------- /source/spi/spi_lib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * This file is part of pyA20. 4 | * spi_lib.c is python SPI extension. 5 | * 6 | * Copyright (c) 2014 Stefan Mavrodiev @ OLIMEX LTD, 7 | * 8 | * pyA20 is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 | * MA 02110-1301, USA. 22 | */ 23 | 24 | #ifndef _I2C_LIB_H 25 | #define _I2C_LIB_H 26 | 27 | #include 28 | 29 | typedef struct { 30 | uint8_t mode; 31 | uint8_t bits_per_word; 32 | uint32_t speed; 33 | uint16_t delay; 34 | } spi_config_t; 35 | 36 | extern int spi_open(char *device, spi_config_t config); 37 | extern int spi_close(int fd); 38 | extern int spi_xfer(int fd, uint8_t *tx_buffer, uint8_t tx_len, uint8_t *rx_buffer, uint8_t rx_len); 39 | extern int spi_read(int fd, uint8_t *rx_buffer, uint8_t rx_len); 40 | extern int spi_write(int fd, uint8_t *tx_buffer, uint8_t tx_len); 41 | 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /test/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | Copyright (c) 2013-2016 Ben Croston 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | """ 23 | 24 | """This test suite assumes the following circuit is connected: 25 | GND_PIN = 6 26 | LED_PIN = 12 (with resistor to 0v) 27 | SWITCH_PIN = 18 (with 0.1 uF capacitor around switch) to 0v 28 | LOOP_IN = 16 connected with 1K resistor to LOOP_OUT 29 | LOOP_OUT = 22 30 | """ 31 | 32 | import sys 33 | import warnings 34 | import time 35 | from threading import Timer 36 | import RPi.GPIO as GPIO 37 | if sys.version[:3] == '2.6': 38 | import unittest2 as unittest 39 | else: 40 | import unittest 41 | 42 | GND_PIN = 6 43 | LED_PIN = 12 44 | LED_PIN_BCM = 18 45 | SWITCH_PIN = 18 46 | LOOP_IN = 16 47 | LOOP_OUT = 22 48 | 49 | non_interactive = False 50 | for i,val in enumerate(sys.argv): 51 | if val == '--non_interactive': 52 | non_interactive = True 53 | sys.argv.pop(i) 54 | 55 | # Test starts with 'AAA' so that it is run first 56 | class TestAAASetup(unittest.TestCase): 57 | def runTest(self): 58 | # Test mode not set (BOARD or BCM) exception 59 | with self.assertRaises(RuntimeError) as e: 60 | GPIO.setup(LED_PIN, GPIO.OUT) 61 | self.assertEqual(str(e.exception), 'Please set pin numbering mode using GPIO.setmode(GPIO.BOARD) or GPIO.setmode(GPIO.BCM)') 62 | 63 | # Test trying to change mode after it has been set 64 | GPIO.setmode(GPIO.BCM) 65 | with self.assertRaises(ValueError) as e: 66 | GPIO.setmode(GPIO.BOARD) 67 | GPIO.setup(LED_PIN_BCM, GPIO.IN) 68 | GPIO.cleanup() 69 | 70 | # Test setting an invalid mode 71 | with self.assertRaises(ValueError): 72 | GPIO.setmode(666) 73 | 74 | # Test getmode() 75 | self.assertEqual(GPIO.getmode(), None) 76 | GPIO.setmode(GPIO.BCM) 77 | self.assertEqual(GPIO.getmode(), GPIO.BCM) 78 | GPIO.setup(LED_PIN_BCM, GPIO.IN) 79 | GPIO.cleanup() 80 | GPIO.setmode(GPIO.BOARD) 81 | self.assertEqual(GPIO.getmode(), GPIO.BOARD) 82 | 83 | # Test not set as OUTPUT message 84 | GPIO.setmode(GPIO.BOARD) 85 | with self.assertRaises(RuntimeError) as e: 86 | GPIO.output(LED_PIN, GPIO.HIGH) 87 | self.assertEqual(str(e.exception), 'The GPIO channel has not been set up as an OUTPUT') 88 | 89 | # Test setup(..., pull_up_down=GPIO.HIGH) raises exception 90 | GPIO.setmode(GPIO.BOARD) 91 | with self.assertRaises(ValueError): 92 | GPIO.setup(LED_PIN, GPIO.IN, pull_up_down=GPIO.HIGH) 93 | 94 | # Test not valid on a raspi exception 95 | GPIO.setmode(GPIO.BOARD) 96 | with self.assertRaises(ValueError) as e: 97 | GPIO.setup(GND_PIN, GPIO.OUT) 98 | self.assertEqual(str(e.exception), 'The channel sent is invalid on a Raspberry Pi') 99 | 100 | # Test 'already in use' warning 101 | GPIO.setmode(GPIO.BOARD) 102 | with open('/sys/class/gpio/export','wb') as f: 103 | f.write(str(LED_PIN_BCM).encode()) 104 | time.sleep(0.2) # wait for udev to set permissions 105 | with open('/sys/class/gpio/gpio%s/direction'%LED_PIN_BCM,'wb') as f: 106 | f.write(b'out') 107 | time.sleep(0.2) 108 | with warnings.catch_warnings(record=True) as w: 109 | GPIO.setup(LED_PIN, GPIO.OUT) # generate 'already in use' warning 110 | self.assertEqual(w[0].category, RuntimeWarning) 111 | with open('/sys/class/gpio/unexport','wb') as f: 112 | f.write(str(LED_PIN_BCM).encode()) 113 | GPIO.cleanup() 114 | 115 | # test initial value of high reads back as high 116 | GPIO.setmode(GPIO.BOARD) 117 | GPIO.setup(LED_PIN, GPIO.OUT, initial=GPIO.HIGH) 118 | self.assertEqual(GPIO.input(LED_PIN), GPIO.HIGH) 119 | GPIO.cleanup() 120 | 121 | # test initial value of low reads back as low 122 | GPIO.setmode(GPIO.BOARD) 123 | GPIO.setup(LED_PIN, GPIO.OUT, initial=GPIO.LOW) 124 | self.assertEqual(GPIO.input(LED_PIN), GPIO.LOW) 125 | GPIO.cleanup() 126 | 127 | # test setup of a list of channels 128 | GPIO.setmode(GPIO.BOARD) 129 | GPIO.setup( [LED_PIN, LOOP_OUT], GPIO.OUT) 130 | self.assertEqual(GPIO.gpio_function(LED_PIN), GPIO.OUT) 131 | self.assertEqual(GPIO.gpio_function(LOOP_OUT), GPIO.OUT) 132 | GPIO.cleanup() 133 | GPIO.setmode(GPIO.BOARD) 134 | with self.assertRaises(ValueError) as e: 135 | GPIO.setup( [LED_PIN, GND_PIN], GPIO.OUT) 136 | self.assertEqual(GPIO.gpio_function(LED_PIN), GPIO.OUT) 137 | self.assertEqual(str(e.exception), 'The channel sent is invalid on a Raspberry Pi') 138 | GPIO.cleanup() 139 | 140 | # test setup of a tuple of channels 141 | GPIO.setmode(GPIO.BOARD) 142 | GPIO.setup( (LED_PIN, LOOP_OUT), GPIO.OUT) 143 | self.assertEqual(GPIO.gpio_function(LED_PIN), GPIO.OUT) 144 | self.assertEqual(GPIO.gpio_function(LOOP_OUT), GPIO.OUT) 145 | GPIO.cleanup() 146 | 147 | # test warning when using pull up/down on i2c channels 148 | GPIO.setmode(GPIO.BOARD) 149 | if GPIO.RPI_INFO['P1_REVISION'] == 0: # compute module 150 | pass # test not vailid 151 | else: # revision 1, 2 or A+/B+ 152 | with warnings.catch_warnings(record=True) as w: 153 | GPIO.setup(3, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) 154 | self.assertEqual(w[0].category, RuntimeWarning) 155 | with warnings.catch_warnings(record=True) as w: 156 | GPIO.setup(5, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) 157 | self.assertEqual(w[0].category, RuntimeWarning) 158 | GPIO.cleanup() 159 | 160 | # test non integer channel 161 | GPIO.setmode(GPIO.BOARD) 162 | with self.assertRaises(ValueError): 163 | GPIO.setup('d', GPIO.OUT) 164 | with self.assertRaises(ValueError): 165 | GPIO.setup(('d',LED_PIN), GPIO.OUT) 166 | 167 | # test setting pull_up_down on an output 168 | GPIO.setmode(GPIO.BOARD) 169 | with self.assertRaises(ValueError): 170 | GPIO.setup(LOOP_OUT, GPIO.OUT, pull_up_down=GPIO.PUD_DOWN) 171 | 172 | # test setting initial on an input 173 | GPIO.setmode(GPIO.BOARD) 174 | with self.assertRaises(ValueError): 175 | GPIO.setup(LOOP_IN, GPIO.IN, initial=GPIO.LOW) 176 | 177 | class TestInputOutput(unittest.TestCase): 178 | def setUp(self): 179 | GPIO.setmode(GPIO.BOARD) 180 | 181 | def test_outputread(self): 182 | """Test that an output() can be input()""" 183 | GPIO.setup(LED_PIN, GPIO.OUT) 184 | GPIO.output(LED_PIN, GPIO.HIGH) 185 | self.assertEqual(GPIO.input(LED_PIN), GPIO.HIGH) 186 | GPIO.output(LED_PIN, GPIO.LOW) 187 | self.assertEqual(GPIO.input(LED_PIN), GPIO.LOW) 188 | 189 | def test_loopback(self): 190 | """Test output loops back to another input""" 191 | GPIO.setup(LOOP_IN, GPIO.IN, pull_up_down=GPIO.PUD_OFF) 192 | GPIO.setup(LOOP_OUT, GPIO.OUT, initial=GPIO.LOW) 193 | self.assertEqual(GPIO.input(LOOP_IN), GPIO.LOW) 194 | GPIO.output(LOOP_OUT, GPIO.HIGH) 195 | self.assertEqual(GPIO.input(LOOP_IN), GPIO.HIGH) 196 | 197 | def test_output_on_input(self): 198 | """Test output() can not be done on input""" 199 | GPIO.setup(SWITCH_PIN, GPIO.IN) 200 | with self.assertRaises(RuntimeError): 201 | GPIO.output(SWITCH_PIN, GPIO.LOW) 202 | 203 | def test_output_list(self): 204 | """Test output() using lists""" 205 | GPIO.setup(LOOP_OUT, GPIO.OUT) 206 | GPIO.setup(LED_PIN, GPIO.OUT) 207 | 208 | GPIO.output( [LOOP_OUT, LED_PIN], GPIO.HIGH) 209 | self.assertEqual(GPIO.input(LOOP_OUT), GPIO.HIGH) 210 | self.assertEqual(GPIO.input(LED_PIN), GPIO.HIGH) 211 | 212 | GPIO.output( (LOOP_OUT, LED_PIN), GPIO.LOW) 213 | self.assertEqual(GPIO.input(LOOP_OUT), GPIO.LOW) 214 | self.assertEqual(GPIO.input(LED_PIN), GPIO.LOW) 215 | 216 | GPIO.output( [LOOP_OUT, LED_PIN], [GPIO.HIGH, GPIO.LOW] ) 217 | self.assertEqual(GPIO.input(LOOP_OUT), GPIO.HIGH) 218 | self.assertEqual(GPIO.input(LED_PIN), GPIO.LOW) 219 | 220 | GPIO.output( (LOOP_OUT, LED_PIN), (GPIO.LOW, GPIO.HIGH) ) 221 | self.assertEqual(GPIO.input(LOOP_OUT), GPIO.LOW) 222 | self.assertEqual(GPIO.input(LED_PIN), GPIO.HIGH) 223 | 224 | with self.assertRaises(RuntimeError): 225 | GPIO.output( [LOOP_OUT, LED_PIN], [0,0,0] ) 226 | 227 | with self.assertRaises(RuntimeError): 228 | GPIO.output( [LOOP_OUT, LED_PIN], (0,) ) 229 | 230 | with self.assertRaises(RuntimeError): 231 | GPIO.output(LOOP_OUT, (0,0)) 232 | 233 | with self.assertRaises(ValueError): 234 | GPIO.output( [LOOP_OUT, 'x'], (0,0) ) 235 | 236 | with self.assertRaises(ValueError): 237 | GPIO.output( [LOOP_OUT, LED_PIN], (0,'x') ) 238 | 239 | with self.assertRaises(ValueError): 240 | GPIO.output( [LOOP_OUT, GND_PIN], (0,0) ) 241 | 242 | with self.assertRaises(RuntimeError): 243 | GPIO.output( [LOOP_OUT, LOOP_IN], (0,0) ) 244 | 245 | def tearDown(self): 246 | GPIO.cleanup() 247 | 248 | class TestSoftPWM(unittest.TestCase): 249 | @unittest.skipIf(non_interactive, 'Non interactive mode') 250 | def runTest(self): 251 | GPIO.setmode(GPIO.BOARD) 252 | GPIO.setup(LED_PIN, GPIO.OUT) 253 | pwm = GPIO.PWM(LED_PIN, 50) 254 | pwm.start(100) 255 | print "\nPWM tests" 256 | response = raw_input('Is the LED on (y/n) ? ').upper() 257 | self.assertEqual(response,'Y') 258 | pwm.start(0) 259 | response = raw_input('Is the LED off (y/n) ? ').upper() 260 | self.assertEqual(response,'Y') 261 | print "LED Brighten/fade test..." 262 | for i in range(0,3): 263 | for x in range(0,101,5): 264 | pwm.ChangeDutyCycle(x) 265 | time.sleep(0.1) 266 | for x in range(100,-1,-5): 267 | pwm.ChangeDutyCycle(x) 268 | time.sleep(0.1) 269 | pwm.stop() 270 | response = raw_input('Did it work (y/n) ? ').upper() 271 | self.assertEqual(response,'Y') 272 | GPIO.cleanup() 273 | 274 | class TestSetWarnings(unittest.TestCase): 275 | def test_alreadyinuse(self): 276 | """Test 'already in use' warning""" 277 | GPIO.setmode(GPIO.BOARD) 278 | GPIO.setwarnings(False) 279 | with open('/sys/class/gpio/export','wb') as f: 280 | f.write(str(LED_PIN_BCM).encode()) 281 | time.sleep(0.2) # wait for udev to set permissions 282 | with open('/sys/class/gpio/gpio%s/direction'%LED_PIN_BCM,'wb') as f: 283 | f.write(b'out') 284 | with warnings.catch_warnings(record=True) as w: 285 | GPIO.setup(LED_PIN, GPIO.OUT) # generate 'already in use' warning 286 | self.assertEqual(len(w),0) # should be no warnings 287 | with open('/sys/class/gpio/unexport','wb') as f: 288 | f.write(str(LED_PIN_BCM).encode()) 289 | GPIO.cleanup() 290 | 291 | GPIO.setmode(GPIO.BOARD) 292 | GPIO.setwarnings(True) 293 | with open('/sys/class/gpio/export','wb') as f: 294 | f.write(str(LED_PIN_BCM).encode()) 295 | time.sleep(0.2) # wait for udev to set permissions 296 | with open('/sys/class/gpio/gpio%s/direction'%LED_PIN_BCM,'wb') as f: 297 | f.write(b'out') 298 | with warnings.catch_warnings(record=True) as w: 299 | GPIO.setup(LED_PIN, GPIO.OUT) # generate 'already in use' warning 300 | self.assertEqual(w[0].category, RuntimeWarning) 301 | with open('/sys/class/gpio/unexport','wb') as f: 302 | f.write(str(LED_PIN_BCM).encode()) 303 | GPIO.cleanup() 304 | 305 | def test_cleanupwarning(self): 306 | """Test initial GPIO.cleanup() produces warning""" 307 | GPIO.setwarnings(False) 308 | GPIO.setmode(GPIO.BOARD) 309 | GPIO.setup(SWITCH_PIN, GPIO.IN) 310 | with warnings.catch_warnings(record=True) as w: 311 | GPIO.cleanup() 312 | self.assertEqual(len(w),0) # no warnings 313 | GPIO.cleanup() 314 | self.assertEqual(len(w),0) # no warnings 315 | 316 | GPIO.setwarnings(True) 317 | GPIO.setmode(GPIO.BOARD) 318 | GPIO.setup(SWITCH_PIN, GPIO.IN) 319 | with warnings.catch_warnings(record=True) as w: 320 | GPIO.cleanup() 321 | self.assertEqual(len(w),0) # no warnings 322 | GPIO.cleanup() 323 | self.assertEqual(w[0].category, RuntimeWarning) # a warning 324 | 325 | class TestVersions(unittest.TestCase): 326 | def test_rpi_info(self): 327 | print 'RPi Board Information' 328 | print '---------------------' 329 | for key,val in GPIO.RPI_INFO.items(): 330 | print '%s => %s'%(key,val) 331 | response = raw_input('\nIs this board info correct (y/n) ? ').upper() 332 | self.assertEqual(response, 'Y') 333 | 334 | def test_gpio_version(self): 335 | response = raw_input('\nRPi.GPIO version %s - is this correct (y/n) ? '%GPIO.VERSION).upper() 336 | self.assertEqual(response, 'Y') 337 | 338 | class TestGPIOFunction(unittest.TestCase): 339 | def runTest(self): 340 | GPIO.setmode(GPIO.BCM) 341 | GPIO.setup(LED_PIN_BCM, GPIO.IN) 342 | self.assertEqual(GPIO.gpio_function(LED_PIN_BCM), GPIO.IN) 343 | GPIO.setup(LED_PIN_BCM, GPIO.OUT) 344 | self.assertEqual(GPIO.gpio_function(LED_PIN_BCM), GPIO.OUT) 345 | GPIO.cleanup() 346 | 347 | GPIO.setmode(GPIO.BOARD) 348 | GPIO.setup(LED_PIN, GPIO.IN) 349 | self.assertEqual(GPIO.gpio_function(LED_PIN), GPIO.IN) 350 | GPIO.setup(LED_PIN, GPIO.OUT) 351 | self.assertEqual(GPIO.gpio_function(LED_PIN), GPIO.OUT) 352 | 353 | def tearDown(self): 354 | GPIO.cleanup() 355 | 356 | class TestSwitchBounce(unittest.TestCase): 357 | def __init__(self, *a, **k): 358 | unittest.TestCase.__init__(self, *a, **k) 359 | self.switchcount = 0 360 | 361 | def cb(self,chan): 362 | self.switchcount += 1 363 | print 'Button press',self.switchcount 364 | 365 | def setUp(self): 366 | GPIO.setmode(GPIO.BOARD) 367 | GPIO.setup(SWITCH_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) 368 | 369 | @unittest.skipIf(non_interactive, 'Non interactive mode') 370 | def test_switchbounce(self): 371 | self.switchcount = 0 372 | print "\nSwitch bounce test. Press switch at least 10 times and count..." 373 | GPIO.add_event_detect(SWITCH_PIN, GPIO.FALLING, callback=self.cb, bouncetime=200) 374 | while self.switchcount < 10: 375 | time.sleep(1) 376 | GPIO.remove_event_detect(SWITCH_PIN) 377 | 378 | @unittest.skipIf(non_interactive, 'Non interactive mode') 379 | def test_event_detected(self): 380 | self.switchcount = 0 381 | print "\nGPIO.event_detected() switch bounce test. Press switch at least 10 times and count..." 382 | GPIO.add_event_detect(SWITCH_PIN, GPIO.FALLING, bouncetime=200) 383 | while self.switchcount < 10: 384 | if GPIO.event_detected(SWITCH_PIN): 385 | self.switchcount += 1 386 | print 'Button press',self.switchcount 387 | GPIO.remove_event_detect(SWITCH_PIN) 388 | 389 | def tearDown(self): 390 | GPIO.cleanup() 391 | 392 | class TestEdgeDetection(unittest.TestCase): 393 | def setUp(self): 394 | GPIO.setmode(GPIO.BOARD) 395 | GPIO.setup(LOOP_IN, GPIO.IN) 396 | GPIO.setup(LOOP_OUT, GPIO.OUT) 397 | 398 | def testWaitForEdgeInLoop(self): 399 | def makelow(): 400 | GPIO.output(LOOP_OUT, GPIO.LOW) 401 | 402 | count = 0 403 | timestart = time.time() 404 | GPIO.output(LOOP_OUT, GPIO.HIGH) 405 | while True: 406 | t = Timer(0.1, makelow) 407 | t.start() 408 | GPIO.wait_for_edge(LOOP_IN, GPIO.FALLING) 409 | GPIO.output(LOOP_OUT, GPIO.HIGH) 410 | count += 1 411 | if time.time() - timestart > 5 or count > 150: 412 | break 413 | 414 | def testWaitForEdgeWithCallback(self): 415 | def cb(): 416 | raise Exception("Callback should not be called") 417 | def makehigh(): 418 | GPIO.output(LOOP_OUT, GPIO.HIGH) 419 | 420 | GPIO.output(LOOP_OUT, GPIO.LOW) 421 | t = Timer(0.1, makehigh) 422 | 423 | GPIO.add_event_detect(LOOP_IN, GPIO.RISING) 424 | t.start() 425 | GPIO.wait_for_edge(LOOP_IN, GPIO.RISING) 426 | 427 | GPIO.output(LOOP_OUT, GPIO.LOW) 428 | GPIO.add_event_callback(LOOP_IN, callback=cb) 429 | with self.assertRaises(RuntimeError): # conflicting edge exception 430 | GPIO.wait_for_edge(LOOP_IN, GPIO.RISING) 431 | 432 | GPIO.remove_event_detect(LOOP_IN) 433 | 434 | def testWaitForEventSwitchbounce(self): 435 | self.finished = False 436 | def bounce(): 437 | GPIO.output(LOOP_OUT, GPIO.HIGH) 438 | time.sleep(0.01) 439 | GPIO.output(LOOP_OUT, GPIO.LOW) 440 | time.sleep(0.01) 441 | GPIO.output(LOOP_OUT, GPIO.HIGH) 442 | time.sleep(0.01) 443 | GPIO.output(LOOP_OUT, GPIO.LOW) 444 | time.sleep(0.2) 445 | GPIO.output(LOOP_OUT, GPIO.HIGH) 446 | time.sleep(0.01) 447 | GPIO.output(LOOP_OUT, GPIO.LOW) 448 | time.sleep(0.01) 449 | GPIO.output(LOOP_OUT, GPIO.HIGH) 450 | time.sleep(0.01) 451 | GPIO.output(LOOP_OUT, GPIO.LOW) 452 | self.finished = True 453 | 454 | GPIO.output(LOOP_OUT, GPIO.LOW) 455 | t1 = Timer(0.1, bounce) 456 | t1.start() 457 | 458 | starttime = time.time() 459 | GPIO.wait_for_edge(LOOP_IN, GPIO.RISING, bouncetime=100) 460 | GPIO.wait_for_edge(LOOP_IN, GPIO.RISING, bouncetime=100) 461 | finishtime = time.time() 462 | self.assertGreater(finishtime-starttime, 0.2) 463 | while not self.finished: 464 | time.sleep(0.1) 465 | 466 | def testInvalidBouncetime(self): 467 | with self.assertRaises(ValueError): 468 | GPIO.add_event_detect(LOOP_IN, GPIO.RISING, bouncetime=-1) 469 | with self.assertRaises(ValueError): 470 | GPIO.wait_for_edge(LOOP_IN, GPIO.RISING, bouncetime=-1) 471 | GPIO.add_event_detect(LOOP_IN, GPIO.RISING, bouncetime=123) 472 | with self.assertRaises(RuntimeError): 473 | GPIO.wait_for_edge(LOOP_IN, GPIO.RISING, bouncetime=321) 474 | GPIO.remove_event_detect(LOOP_IN) 475 | 476 | def testAlreadyAdded(self): 477 | GPIO.add_event_detect(LOOP_IN, GPIO.RISING) 478 | with self.assertRaises(RuntimeError): 479 | GPIO.add_event_detect(LOOP_IN, GPIO.RISING) 480 | GPIO.remove_event_detect(LOOP_IN) 481 | 482 | def testHighLowEvent(self): 483 | with self.assertRaises(ValueError): 484 | GPIO.add_event_detect(LOOP_IN, GPIO.LOW) 485 | with self.assertRaises(ValueError): 486 | GPIO.add_event_detect(LOOP_IN, GPIO.HIGH) 487 | 488 | def testFallingEventDetected(self): 489 | GPIO.output(LOOP_OUT, GPIO.HIGH) 490 | GPIO.add_event_detect(LOOP_IN, GPIO.FALLING) 491 | time.sleep(0.01) 492 | self.assertEqual(GPIO.event_detected(LOOP_IN), False) 493 | GPIO.output(LOOP_OUT, GPIO.LOW) 494 | time.sleep(0.01) 495 | self.assertEqual(GPIO.event_detected(LOOP_IN), True) 496 | GPIO.output(LOOP_OUT, GPIO.HIGH) 497 | time.sleep(0.01) 498 | self.assertEqual(GPIO.event_detected(LOOP_IN), False) 499 | GPIO.remove_event_detect(LOOP_IN) 500 | 501 | def testRisingEventDetected(self): 502 | GPIO.output(LOOP_OUT, GPIO.LOW) 503 | GPIO.add_event_detect(LOOP_IN, GPIO.RISING) 504 | time.sleep(0.01) 505 | self.assertEqual(GPIO.event_detected(LOOP_IN), False) 506 | GPIO.output(LOOP_OUT, GPIO.HIGH) 507 | time.sleep(0.01) 508 | self.assertEqual(GPIO.event_detected(LOOP_IN), True) 509 | GPIO.output(LOOP_OUT, GPIO.LOW) 510 | time.sleep(0.01) 511 | self.assertEqual(GPIO.event_detected(LOOP_IN), False) 512 | GPIO.remove_event_detect(LOOP_IN) 513 | 514 | def testBothEventDetected(self): 515 | GPIO.output(LOOP_OUT, GPIO.LOW) 516 | GPIO.add_event_detect(LOOP_IN, GPIO.BOTH) 517 | time.sleep(0.01) 518 | self.assertEqual(GPIO.event_detected(LOOP_IN), False) 519 | GPIO.output(LOOP_OUT, GPIO.HIGH) 520 | time.sleep(0.01) 521 | self.assertEqual(GPIO.event_detected(LOOP_IN), True) 522 | self.assertEqual(GPIO.event_detected(LOOP_IN), False) 523 | GPIO.output(LOOP_OUT, GPIO.LOW) 524 | time.sleep(0.01) 525 | self.assertEqual(GPIO.event_detected(LOOP_IN), True) 526 | GPIO.remove_event_detect(LOOP_IN) 527 | 528 | def testWaitForRising(self): 529 | def makehigh(): 530 | GPIO.output(LOOP_OUT, GPIO.HIGH) 531 | GPIO.output(LOOP_OUT, GPIO.LOW) 532 | t = Timer(0.1, makehigh) 533 | t.start() 534 | GPIO.wait_for_edge(LOOP_IN, GPIO.RISING) 535 | 536 | def testWaitForFalling(self): 537 | def makelow(): 538 | GPIO.output(LOOP_OUT, GPIO.LOW) 539 | GPIO.output(LOOP_OUT, GPIO.HIGH) 540 | t = Timer(0.1, makelow) 541 | t.start() 542 | GPIO.wait_for_edge(LOOP_IN, GPIO.FALLING) 543 | 544 | def testExceptionInCallback(self): 545 | self.run_cb = False 546 | def cb(channel): 547 | with self.assertRaises(ZeroDivisionError): 548 | self.run_cb = True 549 | a = 1/0 550 | GPIO.output(LOOP_OUT, GPIO.LOW) 551 | GPIO.add_event_detect(LOOP_IN, GPIO.RISING, callback=cb) 552 | time.sleep(0.01) 553 | GPIO.output(LOOP_OUT, GPIO.HIGH) 554 | time.sleep(0.01) 555 | self.assertEqual(self.run_cb, True) 556 | GPIO.remove_event_detect(LOOP_IN) 557 | 558 | def testAddEventCallback(self): 559 | def cb(channel): 560 | self.callback_count += 1 561 | 562 | # falling test 563 | self.callback_count = 0 564 | GPIO.output(LOOP_OUT, GPIO.HIGH) 565 | GPIO.add_event_detect(LOOP_IN, GPIO.FALLING) 566 | GPIO.add_event_callback(LOOP_IN, cb) 567 | time.sleep(0.01) 568 | for i in range(2048): 569 | GPIO.output(LOOP_OUT, GPIO.LOW) 570 | time.sleep(0.001) 571 | GPIO.output(LOOP_OUT, GPIO.HIGH) 572 | time.sleep(0.001) 573 | GPIO.remove_event_detect(LOOP_IN) 574 | self.assertEqual(self.callback_count, 2048) 575 | 576 | # rising test 577 | self.callback_count = 0 578 | GPIO.output(LOOP_OUT, GPIO.LOW) 579 | GPIO.add_event_detect(LOOP_IN, GPIO.RISING, callback=cb) 580 | time.sleep(0.01) 581 | for i in range(2048): 582 | GPIO.output(LOOP_OUT, GPIO.HIGH) 583 | time.sleep(0.001) 584 | GPIO.output(LOOP_OUT, GPIO.LOW) 585 | time.sleep(0.001) 586 | GPIO.remove_event_detect(LOOP_IN) 587 | self.assertEqual(self.callback_count, 2048) 588 | 589 | # both test 590 | self.callback_count = 0 591 | GPIO.output(LOOP_OUT, GPIO.LOW) 592 | GPIO.add_event_detect(LOOP_IN, GPIO.BOTH, callback=cb) 593 | time.sleep(0.01) 594 | for i in range(2048): 595 | GPIO.output(LOOP_OUT, GPIO.HIGH) 596 | time.sleep(0.001) 597 | GPIO.output(LOOP_OUT, GPIO.LOW) 598 | time.sleep(0.001) 599 | GPIO.remove_event_detect(LOOP_IN) 600 | self.assertEqual(self.callback_count, 4096) 601 | 602 | def testEventOnOutput(self): 603 | with self.assertRaises(RuntimeError): 604 | GPIO.add_event_detect(LOOP_OUT, GPIO.FALLING) 605 | 606 | def testAlternateWaitForEdge(self): 607 | def makehigh(): 608 | GPIO.output(LOOP_OUT, GPIO.HIGH) 609 | def makelow(): 610 | GPIO.output(LOOP_OUT, GPIO.LOW) 611 | GPIO.output(LOOP_OUT, GPIO.LOW) 612 | t = Timer(0.1, makehigh) 613 | t2 = Timer(0.15, makelow) 614 | t.start() 615 | t2.start() 616 | GPIO.wait_for_edge(LOOP_IN, GPIO.RISING) 617 | GPIO.wait_for_edge(LOOP_IN, GPIO.FALLING) 618 | 619 | def testWaitForEdgeTimeout(self): 620 | def makehigh(): 621 | GPIO.output(LOOP_OUT, GPIO.HIGH) 622 | def makelow(): 623 | GPIO.output(LOOP_OUT, GPIO.LOW) 624 | 625 | with self.assertRaises(TypeError): 626 | GPIO.wait_for_edge(LOOP_IN, GPIO.RISING, timeout="beer") 627 | 628 | with self.assertRaises(ValueError): 629 | GPIO.wait_for_edge(LOOP_IN, GPIO.RISING, timeout=-1234) 630 | 631 | makelow() 632 | chan = GPIO.wait_for_edge(LOOP_IN, GPIO.RISING, timeout=200) 633 | self.assertEqual(chan, None) 634 | 635 | t = Timer(0.1, makehigh) 636 | t.start() 637 | chan = GPIO.wait_for_edge(LOOP_IN, GPIO.RISING, timeout=200) 638 | self.assertEqual(chan, LOOP_IN) 639 | 640 | def tearDown(self): 641 | GPIO.cleanup() 642 | 643 | class TestCleanup(unittest.TestCase): 644 | def setUp(self): 645 | GPIO.setmode(GPIO.BOARD) 646 | 647 | def test_cleanall(self): 648 | GPIO.setup(LOOP_OUT, GPIO.OUT) 649 | GPIO.setup(LED_PIN, GPIO.OUT) 650 | self.assertEqual(GPIO.gpio_function(LOOP_OUT), GPIO.OUT) 651 | self.assertEqual(GPIO.gpio_function(LED_PIN), GPIO.OUT) 652 | GPIO.cleanup() 653 | GPIO.setmode(GPIO.BOARD) 654 | self.assertEqual(GPIO.gpio_function(LOOP_OUT), GPIO.IN) 655 | self.assertEqual(GPIO.gpio_function(LED_PIN), GPIO.IN) 656 | 657 | def test_cleanone(self): 658 | GPIO.setup(LOOP_OUT, GPIO.OUT) 659 | GPIO.setup(LED_PIN, GPIO.OUT) 660 | self.assertEqual(GPIO.gpio_function(LOOP_OUT), GPIO.OUT) 661 | self.assertEqual(GPIO.gpio_function(LED_PIN), GPIO.OUT) 662 | GPIO.cleanup(LOOP_OUT) 663 | GPIO.setmode(GPIO.BOARD) 664 | self.assertEqual(GPIO.gpio_function(LOOP_OUT), GPIO.IN) 665 | self.assertEqual(GPIO.gpio_function(LED_PIN), GPIO.OUT) 666 | GPIO.cleanup(LED_PIN) 667 | GPIO.setmode(GPIO.BOARD) 668 | self.assertEqual(GPIO.gpio_function(LOOP_OUT), GPIO.IN) 669 | self.assertEqual(GPIO.gpio_function(LED_PIN), GPIO.IN) 670 | 671 | def test_cleantuple(self): 672 | GPIO.setup(LOOP_OUT, GPIO.OUT) 673 | GPIO.setup(LED_PIN, GPIO.OUT) 674 | self.assertEqual(GPIO.gpio_function(LOOP_OUT), GPIO.OUT) 675 | self.assertEqual(GPIO.gpio_function(LED_PIN), GPIO.OUT) 676 | GPIO.cleanup((LOOP_OUT,)) 677 | GPIO.setmode(GPIO.BOARD) 678 | self.assertEqual(GPIO.gpio_function(LOOP_OUT), GPIO.IN) 679 | self.assertEqual(GPIO.gpio_function(LED_PIN), GPIO.OUT) 680 | GPIO.cleanup((LED_PIN,)) 681 | GPIO.setmode(GPIO.BOARD) 682 | self.assertEqual(GPIO.gpio_function(LOOP_OUT), GPIO.IN) 683 | self.assertEqual(GPIO.gpio_function(LED_PIN), GPIO.IN) 684 | GPIO.setup(LOOP_OUT, GPIO.OUT) 685 | GPIO.setup(LED_PIN, GPIO.OUT) 686 | GPIO.cleanup((LOOP_OUT,LED_PIN)) 687 | GPIO.setmode(GPIO.BOARD) 688 | self.assertEqual(GPIO.gpio_function(LOOP_OUT), GPIO.IN) 689 | self.assertEqual(GPIO.gpio_function(LED_PIN), GPIO.IN) 690 | 691 | def test_cleanlist(self): 692 | GPIO.setup(LOOP_OUT, GPIO.OUT) 693 | GPIO.setup(LED_PIN, GPIO.OUT) 694 | self.assertEqual(GPIO.gpio_function(LOOP_OUT), GPIO.OUT) 695 | self.assertEqual(GPIO.gpio_function(LED_PIN), GPIO.OUT) 696 | GPIO.cleanup([LOOP_OUT]) 697 | self.assertEqual(GPIO.gpio_function(LOOP_OUT), GPIO.IN) 698 | self.assertEqual(GPIO.gpio_function(LED_PIN), GPIO.OUT) 699 | GPIO.cleanup([LED_PIN]) 700 | self.assertEqual(GPIO.gpio_function(LOOP_OUT), GPIO.IN) 701 | self.assertEqual(GPIO.gpio_function(LED_PIN), GPIO.IN) 702 | GPIO.setup(LOOP_OUT, GPIO.OUT) 703 | GPIO.setup(LED_PIN, GPIO.OUT) 704 | GPIO.cleanup([LOOP_OUT,LED_PIN]) 705 | self.assertEqual(GPIO.gpio_function(LOOP_OUT), GPIO.IN) 706 | self.assertEqual(GPIO.gpio_function(LED_PIN), GPIO.IN) 707 | 708 | if __name__ == '__main__': 709 | unittest.main() 710 | --------------------------------------------------------------------------------