PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Problem mit 4x20 LCD am Atmega88



High Light
13.11.2012, 13:39
Hallo zusammen,

ich arbeite gerade das Buch "AVR Hardware und C-Programmierung in der Praxis" von Florian Schäffer durch.
Jedoch bin ich auf ein Problem gestoßen und zwar wird bei einer Übung ein 4x20 LCD im 4-bit Modus an einem Atmega betrieben.
Im Buch wird der PORTC verwendet, jedoch benötige ich diesen für eine AD-Wandlung. Somit habe ich den Code auf PortD umgeschrieben und auf den Atmega gebrannt.

Außer 2 Balken wird nichts angezeigt...kann mir jemand weiter helfen?

Hier noch der Code:

lcd-text.c

/* Schreibt Text auf ein angeschlossenes LCD mit Hilfe der eigenen Include-Datei
*
* Demonstration verschiedener Zahlenkonvertierungsmoeglichkeiten
*
*/

#define F_CPU 16000000UL

#include "lcd_tools.c"
#include <util/delay.h>
#include <stdlib.h> // itoa etc.

int main(void);

int main()
{

lcd_ini();


lcd_gotopos (1,1);
lcd_writetext ("Herzlich Willkommen");

lcd_gotopos (4,2);
lcd_writetext ("CPR by pe-products");

while(1);
}



lcd_tools.c

/* LCD Include-Datei
*
* Spezielle Version für 4x20 LCD
*
* LCD_CTRL stellt ein, welcher Controllertyp (wichtig für DD-RAM-Adressen)
* Einige Routinen sind für ein EA DIP204-4 mit KS0073 Controller notwendig, aber auskommentiert
*
* Das LCD ist folgendermaßen angeschlossen:
* PC4 = RS
* PC5 = Enable
* PC0-PC3 = D4-D7
* R/W ist n.c.
*
*
*/

#include <avr/io.h>
#include <util/delay.h>
#include "lcd_tools.h"

#define LCD_CTRL 0 // (0 = HD44780, 1= KS0073)
#define delay(us) _delay_loop_2 (((F_CPU/4000)*us)/1000) // wartet µs

/**
@brief wartet Millisekunden
@param Zeit in Millisekunden
@return none
*/
void delay_ms(uint16_t ms)
{
for(uint16_t t=0; t<=ms; t++)
_delay_ms(1);
}

/**
Enable Leitung togglen
*/
void lcd_flash_e ()
{
PORTD = PORTD | ( 1<<DDD5 ) ; // Enable = HIGH
delay(100); // kurz warten, damit Flanke entsteht
PORTD = PORTD & ~( 1<<DDD5 ) ; // Enable = LOW
delay(100); // Warten, bis der durch Enable angelegte Befehl hoffentlich verarbeitet wurde
}

/**
@brief Kommando oder Zeichen an Display senden:
@param rs=0 => Kommando, rs=1 => Zeichen
@return none
*/
void lcd_write (uint8_t data, uint8_t rs)
{
uint8_t dataBits ;

if (rs) // write data (RS=1, RW=0)
dataBits=0x10; // RS liegt an Pin 4 = B 0001 0000 = H 10
else // write instruction (RS=0, RW=0)
dataBits=0;

PORTD = dataBits | (data>>4); // output high nibble first, zzgl. Zustand für RS-Leitung
lcd_flash_e ();

PORTD = dataBits | (data&0x0F); // output low nibble, zzgl. RS-Leitung
lcd_flash_e ();
}

/**
@brief Display loeschen
@param none
@return none
*/
void lcd_cls ()
{
lcd_write(0x00,0); // B 0000 0000 => Clear
delay(4000); // dauert eine Weile
lcd_write(0x01,0); // B 0000 0001 => Cursor Home
delay(4000); // dauert eine Weile, Wert ausprobiert
}

/**
@brief Zeichenausgabe
@param none
@return none
*/
void lcd_writechar ( char zeichen)
{
lcd_write (zeichen, 1);
}

/**
@brief gibt eine Zeichenkette auf dem LCD aus
@param none
@return none
*/
void lcd_writetext ( char *text)
{
uint8_t i = 0;
while (text[i]!=0)
{
lcd_writechar(text[i]);
i++;
}
}

