miércoles, 16 de junio de 2021

 [Anexo] LED Display & Matrix Keypad Driver

 Manejo de drivers matriciales E/S por protocolo SPI. 
El integrado TM1638


En la entrada anterior vimos cómo utilizar integrados para manejo de displays de cátodo común y escaneo de teclados por protocolo SPI, pero ¿es posible utilizar displays de ánodo común? La respuesta es SÍ. Veamos.


En la entrega anterior les comenté que decidimos utilizar este integrado por razones que analizaremos más adelante en detalle. Llegó el momento de abordar ese tema. Primero recordemos que el TM1638 soporta displays de 10 segmentos, de hasta 8 dígitos, siempre y cuando sean de cátodo común. Esto se verá representado en detalle en el siguiente circuito:




Como se ve en esa imagen, cada bloque de LEDs representa un dígito y su respectivo punto. En total tendríamos 4 dígitos de 8 segmentos (7 y el punto) y para encender, por ejemplo, el punto del primer dígito, SEG8 tendría que tener un estado alto (HIGH) y GRID1 un estado bajo (LOW). Esta instrucción tendría que ser cargada en el arreglo que habíamos hecho en la librería (display[]), para luego ser enviada al registro DRAM correspondiente.


Al profundizar sobre el funcionamiento de este dispositivo, he descubierto que trabaja en colector abierto, por lo que es posible controlar matrices de LEDs de hasta 8x8. Ahora bien, un array de 4 dígitos como el que se ve en la imagen que sigue es simplemente una matriz de 8x4 (8 ánodos y 4 cátodos comunes).





Considerando que este integrado puede controlar hasta 8 cátodos comunes, tranquilamente podemos invertir esa matriz quedando así una de 4x8 (4 ánodos comunes y 8 cátodos). Es por esta razón que elegimos este dispositivo en particular. Veamos entonces cómo sería la conexión cuando se tiene un display de 4 dígitos de Ánodo Común:




Una vez realizada la modificación correspondiente en cada conexión, sólo resta modificar el software, para lo cual, primeramente explicaremos la idea con un poco de detalle.


Observemos que en los registros de memoria que controlan los displays, tenemos 10 columnas (SEG1-SEG10) y 8 filas (GRID1-GRID8), según su Datasheet:



Considérese ahora la siguiente matriz de 14x8:




DRAM ADDRESSBit0Bit1Bit2Bit3Bit4Bit5Bit6Bit7
0x0000000000
0x0100000000
0x0200000000
0x0300000000
0x0400000000
0x0500000000
0x0600000000
0x0700000000
0x0800000000
0x0900000000
0x0A00000000
0x0B00000000
0x0C00000000
0x0D00000000
0x0E00000000
0x0F00000000


Basándonos en la hoja de datos, si por ejemplo quisiéramos colocar HOLA en el display de 4 dígitos, las direcciones que nos interesan son:
0x00 primer dígito (a la izquierda)
0x02 segundo dígito
0x04 tercer dígito
0x06 cuarto dígito

Entonces, nuestro array para un display de Cátodo Común sería:
display{{0x76},{xx},{0x3F},{xx},{0x38},{xx},{0x77},{xx},{xx},{xx},{xx},{xx},{xx},{xx}}
*xx=Irrelevante.

Con esos datos, nuestra matriz modificada quedaría de la siguiente forma:

0 1 1 1 0 1 1 0  (H)
0 0 0 0 0 0 0 0
1 1 1 1 1 1 0 0  (O)
0 0 0 0 0 0 0 0
0 1 1 1 0 0 0 0  (L)
0 0 0 0 0 0 0 0
0 1 1 1 0 1 1 1  (A)
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0

Ahora bien, para representarla en un display de ánodo común, hay que conectar los segmentos (SEG) a los dígitos (GRID) y los dígitos (GRID) a los segmentos (SEG), esto es:

   S E G M E N T O S                         D Í G I T O S
D                                                      S
                                                         E
Í                                                       G
                                          ---->>>  M 
G                                                      E
                                                         N
I                                                        T
                                                         O
T                                                       S

O

S


Básicamente, nuestro objetivo es hacer por software una conversión de matrices. Esto se realiza mediante la siguiente función que deberá añadirse a la librería de la entrada previa:




