Forum: FPGA, VHDL & Co. Mico32 4 Bit Pin setzen


von Peter (Gast)


Lesenswert?

Hallo,
ich habe ein Problem mit einen Lattice ECP2 mit Mico32 Core.
Ich möchte damit ein LCD ansteuern (2x16 Zeichen, mit HD44780 
Controller, in 4 bit Modus), allerdings bekomme ich es nicht so richtig 
zum Laufen.

Leitungen sehen folgendermaßen aus:
1
const char *DB47_GPIO_INSTANCE = "DB4_7";  //4 bit breit
2
const char *E_GPIO_INSTANCE = "E";
3
const char *RW_GPIO_INSTANCE = "RW";
4
const char *RS_GPIO_INSTANCE = "RS";

RW ist softwaremäßig auf 0 gesetzt, ich möchte nur schreiben, das Busy 
Flag lese ich auch nicht aus, verwende delays.

Wenn ich jetzt die init für das LCD ausführe mache ich das so:
1
  delay(Wartezeit_15msec);    // Wartezeit   15msec
2
3
//---------------------------------------------
4
  lcd_out(0x30,0); // LCD_SOFT_RESET, muss 3mal gesendet werden
5
  delay(Wartezeit_5msec);  // Wartezeit 5msec
6
  
7
  lcd_out(0x30,0); // LCD_SOFT_RESET, muss 3mal gesendet werden
8
  delay(Wartezeit_1msec);  // Wartezeit 1msec
9
    
10
  lcd_out(0x30,0); // LCD_SOFT_RESET, muss 3mal gesendet werden
11
  delay(Wartezeit_2msec);
12
13
  lcd_out(0x28,0);// | 0x00);  // 4 bit Modus  
14
  delay(Wartezeit_5msec);          
15
16
  lcd_out(0x06,0);// | 0x00);   // Entry Mode
17
  delay(Wartezeit_5msec);          
18
19
  lcd_out(0x01,0);// | 0x00);    // Display Clear
20
  delay(Wartezeit_5msec);          
21
22
  lcd_out(0x0C,0);// | 0x00);  // Display ON
23
         delay(Wartezeit_5msec);
allerdings leuchtet dann nur die 1. Zeile des Displays kurz auf, dann 
wieder finster.

LCD Ausgabe sieht so aus:
1
void lcd_out (unsigned char data, unsigned char rs)
2
{
3
  volatile MicoGPIOCtx_t *lcd_DB4_7 = (MicoGPIOCtx_t *)MicoGetDevice(DB47_GPIO_INSTANCE);
4
  volatile MicoGPIOCtx_t *lcd_RS= (MicoGPIOCtx_t *)MicoGetDevice(RS_GPIO_INSTANCE);
5
6
  if (rs == 1)
7
  {
8
    *((volatile unsigned char *)(lcd_RS->base)) = 1;
9
  }
10
  else if (rs == 0)
11
  {
12
    *((volatile unsigned char *)(lcd_RS->base)) = 0;
13
  }
14
15
 *((volatile unsigned char *)(lcd_DB4_7->base)) = data;// >> 4;   
16
  delay(10);    // Wartezeit 20usec
17
  lcd_enable();
18
  *((volatile unsigned char *)(lcd_DB4_7->base)) = data >> 4;
19
  delay(10);    // Wartezeit 20usec
20
  lcd_enable();
21
}

Wenn ich jetzt Daten sende werden diese auch teilweise korrekt 
angezeigt, 'A', 'B', 'C', 'D' sind korrekt, 'E' funktioniert dann wieder 
nicht es wird ein 'U' angezeigt. Da das 'U' nur eine Spalte neben dem 
'E' im Zeichensatz liegt, denke ich, dass dort das Problem ist.

An zu kurzen Delays kann es glaube ich nicht liegen, habe sie testweise 
schon mal verzehnfacht, hat keine Änderung gebracht.

