Forum: Mikrocontroller und Digitale Elektronik Bits schieben und auf Port ausgeben (c)


von Sonke A. (soeni)


Lesenswert?

Ich habe folgendes Problem:

für ein LCD muss ich Daten senden. Hierzu teile ich diese zuerst in die 
Nibbels auf (hab ich soweit übernommen aus dem Tutorial.) Mein Problem 
ist nun, dass ich die Ausgabe nicht ganz verstehe. Ich muss nämlich 
nicht wie im Tutorial die ersten 4 Bit des Ports für die Datenleitung 
nutzen, sondern die lezten 4 (also Bit 4-7).

Hier mal der Codeausschnitt mit Kommentaren:
1
  void lcd_data(unsigned char temp1){
2
3
    
4
     unsigned char temp2 = temp1;
5
 
6
     LCD_PORT |= (1<<LCD_RS);        // RS auf 1 setzen funktioniert soweit
7
    temp1 = temp1 >> 4;        // erstes (oberes) Nibbel holen
8
     temp1 = temp1 + 0x0F;      // hiermit verschiebt man doch nach oben
9
     LCD_PORT &= 0x0F;        // obere 4 Bit löschhen
10
     LCD_PORT |= temp1;              // setzen
11
     lcd_enable();
12
 
13
    temp2 = temp2 + 0x0F;      // zweites (unteres) Nibbel holen und nach oben schieben
14
     LCD_PORT &= 0x0F;        // obere 4 Bit löschhen
15
     LCD_PORT |= temp2;              // setzen
16
     lcd_enable();
17
   
18
     _delay_us(42);
19
  
20
  }

Kann mir villeicht jemand sagen, wie ich das Problem löse, das die 
jeweiligen Nibbels an die oberen Bits gelangen???

Gerne auch mit Erklährung, ich bin da noch nicht so fit.

von Matthias L. (Gast)


Lesenswert?

Wenn du meinst, dass du von temp1 erst die oberen vier bits und dann die 
unteren vier bits auf LCD_PORT[7:4] ausgeben willst, dann versuch mal 
das:
1
void lcd_data(unsigned char temp1)
2
{
3
  unsigned char temp2 = (temp1<<4);
4
 
5
  LCD_PORT |= (1<<LCD_RS);                     // RS setzen
6
7
  LCD_PORT |= (  (LCD_PORT & 0x0F) | (temp1 & 0xF0) );  // ausgeben
8
9
  lcd_enable();                                // ???????????????????
10
  _delay_us(42);
11
12
  LCD_PORT |= (  (LCD_PORT & 0x0F) | (temp2 & 0xF0) );  // ausgeben
13
14
  lcd_enable();                                // ???????????????????
15
  _delay_us(42);
16
  
17
  }

Die Fragezeichen zeigen an, dass ich nicht weiß, wie du die Daten im 
Display übernimmst. Also welche Bits da wie gesetzt/gelöscht werden 
müssen...

von Stefan E. (sternst)


Lesenswert?

Matthias Lipinsky wrote:
1
  LCD_PORT |= (  (LCD_PORT & 0x0F) | (temp1 & 0xF0) );  // ausgeben
2
...
3
  LCD_PORT |= (  (LCD_PORT & 0x0F) | (temp2 & 0xF0) );  // ausgeben

Das "|=" ist hier falsch und muss durch "=" ersetzt werden.

von Matthias L. (Gast)


Lesenswert?

>Das "|=" ist hier falsch und muss durch "=" ersetzt werden.

Das ist korrekt.

Ich wollte nur sehen, ob du aufpasst ;-))

von Sonke A. (soeni)


Lesenswert?

also ich habe Port D meines Atmega128 mit einem Display verbunden. 
hierbei nutze ich Pin4-7 als 4 Bit Datenbus zum Display. Pin PD0 ist der 
Enablepin und Pin PD1 ist der RS Pin. Pin 2 und 3 sind für den UART 
reserviert, deshalb diese Belegung. Nun hoffte ich das Beispiel aus dem 
Tutorial abändern zu können, und anstatt der ersten 4Pins des Ports die 
Letzten zu nehmen. Leider funktioniert euer Code auch nicht.

