www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Port wechseln für LCD-Display


Autor: Dietmar P. (dietmar2)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

war schon einmal mit dem LCD-Display-Thema hier im Forum (serielles 
LCD-Display nach U. Radig), das ging nicht.

Habe mir jetzt die Schaltung versuchsweise einmal nach dem 
"AVR-GCC-Tutorial/LCD-Ansteuerung"
http://www.mikrocontroller.net/articles/AVR-GCC-Tu...
aufgebaut und die Software nach der Seite "Erweiterte LCD-Ansteuerung" 
in meinen Atmega32 geflasht.

Das hat auf Anhieb funktioniert!, Jetzt das "Aber".

Die Software verwendet in der lcd-routines.h den PORTD. Der Port ist bei 
mir belegt. Wollte auf PORTC, oder PORTA ausweichen. Habe in 
lcd-routines.h
aus dem "D" jeweils ein "C" bzw. "A" gemacht (PORTD --> PORTC, DDRD --> 
DDRC, PD3 --> PC5, etc.). Für PORTC habe ich auch das JTAG abgeschaltet 
und Verfügbarkeit der Pins geprüft.

Es wird nichts wie auf PORTD angezeigt.
Muss ich da noch mehr umstellen?
Hat das schon jemand einmal probiert?

Autor: Pete K. (pete77)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Poste den (kompletten) Quellcode, dann kann Dir geholfen werden...

Hardware ist soweit ok?

Autor: Rolf Degen (rolfdegen)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hinweis PD3-PD5 reicht nicht. Es werden PD0-PD5 = PC0-PC5 benutzt!

Autor: Dietmar P. (dietmar2)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Rolf Degen:

Dass (fast) der gesamte PORTC notwendig ist, ist mir schon klar. Ich 
hatte angenommen, wenn ich als erstes PORTD und DDRD auf C umstellen, 
zus. zu PD2 - 4 auf PC2 - 4 (hatte mich vertan, ist PD2 - 4, nicht 3 - 
5), dann ist das i.O. Offenbar aber nicht. Bitte mal den für Pete 
geposteten Code ansehen.

@ Pete K. hier mal der Quellcode von lcd-routines.h (ist halt etwas 
viel):
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/LCD-Ansteuerung
//
void lcd_init(void);
void lcd_enable(void);
void lcd_out(uint8_t data);
void lcd_data(uint8_t data);
void lcd_command(uint8_t data);
void lcd_clear(void);
void lcd_home(void);
void lcd_setcursor(uint8_t x, uint8_t y);
void lcd_string(const char *data);
void lcd_generatechar(uint8_t code, const uint8_t *data);
 
////////////////////////////////////////////////////////////////////////////////
// Hier die verwendete Taktfrequenz in Hz eintragen, wichtig!
 
//#define F_CPU 3686400
#define F_CPU 8000000 
////////////////////////////////////////////////////////////////////////////////
// Pinbelegung für das LCD, an verwendete Pins anpassen
 
//  LCD DB4-DB7 <-->  PORTD Bit PD4-PD7
#define LCD_PORT      PORTD
#define LCD_DDR       DDRD
#define LCD_DB        PD4
 
//  LCD RS      <-->  PORTD Bit PD2     (RS: 0=Data, 1=Command)
#define LCD_RS        PD2
 
//  LCD EN      <-->  PORTD Bit PD3     (EN: 1-Impuls für Daten)
#define LCD_EN        PD3
 
////////////////////////////////////////////////////////////////////////////////
// LCD Ausführungszeiten (MS=Millisekunden, US=Mikrosekunden)
 
#define LCD_BOOTUP_MS           15
#define LCD_ENABLE_US           1
#define LCD_WRITEDATA_US        46
#define LCD_COMMAND_US          42
 
#define LCD_SOFT_RESET_MS1      5
#define LCD_SOFT_RESET_MS2      1
#define LCD_SOFT_RESET_MS3      1
#define LCD_SET_4BITMODE_MS     5
 
#define LCD_CLEAR_DISPLAY_MS    2
#define LCD_CURSOR_HOME_MS      2
 
////////////////////////////////////////////////////////////////////////////////
// LCD Befehle
 
