INTRODUCCIÓN.
Este es el segundo artículo de la serie que se ocupa de las pantallas LCD basadas en el controlador PCD8544. Le recomiendo que lea aquí el anterior artículo, donde se describe abundante información, relacionada con estas pantallas o display de cristal líquido.
Las pantallas monocromáticas LCD están siendo aplicadas actualmente con mayor frecuencia, esto probablemente es debido a dos razones; una es, su bajo costo y la otra tal vez, a un mayor conocimiento de su controlador PCD8544 de 84×48 píxeles, dicho controlador se encarga del suministro, el ajuste de tensiones y la comunicación fácil con la pantalla vía Serial. Esto nos evita un montón de componentes y trabajos extra que, requeriría sin el controlador. Con el controlador de pantalla, podemos manejar sus más de 4.000 píxeles.
Un poco más de detalle, 84 píxeles dividido por 6 píxeles a lo ancho, de cada carácter (5 píxeles del carácter + 1 píxel de espacio, son 6 píxeles), nos proporciona 14 caracteres, esto por cada renglón. Claro, la altura de cada carácter es de 8 píxeles, estos realmente son 7 para el carácter mas 1 de espacio, lo que nos proporciona 6 filas, líneas de escritura o renglones.
Actualmente, tenemos una extensa biblioteca que, hace fácil hacer que muestren la impresión de texto o gráficos en un instante. Esta pantalla LCD es un dispositivo de 3,3 V, esto significa que no se debe conectar directamente a un microcontrolador de 5V como el Arduino o el PIC. Sin embargo, puede se conectar directamente a dispositivos como el Arduino Pro y Pro Mini (3.3V). Esta pantalla, según las hojas del fabricante, el controlador permite al LCD comunicarse vía SPI (bus Serial Peripherial Interfase), el cual es un protocolo muy común, para la comunicación serie de datos entre dispositivos.
En las imágenes anteriores, se muestran vistas de la pantalla más comúnmente utilizada por los principiantes que, con un poco de pericia, han recuperado, su pantalla de algún viejo nokia3310, un nokia5110 o similar. En la imagen de abajo, se pueden ver las soldaduras de los contactos de la pantalla con unos cables y un terminal de 8 pines en línea.
Para evitar un deterioro relativamente prematuro de estas pantallas, se deben manejar con tensiones bajas, según consta en las hojas del fabricante Philips, las pantallas controladas por el PCD8544, trabajan de los 2’7V hasta los 3’3V, de manera que, sus E/S deben ser ajustados a este nivel de tensión.
Es cierto que pueden funcionar hasta los 4’5V, sin embargo, su vida se verá drásticamente reducida a unos pocos días en el mejor de los casos, personalmente aconsejo que se tenga muy en consideración estas normas. Se trata de un sencillo sistema divisor de tensión realizado con resistencias, como se puede apreciar en el circuito que se adjunta a continuación. Para estos casos y como costumbre, recomiendo utilizar un circuito similar al que se muestra aquí:
Esta información, es la más común y se puede encontrar en cierto modo, en la mayoría de artículos sobre el tema. Además, una vez más, después de haber conectado correctamente el dispositivo, nos encontramos con el código que, podemos lograr de alguna forma en los propios artículos que, proliferan en la red. Para empezar, tenemos que proveernos de las librerías a descargar, y aquí está el primer paso para vernos en un galimatías de bibliotecas que en principio deberían servir para el mismo fin que es, controlar de forma correcta las PCD8544.
Otra cosa. Hemos dicho que el LCD funciona a 3,3 V así que, tal vez tengamos que utilizar un cambiador de nivel, para lo cual utilizaremos un chip que permita usar la pantalla, con un microcontrolador de 5V. En el caso de estar usando un microcontrolador 3.3V, se puede omitir el cambiador de nivel. El circuito cambiador será el CD4050 o HC4050, vea el esquema siguiente:
Sin embargo, el capítulo referente a las librerías es su punto algo conflictivo ya que, al no respetarse ciertas reglas, no existe un consenso y por tanto, un programa que hayamos realizado con unas librerías, quien lo quiera ejecutar, como no disponga de dichas librerías se convierte fracaso, ya que está tratando de hacer algo que no está cubierto por la biblioteca, es por lo tanto, un programa que no puede usar un principiante.
Entre las librerías que están circulando (ver Google, acerca del PCD8544), el principiante, realmente puede llegar a tener la sensación de estar perdido. Esto viene a colación, por la descentralización que, de alguna manera se pone de manifiesto con el tan pretendido software libre. Y digo yo, software libre si, aún así, considero contraproducente para la propia plataforma Arduino, la disparidad de criterios en cuanto a las bibliotecas para Arduino, pues si no se respetan unas bases, bueno sería poner unas reglas que sirvan de referencia.
Debo decir que me refiero concretamente a las librerías editadas para los PCD8544 y monocromos similares. Estas pantallas, disponen de su propio controlador de Philps, el mencionado PCD8544. El controlador de esta pantalla, no dispone de un mapa de caracteres y esto obliga a incluir dicho mapa de caracteres en cada programa, este hecho, lejos de ser un problema (nos permitirá en un futuro, cuando tengamos mayores conocimientos de él), crear nuestros propios caracteres, si lo consideramos necesario.
Es evidente que, las librerías disponibles en la web de Arduino, son librerías que sirven para un gran número de posibilidades. Algunos ejemplos de programas los podemos ver en Arduino, en pcd8544 o en Adafruit, estas son tres páginas con ejemplos de programas que utilizan sus propias librerías, si usted quiere saber a que me refiero, es muy simple, trate de ejecutar uno de los ejemplos con la librería del otro. ¡Es frustrante!
Si, ya se que, cada librería es para una cosa. Sin embargo, no estoy del todo de acuerdo. El cuerpo de la librería, debe ser el mismo y las rutinas deberían ser válidas para cualquier programa, no obstante, ese no es el caso. Hasta el extremo que en unas librerías, es necesario incluir en el programa el mapa de caracteres y en el otro no, por que ya viene en una librería adjunta (que acertadamente llaman ‘charset.cpp‘), lo que conlleva un código más compacto y a la hora de revisarlo, es mucho más manejable.
Alguna razón si tengo, no es una manía. Es muy fácil introducir un error en un programa cuando tienes que ‘picar‘ un extenso código y este es justamente el caso, si, siempre puedes hacer un copiado/pegado. A mí, me parece en cierto modo, injustificado este riesgo y siempre habrá quien diga que se puede hacer el adjunto a la librería, como ocurre en el segundo caso. Y digo yo, todo el mundo, no tiene tantos conocimientos a la hora de realizar esas aplicaciones, el principiante, ya tiene bastante trabajo y motivación, con crear sus propios códigos, como para que encima tenga que hacer su propia librería o archivo ‘.cpp‘ o similar, no estoy de acuerdo.
Por ese motivo pensé en realizar este artículo, tratando de ayudar a los que empiezan, así como, a todos los que decidan esta vía. Pero, vayamos paso a paso.
Una forma muy simple de comprender lo que se trata en este tutorial, es como siempre mediante ejemplos que muestren y mejoren nuestra experiencia sobre los elementos que manejamos habitualmente y que en muchas ocasiones damos por conocida su manera de operar.
Veamos como mostrar en nuestra pantalla PCD8544, un sencillo programa que muestre un simple saludo, creo que nos puede servir para exponer los pros y contras que observemos, no otra cosa. Por otra parte, el código tiene ‘tela’, no hay nada de código, se puede meter algún error sin querer, y la liamos. Ponte a buscar donde está el problema. Aunque la frase se refiere a otro aspecto, comprenderemos ahora, la necesidad de hacer un ‘código compacto’, estamos hablando de 272 líneas, para un simple saludo.
HOLA AMIGOS.
Este será el saludo que se mostrará.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 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 91 92 93 94 95 96 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 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 |
/* ejemplo01_lcd5110.pde Este Código tiene características adicionales incluyendo una función de posicionamiento XY en la pantalla y una función Dibuja la linea de Nokia 3310 LCD It is modded from the original http://playground.arduino.cc/Code/PCD8544 */ // Mods by Jim Park // jim(^dOt^)buzz(^aT^)gmail(^dOt^)com // // hope it works for you #define PIN_SCE 7 // LCD CS .... Pin 3 #define PIN_RESET 6 // LCD RST .... Pin 1 #define PIN_DC 5 // LCD Dat/Com. Pin 5 #define PIN_SDIN 4 // LCD SPIDat . Pin 6 #define PIN_SCLK 3 // LCD SPIClk . Pin 4 // LCD Gnd .... Pin 2 // LCD Vcc .... Pin 8 // LCD Vlcd ... Pin 7 #define LCD_C LOW #define LCD_D HIGH #define LCD_X 84 #define LCD_Y 48 #define LCD_CMD 0 int a = 0; static const byte ASCII[][5] = { {0x00, 0x00, 0x00, 0x00, 0x00} // 20 ,{0x00, 0x00, 0x5f, 0x00, 0x00} // 21 ! ,{0x00, 0x07, 0x00, 0x07, 0x00} // 22 " ,{0x14, 0x7f, 0x14, 0x7f, 0x14} // 23 # ,{0x24, 0x2a, 0x7f, 0x2a, 0x12} // 24 $ ,{0x23, 0x13, 0x08, 0x64, 0x62} // 25 % ,{0x36, 0x49, 0x55, 0x22, 0x50} // 26 & ,{0x00, 0x05, 0x03, 0x00, 0x00} // 27 ' ,{0x00, 0x1c, 0x22, 0x41, 0x00} // 28 ( ,{0x00, 0x41, 0x22, 0x1c, 0x00} // 29 ) ,{0x14, 0x08, 0x3e, 0x08, 0x14} // 2a * ,{0x08, 0x08, 0x3e, 0x08, 0x08} // 2b + ,{0x00, 0x50, 0x30, 0x00, 0x00} // 2c , ,{0x08, 0x08, 0x08, 0x08, 0x08} // 2d - ,{0x00, 0x60, 0x60, 0x00, 0x00} // 2e . ,{0x20, 0x10, 0x08, 0x04, 0x02} // 2f / ,{0x3e, 0x51, 0x49, 0x45, 0x3e} // 30 0 ,{0x00, 0x42, 0x7f, 0x40, 0x00} // 31 1 ,{0x42, 0x61, 0x51, 0x49, 0x46} // 32 2 ,{0x21, 0x41, 0x45, 0x4b, 0x31} // 33 3 ,{0x18, 0x14, 0x12, 0x7f, 0x10} // 34 4 ,{0x27, 0x45, 0x45, 0x45, 0x39} // 35 5 ,{0x3c, 0x4a, 0x49, 0x49, 0x30} // 36 6 ,{0x01, 0x71, 0x09, 0x05, 0x03} // 37 7 ,{0x36, 0x49, 0x49, 0x49, 0x36} // 38 8 ,{0x06, 0x49, 0x49, 0x29, 0x1e} // 39 9 ,{0x00, 0x36, 0x36, 0x00, 0x00} // 3a : ,{0x00, 0x56, 0x36, 0x00, 0x00} // 3b ; ,{0x08, 0x14, 0x22, 0x41, 0x00} // 3c < ,{0x14, 0x14, 0x14, 0x14, 0x14} // 3d = ,{0x00, 0x41, 0x22, 0x14, 0x08} // 3e > ,{0x02, 0x01, 0x51, 0x09, 0x06} // 3f ? ,{0x32, 0x49, 0x79, 0x41, 0x3e} // 40 @ ,{0x7e, 0x11, 0x11, 0x11, 0x7e} // 41 A ,{0x7f, 0x49, 0x49, 0x49, 0x36} // 42 B ,{0x3e, 0x41, 0x41, 0x41, 0x22} // 43 C ,{0x7f, 0x41, 0x41, 0x22, 0x1c} // 44 D ,{0x7f, 0x49, 0x49, 0x49, 0x41} // 45 E ,{0x7f, 0x09, 0x09, 0x09, 0x01} // 46 F ,{0x3e, 0x41, 0x49, 0x49, 0x7a} // 47 G ,{0x7f, 0x08, 0x08, 0x08, 0x7f} // 48 H ,{0x00, 0x41, 0x7f, 0x41, 0x00} // 49 I ,{0x20, 0x40, 0x41, 0x3f, 0x01} // 4a J ,{0x7f, 0x08, 0x14, 0x22, 0x41} // 4b K ,{0x7f, 0x40, 0x40, 0x40, 0x40} // 4c L ,{0x7f, 0x02, 0x0c, 0x02, 0x7f} // 4d M ,{0x7f, 0x04, 0x08, 0x10, 0x7f} // 4e N ,{0x3e, 0x41, 0x41, 0x41, 0x3e} // 4f O ,{0x7f, 0x09, 0x09, 0x09, 0x06} // 50 P ,{0x3e, 0x41, 0x51, 0x21, 0x5e} // 51 Q ,{0x7f, 0x09, 0x19, 0x29, 0x46} // 52 R ,{0x46, 0x49, 0x49, 0x49, 0x31} // 53 S ,{0x01, 0x01, 0x7f, 0x01, 0x01} // 54 T ,{0x3f, 0x40, 0x40, 0x40, 0x3f} // 55 U ,{0x1f, 0x20, 0x40, 0x20, 0x1f} // 56 V ,{0x3f, 0x40, 0x38, 0x40, 0x3f} // 57 W ,{0x63, 0x14, 0x08, 0x14, 0x63} // 58 X ,{0x07, 0x08, 0x70, 0x08, 0x07} // 59 Y ,{0x61, 0x51, 0x49, 0x45, 0x43} // 5a Z ,{0x00, 0x7f, 0x41, 0x41, 0x00} // 5b [ ,{0x02, 0x04, 0x08, 0x10, 0x20} // 5c ¥ ,{0x00, 0x41, 0x41, 0x7f, 0x00} // 5d ] ,{0x04, 0x02, 0x01, 0x02, 0x04} // 5e ^ ,{0x40, 0x40, 0x40, 0x40, 0x40} // 5f _ ,{0x00, 0x01, 0x02, 0x04, 0x00} // 60 ` ,{0x20, 0x54, 0x54, 0x54, 0x78} // 61 a ,{0x7f, 0x48, 0x44, 0x44, 0x38} // 62 b ,{0x38, 0x44, 0x44, 0x44, 0x20} // 63 c ,{0x38, 0x44, 0x44, 0x48, 0x7f} // 64 d ,{0x38, 0x54, 0x54, 0x54, 0x18} // 65 e ,{0x08, 0x7e, 0x09, 0x01, 0x02} // 66 f ,{0x0c, 0x52, 0x52, 0x52, 0x3e} // 67 g ,{0x7f, 0x08, 0x04, 0x04, 0x78} // 68 h ,{0x00, 0x44, 0x7d, 0x40, 0x00} // 69 i ,{0x20, 0x40, 0x44, 0x3d, 0x00} // 6a j ,{0x7f, 0x10, 0x28, 0x44, 0x00} // 6b k ,{0x00, 0x41, 0x7f, 0x40, 0x00} // 6c l ,{0x7c, 0x04, 0x18, 0x04, 0x78} // 6d m ,{0x7c, 0x08, 0x04, 0x04, 0x78} // 6e n ,{0x38, 0x44, 0x44, 0x44, 0x38} // 6f o ,{0x7c, 0x14, 0x14, 0x14, 0x08} // 70 p ,{0x08, 0x14, 0x14, 0x18, 0x7c} // 71 q ,{0x7c, 0x08, 0x04, 0x04, 0x08} // 72 r ,{0x48, 0x54, 0x54, 0x54, 0x20} // 73 s ,{0x04, 0x3f, 0x44, 0x40, 0x20} // 74 t ,{0x3c, 0x40, 0x40, 0x20, 0x7c} // 75 u ,{0x1c, 0x20, 0x40, 0x20, 0x1c} // 76 v ,{0x3c, 0x40, 0x30, 0x40, 0x3c} // 77 w ,{0x44, 0x28, 0x10, 0x28, 0x44} // 78 x ,{0x0c, 0x50, 0x50, 0x50, 0x3c} // 79 y ,{0x44, 0x64, 0x54, 0x4c, 0x44} // 7a z ,{0x00, 0x08, 0x36, 0x41, 0x00} // 7b { ,{0x00, 0x00, 0x7f, 0x00, 0x00} // 7c | ,{0x00, 0x41, 0x36, 0x08, 0x00} // 7d } ,{0x10, 0x08, 0x08, 0x10, 0x08} // 7e ← ,{0x00, 0x06, 0x09, 0x09, 0x06} // 7f → }; void LcdCharacter(char character) { LcdWrite(LCD_D, 0x00); for (int index = 0; index < 5; index++) { LcdWrite(LCD_D, ASCII[character - 0x20][index]); } LcdWrite(LCD_D, 0x00); } void LcdClear(void) { for (int index = 0; index < LCD_X * LCD_Y / 8; index++) { LcdWrite(LCD_D, 0x00); } } void LcdInitialise(void) { pinMode(PIN_SCE, OUTPUT); pinMode(PIN_RESET, OUTPUT); pinMode(PIN_DC, OUTPUT); pinMode(PIN_SDIN, OUTPUT); pinMode(PIN_SCLK, OUTPUT); digitalWrite(PIN_RESET, LOW); delay(1); digitalWrite(PIN_RESET, HIGH); LcdWrite( LCD_CMD, 0x21 ); // LCD Extended Commands. LcdWrite( LCD_CMD, 0xBf ); // Set LCD Vop (Contrast). //B1 LcdWrite( LCD_CMD, 0x04 ); // Set Temp coefficent. //0x04 LcdWrite( LCD_CMD, 0x14 ); // LCD bias mode 1:48. //0x13 LcdWrite( LCD_CMD, 0x0C ); // LCD en modo normal; 0x0D para invertir LcdWrite(LCD_C, 0x20); LcdWrite(LCD_C, 0x0C); } void LcdString(char *characters) { while (*characters) { LcdCharacter(*characters++); } } void LcdWrite(byte dc, byte data) { digitalWrite(PIN_DC, dc); digitalWrite(PIN_SCE, LOW); shiftOut(PIN_SDIN, PIN_SCLK, MSBFIRST, data); digitalWrite(PIN_SCE, HIGH); } // gotoXY routina para posicionar cursor // x - range: 0 to 84 // y - range: 0 to 5 void gotoXY(int x, int y) { LcdWrite( 0, 0x80 | x); // Column LcdWrite( 0, 0x40 | y); // Row } void drawLine(void) { unsigned char j; for(j=0; j< 84; j++) { // top gotoXY (j,0); LcdWrite (1,0x01); } for(j=0; j< 84; j++) { //Bottom gotoXY (j,5); LcdWrite (1,0x80); } for(j=0; j< 6; j++) { // Right gotoXY (83,j); LcdWrite (1,0xff); } for(j=0; j< 6; j++) { // Left gotoXY (0,j); LcdWrite (1,0xff); } } void setup(void) { LcdInitialise(); LcdClear(); } void loop(void) { // Muestra algunos caracteres animados simples // int a,b; char Str[15]; // Dibuja un Cuadro for(b=1000; b>0; b--){ drawLine(); for(a=0; a< =5 ; a++){ gotoXY(4,1); // Mete texto en Cuadro LcdString ("TestDisplay"); gotoXY(2,3); LcdCharacter('='); LcdCharacter('H'); LcdCharacter('O'); LcdCharacter('L'); LcdCharacter('A'); LcdCharacter(' '); LcdCharacter('A'); LcdCharacter('M'); LcdCharacter('I'); LcdCharacter('G'); LcdCharacter('O'); LcdCharacter('='); //Dibuja = en esta posicion gotoXY(2,5); LcdCharacter('-'); delay(1 *1000); gotoXY(2,3); LcdCharacter('-'); LcdCharacter('h'); LcdCharacter('o'); LcdCharacter('l'); LcdCharacter('a'); LcdCharacter(' '); LcdCharacter('a'); LcdCharacter('m'); LcdCharacter('i'); LcdCharacter('g'); LcdCharacter('o'); LcdCharacter('-'); // Dibuja _ en esta posicion gotoXY(2,5); LcdCharacter('_'); delay(.6* 1000); } } } |
En este código, encontramos además un pequeño ‘crash’ (si, como vidrio roto), pues no llena la totalidad de la pantalla y eso no queda muy profesional, verdad. Tendremos que indagar en la propia librería para ver si encontramos alguna posibilidad. Y ahora que comento la librería, bueno será que usted utilice estas librerías, si quiere ver como funciona. Ábralo, sin miedo y vea el código. Tranquilo, así, hay muchos códigos, y acaban funcionando.
ANALICEMOS UN POCO DE CÓDIGO.
En el primer paso se deben establecer las definiciones, en ellas diremos a Arduino que pines vamos a utilizar y a que corresponde cada uno. Esto no siempre es tan sencillo, pero a eso ya volveremos más adelante. Las cinco líneas de salidas de Arduino que vamos a utilizar para comunicarnos con la pantalla son las siguientes:
1 2 3 4 5 |
#define PIN_SCE 7 // LCD CS .... Pin 3 #define PIN_RESET 6 // LCD RST .... Pin 1 #define PIN_DC 5 // LCD Dat/Com. Pin 5 #define PIN_SDIN 4 // LCD SPIDat . Pin 6 #define PIN_SCLK 3 // LCD SPIClk . Pin 4 |
Personalmente, cuando se presenta una situación como la presente, tiendo a usar, siempre que pueda, las mismas líneas para todos los códigos, de modo que elimino un posible problema de comunicación bastante importante. Y, eso no es todo, siempre que pueda usted, copie y pegue, esta es una técnica que ahorra bastante tiempo y evita errores difíciles de encontrar, por supuesto, comente todo lo que pueda sus códigos, nunca me cansaré de repetirlo.
Estas pantallas, se deben inicializar, es una condición necesaria, mediante una rutina en la que asignamos los pines de la pantalla como pines de entrada, o sea, que la pantalla sólo recibe datos y en ningún caso envía dato alguno de respuesta. Por lo tanto, en Arduino le diremos que estos pines serán de salida (OUTPUT). Además, una condición especial es la referida al pin de RST (Reset), observe el código, y verá que necesita restablecer en cada inicialización, por ese motivo la orden; digitalWrite(PIN_RESET), primero va a bajo (LOW) y una fracción de segundo después, va a alto (HIGH).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
void LcdInitialise(void) { pinMode(PIN_SCE, OUTPUT); pinMode(PIN_RESET, OUTPUT); pinMode(PIN_DC, OUTPUT); pinMode(PIN_SDIN, OUTPUT); pinMode(PIN_SCLK, OUTPUT); digitalWrite(PIN_RESET, LOW); delay(1); digitalWrite(PIN_RESET, HIGH); LcdWrite( LCD_CMD, 0x21 ); // LCD Extended Commands. LcdWrite( LCD_CMD, 0xBf ); // Set LCD Vop (Contrast). //B1 LcdWrite( LCD_CMD, 0x04 ); // Set Temp coefficent. //0x04 LcdWrite( LCD_CMD, 0x14 ); // LCD bias mode 1:48. //0x13 LcdWrite( LCD_CMD, 0x0C ); // LCD en modo normal, 0x0D para invertir LcdWrite(LCD_C, 0x20); LcdWrite(LCD_C, 0x0C); } |
Bueno, me he saltado el mapa de caracteres, eso se verá en su momento. Se puede ver la rutina ‘LcdClear(void)‘ con un bucle ‘for‘ que recorre toda la pantalla, encargada de limpiarla o eso es lo que debería hacer. Pero que ocurre, pruébelo. Lo que ocurre es que realmente, no ‘limpia’ toda la pantalla, deja bastante que desear. Me refiero a esta parte del código:
1 2 3 4 5 |
void LcdClear(void) { for (int index = 0; index < LCD_X * LCD_Y / 8; index++) { LcdWrite(LCD_D, 0x00); } } |
Por que ocurre esto, la rutina parece recorrer toda la pantalla. Eso, no es del todo cierto, así que, ‘le puse un poco de aceite y el candil, se iluminó’, me surgió la pregunta, por qué en la rutina se divide por 8, no sería mejor limpiar píxel a píxel. Y, de esta forma queda así:
1 2 3 4 5 |
void LcdClear(void) { for (int index = 0; index < LCD_X * LCD_Y; index++) { LcdWrite(LCD_D, 0x00); } } |
Modifique su código, y compile de nuevo. Que ocurre ahora. ¡Aja! Ahora si limpia toda la pantalla y muestra el texto que habíamos codificado. Una rutina a tener en cuenta es la referente al posicionado X-Y del cursor, para el carácter que queremos escribir.
1 2 3 4 |
void gotoXY(int x, int y) { LcdWrite( 0, 0x80 | x); // Column. LcdWrite( 0, 0x40 | y); // Row. } |
Estudie este código y analice como se genera el texto, como se indica su posición en cada punto de la zona visible. En el siguiente ejemplo, se puede apreciar unos cambios que pretenden mejorar las prestaciones de esta pequeña pantalla con su controlador. Este ejemplo, no requiere de una librería, digamos ‘especializada’, se trata de la librería estándar ‘stdlib.h’
LCD 3310 COMPLETA.
En esta ocasión mostrará mejores posibilidades.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 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 91 92 93 94 95 96 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 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 |
/* lcd_test2.pde Este Código tiene características adicionales para Nokia 3310 LCD incluyendo una función de posicionamiento XY en la pantalla. Se modificó a partir del original que presenta http://playground.arduino.cc/Code/PCD8544 // // Ahora, muestra un mayor potencial para sus proyectos // Espero que le funcione a usted // Mods by V. García. 25/06/2013. */ #include "stdlib.h" #define PIN_DC 5 // LCD Dat/Com. Pin 5 #define PIN_SCE 7 // LCD CS .... Pin 3 #define PIN_SDIN 4 // LCD SPIDat . Pin 6 #define PIN_RESET 6 // LCD RST .... Pin 1 #define PIN_SCLK 3 // LCD SPIClk . Pin 4 // LCD Gnd .... Pin 2 // LCD Vcc .... Pin 8 // LCD Vlcd ... Pin 7 #define LCD_C LOW #define LCD_D HIGH #define LCD_X 95 // 84 #define LCD_Y 64 // 48 #define LCD_CMD 0 int progress = 0; int prev_prog = 0; int pixels[85][6] = {{0}}; int a = 0; static const byte ASCII[][5] ={ {0x00, 0x00, 0x00, 0x00, 0x00} // 20 ,{0x00, 0x00, 0x5f, 0x00, 0x00} // 21 ! ,{0x00, 0x07, 0x00, 0x07, 0x00} // 22 " ,{0x14, 0x7f, 0x14, 0x7f, 0x14} // 23 # ,{0x24, 0x2a, 0x7f, 0x2a, 0x12} // 24 $ ,{0x23, 0x13, 0x08, 0x64, 0x62} // 25 % ,{0x36, 0x49, 0x55, 0x22, 0x50} // 26 & ,{0x00, 0x05, 0x03, 0x00, 0x00} // 27 ' ,{0x00, 0x1c, 0x22, 0x41, 0x00} // 28 ( ,{0x00, 0x41, 0x22, 0x1c, 0x00} // 29 ) ,{0x14, 0x08, 0x3e, 0x08, 0x14} // 2a * ,{0x08, 0x08, 0x3e, 0x08, 0x08} // 2b + ,{0x00, 0x50, 0x30, 0x00, 0x00} // 2c , ,{0x08, 0x08, 0x08, 0x08, 0x08} // 2d - ,{0x00, 0x60, 0x60, 0x00, 0x00} // 2e . ,{0x20, 0x10, 0x08, 0x04, 0x02} // 2f / ,{0x3e, 0x51, 0x49, 0x45, 0x3e} // 30 0 ,{0x00, 0x42, 0x7f, 0x40, 0x00} // 31 1 ,{0x42, 0x61, 0x51, 0x49, 0x46} // 32 2 ,{0x21, 0x41, 0x45, 0x4b, 0x31} // 33 3 ,{0x18, 0x14, 0x12, 0x7f, 0x10} // 34 4 ,{0x27, 0x45, 0x45, 0x45, 0x39} // 35 5 ,{0x3c, 0x4a, 0x49, 0x49, 0x30} // 36 6 ,{0x01, 0x71, 0x09, 0x05, 0x03} // 37 7 ,{0x36, 0x49, 0x49, 0x49, 0x36} // 38 8 ,{0x06, 0x49, 0x49, 0x29, 0x1e} // 39 9 ,{0x00, 0x36, 0x36, 0x00, 0x00} // 3a : ,{0x00, 0x56, 0x36, 0x00, 0x00} // 3b ; ,{0x08, 0x14, 0x22, 0x41, 0x00} // 3c < ,{0x14, 0x14, 0x14, 0x14, 0x14} // 3d = ,{0x00, 0x41, 0x22, 0x14, 0x08} // 3e > ,{0x02, 0x01, 0x51, 0x09, 0x06} // 3f ? ,{0x32, 0x49, 0x79, 0x41, 0x3e} // 40 @ ,{0x7e, 0x11, 0x11, 0x11, 0x7e} // 41 A ,{0x7f, 0x49, 0x49, 0x49, 0x36} // 42 B ,{0x3e, 0x41, 0x41, 0x41, 0x22} // 43 C ,{0x7f, 0x41, 0x41, 0x22, 0x1c} // 44 D ,{0x7f, 0x49, 0x49, 0x49, 0x41} // 45 E ,{0x7f, 0x09, 0x09, 0x09, 0x01} // 46 F ,{0x3e, 0x41, 0x49, 0x49, 0x7a} // 47 G ,{0x7f, 0x08, 0x08, 0x08, 0x7f} // 48 H ,{0x00, 0x41, 0x7f, 0x41, 0x00} // 49 I ,{0x20, 0x40, 0x41, 0x3f, 0x01} // 4a J ,{0x7f, 0x08, 0x14, 0x22, 0x41} // 4b K ,{0x7f, 0x40, 0x40, 0x40, 0x40} // 4c L ,{0x7f, 0x02, 0x0c, 0x02, 0x7f} // 4d M ,{0x7f, 0x04, 0x08, 0x10, 0x7f} // 4e N ,{0x3e, 0x41, 0x41, 0x41, 0x3e} // 4f O ,{0x7f, 0x09, 0x09, 0x09, 0x06} // 50 P ,{0x3e, 0x41, 0x51, 0x21, 0x5e} // 51 Q ,{0x7f, 0x09, 0x19, 0x29, 0x46} // 52 R ,{0x46, 0x49, 0x49, 0x49, 0x31} // 53 S ,{0x01, 0x01, 0x7f, 0x01, 0x01} // 54 T ,{0x3f, 0x40, 0x40, 0x40, 0x3f} // 55 U ,{0x1f, 0x20, 0x40, 0x20, 0x1f} // 56 V ,{0x3f, 0x40, 0x38, 0x40, 0x3f} // 57 W ,{0x63, 0x14, 0x08, 0x14, 0x63} // 58 X ,{0x07, 0x08, 0x70, 0x08, 0x07} // 59 Y ,{0x61, 0x51, 0x49, 0x45, 0x43} // 5a Z ,{0x00, 0x7f, 0x41, 0x41, 0x00} // 5b [ ,{0x02, 0x04, 0x08, 0x10, 0x20} // 5c ¥ ,{0x00, 0x41, 0x41, 0x7f, 0x00} // 5d ] ,{0x04, 0x02, 0x01, 0x02, 0x04} // 5e ^ ,{0x40, 0x40, 0x40, 0x40, 0x40} // 5f _ ,{0x00, 0x01, 0x02, 0x04, 0x00} // 60 ` ,{0x20, 0x54, 0x54, 0x54, 0x78} // 61 a ,{0x7f, 0x48, 0x44, 0x44, 0x38} // 62 b ,{0x38, 0x44, 0x44, 0x44, 0x20} // 63 c ,{0x38, 0x44, 0x44, 0x48, 0x7f} // 64 d ,{0x38, 0x54, 0x54, 0x54, 0x18} // 65 e ,{0x08, 0x7e, 0x09, 0x01, 0x02} // 66 f ,{0x0c, 0x52, 0x52, 0x52, 0x3e} // 67 g ,{0x7f, 0x08, 0x04, 0x04, 0x78} // 68 h ,{0x00, 0x44, 0x7d, 0x40, 0x00} // 69 i ,{0x20, 0x40, 0x44, 0x3d, 0x00} // 6a j ,{0x7f, 0x10, 0x28, 0x44, 0x00} // 6b k ,{0x00, 0x41, 0x7f, 0x40, 0x00} // 6c l ,{0x7c, 0x04, 0x18, 0x04, 0x78} // 6d m ,{0x7c, 0x08, 0x04, 0x04, 0x78} // 6e n ,{0x38, 0x44, 0x44, 0x44, 0x38} // 6f o ,{0x7c, 0x14, 0x14, 0x14, 0x08} // 70 p ,{0x08, 0x14, 0x14, 0x18, 0x7c} // 71 q ,{0x7c, 0x08, 0x04, 0x04, 0x08} // 72 r ,{0x48, 0x54, 0x54, 0x54, 0x20} // 73 s ,{0x04, 0x3f, 0x44, 0x40, 0x20} // 74 t ,{0x3c, 0x40, 0x40, 0x20, 0x7c} // 75 u ,{0x1c, 0x20, 0x40, 0x20, 0x1c} // 76 v ,{0x3c, 0x40, 0x30, 0x40, 0x3c} // 77 w ,{0x44, 0x28, 0x10, 0x28, 0x44} // 78 x ,{0x0c, 0x50, 0x50, 0x50, 0x3c} // 79 y ,{0x44, 0x64, 0x54, 0x4c, 0x44} // 7a z ,{0x00, 0x08, 0x36, 0x41, 0x00} // 7b { ,{0x00, 0x00, 0x7f, 0x00, 0x00} // 7c | ,{0x00, 0x41, 0x36, 0x08, 0x00} // 7d } ,{0x10, 0x08, 0x08, 0x10, 0x08} // 7e ← // ,{0x00, 0x06, 0x09, 0x09, 0x06} // 7f → ,{0x02, 0x05, 0x02, 0x00, 0x00} // 7f º }; void LcdCharacter(char character) { LcdWrite(LCD_D, 0x00); for (int index = 0; index < 5; index++) { LcdWrite(LCD_D, ASCII[character - 0x20][index]); } LcdWrite(LCD_D, 0x00); } void LcdClear(void) { for (int index = 0; index < LCD_X * LCD_Y; index++) { LcdWrite(LCD_D, 0x00); } } void LcdInitialise(void) { pinMode(PIN_SCE, OUTPUT); pinMode(PIN_RESET, OUTPUT); pinMode(PIN_DC, OUTPUT); pinMode(PIN_SDIN, OUTPUT); pinMode(PIN_SCLK, OUTPUT); digitalWrite(PIN_RESET, LOW); delay(1); digitalWrite(PIN_RESET, HIGH); LcdWrite( LCD_CMD, 0x21 ); // LCD Extended Commands. LcdWrite( LCD_CMD, 0xB9 ); // Set LCD Vop (Contrast). //B1 LcdWrite( LCD_CMD, 0x04 ); // Set Temp coefficent. //0x04 LcdWrite( LCD_CMD, 0x14 ); // LCD bias mode 1:48. //0x13 LcdWrite( LCD_CMD, 0x0C ); // LCD in 0x0C normal mode. 0x0d for inverse LcdWrite(LCD_C, 0x20); LcdWrite(LCD_C, 0x0C); } void LcdString(char *characters) { while (*characters) { LcdCharacter(*characters++); } } void LcdWrite(byte dc, byte data) { digitalWrite(PIN_DC, dc); digitalWrite(PIN_SCE, LOW); shiftOut(PIN_SDIN, PIN_SCLK, MSBFIRST, data); digitalWrite(PIN_SCE, HIGH); } // gotoXY routine to position cursor // x - range: 0 to 84 // y - range: 0 to 5 void gotoXY(int x, int y) { LcdWrite( 0, 0x80 | x); // Column. LcdWrite( 0, 0x40 | y); // Row. } // Activa o desactivar un pixel especifico // x: 0 to 84, y: 0 to 48 void setPixel(int x, int y, int d) { // if (x > 84 || y > 48) { return; } if (x > 96 || y > 56) { return; } // El LCD tiene 6 filas, con 8 píxeles por fila. // 'y_mod' es la fila que el píxel es encendido // 'y_pix' is the pixel in that row we want to enable/disable int y_mod = (int)(y >> 3); // >>3 divides by 8 int y_pix = (y-(y_mod << 3)); // << 3 multiplies by 8 // int val = 1 << y_pix; // Tenemos que mantener un seguimiento de los píxeles son de // encendido/apagado para escribir el carácter correcto en la pantalla LCD. if (d){ pixels[x][y_mod] |= val; } else { pixels[x][y_mod] &= ~val; } // Write the updated pixel out to the LCD // TODO Check if the pixel is already in the state requested, // if so, don't write to LCD. gotoXY(x,y_mod); LcdWrite (1,pixels[x][y_mod]); } void drawLine(int x1, int y1, int x2, int y2) { } // Dibuja una linea horizontal de anchura w desde x,y void drawHorizontalLine(int x, int y, int w){ drawHorizontalLineXY(x,x+w,y); } // Draw a horizontal line between x1 and x2 at row y void drawHorizontalLineXY(int x1, int x2, int y){ for (int i=x1;i<=x2;i++) { setPixel(i,y,1); } } // Draw a vertical line of height h from x,y void drawVerticalLine(int x, int y, int h){ drawVerticalLineXY(y,y+h,x); } // Draw a vertical line from y1 to y2 on column x void drawVerticalLineXY(int y1, int y2, int x){ for (int i=y1;i<=y2;i++) { setPixel(x,i,1); } } void clearPixels() { for (int x=0;x<83;x++) { for(int y=0;y<47;y++) { pixels[x][y] = 0; } } } // Draw a rectangle of width w and height h from x,y void drawRect(int x, int y, int w, int h) { drawHorizontalLineXY(x,x+w,y); drawHorizontalLineXY(x,x+w,y+h); drawVerticalLineXY(y,y+h,x); drawVerticalLineXY(y,y+h,x+w); } // ***** añado ******* void initProgressBar() { LcdClear(); clearPixels(); drawRect(10,21, 64, 5); } void setProgressBar(int value) { int startValue = ((float)prev_prog/100.0)*64; int newValue = ((float)value/100.0)*64; if (newValue < startValue) { startValue = 0; } int pb_x = 10; int pb_y = 21; for (int i=startValue;i<newValue;i++) { drawVerticalLine(pb_x+i,pb_y,5); } prev_prog = value; } // ********hasta aqui ***** void setup(void) { LcdInitialise(); initProgressBar(); while (progress < 100) { progress++; setProgressBar(progress); delay(25); } LcdClear(); clearPixels(); } void loop(void) { gotoXY(4,0); //Poner el texto en la pantalla LcdString ("TestDisplay"); gotoXY(0,1); LcdString ("Este ejemplo"); gotoXY(0,2); LcdString ("lcdtest.pde"); gotoXY(0,3); LcdString ("funciona bien"); gotoXY(8,4); LcdString ("Este LCD3310 "); gotoXY(0,5); LcdString ("realmente "); gotoXY(0,6); LcdString ("tiene 95x64 px"); gotoXY(0,7); LcdString ("fijate bien"); } |
En la imagen que sigue, se aprecia el resultado de este segundo ejemplo, al que digamos he incorporado, una ‘función‘ especial, como es, una barra de proceso de carga, esto le puede dar un aspecto más profesional a nuestros proyectos. Con estos ejemplos se ha utilizado un código que se apoya en una librería estándar y por lo tanto no es problemático su uso en cualquier versión de Arduino.
El primer paso es incluir las variables siguientes:
1 2 3 |
<span style="color: #008000;">int progress = 0; int prev_prog = 0; int pixels[85][6] = {{0}};</span> |
También, se deben añadir las rutinas pertenecientes a propósito de cómo posicionar y trazar la propia barra de progreso. Estas son las referidas rutinas.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
void initProgressBar() { LcdClear(); clearPixels(); drawRect(10,21, 64, 5); } void setProgressBar(int value) { int startValue = ((float)prev_prog/100.0)*64; int newValue = ((float)value/100.0)*64; if (newValue < startValue) { startValue = 0; } int pb_x = 10; int pb_y = 21; for (int i=startValue;i< newValue; i++) { drawVerticalLine(pb_x+i,pb_y,5); } prev_prog = value; } |
Revise el código y trate de comprender que hace cada rutina, atrévase a realizar sus propios cambios y experimente, tenga temor a trastear, siempre podrá recuperar el código, en caso de problemas extremos. Y ahora, vea como se comportan esas adiciones que he comentado.
Puede ser que por falta de algún ‘plugin’, no pueda ver este corto vídeo, si es así, pruebe a descargar una copia, para visionarlo en su computadora.
Con la seguridad de que a usted, le ha funcionado bien, doy por terminado este tutorial de inicio en la aventura de las LCDs. En caso de tener alguna consulta que hacer, no dude en plantearla en el foro o haga un comentario
Exención de responsabilidad:
Este es un software y hardware experimental. Uselo bajo su propio riesgo. El software y hardware presentado en estas páginas aunque ha sido probado por el mantenedor/desarrollador de estas paginas, no se hace responsable bajo ninguna circunstancia por los daños de hardware o software, pérdida de datos o cualquier otro daño directo o indirecto, como resultado del uso de este software o hardware. Ni del mal uso que usted haga con él.
Si usted no está de acuerdo con estas condiciones, no podrá utilizar o distribuir más este software o utilizar cualquiera plantillas para piezas, para la construcción que aquí se presenta.
Esto es todo, por este simple tutorial.