Notes About Working with Various Arduino & Netduino Microcontroller Boards

Wednesday, June 3, 2015

C Program to Read a 4x4 Keypad Connected to an Arduino Galileo

The Arduino keypad library (written in C++) works with Arduino sketches running on the Galileo, but I wanted to access a 4x4 keypad from a standard C program.  The following code shows how to configure the GPIO pins and read the keypad. The program just reads keypresses and prints the characters to the console (at the command line, not the Arduino serial monitor).

The biggest challenge was getting digital pins 2 through 9 configured correctly. The configPins() function in the code below handles the necessary GPIO configuration.

For this sketch, which runs from the Linux command line, to function correctly, make sure that you don't have an Arduino sketch running that accesses digital pins 2 through 9.  Uploading the BareMinimum via the Arduino IDE or deleting the files in the /sketch directory at the command line should take care of any conflicts.

To compile the code on the Galileo, you will need a Yocto image that includes gcc or have a look at this post about installing gcc on Clanton using opkg.

Connections


With the 4x4 keypad face up and the wires pointing down, connect the wires to the Arduino Galileo's digital pins 2 through 9.  It could be connected with wires running from right to left, but then the GPIO numbers and 2-dimensional layout array would need to be revised.

Code


#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

#define KEY_PRESS_DELAY 100000 // Pause microseconds to debounce
                               // Adjust to control repeat rate
#define KEYPAD_COLUMNS 4
#define KEYPAD_ROWS    4

// Base of path to GPIO devices 
const char *gpioBase = "/sys/class/gpio"; 

// sysfs gpio# for       col3   col2  col1  col0
const char *gpioCols[] = {"32", "18", "28", "17"};
// sysfs gpio# for       row3   row2  row1  row0
const char *gpioRows[] = {"24", "27", "26", "19"};
// sysfs gpio# for mux for col3 and col2
const char *gpioMux[]  = {"31", "30"};

/* This layout assumes that keypad is connected to Galileo with leftmost 
   pin (when looking at front of keypad with wires pointed downward) to 
   Galileo digital 2. The other wires are connected in order on digital 
   pins 3 through 9. 
*/
const char layout[KEYPAD_ROWS][KEYPAD_COLUMNS] = {
   {'1','2','3','A'},
   {'4','5','6','B'},
   {'7','8','9','C'},
   {'*','0','#','D'}
};

// Function declarations
void configPins();  // Configure Galileo's GPIO pins 
char getKey();      // Read keypres

int main() {
   configPins();
   while(1) {
      printf("%c\n", getKey());
   }
   return 0;
}

void configPins() {
   /* Configuration involves the following:
1. Export gpio ports for sysfs access
        2. Set direction (out) for mux ports
        3. Set  value (1) for mux ports 
           that control pins 2 & 3
        4. Set port direction (out) 
        5. Set value (1) for columns
        6. Set drive mode for columns to pullup
        7. Set direction (in) for rows
        8. Set drive mode for rows to pullup

        Note that all configuration values are passed as strings.
  
        I have left out error handling if open() fails.
   */
   // path string for gpio devices & settings
   char path[30];
   int fd; // file handle
   int c;  // loop variable
   // Export gpio ports to sysfs
   sprintf(path, "%s/export", gpioBase);
   fd = open(path, O_WRONLY);
   // Loop to export pins for columns & rows
   for(c = 0; c < KEYPAD_COLUMNS; c++) {
      // export column pin
      write(fd, gpioCols[c], 2);
   }
   for(c = 0; c < KEYPAD_ROWS; c++) {
      // export row pin
      write(fd, gpioRows[c], 2);
   }
   // export multiplex pins
   for(c = 0; c < 2; c++) {
      write(fd, gpioMux[c], 2);
   }
   close(fd);
   // Set direction for the 2 mux ports
   for(c = 0; c < 2; c++) {
      sprintf(path, "%s/gpio%s/direction", gpioBase, gpioMux[c]);
      fd = open(path, O_WRONLY);
      write(fd, "out", 3);
      close(fd);
   }
   // Set value (1) for the 2 mux ports
   for(c = 0; c < 2; c++) {
      sprintf(path, "%s/gpio%s/value", gpioBase, gpioMux[c]);
      // Route pins 2 & 3 through Cypress CY8C9540A rather than Quark by
      // setting value to 1. This makes pins 2 & 3 avail. as sysfs 
      // gpio# 32 & 18.
      fd = open(path, O_WRONLY);
      write(fd, "1", 1);
      close(fd);
   }
   // Set direction (out) for ports for columns
   for(c = 0; c < KEYPAD_COLUMNS; c++) {
      sprintf(path, "%s/gpio%s/direction", gpioBase, gpioCols[c]);
      fd = open(path, O_WRONLY);
      write(fd, "out", 3);
      close(fd);
   }
   // Set colum pins to HIGH (1)
   for(c = 0; c < KEYPAD_COLUMNS; c++) {
      sprintf(path,"%s/gpio%s/value", gpioBase, gpioCols[c]);
      fd = open(path, O_WRONLY);
      write(fd, "1", 1);
      close(fd);
   }
   // Set drive mode for column pins to pullup
   for(c = 0; c < KEYPAD_COLUMNS; c++) {
      sprintf(path, "%s/gpio%s/drive", gpioBase, gpioCols[c]);
      fd = open(path, O_WRONLY);
      write(fd, "pullup", 6);
      close(fd);
   }
   // Set direction of row pins to input (in)
   for(c = 0; c < KEYPAD_ROWS; c++) {
      sprintf(path, "%s/gpio%s/direction", gpioBase, gpioRows[c]);
      fd = open(path, O_WRONLY);
      write(fd, "in", 2);
      close(fd);
   }
   // Set drive mode for row pins to pullup
   for(c = 0; c < KEYPAD_ROWS; c++) {
      sprintf(path, "%s/gpio%s/drive", gpioBase, gpioRows[c]);
      fd = open(path, O_WRONLY);
      write(fd, "pullup", 6);
      close(fd);
   }
}

char getKey() {
   // Need 2 file handles for nested loops to read columns & rows
   int fd, fd2;
   // String to hold path to gpio device & setting  
   char path[30];
   // Initialize key to space so loop runs until key pressed
   char key = ' ';
   int c, d; // loop variables
   // Wait for a key
   while(key == ' ') {
      // Loop through columns
      for(c = 0; c < 4; c++) {
         sprintf(path, "%s/gpio%s/value", gpioBase, gpioCols[c]);
         fd = open(path, O_WRONLY);
         // Set column LOW (0)
         write(fd, "0", 1);
         // Loop through rows & check for 0 to identify key
         int d;
         for(d = 0; d < 4; d++) {
            char rowPath[35];
            char value[1];
            sprintf(rowPath, "%s/gpio%s/value", gpioBase, 
                     gpioRows[d]);
            fd2 = open(rowPath, O_RDONLY);
            read(fd2, value, 1);
            close(fd2);
            if(value[0] == '0') {
               // wait & read again to confirm key press
               usleep(KEY_PRESS_DELAY);
               read(fd2, value, 1);
               if(value[0] == '0') {            
                  key = layout[c][d];
               }
            }
         }
         // Set column pin back to HIGH (1)
         write(fd, "1", 1);
         close(fd);
      }
   }
   return key;
}

Note: This code uses usleep(), which is deprecated by POSIX, so compiling with the -std=c99 switch won't work.  I tried to use nanosleep(), but I ran into problems: nanosleep() kept coming up as undeclared and the definition for struct timespec wasn't found. 

No comments:

Post a Comment