/* To connect an anode display, simply exchange pins, connecting
SGx pins to common anodes and GRx pins to the segments:
 ______________________________________
| Common Cathode connection:            |
| -------------------------             |  ****Max number of displays:
|       GRx Pin                         |  7 digits - 11 segments
|   ________|_____________________      |          or
|  _|_  _|_  _|_  _|_  _|_  _|_  _|_    |  6 digits - 12 segments
|  / \  / \  / \  / \  / \  / \  / \    |
|  ¨|¨  ¨|¨  ¨|¨  ¨|¨  ¨|¨  ¨|¨  ¨|¨    |  and 7x11 or 6x12 matrix of leds.
|  SG1  SG2 ...................  SG7    |
|                                       |
 ***************************************
| Common Anode connection:              |  ****NOTICE that you can
| -------------------------             |  connect only 7 segment displays
|       SGx Pin                         |  in this particular configuration.
|   ________|_____________________      |  If your displays has more than 7
|  _|_  _|_  _|_  _|_  _|_  _|_  _|_    |  segments, use the TM1638 instead.
|  \ /  \ /  \ /  \ /  \ /  \ /  \ /    |
|  ¨|¨  ¨|¨  ¨|¨  ¨|¨  ¨|¨  ¨|¨  ¨|¨    |
|  GR1  GR2 ...................  GR7    |
|                                       |
 ***************************************

  ____________                       ____________
  |DRAM ADDRESS|                     |DRAM ADDRESS|
  | /¨¨¨¨¨¨¨¨¨¨                      | /¨¨¨¨¨¨¨¨¨¨
| \/     14 to 1                   | \/    7 to 1
v     -> Segment ->                v     -> Digit ->
D 0a b 1 0 1 0 0 1 1   \           S 0b 1 1 1 1 1 1 1
  1b ? ? t r q p n m   |             1b h g f e d c b
i 2a c 0 1 1 0 1 0 1   |           e 2b 0 0 1 0 1 0 1
  3b ? ? X X X X X X   |             3b & $ % Z Y X m
g 4a d 1 0 1 0 0 1 1   |           g 4b 1 1 0 1 0 1 0
  5b ? ? Y Y Y Y Y Y   |             5b ) $ % Z Y X n
i 6a e 0 1 1 0 1 0 1   |\\\\\\\    m 6b 0 0 0 0 0 0 0
  7b ? ? Z Z Z Z Z Z   |///////      7b ( $ % Z Y X p
t 8a f 1 0 1 0 0 1 1   |           e 8b 1 1 1 1 1 1 1
  9b ? ? % % % % % %   |             9b / $ % Z Y X q
s Aa g 0 1 1 0 1 0 1   |           n Ab 1 1 0 1 0 1 0
  Bb ? ? $ $ $ $ $ $   |             Bb ¬ $ % Z Y X r
| Ca h 1 1 1 0 1 0 1   |           t Cb 1 0 1 0 1 0 1
v Db ? ? ~ ¬ / ( ) &   /             Db ~ $ % Z Y X t
     MSB          LSB                   MSB        LSB
 _^^__________                      _^^_____________
|1 to 7 digits|                    |14 to 1 segments|
***************                    ******************
*/

/*
 The following function will make the conversions between arrays
 in order to write the dRAM according with the common anode configuration:
*/


int to_anode(int* data){
	int i,j,data_i[14],data_o[14];
	// Stores the data into an int:
	for(i=0;i<14;i++){
		data_i[i] = data[i];
	}
	// Data rearrangement:
	for(i=0;i<7;i++){
		for(j=0;j<7;j++){
			if(bit_test(data_i[2*i],j)){
				bit_set(data_o[2*j],i);
			}
			else{
				bit_clear(data_o[2*j],i);
			}
			if(bit_test(data_i[(2*i)+1],j)){
				bit_set(data_o[(2*j)+1],i);
			}
			else{
				bit_clear(data_o[(2*j)+1],i);
			}
		}
	}
	// Assign the eighth segment value (b7 of every int, stored on each pair
	// DRAM address) to the eighth digit:
	for(i=0;i<7;i++){
		data_o[(2*i)+1]<<=data_o[(2*i)+1];
		if(bit_test(data_i[(2*i)],7)){
			bit_set(data_o[(2*i)+1],0);
		}
		else{
			bit_clear(data_o[(2*i)+1],0);
		}
	}
	// Return the result:
	return data_o;
}
  