BTW:
Ist es so korrekt einem 4 bit breiten Bus Werte(Pegel) zuzuweisen, bin 
mir da nicht so sicher?
1
 *((volatile unsigned char *)(lcd_DB4_7->base)) = data;

Vielen Dank
Peter

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

1
  if (rs == 1)
2
  {
3
    *((volatile unsigned char *)(lcd_RS->base)) = 1;
4
  }
5
  else if (rs == 0)
6
  {
7
    *((volatile unsigned char *)(lcd_RS->base)) = 0;
8
  }
Was sollte rs denn ausser 0 und 1 noch werden können?

Warum schreibst du nicht
1
    *((volatile unsigned char *)(lcd_RS->base)) = rs;

Was macht
1
  lcd_enable();

von Peter (Gast)


Lesenswert?

Hallo,

Lothar Miller schrieb:
> Was sollte rs denn ausser 0 und 1 noch werden können?

ist vom Testen übriggeblieben...

Lothar Miller schrieb:
> Warum schreibst du nicht    *((volatile unsigned char *)(lcd_RS->base)) = rs;

Gute Idee, danke!

Lothar Miller schrieb:
> Was macht  lcd_enable();

Sorry, vergessen zu kopieren:
1
void lcd_enable (void)
2
{
3
  volatile MicoGPIOCtx_t *lcd_E = (MicoGPIOCtx_t *)MicoGetDevice(E_GPIO_INSTANCE);
4
  
5
  
6
 *((volatile unsigned char *)(lcd_E->base))  = 1;
7
 delay(Wartezeit_20usec);    // Wartezeit 20usec
8
 *((volatile unsigned char *)(lcd_E->base))  = 0;
9
}

Vielen Dank
Peter

von Peter (Gast)


Lesenswert?

Nochmals Hallo,

ich habe leider noch immer Probleme das LCD zum Laufen zu bringen.
Mal generell, ich muss zuerst das High Nibble, dann das Low Nibble 
senden.
Ist die Ausgaberoutine so korrekt?
1
void lcd_out (unsigned char data, unsigned char rs)
2
{
3
  volatile MicoGPIOCtx_t *lcd_DB4_7 = (MicoGPIOCtx_t *)MicoGetDevice(DB47_GPIO_INSTANCE);
4
  volatile MicoGPIOCtx_t *lcd_RS= (MicoGPIOCtx_t *)MicoGetDevice(RS_GPIO_INSTANCE);
5
6
  *((volatile unsigned char *)(lcd_RS->base)) = rs;
7
 
8
  *((volatile unsigned char *)(lcd_DB4_7->base)) = data;// >> 4;   
9
  delay(10);    // Wartezeit 20usec
10
  lcd_enable();
11
  *((volatile unsigned char *)(lcd_DB4_7->base)) = data >> 4;
12
  delay(10);    // Wartezeit 20usec
13
  lcd_enable();
14
}

Hat schon mal jemand ein 2x16 char LCD mit einem Mico32 verwendet und 
könnte mal ein Beispiel posten, danke vielmals.

Vielen Dank
Peter

von Duke Scarring (Gast)


Lesenswert?

Peter schrieb:
> Hat schon mal jemand ein 2x16 char LCD mit einem Mico32 verwendet und
> könnte mal ein Beispiel posten, danke vielmals.
Nein, aber ein T6963c. Beispiele für HD44780 gibt es doch wie Sand am 
Meer.
Hast Du einen Simulator und ein Oszi um zu prüfen, ob die Pins überhaupt 
wackeln?

Duke

von Peter (Gast)


Lesenswert?

Hallo,
ja, Oszi hab ich, nur dürfte meine Ausgaberoutine nicht funktionieren, 
weil wenn ich zb: "w" an das LCD sende steht dort ein "g".
Wenn ich mir die 4 Datenpins ansehe dann wird auch ein "g" gesendet, 
obwohl ich ein "w" möchte.
Nur kann ich den Fehler in meiner Ausgaberoutine leider nicht finden.

