Accueil > Arcade > Nano Raspicade : raspicade sur écran piTFT 2"8

Nano Raspicade : raspicade sur écran piTFT 2"8

dimanche 26 juillet 2015, par Yann

L’idée est de créer une nano borne arcade sur la base de la distribution Raspicade et de crée la caisse de la borne en impression 3D. Je me suis inspiré du projet CupCade d’Adafruit.

 Version 2 du projet

Nanocab RAsPiCade V2

 Le projet en vidéo

 Modèle de la Caisse pour impression 3D

http://www.thingiverse.com/thing:926435

 La CupCade

Le site Adafruit propose la micro borne CupCade http://www.adafruit.com/product/1783 qui embarque une distribution debian avec l’émulateur mame4all.

Le projet est très bien documenté sur la page https://learn.adafruit.com/cupcade-....

Ils utilisent quelques circuits imprimés développés pour l’occasion qu’ils vendent dans le kit de construction, notamment pour l’amplification audio et la conversion analogique numérique des commandes issues du joystick de type PS2/Xbox.

Dans notre cas nous allons utiliser un ampli audio 5V trouvé sur ebay (2€ fdp inc.) et une astuce pour réaliser un « joystick numérique » en utilisant un joystick analogique http://www.instructables.com/id/Sim...

 Achat de matériel

Pour la matériel, je suis parti d’un Pi 512 revB que j’avais en stock. Ce n’est pas de première jeunesse, mais overclocké à 1.1Ghz avec Raspicade, il permet de faire tourner de nombreux jeux. Ensuite j’ai acheté un écran piTFT 2.8" résistif de chez kubii à 44€ !!! on peut le trouver chez dealextreme à 20€ http://www.dx.com/fr/p/tft-2-8-touc..., des minis boutons chinois trouvé sur banggood (bon ils sont très durs, mais j’en ai pas trouvé ailleurs) http://www.banggood.com/3A-250V-Off.... La taille de la borne étant réduite, il n’est pas possible d’intégrer un joystick standard. Il faut donc utiliser un joystick analogique de type PS2 / Xbox. Je suis parti sur un joystick analogique de type ps2 / xbox http://www.banggood.com/Original-3D... avec son capuchon http://www.banggood.com/Controller-....

Ce type de joystick n’est pas adapté pour les entrées GPIO du Pi. Il va falloir faire quelques modifs en se basant sur l’article http://www.instructables.com/id/Sim.... Il s’agit d’une astuce qui permet d’utiliser ces joysticks analogiques de Xbox/Ps2 en arcade (transformer l’analogique en numérique booléen). Le schéma donné ne fonctionne pas sur les , en fait le branchement sur le pi est encore plus simple. Après avoir couper la piste du potard comme indiqué sur le site (pfiuuuu dur dur sans loupe), on remplace le +5v par la masse et on branche directement Up Down Left et Right sur les GPIO, exit les resistances de tirage.

2 choix sont possible pour la conversion :

  • sans électronique en coupant les pistes des potentiomètres du joystick comme expliqué dans http://www.instructables.com/id/Sim...
  • à l’aide d’un circuit mcp3004 ou 3008 pour réaliser le conversion A/N

J’ai choisi la première. Par contre il est nécessaire de modifier le circuit pour renvoyer les infos sur les GPIO :

On peut aussi réaliser un circuit imprimé comme celui présent dans la cupcade : https://learn.adafruit.com/cupcade-.... Ce circuit permet en plus l’amplification audio à l’aide d’un circuit ts922. J’utiliserai surement cela dans une version ultérieure de la nano borne.

 Préparation de la distribution Raspicade

La distribution raspicade ne support pas en standard l’écran PiTFT. On va devoir la modifier en changeant de noyau. Cela cassera le support du module XinMo. Cece n’est pas vraiment grave, car je ne l’utiliserai pas dans la suite.

Configuration de l’écran

Pour le support de l’ecran, J’ai suivi les tutos suivants :

Etapes :

  • On crée l’image disque raspicade à l’aide de l’image Pi1 issue de sourceforge
  • On installe un nouveau noyau qui prend en charge l’écran pitft 2.8 resistif
$ curl -SLs https://apt.adafruit.com/add | sudo bash
$ sudo apt-get install -y raspberrypi-bootloader
$ sudo apt-get -y install adafruit-pitft-helper

