Forum: Mikrocontroller und Digitale Elektronik LCD HD44780 an STM32F


von LCD (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich habe ein Problem mit meinem HD44780 LCD Display an einem STM32F10x 
(72MHz).

Ich habe nachfolgenden Code im Einsatz, doch das angehängte Bild stellt 
dar was dabei herauskommt.

Das Grundgerüst des Codes habe ich aus dem Internet und dann an Hand des 
Datenblatts eigentlich so angepasst, dass alles im 4-Bit-Mode laufen 
müsste.

main.c:

int main(void)
{

  toLine2(); // Schreibe in die 2. Zeile des Display

  while(1)
  {
      printChar(0x7F); // Schreibe ständig das Zeichen "<-"
  }
}

lcd.c:

#include "stm32f10x.h"

#define LCD_Port GPIOC
#define RS GPIO_Pin_1
#define EN GPIO_Pin_3
#define D4 GPIO_Pin_4
#define D5 GPIO_Pin_5
#define D6 GPIO_Pin_6
#define D7 GPIO_Pin_7

void lcdInit(void);
void sendCMD(uint8_t c);
void printChar(uint8_t c);
void printString(uint8_t *s);
void clearLCD(void);
void toLine1(void);
void toLine2(void);
void Delay(uint32_t nCount);
void strobeEN(void);
void upNib(uint8_t c);
void downNib(uint8_t c);

void lcdInit(void) {

   GPIO_InitTypeDef GPIO_InitStructure;

   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

    //Init GPIOs
    GPIO_InitStructure.GPIO_Pin   = EN | RS | D4 | D5 | D6 | D7;
    GPIO_ResetBits(LCD_Port, EN | RS | D4 | D5 | D6 | D7);
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
    GPIO_Init(LCD_Port, &GPIO_InitStructure);
    GPIO_ResetBits(LCD_Port, EN | RS | D4 | D5 | D6 | D7);

    Delay(0xffff); // Nach Reset 5ms Zeitschleife

    sendCMD(0x30);

    sendCMD(0x30);

    sendCMD(0x30);

    sendCMD(0x20); // In 4-Bit-Mode wechseln, noch im 8-Bit-Mode

    Delay(0x3FFFC); // 20ms Zeitschleife

    sendCMD(0x28); // Function Set, 4-Bit, 2-zeilig, 5x7 Dot Display

    sendCMD(0x0f); // Display an, Cursor anzeigen, Cursorstelle blinkt

    sendCMD(0x01);          // Display löschen

    Delay(0xffff);          // 5ms Zeitschleife

}

void strobeEN(void) {
  Delay(0xffff);
  GPIO_SetBits(LCD_Port, EN);
  Delay(0xffff);
  GPIO_ResetBits(LCD_Port, EN);
}

void upNib(uint8_t c) {
  if(c & 0x80)
    GPIO_SetBits(LCD_Port, D7);
  else
    GPIO_ResetBits(LCD_Port, D7);
  if(c & 0x40)
    GPIO_SetBits(LCD_Port, D6);
  else
    GPIO_ResetBits(LCD_Port, D6);
  if(c & 0x20)
    GPIO_SetBits(LCD_Port, D5);
  else
    GPIO_ResetBits(LCD_Port, D5);
  if(c & 0x10)
    GPIO_SetBits(LCD_Port, D4);
  else
    GPIO_ResetBits(LCD_Port, D4);
}

void downNib(uint8_t c) {
  if(c & 0x8)
    GPIO_SetBits(LCD_Port, D7);
  else
    GPIO_ResetBits(LCD_Port, D7);
  if(c & 0x4)
    GPIO_SetBits(LCD_Port, D6);
  else
    GPIO_ResetBits(LCD_Port, D6);
  if(c & 0x2)
    GPIO_SetBits(LCD_Port, D5);
  else
    GPIO_ResetBits(LCD_Port, D5);
  if(c & 0x1)
    GPIO_SetBits(LCD_Port, D4);
  else
    GPIO_ResetBits(LCD_Port, D4);
}

// Befehl senden
void sendCMD(uint8_t c) {
  GPIO_ResetBits(LCD_Port, RS); // RS 0=Byte als Befehl interpretieren
  upNib(c); // Obere 4 Bits senden
  strobeEN(); // Enable Leitung
  downNib(c); // Untere 4 Bits senden
  strobeEN(); // Enable Leitung
}

// Byte auf Display ausgeben
void printChar(uint8_t c) {
// Überprüfen ob "c" ein gültiges Zeichen ist (Zeichensatztabelle)
  if(((c>=0x20)&&(c<=0x7F)) || ((c>=0xA0)&&(c<=0xFF))) {
    GPIO_SetBits(LCD_Port, RS); // RS 1=Byte auf Display ausgeben
    upNib(c); // Obere 4 Bits senden
    strobeEN(); // Enable Leitung
    downNib(c); // Untere 4 Bits senden
    strobeEN(); // Enable Leitung
    GPIO_ResetBits(LCD_Port, RS); // RS 0=Byte als Befehl interp.
  }
}

void printString(uint8_t *s) {
  uint8_t i=0;

  while(s[i] != '\0') {
    printChar(s[i]);
    i++;
  }
}

// Display löschen
void clearLCD(void) {
  sendCMD(0x01);
}

// In 1. Zeile bewegen
void toLine1(void) {
  sendCMD(0x00);
}

// In 2. Zeile bewegen
void toLine2(void) {
  sendCMD(0x40);
}

// Zeitschleife
void Delay(uint32_t nCount)
{
  for(; nCount != 0; nCount--);
}

Stimmt die Initialisierung soweit?
Mein Kabellänge zum Display beträgt ca. 12cm ... laut Datenblatt treten 
Probleme wenn dann ab ca. 20cm auf ...

Wäre über Hilfe sehr dankbar! :)

