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 * Export a GPIO pin and open the file descriptors
60 * @param pin The GPIO number to be exported
61 * @return true on success, false otherwise
63 bool GPIO_Export(int pin)
65 if (pin < 0 || pin > GPIO_MAX_NUMBER || g_pin_gpio_to_index[pin] == 128)
67 AbortBool("Not a useable pin number: %d", pin);
70 GPIO_Pin *gpio = &g_gpio[g_pin_gpio_to_index[pin]];
71 if (gpio->initialised)
73 Log(LOGNOTE, "GPIO %d already initialised.", pin);
78 sprintf(g_buffer, "%s/export", GPIO_DEVICE_PATH);
79 FILE * file_export = fopen(g_buffer, "w");
80 if (file_export == NULL)
82 AbortBool("Couldn't open %s to export GPIO pin %d - %s", g_buffer, pin, strerror(errno));
84 fprintf(file_export, "%d", pin);
87 // Setup direction file descriptor
88 sprintf(g_buffer, "%s/gpio%d/direction", GPIO_DEVICE_PATH, pin);
89 gpio->fd_direction = open(g_buffer, O_RDWR);
90 if (gpio->fd_direction < 0)
92 AbortBool("Couldn't open %s for GPIO pin %d - %s", g_buffer, pin, strerror(errno));
95 // Setup value file descriptor
96 sprintf(g_buffer, "%s/gpio%d/value", GPIO_DEVICE_PATH, pin);
97 gpio->fd_value = open(g_buffer, O_RDWR);
98 if (gpio->fd_value < 0)
100 close(gpio->fd_direction);
101 AbortBool("Couldn't open %s for GPIO pin %d - %s", g_buffer, pin, strerror(errno));
104 gpio->initialised = true;
105 Log(LOGDEBUG, "Exported GPIO%d", pin);
110 * Unexport a GPIO pin and close its' file descriptors
111 * @param pin The GPIO number to be unexported
113 void GPIO_Unexport(int pin)
115 if (pin < 0 || pin > GPIO_MAX_NUMBER || g_pin_gpio_to_index[pin] == 128)
117 Abort("Not a useable pin number: %d", pin);
120 GPIO_Pin *gpio = &g_gpio[g_pin_gpio_to_index[pin]];
121 if (!gpio->initialised)
123 Abort("GPIO %d is already uninitialised", pin);
126 // Close file descriptors
127 close(gpio->fd_value);
128 close(gpio->fd_direction);
129 // Uninitialise this one
130 gpio->initialised = false;
133 if (g_buffer[0] == '\0')
134 sprintf(g_buffer, "%s/unexport", GPIO_DEVICE_PATH);
135 FILE * file_export = fopen(g_buffer, "w");
136 if (file_export == NULL)
138 Abort("Couldn't open %s to export GPIO pin %d - %s", g_buffer, pin, strerror(errno));
141 fprintf(file_export, "%d", pin);
146 * Initialise all PWM pins and open file descriptors
147 * @param pin - The sysfs pin number
148 * @return true if exported, false otherwise
150 bool PWM_Export(int pin)
152 //goto would make this easier...
153 if (pin < 0 || pin >= PWM_NUM_PINS)
155 AbortBool("Invalid PWM pin %d specified.", pin);
158 PWM_Pin *pwm = &g_pwm[pin];
159 if (pwm->initialised)
161 Log(LOGNOTE, "PWM %d already exported.", pin);
165 // Try export the pin, doesn't matter if it's already exported.
166 sprintf(g_buffer, "%s/export", PWM_DEVICE_PATH);
167 FILE * file_export = fopen(g_buffer, "w");
168 if (file_export == NULL)
170 AbortBool("Couldn't open %s to export PWM pin %d - %s",
171 g_buffer, pin, strerror(errno));
173 fprintf(file_export, "%d\n", pin);
176 // Open file descriptors
177 sprintf(g_buffer, "%s/pwm%d/run", PWM_DEVICE_PATH, pin);
178 pwm->fd_run = open(g_buffer, O_WRONLY);
181 AbortBool("Couldn't open %s for PWM%d - %s", g_buffer, pin, strerror(errno));
184 sprintf(g_buffer, "%s/pwm%d/polarity", PWM_DEVICE_PATH, pin);
185 pwm->fd_polarity = open(g_buffer, O_WRONLY);
186 if (pwm->fd_polarity < 0)
189 AbortBool("Couldn't open %s for PWM%d - %s", g_buffer, pin, strerror(errno));
192 sprintf(g_buffer, "%s/pwm%d/period_ns", PWM_DEVICE_PATH, pin);
193 pwm->file_period = fopen(g_buffer, "w");
194 if (pwm->file_period == NULL)
197 close(pwm->fd_polarity);
198 AbortBool("Couldn't open %s for PWM%d - %s", g_buffer, pin, strerror(errno));
201 sprintf(g_buffer, "%s/pwm%d/duty_ns", PWM_DEVICE_PATH, pin);
202 pwm->file_duty = fopen(g_buffer, "w");
203 if (pwm->file_duty == NULL)
206 close(pwm->fd_polarity);
207 fclose(pwm->file_period);
208 AbortBool("Couldn't open %s for PWM%d - %s", g_buffer, pin, strerror(errno));
211 // Don't buffer the streams
212 setbuf(pwm->file_period, NULL);
213 setbuf(pwm->file_duty, NULL);
215 pwm->initialised = true;
216 Log(LOGDEBUG, "Exported PWM%d", pin);
222 * Unexport a PWM pin and close its file descriptors
223 * @param pin - The sysfs pin number
225 void PWM_Unexport(int pin)
227 if (pin < 0 || pin >= PWM_NUM_PINS)
229 Abort("Invalid PWM pin number %d specified.", pin);
232 PWM_Pin *pwm = &g_pwm[pin];
233 if (!pwm->initialised)
235 Abort("PWM %d not initialised", pin);
238 // Close the file descriptors
239 close(pwm->fd_polarity);
240 //Stop it, if it's still running
241 pwrite(pwm->fd_run, "0", 1, 0);
243 fclose(pwm->file_period);
244 fclose(pwm->file_duty);
246 pwm->initialised = false;
248 // Try unexport the pin, doesn't matter if it's already unexported.
249 sprintf(g_buffer, "%s/unexport", PWM_DEVICE_PATH);
250 FILE * file_unexport = fopen(g_buffer, "w");
251 if (file_unexport == NULL)
253 Abort("Couldn't open %s to unexport PWM pin %d - %s", g_buffer, pin, strerror(errno));
255 fprintf(file_unexport, "%d\n", pin);
256 fclose(file_unexport);
260 * Initialise ADC structures
261 * @param pin The ADC pin number
263 bool ADC_Export(int pin)
265 if (pin < 0 || pin >= ADC_NUM_PINS)
267 AbortBool("Invalid ADC pin %d specified.", pin);
269 else if (g_adc[pin].initialised)
271 Log(LOGNOTE, "ADC %d already initialised", pin);
275 sprintf(g_buffer, "%s/in_voltage%d_raw", g_options.adc_device_path, pin);
276 g_adc[pin].fd_value = open(g_buffer, O_RDONLY);
277 if (g_adc[pin].fd_value <0)
279 AbortBool("Couldn't open ADC %d device file %s - %s", pin, g_buffer, strerror(errno));
282 g_adc[pin].initialised = true;
283 Log(LOGDEBUG, "Opened ADC %d", pin);
289 * @param pin The ADC pin number
291 void ADC_Unexport(int pin)
293 if (pin < 0 || pin >= ADC_NUM_PINS)
295 Abort("Invalid ADC pin %d specified.", pin);
297 else if (!g_adc[pin].initialised)
299 Abort("ADC %d already uninitialised", pin);
302 close(g_adc[pin].fd_value);
303 g_adc[pin].fd_value = -1;
304 g_adc[pin].initialised = false;
309 * @param pin - The pin to set. MUST have been exported before calling this function.
311 bool GPIO_Set(int pin, bool value)
313 if (pin < 0 || pin > GPIO_MAX_NUMBER || g_pin_gpio_to_index[pin] == 128)
315 AbortBool("Not a useable pin number: %d", pin);
318 GPIO_Pin *gpio = &g_gpio[g_pin_gpio_to_index[pin]];
319 if (!gpio->initialised)
321 AbortBool("GPIO %d is not initialised.", pin);
323 //Set the pin direction
324 if (pwrite(gpio->fd_direction, "out", 3, 0) != 3)
326 AbortBool("Couldn't set GPIO %d direction - %s", pin, strerror(errno));
329 char c = value ? '1' : '0';
330 if (pwrite(gpio->fd_value, &c, 1, 0) != 1)
332 AbortBool("Couldn't read GPIO %d value - %s", pin, strerror(errno));
339 * Read from a GPIO Pin
340 * @param pin - The pin to read
341 * @param result A pointer to store the result
342 * @return true on success, false otherwise
344 bool GPIO_Read(int pin, bool *result)
346 if (pin < 0 || pin > GPIO_MAX_NUMBER || g_pin_gpio_to_index[pin] == 128)
348 AbortBool("Not a useable pin number: %d", pin);
351 GPIO_Pin *gpio = &g_gpio[g_pin_gpio_to_index[pin]];
352 if (!gpio->initialised)
354 AbortBool("GPIO %d is not initialised.", pin);
357 if (pwrite(gpio->fd_direction, "in", 2, 0) != 2)
359 AbortBool("Couldn't set GPIO %d direction - %s", pin, strerror(errno));
363 if (pread(gpio->fd_value, &c, 1, 0) != 1)
365 AbortBool("Couldn't read GPIO %d value - %s", pin, strerror(errno));
368 *result = (c == '1');
374 * @param pin - The sysfs pin number
375 * @param polarity - if true, pin is active high, else active low
376 * @param period - The period in ns
377 * @param duty - The time the pin is active in ns
379 bool PWM_Set(int pin, bool polarity, long period, long duty)
381 Log(LOGDEBUG, "Pin %d, pol %d, period: %lu, duty: %lu", pin, polarity, period, duty);
383 if (pin < 0 || pin >= PWM_NUM_PINS)
385 AbortBool("Invalid PWM pin number %d specified.", pin);
388 PWM_Pin *pwm = &g_pwm[pin];
389 if (!pwm->initialised)
391 AbortBool("PWM %d is not initialised.", pin);
394 // Have to stop PWM before changing it
395 if (pwrite(pwm->fd_run, "0", 1, 0) != 1)
397 AbortBool("Couldn't stop PWM %d - %s", pin, strerror(errno));
400 char c = polarity ? '1' : '0';
401 if (pwrite(pwm->fd_polarity, &c, 1, 0) != 1)
403 AbortBool("Couldn't set PWM %d polarity - %s", pin, strerror(errno));
406 //This must be done first, otherwise period/duty settings can conflict
407 if (fwrite("0", 1, 1, pwm->file_duty) < 1)
409 AbortBool("Couldn't zero the duty for PWM %d - %s", pin, strerror(errno));
412 if (fprintf(pwm->file_period, "%lu", period) < 0)
414 AbortBool("Couldn't set period for PWM %d - %s", pin, strerror(errno));
418 if (fprintf(pwm->file_duty, "%lu", duty) < 0)
420 AbortBool("Couldn't set duty cycle for PWM %d - %s", pin, strerror(errno));
424 if (pwrite(pwm->fd_run, "1", 1, 0) != 1)
426 AbortBool("Couldn't start PWM %d - %s", pin, strerror(errno));
433 * Deactivate a PWM pin
434 * @param pin - The syfs pin number
435 * @return true on success, false otherwise
437 bool PWM_Stop(int pin)
439 if (pin < 0 || pin >= PWM_NUM_PINS)
441 AbortBool("Invalid PWM pin number %d specified.", pin);
443 else if (!g_pwm[pin].initialised)
445 AbortBool("PWM %d is not initialised.", pin);
448 if (pwrite(g_pwm[pin].fd_run, "0", 1, 0) != 1)
450 AbortBool("Couldn't stop PWM %d - %s", pin, strerror(errno));
458 * @param id - The ID of the ADC pin to read
459 * @param value - A pointer to store the value read from the ADC
460 * @returns - The true if succeeded, false otherwise.
462 bool ADC_Read(int id, int *value)
464 char adc_str[ADC_DIGITS] = {0};
466 if (id < 0 || id >= ADC_NUM_PINS)
468 AbortBool("Invalid ADC pin %d specified.", id);
470 else if (!g_adc[id].initialised)
472 AbortBool("ADC %d is not initialised.", id);
475 if (pread(g_adc[id].fd_value, adc_str, ADC_DIGITS-1, 0) == -1)
477 AbortBool("ADC %d read failed: %s", id, strerror(errno));
480 *value = strtol(adc_str, NULL, 10);
485 //For running on systems that are not the BBB
486 bool True_Stub(void *arg, ...) { return true; }
487 bool ADC_Read_Stub(int *val, ...) { *val = 0; return true; }
488 bool GPIO_Read_Stub(bool *val, ...) { *val = false; return true; }