PS. Es geht um ein alphanumerisches LCD

Hier nochmal der jetzige Code:
1
void lcd_data(unsigned char temp1){
2
3
    
4
   unsigned char temp2 = (temp1<<4);
5
 
6
  LCD_PORT |= (1<<LCD_RS);                     // RS setzen
7
8
  LCD_PORT = (  (LCD_PORT & 0x0F) | (temp1 & 0xF0) );  // ausgeben
9
10
  lcd_enable();                                // ???????????????????
11
  _delay_us(42);
12
13
  LCD_PORT = (  (LCD_PORT & 0x0F) | (temp2 & 0xF0) );  // ausgeben
14
15
  lcd_enable();                                // ???????????????????
16
  _delay_us(42);
17
18
  
19
  }

von soeni (nicht angemeldet) (Gast)


Lesenswert?

kann mir hierbei nochmal jemand helfen??

von Matthias L. (Gast)


Lesenswert?

>Leider funktioniert euer Code auch nicht.

Da meine ??????????????? in dem Code noch drin sind, vermute ich, dass 
du nur COdeschnipsel zusammenkopierst und nict selbst schreibst, sonst 
hättest du das wohl beachtet:

>>Die Fragezeichen zeigen an, dass ich nicht weiß, wie du die Daten im
>>Display übernimmst. Also welche Bits da wie gesetzt/gelöscht werden
>>müssen...

Wir kennen ja weder die Funktion lcd_enable, noch das Display noch den 
Anschluss desselben an den µC

von Sonke A. (soeni)


Lesenswert?

hab ich doch oben geschrieben, wie der Anschluss ist und was ich für ein 
Display habe.

Hier nochmal die Enablefunktion. Diese muss funktionieren, da ich sie 
beim Initialisieren nutze und das funktioniert.
1
  // erzeugt den Enable-Puls
2
  void lcd_enable(void){
3
 
4
     LCD_PORT |= (1<<LCD_EN);
5
      _delay_us(1);                   // kurze Pause
6
 
7
     LCD_PORT &= ~(1<<LCD_EN);
8
  }

von Karl B. (gustav)


Lesenswert?

In Assembler ist's einfacher zu erklären.
Zunächst die Portdefinition. Welche D's am ATMega nimmst Du. Da hast Du 
Wahlfreiheit. Entweder Bits D0 bis D3 oder Bist D4 bis D7 für die vier 
aktivenb Datenleitungen. Dass ist pure Definitionssache und kann hernach 
leicht geändert werden.
Darauf bin ich am Anfang nämlich auch drauf reingefallen.

Das Display verlangt demhingegen strengste Konventionen.
Also zunächst Hig-Nibble dann Low nibble.
Den Grund werde ich später noch angeben.
Ist höchst interessant.

Da machst Du erst eine Portdefinition als Ausgang über das 
Datenrichtungsregister, dann lädst Du das Temorärregister.
Dann kopierst Du das Temnporärregister zu Sicherheit und
Verundest mit andi so, daß nur die oberen vier Bit einsen bekommen.
Oder in Hex F0
jetzt veroderst Du mit dem RS-Impuls der sitzt wo? Port sowieso Bit 
sowieso
Bei mir Port D und Bit D1 also ori Hex 0x02.

Dann kannst Du den RS Impuls einmal durch Registerladen erzeugen oder 
durch Direrktsetzung von Bits. Mit dem Sbi bzw Sbr Befehl
Und Rücksetzen mit cbi und cbr.

Achtung. Bei sbi/ cbi ist die Syntax etwas anders als beim sbr /cbr.

Und jetzt kommt ein Enableimpuls.

Dann der Befehl swap
Swap nibbles.
Nur Du nimmst jetzt das ins zweite Temprärregister kopierte 
TEMP-Argument.
hier vertauschst Du jetzt die Nibbles.
dann kommt wieder der andi temp1, 0xF0
und so weiter
Wichtig zwei Enable-Impulse, obwohl das Busy-Flag nur einmal abgefragt 
werden darf. Das Busy spinnt dann nämlich sonst.

von Karl B. (gustav)


Lesenswert?

