Accéder au contenu principal

Ecran ITEAD 1602 LCD pour le FEZ Panda II ou le Fez Cerbuino Bee

L'écran FEZ Touch fait plus que largement l'affaire pour la plupart des usages (écran couleur 320x240, tactile, affichage rapide, bitmaps, etc). Pour des usages simples ou sur le FEZ Cerbuino Bee, il peut cependant être utile d'avoir un écran LCD simple. J'ai craqué pour l'ITEAD 1602 LCD Shield d'ITEAD Studio (http://imall.iteadstudio.com/development-platform/arduino/shields/im120417018.html) en promotion pour 5 USD.
L'écran LCD est un écran de 2 lignes rétro-éclairé en bleu. Il dispose de 4 écrans de direction (haut, bas, gauche, droite), d'un bouton Reset (à droite) et d'un bouton Select (à gauche). Il reprend également les 5 ports analogiques au pas 2,54 cm, par contre, il n'y a pas de reprise de port digitaux. Il est cependant possible de l'empiler sur une carte Grove de manière à utiliser les quelques ports digitaux encore utilisables.
Le driver de GHI Electronics Black Shield convient largement. Je l'ai légèrement adapté avec du code Netduino pour pouvoir faire des défilements et des caractères graphiques. Voici le driver correspondant.

 //BLACK SHIELD DRIVER  
 /*  
 Copyright 2010 GHI Electronics LLC  
 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at  
 http://www.apache.org/licenses/LICENSE-2.0  
 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.   
 */  
 using System;  
 using System.Threading;  
 using Microsoft.SPOT;  
 using Microsoft.SPOT.Hardware;  
 using GHIElectronics.NETMF.Hardware;  
 using GHIElectronics.NETMF.FEZ;  
 namespace ITEADStudio  
 {  
     static public class LCDKeypad  
     {  
       public enum Keys  
       {  
         Up,  
         Down,  
         Right,  
         Left,  
         Select,  
         None,  
       }  
       static OutputPort LCD_RS;  
       static OutputPort LCD_E;  
       static OutputPort LCD_D4;  
       static OutputPort LCD_D5;  
       static OutputPort LCD_D6;  
       static OutputPort LCD_D7;  
       static AnalogIn AnKey;  
       static OutputPort BackLight;  
       const byte DISP_ON = 0xC;  //Turn visible LCD on  
       const byte CLR_DISP = 1;   //Clear display  
       const byte CUR_HOME = 2;   //Move cursor home and clear screen memory  
       const byte SET_CURSOR = 0x80;  //SET_CURSOR + X : Sets cursor position to X  
       const byte SCROLL_LEFT = 0x18;   // scroll all rows 1 char left  
       const byte SCROLL_RIGHT = 0x1e;   // scroll all rows 1 char right  
       const byte CGRAM = 64; // writing memory location  
       const byte DDRAM = 128; // reading memory location  
       const byte DISPLAY_BLANK = 0x08;  // Blank display without clearing it  
       const byte DISPLAY_RESTORE = 0x0c; // restore display, cursor hidden  
       const byte EM_DECREMENT_OFF = 0x04; // fixed first char, scroll cursor left when printing  
       const byte EM_DECREMENT_ON = 0x05; // fixed last char, scroll all right when printing  
       const byte EM_INCREMENT_ON = 0x06; // fixed first char, move cursor right when printing  
       const byte EM_INCREMENT_OFF = 0x07; // fixed last char, scroll all left when printing  
       const byte CUR_INVISIBLE = 0x0c;  // set cursor to be invisible  
       const byte CUR_SET_POSITION = 0x80; //SET_CURSOR + X : Sets cursor position to X      
       const byte CUR_VISIBLE_BLINK_BOX = 0x0f;  
       const byte CUR_VISIBLE_UNDERLINE = 0x0e;  
       public static void Initialize(bool analogic)  
       {  
         LCD_RS = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di8, false);  
         LCD_E = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di9, false);  
         LCD_D4 = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di4, false);  
         LCD_D5 = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di5, false);  
         LCD_D6 = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di6, false);  
         LCD_D7 = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di7, false);  
         if (analogic)  
           AnKey = new AnalogIn((AnalogIn.Pin)FEZ_Pin.AnalogIn.An0);  
         BackLight = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di10, true);  
         LCD_RS.Write(false);  
         // 4 bit data communication  
         Thread.Sleep(50);  
         LCD_D7.Write(false);  
         LCD_D6.Write(false);  
         LCD_D5.Write(true);  
         LCD_D4.Write(true);  
         LCD_E.Write(true);  
         LCD_E.Write(false);  
         Thread.Sleep(50);  
         LCD_D7.Write(false);  
         LCD_D6.Write(false);  
         LCD_D5.Write(true);  
         LCD_D4.Write(true);  
         LCD_E.Write(true);  
         LCD_E.Write(false);  
         Thread.Sleep(50);  
         LCD_D7.Write(false);  
         LCD_D6.Write(false);  
         LCD_D5.Write(true);  
         LCD_D4.Write(true);  
         LCD_E.Write(true);  
         LCD_E.Write(false);  
         Thread.Sleep(50);  
         LCD_D7.Write(false);  
         LCD_D6.Write(false);  
         LCD_D5.Write(true);  
         LCD_D4.Write(false);  
         LCD_E.Write(true);  
         LCD_E.Write(false);  
         SendCmd(DISP_ON);  
         SendCmd(CLR_DISP);  
       }  
       //Sends an ASCII character to the LCD  
       static void Putc(byte c)  
       {  
         LCD_D7.Write((c & 0x80) != 0);  
         LCD_D6.Write((c & 0x40) != 0);  
         LCD_D5.Write((c & 0x20) != 0);  
         LCD_D4.Write((c & 0x10) != 0);  
         LCD_E.Write(true); LCD_E.Write(false); //Toggle the Enable Pin  
         LCD_D7.Write((c & 0x08) != 0);  
         LCD_D6.Write((c & 0x04) != 0);  
         LCD_D5.Write((c & 0x02) != 0);  
         LCD_D4.Write((c & 0x01) != 0);  
         LCD_E.Write(true); LCD_E.Write(false); //Toggle the Enable Pin  
         //Thread.Sleep(1);  
       }  
       //Sends an LCD command  
       static void SendCmd(byte c)  
       {  
         LCD_RS.Write(false); //set LCD to data mode  
         LCD_D7.Write((c & 0x80) != 0);  
         LCD_D6.Write((c & 0x40) != 0);  
         LCD_D5.Write((c & 0x20) != 0);  
         LCD_D4.Write((c & 0x10) != 0);  
         LCD_E.Write(true); LCD_E.Write(false); //Toggle the Enable Pin  
         LCD_D7.Write((c & 0x08) != 0);  
         LCD_D6.Write((c & 0x04) != 0);  
         LCD_D5.Write((c & 0x02) != 0);  
         LCD_D4.Write((c & 0x01) != 0);  
         LCD_E.Write(true); LCD_E.Write(false); //Toggle the Enable Pin  
         Thread.Sleep(1);  
         LCD_RS.Write(true); //set LCD to data mode  
       }  
       /// <summary>  
       /// Print a string   
       /// </summary>  
       /// <param name="str"></param>  
       /// <param name="sleep"></param>  
       static void iPrint(string str, int sleep = 0)  
       {  
         for (int i = 0; i < str.Length; i++)  
         {  
           Putc((byte)str[i]);  
           Thread.Sleep(sleep);  
         }  
       }  
       public static void Clear()  
       {  
         SendCmd(CLR_DISP);  
       }  
       public static void CursorHome()  
       {  
         SendCmd(CUR_HOME);  
       }  
       public static void SetCursor(byte row, byte col)  
       {  
         SendCmd((byte)(SET_CURSOR | row << 6 | col));  
       }  
       public static Keys GetKey()  
       {  
         int i = AnKey.Read();  
         //if (i != 1023)  
         //  Debug.Print(i.ToString());  
         // use this to read values to calibrate  
         /*while (true)  
         {  
           i = AnKey.Read();  
           Debug.Print(i.ToString());  
           Thread.Sleep(300);  
         }*/  
         const int ERROR = 50;  
         if (i > 1024 - ERROR)  
           return Keys.None;  
         if (i < 0 + ERROR)  
           return Keys.Right;  
         if (i < 200 + ERROR && i > 200 - ERROR)  
           return Keys.Up;  
         if (i < 450 + ERROR && i > 450 - ERROR)  
           return Keys.Down;  
         if (i < 700 + ERROR && i > 700 - ERROR)  
           return Keys.Left;  
         if (i < 780 + ERROR && i > 780 - ERROR)  
           return Keys.Select;  
         return Keys.None;  
       }  
       public static void TurnBacklightOn()  
       {  
         BackLight.Write(true);  
       }  
       public static void ShutBacklightOff()  
       {  
         BackLight.Write(false);  
       }  
       /// <summary>  
       /// Scroll the display a number of places Left   
       /// The operation ay be paused a number of milliseconds between each movement to  
       /// provide annimation.  
       /// </summary>  
       /// <param name="counter"></param>  
       /// <param name="sleep"></param>  
       public static void DisplayScrollLeft(int counter = 1, int sleep = 0)  
       { while (counter-- > 1) { SendCmd(SCROLL_LEFT); ; Thread.Sleep(sleep); } }  
       /// <summary>  
       /// Scroll the display a number of places Right  
       /// The operation ay be paused a number of milliseconds between each movement to  
       /// provide annimation.  
       /// </summary>  
       /// <param name="counter"></param>  
       /// <param name="sleep"></param>  
       public static void DisplayScrollRight(int counter = 1, int sleep = 0)  
       {   
         while (counter-- > 1) {  
           SendCmd(SCROLL_RIGHT);  
           Thread.Sleep(sleep);   
         }  
       }  
       /// <summary>  
       /// Used to user define the first eight characters  
       /// Use (char)8 to print the first character instead of (char)0  
       /// If using a string of only custom characters terminate with + ""  
       /// </summary>  
       /// <param name="asciiCharPos"></param>  
       /// <param name="customChars"></param>  
       public static void SetCustomChars(byte asciiCharPos, byte[] customChars)  
       {  
         //if (customChars.Length > 8) throw new Exception("A byte array length greater than 8 will overwrite the following character");  
         // write a   
         SendCmd((byte)(CGRAM + (8 * asciiCharPos)));  
         for (byte i = 0; i < customChars.Length; i++)  
         {  
           Putc(customChars[i]);  
           Thread.Sleep(1);  
         }  
         SendCmd(DDRAM);  
       }  
       /// <summary>  
       /// Blank the display without clearing it  
       /// </summary>  
       public static void DisplayHide()  
       {  
         SendCmd(DISPLAY_BLANK);  
       }  
       /// <summary>  
       /// Restore display, cursor hidden  
       /// </summary>  
       public static void DisplayRestore()  
       {  
         SendCmd(DISPLAY_RESTORE);  
       }  
       /// <summary>  
       /// Fixed first char, move cursor right when printing  
       /// </summary>  
       public static void ModeForwardStatic()  
       {  
         SendCmd(EM_INCREMENT_ON);  
       }  
       /// <summary>  
       /// Fixed last char, scroll all left when printing  
       /// </summary>  
       public static void ModeForwardRunLeft()  
       {  
         SendCmd(EM_INCREMENT_OFF);  
       }  
       /// <summary>  
       /// Fixed last char, scroll all right when printing  
       /// </summary>  
       public static void ModeReversedStatic()  
       {  
         SendCmd(EM_DECREMENT_ON);  
       }  
       /// <summary>  
       /// ixed first char, scroll cursor left when printing  
       /// </summary>  
       public static void ModeReversedRunRight()  
       {  
         SendCmd(EM_DECREMENT_OFF);  
       }  
       public static void BacklightOn()  
       {  
         BackLight.Write(true);  
       }  
       public static void BacklightOff()  
       {  
         BackLight.Write(false);  
       }  
       public static void CursorInvisible()  
       {  
         SendCmd(CUR_INVISIBLE);  
       }  
       public static void CursorBlinkBox()  
       {  
         SendCmd(CUR_VISIBLE_BLINK_BOX);  
       }  
       public static void CursorUnderline()  
       {  
         SendCmd(CUR_VISIBLE_UNDERLINE);  
       }  
       /// <summary>  
       /// Places the cursor at a given row and column  
       /// Top left is 0 , 0  
       /// </summary>  
       /// <param name="row"></param>  
       /// <param name="col"></param>  
       public static void CursorSet(byte row, byte col)  
       {  
         SendCmd((byte)(CUR_SET_POSITION | row << 6 | col));  
       }  
       /// <summary>  
       /// Remove all data from all lines of the display  
       /// </summary>  
       public static void Clear(int delayBefore = 0)  
       {  
         Thread.Sleep(delayBefore);  
         SendCmd(CLR_DISP);  
       }  
       /// <summary>  
       /// Print a string with a pause ( milliseconds) between characters  
       /// </summary>  
       /// <param name="str"></param>  
       /// <param name="sleep"></param>  
       public static void Print(string str, int sleep)  
       {  
         iPrint(str, sleep);  
       }  
       /// <summary>  
       /// Print a string at a the current cursor location   
       /// </summary>  
       /// <param name="str"></param>  
       public static void Print(string str)  
       {  
         iPrint(str);  
       }  
       /// <summary>  
       /// Print a string at a location   
       /// </summary>  
       /// <param name="row"></param>  
       /// <param name="col"></param>  
       /// <param name="str"></param>  
       public static void Print(byte row, byte col, string str)  
       {  
         CursorSet(row, col);  
         iPrint(str);  
       }  
       /// <summary>  
       /// Print a string at a location with a pause ( milliseconds) between characters  
       /// </summary>  
       /// <param name="row"></param>  
       /// <param name="col"></param>  
       /// <param name="str"></param>  
       /// <param name="sleep"></param>  
       public static void Print(byte row, byte col, string str, int sleep)  
       {  
         CursorSet(row, col);  
         iPrint(str, sleep);  
       }  
     }  
 }  

