Concevoir un clavier : logiciel embarqué
Retour vers Concevoir un clavier
TMK
Pour programmer un microcontrolleur sans réinventer la roue, le mieux est de se reporter à un projet existant. TMK est relativement complet et flexible, il devrait pouvoir être adapté à la majorité des projet. La documentation est relativement claire, elle est à lire avant de se lancer dans un projet de développement.
C : pages et pointeurs de fonctions
Une technique simple est d'enregistrer le mappage des touches dans des «pages» représentées dans le code par des tableaux. Les majuscules et les minuscules ont chacune une page.
En utilisant des tableaux de pointeurs, il est possible qu'une touche donne accès à un caractère, une chaine ou une fonction.
TODO exemple d'utilisation des pointeurs de fonctions
Teensy
Les différentes «pages» de configuration de touches prennent pas mal de place en RAM. Lorsqu'il y en a trop, ça dépasse la capacité du processeur qui donne des résultats imprévus. Il faut alors stocker ces pages dans la mémoire de programmation (PROGMEM) et utiliser des fonctions spéciales pour y accéder. La Tensy 2++ ou 3 ont une mémoire vive plus importante qui peut éviter l'utilisation de PROGMEM.
TODO exemple d'utilisation PROGMEM
USB
- type de périphérique (clavier, joystick, souris, stockage)
- codage des caractères
Lecture PS2
La lecture des trames PS2 d'une puce de clavier peut être réalisée grâce à la bibliothèque PS2Keyborard http://www.pjrc.com/teensy/td_libs_PS2Keyboard.html . S'il ne s'agit pas de transmettre directement un code via le port USB, certaines adaptations peuvent être nécessaire.
TODO relire le code pour vérifier
Commande IO expander
Le code suivant est destiné à piloter un MCP23017 mais peut être facilement adapté pour les autres composants de la même famille. Ce sont des extensions de port commandées par un bus I2C. Pour piloter ce bus à partir d'un microcontroleur (teensy, arduino) la bibliothèque I2C.h (https://github.com/rambo/I2C) est plus facile à utiliser que wire.h. C'est donc celle qui sera utilisée dans l'exemple ci après qui est donné à titre d'illustration, ce n'est pas un code fonctionnel.
Le schéma suivant est un exemple de montage destiné à illustrer le code qui suit (à confirmer avec un teensy 2.0, en fait seulement testé avec un teensy 2.0++)
Il faut d'abord définir les adresses spécifiques au MCP23017 (voir datasheet du composant )
#define IODIRA 0x00 // IO direction (0 = output, 1 = input (Default)) #define IODIRB 0x01 #define IOPOLA 0x02 // IO polarity (0 = normal, 1 = inverse) #define IOPOLB 0x03 #define GPINTENA 0x04 // Interrupt on change (0 = disable, 1 = enable) #define GPINTENB 0x05 #define DEFVALA 0x06 // Default comparison for interrupt on change (interrupts on opposite) #define DEFVALB 0x07 #define INTCONA 0x08 // Interrupt control //(0 = interrupt on change from previous, 1 = interrupt on change from DEFVAL) #define INTCONB 0x09 #define IOCON 0x0A // IO Configuration: bank/mirror/seqop/disslw/haen/odr/intpol/notimp //#define IOCON 0x0B // same as 0x0A #define GPPUA 0x0C // Pull-up resistor (0 = disabled, 1 = enabled) #define GPPUB 0x0D #define INFTFA 0x0E // Interrupt flag (read only) : (0 = no interrupt, 1 = pin caused interrupt) #define INFTFB 0x0F #define INTCAPA 0x10 // Interrupt capture (read only) : value of GPIO at time of last interrupt #define INTCAPB 0x11 #define GPIOA 0x12 // Port value. Write to change, read to obtain value #define GPIOB 0x13 #define OLLATA 0x14 // Output latch. Write to latch output. #define OLLATB 0x15
On définit ensuite les adresses spécifiques au montage. Ici le clavier comporte 48 touches, c'est suffisant pour un clavier en deux parties.
#define MCP23017_ADDR 0x20 // En fonction du cablage de ses pattes d'adresse, //le composant peut prendre son adresse de 0.20 à 0x27 #define MCP23017_ROW 6 #define MCP23017_COL 8 #define MCP23017_NB MCP23017_ROW * MCP23017_COL
Le composant est ensuite initialisé avant sa première utilisation :
void init_mcp23_kbd(){ // direction I2c.write(MCP23017_ADDR,IODIRA,0x00); // le port A est utilisé comme sortie I2c.write(MCP23017_ADDR,IODIRB,0xFF); // le port B est utilisé comme entrée // enable pull-up on switches I2c.write(MCP23017_ADDR,GPPUB,0xFF); // pour détecter quand une touche est relachée // invert polarity I2c.write(MCP23017_ADDR,IOPOLA, 0xFF); // pour avoir un 1 quand la touche est enfoncée I2c.write(MCP23017_ADDR,IOPOLB, 0xFF); // enable interrupts // (GPINTENA, 0xFF); // enable interrupts - both ports // pour l'instant, pas d'utilisation des interruptions // no interrupt yet // keyPressed = false // read from interrupt capture ports to clear them // Read (INTCAPA); // Read (INTCAPB); }
Ensuite la boucle active les rangs alternativement et lit le résultat sur les colonnes :
void loop_mcp23_kbd(){ int index = 0 ; for (int i = 0;i<MCP23017_ROW; i++) { I2c.write(MCP23017_ADDR,GPIOA,~(1<<i)); I2c.read(MCP23017_ADDR,GPIOB,2); // (pourquoi 2 ?) int tot= I2c.receive(); for (int j = 0;j<MCP23017_COL; j++) { //code de gestion des touches if (tot & (1<<j) ) { index = j*MCP23017_ADRR_ROW+i ; Serial.print("touche : "); Serial.print(index); Serial.println(" appuyée "); } } } }
Commande registre à décalage
Pilotage d'éléments annexes
- I2C
- série
- parallèle