3 * @purpose Implementation of BBB pin control functions and structures
4 * THIS CODE IS NOT THREADSAFE
10 #include <sys/types.h>
16 * Structure to represent a GPIO pin
17 * Note: Only accessable to this file; to use the functions pass a GPIOId
27 * Structure to represent an ADC pin
28 * Note: Only accessable to this file; to use the functions pass a ADCId
37 * Structure to represent a PWM pin
38 * Note: Only accessable to this file; to use the functions pass a PWMId
49 /** Array of GPIO pins **/
50 static GPIO_Pin g_gpio[GPIO_NUM_PINS] = {{0}};
51 /** Array of ADC pins **/
52 static ADC_Pin g_adc[ADC_NUM_PINS] = {{0}};
53 /** Array of PWM pins **/
54 static PWM_Pin g_pwm[PWM_NUM_PINS] = {{0}};
56 static char g_buffer[BUFSIZ] = {0};
59 * Maps a GPIO number to an index into g_gpio (only for use in bbb_pin.c)
60 * If there is no index for that GPIO number, 128 is returned.
62 const unsigned char g_pin_gpio_to_index[GPIO_MAX_NUMBER+1] = {
63 128, 128, 128, 128, 0, 1, 128, 128, 2, 3, 4, 5, 128, 128,
64 6, 7, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 8, 9,
65 128, 128, 10, 11, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
66 128, 128, 12, 13, 14, 15, 16, 17, 128, 128, 128, 128, 128, 128,
67 128, 128, 128, 128, 18, 19, 128, 128, 128, 20, 21, 22, 23, 24,
68 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 128, 128,
69 128, 128, 37, 38, 39, 40, 128, 128, 128, 128, 128, 128, 128, 128,
70 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
75 * Maps an index in g_gpio to the corresponding GPIO number.
77 const unsigned char g_pin_index_to_gpio[GPIO_NUM_PINS] = {
78 4, 5, 8, 9, 10, 11, 14, 15, 26, 27, 30, 31, 44, 45,
79 46, 47, 48, 49, 60, 61, 65, 66, 67, 68, 69, 70, 71, 72,
80 73, 74, 75, 76, 77, 78, 79, 80, 81, 86, 87, 88, 89, 112,
85 * Export a GPIO pin and open the file descriptors
86 * @param pin The GPIO number to be exported
87 * @return true on success, false otherwise
89 bool GPIO_Export(int pin)
91 if (pin < 0 || pin > GPIO_MAX_NUMBER || g_pin_gpio_to_index[pin] == 128)
93 AbortBool("Not a useable pin number: %d", pin);
96 GPIO_Pin *gpio = &g_gpio[g_pin_gpio_to_index[pin]];
97 if (gpio->initialised)
99 Log(LOGNOTE, "GPIO %d already initialised.", pin);
104 sprintf(g_buffer, "%s/export", GPIO_DEVICE_PATH);
105 FILE * file_export = fopen(g_buffer, "w");
106 if (file_export == NULL)
108 AbortBool("Couldn't open %s to export GPIO pin %d - %s", g_buffer, pin, strerror(errno));
110 fprintf(file_export, "%d", pin);
113 // Setup direction file descriptor
114 sprintf(g_buffer, "%s/gpio%d/direction", GPIO_DEVICE_PATH, pin);
115 gpio->fd_direction = open(g_buffer, O_RDWR);
116 if (gpio->fd_direction < 0)
118 AbortBool("Couldn't open %s for GPIO pin %d - %s", g_buffer, pin, strerror(errno));
121 // Setup value file descriptor
122 sprintf(g_buffer, "%s/gpio%d/value", GPIO_DEVICE_PATH, pin);
123 gpio->fd_value = open(g_buffer, O_RDWR);
124 if (gpio->fd_value < 0)
126 close(gpio->fd_direction);
127 AbortBool("Couldn't open %s for GPIO pin %d - %s", g_buffer, pin, strerror(errno));
130 gpio->initialised = true;
131 Log(LOGDEBUG, "Exported GPIO%d", pin);
136 * Unexport a GPIO pin and close its' file descriptors
137 * @param pin The GPIO number to be unexported
139 void GPIO_Unexport(int pin)
141 if (pin < 0 || pin > GPIO_MAX_NUMBER || g_pin_gpio_to_index[pin] == 128)
143 Abort("Not a useable pin number: %d", pin);
146 GPIO_Pin *gpio = &g_gpio[g_pin_gpio_to_index[pin]];
147 if (!gpio->initialised)
149 Abort("GPIO %d is already uninitialised", pin);
152 // Close file descriptors
153 close(gpio->fd_value);
154 close(gpio->fd_direction);
155 // Uninitialise this one
156 gpio->initialised = false;
159 if (g_buffer[0] == '\0')
160 sprintf(g_buffer, "%s/unexport", GPIO_DEVICE_PATH);
161 FILE * file_export = fopen(g_buffer, "w");
162 if (file_export == NULL)
164 Abort("Couldn't open %s to export GPIO pin %d - %s", g_buffer, pin, strerror(errno));
167 fprintf(file_export, "%d", pin);
172 * Initialise all PWM pins and open file descriptors
173 * @param pin - The sysfs pin number
174 * @return true if exported, false otherwise
176 bool PWM_Export(int pin)
178 //goto would make this easier...
179 if (pin < 0 || pin >= PWM_NUM_PINS)
181 AbortBool("Invalid PWM pin %d specified.", pin);
184 PWM_Pin *pwm = &g_pwm[pin];
185 if (pwm->initialised)
187 Log(LOGNOTE, "PWM %d already exported.", pin);
191 // Try export the pin, doesn't matter if it's already exported.
192 sprintf(g_buffer, "%s/export", PWM_DEVICE_PATH);
193 FILE * file_export = fopen(g_buffer, "w");
194 if (file_export == NULL)
196 AbortBool("Couldn't open %s to export PWM pin %d - %s",
197 g_buffer, pin, strerror(errno));
199 fprintf(file_export, "%d\n", pin);
202 // Open file descriptors
203 sprintf(g_buffer, "%s/pwm%d/run", PWM_DEVICE_PATH, pin);
204 pwm->fd_run = open(g_buffer, O_WRONLY);
207 AbortBool("Couldn't open %s for PWM%d - %s", g_buffer, pin, strerror(errno));
210 sprintf(g_buffer, "%s/pwm%d/polarity", PWM_DEVICE_PATH, pin);
211 pwm->fd_polarity = open(g_buffer, O_WRONLY);
212 if (pwm->fd_polarity < 0)
215 AbortBool("Couldn't open %s for PWM%d - %s", g_buffer, pin, strerror(errno));
218 sprintf(g_buffer, "%s/pwm%d/period_ns", PWM_DEVICE_PATH, pin);
219 pwm->file_period = fopen(g_buffer, "w");
220 if (pwm->file_period == NULL)
223 close(pwm->fd_polarity);
224 AbortBool("Couldn't open %s for PWM%d - %s", g_buffer, pin, strerror(errno));
227 sprintf(g_buffer, "%s/pwm%d/duty_ns", PWM_DEVICE_PATH, pin);
228 pwm->file_duty = fopen(g_buffer, "w");
229 if (pwm->file_duty == NULL)
232 close(pwm->fd_polarity);
233 fclose(pwm->file_period);
234 AbortBool("Couldn't open %s for PWM%d - %s", g_buffer, pin, strerror(errno));
237 // Don't buffer the streams
238 setbuf(pwm->file_period, NULL);
239 setbuf(pwm->file_duty, NULL);
241 pwm->initialised = true;
242 Log(LOGDEBUG, "Exported PWM%d", pin);
248 * Unexport a PWM pin and close its file descriptors
249 * @param pin - The sysfs pin number
251 void PWM_Unexport(int pin)
253 if (pin < 0 || pin >= PWM_NUM_PINS)
255 Abort("Invalid PWM pin number %d specified.", pin);
258 PWM_Pin *pwm = &g_pwm[pin];
259 if (!pwm->initialised)
261 Abort("PWM %d not initialised", pin);
264 // Close the file descriptors
265 close(pwm->fd_polarity);
266 //Stop it, if it's still running
267 pwrite(pwm->fd_run, "0", 1, 0);
269 fclose(pwm->file_period);
270 fclose(pwm->file_duty);
272 pwm->initialised = false;
274 // Try unexport the pin, doesn't matter if it's already unexported.
275 sprintf(g_buffer, "%s/unexport", PWM_DEVICE_PATH);
276 FILE * file_unexport = fopen(g_buffer, "w");
277 if (file_unexport == NULL)
279 Abort("Couldn't open %s to unexport PWM pin %d - %s", g_buffer, pin, strerror(errno));
281 fprintf(file_unexport, "%d\n", pin);
282 fclose(file_unexport);
286 * Initialise ADC structures
287 * @param pin The ADC pin number
289 bool ADC_Export(int pin)
291 if (pin < 0 || pin >= ADC_NUM_PINS)
293 AbortBool("Invalid ADC pin %d specified.", pin);
295 else if (g_adc[pin].initialised)
297 Log(LOGNOTE, "ADC %d already initialised", pin);
301 sprintf(g_buffer, "%s/in_voltage%d_raw", ADC_DEVICE_PATH, pin);
302 g_adc[pin].fd_value = open(g_buffer, O_RDONLY);
303 if (g_adc[pin].fd_value <0)
305 AbortBool("Couldn't open ADC %d device file %s - %s", pin, g_buffer, strerror(errno));
308 g_adc[pin].initialised = true;
309 Log(LOGDEBUG, "Opened ADC %d", pin);
315 * @param pin The ADC pin number
317 void ADC_Unexport(int pin)
319 if (pin < 0 || pin >= ADC_NUM_PINS)
321 Abort("Invalid ADC pin %d specified.", pin);
323 else if (!g_adc[pin].initialised)
325 Abort("ADC %d already uninitialised", pin);
328 close(g_adc[pin].fd_value);
329 g_adc[pin].fd_value = -1;
330 g_adc[pin].initialised = false;
335 * @param pin - The pin to set. MUST have been exported before calling this function.
337 bool GPIO_Set(int pin, bool value)
339 if (pin < 0 || pin > GPIO_MAX_NUMBER || g_pin_gpio_to_index[pin] == 128)
341 AbortBool("Not a useable pin number: %d", pin);
344 GPIO_Pin *gpio = &g_gpio[g_pin_gpio_to_index[pin]];
345 if (!gpio->initialised)
347 AbortBool("GPIO %d is not initialised.", pin);
349 //Set the pin direction
350 if (pwrite(gpio->fd_direction, "out", 3, 0) != 3)
352 AbortBool("Couldn't set GPIO %d direction - %s", pin, strerror(errno));
355 char c = value ? '1' : '0';
356 if (pwrite(gpio->fd_value, &c, 1, 0) != 1)
358 AbortBool("Couldn't read GPIO %d value - %s", pin, strerror(errno));
365 * Read from a GPIO Pin
366 * @param pin - The pin to read
367 * @param result A pointer to store the result
368 * @return true on success, false otherwise
370 bool GPIO_Read(int pin, bool *result)
372 if (pin < 0 || pin > GPIO_MAX_NUMBER || g_pin_gpio_to_index[pin] == 128)
374 AbortBool("Not a useable pin number: %d", pin);
377 GPIO_Pin *gpio = &g_gpio[g_pin_gpio_to_index[pin]];
378 if (!gpio->initialised)
380 AbortBool("GPIO %d is not initialised.", pin);
383 if (pwrite(gpio->fd_direction, "in", 2, 0) != 2)
385 AbortBool("Couldn't set GPIO %d direction - %s", pin, strerror(errno));
389 if (pread(gpio->fd_value, &c, 1, 0) != 1)
391 AbortBool("Couldn't read GPIO %d value - %s", pin, strerror(errno));
394 *result = (c == '1');
400 * @param pin - The sysfs pin number
401 * @param polarity - if true, pin is active high, else active low
402 * @param period - The period in ns
403 * @param duty - The time the pin is active in ns
405 bool PWM_Set(int pin, bool polarity, long period, long duty)
407 Log(LOGDEBUG, "Pin %d, pol %d, period: %lu, duty: %lu", pin, polarity, period, duty);
409 if (pin < 0 || pin >= PWM_NUM_PINS)
411 AbortBool("Invalid PWM pin number %d specified.", pin);
414 PWM_Pin *pwm = &g_pwm[pin];
415 if (!pwm->initialised)
417 AbortBool("PWM %d is not initialised.", pin);
420 // Have to stop PWM before changing it
421 if (pwrite(pwm->fd_run, "0", 1, 0) != 1)
423 AbortBool("Couldn't stop PWM %d - %s", pin, strerror(errno));
426 char c = polarity ? '1' : '0';
427 if (pwrite(pwm->fd_polarity, &c, 1, 0) != 1)
429 AbortBool("Couldn't set PWM %d polarity - %s", pin, strerror(errno));
432 //This must be done first, otherwise period/duty settings can conflict
433 if (fwrite("0", 1, 1, pwm->file_duty) < 1)
435 AbortBool("Couldn't zero the duty for PWM %d - %s", pin, strerror(errno));
438 if (fprintf(pwm->file_period, "%lu", period) < 0)
440 AbortBool("Couldn't set period for PWM %d - %s", pin, strerror(errno));
444 if (fprintf(pwm->file_duty, "%lu", duty) < 0)
446 AbortBool("Couldn't set duty cycle for PWM %d - %s", pin, strerror(errno));
450 if (pwrite(pwm->fd_run, "1", 1, 0) != 1)
452 AbortBool("Couldn't start PWM %d - %s", pin, strerror(errno));
459 * Deactivate a PWM pin
460 * @param pin - The syfs pin number
461 * @return true on success, false otherwise
463 bool PWM_Stop(int pin)
465 if (pin < 0 || pin >= PWM_NUM_PINS)
467 AbortBool("Invalid PWM pin number %d specified.", pin);
469 else if (!g_pwm[pin].initialised)
471 AbortBool("PWM %d is not initialised.", pin);
474 if (pwrite(g_pwm[pin].fd_run, "0", 1, 0) != 1)
476 AbortBool("Couldn't stop PWM %d - %s", pin, strerror(errno));
484 * @param id - The ID of the ADC pin to read
485 * @param value - A pointer to store the value read from the ADC
486 * @returns - The true if succeeded, false otherwise.
488 bool ADC_Read(int id, int *value)
490 char adc_str[ADC_DIGITS] = {0};
492 if (id < 0 || id >= ADC_NUM_PINS)
494 AbortBool("Invalid ADC pin %d specified.", id);
496 else if (!g_adc[id].initialised)
498 AbortBool("ADC %d is not initialised.", id);
501 if (pread(g_adc[id].fd_value, adc_str, ADC_DIGITS-1, 0) == -1)
503 AbortBool("ADC %d read failed: %s", id, strerror(errno));
506 *value = strtol(adc_str, NULL, 10);
511 //For running on systems that are not the BBB
512 bool True_Stub(void *arg, ...) { return true; }
513 bool ADC_Read_Stub(int *val, ...) { *val = 0; return true; }
514 bool GPIO_Read_Stub(bool *val, ...) { *val = false; return true; }