/**
@brief Zeilenwechsel
@param none
@return none
*/
void lcd_gotoline (uint8_t zeile)
{
if (LCD_CTRL == 0)
{
if (zeile == 1) lcd_write(0x80,0); // B 1000 0000 => DD-RAM Adress Set 1. Zeile/1.Spalte
if (zeile == 2) lcd_write(0x80+0x40,0); // B 1010 0000 => DD-RAM Adress Set 2. Zeile/1.Spalte
if (zeile == 3) lcd_write(0x80+0x14,0); // B 1010 0000 => DD-RAM Adress Set 3. Zeile/1.Spalte
if (zeile == 4) lcd_write(0x80+0x54,0); // B 1010 0000 => DD-RAM Adress Set 4. Zeile/1.Spalte
}
else
{
if (zeile == 1) lcd_write(0x80,0); // B 1000 0000 => DD-RAM Adress Set 1. Zeile/1.Spalte
if (zeile == 2) lcd_write(0x80+0x20,0); // B 1010 0000 => DD-RAM Adress Set 2. Zeile/1.Spalte
if (zeile == 3) lcd_write(0x80+0x40,0); // B 1010 0000 => DD-RAM Adress Set 3. Zeile/1.Spalte
if (zeile == 4) lcd_write(0x80+0x60,0); // B 1010 0000 => DD-RAM Adress Set 4. Zeile/1.Spalte
}

}

/**
@brief Cursorpositionieren
@param Zeile, Spalte
@return none
*/
void lcd_gotopos (uint8_t zeile, uint8_t spalte)
{
if (LCD_CTRL == 0)
{
if (zeile == 1) lcd_write(0x80+0x00+spalte-1,0); // DD-RAM Adress 1. Zeile + Spalte
if (zeile == 2) lcd_write(0x80+0x40+spalte-1,0); // DD-RAM Adress 2. Zeile + Spalte
if (zeile == 3) lcd_write(0x80+0x14+spalte-1,0); // DD-RAM Adress 3. Zeile + Spalte
if (zeile == 4) lcd_write(0x80+0x54+spalte-1,0); // DD-RAM Adress 4. Zeile + Spalte
}
else
{
if (zeile == 1) lcd_write(0x80+0x00+spalte-1,0); // DD-RAM Adress 1. Zeile + Spalte
if (zeile == 2) lcd_write(0x80+0x20+spalte-1,0); // DD-RAM Adress 2. Zeile + Spalte
if (zeile == 3) lcd_write(0x80+0x40+spalte-1,0); // DD-RAM Adress 3. Zeile + Spalte
if (zeile == 4) lcd_write(0x80+0x60+spalte-1,0); // DD-RAM Adress 4. Zeile + Spalte
}
}

/**
@brief gibt eine int-Zahl auf dem Display aus. Integriert, damit auf string.h verzichtet werden kann
@param Zahl
Anzahl der Stellen (führende Leerzeichen)
space=0 => führende Nullen, 1 => führende Leerzeichen
@return none
*/
void lcd_writezahl (int32_t zahl, uint8_t pos, uint8_t spacer)
{
int32_t output;
char sign = '0';

pos--; // Korrektur des Parameters für Benutzerfreundlichkeit

if (spacer) sign=' ';

// negatives Vorzeichen oder Leerzeichen
if (zahl < 0)
{
lcd_writechar ('-');
zahl *= -1; // positive Zahl
}

output = zahl;

// Hunderttausender-Dezimalstelle oder Leerzeichen (Ausblenden fuehrender Nullen)
if (zahl >= 100000) lcd_writechar (0x30 + output/100000); else if (pos >= 5) lcd_writechar (sign);
output = output % 100000;

// Zehntausender-Dezimalstelle oder Leerzeichen (Ausblenden fuehrender Nullen)
if (zahl >= 10000) lcd_writechar (0x30 + output/10000); else if (pos >= 4) lcd_writechar (sign);
output = output % 10000;

// Tausender-Dezimalstelle oder Leerzeichen
if (zahl >= 1000) lcd_writechar (0x30 + output/1000); else if (pos >= 3) lcd_writechar (sign);
output = output % 1000;

// Hunderter-Dezimalstelle oder Leerzeichen
if (zahl >= 100) lcd_writechar (0x30 + output/100); else if (pos >= 2) lcd_writechar (sign);
output = output % 100;

// Zehner-Dezimalstelle oder Leerzeichen
if (zahl >= 10) lcd_writechar (0x30 + output/10); else if (pos >= 1) lcd_writechar (sign);

// einer Dezimalstelle oder Leerzeichen
lcd_writechar (0x30 + output % 10);
}

