mikrocontroller.net

Forum: Compiler & IDEs LCD, kryptische Zeichen, Zeilenumbruch


Autor: spyder (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

Hab vor mir ein 2x16 Zeilen LCD liegen. Textausgabe funktioniert 
einwandfrei, nur wenn ich versuche das Display zu löschen, schreibt es 2 
irgenwelche kryptische Zeichen hin, ohne die Anzeige zu löschen.

Außerdem kann ich nicht in die 2. Zeile hüpfen. Erst wenn ich (glaub ich 
halt) einen Überlauf produzier komm ich in die nächste Zeile.

Hab mal den C-Code angehängt. Die Ausgabe zu diesem Code sieht so aus:

test53685**abcde
456789HALLO!:

wobei für * diese kryptischen "Zeichen" einzusetzen ist, die ich aber 
hier nicht darstellen kann (sind glaub ich gar nicht im ASCII 
enthalten).

main.c
//    ATMega8 1MHz
//    Verwendetes Prog: Programmers Notepad 2
//    LCD: PVC160101PTN03
//    Anschlussbelegung: 
/*    Db4 = Portb.0 
      Db5 = Portb.1
      Db6 = Portb.2
      Db7 = Portb.3
      E = Portb.4
      Rs = Portb.5
*/ 

#include <avr/io.h>
#include "lcd-routines.h"
#include <util/delay.h>
#ifndef F_CPU
#define F_CPU    1000000
#endif

void delay_ms(int ms)
{
  int t;
  for(t=0; t<=ms; t++)
  _delay_ms(1); 
}
 
int main(void)
{
   DDRB = 0xFF;   // Port B komplett als Ausgang einstellen
   lcd_init();  //Initialisierung
   lcd_clear();
   lcd_out("test53685");
   delay_ms (2000); // 2 Sekunden Verzögerung
   lcd_clear();  //LCD löschen
   jumpline();   //In die 2. Zeile springen
   delay_ms (2000);
   lcd_out("abcdefghijklmnopqrstuvwxyz");
   lcd_out("123456789"); 
   lcd_data('H');
   lcd_data('A');
   lcd_data('L');
   lcd_data('L');
   lcd_data('O');
   lcd_data('!');   
   lcd_data(':'); 
   return 0;
}
und da die dazugehörende lcd_routines.c:
#include <avr/io.h>
#include <util/delay.h>
#include "lcd-routines.h"
#define DELAY50US() _delay_us(25); _delay_us(25)
// LCD Befehle
#define CLEAR_DISPLAY 0x01
#define CURSOR_HOME   0x02
// Pinbelegung für das LCD
#define PORT_LCD      PORTB
#define LCD_RS        5
#define  LCD_EN        4
// sendet einen Befehl an das LCD
// wie lcd_data, nur ohne RS zu setzen
void lcd_command(unsigned char temp1)
{
   unsigned char temp2 = temp1;
   temp1 = temp1 >> 4;   // oberes Nibble holen
   temp1 = temp1 & 0x0F; // maskieren
   PORT_LCD &= 0xF0;
   PORT_LCD |= temp1;        // setzen
   lcd_enable();
 
   temp2 = temp2 & 0x0F; // unteres Nibble holen und maskieren
   PORT_LCD &= 0xF0;
   PORT_LCD |= temp2;        // setzen
   lcd_enable();
   
   DELAY50US();
}
 
// erzeugt den Enable-Puls
void lcd_enable(void)
{
   PORT_LCD |= (1<<LCD_EN);
   asm("nop");
   asm("nop");
   asm("nop");
   PORT_LCD &= ~(1<<LCD_EN);
}
 
// Initialisierung: 
void lcd_init(void)
{
   unsigned char temp3 = 50;
 
   while(temp3 != 0)
   {
      _delay_ms(5);  // 5ms = 250ms warten
      temp3 = temp3 - 1;
   }
   // muss 3mal hintereinander gesendet werden zur Initialisierung
   PORT_LCD &= 0xF0;
   PORT_LCD |= 0x03;       // 0b00000011  
   lcd_enable();
   PORT_LCD |= 0x03;
   lcd_enable();
   PORT_LCD |= 0x03;
   lcd_enable();
 
   // 4bit-Modus einstellen
   PORT_LCD |= 0x02;       // 0b00000010  
   lcd_enable();
   _delay_ms(5);
 
   // 4Bit / 2 Zeilen / 5x7
   lcd_command(0x28);  //Mache das 3mal wegen Timing (einfach ausprobiert)    
   lcd_command(0x28);
   lcd_command(0x28);
   
   // Display ein / Cursor aus / kein Blinken
   lcd_command(0x0C); 
 
   // inkrement / kein Scrollen
   lcd_command(0x06); 
}
 
// Sendet den Befehl zur Löschung des Displays
void lcd_clear(void)
{
   lcd_command(CLEAR_DISPLAY);
    _delay_ms(7);
}
 
// Sendet den Befehl: Cursor Home
void lcd_home(void)
{
   lcd_command(CURSOR_HOME);
   _delay_ms(5);
}

void lcd_out(char *s)
{
    while (*s)            //so lange *s != '\0' also ungleich dem "String-Endezeichen"
    {
        lcd_data(*s);        //Zeichen ausgeben
        s++;
    }
}


void jumpline(void)
{
lcd_command(0xC0); //in 2. zeile springen (2nd line starts at 40h)
_delay_ms(5);
}

Hoffentlich kann mir da jemand weiterhelfen!

lg spyder

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ spyder (Gast)

>Außerdem kann ich nicht in die 2. Zeile hüpfen. Erst wenn ich (glaub ich
>halt) einen Überlauf produzier komm ich in die nächste Zeile.

Dann scheint deine Funktion lcd_command nicht ganz zu funktionieren.
Ist auch klar, du MUSST RS expliziet auf NUll setzen! Das tust du nicht.

Ausserdem ist dein Initialisierung nicht ganz korrekt, du musst zwischen 
den 3 Initialisierungsbefehlen jeweils Pausen einlegen.

Und warum verwendest du mehrere Versionen von Warteschleifen? Das ist 
nicht gut. Kostest Sinnlos Programmspeicher und wird schnell 
unübersichtlich.

MfG
Falk

Autor: spyder (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Komisch, dass ist eigentlich der Code ausn LCD-Tut. Naja, was solls. Ähm 
könntest du mir sagen, wo genau RS auf Null setzen muss und meinst du 
mit Initialisierungsbefehlen "lcd_command(0x28);" ??

lg spyder

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
void delay_ms(int ms)
{
  for(; ms>0; ms--) _delay_ms(1);
}

void lcd_data(unsigned char temp1)
{
  PORT_LCD | = (1<<LCD_RS);      // set RS

   PORT_LCD &= 0xF0;
   PORT_LCD |= (temp1 >> 4) & 0xF;   // Oberes Nibble setzen
   lcd_enable();

   PORT_LCD &= 0xF0;
   PORT_LCD |= temp1 & 0xF;          // Unteres Nibble setzen
   lcd_enable();

   _delay_us(50);
}

void lcd_command(unsigned char temp1)
{
  PORT_LCD & = ~(1<<LCD_RS);      // clear RS

   PORT_LCD &= 0xF0;
   PORT_LCD |= (temp1 >> 4) & 0xF;   // Oberes Nibble setzen
   lcd_enable();

   PORT_LCD &= 0xF0;
   PORT_LCD |= temp1 & 0xF;          // Unteres Nibble setzen
   lcd_enable();

   _delay_us(50);
}

// erzeugt den Enable-Puls

void lcd_enable(void)
{
   PORT_LCD |= (1<<LCD_EN);
  _delay_us(1);
   PORT_LCD &= ~(1<<LCD_EN);
}

// Initialisierung:
void lcd_init(void)
{
   // muss 3mal hintereinander gesendet werden zur Initialisierung

  _delay_ms(15);
   PORT_LCD &= 0xF0;
   PORT_LCD |= 0x03;
  PORT_LCD & = ~(1<<LCD_RS);    // clear RS
   lcd_enable();

  _delay_ms(5);
   lcd_enable();

  _delay_ms(1);
   lcd_enable();

   // 4Bit  2 Zeilen  5x7
  lcd_command(0x28);

   // Display ein  Cursor aus  kein Blinken
   lcd_command(0x0C);

   // inkrement / kein Scrollen
   lcd_command(0x06);

  lcd_clear();
}

// Sendet den Befehl zur Löschung des Displays
void lcd_clear(void)
{
   lcd_command(CLEAR_DISPLAY);
    _delay_ms(2);
}

void set_cursor(uint8_t x, uint8_t y)
{
  switch (y) {
    1: lcd_command(0x80+0x00+x); break;    // 1. Zeile
    2: lcd_command(0x80+0x40+x); break;    // 2. Zeile
    3: lcd_command(0x80+0x10+x); break;    // 3. Zeile
    4: lcd_command(0x80+0x50+x); break;    // 4. Zeile
  }
}

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Im Tutorial gibt es leider drei verschiedene Routinen

1/ die Assembler Routine

2/ die C Routine analog zur Assembler Routine
// sendet einen Befehl an das LCD
// wie lcd_data, nur ohne RS zu setzen
void lcd_command(unsigned char temp1)
{
   unsigned char temp2 = temp1;
 
   temp1 = temp1 >> 4;   // oberes Nibble holen
   temp1 = temp1 & 0x0F; // maskieren
   PORTD = temp1;        // setzen
   lcd_enable();
 
   temp2 = temp2 & 0x0F; // unteres Nibble holen und maskieren
   PORTD = temp2;        // setzen
   lcd_enable();
   
   DELAY50US();
}

3/ eine weitere C Routine mit dem Zusatz
// Ich habe kein Hardwareaufbau und kann das Programm nicht testen 
// deshalb bitte ich jemanden kurz das Programm zu verifizieren.
// sendet einen Befehl an das LCD
// wie lcd_data, nur ohne RS zu setzen
void lcd_command(unsigned char temp1)
{
   unsigned char temp2 = temp1;
 
   temp1 = temp1 >> 4;   // oberes Nibble holen
   temp1 = temp1 & 0x0F; // maskieren
   PORT_LCD &= 0xF0;     // <==================== BUG!
   PORT_LCD |= temp1;    // setzen
   lcd_enable();
 
   temp2 = temp2 & 0x0F; // unteres Nibble holen und maskieren
   PORT_LCD &= 0xF0;     // <==================== BUG!
   PORT_LCD |= temp2;    // setzen
   lcd_enable();
   
   DELAY50US();
}

Du verwendest die 3. Variante. Und die hat einen Bug - Falk Brunner 
bemerkt zu recht, dass RS an dieser Stelle NICHT auf 0 gesetzt wird, im 
Gegenteil...

Bei den C-Makros (http://www.mikrocontroller.net/articles/C_Makros
) ist ein Löschmakro vorhanden
/* Bit löschen */
#define clear_bit(var, bit) ((var) &= (unsigned)~(1 << (bit)))

Damit ist hinter der den beiden betreffenden Zeilen jeweils diese Zeile 
einzufügen:

   clear_bit(PORT_LCD, LCD_RS);

Das Tutorial wurde an dieser Stelle korrigiert (mit der ausformulierten, 
nicht der Makroversion).

Autor: spyder (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für die Info!!

Hab das mal so wie Falk Brunner es gepostet hat reinprogrammiert - 
funktioniert super. Nur mag der Zeilenwechsel immer noch nicht :(
Der Compiler schreit bei der set_cursor Funktion immer: "error: expected 
';' before ':' token". Ich hab den Code mit Copy-Paste eingefügt, also 
sollte da ja wohl nix sein. Zum Test hab ich meinen Aufruf jumpline(); 
ausprobiert. Sollte ja das gleiche nur vereinfacht bewirken (in die 2. 
Zeile springen).
void jumpline(void)
{
lcd_command(0x80+0x40); //in 2. zeile springen  0xc0
}
irgendwie mag der nicht wechseln.

lg spyder

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
void set_cursor(uint8_t x, uint8_t y)
{
  switch (y) {
    case 1: lcd_command(0x80+0x00+x); break;    // 1. Zeile
    case 2: lcd_command(0x80+0x40+x); break;    // 2. Zeile
    case 3: lcd_command(0x80+0x10+x); break;    // 3. Zeile
    case 4: lcd_command(0x80+0x50+x); break;    // 4. Zeile
  }
}

So ist es glaube ich besser ;-)

MFG
Falk

Autor: spyder (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jetzt spuckt der Compiler keine Fehlermeldungen aus, aber das LCD ist 
anscheinend immer noch nicht zufrieden.
bei diesem Codeabschnitt:
lcd_out("abcdefghi");
set_cursor(0x00, 0x02);
lcd_out("123456789");
schreibt das LCD in die 1. Zeile: "abcdefghi" und dann nichts mehr, 
weder in die 1. noch in die 2. Zeile. Hab jetzt nochmal im Datenblatt 
nachgeschaut:
In 1-line display mode (N=0, NW=0), DDRAM address is from "00H" to 
"4FH".
In 2-line display mode (N=1, NW=0), DDRAM address in the 1st line is 
from "00H" to "27H",and DDRAM address in
the 2nd line is from "40H" to "67H".

Also müsste das eigentlich passen, wenn ich die Adresse auf 0xC0 
setzen??

lg spyder

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ spyder (Gast)

>Jetzt spuckt der Compiler keine Fehlermeldungen aus, aber das LCD ist
>anscheinend immer noch nicht zufrieden.
>bei diesem Codeabschnitt:

lcd_out("abcdefghi");
set_cursor(0x00, 0x02);
lcd_out("123456789");

>Also müsste das eigentlich passen, wenn ich die Adresse auf 0xC0
>setzen??

Das macht doch set_cursor(), 0xC0 = 0x80 + 0x40;

Hmm, keine Ahnung.

MFG
Falk

Autor: spyder (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, das ist mir schon klar, dass das set_cursor() macht, aber irgendwie 
macht es dann doch nicht. Vielleicht hats da was mit der DDRAM 
Adresse?!?

bitte helft mir

Autor: Werner B. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> set_cursor(0x00, 0x02);

Ich denke Du hast ein zwei Zeilen LCD?
Dann hat (üblicherweise) die zweite Zeile die Nummer 1 und nicht 2.
set_cursor(0x00, 0x01);

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Werner B. (Gast)

>Ich denke Du hast ein zwei Zeilen LCD?
>Dann hat (üblicherweise) die zweite Zeile die Nummer 1 und nicht 2.
>set_cursor(0x00, 0x01);

Ein Blick auf den Quellcode verrät schnell, dass die Zeilen von der 
Funktion als 1..4 gezählt werden.

MFG
Falk

Autor: spyder (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und wo kann nun der Fehler liegen?

lg spyder

Autor: Falk (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@  spyder (Gast)

>Und wo kann nun der Fehler liegen?

Probier mal in der ERSTEN Zeile den Cursor an verschiedene Postitionen 
zu setzen. Das würde erstmal zeigen, dass das halbwegs funktioniert. 
Dann soltest du mal verschiedene Zeichen ausgeben, wo jeweils nur ein 
Bit gesetzt ist. Also 0x01, 0x02, 0x04, 0x08 etc. Das testet deine 
Verkabelung. Und wenn du hast mit dem Oszi mal alle Leitungen anschauen.

MfG
Falk

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.