ensuite on edit le fichier /boot/config.txt et on y ajoute :

[pi1]
device_tree=bcm2708-rpi-b-plus.dtb
[pi2]
device_tree=bcm2709-rpi-2-b.dtb
[all]
dtparam=spi=on
dtparam=i2c1=on
dtparam=i2c_arm=on
dtoverlay=pitft28r,rotate=90,speed=32000000,fps=20

La variable “rotate” ordonne au pilote de réalise rune rotation de l’écran de 0 90 180 ou 270 degrés.

  • 0 est le mode “portrait (hauteur plus grande que largeur), et le bas de l’ecran est près des prises USB
  • 90 est le mode “paysage” et le bas de l’écran est proche de la sortie son jack 3”5
  • 180 est le mode portrait avec le haut de l’écran près des prises USB
  • 90 est le mode “paysage” et le haut de l’écran est proche de la sortie son jack 3“5
    La variable speed indique au pilote la vitesse de pilotage de l’écran. 32MHz (32000000) est un bon début, mais si vous avez des problèmes de stabilité d’images ou de rétro éclairage, il faudra réglé empiriquement cette valeur. On peut essayer 16MHz (16000000) et augmenter progressivement dans le cas ou l’écran est déporté du Pi.

On peut alors rebooter le raspberry Pi. Normalement au redémarrage, l’écran PiTFT s’allume blanc puis repasse au noir , c’est le signe qu’il est reconnu par le système.

le touchscreen n’est pas configuré, je n’en ai pas besoin pour une borne arcade. Si vous désirez l’activer, je vous renvoie vers l’adresse https://learn.adafruit.com/adafruit...

Si on veut faire de la console pure onsuivra le tutoriel suivant : https://learn.adafruit.com/adafruit...

Mais dans notre cas, nous voulons utiliser des programmes graphiques comme ES et les emulateurs. Pour cela on va devoir utiliser un programme qui va copier du fb0 au fb1 afin de faire croire au raspi que la sortie hdmi est présente mais on envoie tout sur le piTFT en spi.

Dans le cas de l’utilisation de la console, on changera les polices de caractères afin d’obtenir quelques choses de plus lisible :

sudo dpkg-reconfigure console-setup

Selectionnez UTF-8 puis Guess optimal character set, puis Terminus et 6x12 (framebuffer only).

Pour cela on relance la configuration à l’aide du programme adafruit-pitft-helper

sudo adafruit-pitft-helper -t 28r

A la question sur l’utilisation de la console, il faut répondre non. Ensuite on installe le programme qui va faire la copie du framebuffer primaire sur le second framebuffer (par exemple FBTFT).

sudo apt-get install cmake
git clone https://github.com/tasanakorn/rpi-fbcp
cd rpi-fbcp/
mkdir build
cd build/
cmake ..
make
sudo install fbcp /usr/local/bin/fbcp

On édite ensuite le fichier /boot/config.txt, et on ajoute les lignes suivantes :

 
hdmi_force_hotplug=1
hdmi_cvt=320 240 60 1 0 0 0
hdmi_group=2
hdmi_mode=87
dtoverlay=pitft28r,rotate=90,speed=80000000,fps=60

Les 4 lignes contenant « hdmi » permettent de configurer la sortie HDMI à la résolution de 320x240 pixels afin de correspondre à la résolution de l’écran PiTFT.
La ligne dtoverlay concerne le driver de l’écran PiTFT. Si la ligne n’est pas présente, ne l’ajoutez pas manuellement, mais relancez plutôt le programme adafruit-pitft-helper

Si elle est présente éditez simplement les valeurs des variables de vitesse speed (80000000) et fps (60). Ne touchez pas au reste de la ligne.

C’est une sorte d’overclockage de la connexion entre le Raspberry Pi et l’écran afin d’accélérer le ratio d’image par seconde. Les valeurs sont techniquement en dehors des valeurs. Si vous rencontrez des clognotements ou une image déformée, il faudra diminuer la valeur de vitesse (par exemple 64000000 voir beaucoup moins.).

OPn va maintenant lancer le programme fbcp au démarrage du système. Pour cela in crée le fichier /etc/init.d/afbcp

 
#!/bin/bash
### BEGIN INIT INFO
# Provides:          afbcp
# Required-Start:	
# Required-Stop:
# Should-Start:      
# Default-Start:     S
# Default-Stop:
# Short-Description: Start framebuffer copy utility
# Description:       Start framebuffer copy utility
### END INIT INFO
 
 
do_start () {
 
    /usr/local/bin/fbcp &
    exit 0
}
 
