Forum: Mikrocontroller und Digitale Elektronik 4Bit LCD, restliche Portpins Problem


von Sebastian F. (snake080)


Lesenswert?

Hallo liebe Gemeinde,
ich habe folgendes Problem:

Ein Display im 4-Bit Modus wie im Tutorial angeschlossen (PD0-PD5). PD6 
& PD7
benutze ich als Eingänge.
Allerdings werden mir jedes mal, wenn ich etwas mit dem Display mache, 
meine Eingangspins auf 0 gesetzt... (ist nicht gerade Sinnvoll, da ich 
dort abfragen mache). In den LCD-Routinen wird immer der ganze Port 
beschrieben. Daran wirds liegen.
Hat jemand einen Tipp, wie man das hinbekommt ohne die ganze Datei 
umzuschreiben?

von XY (Gast)


Lesenswert?

(Port & Pinmaskierung) |= Daten

von Michael B. (planlessmichi)


Angehängte Dateien:

Lesenswert?

Schau mal, ob Du mit den Funktionen in den angehängten LCD-Sourcen 
auskommst; ich habe die mal vor längerer Zeit so umgeschrieben, dass ich 
jeden beliebigen PIN nehmen kann und auch nur die benötigt werden...
Vielleicht musst Du es noch etwas umbauen, aber sollte eiegentlich 
passen...

Viele Grüße,
Michael

von Peter D. (peda)


Lesenswert?

Sebastian Fässer schrieb:
> Hat jemand einen Tipp, wie man das hinbekommt ohne die ganze Datei
> umzuschreiben?

Nö, man muß viele Stellen anpassen.

Oder gleich nen universellen Code nehmen:

Beitrag "Re: LCD nicht nur für einen Port in C"


Peter

von Sebastian F. (snake080)


Lesenswert?

Danke für die Antworten,
ich versuche mich mal da durchzuarbeiten. Ich habe keine Ahnung von C. 
Hätte ich vielleicht dazuschreiben sollen :)

von Michael B. (planlessmichi)


Lesenswert?

Sebastian Fässer schrieb:
> Ich habe keine Ahnung von C.

Kein Problem; ändere einfach nur die Zeilen 37-45 in der .h Datei, dass 
es mit Deinem Aufbau übereinstimmt. "Sollte" reichen.

von Sebastian F. (snake080)


Lesenswert?

Joa, das mit dem ändern habe ich gemacht. Dann mit .include die Datei 
eingefügt und die ganzen Verknüpfungen rausgeworfen, die ich nicht habe. 
Und jetzt kommen lauter Fehler: Syntax error Unexpected "("  Ab den 
Zeilen:
void lcd_data(unsigned char temp1);

von Michael B. (planlessmichi)


Lesenswert?

.include?? Arbeitest Du mit WinAVR (über AVRStudio?) oder CodeVision?
Oder meintest Du #include?

Welche includes hast Du denn in der C-Datei entfernt? Oder besser: 
Welche stehen noch drin?

von Sebastian F. (snake080)


Lesenswert?

Nur die Verknüpfungen zu deinen Dateien, die ich nicht habe:
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>

#include <util/delay.h>

Die #include "lcd-routines.h" steht natürlich noch drin :-)
Ich arbeite mit dem AVR Studio. Schön im alten Assembler...

von Hc Z. (mizch)


Lesenswert?

Includes in spitzen Klammern sind systemweit; sie gehören zum Compiler, 
nicht zum Projekt.  Die hast Du also mit dem Compiler installiert.

von Michael B. (planlessmichi)


Lesenswert?

O.k.; das ist jetzt dann aber schon ein Problem :-)
"Keine Ahnung von C" haben und gar nicht in C programmieren ist ein 
kleiner Unterschied :-)
In C wird das üblicherweise so gehandhabt, das includes mit spitzen 
Klammern also <xyz.h> includes von einer Entwicklungsumgebung sind; 
globale Header hört sich doof an; also welche, die in irgendeinem Paket 
(IDE) waren. "Persönliche" includes werden dann mit "" eingebunden. (hat 
alles was mit der Suchreihenfolge zu tun).
Kurz: (Fast) Alle includes sind nötig und können nicht einfach 
weggelassen werden. Wie man das jetzt NUR auf Assembler umbiegen kann... 
Keine Ahnung.

Vielleicht installierst Du Dir doch noch das WinAVR-Paket dazu?

EDIT: Mein Vorredner hat die bessere Bezeichnung für "globale" Header 
gehabt: systemweit :-)

Die stehen halt dann an Orten, die irgendwie in den "PATH"-Variablen des 
Systems bekannt gemacht wurden (während der Installation der 
entsprechenden Bibliotheken / IDEs)

