Yet Another WebIOPi+
 All Classes Namespaces Files Functions Variables Macros Pages
gpio.c
Go to the documentation of this file.
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 <stdio.h>
24 #include <stdint.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <fcntl.h>
28 #include <sys/mman.h>
29 #include <time.h>
30 #include <pthread.h>
31 #include "gpio.h"
32 #include "cpuinfo.h"
33 
34 #define BCM2708_PERI_BASE 0x20000000
35 #define BCM2708_GPIO_BASE (BCM2708_PERI_BASE + 0x200000)
36 #define BCM2709_PERI_BASE 0x3F000000
37 #define BCM2709_GPIO_BASE (BCM2709_PERI_BASE + 0x200000)
38 #define FSEL_OFFSET 0 // 0x0000
39 #define SET_OFFSET 7 // 0x001c / 4
40 #define CLR_OFFSET 10 // 0x0028 / 4
41 #define PINLEVEL_OFFSET 13 // 0x0034 / 4
42 #define EVENT_DETECT_OFFSET 16 // 0x0040 / 4
43 #define RISING_ED_OFFSET 19 // 0x004c / 4
44 #define FALLING_ED_OFFSET 22 // 0x0058 / 4
45 #define HIGH_DETECT_OFFSET 25 // 0x0064 / 4
46 #define LOW_DETECT_OFFSET 28 // 0x0070 / 4
47 #define PULLUPDN_OFFSET 37 // 0x0094 / 4
48 #define PULLUPDNCLK_OFFSET 38 // 0x0098 / 4
49 
50 #define PAGE_SIZE (4*1024)
51 #define BLOCK_SIZE (4*1024)
52 
53 static volatile uint32_t *gpio_map;
54 
55 struct tspair {
56  struct timespec up;
57  struct timespec down;
58 };
59 
60 static struct pulse gpio_pulses[GPIO_COUNT];
62 static pthread_t *gpio_threads[GPIO_COUNT];
63 
64 void short_wait(void)
65 {
66  int i;
67 
68  for (i=0; i<150; i++) // wait 150 cycles
69  {
70  asm volatile("nop");
71  }
72 }
73 
74 int setup(void)
75 {
76  int mem_fd;
77  uint8_t *gpio_mem;
78 
79  if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0)
80  {
81  return SETUP_DEVMEM_FAIL;
82  }
83 
84  if ((gpio_mem = malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL)
85  return SETUP_MALLOC_FAIL;
86 
87  if ((uint32_t)gpio_mem % PAGE_SIZE)
88  gpio_mem += PAGE_SIZE - ((uint32_t)gpio_mem % PAGE_SIZE);
89 
90  if (get_rpi_revision() <= 2 || number_of_cores() <= 2)
91  gpio_map = (uint32_t *)mmap( (caddr_t)gpio_mem, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, mem_fd, BCM2708_GPIO_BASE);
92  else
93  gpio_map = (uint32_t *)mmap( (caddr_t)gpio_mem, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, mem_fd, BCM2709_GPIO_BASE);
94 
95  if ((uint32_t)gpio_map < 0)
96  return SETUP_MMAP_FAIL;
97 
98  return SETUP_OK;
99 }
100 
101 void set_pullupdn(int gpio, int pud)
102 {
103  int clk_offset = PULLUPDNCLK_OFFSET + (gpio/32);
104  int shift = (gpio%32);
105 
106  if (pud == PUD_DOWN)
108  else if (pud == PUD_UP)
110  else // pud == PUD_OFF
111  *(gpio_map+PULLUPDN_OFFSET) &= ~3;
112 
113  short_wait();
114  *(gpio_map+clk_offset) = 1 << shift;
115  short_wait();
116  *(gpio_map+PULLUPDN_OFFSET) &= ~3;
117  *(gpio_map+clk_offset) = 0;
118 }
119 
120 //updated Eric PTAK - trouch.com
121 void set_function(int gpio, int function, int pud)
122 {
123  if (function == PWM) {
124  function = OUT;
125  enablePWM(gpio);
126  }
127  else {
128  disablePWM(gpio);
129  }
130 
131  int offset = FSEL_OFFSET + (gpio/10);
132  int shift = (gpio%10)*3;
133 
134  set_pullupdn(gpio, pud);
135  *(gpio_map+offset) = (*(gpio_map+offset) & ~(7<<shift)) | (function<<shift);
136 }
137 
138 //added Eric PTAK - trouch.com
140 {
141  int offset = FSEL_OFFSET + (gpio/10);
142  int shift = (gpio%10)*3;
143  int value = *(gpio_map+offset);
144  value >>= shift;
145  value &= 7;
146  if ((value == OUT) && isPWMEnabled(gpio)) {
147  value = PWM;
148  }
149  return value; // 0=input, 1=output, 4=alt0
150 }
151 
152 //updated Eric PTAK - trouch.com
153 int input(int gpio)
154 {
155  int offset, value, mask;
156 
157  offset = PINLEVEL_OFFSET + (gpio/32);
158  mask = (1 << gpio%32);
159  value = *(gpio_map+offset) & mask;
160  return value;
161 }
162 
163 void output(int gpio, int value)
164 {
165  int offset, shift;
166 
167  if (value) // value == HIGH
168  offset = SET_OFFSET + (gpio/32);
169  else // value == LOW
170  offset = CLR_OFFSET + (gpio/32);
171 
172  shift = (gpio%32);
173 
174  *(gpio_map+offset) = 1 << shift;
175 }
176 
177 //added Eric PTAK - trouch.com
178 void outputSequence(int gpio, int period, char* sequence) {
179  int i, value;
180  struct timespec ts;
181  ts.tv_sec = period/1000;
182  ts.tv_nsec = (period%1000) * 1000000;
183 
184  for (i=0; sequence[i] != '\0'; i++) {
185  if (sequence[i] == '1') {
186  value = 1;
187  }
188  else {
189  value = 0;
190  }
191  output(gpio, value);
192  nanosleep(&ts, NULL);
193  }
194 }
195 
196 void resetPWM(int gpio) {
197  gpio_pulses[gpio].type = 0;
198  gpio_pulses[gpio].value = 0;
199 
200  gpio_tspairs[gpio].up.tv_sec = 0;
201  gpio_tspairs[gpio].up.tv_nsec = 0;
202  gpio_tspairs[gpio].down.tv_sec = 0;
203  gpio_tspairs[gpio].down.tv_nsec = 0;
204 }
205 
206 //added Eric PTAK - trouch.com
207 void pulseTS(int gpio, struct timespec *up, struct timespec *down) {
208  if ((up->tv_sec > 0) || (up->tv_nsec > 0)) {
209  output(gpio, 1);
210  nanosleep(up, NULL);
211  }
212 
213  if ((down->tv_sec > 0) || (down->tv_nsec > 0)) {
214  output(gpio, 0);
215  nanosleep(down, NULL);
216  }
217 }
218 
219 //added Eric PTAK - trouch.com
220 void pulseOrSaveTS(int gpio, struct timespec *up, struct timespec *down) {
221  if (gpio_threads[gpio] != NULL) {
222  memcpy(&gpio_tspairs[gpio].up, up, sizeof(struct timespec));
223  memcpy(&gpio_tspairs[gpio].down, down, sizeof(struct timespec));
224  }
225  else {
226  pulseTS(gpio, up, down);
227  }
228 }
229 
230 //added Eric PTAK - trouch.com
231 void pulseMilli(int gpio, int up, int down) {
232  struct timespec tsUP, tsDOWN;
233 
234  tsUP.tv_sec = up/1000;
235  tsUP.tv_nsec = (up%1000) * 1000000;
236 
237  tsDOWN.tv_sec = down/1000;
238  tsDOWN.tv_nsec = (down%1000) * 1000000;
239  pulseOrSaveTS(gpio, &tsUP, &tsDOWN);
240 }
241 
242 //added Eric PTAK - trouch.com
243 void pulseMilliRatio(int gpio, int width, float ratio) {
244  int up = ratio*width;
245  int down = width - up;
246  pulseMilli(gpio, up, down);
247 }
248 
249 //added Eric PTAK - trouch.com
250 void pulseMicro(int gpio, int up, int down) {
251  struct timespec tsUP, tsDOWN;
252 
253  tsUP.tv_sec = 0;
254  tsUP.tv_nsec = up * 1000;
255 
256  tsDOWN.tv_sec = 0;
257  tsDOWN.tv_nsec = down * 1000;
258  pulseOrSaveTS(gpio, &tsUP, &tsDOWN);
259 }
260 
261 //added Eric PTAK - trouch.com
262 void pulseMicroRatio(int gpio, int width, float ratio) {
263  int up = ratio*width;
264  int down = width - up;
265  pulseMicro(gpio, up, down);
266 }
267 
268 //added Eric PTAK - trouch.com
269 void pulseAngle(int gpio, float angle) {
271  gpio_pulses[gpio].value = angle;
272  int up = 1520 + (angle*400)/45;
273  int down = 20000-up;
274  pulseMicro(gpio, up, down);
275 }
276 
277 //added Eric PTAK - trouch.com
278 void pulseRatio(int gpio, float ratio) {
280  gpio_pulses[gpio].value = ratio;
281  int up = ratio * 20000;
282  int down = 20000 - up;
283  pulseMicro(gpio, up, down);
284 }
285 
286 struct pulse* getPulse(int gpio) {
287  return &gpio_pulses[gpio];
288 }
289 
290 //added Eric PTAK - trouch.com
291 void* pwmLoop(void* data) {
292  int gpio = (int)data;
293 
294  while (1) {
295  pulseTS(gpio, &gpio_tspairs[gpio].up, &gpio_tspairs[gpio].down);
296  }
297 }
298 
299 //added Eric PTAK - trouch.com
300 void enablePWM(int gpio) {
301  pthread_t *thread = gpio_threads[gpio];
302  if (thread != NULL) {
303  return;
304  }
305 
306  resetPWM(gpio);
307 
308  thread = (pthread_t*) malloc(sizeof(pthread_t));
309  pthread_create(thread, NULL, pwmLoop, (void*)gpio);
310  gpio_threads[gpio] = thread;
311 }
312 
313 //added Eric PTAK - trouch.com
314 void disablePWM(int gpio) {
315  pthread_t *thread = gpio_threads[gpio];
316  if (thread == NULL) {
317  return;
318  }
319 
320  pthread_cancel(*thread);
321  gpio_threads[gpio] = NULL;
322  output(gpio, 0);
323  resetPWM(gpio);
324 }
325 
326 //added Eric PTAK - trouch.com
327 int isPWMEnabled(int gpio) {
328  return gpio_threads[gpio] != NULL;
329 }
330 
331 
332 void cleanup(void)
333 {
334  // fixme - set all gpios back to input
335  munmap((caddr_t)gpio_map, BLOCK_SIZE);
336 }
337 
339 {
340 char str[256];
341 int procCount = 0;
342 FILE *fp;
343 
344 if( (fp = fopen("/proc/cpuinfo", "r")) )
345 {
346  while(fgets(str, sizeof str, fp))
347  if( !memcmp(str, "processor", 9) ) procCount++;
348 }
349 
350 if ( !procCount )
351 {
352 printf("Unable to get proc count. Defaulting to 2");
353 procCount=2;
354 }
355 
356 return procCount;
357 }
#define BCM2708_GPIO_BASE
Definition: gpio.c:35
#define ANGLE
Definition: gpio.h:48
void pulseOrSaveTS(int gpio, struct timespec *up, struct timespec *down)
Definition: gpio.c:220
#define PAGE_SIZE
Definition: gpio.c:50
#define SETUP_MMAP_FAIL
Definition: gpio.h:26
#define PULLUPDNCLK_OFFSET
Definition: gpio.c:48
int type
Definition: gpio.h:51
#define OUT
Definition: gpio.h:31
void enablePWM(int gpio)
Definition: gpio.c:300
void * pwmLoop(void *data)
Definition: gpio.c:291
void output(int gpio, int value)
Definition: gpio.c:163
#define BCM2709_GPIO_BASE
Definition: gpio.c:37
static volatile uint32_t * gpio_map
Definition: gpio.c:53
int input(int gpio)
Definition: gpio.c:153
void pulseMicro(int gpio, int up, int down)
Definition: gpio.c:250
#define SETUP_DEVMEM_FAIL
Definition: gpio.h:24
#define PINLEVEL_OFFSET
Definition: gpio.c:41
void cleanup(void)
Definition: gpio.c:332
void disablePWM(int gpio)
Definition: gpio.c:314
struct pulse * getPulse(int gpio)
Definition: gpio.c:286
void resetPWM(int gpio)
Definition: gpio.c:196
#define FSEL_OFFSET
Definition: gpio.c:38
void pulseMicroRatio(int gpio, int width, float ratio)
Definition: gpio.c:262
void set_pullupdn(int gpio, int pud)
Definition: gpio.c:101
#define RATIO
Definition: gpio.h:47
float value
Definition: gpio.h:52
void pulseTS(int gpio, struct timespec *up, struct timespec *down)
Definition: gpio.c:207
#define PWM
Definition: gpio.h:38
#define PUD_UP
Definition: gpio.h:45
#define SET_OFFSET
Definition: gpio.c:39
#define CLR_OFFSET
Definition: gpio.c:40
int get_rpi_revision(void)
Definition: cpuinfo.c:53
#define SETUP_MALLOC_FAIL
Definition: gpio.h:25
#define PUD_DOWN
Definition: gpio.h:44
void outputSequence(int gpio, int period, char *sequence)
Definition: gpio.c:178
static struct pulse gpio_pulses[GPIO_COUNT]
Definition: gpio.c:60
void short_wait(void)
Definition: gpio.c:64
void pulseMilli(int gpio, int up, int down)
Definition: gpio.c:231
static struct tspair gpio_tspairs[GPIO_COUNT]
Definition: gpio.c:61
int number_of_cores(void)
Definition: gpio.c:338
int isPWMEnabled(int gpio)
Definition: gpio.c:327
#define BLOCK_SIZE
Definition: gpio.c:51
struct timespec up
Definition: gpio.c:56
void pulseMilliRatio(int gpio, int width, float ratio)
Definition: gpio.c:243
#define GPIO_COUNT
Definition: gpio.h:28
struct timespec down
Definition: gpio.c:57
void set_function(int gpio, int function, int pud)
Definition: gpio.c:121
#define SETUP_OK
Definition: gpio.h:23
#define PULLUPDN_OFFSET
Definition: gpio.c:47
void pulseRatio(int gpio, float ratio)
Definition: gpio.c:278
void pulseAngle(int gpio, float angle)
Definition: gpio.c:269
Definition: gpio.h:50
static pthread_t * gpio_threads[GPIO_COUNT]
Definition: gpio.c:62
Definition: gpio.c:55
int get_function(int gpio)
Definition: gpio.c:139
int setup(void)
Definition: gpio.c:74