Adafruit: 2-Axis Thumb Joystick

 Overview: 2 Wiring:  2 Code:    2 git/Compiles with make: 2 gcc works Demo:    2 Total:   10 Comments: Looks good.



Overview
The Adafruit: 2-Axis Thumb Joystick uses 2 10 k&Omega; pots which are adjusted by moving the joystick. The joystick and development board can be purchased from the Adafruit Website. The joystick is simplistic enough that Adafruit does not provide a datasheet.

From the Manufactorer: ''This mini-kit makes it easy to mount a PSP/Xbox-like thumb joystick to your project. The thumbstick is an analog joystick - more accurate and sensitive than just 'directional' joysticks - with a 'press in to select' button. Since it's analog, you'll need to analog reading pins on your microcontroller to determine X and Y. Having an extra digital input will let you read the switch. ''

Inputs and Outputs
The Adafruit joystick takes a supply voltage (Vs) of up to 5V. The analog outputs have a direct correlation to the resistance observed by the analog joystick. At the resting position, the resistance in both axis is at its middle resistance or 5k. At either extreme of the range of motion, there is either enough resistance for the analog inputs to register a 0 or so little resistance that the supply line to the analog input looks open.

BeagleBone Usage
The BeagleBone can support up to 8 analog inputs and has the ability to supply an analog 1.8 V voltage and an analog ground. I have connected my beagle as shown after soldering the joystick and some wires to the dev board:



The pinout is as shown:



Pins 36 and 38 I have wired to the 0-1.8V analog inputs. I have wired VCC to the Beagle's 1.8V analog VCC output(pin 32). The ground wire from the joystick is wired to the analog ground on the beagle(pin 34). The select GPIO signal I have routed to the GPIO_7 pin(pin 42).

I have written a small program which displays the x-Axis and y-Axis positions in the terminal. It can be found in a git repository here.

Using this configuration one can take the analog inputs with the GPIO selector and do about anything that requires two axises of movement and a selection knob. If you need some help interfacing with the analog ports or the GPIO ports, check out Dr. Yoder's succinct exercise at Analog and GPIO Info

Enjoy!

Control a 8x8 LED Matrix With The Joystick
Now we have the joystick as an input, and we can do something interesting if there's an output part. This is a mini project about using the joysick to control a 8x8 LED matrix.

The details about the LED matrix can be found here: Mini 8x8 LED Matrix w/I2C - Yellow The LED matrix has an i2c port. Wire up the LED matrix to the Beagle Bone by attaching the Vdd to the 3.3V + bus, the GND to the - bus, SDA to SDA (pin 20), and SCLK to SCLK (pin 19). The joystick can be wired up as described above. Here is a picture of the hardware configuration.



Our main goal is to create a bright dot on the LED matrix and use the joystick to move it. static __u16 dot_bmp[] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; pos = 0x80;

if(analog_value_x >= 4000 && row < 7) { row ++; dot_bmp[row] = pos; dot_bmp[row - 1] = 0x00; } if(analog_value_x <= 1000 && row > 0) { row --; dot_bmp[row] = pos; dot_bmp[row + 1] = 0x00; } if(analog_value_y >= 4000 && pos < 0x80) { pos = pos << 1; dot_bmp[row] = pos; } if(analog_value_y <= 1000 && pos > 0x01) { pos = pos >> 1; dot_bmp[row] = pos; } This is how we move the dot around: First, we create a bmp array whose first element is 0x80, which means only the dot on the right-down corner should be bright. 'analog_value_x' and 'analogy_value_y' are the two variables we set up to represent the voltage of x-axis and y-axis analog-in. 'pos' represents the position of the bright dot in the row. If 'analog_value_x' changes significantly and the bright dot does not reach the boundary of the 8x8 matrix, we should light up the dot next to it and turn off the dot itself.

for(i = 0; i < 8; i ++) block[i] = (bright_bmp[i] & 0xfe) >>1 | (bright_bmp[i] & 0x01) << 7;

res = i2c_smbus_write_i2c_block_data(file, daddress, 16, (__u8 *)block); For some reason the display is rotated one column, so after pre-unrotating the data, we can write the block data through the i2c bus. Thus, a dot can be lighten up on the matrix and can be moved as the way we want.



Next, we would like to make use of the button on the joystick. This is our objective: when pushing the button down, an interrupt is triggered and a frown face is presented on the LED matrix. Then if we move the dot across the face, it can be changed to a smile face.

According to the instructions on the joystick, the SEL shorts to ground when pressed. So the first important thing to do is to change gpio0_7 to pull-up mode. So that the interrupt can work correctly. Instructions about setting the interrupt can be found here.