Hier der Code mit Comments.
Das funktioniert sogar (RS-Toggeln ist nicht nötig, dient nur zur 
Kontrolle, ob die Don't cares auch wirklich ignoriert werden)

Datenuebernahme:

mov  temp1,  temp    ;Kopie des Wertes fuer weitere;
;        ;Operation unten
andi   temp,  0b11110000  ;unteres Nibble ausblenden
ori  temp,  0b00000010  ;RS auf "high" maskieren(Pinout PORTB)
out   daten,  temp    ;Ausgabe oberes Nibble auf D4-D7
ldi  temp,  0x02    ;RS auf "high" Pinout PORTD
out  impuls,  temp    ;Funktion wie "sbi impuls, 1"
rcall  enable
nop
nop
swap   temp1      ;Nibble-Vertauschen, Wert aus temp;
;        ;kopiert, s.o.
andi  temp1,  0b11110000  ;unteres Nibble ausblenden
ori  temp1,  0b00000010  ;RS auf "high" maskieren(Pinout PORTB)
out  daten,  temp1    ;Ausgabe unteres Nibble auf D4-D7
rcall  enable
nop
rcall  Verzoegerung
ldi  temp,  0x02  ;RS kurz auf "high" Pinout PORTD
out  impuls,  temp
ldi  temp,  0x00  ;RS auf "low" Pinout PORTD
out  impuls,  temp
ret

Kommando:

mov  temp1,  temp    ;Kopie des Wertes fuer weitere;
;        ;Operation unten
andi   temp,  0b11110000  ;unteres Nibble ausblenden
out   daten,  temp    ;Ausgabe oberes Nibble auf D4-D7
ldi  temp,  0x00    ;RS auf "low" Pinout PORTD
out  impuls,  temp    ;Funktion wie "cbi impuls, 1"
rcall  enable
nop
nop
swap   temp1      ;Nibble-Vertauschen, Wert aus temp;
;        ;kopiert, siehe oben
andi   temp1,  0b11110000  ;unteres Nibble ausblenden
out  daten,  temp1    ;Ausgabe unteres Nibble auf D4-D7
rcall  enable
rcall  Verzoegerung
ldi  temp,  0x02  ;RS kurz auf "high" Pinout PORTD
out  impuls,  temp
ldi  temp,  0x00  ;RS auf "low" Pinout PORTD
out  impuls,  temp
ret

Enable:

nop
sbi  impuls, 0  ;Enable auf "high" setzen
nop      ;Impulsdauer mindestens
nop      ;0,5 Mikrosekunden laut Datenblatt
nop      ;lieber ein paar "nops" mehr
nop      ;spendieren
nop
nop
nop
cbi  impuls, 0  ;Enable auf "low" zurücksetzen
nop
ret

Verzoegerung:  ;2+[4x(255-1)+3]x[5x(2-1)]+7=1,276 ms bei 4 MHz

ldi  zeit,  0xFF
ldi  zeit1,  0x05

Verzoegerungs_Schleife:

dec  zeit
cpi  zeit,  1
brlt  Verzoegerungs_Schleife
ldi  zeit,  0xFF
dec  zeit1
cpi  zeit1,  1
brlt  Verzoegerungs_Schleife
ret

Verzoegerung2:  ;2+[4x(255-1)+3]x[5x(32-1)]+7= 39,49 ms bei 4 MHz

ldi   zeit,  0xFF
ldi   zeit1,  0x20

Verzoegerungs_Schleife2:

dec  zeit
cpi  zeit,  1
brlt  Verzoegerungs_Schleife2
ldi  zeit,  0xFF
dec  zeit1
cpi  zeit1,  1
brlt  Verzoegerungs_Schleife2
ret

von Karl B. (gustav)


Lesenswert?

die Berechnung der Verz-Schleife statt 2 in der zweiten Klammer ne 5 ich 
hatte zwischenzeitlich noch was am Ladewert geändert und den Kommentar 
nicht upgedatet.

von Sonke A. (soeni)


Lesenswert?

soweit ich das überblicken kann funktioniert das doch auch bei meinem c 
code so oder?? kann mir da jemand helfen?? ich finde da keinen Fehler, 
nur funktionieren tuts auch nicht

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.