Forum: Mikrocontroller und Digitale Elektronik LCD mit interupts


von MySystem (Gast)


Lesenswert?

hallo,
ich weiß ich bin nicht der erste und vermutlich nicht der letzte der 
Probleme mit lcd's Displays hat.
momentan folge ich dem avr tut und bin bei dem Display angekommen ,geht 
auch soweit, nur störte mich an der lcd-routines das der µC immer 5ms 
warte und das sind bekannter maßen welten also habe ich versucht die 
pausen mit hilfe von timern und interupts zu realisieren.

Problem:
wen das Display mal an war funktioniert alles und ich kann problemloses 
die Anzeige ändern  in dem ich den Inhalt des rams ändere. Das Problem 
ist ich habe ist das das Display nur in 30% alles fälle richtig 
Initialisierung wird...

wobei ich noch zur Initialisierung die normalen Zeitschleifen des tut’s 
nutze weil wenn der µC ne halbe Sekunde braucht bis er läuft ist mir das 
noch egal(ich arbeit mit µC's seid dienstags).

hier mal der Code vom eigentlichem Programm:
1
.include "m8515def.inc"
2
3
4
.def temp1 = r16
5
.def temp2 = r17
6
.def temp3 = r18
7
8
//----------------Interupts------------------
9
Vector:
10
11
       rjmp Reset             ;Program Reset:1
12
       rjmp VECInt0           ;External IRQ 0:2
13
       rjmp VECInt1           ;External IRQ 1:3
14
       rjmp VECTmr1_Capt      ;Timer/Ctr 1 Capture Event:4
15
       rjmp VECTmr1_CompA     ;Timer/Ctr 1 Compare Match A:5
16
       rjmp VECTmr1_CompB     ;Timer/Ctr 1 Compare Match B:6
17
       rjmp VECTmr1_Ovf       ;Timer/Ctr 1 Overflow:7
18
       rjmp VECTmr0_Ovf       ;Timer/Ctr 0 Overflow:8
19
       rjmp VECSPI_STC        ;Serial Transfer Complete:9
20
       rjmp VECUART_RX        ;UART, RX Complete:10
21
       rjmp VECUART_UDRE      ;UART, Data Register Empty:11
22
       rjmp VECUART_TX        ;UART, TX Complete:12
23
       rjmp VECAna_Comp       ;Analog Comparator:13
24
  
25
Reset:
26
     rjmp Init
27
VECInt0:
28
   reti
29
VECInt1:
30
     reti
31
VECTmr1_Capt:
32
     reti
33
VECTmr1_CompA: 
34
   reti
35
VECTmr1_CompB:
36
     reti
37
VECTmr1_Ovf:
38
     reti
39
VECTmr0_Ovf:
40
     rjmp timer0_overflow
41
VECSPI_STC:
42
     reti
43
VECUART_RX:
44
     reti
45
VECUART_UDRE:
46
     reti
47
VECUART_TX:
48
     reti
49
VECAna_Comp:
50
     reti
51
52
53
54
55
Init:
56
//------------------Stack--------------------
57
             ldi temp1, LOW(RAMEND)      ; LOW-Byte der obersten RAM-Adresse
58
             out SPL, temp1
59
             ldi temp1, HIGH(RAMEND)     ; HIGH-Byte der obersten RAM-Adresse
60
             out SPH, temp1
61
62
63
             ldi     temp1,     0xFF            ; alle Pins am Ausgabeport auf Ausgang
64
             out     DDRC,     temp1
65
      ldi     temp1,     0xFF
66
      out    PORTC,    temp1
67
//-------------------RAM---------------------
68
      ldi    r30,  0x60
69
70
      ldi    temp1,   't'
71
      ST Z+,  temp1
72
      ldi    temp1,   'e'
73
      ST Z+,  temp1
74
      ldi    temp1,   's'
75
      ST Z+,  temp1
76
      ldi    temp1,   't'
77
      ST Z+,  temp1
78
      ldi    temp1,   ' '
79
      ST Z+,  temp1
80
      ldi    temp1,   't'
81
      ST Z+,  temp1
82
      ldi    temp1,   'e'
83
      ST Z+,  temp1
84
      ldi    temp1,   's'
85
      ST Z+,  temp1
86
      ldi    temp1,   't'
87
      ST Z+,  temp1
88
      
89
      ldi    temp1,   ' '
90
      ST Z+,  temp1
91
      ldi    temp1,   't'
92
      ST Z+,  temp1
93
      ldi    temp1,   'e'
94
      ST Z+,  temp1
95
      ldi    temp1,   's'
96
      ST Z+,  temp1
97
      ldi    temp1,   't'
98
      ST Z+,  temp1
99
      ldi    temp1,   ' '
100
      ST Z+,  temp1
101
      ldi    temp1,   't'
102
      ST Z+,  temp1
103
      ldi    temp1,   'e'
104
      ST Z+,  temp1
105
      ldi    temp1,   's'
106
      ST Z+,  temp1
107
      ldi    temp1,   't'
108
      ST Z+,  temp1
109
110
      ldi    temp1,   ' '
111
      ST Z+,  temp1
112
      ldi    temp1,   't'
113
      ST Z+,  temp1
114
      ldi    temp1,   'e'
115
      ST Z+,  temp1
116
      ldi    temp1,   's'
117
      ST Z+,  temp1
118
      ldi    temp1,   't'
119
      ST Z+,  temp1
120
      ldi    temp1,   ' '
121
      ST Z+,  temp1
122
      ldi    temp1,   't'
123
      ST Z+,  temp1
124
      ldi    temp1,   'e'
125
      ST Z+,  temp1
126
      ldi    temp1,   's'
127
      ST Z+,  temp1
128
      ldi    temp1,   't'
129
      ST Z+,  temp1
130
            
131
      ldi    temp1,   't'
132
      ST Z+,  temp1
133
      ldi    temp1,   'e'
134
      ST Z+,  temp1
135
      ldi    temp1,   's'
136
      ST Z+,  temp1
137
      ldi    temp1,   't'
138
      ST Z+,  temp1
139
      ldi    temp1,   ' '
140
      ST Z+,  temp1
141
      ldi    temp1,   't'
142
      ST Z+,  temp1
143
      ldi    temp1,   'e'
144
      ST Z+,  temp1
145
      ldi    temp1,   's'
146
      ST Z+,  temp1
147
      ldi    temp1,   't'
148
      ST Z+,  temp1
149
      ldi    temp1,   ' '
150
      ST Z+,  temp1
151
      ldi    temp1,   't'
152
      ST Z+,  temp1
153
154
      ldi   temp1,   0xFF  
155
      ST Z+,  temp1
156
      
157
158
//-----------------LCD INIT------------------
159
160
             rcall   lcd_init     ; Display initialisieren
161
             rcall    lcd_clear    ; Display löschen
162
      rcall    set_timer_30ms
163
      sei
164
165
//=================Main Programm==================
166
main:
167
168
             rjmp   main
169
170
171
172
173
//------------------Display-------------------
174
175
         
176
       
177
178
179
180
181
.include "LCD-Routinen.asm"


meine lcd-routines:
1
.equ LCD_PORT = PORTD
2
.equ LCD_DDR  = DDRD
3
.equ PIN_E    = 5
4
.equ PIN_RS   = 4
5
6
.equ disp_ram_start = 0x60
7
.equ z_pointer     = 0x88
8
9
10
 ; sendet einen Befehl an das LCD
11
 
12
lcd_command:                                ; wie lcd_data, nur RS=0
13
             mov   temp2,     temp1
14
             swap   temp1
15
             andi   temp1,     0b00001111
16
             out   LCD_PORT,   temp1
17
             rcall   lcd_enable
18
             andi   temp2,     0b00001111
19
             out   LCD_PORT,   temp2
20
             rcall   lcd_enable
21
             ret
22
 
23
 ; erzeugt den Enable-Puls
24
lcd_enable:
25
             sbi   LCD_PORT,   PIN_E            ; Enable high
26
             nop                              ; 3 Taktzyklen warten
27
      nop
28
      nop
29
      nop
30
      nop
31
             cbi   LCD_PORT,   PIN_E           ; Enable wieder low
32
             ret                              ; Und wieder zurück                     
33
 
34
 ; Pause nach jeder Übertragung
35
delay50us:                                  ; 50us Pause
36
             ldi    temp1,     $42
37
delay50us_:  dec    temp1
38
             brne   delay50us_
39
             ret                              ; wieder zurück
40
 
41
 ; Längere Pause für manche Befehle
42
delay5ms:                                   ; 5ms Pause
43
             ldi    temp1,     $21
44
WGLOOP0:     ldi    temp2,     $C9
45
      nop
46
      nop
47
WGLOOP1:     dec    temp2
48
             brne   WGLOOP1
49
      nop
50
      nop
51
             dec    temp1
52
             brne   WGLOOP0
53
             ret                              ; wieder zurück
54
55
; Sendet den Befehl zur Löschung des Displays
56
lcd_clear:
57
           ldi   temp1, 0b00000001      ; Display löschen
58
           rcall lcd_command
59
           rcall delay5ms
60
           ret
61
62
 ; Initialisierung: muss ganz am Anfang des Programms aufgerufen werden
63
lcd_init:
64
           ldi   temp1, 0xFF            ; alle Pins am Ausgabeport auf Ausgang
65
           out   LCD_DDR, temp1
66
 
67
           ldi   temp3,0x06
68
powerupwait:
69
           rcall delay5ms
70
           dec   temp3
71
           brne  powerupwait
72
           ldi   temp1,    0b00000011   ; muss 3mal hintereinander gesendet
73
           out   LCD_PORT, temp1        ; werden zur Initialisierung
74
           rcall lcd_enable             ; 1
75
           rcall delay5ms
76
           rcall lcd_enable             ; 2
77
           rcall delay5ms
78
           rcall lcd_enable             ; und 3!
79
           rcall delay5ms
80
           ldi   temp1, 0b00000010      ; 4bit-Modus einstellen
81
           out   LCD_PORT, temp1
82
           rcall lcd_enable
83
           rcall delay5ms
84
           ldi   temp1, 0b00101000      ; 4 Bot, 2 Zeilen
85
           rcall lcd_command
86
           ldi   temp1, 0b00001100      ; Display on, Cursor off
87
           rcall lcd_command
88
           ldi   temp1, 0b00000100      ; endlich fertig
89
           rcall lcd_command
90
           ret
91
             
92
93
//------------------Timer 30ms------------------
94
set_timer_30ms:
95
            ldi     temp1,     0b00000101      ; CS00 setzen: Teiler 1024
96
          out     TCCR0,     temp1
97
 
98
          ldi     temp1,     0x02          ; TOIE0: Interrupt bei Timer Overflow
99
          out     TIMSK,     temp1
100
 
101
       ldi    temp1,    0xFF      ;flag reset
102
      out    TIFR,    temp1
103
104
       ldi    temp1,     0x16        ; count start bei 1 (5ms = 211 = D3)
105
          out    TCNT0,     temp1      ; 10ms = 171 A6
106
107
      inc    r20
108
      out    PORTC,    r20
109
110
      ret
111
112
//------------------Timer 50us------------------
113
set_timer_50us:
114
      ldi     temp1,     0b00000010      ; CS00 setzen: Teiler 8
115
          out     TCCR0,     temp1
116
117
          ldi     temp1,     0x02          ; TOIE0: Interrupt bei Timer Overflow
118
          out     TIMSK,     temp1
119
120
      ldi    temp1,    0xFF      ;flag reset
121
      out    TIFR,    temp1 
122
123
      ldi    temp1,     0x00        ; count start bei 206 (50µ = 213 = 0xD5)
124
          out    TCNT0,     temp1      ; 30ms = 0x16
125
126
      ret
127
128
//----------------------Disp Home----------------------
129
disp_home:  
130
      ldi     temp1,     0b00000010  ; Display Home
131
      rcall  lcd_command
132
      
133
      ldi    temp1,    0xFE
134
      ldi    r30,     0x88    ;Z-pointer = 0x88
135
      ST Z,  temp1
136
      rjmp   disp_next
137
138
//--------------------Disp 50u Timer--------------------
139
disp_timer_set:
140
      rcall   set_timer_50us
141
142
      ldi    temp1,    0x60
143
      ldi    r30,     0x88    ;Z-pointer = 0x88
144
      ST Z,  temp1
145
146
      rjmp   disp_next
147
148
timer0_overflow:
149
      
150
      ldi    r30,    z_pointer
151
      LD    temp1, Z          ;0010 in temp1 laden
152
153
      cpi   temp1,     0xFF
154
      BREQ    disp_home
155
156
      cpi   temp1,     0xFE
157
      BREQ    disp_timer_set
158
159
160
161
//--------------------Schreiben-------------------------
162
disp_schreiben:
163
      ldi   r30,    0x88
164
      LD     r30,     Z
165
      LD     temp1,     Z+
166
      
167
        
168
             mov   temp2,     temp1           ; "Sicherungskopie" für
169
                                             ; die Übertragung des 2.Nibbles
170
             swap   temp1                     ; Vertauschen
171
             andi   temp1,     0b00001111      ; oberes Nibble auf Null setzen
172
             sbr   temp1,     1<<PIN_RS       ; entspricht 0b00010000
173
             out   LCD_PORT,   temp1           ; ausgeben
174
             rcall   lcd_enable                 ; Enable-Routine aufrufen
175
                                            ; 2. Nibble, kein swap da es schon
176
                                             ; an der richtigen stelle ist
177
             andi   temp2,     0b00001111      ; obere Hälfte auf Null setzen 
178
             sbr   temp2,     1<<PIN_RS       ; entspricht 0b00010000
179
             out   LCD_PORT,   temp2           ; ausgeben
180
             rcall   lcd_enable                 ; Enable-Routine aufrufen
181
      
182
      rcall   set_timer_50us
183
184
      mov    temp1,     r30        
185
      ldi   r30,    0x88
186
      ST Z,   temp1                  
187
          
188
      cpi     temp1,     0x88
189
      brne  disp_next
190
            
191
      ldi    r30,    0x88
192
      ldi    temp1,    0xFF
193
      ST Z,   temp1
194
      
195
      rcall   set_timer_30ms
196
                    
197
disp_next:
198
199
      reti

ich benutze ein AtMega8515 auf einem STK500 und einer takt Frequenz von 
1MHz, reiset delay = 64ms und einem Standart 2x20 Display.

ich hoffe das ich den Fehler nicht sehe weil er einfach zu 
offensichtlich ist.

mfg MySystem

von jack (Gast)


Lesenswert?

Au weia, so was sollte man eigentlich als Anhang posten.

Ich habe mir das Programm nicht durchgelesen, nur so viel dazu:

Schreib Deinen Text, den Du ausgeben willst ins RAM.
In einer Interruptroutine (alle 100µs) gibst Du dann jeweils ein Zeichen
an das LCD aus (das gilt nicht für die Initialisierung).

von holger (Gast)


Lesenswert?

>In einer Interruptroutine (alle 100µs)

Alle 1ms reicht völlig.

von Falk B. (falk)


Lesenswert?

@ MySystem (Gast)

>ich weiß ich bin nicht der erste und vermutlich nicht der letzte der
>Probleme mit lcd's Displays hat.

Und vor allem mit der Rechtschreibung.

http://www.apostroph.de/

Und mit der Netiquette.

von spess53 (Gast)


Lesenswert?

Hi

Als Allererstes solltest du folgendes machen: Im AVR-Studio unter 
Tools->Options->Editor folgendes einstellen:
   'Replace Tabs with Space' und Tabwidth=1.
Und wenn du es ganz gut machen willst schaltest du unter 'Edit' mit 
'Show Whitespace' die nicht sichtbaren Zeichen ein und schmeisst die 
Tabulatoren raus. Dann kann man deinen Quelltext in jedem Editor ohne 
Augenschmerzen betrachten. Und am besten, beim Programmieren die Finger 
von der Tabulatortaste lassen.

>Das Problem ist ich habe ist das das Display nur in 30% alles fälle >richtig 
Initialisierung wird...

Wenn dem so ist, warum schreibst du erst so ein Programm, um das zu 
merken.

So etwas macht man erst, wenn die grundlegenden Routinen sicher 
funktionieren.

Interruptroutinen so lang wie nötig, aber so kurz wie möglich machen.
Deine Aussgaberoutine gehört in die Main. In der IR nur ein Flag setzen
das in der Main abgefragt wird. Wenn gesetzt Routine ausführen,Flag 
zurücksetzen und weiter warten.

....

MfG Spess

von Thomas P. (topla)


Lesenswert?

MySystem wrote:

> nur störte mich an der lcd-routines das der µC immer 5ms
> warte und das sind bekannter maßen welten also habe ich versucht die
> pausen mit hilfe von timern und interupts zu realisieren.

Ich habe mir das mit den Zeitschleifen gleich am Anfang wieder abgewöhnt 
und frage bis auf die Initialisierung immer das Busy-Bit ab; ist auch 
nicht schwer und die schnellstmögliche Ausgabe auf das LCD ist so auch 
gewährleistet.

Gruß
Thomas

von spess53 (Gast)


Lesenswert?

Hi

>Ich habe mir das mit den Zeitschleifen gleich am Anfang wieder abgewöhnt
>und frage bis auf die Initialisierung immer das Busy-Bit ab;

Du sprichst mir aus der Seele. Aber es gibt noch eine starke Fraktion, 
die noch nicht gemerkt haben, das die Zeit der HD44780 100%-kompatiblen 
Displays vorbei sind. Trotzdem wird Anfängern immer noch 
Minimalvarianten angeraten. Leider noch kein Fall für den Staatsanwalt.

MfG Spess

von Michael A. (micha54)


Lesenswert?

Hallo,

ich spendiere 80 Bytes als Puffer und aktualisiere zyklisch im 5ms 
Timer-Interrupt.
Die Ausgabe selbst ist ja nur 1-2us.
Einen Reset im laufenden Betrieb brauch ich nicht, bzw. lasse ich den 
mit Warteschleifen in der init-Function ablaufen.

Gruss,
Michael

von spess53 (Gast)


Lesenswert?

Hi

>lasse ich den mit Warteschleifen in der init-Function ablaufen.

Und wann willst du ein richtiges Programm schreiben?

MfG Spess

von Peter D. (peda)


Lesenswert?

spess53 wrote:
> Du sprichst mir aus der Seele. Aber es gibt noch eine starke Fraktion,
> die noch nicht gemerkt haben, das die Zeit der HD44780 100%-kompatiblen
> Displays vorbei sind. Trotzdem wird Anfängern immer noch
> Minimalvarianten angeraten. Leider noch kein Fall für den Staatsanwalt.

Interessant, mit welcher Verbissenheit manche nur ihre eigene Lösung als 
die einzig Wahre akzeptieren und alle anderen ohne Begründung als 
grundsätzlich schlecht abkanzeln.


Ich benutze die Minimallösung und habe keinerlei Probleme damit.
Die CPU-Auslastung ist trotz Wartezeitmethode weit unter 1%, also wozu 
unnütz nen Portpin verschwenden?

Manchmal benutze ich auch die Interruptmethode (ein Zeichen je 1ms 
ausgeben), da wäre das Busybit eh völlig unnütz.
Diese Methode ist vorteilhaft, wenn man viel Ausgaben in 
unterschiedlichen Bereichen des LCD anzeigen will. Man kann gleich 
direkt in den Bildspeicher im SRAM schreiben, ohne erst umständlich 
positionieren zu müssen.

Ich habe früher auch LCDs mit 3 Pins und 74HC164 im 8Bit-Modus 
angesteuert. Jetzt benutze ich aber ausschließlich die Ansteuerung im 
4Bit-Modus mit 6 Pins.

In der Initialisierung benutze ich selbstverständlich die Delay-Macros, 
da ja nach dem Poweron nicht sofort zeitkritische Tasks auszuführen 
sind. Das Delay stört also in keinster Weise.

Ich benutze auch nicht die Clear-Funktion, sondern überschreibe immer 
den alten Text. Die Clear-Funktion hat neben der langen Wartezeit noch 
den Nachteil, daß ein deutliches Flackern zu sehen ist.


Peter

von juppi (Gast)


Lesenswert?

>Und vor allem mit der Rechtschreibung.

Das war nicht erforderlich

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.