semifix. PWM stuff mostly works. ADC should work.
[matches/MCTX3420.git] / server / bbb_pin.c
1 /**
2  * @file bbb_pin.c
3  * @purpose Implementation of BBB pin control functions and structures
4  * THIS CODE IS NOT THREADSAFE
5  */
6
7 #include "bbb_pin.h"
8
9 #include <sys/types.h>
10 #include <sys/stat.h>
11 #include <fcntl.h>
12 #include <ctype.h>
13 #include "options.h"
14
15 /**
16  * Structure to represent a GPIO pin
17  * Note: Only accessable to this file; to use the functions pass a GPIOId
18  */
19 typedef struct
20 {
21         int fd_value;
22         int fd_direction;
23 } GPIO_Pin;
24
25 /**
26  * Structure to represent an ADC pin
27  * Note: Only accessable to this file; to use the functions pass a ADCId
28  */
29 typedef struct
30 {
31         int fd_value;
32 } ADC_Pin;
33
34 /**
35  * Structure to represent a PWM pin
36  * Note: Only accessable to this file; to use the functions pass a PWMId
37  */
38 typedef struct
39 {
40         int fd_run;
41         FILE * file_duty;
42         FILE * file_period;
43         int fd_polarity;
44 } PWM_Pin;
45
46 /** Array of GPIO pins **/
47 static GPIO_Pin g_gpio[GPIO_NUM_PINS] = {{0}};
48 /** Array of ADC pins **/
49 static ADC_Pin g_adc[ADC_NUM_PINS] = {{0}};
50 /** Array of PWM pins **/
51 static PWM_Pin g_pwm[PWM_NUM_PINS] = {{0}};
52
53 static char g_buffer[BUFSIZ] = "";
54
55
56 /**
57  * Export a GPIO pin and open the file descriptors
58  */
59 void GPIO_Export(int pin)
60 {
61         if (pin < 0 || pin >= GPIO_INDEX_SIZE || g_gpio_to_index[pin] == 128)
62         {
63                 Abort("Not a useable pin number: %d", pin);
64         }
65
66         GPIO_Pin *gpio = &g_gpio[g_gpio_to_index[pin]];
67         // Export the pin
68         sprintf(g_buffer, "%s/export", GPIO_DEVICE_PATH);
69         FILE * export = fopen(g_buffer, "w");
70         if (export == NULL)
71         {
72                 Abort("Couldn't open %s to export GPIO pin %d - %s", g_buffer, pin, strerror(errno));
73         }
74
75         fprintf(export, "%d", pin);     
76         fclose(export);
77         
78         // Setup direction file descriptor
79         sprintf(g_buffer, "%s/gpio%d/direction", GPIO_DEVICE_PATH, pin);
80         gpio->fd_direction = open(g_buffer, O_RDWR);
81         if (gpio->fd_direction < 0)
82         {
83                 Abort("Couldn't open %s for GPIO pin %d - %s", g_buffer, pin, strerror(errno));
84         }
85
86
87         // Setup value file descriptor
88         sprintf(g_buffer, "%s/gpio%d/value", GPIO_DEVICE_PATH, pin);
89         gpio->fd_value = open(g_buffer, O_RDWR);
90         if (gpio->fd_value < 0)
91         {
92                 Abort("Couldn't open %s for GPIO pin %d - %s", g_buffer, pin, strerror(errno));
93         }
94
95         Log(LOGDEBUG, "Exported GPIO%d", pin);
96         //sleep(1);
97 }
98
99 /**
100  * Unexport a GPIO pin and close its' file descriptors
101  */
102 void GPIO_Unexport(int pin)
103 {
104         if (pin < 0 || pin >= GPIO_INDEX_SIZE || g_gpio_to_index[pin] == 128)
105         {
106                 Abort("Not a useable pin number: %d", pin);
107         }
108
109         GPIO_Pin *gpio = &g_gpio[g_gpio_to_index[pin]];
110         // Close file descriptors
111         close(gpio->fd_value);
112         close(gpio->fd_direction);
113
114         // Unexport the pin
115
116         if (g_buffer[0] == '\0')
117                 sprintf(g_buffer, "%s/unexport", GPIO_DEVICE_PATH);     
118         FILE * export = fopen(g_buffer, "w");
119         if (export == NULL)
120         {
121                 Abort("Couldn't open %s to export GPIO pin %d - %s", g_buffer, pin, strerror(errno));
122         }
123
124         fprintf(export, "%d", pin);     
125         fclose(export);
126 }
127
128
129 /**
130  * Initialise all PWM pins and open file descriptors
131  * @param pin - The sysfs pin number
132  */
133 void PWM_Export(int pin)
134 {
135         if (pin < 0 || pin >= PWM_NUM_PINS)
136         {
137                 Abort("Invalid PWM pin number %d specified.", pin);
138         }
139
140         PWM_Pin *pwm = &g_pwm[pin];
141
142         if (pwm->file_duty != NULL)
143         {
144                 Abort("PWM %d already exported.", pin);
145         }
146
147         // Open file descriptors
148         sprintf(g_buffer, "%s/pwm%d/run", PWM_DEVICE_PATH, pin);
149         pwm->fd_run = open(g_buffer, O_WRONLY);
150         if (pwm->fd_run < 0)
151         {
152                 Abort("Couldn't open %s for PWM%d - %s", g_buffer, pin, strerror(errno));
153         }
154
155         sprintf(g_buffer, "%s/pwm%d/polarity", PWM_DEVICE_PATH, pin);
156         pwm->fd_polarity = open(g_buffer, O_WRONLY);
157         if (pwm->fd_polarity < 0)
158         {
159                 Abort("Couldn't open %s for PWM%d - %s", g_buffer, pin, strerror(errno));
160         }
161
162         sprintf(g_buffer, "%s/pwm%d/period_ns", PWM_DEVICE_PATH, pin);
163         pwm->file_period = fopen(g_buffer, "w");
164         if (pwm->file_period == NULL)
165         {
166                 Abort("Couldn't open %s for PWM%d - %s", g_buffer, pin, strerror(errno));
167         }
168
169         sprintf(g_buffer, "%s/pwm%d/duty_ns", PWM_DEVICE_PATH, pin);
170         pwm->file_duty = fopen(g_buffer, "w");
171         if (pwm->file_duty == NULL)
172         {
173                 Abort("Couldn't open %s for PWM%d - %s", g_buffer, pin, strerror(errno));
174         }
175
176         // Don't buffer the streams
177         setbuf(pwm->file_period, NULL);
178         setbuf(pwm->file_duty, NULL);   
179
180         Log(LOGDEBUG, "Exported PWM%d", pin);
181 }
182
183 /**
184  * Unexport a PWM pin and close its file descriptors
185  * @param pin - The sysfs pin number
186  */
187 void PWM_Unexport(int pin)
188 {
189         if (pin < 0 || pin >= PWM_NUM_PINS)
190         {
191                 Abort("Invalid PWM pin number %d specified.", pin);
192         }
193
194         PWM_Pin *pwm = &g_pwm[pin];
195         // Close the file descriptors
196         if (pwm->fd_polarity != -1)
197                 close(pwm->fd_polarity);
198         if (pwm->fd_run != -1)
199                 close(pwm->fd_run);
200         if (pwm->file_period != NULL)
201                 fclose(pwm->file_period);
202         if (pwm->file_duty != NULL)
203                 fclose(pwm->file_duty);
204
205         //So that another call to PWM_Unexport is OK.
206         pwm->fd_polarity = pwm->fd_run = -1;
207         pwm->file_period = pwm->file_duty = NULL;
208 }
209
210
211 /**
212  * Initialise ADC structures
213  * @param pin The ADC pin number
214  */
215 void ADC_Export(int pin)
216 {
217         if (pin < 0 || pin >= ADC_NUM_PINS)
218         {
219                 Abort("Invalid ADC pin %d specified.", pin);
220         }
221
222         sprintf(g_buffer, "%s/in_voltage%d_raw", g_options.adc_device_path, pin);
223         g_adc[pin].fd_value = open(g_buffer, O_RDONLY);
224         if (g_adc[pin].fd_value <0)
225         {
226                 Abort("Couldn't open ADC %d device file %s - %s", pin, g_buffer, strerror(errno));
227         }
228         Log(LOGDEBUG, "Opened ADC %d", pin);
229 }
230
231 /**
232  * Unexport ADC pins
233  * @param pin The ADC pin number
234  */
235 void ADC_Unexport(int pin)
236 {
237         if (pin < 0 || pin >= ADC_NUM_PINS)
238         {
239                 Abort("Invalid ADC pin %d specified.", pin);
240         }
241         if (pin != -1)
242                 close(g_adc[pin].fd_value);     
243
244         g_adc[pin].fd_value = -1;
245 }
246
247 /**
248  * Set a GPIO pin
249  * @param pin - The pin to set. MUST have been exported before calling this function.
250  */
251 void GPIO_Set(int pin, bool value)
252 {
253         if (pin < 0 || pin >= GPIO_INDEX_SIZE || g_gpio_to_index[pin] == 128)
254         {
255                 Abort("Not a useable pin number: %d", pin);
256         }
257
258         GPIO_Pin *gpio = &g_gpio[g_gpio_to_index[pin]];
259
260         if (pwrite(gpio->fd_direction, "out", 3, 0) != 3)
261         {
262                 Abort("Couldn't set GPIO %d direction - %s", pin, strerror(errno));
263         }
264
265         char c = '0' + (value);
266         if (pwrite(gpio->fd_value, &c, 1, 0) != 1)
267         {
268                 Abort("Couldn't read GPIO %d value - %s", pin, strerror(errno));
269         }
270
271 }
272
273 /** 
274  * Read from a GPIO Pin
275  * @param pin - The pin to read
276  */
277 bool GPIO_Read(int pin)
278 {
279         if (pin < 0 || pin >= GPIO_INDEX_SIZE || g_gpio_to_index[pin] == 128)
280         {
281                 Log(LOGERR, "Not a useable pin number: %d", pin);
282                 return false;
283         }
284
285         GPIO_Pin *gpio = &g_gpio[g_gpio_to_index[pin]];
286
287         if (pwrite(gpio->fd_direction, "in", 2, 0) != 2)
288                 Log(LOGERR,"Couldn't set GPIO %d direction - %s", pin, strerror(errno)); 
289         char c = '0';
290         if (pread(gpio->fd_value, &c, 1, 0) != 1)
291                 Log(LOGERR,"Couldn't read GPIO %d value - %s", pin, strerror(errno));
292
293         return (c == '1');
294
295 }
296
297 /**
298  * Activate a PWM pin
299  * @param pin - The sysfs pin number
300  * @param polarity - if true, pin is active high, else active low
301  * @param period - The period in ns
302  * @param duty - The time the pin is active in ns
303  */
304 void PWM_Set(int pin, bool polarity, long period, long duty)
305 {
306         Log(LOGDEBUG, "Pin %d, pol %d, period: %lu, duty: %lu", pin, polarity, period, duty);
307         
308         if (pin < 0 || pin >= PWM_NUM_PINS)
309         {
310                 Abort("Invalid PWM pin number %d specified.", pin);
311         }
312
313         PWM_Pin *pwm = &g_pwm[pin];
314
315         // Have to stop PWM before changing it
316         if (pwrite(pwm->fd_run, "0", 1, 0) != 1)
317         {
318                 Abort("Couldn't stop PWM%d - %s", pin, strerror(errno));
319         }
320
321         char c = polarity ? '1' : '0';
322         if (pwrite(pwm->fd_polarity, &c, 1, 0) != 1)
323         {
324                 Abort("Couldn't set PWM%d polarity - %s", pin, strerror(errno));
325         }
326
327         if (fprintf(pwm->file_period, "%lu", period) < 0)
328         {
329                 Abort("Couldn't set period for PWM%d - %s", pin, strerror(errno));
330         }
331
332         if (fprintf(pwm->file_duty, "%lu", duty) < 0)
333         {
334                 Abort("Couldn't set duty cycle for PWM%d - %s", pin, strerror(errno));
335         }
336
337         if (pwrite(pwm->fd_run, "1", 1, 0) != 1)
338         {
339                 Abort("Couldn't start PWM%d - %s", pin, strerror(errno));
340         }
341 }
342
343 /**
344  * Deactivate a PWM pin
345  * @param pin - The syfs pin number
346  */
347 void PWM_Stop(int pin)
348 {
349         if (pin < 0 || pin >= PWM_NUM_PINS)
350         {
351                 Abort("Invalid PWM pin number %d specified.", pin);
352         }
353
354         if (pwrite(g_pwm[pin].fd_run, "0", 1, 0) != 1)
355         {
356                 Abort("Couldn't stop PWM %d - %s", pin, strerror(errno));
357         }
358 }
359
360 /**
361  * Read an ADC value
362  * @param id - The ID of the ADC pin to read
363  * @returns - The reading of the ADC channel
364  */
365 int ADC_Read(int id)
366 {
367         char adc_str[ADC_DIGITS] = {0};
368
369         if (pread(g_adc[id].fd_value, adc_str, ADC_DIGITS-1, 0) == -1)
370         {
371                 Log(LOGERR, "ADC %d read failed: %s", id, strerror(errno));
372                 return 0;
373         }
374
375         return strtol(adc_str, NULL, 10);
376 }

UCC git Repository :: git.ucc.asn.au