von Micha (Gast)


Lesenswert?

Sebastian Fässer schrieb:
> Allerdings werden mir jedes mal, wenn ich etwas mit dem Display mache,
> meine Eingangspins auf 0 gesetzt
Sicher? Egtl. sollte da nichts passieren, wenn du die entsprechenden 
DDR-Bits nicht veränderst, außer dass du die Pullups ein-/ausschaltest.

Ändere doch deine ASM-Routinen entsprechend: lies zuerst die Zustände 
der Bits, maskiere alles entsprechend und schreibe die neuen Werte dann 
wieder nach draussen.

von Sebastian F. (snake080)


Lesenswert?

Danke für die gute Erklärung.
Man sollte aber trotzdem nicht versuchen Assembler und C zu mischen, 
oder?
Ich habe mal versucht die Dateien zu finden. Im System sind sie schon 
mal nirgends... und irgenwas installieren wovon ich nix kenne macht auch 
nicht viel Sinn, oder? Dann lieber umständlich und hoffen dass man 
versteht was man macht :)
Also werde ich mal die Variante mit dem Maskieren versuchen...

von Hc Z. (mizch)


Lesenswert?

Bis auf die Bemerkung, dass Du Dich nicht mit C auskennst, hast Du noch 
nichts über die verwendete Sprache gesagt.  Es wäre recht günstig, Deine 
Mitleser darüber nicht im Dunkeln tappen zu lassen und dabei vielleicht 
auch mal den Code, über den Du redest, zu posten.

Das Mischen von Assembler und C ergibt sogar sehr viel Sinn.  Man kann 
dann jeweils die Sprache wählen, die für die momentane Teilaufgabe am 
besten geeignet ist.  Die Übergabe-Modalitäten zwischen beiden Sprachen 
sind einfach und gut dokumentiert (ich rede hier über gcc und avr-as) 
und leicht zu kapieren.

Aber äußere Dich doch zuerst mal zu Deiner Programmier-Umgebung.  Die 
ist (jedenfalls mir) noch nicht klar.  Es scheint, Du verwendest 
avrasm2.  In Sachen Hochsprachenkopplung ist der nicht einfach zu 
handhaben und eine ziemliche Sackgasse.

von Sebastian F. (snake080)


Lesenswert?