// Clear Display -------------- 0b00000001
#define LCD_CLEAR_DISPLAY       0x01
 
// Cursor Home ---------------- 0b0000001x
#define LCD_CURSOR_HOME         0x02
 
// Set Entry Mode ------------- 0b000001xx
#define LCD_SET_ENTRY           0x04
 
#define LCD_ENTRY_DECREASE      0x00
#define LCD_ENTRY_INCREASE      0x02
#define LCD_ENTRY_NOSHIFT       0x00
#define LCD_ENTRY_SHIFT         0x01
 
// Set Display ---------------- 0b00001xxx
#define LCD_SET_DISPLAY         0x08
 
#define LCD_DISPLAY_OFF         0x00
#define LCD_DISPLAY_ON          0x04
#define LCD_CURSOR_OFF          0x00
#define LCD_CURSOR_ON           0x02
#define LCD_BLINKING_OFF        0x00
#define LCD_BLINKING_ON         0x01
 
// Set Shift ------------------ 0b0001xxxx
#define LCD_SET_SHIFT           0x10
 
#define LCD_CURSOR_MOVE         0x00
#define LCD_DISPLAY_SHIFT       0x08
#define LCD_SHIFT_LEFT          0x00
#define LCD_SHIFT_RIGHT         0x01
 
// Set Function --------------- 0b001xxxxx
#define LCD_SET_FUNCTION        0x20
 
#define LCD_FUNCTION_4BIT       0x00
#define LCD_FUNCTION_8BIT       0x10
#define LCD_FUNCTION_1LINE      0x00
#define LCD_FUNCTION_2LINE      0x08
#define LCD_FUNCTION_5X7        0x00
#define LCD_FUNCTION_5X10       0x04
 
#define LCD_SOFT_RESET          0x30
 
// Set CG RAM Address --------- 0b01xxxxxx  (Character Generator RAM)
#define LCD_SET_CGADR           0x40
 
#define LCD_GC_CHAR0            0
#define LCD_GC_CHAR1            1
#define LCD_GC_CHAR2            2
#define LCD_GC_CHAR3            3
#define LCD_GC_CHAR4            4
#define LCD_GC_CHAR5            5
#define LCD_GC_CHAR6            6
#define LCD_GC_CHAR7            7
 
// Set DD RAM Address --------- 0b1xxxxxxx  (Display Data RAM)
#define LCD_SET_DDADR           0x80
 
#define LCD_DDADR_LINE1         0x00
#define LCD_DDADR_LINE2         0x40
#define LCD_DDADR_LINE3         0x10
#define LCD_DDADR_LINE4         0x50  