Vielen Dank
Peter

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Peter schrieb:
> Wenn ich mir die 4 Datenpins ansehe dann wird auch ein "g" gesendet,
> obwohl ich ein "w" möchte.
Das ist ominös...
g = 0x67
w = 0x77

Hast du noch andere Paare?

von Peter (Gast)


Lesenswert?

Hallo,

ja, wie oben beschrieben tritt es auch bei anderen Paaren auf zb. "E" 
--> "U" ist auch wieder eine Spalte daneben.
Und auch hier stimmen die Datenports mit dem "U" überein obwohl ich ja 
ein "E" haben möchte.


Vielen Dank
Peter

von Duke Scarring (Gast)


Lesenswert?

Peter schrieb:
> ja, Oszi hab ich,
Gut. Dann guck, ob die Pinzuordnung stimmt. Schreib Dir eine Routine die 
ein bestimmtes Pin mit z.B. 1 Hertz toggelt. Dann kannst Du gut prüfen, 
ob der passende Ausgang dazu reagiert (und auch nur der).

> nur dürfte meine Ausgaberoutine nicht funktionieren,
> weil wenn ich zb: "w" an das LCD sende steht dort ein "g".
w = 0x77 = 0b0111 0111
g = 0x67 = 0b0110 0111

Du verwendest den 4-Bit Modus? Dann könnte es ein Softwareproblem zu 
sein.
Aber Software kann nur so gut sein, wie die Hardware. Also erstmal alle 
LCD-Pins (einzeln) mit der obigen Methode durchklingeln.

Duke

von Peter (Gast)


Lesenswert?

Hallo,
ja, ich verwende den 4 bit Modus.
Pin durchklingeln hab ich gemacht, dann habe ich habe mir die Werte 1 
bis 16 auf die Datenpins ausgeben lassen und bekomme am Oszi (4 Kanal, 
je Kanal ein Datenpin) genau das was ich erwarte.
DB4 toggelt jedesmal
DB5 toggelt jedes zweite mal
DB6 toggelt jedes vierte mal
DB7 toggelt jedes achte mal
Die Pinzuordnung scheint also zu funktionieren.
Kurzschlüsse oder kalte Lötstellen kann man ja eigentlich auch 
ausschließen, weil manche Buchstaben ja korrekt angezeigt werden, oder?

Wenn ich den String "helloworld" raussende wird im LCD "hellog<-bld" 
("<-" ist ein einziges Zeichen, ist wieder eine Spalte neben dem "o") 
angezeigt.
"g" --> "w"
"<-" --> "o"
"b" --> "r"

das "l" und das "d" werden wieder korrekt angezeigt.


Vielen Dank
Peter

von Duke Scarring (Gast)


Lesenswert?

Peter schrieb:
> Die Pinzuordnung scheint also zu funktionieren.
> Kurzschlüsse oder kalte Lötstellen kann man ja eigentlich auch
> ausschließen, weil manche Buchstaben ja korrekt angezeigt werden, oder?
Ok, das klingt wirklich gut.

Also scheint es an der Software zu liegen.
Hast Du die Wartezeiten mal mit dem Oszi geprüft?
Verwendest Du immer noch den Code vom Anfang dieser Seite?
Wenn ja, der sieht mir reichlich kompliziert aus.

Irgendwo hab ich mal sowas gefunden:
1
// Erzeugt einen Enable-Puls
2
static void lcd_enable( void )
3
{
4
    LCD_PORT |= (1<<LCD_EN);     // Enable auf 1 setzen
5
    usleep( LCD_ENABLE_US );     // kurze Pause
6
    LCD_PORT &= ~(1<<LCD_EN);    // Enable auf 0 setzen
7
}