Voici un exemple simple d'utilisation qui affiche des caractères et récupère la valeur des touches appuyées. Attention !!!. A cause d'un problème de voltage de la carte, la touche Select ne peut pas être détectée. Toutes les touches utilisent le même Pin analogique et des résistances sont associées à chaque touches. La résistance associée à la touche Select a une valeur de 0, ce qui ne permet pas de la distinguer du fait qu'aucune touche n'est enfoncée. Vu le prix de la carte, ce léger bug reste mineur. Il est toujours possible de connecter un bouton sur l'un des connecteurs libres.
     LCDKeypad.Initialize();  
     LCDKeypad.TurnBacklightOn();  
     int i = 0;  
     while (true)  
     {  
       LCDKeypad.CursorHome();  
       LCDKeypad.Print("Count " + i++);  
       LCDKeypad.SetCursor(1, 2);  
       switch (LCDKeypad.GetKey())  
       {  
         case LCDKeypad.Keys.Up:  
           LCDKeypad.Print("Up  ");  
           break;  
         case LCDKeypad.Keys.Down:  
           LCDKeypad.Print("Down ");  
           break;  
         case LCDKeypad.Keys.Left:  
           LCDKeypad.Print("Left ");  
           break;  
         case LCDKeypad.Keys.Right:  
           LCDKeypad.Print("Right ");  
           LCDKeypad.ShutBacklightOff();  
           break;  
         case LCDKeypad.Keys.Select:  
           LCDKeypad.Print("Select");  
           break;  
         case LCDKeypad.Keys.None:  
           LCDKeypad.Print("");  
           break;  
         }  
       Thread.Sleep(100);  
       }  

