diff options
Diffstat (limited to 'python/native/gpio.c')
| -rw-r--r-- | python/native/gpio.c | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/python/native/gpio.c b/python/native/gpio.c new file mode 100644 index 0000000..3950b16 --- /dev/null +++ b/python/native/gpio.c | |||
| @@ -0,0 +1,329 @@ | |||
| 1 | /* | ||
| 2 | Copyright (c) 2012 Ben Croston / 2012-2013 Eric PTAK | ||
| 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 <stdint.h> | ||
| 24 | #include <stdlib.h> | ||
| 25 | #include <string.h> | ||
| 26 | #include <fcntl.h> | ||
| 27 | #include <sys/mman.h> | ||
| 28 | #include <time.h> | ||
| 29 | #include <pthread.h> | ||
| 30 | #include "gpio.h" | ||
| 31 | |||
| 32 | #define BCM2708_PERI_BASE 0x20000000 | ||
| 33 | #define GPIO_BASE (BCM2708_PERI_BASE + 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 | static volatile uint32_t *gpio_map; | ||
| 50 | |||
| 51 | struct tspair { | ||
| 52 | struct timespec up; | ||
| 53 | struct timespec down; | ||
| 54 | }; | ||
| 55 | |||
| 56 | static struct pulse gpio_pulses[GPIO_COUNT]; | ||
| 57 | static struct tspair gpio_tspairs[GPIO_COUNT]; | ||
| 58 | static pthread_t *gpio_threads[GPIO_COUNT]; | ||
| 59 | |||
| 60 | void short_wait(void) | ||
| 61 | { | ||
| 62 | int i; | ||
| 63 | |||
| 64 | for (i=0; i<150; i++) // wait 150 cycles | ||
| 65 | { | ||
| 66 | asm volatile("nop"); | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | int setup(void) | ||
| 71 | { | ||
| 72 | int mem_fd; | ||
| 73 | uint8_t *gpio_mem; | ||
| 74 | |||
| 75 | if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) | ||
| 76 | { | ||
| 77 | return SETUP_DEVMEM_FAIL; | ||
| 78 | } | ||
| 79 | |||
| 80 | if ((gpio_mem = malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) | ||
| 81 | return SETUP_MALLOC_FAIL; | ||
| 82 | |||
| 83 | if ((uint32_t)gpio_mem % PAGE_SIZE) | ||
| 84 | gpio_mem += PAGE_SIZE - ((uint32_t)gpio_mem % PAGE_SIZE); | ||
| 85 | |||
| 86 | gpio_map = (uint32_t *)mmap( (caddr_t)gpio_mem, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, mem_fd, GPIO_BASE); | ||
| 87 | |||
| 88 | if ((uint32_t)gpio_map < 0) | ||
| 89 | return SETUP_MMAP_FAIL; | ||
| 90 | |||
| 91 | return SETUP_OK; | ||
| 92 | } | ||
| 93 | |||
| 94 | void set_pullupdn(int gpio, int pud) | ||
| 95 | { | ||
| 96 | int clk_offset = PULLUPDNCLK_OFFSET + (gpio/32); | ||
| 97 | int shift = (gpio%32); | ||
| 98 | |||
| 99 | if (pud == PUD_DOWN) | ||
| 100 | *(gpio_map+PULLUPDN_OFFSET) = (*(gpio_map+PULLUPDN_OFFSET) & ~3) | PUD_DOWN; | ||
| 101 | else if (pud == PUD_UP) | ||
| 102 | *(gpio_map+PULLUPDN_OFFSET) = (*(gpio_map+PULLUPDN_OFFSET) & ~3) | PUD_UP; | ||
| 103 | else // pud == PUD_OFF | ||
| 104 | *(gpio_map+PULLUPDN_OFFSET) &= ~3; | ||
| 105 | |||
| 106 | short_wait(); | ||
| 107 | *(gpio_map+clk_offset) = 1 << shift; | ||
| 108 | short_wait(); | ||
| 109 | *(gpio_map+PULLUPDN_OFFSET) &= ~3; | ||
| 110 | *(gpio_map+clk_offset) = 0; | ||
| 111 | } | ||
| 112 | |||
| 113 | //updated Eric PTAK - trouch.com | ||
| 114 | void set_function(int gpio, int function, int pud) | ||
| 115 | { | ||
| 116 | if (function == PWM) { | ||
| 117 | function = OUT; | ||
| 118 | enablePWM(gpio); | ||
| 119 | } | ||
| 120 | else { | ||
| 121 | disablePWM(gpio); | ||
| 122 | } | ||
| 123 | |||
| 124 | int offset = FSEL_OFFSET + (gpio/10); | ||
| 125 | int shift = (gpio%10)*3; | ||
| 126 | |||
| 127 | set_pullupdn(gpio, pud); | ||
| 128 | *(gpio_map+offset) = (*(gpio_map+offset) & ~(7<<shift)) | (function<<shift); | ||
| 129 | } | ||
| 130 | |||
| 131 | //added Eric PTAK - trouch.com | ||
| 132 | int get_function(int gpio) | ||
| 133 | { | ||
| 134 | int offset = FSEL_OFFSET + (gpio/10); | ||
| 135 | int shift = (gpio%10)*3; | ||
| 136 | int value = *(gpio_map+offset); | ||
| 137 | value >>= shift; | ||
| 138 | value &= 7; | ||
| 139 | if ((value == OUT) && isPWMEnabled(gpio)) { | ||
| 140 | value = PWM; | ||
| 141 | } | ||
| 142 | return value; // 0=input, 1=output, 4=alt0 | ||
| 143 | } | ||
| 144 | |||
| 145 | //updated Eric PTAK - trouch.com | ||
| 146 | int input(int gpio) | ||
| 147 | { | ||
| 148 | int offset, value, mask; | ||
| 149 | |||
| 150 | offset = PINLEVEL_OFFSET + (gpio/32); | ||
| 151 | mask = (1 << gpio%32); | ||
| 152 | value = *(gpio_map+offset) & mask; | ||
| 153 | return value; | ||
| 154 | } | ||
| 155 | |||
| 156 | void output(int gpio, int value) | ||
| 157 | { | ||
| 158 | int offset, shift; | ||
| 159 | |||
| 160 | if (value) // value == HIGH | ||
| 161 | offset = SET_OFFSET + (gpio/32); | ||
| 162 | else // value == LOW | ||
| 163 | offset = CLR_OFFSET + (gpio/32); | ||
| 164 | |||
| 165 | shift = (gpio%32); | ||
| 166 | |||
| 167 | *(gpio_map+offset) = 1 << shift; | ||
| 168 | } | ||
| 169 | |||
| 170 | //added Eric PTAK - trouch.com | ||
| 171 | void outputSequence(int gpio, int period, char* sequence) { | ||
| 172 | int i, value; | ||
| 173 | struct timespec ts; | ||
| 174 | ts.tv_sec = period/1000; | ||
| 175 | ts.tv_nsec = (period%1000) * 1000000; | ||
| 176 | |||
| 177 | for (i=0; sequence[i] != '\0'; i++) { | ||
| 178 | if (sequence[i] == '1') { | ||
| 179 | value = 1; | ||
| 180 | } | ||
| 181 | else { | ||
| 182 | value = 0; | ||
| 183 | } | ||
| 184 | output(gpio, value); | ||
| 185 | nanosleep(&ts, NULL); | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | void resetPWM(int gpio) { | ||
| 190 | gpio_pulses[gpio].type = 0; | ||
| 191 | gpio_pulses[gpio].value = 0; | ||
| 192 | |||
| 193 | gpio_tspairs[gpio].up.tv_sec = 0; | ||
| 194 | gpio_tspairs[gpio].up.tv_nsec = 0; | ||
| 195 | gpio_tspairs[gpio].down.tv_sec = 0; | ||
| 196 | gpio_tspairs[gpio].down.tv_nsec = 0; | ||
| 197 | } | ||
| 198 | |||
| 199 | //added Eric PTAK - trouch.com | ||
| 200 | void pulseTS(int gpio, struct timespec *up, struct timespec *down) { | ||
| 201 | if ((up->tv_sec > 0) || (up->tv_nsec > 0)) { | ||
| 202 | output(gpio, 1); | ||
| 203 | nanosleep(up, NULL); | ||
| 204 | } | ||
| 205 | |||
| 206 | if ((down->tv_sec > 0) || (down->tv_nsec > 0)) { | ||
| 207 | output(gpio, 0); | ||
| 208 | nanosleep(down, NULL); | ||
| 209 | } | ||
| 210 | } | ||
| 211 | |||
| 212 | //added Eric PTAK - trouch.com | ||
| 213 | void pulseOrSaveTS(int gpio, struct timespec *up, struct timespec *down) { | ||
| 214 | if (gpio_threads[gpio] != NULL) { | ||
| 215 | memcpy(&gpio_tspairs[gpio].up, up, sizeof(struct timespec)); | ||
| 216 | memcpy(&gpio_tspairs[gpio].down, down, sizeof(struct timespec)); | ||
| 217 | } | ||
| 218 | else { | ||
| 219 | pulseTS(gpio, up, down); | ||
| 220 | } | ||
| 221 | } | ||
| 222 | |||
| 223 | //added Eric PTAK - trouch.com | ||
| 224 | void pulseMilli(int gpio, int up, int down) { | ||
| 225 | struct timespec tsUP, tsDOWN; | ||
| 226 | |||
| 227 | tsUP.tv_sec = up/1000; | ||
| 228 | tsUP.tv_nsec = (up%1000) * 1000000; | ||
| 229 | |||
| 230 | tsDOWN.tv_sec = down/1000; | ||
| 231 | tsDOWN.tv_nsec = (down%1000) * 1000000; | ||
| 232 | pulseOrSaveTS(gpio, &tsUP, &tsDOWN); | ||
| 233 | } | ||
| 234 | |||
| 235 | //added Eric PTAK - trouch.com | ||
| 236 | void pulseMilliRatio(int gpio, int width, float ratio) { | ||
| 237 | int up = ratio*width; | ||
| 238 | int down = width - up; | ||
| 239 | pulseMilli(gpio, up, down); | ||
| 240 | } | ||
| 241 | |||
| 242 | //added Eric PTAK - trouch.com | ||
| 243 | void pulseMicro(int gpio, int up, int down) { | ||
| 244 | struct timespec tsUP, tsDOWN; | ||
| 245 | |||
| 246 | tsUP.tv_sec = 0; | ||
| 247 | tsUP.tv_nsec = up * 1000; | ||
| 248 | |||
| 249 | tsDOWN.tv_sec = 0; | ||
| 250 | tsDOWN.tv_nsec = down * 1000; | ||
| 251 | pulseOrSaveTS(gpio, &tsUP, &tsDOWN); | ||
| 252 | } | ||
| 253 | |||
| 254 | //added Eric PTAK - trouch.com | ||
| 255 | void pulseMicroRatio(int gpio, int width, float ratio) { | ||
| 256 | int up = ratio*width; | ||
| 257 | int down = width - up; | ||
| 258 | pulseMicro(gpio, up, down); | ||
| 259 | } | ||
| 260 | |||
| 261 | //added Eric PTAK - trouch.com | ||
| 262 | void pulseAngle(int gpio, float angle) { | ||
| 263 | gpio_pulses[gpio].type = ANGLE; | ||
| 264 | gpio_pulses[gpio].value = angle; | ||
| 265 | int up = 1520 + (angle*400)/45; | ||
| 266 | int down = 20000-up; | ||
| 267 | pulseMicro(gpio, up, down); | ||
| 268 | } | ||
| 269 | |||
| 270 | //added Eric PTAK - trouch.com | ||
| 271 | void pulseRatio(int gpio, float ratio) { | ||
| 272 | gpio_pulses[gpio].type = RATIO; | ||
| 273 | gpio_pulses[gpio].value = ratio; | ||
| 274 | int up = ratio * 20000; | ||
| 275 | int down = 20000 - up; | ||
| 276 | pulseMicro(gpio, up, down); | ||
| 277 | } | ||
| 278 | |||
| 279 | struct pulse* getPulse(int gpio) { | ||
| 280 | return &gpio_pulses[gpio]; | ||
| 281 | } | ||
| 282 | |||
| 283 | //added Eric PTAK - trouch.com | ||
| 284 | void* pwmLoop(void* data) { | ||
| 285 | int gpio = (int)data; | ||
| 286 | |||
| 287 | while (1) { | ||
| 288 | pulseTS(gpio, &gpio_tspairs[gpio].up, &gpio_tspairs[gpio].down); | ||
| 289 | } | ||
| 290 | } | ||
| 291 | |||
| 292 | //added Eric PTAK - trouch.com | ||
| 293 | void enablePWM(int gpio) { | ||
| 294 | pthread_t *thread = gpio_threads[gpio]; | ||
| 295 | if (thread != NULL) { | ||
| 296 | return; | ||
| 297 | } | ||
| 298 | |||
| 299 | resetPWM(gpio); | ||
| 300 | |||
| 301 | thread = (pthread_t*) malloc(sizeof(pthread_t)); | ||
| 302 | pthread_create(thread, NULL, pwmLoop, (void*)gpio); | ||
| 303 | gpio_threads[gpio] = thread; | ||
| 304 | } | ||
| 305 | |||
| 306 | //added Eric PTAK - trouch.com | ||
| 307 | void disablePWM(int gpio) { | ||
| 308 | pthread_t *thread = gpio_threads[gpio]; | ||
| 309 | if (thread == NULL) { | ||
| 310 | return; | ||
| 311 | } | ||
| 312 | |||
| 313 | pthread_cancel(*thread); | ||
| 314 | gpio_threads[gpio] = NULL; | ||
| 315 | output(gpio, 0); | ||
| 316 | resetPWM(gpio); | ||
| 317 | } | ||
| 318 | |||
| 319 | //added Eric PTAK - trouch.com | ||
| 320 | int isPWMEnabled(int gpio) { | ||
| 321 | return gpio_threads[gpio] != NULL; | ||
| 322 | } | ||
| 323 | |||
| 324 | |||
| 325 | void cleanup(void) | ||
| 326 | { | ||
| 327 | // fixme - set all gpios back to input | ||
| 328 | munmap((caddr_t)gpio_map, BLOCK_SIZE); | ||
| 329 | } | ||
