Forum: Compiler & IDEs LCD, kryptische Zeichen, Zeilenumbruch


von spyder (Gast)


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
1
//    ATMega8 1MHz
2
//    Verwendetes Prog: Programmers Notepad 2
3
//    LCD: PVC160101PTN03
4
//    Anschlussbelegung: 
5
/*    Db4 = Portb.0 
6
      Db5 = Portb.1
7
      Db6 = Portb.2
8
      Db7 = Portb.3
9
      E = Portb.4
10
      Rs = Portb.5
11
*/ 
12
13
#include <avr/io.h>
14
#include "lcd-routines.h"
15
#include <util/delay.h>
16
#ifndef F_CPU
17
#define F_CPU    1000000
18
#endif
19
20
void delay_ms(int ms)
21
{
22
  int t;
23
  for(t=0; t<=ms; t++)
24
  _delay_ms(1); 
25
}
26
 
27
int main(void)
28
{
29
   DDRB = 0xFF;   // Port B komplett als Ausgang einstellen
30
   lcd_init();  //Initialisierung
31
   lcd_clear();
32
   lcd_out("test53685");
33
   delay_ms (2000); // 2 Sekunden Verzögerung
34
   lcd_clear();  //LCD löschen
35
   jumpline();   //In die 2. Zeile springen
36
   delay_ms (2000);
37
   lcd_out("abcdefghijklmnopqrstuvwxyz");
38
   lcd_out("123456789"); 
39
   lcd_data('H');
40
   lcd_data('A');
41
   lcd_data('L');
42
   lcd_data('L');
43
   lcd_data('O');
44
   lcd_data('!');   
45
   lcd_data(':'); 
46
   return 0;
47
}
und da die dazugehörende lcd_routines.c:
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#include "lcd-routines.h"
4
#define DELAY50US() _delay_us(25); _delay_us(25)
5
// LCD Befehle
6
#define CLEAR_DISPLAY 0x01
7
#define CURSOR_HOME   0x02
8
// Pinbelegung für das LCD
9
#define PORT_LCD      PORTB
10
#define LCD_RS        5
11
#define  LCD_EN        4
12
// sendet einen Befehl an das LCD
13
// wie lcd_data, nur ohne RS zu setzen
14
void lcd_command(unsigned char temp1)
15
{
16
   unsigned char temp2 = temp1;
17
   temp1 = temp1 >> 4;   // oberes Nibble holen
18
   temp1 = temp1 & 0x0F; // maskieren
19
   PORT_LCD &= 0xF0;
20
   PORT_LCD |= temp1;        // setzen
21
   lcd_enable();
22
 
23
   temp2 = temp2 & 0x0F; // unteres Nibble holen und maskieren
24
   PORT_LCD &= 0xF0;
25
   PORT_LCD |= temp2;        // setzen
26
   lcd_enable();
27
   
28
   DELAY50US();
29
}
30
 
31
// erzeugt den Enable-Puls
32
void lcd_enable(void)
33
{
34
   PORT_LCD |= (1<<LCD_EN);
35
   asm("nop");
36
   asm("nop");
37
   asm("nop");
38
   PORT_LCD &= ~(1<<LCD_EN);
39
}
40
 
41
// Initialisierung: 
42
void lcd_init(void)
43
{
44
   unsigned char temp3 = 50;
45
 
46
   while(temp3 != 0)
47
   {
48
      _delay_ms(5);  // 5ms = 250ms warten
49
      temp3 = temp3 - 1;
50
   }
51
   // muss 3mal hintereinander gesendet werden zur Initialisierung
52
   PORT_LCD &= 0xF0;
53
   PORT_LCD |= 0x03;       // 0b00000011  
54
   lcd_enable();
55
   PORT_LCD |= 0x03;
56
   lcd_enable();
57
   PORT_LCD |= 0x03;
58
   lcd_enable();
59
 
60
   // 4bit-Modus einstellen
61
   PORT_LCD |= 0x02;       // 0b00000010  
62
   lcd_enable();
63
   _delay_ms(5);
64
 
65
   // 4Bit / 2 Zeilen / 5x7
66
   lcd_command(0x28);  //Mache das 3mal wegen Timing (einfach ausprobiert)    
67
   lcd_command(0x28);
68
   lcd_command(0x28);
69
   
70
   // Display ein / Cursor aus / kein Blinken
71
   lcd_command(0x0C); 
72
 
73
   // inkrement / kein Scrollen
74
   lcd_command(0x06); 
75
}
76
 
77
// Sendet den Befehl zur Löschung des Displays
78
void lcd_clear(void)
79
{
80
   lcd_command(CLEAR_DISPLAY);
81
    _delay_ms(7);
82
}
83
 
84
// Sendet den Befehl: Cursor Home
85
void lcd_home(void)
86
{
87
   lcd_command(CURSOR_HOME);
88
   _delay_ms(5);
89
}
90
91
void lcd_out(char *s)
92
{
93
    while (*s)            //so lange *s != '\0' also ungleich dem "String-Endezeichen"
94
    {
95
        lcd_data(*s);        //Zeichen ausgeben
96
        s++;
97
    }
98
}
99
100
101
void jumpline(void)
102
{
103
lcd_command(0xC0); //in 2. zeile springen (2nd line starts at 40h)
104
_delay_ms(5);
105
}

Hoffentlich kann mir da jemand weiterhelfen!

lg spyder

von Falk B. (falk)


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

von spyder (Gast)


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

von Falk B. (falk)


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

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Im Tutorial gibt es leider drei verschiedene Routinen

1/ die Assembler Routine

2/ die C Routine analog zur Assembler Routine
1
// sendet einen Befehl an das LCD
2
// wie lcd_data, nur ohne RS zu setzen
3
void lcd_command(unsigned char temp1)
4
{
5
   unsigned char temp2 = temp1;
6
 
7
   temp1 = temp1 >> 4;   // oberes Nibble holen
8
   temp1 = temp1 & 0x0F; // maskieren
9
   PORTD = temp1;        // setzen
10
   lcd_enable();
11
 
12
   temp2 = temp2 & 0x0F; // unteres Nibble holen und maskieren
13
   PORTD = temp2;        // setzen
14
   lcd_enable();
15
   
16
   DELAY50US();
17
}

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

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
1
/* Bit löschen */
2
#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).

von spyder (Gast)


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).
1
void jumpline(void)
2
{
3
lcd_command(0x80+0x40); //in 2. zeile springen  0xc0
4
}
irgendwie mag der nicht wechseln.

lg spyder

von Falk B. (falk)


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

von spyder (Gast)


Lesenswert?

Jetzt spuckt der Compiler keine Fehlermeldungen aus, aber das LCD ist 
anscheinend immer noch nicht zufrieden.
bei diesem Codeabschnitt:
1
lcd_out("abcdefghi");
2
set_cursor(0x00, 0x02);
3
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

von Falk B. (falk)


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

von spyder (Gast)


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

von Werner B. (Gast)


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

von Falk B. (falk)


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

von spyder (Gast)


Lesenswert?

Und wo kann nun der Fehler liegen?

lg spyder

von Falk (Gast)


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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.