Autor: avr (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der "Fehler" sollte in der Routine LCD_init liegen.
Die ist glaube ich nicht für den Portwechsel (bzw. Pinwechsel)
geeignet.
Poste mal deine Routine + Portwunsch (C oder A).

avr

Autor: Dietmar P. (dietmar2)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ AVR

Hier mal den Code lcd-routines.c, in der die Funktion lcd_init enthalten 
ist. Es gibt außer einem kleinen main.c nur die beiden lcd-routines.c 
und .h.
Der Portwunsch wäre PORTC, Pins wie im Original bei PORTD.
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus
// http://www.mikrocontroller.net/articles/HD44780
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/LCD-Ansteuerung
//
// Die Pinbelegung ist über defines in lcd-routines.h einstellbar
 
#include <avr/io.h>
#include "lcd-routines.h"
#include <util/delay.h>
 
////////////////////////////////////////////////////////////////////////////////
// Initialisierung: muss ganz am Anfang des Programms aufgerufen werden.
 
void lcd_init(void)
{
    // verwendete Pins auf Ausgang schalten
    uint8_t pins = (0x0F<<LCD_DB)|(1<<LCD_RS)|(1<<LCD_EN);
    LCD_DDR |= pins;
 
    // initial alle Ausgänge auf Null
    LCD_PORT &= ~pins;
 
    // warten auf die Bereitschaft des LCD
    _delay_ms(LCD_BOOTUP_MS);
    
    // Soft-Reset muss 3mal hintereinander gesendet werden zur Initialisierung
    lcd_out(LCD_SOFT_RESET);
    _delay_ms(LCD_SOFT_RESET_MS1);
 
    lcd_enable();
    _delay_ms(LCD_SOFT_RESET_MS2);
 
    lcd_enable();
    _delay_ms(LCD_SOFT_RESET_MS3);
 
    // 4-bit Modus aktivieren 
    lcd_out(LCD_SET_FUNCTION|LCD_FUNCTION_4BIT);
    _delay_ms(LCD_SET_4BITMODE_MS);
 
    // 4-bit Modus / 2 Zeilen / 5x7
    lcd_command(LCD_SET_FUNCTION|LCD_FUNCTION_4BIT|LCD_FUNCTION_2LINE|LCD_FUNCTION_5X7);
 
    // Display ein / Cursor aus / Blinken aus
    lcd_command(LCD_SET_DISPLAY|LCD_DISPLAY_ON|LCD_CURSOR_OFF|LCD_BLINKING_OFF); 
 
    // Cursor inkrement / kein Scrollen
    lcd_command(LCD_SET_ENTRY|LCD_ENTRY_INCREASE|LCD_ENTRY_NOSHIFT);
 
    lcd_clear();
}
 
////////////////////////////////////////////////////////////////////////////////
// Erzeugt einen Enable-Puls
 
void lcd_enable(void)
{
    LCD_PORT |= (1<<LCD_EN);     // EN auf 1 setzen
    _delay_us(LCD_ENABLE_US);    // kurze Pause
    LCD_PORT &= ~(1<<LCD_EN);    // EN auf 0 setzen
}
 
////////////////////////////////////////////////////////////////////////////////
// Sendet eine 4-bit Ausgabeoperation an das LCD
 
void lcd_out(uint8_t data)
{
    data &= 0xF0;                       // obere 4 Bit maskieren
 
    LCD_PORT &= ~(0xF0>>(4-LCD_DB));    // Maske löschen
    LCD_PORT |= (data>>(4-LCD_DB));     // Bits setzen
    lcd_enable();
}
 
////////////////////////////////////////////////////////////////////////////////
// Sendet ein Datenbyte an das LCD
 
void lcd_data(uint8_t data)
{
    LCD_PORT |= (1<<LCD_RS);    // RS auf 1 setzen
 
    lcd_out(data);              // zuerst die oberen, 
    lcd_out(data<<4);           // dann die unteren 4 Bit senden
 
    _delay_us(LCD_WRITEDATA_US);
}
 
////////////////////////////////////////////////////////////////////////////////
// Sendet einen Befehl an das LCD
 
void lcd_command(uint8_t data)
{
    LCD_PORT &= ~(1<<LCD_RS);    // RS auf 0 setzen
 
    lcd_out(data);               // zuerst die oberen, 
    lcd_out(data<<4);            // dann die unteren 4 Bit senden
 
    _delay_us(LCD_COMMAND_US);
}
 
////////////////////////////////////////////////////////////////////////////////
// Sendet den Befehl zur Löschung des Displays
 
void lcd_clear(void)
{
    lcd_command(LCD_CLEAR_DISPLAY);
    _delay_ms(LCD_CLEAR_DISPLAY_MS);
}
 
////////////////////////////////////////////////////////////////////////////////
// Sendet den Befehl: Cursor Home
 
void lcd_home(void)
{
    lcd_command(LCD_CURSOR_HOME);
    _delay_ms(LCD_CURSOR_HOME_MS);
}
 
////////////////////////////////////////////////////////////////////////////////
// Setzt den Cursor in Zeile y (1..4) Spalte x (0..15)
 
void lcd_setcursor(uint8_t x, uint8_t y)
{
    uint8_t data;
 
    switch (y)
    {
        case 1: data=LCD_SET_DDADR|LCD_DDADR_LINE1|x; break;    // 1. Zeile
        case 2: data=LCD_SET_DDADR|LCD_DDADR_LINE2|x; break;    // 2. Zeile
        case 3: data=LCD_SET_DDADR|LCD_DDADR_LINE3|x; break;    // 3. Zeile
        case 4: data=LCD_SET_DDADR|LCD_DDADR_LINE4|x; break;    // 4. Zeile
        default: return;                                   // für den Fall einer falschen Zeile
    }
    lcd_command(data);
}
 
////////////////////////////////////////////////////////////////////////////////
// Schreibt einen String auf das LCD
 
void lcd_string(const char *data)
{
    while(*data)
    {
        lcd_data(*data);
        data++;
    }
}
 
////////////////////////////////////////////////////////////////////////////////
// Schreibt ein Zeichen in den Character Generator RAM
 
void lcd_generatechar(uint8_t code, const uint8_t *data)
{
    // Startposition des Zeichens einstellen
    lcd_command(LCD_SET_CGADR|(code<<3));
 
    // Bitmuster übertragen
    for (uint8_t i=0; i<8; i++)
    {
        lcd_data(data[i]);
    }
} 


Autor: avr (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Routinen scheinen OK.
Hier für EN an PORTC PC3, RS an PORTC PC2 und Daten 4-7 an PORTC
PC 4-7.

wenn die verdrahtung stimmt sollte es gehn. (JTAG ist ja aus)
//  LCD DB4-DB7 <-->  PORTC Bit PC4-PC7
#define LCD_PORT      PORTC
#define LCD_DDR       DDRC
#define LCD_DB        PC4
 
//  LCD RS      <-->  PORTC Bit PC2     (RS: 0=Data, 1=Command)
#define LCD_RS        PC2
 
//  LCD EN      <-->  PORTC Bit PC3     (EN: 1-Impuls für Daten)
#define LCD_EN        PC3
 

avr

Autor: Dietmar P. (dietmar2)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Genauso hatte ich es gemacht und es ging leider nicht.

JTAG hatte ich mit 2 x

  MCUCSR |= (1<<JTD);  // JTAG (PORTC)

abgeschaltet. Die Pins an PORTC sind danach schaltbar, habe es 
überprüft.
PORTA bringt leider auch kein Ergebnis.
Verdrahtung stimmt, PORTD geht ja.

Noch eine Idee?

Autor: avr (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also in den Programmausschnitten ist nichts zu sehen.

Gibt es im Programm noch Routinen die auf Port A bzw. C
zugreifen bzw. Sonderfunktionen an den Pins aktivieren?

Evtl. JTAG mal über die Fuses abschalten (wenn du ISP
programmierst natürlich).

avr

Autor: Dietmar P. (dietmar2)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nein, ich habe bewußt nur die LCD-Routine ein ein Mini-main.c zum 
Probieren:
#include <avr/io.h>
#include "lcd-routines.h"
 
int main(void)
{
    lcd_init();
 
    set_cursor(0,1);
    lcd_string("Test - Zeile 1");
    set_cursor(0,2);
    lcd_string("Test - Zeile 2");
 
    while(1)
    {
    }
 
    return 0;
} 

Die Fuses habe ich auch schon über das Makefile gesetzt.

Autor: Thomas O. (kosmos)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
benutze einen oder mehrere 74HC573 Latch parallel an einem Port, dann 
kannst du Daten Ausgeben und dem entsprechendem Latch sagen für wen Sie 
sind.

Ich hatte damit schon einen EEPROM Programmierer gebaut da ich hier sehr 
viele Adress, Daten- und Steuerleitungen brauchte und zusätzlich ein LCD 
angeschlossen habe.

Autor: Dietmar P. (dietmar2)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Thomas:

Danke für die Info.
Gibt es dazu evtl. auch noch ein Schaltplan als Beispiel und vor allen 
Dingen C-Software Beispiele?
Bin ja schon mit der Schaltung von Ulrich Radig nicht klar gekommen.

Autor: Thomas O. (kosmos)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ließ dir mal das Datenblatt durch wie alles funktioniert.

Im Prinzip gibts du die Daten an einen Port aus und musst dann über 
einen anderen Port ein paar Steuerleitungen ändern.

Daten vom Eingang einlesen und Daten aus dem Latch an den Ausgang geben, 
dann Eingänge wieder hochohmig schalten und der Latch hält das 
gespeicherte Signal an den Ausgängen vor. Dann gibts noch einen 
Transparenten Modus Eingang=Ausgang

Wie man einen Ausgang ansteuerst kannst du dir im AVR Tutorial 
anschauen.

Autor: Dietmar P. (dietmar2)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke Thomas,
werde mir das einmal zu Gemüte führen.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Parallele Latches sind nur sinnvoll an größeren MCs mit externem 
Adreß-Datenbus.
Und sie benötigen eine ganz erhebliche Layoutarbeit, um die vielen 
Leitungen zu routen.

MCs ohne externen Bus erweitert man besser per SPI (74HC165/74HC595).
Die sind außerdem kaskadierbar, d.h. man benötigt keine separaten Enable 
für jedes Register.
Und das Layout wird auch deutlich einfacher.


Peter

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die meisten LCD-Routinen haben das Problem, daß sie an vielen 
verschiedenen Stellen auf das LCD zugreifen.
Vergißt man dann nur eine Stelle zu ändern, krachts.

Macht man dagegen eine einzige Nibble-Funktion und ruft nur diese auf, 
ist das Ändern der Pins ganz easy und sicher:

http://www.mikrocontroller.net/attachment/30300/lcd_drv.zip


Peter

Autor: Dietmar P. (dietmar2)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Peter,

danke für den C-Code.

Du ziehst verschiedene Ports an. Kann ich alles auf einen Port legen, 
z.B. PortC (muss dann natürlich JTAG abschalten)?

Gruß
Dietmar

Autor: Rolf Degen (rolfdegen)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hier eine Lösung,um mit möglich wenig Portpins ein LC-Display mit einem 
ATmega anzusteuern:

http://www.cczwei-forum.de/cc2/thread.php?postid=2...

Autor: Dietmar P. (dietmar2)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Rolf,

danke für die Info.

Hardware verstanden, wo finde ich den C-Code dafür.
Ein Tausch auf Atmega 32 dürfte ja wohl problemlos sein.

Gruß
Dietmar

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dietmar P. schrieb:
> Du ziehst verschiedene Ports an. Kann ich alles auf einen Port legen,

Ausnahmslos jede 6 IOs gehen, nur die Defines anpassen.
Die LCD.C bleibt unverändert.


Oder LCD mit einem Pin:

Beitrag "LCD über nur einen IO-Pin ansteuern"


Peter

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@  Peter Dannegger (peda)

>Macht man dagegen eine einzige Nibble-Funktion und ruft nur diese auf,
>ist das Ändern der Pins ganz easy und sicher:

Richtig, aber das ist im Code im Tutorial auch so.

MfG
Falk

Autor: Rolf Degen (rolfdegen)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dietmar P. schrieb:
> Hallo Rolf,
>
> danke für die Info.
>
> Hardware verstanden, wo finde ich den C-Code dafür.
> Ein Tausch auf Atmega 32 dürfte ja wohl problemlos sein.
>
> Gruß
> Dietmar

Hier: http://www.cczwei-forum.de/cc2/thread.php?postid=2...

Gruß Rolf

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Falk Brunner schrieb:
> Richtig, aber das ist im Code im Tutorial auch so.

http://www.mikrocontroller.net/articles/AVR-GCC-Tu...

Also ich sehe da sogar 6 Nibble-Routinen, die alle geändert werden 
müssen bei Pinumordnung (beliebige Pins).
Je 2 in lcd_data, lcd_command und lcd_init.


Peter

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@  Peter Dannegger (peda)

>Also ich sehe da sogar 6 Nibble-Routinen, die alle geändert werden
>müssen bei Pinumordnung (beliebige Pins).

Eben DAS ist vom OP gar nicht gefordert bzw. nötig.

"Die Software verwendet in der lcd-routines.h den PORTD. Der Port ist 
bei
mir belegt. Wollte auf PORTC, oder PORTA ausweichen. Habe in 
lcd-routines.h
aus dem "D" jeweils ein "C" bzw. "A" gemacht (PORTD --> PORTC, DDRD -->
"

Und genau das sollte funktionieren. Warum es bei ihm nicht geht, weiß 
der Geier.

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.