von holger (Gast)


Lesenswert?

>Stimmt die Initialisierung soweit?

Nein. Die ersten 4 Befehle werden als Nibbles übertragen.
Also

3
3
3
2

Mit sendCMD() wird das nix.

von Wolfgang (Gast)


Lesenswert?

Hallo holger,

stimmt - Danke für den Hinweis!
Das bedeutet also eine eigene Funktion nur für die ersten vier Befehle.

Dazu habe ich noch eine Verständnisfrage:

Im 4-Bit-Mode, wir das Byte ja in High-byte und Low-byte aufgeteilt.
Das High-Byte soll zuerst übertragen werden, danach das Low-Byte.
Ich habe 4 Datenleitungen, das passt.

Wie soll ich nun aber dieses Byte im 8-Bit-Mode senden?
Ich kann ja auf Grund der 4 Datenleitungen auch nicht alle 8 bits auf 
einmal senden?!

Wie funktioniert das?

von Jens G. (jensig)


Lesenswert?

steht doch im DB des HD44780, wie der 4bit-Mode zu bedienen ist.

von Mitlesa (Gast)


Lesenswert?

Wolfgang schrieb:
> Wie funktioniert das?

etwa so:
1
// Nibble senden
2
void sendNibble (uint8_t c)
3
{
4
  GPIO_ResetBits(LCD_Port, RS); // RS 0=Byte als Befehl interpretieren
5
  upNib(c);   // Obere 4 Bits senden
6
  strobeEN(); // Enable Leitung
7
}

Ist das so schwer?

Ist es so schwer seine C-Code entsprechend der Richtlinien zu posten?

von Wolfgang (Gast)


Lesenswert?

Hallo Mitlesa,

vielen Dank für deine Hilfe und deinen Hinweis bezüglich der 
Formatierung.
Ich habe es nun genau durchgelesen.

Das Code-Beispiel von dir erscheint mir richtig, allerdings muss noch 
ein anderer Fehler im Quellcode sein.


Hier nochmal die Initialisierung:
1
void lcdInit(void) {
2
3
    GPIO_InitTypeDef GPIO_InitStructure;
4
5
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
6
7
    //Init GPIOs
8
    GPIO_InitStructure.GPIO_Pin   = EN | RS | D4 | D5 | D6 | D7;
9
    GPIO_ResetBits(LCD_Port, EN | RS | D4 | D5 | D6 | D7);
10
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
11
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
12
    GPIO_Init(LCD_Port, &GPIO_InitStructure);
13
    GPIO_ResetBits(LCD_Port, EN | RS | D4 | D5 | D6 | D7);
14
15
    Delay(0xffff); // 5ms Zeitschleife
16
17
    sendNibble(0x30);
18
19
    sendNibble(0x30);
20
21
    sendNibble(0x30);
22
23
    sendNibble(0x20); // In 4-Bit-Mode wechseln, noch im 8-Bit-Mode
24
25
    Delay(0x3FFFC); // 20ms Zeitschleife
26
27
    sendCMD(0x28); // Function Set, 4-Bit, 2-zeilig, 5x7 Dot Display
28
29
    sendCMD(0x0f); // Display an, Cursor anzeigen, Cursorstelle blinkt
30
31
    sendCMD(0x01);
32
33
    Delay(0xffff); // 5ms Zeitschleife
34
35
}