Das Mapping auf die Hardware passiert dann so:
1
#define LCD_PORT      (gpio0->ioout)
2
#define LCD_DDR       (gpio0->iodir)
3
#define LCD_DB        (8)
4
#define LCD_RS        (14)
5
#define LCD_EN        (13)
6
#define LCD_RW        (15)

Hier ist (u.a.) die Adresse des GPIO definiert:
1
timer_t         *timer0     = (timer_t *)  0x80000200;
2
gpio_t          *gpio0      = (gpio_t *)   0x80000400;
3
i2cmst_t        *i2c0       = (i2cmst_t *) 0x80000a00;

Und hier ist die Typdefinition des GPIO:
1
// gpio
2
typedef struct {
3
    volatile uint32_t iodata;   // base + 0x0
4
    volatile uint32_t ioout;    // base + 0x4
5
    volatile uint32_t iodir;    // base + 0x8, for bidir port pins
6
} gpio_t;

Mit dieser Methode kann ich mir soviele GPIOs in mein Design bauen, wie 
im Speicher (32-Bit) Platz haben. An welchem das LCD dranhängt steht in 
einer extra Datei. Wie die LCD-Pins anzusteuern sind steht wieder in 
einer anderen Datei.

Diese Trennung hat sich für mich als recht flexibel herausgestellt.

Duke

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Probier mal das hier noch aus:
1
void lcd_enable (void)
2
{
3
  volatile MicoGPIOCtx_t *lcd_E = (MicoGPIOCtx_t *)MicoGetDevice(E_GPIO_INSTANCE);
4
    
5
 *((volatile unsigned char *)(lcd_E->base))  = 1;
6
 delay(Wartezeit_20usec);    // Wartezeit 20usec
7
 *((volatile unsigned char *)(lcd_E->base))  = 0;
8
 // nicht SOFORT nach dem Enable neue Daten anlegen 
9
 // th ist zwar laut Datenblatt nur 10ns, aber wer weiß...
10
 delay(Wartezeit_20usec);    // Wartezeit 20usec  
11
}