Entschuldigung, wenn das noch undeutlich ist.
Ich programmiere im AVR-Studio4 mit dem Assembler.
Code von der LCD Ansteuerung ist so ziemlich der des Tutorials. 
(http://www.mikrocontroller.net/articles/AVR-Tutorial:_LCD#Initialisierung_f.C3.BCr_4_Bit_Modus)

Mein Code ist ja egal, da ja feststeht, dass in der Routine an den 
Datenrichtungsregistern etwas geändert wird... aber ich kanns ja mal 
posten:


.include "m8def.inc"

.def temp1  = r16  ; Temporärregister
.def temp2  = r17  ; Temp2
.def temp3  = r18

ldi temp1, LOW(RAMEND)
out SPL,temp1
ldi temp1, HIGH(RAMEND)
out SPH,temp1

; ADC initialisieren

ldi temp1, 0b11000000  ; Int.2,56V ref
out ADMUX, temp1

ldi temp1, 0b10000011  ; ADC enable / f/8
out ADCSRA, temp1

;**************************************
; Portpins zuweisen

ldi temp1, 0xfe
out DDRB, temp1

sbi DDRC,4
sbi DDRC,5

ldi temp1, 0x3f
out DDRD, temp1

; Pull-ups an
sbi PORTB,0
sbi PORTD,6
sbi PORTD,7

rcall lcd_init
rcall lcd_clear

main:
nop
rjmp main

Sobald ich das lcd initialisiere bleiben PD6 & PD7 auf GND. Wenn ich die 
beiden rausnehme funktioniert es mit den Pull-ups...

von Hc Z. (mizch)


Lesenswert?

Die LCD-Routinen im Tutorial machen nichts mit DDRD, nur in der danach 
folgenden Anwendung wird DDRD geändert.

Mit Deiner Aussage „Code von der LCD Ansteuerung ist so ziemlich der des 
Tutorials" kann ich in diesem Zusammenhang nichts anfangen, denn mit 
Deiner Fehlerbeschreibung am Beginn dieses Threads passt das nicht 
zusammen - nach der muss irgendwas in DDRD rumschreiben.  Eine 
Überprüfung lässt Du aber nicht zu, denn den Display-Code verschweigst 
Du nach wie vor.  Somit kann man nur raten (und das ist nicht mein 
Ding).

EDIT: siehe nächsten Post (kommt gleich).

von Hc Z. (mizch)


Lesenswert?

Eine Möglichkeit wäre allerdingst, wenn Du keine "harte" Null an den 
Ausgängen 6 und 7 bekommst, sondern nur die Pullups weggeschaltet 
werden.  Das ist beim Code im Tutorial natürlich (deutlich sichtbar) der 
Fall.  Falls das zutrifft, musst Du an allen Stellen, an denen nach 
PORTS geschrieben wird, die beiden oberen Bits setzen, z.B aus
1
           ldi temp1, 0b00000011        ; muss 3mal hintereinander gesendet
2
           out PORTD, temp1             ; werden zur Initialisierung
wird
1
           ldi temp1, 0b11000011        ; muss 3mal hintereinander gesendet
2
           out PORTD, temp1             ; werden zur Initialisierung
und so weiter.  Einfach im Quelltext nach „PORTD“ suchen und dafür 
sorgen, dass Bit 6 und 7 gesetzt sind.

von Sebastian F. (snake080)


Lesenswert?

So, hier haben wir den Code den ich benutze:
bei dem lcd_init wird der Port geändert... Aber auch wenn ich die bits 
einzelnd auf 1 setze klappts nicht...



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                 LCD-Routinen                ;;
;;                 ============                ;;
;;              (c)andreas-s@web.de            ;;
;;                                             ;;
;; 4bit-Interface                              ;;
;; DB4-DB7:       PD0-PD3                      ;;
;; RS:            PD4                          ;;
;; E:             PD5                          ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


.equ LCD_PORT = PORTD
.equ LCD_DDR  = DDRD
.equ PIN_E    = 5
.equ PIN_RS   = 4

 ;sendet ein Datenbyte an das LCD
lcd_data:
  push temp1
  push temp2
           mov temp2, temp1             ; "Sicherungskopie" für
                                        ; die Übertragung des 2.Nibbles
           swap temp1                   ; Vertauschen
           andi temp1, 0b00001111       ; oberes Nibble auf Null setzen
           sbr temp1, 1<<PIN_RS         ; entspricht 0b00010000
           out LCD_PORT, temp1          ; ausgeben
           rcall lcd_enable             ; Enable-Routine aufrufen
                                        ; 2. Nibble, kein swap da es 
schon
                                        ; an der richtigen stelle ist
           andi temp2, 0b00001111       ; obere Hälfte auf Null setzen
           sbr temp2, 1<<PIN_RS         ; entspricht 0b00010000
           out LCD_PORT, temp2          ; ausgeben
           rcall lcd_enable             ; Enable-Routine aufrufen
           rcall delay50us              ; Delay-Routine aufrufen
  pop temp2
  pop temp1
          ret                          ; zurück zum Hauptprogramm


 ; sendet einen Befehl an das LCD

lcd_command:                            ; wie lcd_data, nur RS=0
  push temp1
  push temp2
     mov temp2, temp1
           swap temp1
           andi temp1, 0b00001111
           out LCD_PORT, temp1
           rcall lcd_enable
           andi temp2, 0b00001111
           out LCD_PORT, temp2
           rcall lcd_enable
           rcall delay50us
  pop temp2
  pop temp1
          ret

 ; erzeugt den Enable-Puls
lcd_enable:
           sbi LCD_PORT, PIN_E          ; Enable high
           nop                          ; 3 Taktzyklen warten
           nop
           nop
           cbi LCD_PORT, PIN_E          ; Enable wieder low
           ret                          ; Und wieder zurück

 ; Pause nach jeder Übertragung
delay50us:                              ; 50us Pause
           ldi  temp1, $42
delay50us_:dec  temp1
           brne delay50us_
           ret                          ; wieder zurück

 ; Längere Pause für manche Befehle
delay5ms:                               ; 5ms Pause
       push temp1
       push temp2
           ldi  temp1, $21
WGLOOP0:   ldi  temp2, $C9
WGLOOP1:   dec  temp2
           brne WGLOOP1
           dec  temp1
           brne WGLOOP0
           pop temp2
       pop temp1
       ret                          ; wieder zurück

 ; Initialisierung: muss ganz am Anfang des Programms aufgerufen werden
lcd_init:
   push temp1
        ldi   temp1, 0xFF            ; alle Pins am Ausgabeport auf 
Ausgang
        out   LCD_DDR, temp1

           ldi   temp3,6
powerupwait:
           rcall delay5ms
           dec   temp3
           brne  powerupwait
           ldi   temp1,    0b00000011   ; muss 3mal hintereinander 
gesendet
           out   LCD_PORT, temp1        ; werden zur Initialisierung
           rcall lcd_enable             ; 1
           rcall delay5ms
           rcall lcd_enable             ; 2
           rcall delay5ms
           rcall lcd_enable             ; und 3!
           rcall delay5ms
           ldi   temp1, 0b00000010      ; 4bit-Modus einstellen
           out   LCD_PORT, temp1
           rcall lcd_enable
           rcall delay5ms
           ldi   temp1, 0b00101000      ; 4 Bit, 2 Zeilen
           rcall lcd_command
           ldi   temp1, 0b00001100      ; Display on, Cursor off
           rcall lcd_command
           ldi   temp1, 0b00000100      ; endlich fertig
           rcall lcd_command
       pop temp1
          ret

 ; Sendet den Befehl zur Löschung des Displays
lcd_clear:
     push temp1
           ldi   temp1, 0b00000001      ; Display löschen
           rcall lcd_command
           rcall delay5ms
  pop temp1
  ret

 ; Sendet den Befehl: Cursor Home
lcd_home:
  push temp1
           ldi   temp1, 0b00000010      ; Cursor Home
           rcall lcd_command
           rcall delay5ms
    pop temp1
          ret

 ; Einen konstanten Text aus dem Flash Speicher
 ; ausgeben. Der Text wird mit einer 0 beendet
lcd_flash_string:
           push  temp1
           push  ZH
           push  ZL

lcd_flash_string_1:
           lpm   temp1, Z+
           cpi   temp1, 0
           breq  lcd_flash_string_2
           rcall  lcd_data
           rjmp  lcd_flash_string_1

lcd_flash_string_2:
           pop   ZL
           pop   ZH
           pop   temp1
           ret

von Sebastian F. (snake080)


Lesenswert?

Danke für den Tipp, aber klappt leider auch nicht :(

von Hc Z. (mizch)


Lesenswert?

Erfreulich ist, dass Du in Deinem Code Labels verwendet hast anstelle 
der festen Zahlen.

Ansonsten hat sich Deine Antwort mit meinem Post wohl überschnitten (in 
dem es PORTD statt PORTS an einer Stelle heißen muss).  Trifft es denn 
zu, dass die Pins 6 und 7 gar nicht Ausgang sind, sondern nur der Pullup 
weggeschaltet wird?  Das macht nämlich Dein Code auch.

von Hc Z. (mizch)


Lesenswert?

Nun ja ...
1
lcd_init:
2
   push temp1
3
        ldi   temp1, 0xFF            ; alle Pins am Ausgabeport auf
4
Ausgang
5
        out   LCD_DDR, temp1
Das wurde von Dir den Routinen aus dem Tutorial zugefügt.  Was glaubst 
Du denn, was das aus Deinen Inputs an Bit 6 und 7 von LCD_PORT macht?

von Sebastian F. (snake080)


Lesenswert?

Ja wir haben uns ein wenig überschnitten.
I ch muss zugeben, das ich noch gar nicht probiert habe, ob es noch 
Ausgänge sind oder nicht. Das werde ich mal schnell machen...

Das hier habe ich gerade schon geändert:

lcd_init:
   push temp1
   ldi   temp1, 0xFF            ; alle Pins am Ausgabeport auf
Ausgang
   out   LCD_DDR, temp1

von Hc Z. (mizch)


Lesenswert?

Schon wieder überschnitten.  <Westernmelodie>Er war einsam, aber 
schneller ...</Westernmelodie>

von Sebastian F. (snake080)


Lesenswert?

Hehe, ja das können wir gut :)
So, ich habe das mal ein wenig umgeschrieben:
- Definition DDRD
- Initialisierung lcd_init
- Erneute definition DDRD
- Pull-ups einschalten

Ergebis: Alles funktioniert, wie es soll.

Dann habe ich ein lcd_clear hinter die Pull-ups gesetzt (mit umschreiben 
der lcd_command).

Ergebnis: Eingänge auf 0. Funktionieren aber noch.

Nachträgliches einschalten mit "sbi PORTD,6" bzw 7 funktioniert auch...

von Hc Z. (mizch)


Lesenswert?

> Dann habe ich ein lcd_clear hinter die Pull-ups gesetzt (mit umschreiben
> der lcd_command).
> Ergebnis: Eingänge auf 0. Funktionieren aber noch.

Hast Du aus
1
           andi temp1, 0b00001111
2
           out LCD_PORT, temp1
in lcd_command ein
1
           andi temp1, 0b00001111
2
           ori temp1, 0xC0
3
           out LCD_PORT, temp1
gemacht und danach analog für temp2 auch?  Dann sollte das nicht 
passieren.

von Sebastian F. (snake080)


Lesenswert?

Jupp, das ist auch wesentlich eleganter als am ende von lcd_command die 
Pull-ups wieder einzuschalten.
Jetzt funktioniert alles.
Vielen Dank!!!

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.