Noch zur Info:

Die R/W Leitung habe ich direkt auf Masse gelötet.

von Mitlesa (Gast)


Lesenswert?

Wolfgang schrieb:
> allerdings muss noch ein anderer Fehler im Quellcode sein.

Lies das Datenblatt zur Initialisierung eines HD44780.

Vor und zwischen dem Senden der Initialisierungs-Nibbles müssen
Delays eingebaut sein.

von Mitlesa (Gast)


Lesenswert?

LCD schrieb:
> // Zeitschleife
> void Delay(uint32_t nCount)
> {
>   for(; nCount != 0; nCount--);
> }

Da müsstest du dir vielleicht noch etwas intelligenteres
einfallen lassen. Im schlimmsten Fall wird dir diese Schleife
vom Compiler wegoptimiert sodass dein Delay nahezu Null ist.

von m.n. (Gast)


Lesenswert?

Wolfgang schrieb:
> Noch zur Info:
>
> Die R/W Leitung habe ich direkt auf Masse gelötet.

Hast Du das beim Probieren dort auch so gemacht? 
Beitrag "Re: LCD-Modul 2x16 am STM32F4Discovery-Board"
Da muß nämlich R/W beschaltet sein.

von Mitlesa (Gast)


Lesenswert?

m.n. schrieb:
> Da muß nämlich R/W beschaltet sein.

Begündung?

Gegenbegründung: Solange man das LCD nur beschreibt, also keinen
Handshake macht indem man den Status liest, muss die R/W Leitung
auch nicht bedient werden und kann statisch auf Low gezogen werden.
Nur das Timing durch Delays muss eingehalten werden ....

von m.n. (Gast)


Lesenswert?

Änder mal Deinen Namen in "Nichtleser". Deine unqualifizierten 
Kommentare beginnen zu nerven.

von W.S. (Gast)


Lesenswert?

LCD schrieb:
> ich habe ein Problem mit meinem HD44780 LCD Display an einem STM32F10x

LCD schrieb:
> void upNib(uint8_t c) {
>   if(c & 0x80)
>     GPIO_SetBits(LCD_Port, D7);
>   else
>     GPIO_ResetBits(LCD_Port, D7);
>   if(c & 0x40)
>     GPIO_SetBits(LCD_Port, D6);
>   else
>     GPIO_ResetBits(LCD_Port, D6);
>   if(c & 0x20)
>     GPIO_SetBits(LCD_Port, D5);
>   else
>     GPIO_ResetBits(LCD_Port, D5);
>   if(c & 0x10)
>     GPIO_SetBits(LCD_Port, D4);
>   else
>     GPIO_ResetBits(LCD_Port, D4);
> }

Nein.
Du hast kein Problem mit deinem HD44780.
Du hast hingegen ein ernstes Problem mit deinem STM32F10x - und zwar mit 
dessen sinnvoller Benutzung. Was du da mit den Portbits anstellst, sieht 
ja grauenhaft aus. Lies mal das Manual zu deinem Chip, insbesondere 
dazu:
1
  volatile unsigned long BSRR;  /* Bit-SetReset:
2
                                   Bits 0..15->set ODR Bits 0..15,
3
           Bits 16..31->reset ODR Bits 0..15 */

Abgesehen davon sollte dir die meiner Erinnerung nach knapp 1 seitige 
Tabelle mit den Befehlscodes zum Display genug sagen, um so ein Display 
sinnvoll ansteuern zu können.

W.S.

von Wolfgang (Gast)


Lesenswert?

Hallo W.S.,
vielen Dank für deinen Tipp.
Ich werde mich nun umgehend damit befassen.
Aber eigentlich ist es ja nur die Schreibweise die verbessert werden 
sollte, die Funktion an sich sollte ja trotzdem funktionieren.

von Wolfgang (Gast)


Angehängte Dateien:

Lesenswert?

@Mitlesa, danke für deinen Tipp mit volatile!

Da lag der Fehler - die Zeitschleife wurde wohl wegoptimiert.

So funktioniert es jetzt:
1
void Delay(volatile uint32_t nCount)
2
{
3
  for(; nCount != 0; nCount--);
4
}

Vielen Dank!

von Mitlesa (Gast)


Lesenswert?

Wolfgang schrieb:
> Da lag der Fehler - die Zeitschleife wurde wohl wegoptimiert.

Ja freut mich für dich!

Aber etwas gut Definiertes ist das ja nicht mit deiner Zählschleife.

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.