case "$1" in
  start|"")
    do_start
    ;;
  restart|reload|force-reload)
    echo "Error: argument '$1' not supported" >&2
    exit 3
    ;;
  stop)
    # No-op
    ;;
  status)
    exit 0
    ;;
  *)
    echo "Usage: afbcp [start|stop]" >&2
    exit 3
    ;;
esac
 
:

On met en place le service au démarrage :

 
$ sudo insserv  /etc/init.d/afbcp 

pour le désactiver :

 
$ sudo insserv -r /etc/init.d/afbcp 

Au démarrage, on obtient la vidéo de démarrage de Raspicade, puis EmulationStation.

Modification programme Retrogame

Sur le Pi 1 512 revB, il n’y a que 17 GPIO utilisables. De plus l’écran PiTFT utilise le bus SPI : « This design uses the hardware SPI pins (SCK, MOSI, MISO, CE0, CE1) as well as GPIO #25 and #24. » https://learn.adafruit.com/adafruit...

Ce qui fait que 7 Gpio sont inutilisables ... sur Ce Pi1 512 revB, cela ne laisse que 10 GPIO libres ce qui est insuffisant pour une panel 6 boutond, qui necessite au minimum 12 GPIOs avec les boutons Credits et Start P1. On peut cependant faire un essais avec 4 boutons et ainsi jouer avec les jeux du type Metal Slug, mais impossible de jouer à Street Fighter ou en encore Mazrvel Vs Capcom.

Il a donc fallu modifier en conséquence le programme retrogame qui refusait de se lancer car il utilisais les GPIO 24 et 25 utilisés par l’écran !

Si vous avez besoin de moins d’entrée, pas de pb, voici le pg retrogame modifié :