In the interrupt, we set the smile face as the 'hidden_bmp', the frown_face as the 'bright_bmp', and set up a 'trace_bmp' to record the LEDs that the dot has passed. The LEDs we should light up is the LEDs in the bright_bmp that the dot hasn't passed, the LEDs in the hidden_bmp that the dot has already passed, and the dot itself. trace_bmp[row] = trace_bmp[row] | dot_bmp[row]; for(i = 0; i < 8; i ++) bright_bmp[i] = (bright_bmp[i] & ~trace_bmp[i]) | (hidden_bmp[i] & trace_bmp[i]) | dot_bmp[i];



We've reached our goal! Try to change the graphs and have fun!

The main code are listed as below. And all the resources including the header files can be found here. /***************************************************** Project Name: Joystick controlled 8x8 LED Matrix Author: Yue Zhang Date: 10/10/2012


 * 1) include 
 * 2) include 
 * 3) include 
 * 4) include 
 * 5) include 
 * 6) include 
 * 7) include 
 * 8) include 	// Defines signal-handling functions (i.e. trap Ctrl-C)
 * 9) include "i2c-dev.h"
 * 10) include "i2cbusses.h"
 * 11) include "gpio.h"

/**************************************************************** * Constants
 * 1) define SYSFS_GPIO_DIR "/sys/class/gpio"
 * 2) define POLL_TIMEOUT (100) /* 0.1 seconds */
 * 3) define MAX_BUF 64

/**************************************************************** * Global variables ****************************************************************/ int keepgoing = 1;	// Set to 0 when ctrl-c is pressed

/**************************************************************** * signal_handler ****************************************************************/ // Callback called when SIGINT is sent to the process (Ctrl-C) void signal_handler(int sig) {	printf( "Ctrl-C pressed, cleaning up and exiting..\n" ); keepgoing = 0; }

/**************************************************************** int analog_in(char *ain) {	FILE *fp; char ainPath[MAX_BUF]; char ainVal[MAX_BUF]; snprintf(ainPath, sizeof ainPath, "/sys/devices/platform/omap/tsc/%s", ain);
 * analog_in

if((fp = fopen(ainPath, "r")) == NULL){ printf("Can't open this pin, %s\n", ain); return 1; }

fgets(ainVal, MAX_BUF, fp);

fclose(fp); return atoi(ainVal); }

/**************************************************************** static void help(void) __attribute__ ((noreturn));
 * Check

static void help(void) {	fprintf(stderr, "Usage: my2cset (hardwired to bus 3, address 0x70)\n"); exit(1); }

static int check_funcs(int file, int size) {	unsigned long funcs;

/* check adapter functionality */ if (ioctl(file, I2C_FUNCS, &funcs) < 0) { fprintf(stderr, "Error: Could not get the adapter "			"functionality matrix: %s\n", strerror(errno)); return -1; }

switch (size) { case I2C_SMBUS_BYTE: if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE)) { fprintf(stderr, MISSING_FUNC_FMT, "SMBus send byte"); return -1; }		break;

case I2C_SMBUS_BYTE_DATA: if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { fprintf(stderr, MISSING_FUNC_FMT, "SMBus write byte"); return -1; }		break;

case I2C_SMBUS_WORD_DATA: if (!(funcs & I2C_FUNC_SMBUS_WRITE_WORD_DATA)) { fprintf(stderr, MISSING_FUNC_FMT, "SMBus write word"); return -1; }		break;

case I2C_SMBUS_BLOCK_DATA: if (!(funcs & I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)) { fprintf(stderr, MISSING_FUNC_FMT, "SMBus block write"); return -1; }		break; case I2C_SMBUS_I2C_BLOCK_DATA: if (!(funcs & I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) { fprintf(stderr, MISSING_FUNC_FMT, "I2C block write"); return -1; }		break; }

return 0; }