Il est possible d'utiliser cette carte pour des exemples plus complexes, comme un réveil, un instrument de mesure (température, hygrométrie, etc).

Commentaires

Posts les plus consultés de ce blog

Utilisez votre tablette Android comme second écran pour Linux (Raspberry Pi, MK908II)

Les tablettes Android atteignent désormais des prix qui défient toute concurrence. On trouve désormais des modèles à 39 € TTC en super marché, soit à peine plus cher que le Raspberry PI, mais avec un écran. Ces modèles souvent mono-core 1Ghz ou 1,4 Ghz avec 512 ou 1Go de mémoire ne sont très probablement pas utilisables pour une utilisation régulière sur Internet et ne sont en aucun point comparables à leur équivalent de marque (Samsung, Sony, LG, HTC, Lenovo, etc). Plusieurs tutoriels indiquent comment connecter utiliser une tablette Android comme second écran ( http://www.linux-magazine.com/Online/Blogs/Productivity-Sauce/Use-an-Android-Device-as-Screen-and-Input-for-Raspberry-Pi ). Ces méthodes utilisent généralement l'USB Tethering qui n'est malheureusement disponible que sur les téléphones ou tablettes avec un accès mobile (3G ou 4G) inclus. Dans ce billet, je vais vous montrer comment se connecter à une tablette en utilisant le mode Debug adb (Android Debug Bridge...

Ardublock ou S4A pour développer graphiquement

Si vous n'aimez pas le développement en C, ou C# sur les micro-contrôleurs, vous pouvez vous essayer au développement graphique avec Ardublock. Historique Cet environnement de développement est issu d' OpenBlocks développé par le MIT qui se positionne lui même dans la suite du langage Logo de Seymour Papert . Le langage Logo est un langage issu de l'Intelligence Artificielle dans les années 1970 dont l'objectif était de faciliter l'apprentissage de la programmation à de jeunes enfants par le biais du pilotage d'une tortue munie d'un crayon. Les ordres étaient relativement simples : avance de 90 cm, tourne à droite de 90°, etc. Ceci, permettait de réaliser des dessins assez simple, de piloter un petit robot et d'apprendre la programmation. C'était cependant un langage textuel. Exemple pour tracer un carré : POUR CARRE REPETE 4 [AV 100 TD 90] FIN   Son digne successeur, le langage Scratch désormais intégré à l'image Raspbian du Raspberry Pi...

Hack du RoboSapien en Infra-Rouge

Mon fils a eu un RoboSapien V1 il y a une dizaine d'années. Il prenait la poussière sur le haut d'une armoire, jusqu'à ce que j'ai envie de le ramener à la vie. Il était temps, les piles étaient en train de commencer à couler et vu le nombre de servo moteurs qu'il contient, ses jours étaient comptés. Mais non, j'ai réussi à contenir mon irrésistible envie de tout démonter et j'ai décidé de passer par la télécommande Infrarouge pour le piloter. Le protocole est assez similaire de celui d'une télécommande infrarouge classique avec cependant quelques petites différences. Il est correctement expliqué sur les sites http://www.aibohack.com/robosap/ir_codes.htm et http://www.markcra.com/robot/ir_codes.php . Je vais traduire en français pour ceux qui auraient un peu de difficulté. Il existe des librairies Arduino, mais comme d'habitude, pas toujours de librairie en C#. Voici les choses importantes à connaitre sur le protocole : Le protocole envoie...