Añadida esa función a la librería, solamente basta con convertir el array con los valores asignados que le pasamos al dispositivo como si fuésemos a controlar un display de cátodo común, sólo que previo a pasarle el array a la función para que lo muestre, primero tendremos que transformar ese array usando:


int8 display[14]               //array de 14 elementos de 8 bits
display[0] = 0x76;             //coloco una H en el primer dígito
display[2] = 0x3F;             //coloco una O en el segundo dígito
display[4] = 0x38;             //coloco una L en el tercer dígito
display[6] = 0x77;             //coloco una A en el cuarto dígito
display = to_anode(display);   //transformo la variable
write_dram(display);           //la muestro en el display
  


Luego pasaremos ese array normalmente a la DRAM del dispositivo con el comando:


O bien se puede hacer todo en un solo paso, esto es, asignar los valores al array como si fuera un display de cátodo común y realizar una composición de funciones de tipo g{f(x)} de este modo:


int8 display[14]               //array de 14 elementos de 8 bits
display[0] = 0x76;             //coloco una H en el primer dígito
display[2] = 0x3F;             //coloco una O en el segundo dígito
display[4] = 0x38;             //coloco una L en el tercer dígito
display[6] = 0x77;             //coloco una A en el cuarto dígito
write_dram(to_anode(display)); //transformo la variable y la muestro en el display
  

martes, 15 de junio de 2021

LED Display & Matrix Keypad Driver

 Manejo de drivers matriciales E/S por protocolo SPI. 
El integrado TM1638


Existen en la actualidad diversos fabricantes de drivers matriciales para diferentes aplicaciones en lo referente a periféricos de entrada o salida, como teclados, displays de siete segmentos, etc. En esta entrada nos enfocaremos en el TM1638 por razones que más adelante analizaremos en detalle.



i. Funcionamiento.

Este dispositivo desarrollado por Titan Microelectronics utiliza tecnología CMOS y es capaz de soportar hasta 8 displays de 10 segmentos como salida y además posee un key-scanner interno para un teclado matricial de hasta 24 teclas en 3 columnas (Kx) de 8 filas (KSy) y es operado como esclavo mediante la interfaz SPI haciendo uso de cualquier microcontrolador del mercado. Veamos a continuación su disposición de pines.



Pin #Nombre Caract Descripción
1 a 3K1 - K3EntradaConexión a teclado (Filas)
4 y 15VCC/VDDAlim (+)VCC es la alimentación de la lógica y VDD la alimentación de la matriz
5 a 12SEG1/KS1 - SEG8/KS8SalidaConexión a segmentos de display/Conexión a teclado (Columnas)
13 y 14SEG9 y SEG10SalidaConexión a segmentos de display
16, 17, 19-24GRID1 - GRID8SalidaConexión a cátodo común de display
18 y 25GNDAlim (-)Ground/Tierra/Aterrizaje/(-)
26DIOE/SData Input + Data Output pin
27CLKEntradaSeñal de entrada de reloj
28STBEntradaStrobe/¬SS/Slave-Select



ii. Circuito de prueba.

En este apartado construiremos un circuito de prueba cuya finalidad práctica consiste en el manejo de este integrado (e integrados similares, tales como el PT6961, ETK6207, ...). Mientras espero el lote de TM1638, probaremos con el PT6961 cuyo Datasheet es similar, así como también sus direcciones de lectura/escritura.



Obsérvese que en el simulador no hay STB/SS pin del TM1637 (utilizaremos este en la simulación del software, ya que no tengo las librerías de los que utilizo), sin embargo, de existir físicamente en el suyo, deberá declararlo posteriormente en su firmware. Asimismo sólo hay un pin para el bus de datos, por lo que deberá colocar un resistor de 1K para conectarlo, como se muestra en el circuito.  


iii. Firmware.


En este apartado desarrollaremos una librería para el integrado y también el firmware del microcontrolador. El compilador utilizado es PIC C, pero esta librería puede adaptarse a otros compiladores. Comencemos.


a) Librería.


En esta librería controlaremos displays de 8 segmentos de cátodo común.

    
      