/**************************************************************** int main(int argc, char *argv[]) {	struct pollfd fdset[1]; int nfds = 1; int gpio_button_fd, ain_pin_x, ain_pin_y, timeout; char *buf[MAX_BUF]; char ainx[MAX_BUF], ainy[MAX_BUF]; unsigned int gpio_button; float analog_value_x = 0, analog_value_y = 0; int res, i2cbus, address, size, file; int value, daddress; char filename[20]; int force = 0, readback = 1; __u16 block[I2C_SMBUS_BLOCK_MAX], hidden_bmp[I2C_SMBUS_BLOCK_MAX], bright_bmp[I2C_SMBUS_BLOCK_MAX], trace_bmp[I2C_SMBUS_BLOCK_MAX]; int row = 0, pos = 0x80; int i, n = 0; static __u16 dot_bmp[] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; static __u16 smile_bmp[] = {0x3C, 0x42, 0x95, 0xA1, 0xA1, 0x95, 0x42, 0x3C}; static __u16 frown_bmp[] = {0x3C, 0x42, 0xA5, 0x91, 0x91, 0xA5, 0x42, 0x3C};
 * Main

// Set the signal callback for Ctrl-C signal(SIGINT, signal_handler); // Get the pin values gpio_button = 7; ain_pin_x = 6; ain_pin_y = 4;

// Set gpio0_7 to be pullup gpio_pullup;

// Set the gpio_button to be input and set its working pattern gpio_export(gpio_button); gpio_set_dir(gpio_button, 0); gpio_set_edge(gpio_button, "falling"); // Can be rising, falling or both gpio_button_fd = gpio_fd_open(gpio_button);

// Set up analog-in snprintf(ainx, sizeof ainx, "ain%d", ain_pin_x); snprintf(ainy, sizeof ainy, "ain%d", ain_pin_y); // Set time out timeout = POLL_TIMEOUT; // Set up i2c i2cbus = lookup_i2c_bus("3"); printf("i2cbus = %d\n", i2cbus); if (i2cbus < 0) help;

address = parse_i2c_address("0x70"); printf("address = 0x%2x\n", address); if (address < 0) help;

size = I2C_SMBUS_BYTE;

daddress = 0x21; if (daddress < 0 || daddress > 0xff) { fprintf(stderr, "Error: Data address invalid!\n"); help; }

file = open_i2c_dev(i2cbus, filename, sizeof(filename), 0); printf("file = %d\n", file); if (file < 0	 || check_funcs(file, size)	 || set_slave_addr(file, address, force)) exit(1);

daddress = 0x21;	// Start oscillator printf("writing: 0x%02x\n", daddress); res = i2c_smbus_write_byte(file, daddress);

daddress = 0x81;	// Display on, blinking off printf("writing: 0x%02x\n", daddress); res = i2c_smbus_write_byte(file, daddress);

daddress = 0xe7;	// Full brightness printf("writing: 0x%02x\n", daddress); res = i2c_smbus_write_byte(file, daddress);

daddress = 0x00;	// Start writing to address 0 printf("writing: 0x%02x\n", daddress); res = i2c_smbus_write_byte(file, daddress);

while (keepgoing) { memset((void*)fdset, 0, sizeof(fdset));

fdset[0].fd = gpio_button_fd; fdset[0].events = POLLPRI;

analog_value_x = analog_in(ainx); analog_value_y = analog_in(ainy);

poll(fdset, nfds, timeout);

if(analog_value_x >= 4000 && row < 7) { row ++; dot_bmp[row] = pos; dot_bmp[row - 1] = 0x00; }		if(analog_value_x <= 1000 && row > 0) { row --; dot_bmp[row] = pos; dot_bmp[row + 1] = 0x00; }		if(analog_value_y >= 4000 && pos < 0x80) { pos = pos << 1; dot_bmp[row] = pos; }		if(analog_value_y <= 1000 && pos > 0x01) { pos = pos >> 1; dot_bmp[row] = pos; }		// The LEDs we should light up is the LEDs in the bright_bmp that the dot hasn't passed, the LEDs in the hidden_bmp that the dot has already passed, and the dot itself. trace_bmp[row] = trace_bmp[row] | dot_bmp[row]; for(i = 0; i < 8; i ++) bright_bmp[i] = (bright_bmp[i] & ~trace_bmp[i]) | (hidden_bmp[i] & trace_bmp[i]) | dot_bmp[i];

// For some reason the display is rotated one column, so pre-unrotate the data. for(i = 0; i < 8; i ++) block[i] = (bright_bmp[i] & 0xfe) >>1 | (bright_bmp[i] & 0x01) << 7;

res = i2c_smbus_write_i2c_block_data(file, daddress, 16, (__u8 *)block);

// Button interrupt if (fdset[0].revents & POLLPRI) { lseek(fdset[0].fd, 0, SEEK_SET); // Read from the start of the file read(fdset[0].fd, buf, MAX_BUF); switch(n){ case(0):		// Initialize in the initial interrupt. for(i = 0; i < 8; i ++) { hidden_bmp[i] = 0x00; bright_bmp[i] = 0x00; trace_bmp[i] = 0x00; }	 					break; default: for(i = 0; i < 8; i ++) block[i] = (frown_bmp[i]&0xfe) >>1 | (frown_bmp[i]&0x01) << 7; res = i2c_smbus_write_i2c_block_data(file, daddress, 16, (__u8 *)block); sleep(1); for(i = 0; i < 8; i ++) { hidden_bmp[i] = smile_bmp[i]; bright_bmp[i] = frown_bmp[i]; trace_bmp[i] = 0x00; }						break; }						n++; }

fflush(stdout); }

gpio_fd_close(gpio_button_fd); return 0;

exit(0); }