SE4Binome2025-3

De projets-se.plil.fr
Aller à la navigation Aller à la recherche

Présentation projet :

Dans ce projet Pico, l'objectif est de réaliser un pico ordinateur composé de différentes cartes fille. La conception des cartes est répartie entre les binômes et dans notre cas, nous nous occuperons de la carte fille clavier.

Pour suivre l'avancée du travail, les fichiers seront déposés sur notre archive git.

Partie Shield

Nous avons tout d'abord commencé par le routage d'un shield Arduino en guise de carte de test pour les cartes filles, dans le cas où la carte mère ne serait pas opérationnelle.

Schématique

Screenshot 2025-11-14 13-52-53.png

En utilisant un modèle donné nous avons fait cette schématique pour notre shield.

Routage

Nous avons routé le shield comme ceci

shield


Notre carte ressemblant à cellue du binôme 1, nous utiliserons par conséquent la leur.

Tests

Partie Carte Clavier

Mise en place

Nous avons d'abord eu à définir le projet pour le clavier et nous avons décidé de réaliser entièrement notre carte clavier de manière matricielle. Nous avons choisi de créer un clavier de 30 touches permettant d'écrire 26 caractères par mode avec 3 modes différents. Les 4 touches restantes serviront de touches Supprimer, Espace, Maj et Symbole. Ces touches seront communes aux 3 modes du clavier. Nous allons profiter du fait d'avoir attribué une diode à chaque touche pour pouvoir en presser deux en même temps. Comme le fonctionnement de la touche Shift, il faut garder la touche enfoncée pour changer de mode. MAJ enfoncée pour les majuscules et SYMBOLE pour les chiffres et symboles. En appuyant sur MAJ et SYMBOLE simultanément on pourra activer la touche Entrée.

Disposition des touches

La disposition est trouvable dans le fichier bind_clavier trouvable sur le git.

Mode 1: Minuscule
a b c d e f
g h i j k i
m n o p q r
s t u v w x
y z del space maj symbole
Mode 2 : Majuscule
A B C D E F
G H I J K L
M N O P Q R
S T U V W X
Y Z del space maj symbole
Mode 3 : Num et symbole
1 2 3 & ; ,
4 5 6 " ? !
7 8 9 ' = +
/ 0 : ( ) _
* - del space maj symbole

Kicad

Nous avons donc mis en place cette matrice sur notre carte KiCad à l'aide d'un atmega328 programmable par un ISP.

Carte2.png

Les lignes et colonnes de touches sont donc reliées aux broches des ports C et D et chacun des boutons est relié à une diode, ce qui nous permettra de taper sur plusieurs touches en même temps, nous permettant de changer les modes de fonctionnement du clavier entre minuscule, majuscule et symbole.

Pour s'assurer d'être dans le bon mode de clavier, nous avons placé 3 LEDs qui indiquent chacune un mode différent.

Notre carte sera relié à la carte mère via un connecteur 1*8 placé à l'extremité supérieure de la carte.


Notre carte, une fois routée, ressemble donc à ceci :

Image du routage du clavier

Images plus en détails de la carte :

Matrice touche.png
Partie avr.png








Code clavier

Le code C du clavier peut être trouvé sur le git.

Variables et constantes

Tout d'abord nous avons initialisé les différentes librairies et les constantes globales utiles au programme.

#include <avr/interrupt.h>
#include <avr/io.h>
#include <stdint.h>
#include <util/delay.h>

#define NB_COL 6
#define NB_ROW 5
#define NB_MODE 3
#define ASCII_ENTER 13
#define MAJ 128
#define MAJ_ROW 4
#define MAJ_COL 4
#define SYMBOLE 129 //pour dépasser la table ascii "normale"
#define SYMBOLE_ROW 4
#define SYMBOLE_COL 5
#define MAX_PRESSED_KEYS 2
#define ANTI_REBOND_mS 10 //10ms pour éviter le "spam" d'envoi

Ensuite, nous avons déclaré les variables globales liées aux ports utilisés par les touches du clavier. On enregistre les ports exacts de chaques lignes et colonnes.

uint8_t BIT_Row[NB_ROW] = {PD1,PD0,PC5,PC4,PC3};                                  
uint8_t BIT_Col[NB_COL] = {PC0,PC1,PC2,PD2,PD3,PD4};

volatile uint8_t *DDR_Row[NB_ROW] = {&DDRD,&DDRD,&DDRC,&DDRC,&DDRC};              
volatile uint8_t *DDR_Col[NB_COL] = {&DDRC,&DDRC,&DDRC,&DDRD,&DDRD,&DDRD};

volatile uint8_t *PORT_Row[NB_ROW] = {&PORTD,&PORTD,&PORTC,&PORTC,&PORTC};        
volatile uint8_t *PORT_Col[NB_COL] = {&PORTC,&PORTC,&PORTC,&PORTD,&PORTD,&PORTD};

volatile uint8_t *PIN_Col[NB_COL] = {&PINC,&PINC,&PINC,&PIND,&PIND,&PIND};

Nous avons également déclaré une matrice 3D représentant notre clavier avec les codes ascii décimaux correspondant à chaque touche.

