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
  

No hay comentarios:

Publicar un comentario