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
Enregistrer un commentaire