Saturday, October 19, 2013

BeagleBone GPIO in C for iGaging DRO

I'm currently trying to build a DRO interface to iGaging scales based on Yuriy's work : http://www.yuriystoys.com/p/android-dro.html.  As a first step, I have to duplicate the signal pulses from the iGaging controller using the BeagleBone Black GPIOs.

I scoped out my iGaging controller and found that it is providing clock pulses of 24uSec pulse width with a duty cycle of 128uSec.  The iGaging protocol is basically a synchronous clock read only protocol with 21 bits.

After Google'ing many, many sites, (and no, I can't use Python, as I need microsecond level control of the pulses, Python would definitely be too slow), I found some sites that recommend using buffered writes (fopen/fwrite).  Attempting that at first led me down an unreliable path.  I eventually changed the write methods to use low level unix i/o (open/write), and that gave me much better results.

Here are some oscilloscope screenshots showing multiple runs of the code.  Note that there is a random variable pulse delay that occurs on occasion.  That's probably something going on in the Linux O/S (which is why Linux is not meant to be a real time operating system!).  I'll eventually rewrite the code to use the BeagleBone's PRU (Programmable Realtime Unit - http://processors.wiki.ti.com/index.php/Programmable_Realtime_Unit ) but I'm going to see if the iGaging scales will work even with the extra pulse delay.

10 Pulses correctly spaced

Random delay before last pulse

Delay on the high portion of the 2nd pulse

Delay in the middle of the pulse train


Here's the code I'm using right now to test (feel free to use it as a basis for your GPIO code):
 //  
 // BB-DRO.c - Read iGaging Scales on a BeagleBone Black  
 //  
 // Copyright (C) 2013, Exadler Technologies Inc.  
 // http://www.exadler.com  
 //  
 // Based on a similar work (ArduinoDRO_V2_2) by Yuriy Krushelnytskiy  
 // http://www.yuriystoys.com  
 //  
 // This code is is licensed under   
 // Creative Commons Attribution-ShareAlike 3.0 Unported License.   
 //   
 //  
 //  
 #include <fcntl.h>  
 #include <unistd.h>  
 #include <stdio.h>  
 #include <stdlib.h>  
 #include <string.h>  
 //  
 // Setup GPIO and return a unix file descriptor handle to  
 // that GPIO. The user must close that file descriptor when finished  
 //   
 int setupGPIO(char *pszGPIO, char *pszDir)  
 {  
   int fd;  
   int len;  
   int result;  
   char buf[120];  
   if (pszGPIO == NULL) {  
     printf("Error: pszGPIO is NULL!\n");  
     exit(1);  
   }  
   if (pszDir == NULL) {  
     printf("Error: pszDir is NULL!\n");  
     exit(1);  
   }  
   if ((strcmp(pszDir,"in") != 0) && (strcmp(pszDir,"out") != 0)) {  
     printf("Error: pszDir must be either 'in' or 'out'\n");  
     exit(1);  
   }  
   len = strlen(pszGPIO);  
   if ((len < 1) || (len > 40)) {  
     printf("Error: pszGPIO = %s is too long or too short\n",pszGPIO);  
     exit(1);  
   }  
   // First enable GPIO30  
   fd = open("/sys/class/gpio/export",O_WRONLY | O_APPEND);  
   if (fd == -1) {  
     printf("Cannot open /sys/class/gpio/export to setup GPIO\n");  
     exit(1);  
   }  
   result = write( fd, pszGPIO, len );  
   if (result != len) {  
     printf("Cannot write to /sys/class/gpio/export to setup GPIO %s\n",pszGPIO);  
     printf("Wrote %d bytes\n",result);  
     close(fd);  
     // Remove exiting here for now  
     // as it will fail if the GPIO was already exported  
     // so just ignore the error  
 //    exit(1);  
   }  
   close(fd);  
   // Now set GPIO 30 direction to output  
   sprintf(buf, "/sys/class/gpio/gpio%s/direction", pszGPIO);  
   fd = open(buf,O_WRONLY);  
   if (fd == -1) {  
     printf("Cannot open %s to setup GPIO\n",buf);  
     exit(1);  
   }  
   result = write( fd, pszDir, strlen(pszDir) );  
   if (result != strlen(pszDir)) {  
     printf("Cannot write to %s setup GPIO\n", buf);  
     close(fd);  
     exit(1);  
   }  
   close(fd);    
   // Now open the GPIO value for writing  
   sprintf(buf, "/sys/class/gpio/gpio%s/value", pszGPIO);  
   fd = open(buf,O_WRONLY);  
   if (fd == -1) {  
     printf("Cannot open %s to write to the GPIO\n", buf);  
     exit(1);  
   }  
   return fd;  
 }  
 //  
 // freeGPIO - release the GPIO port  
 //   
 void freeGPIO(char *pszGPIO)  
 {  
   int fd;  
   int len;  
   int result;  
   if (pszGPIO == NULL) {  
     printf("Error: pszGPIO is NULL!\n");  
     exit(1);  
   }  
   // unexport the GPIO  
   // First open the unexport file  
   fd = open("/sys/class/gpio/unexport",O_WRONLY );  
   if (fd == -1) {  
     printf("Cannot open /sys/class/gpio/unexport to free GPIO\n");  
     exit(1);  
   }  
   len = strlen(pszGPIO);  
   result = write( fd, pszGPIO, len );  
   if (result != len) {  
     printf("Cannot write to /sys/class/gpio/unexport to setup GPIO %s\n",pszGPIO);  
     printf("Wrote %d bytes\n",result);  
     close(fd);  
     // Remove exiting here for now  
     // as it will fail if the GPIO was already exported  
     // so just ignore the error  
 //    exit(1);  
   }  
   close(fd);  
 }  
 inline void tickTock(int fd)  
 {  
   int result;  
   int j;  
   if (fd == -1) {  
     printf("Error: tickTock called with bad fd\n");  
     exit(1);  
   }  
   for (j=0;j<1000;j++) ;  
   result = write( fd, "1" , 1);  
   if (result != 1) {  
     printf("Cannot write to fd to set GPIO\n");  
     close(fd);  
     exit(1);  
   }  
   for (j=0;j<1000;j++) ;  
   result = write( fd, "0" , 1);  
   if (result != 1) {  
     printf("Cannot write to fd to set GPIO\n");  
     close(fd);  
     exit(1);  
   }  
 }  
 int main(int argc, char *argv[])  
 {  
   int fd30;  
   // First enable GPIO30  
   fd30 = setupGPIO("30","out");  
   int i;  
   for (i=0; i < 10; i++) {  
     tickTock(fd30);  
   }  
   close(fd30);    
 //  freeGPIO("30");  
   return 0;  
 }