int bind[NB_MODE][NB_ROW][NB_COL]={                                     //ASCII décimal
				    { {97,98,99,100,101,102},  
				      {103,104,105,106,107,108},
				      {109,110,111,112,113,114},
				      {115,116,117,118,119,120},
				      {121,122,127,32,MAJ,SYMBOLE} } ,  //mode défaut

				    { {65,66,67,68,69,70},
				      {71,72,73,74,75,76},
				      {77,78,79,80,81,82},
				      {83,84,85,86,87,88},
				      {89,90,127,32,MAJ,SYMBOLE} } ,    // Maj
				    
				    { {49,50,51,38,59,44},
				      {52,53,54,34,63,33},
				      {55,56,57,92,61,43},
				      {47,48,58,40,41,95},
				      {42,45,127,32,MAJ,SYMBOLE}}} ;    //Symbole

Fonctions

Les ports doivent êtres initialisés, les ports correspondant aux colonnes seront en entrée pour détecter un appui et les lignes en sortie.

Pour le SPI, on le met en maître et on assigne MOSI,SCK et SS en sortie.

void initIn(void){ 
  for(int row = 0; row < NB_ROW ; row++){
    *(DDR_Row[row]) |= (1 << BIT_Row[row]);
  }
  for(int col = 0; col < NB_COL ; col++){
    *(DDR_Col[col]) &= ~(1 << BIT_Col[col]);
    *(PORT_Col[col]) |= (1 << BIT_Col[col]);
  }
}

[...]

void initSPI(void) {
    DDRB |= (1 << PB2) | (1 << PB3) | (1 << PB5); 
    SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR0);
}

Notre fonction principale permettant le scan des touches parcourt d'abord chaque ligne en l'activant puis en parcourant chaque colonne pour vérifier si un appui est réalisé. Si on dépasse le nombre de touches appuyéess elle s'arrête sinon elle sauvegarde la ligne et la colonne correspondantes à la touche pressée dans un tableau déclaré globalement. Après le parcours de toute la ligne cette dernière est désactivée.

void scanTouche(void){
  counter_pressed = 0; 
  for(uint8_t row = 0 ; row < NB_ROW; row++ ){
    *(PORT_Row[row]) &= ~(1 << BIT_Row[row]);                       
    for(uint8_t col = 0; col < NB_COL; col++){
      if(counter_pressed > MAX_PRESSED_KEYS-1){return;}             
      if(!(*(PIN_Col[col])&(1 << BIT_Col[col]))){                    
	pressed_keys[counter_pressed][0] = row;                      
	pressed_keys[counter_pressed][1] = col;
	counter_pressed++;
      }
    }
    *(PORT_Row[row]) |= (1 <<(BIT_Row[row]));                         
  }
}

Main

On initialise les variables utiles au programme.

valeur_touche est la valeur ASCII de la touche, à 0 initialement pour le caractère NULL.

row_touche est l'index de la ligne, à -1 initialement pour mettre une condition bloquante à l'envoi si seule une touche MAJ ou SYMBOLE est pressée.

mode est à 0 car le mode minuscule est le mode par défaut.

int valeur_touche = 0;
int row_touche = -1;
int col_touche = -1;
int mode = 0;
Boucle while

On scanne tout d'abord la touche pressée et on place ses coordonnées dans des variables plus compréhensibles.

while(1){
  scanTouche();
  for(int i = 0;i < counter_pressed ; i++){
    int r=pressed_keys[i][0];
    int c=pressed_keys[i][1];
Analyse de la touche

Premièrement on vérifie si la touche est SYMBOLE ou MAJ, si c'est le cas on change le mode en la valeur correspondante seulement si le mode n'a pas déjà été changé ce qui voudrait dire que l'autre touche aurait été pressée et dans ce cas la touche ENTER est envoyée.

 if(r == MAJ_ROW && c == MAJ_COL ){
	if(mode == 2) {
	  valeur_touche = ASCII_ENTER;                                                
	  sendSPI(valeur_touche);
	  break;
	}
	mode = 1;	
      }
      else if(r == SYMBOLE_ROW && c == SYMBOLE_COL ){
	if(mode == 1) {
	  valeur_touche = ASCII_ENTER;                                                 
	  sendSPI(valeur_touche);
	  break;
	}
	mode = 2;	
      }

Dans le cas d'une touche "normale" on retient les coordonnées, et on envoie via SPI le caractère correspondant si jamais une touche est pressée. On rajoute un délai pour éviter le rebond lors d'un appui.

else {
	row_touche = r;
	col_touche = c;
      }	
if(counter_pressed > 0 && row_touche > -1 && col_touche > -1){                   
      valeur_touche = bind[mode][row_touche][col_touche];
      sendSPI(valeur_touche);
    }
    if (valeur_touche!=0) {_delay_ms(ANTI_REBOND_mS);}

On finit ensuite par reset les valeurs des variables pour continuer le scan

mode = 0;
row_touche = -1;
col_touche = -1;
valeur_touche = 0;

Ordonnanceur

//code ordonnanceur

Vidéo test ordonnanceur