/************************************************************\
|                 LED DISPLAY DRIVER LIBRARY   	             |
|                 --------------------------                 |
| Author: Farias, Ezequiel                                   |
| Release date: MAY 18 2010                                  |
|------------------------------------------------------------|
|                                                            |
\.***********************************************************/

#ifndef SS_PIN
	#define SS_PIN PIN_C2
#endif

#ifndef DISPLAY_MODE_7
	#define DISPLAY_MODE 0x02		//6 digits - 12 segments
#else
	#define DISPLAY_MODE 0x03		//7 digits - 11 segments
#endif

#define NORM_INC_W 0x40
#define NORM_INC_R 0x42
#define NORM_FIX_W 0x44
// where COMMAND 2 is: | 0| 1| 0| 0|b3|b2|b1|b0|
//                                   |  |  |__|_________________________________
//                                   |  |     | Data Write & Read Mode Settings |
//                                   |  |     |---------------------------------|
//                                   |  |     | 00: Write Data to Display Mode  |
//                                   |  |     | 10: Read Key Data               |
//                                   |  |      *********************************
//                                   |  |      __________________________________________________
//                                   |  |_____|  Address Increment Mode Settings (Display Mode)  |
//                                   |        |--------------------------------------------------|
//                                   |        | 0: Increment Address after Data has been Written |
//                                   |        | 1: Fixed Address                                 |
//                                   |         **************************************************
//                                   |         __________________________
//                                   |________|      Mode Settings:      |
//                                            |--------------------------|
//                                            | 0: Normal Operation Mode |
//                                            | 1: Test Mode             |
//                                             **************************

#define SET_ADDRESS_INIT 0xC0

// Where COMMAND3 = | 1| 1| 0| 0|b3|b2|b1|b0|
//                               \_________/
//                                    |_______Address from 0x00 to 0x0D (0 to D)
//                                            Use the 14 bytes of the Display RAM
//                                            according with your display type.

//       COMMAND4 = | 1| 0| 0| 0|b3|b2|b1|b0|
//                                | \______/
//                                |     |    ____________________________
//                                |     |___| Dimming Quantity Settings: |
//                                |         |----------------------------|
//                                |         | 000: Pulse Width = 1/16    |
//                                |         | 001: Pulse Width = 2/16    |
//                                |         | 010: Pulse Width = 4/16    |
//                                |         | 011: Pulse Width = 10/16   |
//                                |         | 100: Pulse Width = 11/16   |
//                                |         | 101: Pulse Width = 12/16   |
//                                |         | 110: Pulse Width = 13/16   |
//                                |         | 111: Pulse Width = 14/16   |
//                                |          ****************************
//                                |         __________________________
//                                |________|     Display Settings:    |
//                                         |--------------------------|
//                                         | 0: Display OFF           |
//                                         | 1: Display ON            |
//                                          **************************

// ---------Functions ------------


//--------------------------------------------------------------------------------
// This function simply rotate the bits, in order to send LSB firstly, that way
// we can use more uC's which haven't that option or is not supported:
int mirror(int x){
   int j,data;
   for(j=0;j<8;j++){
      if(bit_test(x,j)){
         bit_set(data,7-j);
      }
      else{
         bit_clear(data,7-j);
      }
   }
   return data;
}


//-------------------------------------------------------------------------
// This function initialize the display:

void PT6961_init(){
	output_high(SS_PIN);
	int i,data;
	delay_ms(200);	 //time to initialize chip
	data = mirror(NORM_INC_W);
	output_low(SS_PIN);
	for(i=0;i<14;i++){
		spi_write(0x00);
	}

	data = mirror(SET_ADDRESS_INIT);
	output_low(SS_PIN);
	spi_write(data);
	output_high(SS_PIN);

	output_high(SS_PIN);
	data = mirror(DISPLAY_MODE);
	output_low(SS_PIN);
	spi_write(data);
	output_high(SS_PIN);

	output_low(SS_PIN);
	spi_write(mirror(0x80));
	output_high(SS_PIN);

	output_high(SS_PIN);
	data = mirror(DISPLAY_MODE);
	output_low(SS_PIN);
	spi_write(data);
	output_high(SS_PIN);

	output_low(SS_PIN);
	spi_write(mirror(0x8A));
	output_high(SS_PIN);
}


//-------------------------------------------------------------------------
// This function reads all the keys:

void read_keys(int* keys){
	int h;
	output_low(SS_PIN);
	spi_write(mirror(NORM_INC_R));
	for(h = 0; h<5; h++){
		keys[h] = spi_read(0xFF);
	}
	output_high(SS_PIN);
}


//-------------------------------------------------------------------------
// This function sends an entire array to all 14 DRAM address:

void write_dram(int *data){
	int j,aux;
	signed int i;
	output_low(SS_PIN);
	spi_write(mirror(0x40));
	output_high(SS_PIN);

	output_low(SS_PIN);
	spi_write(mirror(0xC0));
	output_high(SS_PIN);

	output_low(SS_PIN);
	for(j=0;j<7;j++){
		for(i=1;i>=0;i--){
			spi_write(mirror(data[(2*j)+i]));
		}
	}
	output_high(SS_PIN);
	aux = mirror(DISPLAY_MODE);
	output_low(SS_PIN);
	spi_write(aux);
	output_high(SS_PIN);

	output_low(SS_PIN);
	spi_write(mirror(0x8A));
	output_high(SS_PIN);
}


//-------------------------------------------------------------------------
// This function clears the display:

void cls(){
	int x;
	output_low(SS_PIN);
	spi_write(mirror(0x40));
	output_high(SS_PIN);
	output_low(SS_PIN);
	for(x=0;x<14;x++){
		spi_write(0x00);
	}
	output_high(SS_PIN);
	output_low(SS_PIN);
	spi_write(mirror(0x8A));
	output_high(SS_PIN);
}



b) Explicación.

De esta librería nos interesan 4 funciones: cls(), write_dram(int *data), read_keys(int keys) y PT6961_Init(). 

cls() = Borra el display.

write_dram(int *data) = Escribe en las direcciones DRAM del dispositivo el array dado como argumento.

read_keys(int keys) = Lee las teclas que fueron presionadas y guarda esa lectura en otro array.

PT6961_Init() = Inicializa el display, ajustando los parámetros iniciales necesarios.

En la sección que sigue haremos un ejemplo de uso de esta librería en el que quedará detallado el uso cada una de esas funciones y las definiciones previas.


c) Firmware.

El microcontrolador elegido es el PIC16F876A, con un cristal de 8MHz.



#include <16F876A.h>
#FUSES HS NOLVP NOBROWNOUT NOPUT
#USE delay(Clock=8M)

// 7 Digits - 11 Segments (Common Cathode) or 11 Digits - 7 Segments (Common Anode)
#define DISPLAY_MODE_7 TRUE
#define SS_PIN PIN_C2
#include 

#byte SSPBUF = 0x211  // SPI Buffer Address Register

// Modes:
#define SPI_MODE_0  (SPI_L_TO_H | SPI_XMIT_L_TO_H) 
#define SPI_MODE_1  (SPI_L_TO_H) 
#define SPI_MODE_2  (SPI_H_TO_L) 
#define SPI_MODE_3  (SPI_H_TO_L | SPI_XMIT_L_TO_H) 

#use SPI(MASTER,MODE=0,SPI1)

int display[14],keys[5],button;

