Arduino

Democode: LCD 8-Bit Interface Test

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

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.