/*
ADAFRUIT RETROGAME UTILITY: remaps buttons on Raspberry Pi GPIO header
to virtual USB keyboard presses.  Great for classic game emulators!
Retrogame is interrupt-driven and efficient (usually under 0.3% CPU use)
and debounces inputs for glitch-free gaming.
 
Connect one side of button(s) to GND pin (there are several on the GPIO
header, but see later notes) and the other side to GPIO pin of interest.
Internal pullups are used; no resistors required.  Avoid pins 8 and 10;
these are configured as a serial port by default on most systems (this
can be disabled but takes some doing).  Pin configuration is currently
set in global table; no config file yet.  See later comments.
 
Must be run as root, i.e. 'sudo ./retrogame &' or configure init scripts
to launch automatically at system startup.
 
Requires uinput kernel module.  This is typically present on popular
Raspberry Pi Linux distributions but not enabled on some older varieties.
To enable, either type:
 
    sudo modprobe uinput
 
Or, to make this persistent between reboots, add a line to /etc/modules:
 
    uinput
 
Prior versions of this code, when being compiled for use with the Cupcade
or PiGRRL projects, required CUPCADE to be #defined.  This is no longer
the case; instead a test is performed to see if a PiTFT is connected, and
one of two I/O tables is automatically selected.
 
Written by Phil Burgess for Adafruit Industries, distributed under BSD
License.  Adafruit invests time and resources providing this open source
code, please support Adafruit and open-source hardware by purchasing
products from Adafruit!
 
 
Copyright (c) 2013 Adafruit Industries.
All rights reserved.
 
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
 
- Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.
 
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <sys/mman.h>
#include <linux/input.h>
#include <linux/uinput.h>
 
 
// START HERE ------------------------------------------------------------
// This table remaps GPIO inputs to keyboard values.  In this initial
// implementation there's a 1:1 relationship (can't attach multiple keys
// to a button) and the list is fixed in code; there is no configuration
// file.  Buttons physically connect between GPIO pins and ground.  There
// are only a few GND pins on the GPIO header, so a breakout board is
// often needed.  If you require just a couple extra ground connections
// and have unused GPIO pins, set the corresponding key value to GND to
// create a spare ground point.
 
#define GND -1
struct {
	int pin;
	int key;
} *io, // In main() this pointer is set to one of the two tables below.
   ioTFT[] = {
	// This pin/key table is used if an Adafruit PiTFT display
	// is detected (e.g. Cupcade or PiGRRL).
	// Input   Output (from /usr/include/linux/input.h)
	{   2,     KEY_LEFT     },   // Joystick (4 pins)
	{   3,     KEY_RIGHT    },
	{   4,     KEY_DOWN     },
	{  17,     KEY_UP       },
	{  27,     KEY_Z        },   // A/Fire/jump/primary
	{  22,     KEY_X        },   // B/Bomb/secondary
	{  23,     KEY_R        },   // Credit
	{  18,     KEY_Q        },   // Start 1P
	{  -1,     -1           } }, // END OF LIST, DO NOT CHANGE
	// MAME must be configured with 'z' & 'x' as buttons 1 & 2 -
	// this was required for the accompanying 'menu' utility to
	// work (catching crtl/alt w/ncurses gets totally NASTY).
	// Credit/start are likewise moved to 'r' & 'q,' reason being
	// to play nicer with certain emulators not liking numbers.
	// GPIO options are 'maxed out' with PiTFT + above table.
	// If additional buttons are desired, will need to disable
	// serial console and/or use P5 header.  Or use keyboard.
   ioStandard[] = {
	// This pin/key table is used when the PiTFT isn't found
	// (using HDMI or composite instead), as with our original
	// retro gaming guide.
	// Input   Output (from /usr/include/linux/input.h)
        //Player 1 config
        {  2,     KEY_UP       },   // Up
        {  3,     KEY_DOWN     },   // Down
        {  4,     KEY_LEFT     },   // Left Joystick (4 pins)
        { 17,     KEY_RIGHT    },   // Right
        { 27,     KEY_LEFTCTRL },   // Button 1
        { 22,     KEY_LEFTALT  },   // Button 2
        //{ 10,     KEY_SPACE  },     // Button 3 -> not usable dur to piTFT
        //{  9,     KEY_LEFTSHIFT  },   // Button 4 -> not usable dur to piTFT
        //{ 11,     KEY_Z  },   // Button 5 -> not usable dur to piTFT
        { 23,     KEY_SPACE  },     // Button 3 -> not usable dur to piTFT
        { 24,     KEY_LEFTSHIFT  },   // Button 4 -> not usable dur to piTFT
        { 25,     KEY_Z  },   // Button 5 -> not usable dur to piTFT
 
        { 14,     KEY_X  },   // Button 6
        { 15,     KEY_1  },   // Button Start P1
        { 18,     KEY_5  },   // Button Coins/Credits P1
	//{ 23,     KEY_ESC  }, // Button Escape -> button replaced by another du to piTFT (not enough gpio on revB) : ESC is made by Start P1 + Coin P1
	//{ 24,     KEY_0  },   // Button to halt system -> button replaced by another du to piTFT(not enough gpio on revB).
 
// For credit/start/etc., use USB keyboard or add more buttons.
	{  -1,     -1           } }; // END OF LIST, DO NOT CHANGE
 
// A "Vulcan nerve pinch" (holding down a specific button combination
// for a few seconds) issues an 'esc' keypress to MAME (which brings up
// an exit menu or quits the current game).  The button combo is
// configured with a bitmask corresponding to elements in the above io[]
// array.  The default value here uses elements 6 and 7 (credit and start
// in the Cupcade pinout).  If you change this, make certain it's a combo
// that's not likely to occur during actual gameplay (i.e. avoid using
// joystick directions or hold-for-rapid-fire buttons).
// Also key auto-repeat times are set here.  This is for navigating the
// game menu using the 'gamera' utility; MAME disregards key repeat
// events (as it should).
const unsigned long vulcanMask = (1L << 6) | (1L << 7);
const int           vulcanKey  = KEY_ESC, // Keycode to send
                    vulcanTime = 1500,    // Pinch time in milliseconds
                    repTime1   = 500,     // Key hold time to begin repeat
                    repTime2   = 100;     // Time between key repetitions
 
 
// A few globals ---------------------------------------------------------
 
char
  *progName,                         // Program name (for error reporting)
   sysfs_root[] = "/sys/class/gpio", // Location of Sysfs GPIO files
   running      = 1;                 // Signal handler will set to 0 (exit)
volatile unsigned int
  *gpio;                             // GPIO register table
const int
   debounceTime = 20;                // 20 ms for button debouncing
 
 
// Some utility functions ------------------------------------------------
 
// Set one GPIO pin attribute through the Sysfs interface.
int pinConfig(int pin, char *attr, char *value) {
	char filename[50];
	int  fd, w, len = strlen(value);
	sprintf(filename, "%s/gpio%d/%s", sysfs_root, pin, attr);
	if((fd = open(filename, O_WRONLY)) < 0) return -1;
	w = write(fd, value, len);
	close(fd);
	return (w != len); // 0 = success
}
 
// Un-export any Sysfs pins used; don't leave filesystem cruft.  Also
// restores any GND pins to inputs.  Write errors are ignored as pins
// may be in a partially-initialized state.
void cleanup() {
	char buf[50];
	int  fd, i;
	sprintf(buf, "%s/unexport", sysfs_root);
	if((fd = open(buf, O_WRONLY)) >= 0) {
		for(i=0; io[i].pin >= 0; i++) {
			// Restore GND items to inputs
			if(io[i].key == GND)
				pinConfig(io[i].pin, "direction", "in");
			// And un-export all items regardless
			sprintf(buf, "%d", io[i].pin);
			write(fd, buf, strlen(buf));
		}
		close(fd);
	}
}
 
// Quick-n-dirty error reporter; print message, clean up and exit.
void err(char *msg) {
	printf("%s: %s.  Try 'sudo %s'.\n", progName, msg, progName);
	cleanup();
	exit(1);
}
 
// Interrupt handler -- set global flag to abort main loop.
void signalHandler(int n) {
	running = 0;
}
 
// Returns 1 if running on early Pi board, 0 otherwise.
// Relies on info in /proc/cmdline by default; if this is
// unreliable in the future, easy change to /proc/cpuinfo.
int isRevOnePi(void) {
	FILE *fp;
	char  buf[1024], *ptr;
	int   n, rev = 0;
#if 1
	char *filename = "/proc/cmdline",
	     *token    = "boardrev=",
	     *fmt      = "%x";
#else
	char *filename = "/proc/cpuinfo",
	     *token    = "Revision", // Capital R!
	     *fmt      = " : %x";
#endif
 
	if((fp = fopen(filename, "r"))) {
		if((n = fread(buf, 1, sizeof(buf)-1, fp)) > 0) {
			buf[n] = 0;
			if((ptr = strstr(buf, token))) {
				sscanf(&ptr[strlen(token)], fmt, &rev);
			}
		}
		fclose(fp);
	}
 
	return ((rev == 0x02) || (rev == 0x03));
}
 
 
// Main stuff ------------------------------------------------------------
 
#define BCM2708_PERI_BASE 0x20000000
#define GPIO_BASE         (BCM2708_PERI_BASE + 0x200000)
#define BLOCK_SIZE        (4*1024)
#define GPPUD             (0x94 / 4)
#define GPPUDCLK0         (0x98 / 4)
 
int main(int argc, char *argv[]) {
 
	// A few arrays here are declared with 32 elements, even though
	// values aren't needed for io[] members where the 'key' value is
	// GND.  This simplifies the code a bit -- no need for mallocs and
	// tests to create these arrays -- but may waste a handful of
	// bytes for any declared GNDs.
	char                   buf[50],      // For sundry filenames
	                       c;            // Pin input value ('0'/'1')
	int                    fd,           // For mmap, sysfs, uinput
	                       i, j,         // Asst. counter
	                       bitmask,      // Pullup enable bitmask
	                       timeout = -1, // poll() timeout
	                       intstate[32], // Last-read state
	                       extstate[32], // Debounced state
	                       lastKey = -1; // Last key down (for repeat)
	unsigned long          bitMask, bit; // For Vulcan pinch detect
	volatile unsigned char shortWait;    // Delay counter
	struct input_event     keyEv, synEv; // uinput events
	struct pollfd          p[32];        // GPIO file descriptors
 
	progName = argv[0];             // For error reporting
	signal(SIGINT , signalHandler); // Trap basic signals (exit cleanly)
	signal(SIGKILL, signalHandler);
 
	// Select io[] table for Cupcade (TFT) or 'normal' project.
	io = (access("/etc/modprobe.d/adafruit.conf", F_OK) ||
	      access("/dev/fb1", F_OK)) ? ioStandard : ioTFT;
 
	// If this is a "Revision 1" Pi board (no mounting holes),
	// remap certain pin numbers in the io[] array for compatibility.
	// This way the code doesn't need modification for old boards.
	if(isRevOnePi()) {
		for(i=0; io[i].pin >= 0; i++) {
			if(     io[i].pin ==  2) io[i].pin = 0;
			else if(io[i].pin ==  3) io[i].pin = 1;
			else if(io[i].pin == 27) io[i].pin = 21;
		}
	}
 
	// ----------------------------------------------------------------
	// Although Sysfs provides solid GPIO interrupt handling, there's
	// no interface to the internal pull-up resistors (this is by
	// design, being a hardware-dependent feature).  It's necessary to
	// grapple with the GPIO configuration registers directly to enable
	// the pull-ups.  Based on GPIO example code by Dom and Gert van
	// Loo on elinux.org
 
	if((fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0)
		err("Can't open /dev/mem");
	gpio = mmap(            // Memory-mapped I/O
	  NULL,                 // Any adddress will do
	  BLOCK_SIZE,           // Mapped block length
	  PROT_READ|PROT_WRITE, // Enable read+write
	  MAP_SHARED,           // Shared with other processes
	  fd,                   // File to map
	  GPIO_BASE );          // Offset to GPIO registers
	close(fd);              // Not needed after mmap()
	if(gpio == MAP_FAILED) err("Can't mmap()");
	// Make combined bitmap of pullup-enabled pins:
	for(bitmask=i=0; io[i].pin >= 0; i++)
		if(io[i].key != GND) bitmask |= (1 << io[i].pin);
	gpio[GPPUD]     = 2;                    // Enable pullup
	for(shortWait=150;--shortWait;);        // Min 150 cycle wait
	gpio[GPPUDCLK0] = bitmask;              // Set pullup mask
	for(shortWait=150;--shortWait;);        // Wait again
	gpio[GPPUD]     = 0;                    // Reset pullup registers
	gpio[GPPUDCLK0] = 0;
	(void)munmap((void *)gpio, BLOCK_SIZE); // Done with GPIO mmap()
 
 
	// ----------------------------------------------------------------
	// All other GPIO config is handled through the sysfs interface.
 
	sprintf(buf, "%s/export", sysfs_root);
	if((fd = open(buf, O_WRONLY)) < 0) // Open Sysfs export file
		err("Can't open GPIO export file");
	for(i=j=0; io[i].pin >= 0; i++) { // For each pin of interest...
		sprintf(buf, "%d", io[i].pin);
		write(fd, buf, strlen(buf));             // Export pin
		pinConfig(io[i].pin, "active_low", "0"); // Don't invert
		if(io[i].key == GND) {
			// Set pin to output, value 0 (ground)
			if(pinConfig(io[i].pin, "direction", "out") ||
			   pinConfig(io[i].pin, "value"    , "0"))
				err("Pin config failed (GND)");
		} else {
			// Set pin to input, detect rise+fall events
			if(pinConfig(io[i].pin, "direction", "in") ||
			   pinConfig(io[i].pin, "edge"     , "both"))
				err("Pin config failed");
			// Get initial pin value
			sprintf(buf, "%s/gpio%d/value",
			  sysfs_root, io[i].pin);
			// The p[] file descriptor array isn't necessarily
			// aligned with the io[] array.  GND keys in the
			// latter are skipped, but p[] requires contiguous
			// entries for poll().  So the pins to monitor are
			// at the head of p[], and there may be unused
			// elements at the end for each GND.  Same applies
			// to the intstate[] and extstate[] arrays.
			if((p[j].fd = open(buf, O_RDONLY)) < 0)
				err("Can't access pin value");
			intstate[j] = 0;
			if((read(p[j].fd, &c, 1) == 1) && (c == '0'))
				intstate[j] = 1;
			extstate[j] = intstate[j];
			p[j].events  = POLLPRI; // Set up poll() events
			p[j].revents = 0;
			j++;
		}
	} // 'j' is now count of non-GND items in io[] table
	close(fd); // Done exporting
 
 
	// ----------------------------------------------------------------
	// Set up uinput
 
#if 1
	// Retrogame normally uses /dev/uinput for generating key events.
	// Cupcade requires this and it's the default.  SDL2 (used by
	// some newer emulators) doesn't like it, wants /dev/input/event0
	// instead.  Enable that code by changing to "#if 0" above.
	if((fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK)) < 0)
		err("Can't open /dev/uinput");
	if(ioctl(fd, UI_SET_EVBIT, EV_KEY) < 0)
		err("Can't SET_EVBIT");
	for(i=0; io[i].pin >= 0; i++) {
		if(io[i].key != GND) {
			if(ioctl(fd, UI_SET_KEYBIT, io[i].key) < 0)
				err("Can't SET_KEYBIT");
		}
	}
	if(ioctl(fd, UI_SET_KEYBIT, vulcanKey) < 0) err("Can't SET_KEYBIT");
	struct uinput_user_dev uidev;
	memset(&uidev, 0, sizeof(uidev));
	snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "retrogame");
	uidev.id.bustype = BUS_USB;
	uidev.id.vendor  = 0x1;
	uidev.id.product = 0x1;
	uidev.id.version = 1;
	if(write(fd, &uidev, sizeof(uidev)) < 0)
		err("write failed");
	if(ioctl(fd, UI_DEV_CREATE) < 0)
		err("DEV_CREATE failed");
#else // SDL2 prefers this event methodology
	if((fd = open("/dev/input/event0", O_WRONLY | O_NONBLOCK)) < 0)
		err("Can't open /dev/input/event0");
#endif
 
	// Initialize input event structures
	memset(&keyEv, 0, sizeof(keyEv));
	keyEv.type  = EV_KEY;
	memset(&synEv, 0, sizeof(synEv));
	synEv.type  = EV_SYN;
	synEv.code  = SYN_REPORT;
	synEv.value = 0;
 
	// 'fd' is now open file descriptor for issuing uinput events
 
 
	// ----------------------------------------------------------------
	// Monitor GPIO file descriptors for button events.  The poll()
	// function watches for GPIO IRQs in this case; it is NOT
	// continually polling the pins!  Processor load is near zero.
 
	while(running) { // Signal handler can set this to 0 to exit
		// Wait for IRQ on pin (or timeout for button debounce)
		if(poll(p, j, timeout) > 0) { // If IRQ...
			for(i=0; i<j; i++) {       // Scan non-GND pins...
				if(p[i].revents) { // Event received?
					// Read current pin state, store
					// in internal state flag, but
					// don't issue to uinput yet --
					// must wait for debounce!
					lseek(p[i].fd, 0, SEEK_SET);
					read(p[i].fd, &c, 1);
					if(c == '0')      intstate[i] = 1;
					else if(c == '1') intstate[i] = 0;
					p[i].revents = 0; // Clear flag
				}
			}
			timeout = debounceTime; // Set timeout for debounce
			c       = 0;            // Don't issue SYN event
			// Else timeout occurred
		} else if(timeout == debounceTime) { // Button debounce timeout
			// 'j' (number of non-GNDs) is re-counted as
			// it's easier than maintaining an additional
			// remapping table or a duplicate key[] list.
			bitMask = 0L; // Mask of buttons currently pressed
			bit     = 1L;
			for(c=i=j=0; io[i].pin >= 0; i++, bit<<=1) {
				if(io[i].key != GND) {
					// Compare internal state against
					// previously-issued value.  Send
					// keystrokes only for changed states.
					if(intstate[j] != extstate[j]) {
						extstate[j] = intstate[j];
						keyEv.code  = io[i].key;
						keyEv.value = intstate[j];
						if ((keyEv.code==KEY_0)&&(keyEv.value==1))
						{
							system("sudo halt");
							//system("echo \"that works\"");
						}
						else
						{
							write(fd, &keyEv,
							sizeof(keyEv));
						}
						//write(fd, &keyEv,
						//sizeof(keyEv));
 
						c = 1; // Follow w/SYN event
						if(intstate[j]) { // Press?
							// Note pressed key
							// and set initial
							// repeat interval.
							lastKey = i;
							timeout = repTime1;
						} else { // Release?
							// Stop repeat and
							// return to normal
							// IRQ monitoring
							// (no timeout).
							lastKey = timeout = -1;
						}
					}
					j++;
					if(intstate[i]) bitMask |= bit;
				}
			}
 
			// If the "Vulcan nerve pinch" buttons are pressed,
			// set long timeout -- if this time elapses without
			// a button state change, esc keypress will be sent.
			if((bitMask & vulcanMask) == vulcanMask)
				timeout = vulcanTime;
		} else if(timeout == vulcanTime) { // Vulcan timeout occurred
			// Send keycode (MAME exits or displays exit menu)
			keyEv.code = vulcanKey;
			for(i=1; i>= 0; i--) { // Press, release
				keyEv.value = i;
				write(fd, &keyEv, sizeof(keyEv));
				usleep(10000); // Be slow, else MAME flakes
				write(fd, &synEv, sizeof(synEv));
				usleep(10000);
			}
			timeout = -1; // Return to normal processing
			c       = 0;  // No add'l SYN required
		} else if(lastKey >= 0) { // Else key repeat timeout
			if(timeout == repTime1) timeout = repTime2;
			else if(timeout > 30)   timeout -= 5; // Accelerate
			c           = 1; // Follow w/SYN event
			keyEv.code  = io[lastKey].key;
			keyEv.value = 2; // Key repeat event
			write(fd, &keyEv, sizeof(keyEv));
		}
		if(c) write(fd, &synEv, sizeof(synEv));
	}
 
	// ----------------------------------------------------------------
	// Clean up
 
	ioctl(fd, UI_DEV_DESTROY); // Destroy and
	close(fd);                 // close uinput
	cleanup();                 // Un-export pins
 
	puts("Done.");
 
	return 0;
}

Overclockage

Sur le Pi 1 il est possible de pousser l’overclockage de manière stable jusqu’à 1.1 GHz. Cela a été réaliser par l’auteur de la distribution Recalbox.

Les paramètres sont les suivants :

#overclocking extreme recalbox
arm_freq=1100
core_freq=550
sdram_freq=600
over_voltage=8
over_voltage_sdram=6
force_turbo=1

L’augmentation de la vitesse du processeur s’accompagne d’une instabilité de l’écran !!! il faut donc recommencer le réglage pour 1.1GHz :

#resolution configuration
hdmi_force_hotplug=1
hdmi_cvt=320 240 60 1 0 0 0
hdmi_group=2
hdmi_mode=87
# working config 
# 900 Mhz -> 48000000
# 1Ghz -> 35000000
# 1.1Ghz -> 22000000
#modification of the speed   
dtoverlay=pitft28r,rotate=90,speed=22000000,fps=60

Ajout module MCP pour le panel 6 boutons
Il n’est pas possible sur un Pi1 de mettre l’écran pitft et d’avoir un panel 6 boutons + start P1 + Credits + Escape/Quit. L’écran PiTFT utilise le port SPI, et, en plus des 5 GPIOs, il utilise 2 Gpio supplémentaires. Du fait il ne reste pas assez de GPIO. La solution consiste à utiliser un module mcp23017 sur la bus Ic2 pour multiplexer les entrées http://atmjoy.com/pikeyd-pi-gpio-ke.... On peut le brancher sur le GPIO délocalisé de l’écran. On utilise alors le démon pikeyd comme dans le cas de la microborne Raspicade. La mise en œuvre est très simple. Il suffit de relancer la configuration du type d’entrées au démarrage de raspicade. En effet ce module est supporté par défaut par Raspicade. On peut donc câbler l’ensemble du panel 6 boutons :

 Réalisation de la caisse

l’idée est ensuite de faire des modifs du plan PDF de la borne, https://learn.adafruit.com/system/a... pour la réaliser en impression 3D sur une imprimante PrintrbotSimpleMetal

Modélisation 3D

J’ai donc repris le fichier PDF de la cupcade et modélisé cette dernière avec blender :

Cependant comme je voulais imprimer les pièces, et que la surface max imprimable sur le printrbot est de 15cmx15cm, j’ai modifié la borne :

Impression 3D

Impression des différentes pièces :

 Montage

Le montage nécessite un peu de matériel afin de souder les boutons et aussi ébavurer les pièces :

Test de la partie son :

Pour la partie son j’ai utilisé des HP de téléphone sans fil de maison (8 Ohms) et un amplificateur 5V acheté sur Ebay : http://www.ebay.fr/itm/DC-5V-PAM840...

Test de l’ensemble avant le montage en utilisant un potentiomère de volume récupéré sur une casque micro HS

montage final et fixation du potentiomètre de volume avec de la colle à chaud :

Visserie noire en 6 pans, c’est bien plus joli :

 Vidéo de la vue éclatée de la nano borne

Portfolio