void main(){
   output_high(SS_PIN);
   spi_init(TRUE);
   PT6961_init();
   //This is the array that we'll pass to the function later.
   display[0] = 0x76;      // First grid     H
   display[1] = 0x00;
   display[2] = 0x3F;      // Second grid    O
   display[3] = 0x00;
   display[4] = 0x38;      // Third grid     L
   display[5] = 0x00;
   display[6] = 0x77;      // Fourth grid    A
   display[7] = 0x00;
   display[8] = 0x00;
   display[9] = 0x00;
   display[10] = 0x00;
   display[11] = 0x00;
   display[12] = 0x00;
   display[13] = 0x00;
   write_dram(display);		//write the values of the "display" array on the DRAM
   delay_ms(2000);
   cls();					//clears the display
   /*
   
   0 = 0x3F
   1 = 0x06
   2 = 0x5B
   3 = 0x4F
   4 = 0x66
   5 = 0x6D
   6 = 0x7D
   7 = 0x07
   8 = 0x7F
   9 = 0x6F
   A = 0x77
   b = 0x7C
   C = 0x39
   d = 0x5E
   E = 0x79
   F = 0x71
   
   
   */
   
   /* ** KEY MAP: ******
    ____________________________________________________________________________________________________
   |            |          |          |          |          |          |          |          |          |
   |            |    B7    |    B6    |    B5    |    B4    |    B3    |    B2    |    B1    |    B0    |
   |____________|__________|__________|__________|__________|__________|__________|__________|__________|
   |            |          |          |          |          |          |          |          |          |
   |   Byte 1   |    XX    |    XX    |  SG2/K3  |  SG2/K2  |  SG2/K1  |  SG1/K3  |  SG1/K2  |  SG1/K1  |
   |____________|__________|__________|__________|__________|__________|__________|__________|__________|
   |            |          |          |          |          |          |          |          |          |
   |   Byte 2   |    XX    |    XX    |  SG4/K3  |  SG4/K2  |  SG4/K1  |  SG3/K3  |  SG3/K2  |  SG3/K1  |
   |____________|__________|__________|__________|__________|__________|__________|__________|__________|
   |            |          |          |          |          |          |          |          |          |
   |   Byte 3   |    XX    |    XX    |  SG6/K3  |  SG6/K2  |  SG6/K1  |  SG5/K3  |  SG5/K2  |  SG5/K1  |
   |____________|__________|__________|__________|__________|__________|__________|__________|__________|
   |            |          |          |          |          |          |          |          |          |
   |   Byte 4   |    XX    |    XX    |  SG8/K3  |  SG8/K2  |  SG8/K1  |  SG7/K3  |  SG7/K2  |  SG7/K1  |
   |____________|__________|__________|__________|__________|__________|__________|__________|__________|
   |            |          |          |          |          |          |          |          |          |
   |   Byte 5   |    XX    |    XX    |  SG0/K3  |  SG0/K2  |  SG0/K1  |  SG9/K3  |  SG9/K2  |  SG9/K1  |
   |____________|__________|__________|__________|__________|__________|__________|__________|__________|
   
   
   */
   
   while(true){
      while(input(PIN_B0)){
         read_keys(keys);		//we store the reading results on the "keys" array
         if((keys[0] | keys[1] | keys[2] | keys[3] | keys[4]) != 0x00){
            // if some button was pressed, scan it into the array, then show it on the display.
            if(keys[0] != 0x00){
               button = mirror(keys[0]);
               if(button == 0x01){
                  display[2] = 0x06;
                  display[6] = 0x06;
               }
               if(button == 0x02){
                  display[2] = 0x5B;
                  display[6] = 0x06;
               }
               if(button == 0x08){
                  display[2] = 0x06;
                  display[6] = 0x5B;
               }
               if(button == 0x10){
                  display[2] = 0x5B;
                  display[6] = 0x5B;
               }
            }
            // 5 6 7 8
            if(keys[1] != 0x00){
               button = mirror(keys[1]);
               if(button == 0x01){
                  display[2] = 0x06;
                  display[6] = 0x4F;
               }
               if(button == 0x02){
                  display[2] = 0x5B;
                  display[6] = 0x4F;
               }
               if(button == 0x08){
                  display[2] = 0x06;
                  display[6] = 0x66;
               }
               if(button == 0x10){
                  display[2] = 0x5B;
                  display[6] = 0x66;
               }
            }
            // 9 10 11 12
            if(keys[2] != 0x00){
               button = mirror(keys[2]);
               if(button == 0x01){
                  display[2] = 0x06;
                  display[6] = 0x6D;
               }
               if(button == 0x02){
                  display[2] = 0x5B;
                  display[6] = 0x6D;
               }
               if(button == 0x08){
                  display[2] = 0x06;
                  display[6] = 0x7D;
               }
               if(button == 0x10){
                  display[2] = 0x5B;
                  display[6] = 0x7D;
               }
            }
            display[0] = 0x38;      // First grid	"L"
            display[4] = 0x7C;      // Third grid	"b"
            write_dram(display);
            delay_ms(500);
            cls();
         }
      }
   }
}
    
    



Una vez finalizada la compilación, se procede a cargar el firmware al microcontrolador. El programa comienza, el display se ilumina mostrando un mensaje de bienvenida (HOLA), luego queda a la espera escaneando el teclado.



Si se pulsa uno de los de la fila superior mostrará L1bX  y si se pulsa uno de los de la fila inferior, mostrará L2bX, donde X es el número de botón que se ha pulsado, del 1 al 6, de izquierda a derecha, respectivamente.