/**
Zuerst den 8-Bit-Modus aktivieren, Ist wichtig, falls LCD schon im 4-Bit Modus war und dann
nach einem Programm-Reset vergeblich versucht würde, den 4-Bit Modus erneut zu aktivieren.
dann in 4-Bit Modus umschalten.
*/
void lcd_set4bit ()
{
// A C H T U N G ! Diese Initialisierung entspricht nicht den Vorgaben aus dem Datenblatt

PORTD = 0x02; // B 0000 0010 => in 4-Bit-Modus umschalten
lcd_flash_e ();
delay(5);
}

/**
Portrichtung einstellen. Extern, damit man dies separat aufrufen kann, wenn bspw. LCD schon initialisiert ist und
per Watchdog ein Reset ausgelöst wurde, man dann aber nicht das Display neu initialisieren möchte, damit es schneller weiter geht
*/
void lcd_port_ini ()
{
DDRD |= 0b00111111; // setze Portrichtung (1 = Ausgang): 0011 1111
}

/**
@brief Display initialisieren. Einmal als erstes aufrufen
@param none
@return none
*/
void lcd_ini ()
{
lcd_port_ini();

delay_ms(15); // 15ms(40ms) warten bis LCD wirklich bereit! Wir können nicht BUSY auswerten.
// Es gibt Displays die so lange brauchen (z. B. Optrex DMC20481)

lcd_set4bit(); // ab jetzt 4-Bit-Modus

// lcd_write(0x2C,0); // 4x20 Spezial: Function Set (0010 1100): 4-Bit, 2 Line, RE-Bit, Dot Scroll, Normal Mode
// lcd_write(0x09,0); // 4x20: Extended Function Set (0000 1001): 5 dot font, Normal Cursor, 4 Line Display

lcd_write(0x28,0); // B 0010 1000 => Function Set: 4Bit (kein 8-Bit Bus), zweizeiliges Display, 5x7 Dots/Zeichen (kein 5x10), RE-Bit aus
// lcd_write(0x0F,0); // B 0000 1000 => Display On/Off: Display ein, Cursor an, Blinken an
lcd_write(0x0C,0); // B 0000 1000 => Display On/Off: Display ein, Cursor aus, Blinken aus
lcd_write(0x06,0); // B 0000 0110 => Entry Mode Set: DD-RAM autom. inkr., Cursor bewegen

lcd_cls(); // Display löschen
}




Grüße High Light

robocat
13.11.2012, 14:52
* R/W ist n.c. <--- Hatte ein ähnliches Problem auch schon, lege mal den R/W Pin auf GND.
Dass Balken angezeigt werden bedeutet, dass die Initialisierung nicht ok ist.

Grüße

robo_tom_24
13.11.2012, 18:52
Kannst du den Originalcode mal auf Port C testen? also nicht dauerhaft, nur um zu sehen ob der Fehler in der Software oder an der Beschaltung liegt ;)

Hubert.G
13.11.2012, 20:33
Zu testen fehlt die lcd_tools.h

High Light
14.11.2012, 06:21
Also das mit R/W auf GND zu legen hat nicht geklappt.

Bevor ich den Code für PORTD umgeschrieben habe, habe ich ihn auf dem PORTC getestet und es hat alles geklappt.

Hier noch die lcd_tools.h :


#include <stdint.h>

void delay_ms(uint16_t);
void lcd_flash_e (void);
void lcd_write (uint8_t, uint8_t);
void lcd_cls (void);
void lcd_writechar (char);
void lcd_writetext (char *);
void lcd_gotoline (uint8_t);
void lcd_gotopos (uint8_t, uint8_t);
void lcd_writezahl (int32_t, uint8_t, uint8_t);
void lcd_set4bit (void);
void lcd_port_ini (void);
void lcd_ini (void);

Hubert.G
14.11.2012, 13:29
Ich habe nur ein 2-Zeiliges Disp, aber es wird initialisiert und Herzlich Willko steht auch drinnen.
Der Code scheint also zu passen.

High Light
18.11.2012, 20:07
Hallo zusammen,

ich habe das Problem heraus gefunden. Das Problem lag nicht an der Software, sondern an der Hardware... *PEINLICH*

Trotzdem nochmal Danke für die Hilfe.

Grüße High Light