von Peter (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

@Duke:
Vielen Dank für dein ausführliches Posting, denkst du mein Problem 
bezieht sich auf die Pins inkl. Zuordnung oder war das ein genereller 
Ansatz wie mans besser machen kann?
Ja, die Wartezeiten habe ich überprüft, sind korrekt, habe jetzt alle 
Wartezeiten auf 100msec gesetzt (um leichter mitschauen zu können, 100ms 
sind 96,5 msec in Wirklichkeit)
Ja, ich verwende noch den Code vom Anfang dieser Seite, habe nur gerade 
den 4 bit breiten Datenport DB4_7 in 4 einzelne DB4, DB5, DB6 und DB7 
zerlegt, weil ich dachte dass dort das Problem zu suchen ist.
Und habe meine Ausgaberoutine auf das hier geändert, allerdings mit dem 
gleichen Ergebnis:
1
void lcd_out (unsigned char data)
2
{
3
  volatile MicoGPIOCtx_t *lcddb4 = (MicoGPIOCtx_t *)MicoGetDevice(DB4_GPIO_INSTANCE);
4
  volatile MicoGPIOCtx_t *lcddb5 = (MicoGPIOCtx_t *)MicoGetDevice(DB5_GPIO_INSTANCE);
5
  volatile MicoGPIOCtx_t *lcddb6 = (MicoGPIOCtx_t *)MicoGetDevice(DB6_GPIO_INSTANCE);
6
  volatile MicoGPIOCtx_t *lcddb7 = (MicoGPIOCtx_t *)MicoGetDevice(DB7_GPIO_INSTANCE);
7
8
 
9
  if (data & 0x01)  *((volatile unsigned char *)(lcddb4->base)) = 1;
10
  else              *((volatile unsigned char *)(lcddb4->base)) = 0;
11
  
12
  if (data & 0x02)  *((volatile unsigned char *)(lcddb5->base)) = 1;
13
  else              *((volatile unsigned char *)(lcddb5->base)) = 0;
14
15
  if (data & 0x04)  *((volatile unsigned char *)(lcddb6->base)) = 1;
16
  else              *((volatile unsigned char *)(lcddb6->base)) = 0;
17
18
  if (data & 0x08)  *((volatile unsigned char *)(lcddb7->base)) = 1;
19
  else              *((volatile unsigned char *)(lcddb7->base)) = 0;
20
 
21
  lcd_enable();
22
23
}
Diese Funktion wird dann so aufgerufen:
1
  lcd_out(data);        // High Nibble senden
2
  delay(1000);          // 1 sec Wartezeit, damit man am Oszi mitschauen kann
3
  lcd_out(data >> 4);  // Low Nibble senden

anbei noch ein Oszibild von Wert 0 bis 15 der Ausgabe, Kanal 1 ist DB4, 
Kanal 2 ist DB5, Kanal 3 ist DB6 und Kanal 4 ist DB7.

@ Lothar:
hab ich gerade probiert, leider auch ohne Erfolg.


Vielen Dank
Peter

von Peter (Gast)


Lesenswert?

Edit:
>anbei noch ein Oszibild von Wert 0 bis 15 der Ausgabe, Kanal 1 ist DB4,
>Kanal 2 ist DB5, Kanal 3 ist DB6 und Kanal 4 ist DB7.

Das Bild ist ohne 1sec Wartezeit und die Funktion wird so aufgerufen:
1
for (i = 0; i < 16; i++)  lcd_out(i);

Vielen Dank
Peter

von Duke Scarring (Gast)


Lesenswert?

Peter schrieb:
> war das ein genereller
> Ansatz wie mans besser machen kann?
Deine Pinzuordnung scheint ja zu stimmen. War eher so als genereller 
Ansatz gedacht.

> volatile MicoGPIOCtx_t *lcddb4 = (MicoGPIOCtx_t 
*)MicoGetDevice(DB4_GPIO_INSTANCE);
>   volatile MicoGPIOCtx_t *lcddb5 = (MicoGPIOCtx_t 
*)MicoGetDevice(DB5_GPIO_INSTANCE);
>   volatile MicoGPIOCtx_t *lcddb6 = (MicoGPIOCtx_t 
*)MicoGetDevice(DB6_GPIO_INSTANCE);
>   volatile MicoGPIOCtx_t *lcddb7 = (MicoGPIOCtx_t 
*)MicoGetDevice(DB7_GPIO_INSTANCE);
Du hast für jedes Bit/Pin eine eigene GPIO Instanz?
Oder sehe ich da was falsch?

Duke

von Peter (Gast)


Lesenswert?

Hallo,

Duke Scarring schrieb:
> Du hast für jedes Bit/Pin eine eigene GPIO Instanz?

ja, genau, ich dachte, dass es vielleicht bei der Zuweisung zu dem 4 bit 
breiten Datenport (DB4_7) Probleme gibt, deshalb hab ich das 
aufgedröselt.

Vielen Dank
Peter

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Kurios, das Ganze...  :-/

Kontrollier doch mal, ob überhaupt alle Zeichen dargestellt werden:
Abschnittsweise den ASCII Zeichenbereich 0..127 darstellen.


BTW:
Das stimmt nicht so ganz:
1
  lcd_out(data);        // High Nibble senden
2
  lcd_out(data >> 4);   // Low Nibble senden
Ich würde die Kommentare tauschen...

von Peter (Gast)


Lesenswert?

Hallo,

Verda..... Sche.... ich habe die Reihenfolge vertauscht!

so ist es korrekt:
1
lcd_out(data>>4);      // Zuerst das obere Nibble
2
lcd_out(data);         // dann das untere Nibble

und hatte noch einen weiteren Fehler in der init des LCD.

Jetzt funktioniert es, vielen Dank an euch zwei!

Vielen Dank
Peter

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.