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); }