Standardcode für die parallele Ansteuerung eines LCD Moduls (Autor: Manfred Dietrich). Ausführliche Informationen zur LCD Ansteuerung findet man überall, zum Beispiel hier: http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/LCD-Ansteuerung.
/*
* LCD 8-Bit Interface Test
* fuer LCD-Controller KS0073 von SAMSUNG mit 4×20 Display
* Anschlusskonfiguration:
* LowerNibble : IO-Pins 4,5,6,7 = PD 4-7
* HigherNibble : IO-Pins 8,9,10,11 = PB 0-3
* RS = RegisterSelect: IO-Pin 2
* RW = Read/write : immer 0 fest verdrahtet
* E = Enable : IO-Pin 3
* Im 4 Bit Modus muessen D0-D3 des LCD auf 0 gelegt werden. D4-D7 muessen an an das LowerNibble angeschlossen werden.
*/
#include <LCD4x20.h>
#include <NibbleInterface.h>
#include "WProgram.h"
void setup();
void loop();
LCD4x20 lcd = LCD4x20(); // So wird in der Arduino Umgebung ein Objekt erzeugt.
void setup() // run once, when the sketch starts
{
pinMode(13, OUTPUT);
digitalWrite(13, HIGH); // OK, Programm ist gestartet
pinMode(5, OUTPUT);
digitalWrite(5, HIGH);
lcd.initLCD(true);
lcd.lcdPrintString("Hallo Welt");
//char bTemp[16];
/*
for(int i = 0; i < 32767; i++)
{
lcd.setCursor(1,0);
lcd.lcdPrintString(itoa(i,bTemp,10)); // Benoetigt nur 160 Bytes
//*dtostrf(double __val, char __width, char __prec, char *__s);
//lcd.lcdPrintString(dtostrf(123.45, 8, 2, bTemp)); // Benoetigt mehr als 1.5kB Programmspeicher
}
*/
/*
volatile int i = B10101111;
lcd.setCursor(2,0);
lcd.lcdPrintString(itoa(i>>4,bTemp,10));
lcd.lcdPrintString(";");
lcd.lcdPrintString(itoa(i&15,bTemp,10));
delay(2000);
*/
}
void loop() // run over and over again
{
for(byte i=0;i<101;i++)
{
lcd.levelBarPosLR(i,i,1);
lcd.levelBarPosR(i,2,false);
lcd.levelBarPosL(i,3,false);
delay(50);
}
for(byte i=100;i>0;i--)
{
lcd.levelBarPosLR(100-i,i,1);
lcd.levelBarPosR(i,2,false);
lcd.levelBarPosL(i,3,false);
delay(50);
}
}
// main
int main(void)
{
init();
setup();
for (;;)
loop();
return 0;
}
Die benötigen Libraries:
LCD4x20.h
// ____________________________________________________________________________
// LCD-Treiber 4x20
// 081020 manfred.dietrich@clustertec.com
// ____________________________________________________________________________
#include
#include <../NibbleInterface/NibbleInterface.h>
#define LCDPIN_RS 2
#define LCDPIN_E 3
#ifndef LCD4x20_h
#define LCD4x20_h
class LCD4x20
{
public:
LCD4x20();
void sendDataByte(byte dataByte);
void sendInstructionByte(byte instrByte);
void clearDisplay();
void clearLine(byte Line);
void setCursor(byte Row, byte Col);
void lcdPrintString(char *Text); // printString wird schon verwendet
void levelBarPosR(byte Level, byte Row, bool fHalf);
void levelBarPosL(byte Level, byte Row, bool fHalf);
void levelBarPosLR(byte levelL, byte levelR, byte Row);
void cursorOn(byte cursorMode);
void initLCD(bool use4Bit);
private:
NibbleInterface nibble;
void initLines();
void initUserdefChars();
void enableData(void);
void sendDataByte8Bit(byte dataByte);
void sendInstructionByte8Bit(byte instrByte);
void sendDataByte4Bit(byte dataByte);
void sendInstructionByte4Bit(byte instrByte);
byte levelPosR[4];
byte levelPosL[4];
bool f4Bit;
};
#endif
LCD4x20.cpp
// LCD-Treiber 4x20
// 080917 manfred.dietrich@clustertec.com
// ____________________________________________________________________________
/*
* Anschlusskonfiguration:
* LowerNibble : IO-Pins 4,5,6,7 = PD 4-7
* HigherNibble : IO-Pins 8,9,10,11 = PB 0-3
* Im 4 Bit Modus müssen D0-D3 des LCD auf 0 gelegt werden. D4-D7 müssen an an das LowerNibble angeschlossen werden.
* RS = RegisterSelect: IO-Pin 2
* RW = Read/write : immer 0 fest verdrahtet
* E = Enable : IO-Pin 3
*/
#include
LCD4x20::LCD4x20()
{
// initLCD darf nicht schon im Konstruktor aufgerufen werden.
// Dies führt zu Problemen mit den IO-Ports, wenn die Klasse global deklariert wird.
//initLCD();
f4Bit = false;
}
void LCD4x20::initLines()
{
pinMode(LCDPIN_RS, OUTPUT);
digitalWrite(LCDPIN_RS,LOW);
pinMode(LCDPIN_E, OUTPUT);
digitalWrite(LCDPIN_E,LOW);
}
void LCD4x20::initLCD(bool use4Bit)
{
f4Bit = use4Bit;
initLines();
if(f4Bit)
{
nibble.setLowerNibble(3); // Sicherstellen, dass der 8 Bit Modus aktiv ist. Z.B. nach einem Reset des uP befindet sich das Display immer noch im 4 Bit Modus
enableData();
delay(20);
enableData();
enableData();
nibble.setLowerNibble(2); // 4 Bit Modus aktivieren
enableData();
sendInstructionByte(B00101100); // Function Set: 4-bit, RE(1)
sendInstructionByte(B00001001); // Extended Function Set: 5-font, 4-lin
sendInstructionByte(B00101000); // Function Set: RE(0)
}
else
{
sendInstructionByte(B00111100); // Function Set: 8-bit, RE(1)
sendInstructionByte(B00001001); // Extended Function Set: 5-font, 4-lin
sendInstructionByte(B00111000); // Function Set: RE(0)
}
clearDisplay();
sendInstructionByte(B00001100); // Display ON/OFF Control: Display/Cursor off
initUserdefChars();
levelPosR[0] = 0;
levelPosR[1] = 0;
levelPosR[2] = 0;
levelPosR[3] = 0;
levelPosL[0] = 0;
levelPosL[1] = 0;
levelPosL[2] = 0;
levelPosL[3] = 0;
}
void LCD4x20::initUserdefChars()
{
// 5 rechtsbuendige Bloecke definieren
// Diese werden fuer den Balken von rechts nach links verwendet.
sendInstructionByte(B01001000);
byte pattern = 1;
for(byte j=0; j < 5; j++)
{
for(byte i=0; i<7; i++)
{
sendDataByte(pattern);
}
sendDataByte(0);
pattern = (pattern<<1)+1; } setCursor(0,0); } void LCD4x20::enableData(void) { digitalWrite(LCDPIN_E,HIGH); delayMicroseconds(10); digitalWrite(LCDPIN_E,LOW); // Daten werden bei der fallenden Flanke uebernommen delayMicroseconds(40); // Im 4 Bit Modus gibt es unterhalb von 20 uS Probleme digitalWrite(LCDPIN_E,HIGH); } void LCD4x20::sendDataByte(byte dataByte) { if(f4Bit) sendDataByte4Bit(dataByte); else sendDataByte8Bit(dataByte); } void LCD4x20::sendDataByte8Bit(byte dataByte) { digitalWrite(LCDPIN_RS,HIGH); nibble.setByte(dataByte); enableData(); delayMicroseconds(50); // Siehe Datenblatt zu KS0073. Mit diesem Delay muss das Busyflag nicht abgefragt werden } void LCD4x20::sendDataByte4Bit(byte dataByte) { digitalWrite(LCDPIN_RS,HIGH); nibble.setLowerNibble(dataByte>>4);
enableData();
delayMicroseconds(50);
nibble.setLowerNibble(dataByte & 15);
enableData();
delayMicroseconds(50);
}
void LCD4x20::sendInstructionByte(byte instrByte)
{
if(f4Bit)
sendInstructionByte4Bit(instrByte);
else
sendInstructionByte8Bit(instrByte);
delayMicroseconds(50);
}
void LCD4x20::sendInstructionByte8Bit(byte instrByte)
{
digitalWrite(LCDPIN_RS,LOW);
nibble.setByte(instrByte);
enableData();
delayMicroseconds(50);
}
void LCD4x20::sendInstructionByte4Bit(byte instrByte)
{
digitalWrite(LCDPIN_RS,LOW);
nibble.setLowerNibble(instrByte>>4);
enableData();
delayMicroseconds(50);
nibble.setLowerNibble(instrByte & 15);
enableData();
delayMicroseconds(50);
}
void LCD4x20::clearDisplay()
{
sendInstructionByte(1);
delay(2);
}
void LCD4x20::clearLine(byte Line)
{
if(Line > 3)
return;
setCursor(Line,0);
lcdPrintString((char*)" ");
// 01234567890123456789
setCursor(Line,0);
}
void LCD4x20::setCursor(byte Row, byte Col)
{
if(Row > 3 || Col > 19)
return;
sendInstructionByte(128+Row*32+Col);
}
void LCD4x20::lcdPrintString(char *Text)
{
byte i = 0;
while(Text[i] !='\0')
{
// Umlaute übersetzen
switch(Text[i])
{
//case 'ä': sendDataByte(123); break;
//case 'ö': sendDataByte(92); break;
//case 'ü': sendDataByte(126); break;
default: sendDataByte((byte)Text[i]);break;
}
i++;
}
}
void LCD4x20::levelBarPosR(byte Level, byte Row, bool fHalf)
{
// Links 0; rechts 100
byte offset = 0;
byte shrink = 0; // 0 mal rechts schieben
if(fHalf)
{
offset = 10;
shrink = 1; // 1 mal nach rechts schieben
}
if(Level > 100)
Level = 100;
byte fullBlocks = (Level>>shrink)/5;
if(Level < levelPosR[Row])
{
setCursor(Row,offset + fullBlocks+1);
for(byte i = fullBlocks+1; i < 20-offset; i++)
sendDataByte(' ');
}
//delay(250);
setCursor(Row,offset);
levelPosR[Row] = Level;
for(byte i= 0; i < fullBlocks; i++) sendDataByte(214); //delay(250); if(Level == 100) // Es wurden schon 20 Fullblocks gezeichnet, also kein Space mehr drucken, sonst wird an anderer Stelle etwas überschrieben. return; switch((Level>>shrink) % 5)
{
case 0: sendDataByte(' '); break;
case 1: sendDataByte(218); break;
case 2: sendDataByte(217); break;
case 3: sendDataByte(216); break;
case 4: sendDataByte(215); break;
default: break;
}
}
void LCD4x20::levelBarPosL(byte Level, byte Row, bool fHalf)
{
// Rechts 0; links 100
byte offset = 0;
byte shrink = 0; // 0 mal rechts schieben
if(fHalf)
{
offset = 10;
shrink = 1; // 1 mal nach rechts schieben
}
if(Level > 100)
Level = 100;
byte fullBlocks = (Level>>shrink)/5;
if(Level < levelPosL[Row])
{
setCursor(Row,0);
for(byte i = 0; i < 20-offset-fullBlocks; i++)
sendDataByte(' ');
}
//delay(250);
levelPosL[Row] = Level;
if(fullBlocks < 20-offset) { setCursor(Row,19-offset-fullBlocks); switch((Level>>shrink) % 5)
{
case 1: sendDataByte(1); break;
case 2: sendDataByte(2); break;
case 3: sendDataByte(3); break;
case 4: sendDataByte(4); break;
default:break;
}
}
setCursor(Row,20-offset-fullBlocks);
for(byte i= 0; i < fullBlocks; i++)
sendDataByte(5);
}
void LCD4x20::levelBarPosLR(byte levelL, byte levelR, byte Row)
{
levelBarPosL(levelL,Row,true);
levelBarPosR(levelR,Row,true);
}
void LCD4x20::cursorOn(byte cursorMode)
{
// 0 = kein Cursor
// 1 = Cursorblock blinken
// 2 = underscore Cursor
sendInstructionByte(B00001100 + cursorMode);
}
NibbleInterface.h
// 2*4 Bit output interface
// 080917 manfred.dietrich@clustertec.com
// ____________________________________________________________________________
#include
#include
#undef int
#undef abs
#undef double
#undef float
#undef round
#ifndef nibbleinterface_h
#define nibbleinterface_h
class NibbleInterface
{
public:
NibbleInterface();
bool setLowerNibble(byte mask);
bool setHigherNibble(byte mask);
void setByte(byte mask);
private:
void setLowerNibbleAsOutput();
void setHigherNibbleAsOutput();
};
#endif
NibbleInterface.cpp
// 2*4 Bit output interface
// Lower Nibble auf Pins 4,5,6,7
// Higher Nibble auf Pins 8,9,10,11
// 080917 manfred.dietrich@clustertec.com
// ____________________________________________________________________________
//#include
#include
NibbleInterface::NibbleInterface()
{
setLowerNibbleAsOutput();
setHigherNibbleAsOutput();
}
bool NibbleInterface::setLowerNibble(byte mask)
{
// IO-Pins 4,5,6,7 = PD 4-7 auf L oder H setzen
if(mask > 15)
return false;
byte pd = PORTD;
pd &= B00001111; // Bit 4-7 auf 0 setzen
pd |= mask<<4; PORTD = pd; return true; } bool NibbleInterface::setHigherNibble(byte mask) { // IO-Pins 8,9,10,11 = PB 0-3 auf L oder H setzen if(mask > 15)
return false;
byte pb = PORTB;
pb &= B11110000; // Bit 0-3 auf 0 setzen
pb |= mask;
PORTB = pb;
return true;
}
void NibbleInterface::setLowerNibbleAsOutput()
{
// IO-Pins 4,5,6,7 = PD 4-7
setLowerNibble(0);
DDRD |= B11110000;
}
void NibbleInterface::setHigherNibbleAsOutput()
{
// IO-Pins 8,9,10,11 = PB 0-3
setHigherNibble(0);
DDRB |= B00001111;
}
void NibbleInterface::setByte(byte mask)
{
setLowerNibble(mask & B00001111);
setHigherNibble(mask>>4);
}