Forum: Mikrocontroller und Digitale Elektronik Counter mit LCD Ausgabe


von Bernd S. (kurtel)


Lesenswert?

Ich krieg das mit den Zähler nicht. Ich hab mir dieses Programm 
zusammen-gestellt.  Ich wollte erstmal zum lernen, das der Timer 
hochzählt und das auf der LCD zu sehen ist. Die Impulse kommen v. einen 
Durchfluss-mengenmesser (2500/1Liter)der an PORTD3 angeschlossen ist.Das 
Programm hab ich mit den CodeWizzard erstellt. Leider kommt auf d. LCD 
nicht mal ne 1.
Bin für jeden (ausführlichen) Tipp oder Codeschnipsel dankbar.


;----------------------------------------------------------------------- 
--
; Titel : test timer 1
;----------------------------------------------------------------------- 
--
; Funktion :
; Schaltung :
;----------------------------------------------------------------------- 
--
; Prozessor : ATmega8
; Takt : 3686400 Hz
; Sprache : Assembler (GNU)
; Datum : 15.1.2009
; Version : 1.0
; Autor :
; Programmer:
; Port :
;----------------------------------------------------------------------- 
--
; created by myAVR-CodeWizard
;----------------------------------------------------------------------- 
--
;
.include "avr.h"

begin:   rjmp   main   ; 1 POWER ON RESET
   reti      ; 2 Int0-Interrupt
   reti      ; 3 Int1-Interrupt
   reti      ; 4 TC2 Compare Match
   reti      ; 5 TC2 Overflow
   reti      ; 6 TC1 Capture
   rjmp   onTimer1Cmp   ; 7 Timer1 Interrupt bei Vergleichswert
   reti      ; 8 TC1 Compare Match B
   reti      ; 9 TC1 Overflow
   reti      ;10 TC0 Overflow
   reti      ;11 SPI, STC Serial Transfer Complete
   reti      ;12 UART Rx Complete
   reti      ;13 UART Data Register Empty
   reti      ;14 UART Tx Complete
   reti      ;15 ADC Conversion Complete
   reti      ;16 EEPROM Ready
   reti      ;17 Analog Comparator
   reti      ;18 TWI (I²C) Serial Interface
   reti      ;19 Store Program Memory Ready

;----------------------------------------------------------------------- 
-
; Initialisierungen
;----------------------------------------------------------------------- 
-
main:
   ;--- Stack Initialisierung ---
   ldi r16,hi8(RAMEND)
   out SPH,r16
   ldi r16,lo8(RAMEND)
   out SPL,r16
   ; Ports initialisieren
   cbi   DDRD,3   ; PORTD3 auf Eingang mit PullUp
   sbi   PORTD,3
   ;--- Timer 1 initialisieren ---
   ldi   r16,0b00000111   ; Teiler 1/exR
   ori   r16,0b00001000   ; Modus: Zählen bis Vergleichswert (WGM12=1)
   out   TCCR1B,r16   ; Teiler+Modus schreiben
   ldi   r16,lo8(1000)   ; Vergleichswert speichern
   ldi   r17,hi8(1000)
   out   OCR1AH,r17
   out   OCR1AL,r16
   in   r16,TIMSK   ; Interrupt bei Vergleichswert
   ori   r16,0b00010000
   out   TIMSK,r16
   ;--- Interrupts erlauben ---
   sei
;----------------------------------------------------------------------- 
-
; Hauptprogramm-Schleife
;----------------------------------------------------------------------- 
-
mainloop:   wdr
   ; ...
   rjmp   mainloop   ; Sprung zum Beginn der Hauptprogrammschleife
;--------------------------------------------------------------------
; onTimer1Cmp - Timer1 Interrupt bei Vergleichswert
; Zyklus unbestimmt
; PE: ...
; PA: ...
;--------------------------------------------------------------------
onTimer1Cmp:
  ; WAS MUSS HIER REIN ????????????????
   ; Hier Interruptbehandlung
   reti      ;Rücksprung
;--------------------------------------------------------------------
; myWait_ms - Warte-Routine für x-Millisekunden
; ein Millisekundenzyklus dauert 1,052 ms
; PE: r16 = Anzahl der zu wartenden Milisekunden
; PA: r16 = 0
;--------------------------------------------------------------------
myWait_ms:
   push   r16
   ldi   r16,1
myWait_ms_3:
   push   r16
   ldi   r16,5
myWait_ms_2:
   push   r16
   ldi   r16,255
myWait_ms_1:
   dec   r16
   brne   myWait_ms_1
   pop   r16
   dec   r16
   brne   myWait_ms_2
   pop   r16
   dec   r16
   brne   myWait_ms_3
   pop   r16
   dec   r16
   brne   myWait_ms
   ret

;----------------------------------------------------------------------- 
------
lcdOut:   mov    r17, r16
   swap   r17
   andi   r16, 0b11110000
   or     r16,r18
   andi   r17, 0b11110000
   or     r17,r18
   out    PORTD, r16
   rcall   lcdEnable
   out    PORTD, r17
   rcall   lcdEnable
   rcall   lcdWait
   ret
;----------------------------------------------------------------------- 
-
;----------------------------------------------------------------------- 
------
lcdData:   ldi    r18,0b0000100     ; RS = hi
   rjmp   lcdOut
   ret
;----------------------------------------------------------------------- 
-
;--------------------------------------------------------------------
; Warte-Routine für 5 ms
; die Routine wartet inclusive Aufruf 5,031 ms
;--------------------------------------------------------------------
lcdWait:
    ldi    r16,5
   rcall   myWait_ms
    ret
;----------------------------------------------------------------------- 
-
;----------------------------------------------------------------------- 
------
lcdCmd:   ldi    r18,0b0000000     ; RS = lo
   rjmp   lcdOut
   ret
;----------------------------------------------------------------------- 
-
;----------------------------------------------------------------------- 
------
lcdOn:    ldi    r16, 0x0E
   rcall   lcdCmd
   rcall   lcdWait
   ret
;----------------------------------------------------------------------- 
-
;----------------------------------------------------------------------- 
------
lcdClear:  ldi    r16, 0b00000001     ; Display löschen
   rcall   lcdCmd
   rcall   lcdWait
   ret
;----------------------------------------------------------------------- 
-
;----------------------------------------------------------------------- 
------
lcdOff:   ldi    r16, 0b00001000
   rcall   lcdCmd
   rcall   lcdWait
   ret
;----------------------------------------------------------------------- 
-
;----------------------------------------------------------------------- 
------
lcdEnable: sbi    PORTD, 3     ; Enable high
   nop    ; kurz warten
   nop
   nop
   cbi    PORTD, 3     ; Enable wieder low
   ret
;----------------------------------------------------------------------- 
-
;----------------------------------------------------------------------- 
------
lcdInit:      sbi   DDRD,2              ; LCD RS = OUT
      sbi   DDRD,3              ; LCD E  = OUT
      sbi   DDRD,4            ; LCD D4 = OUT
      sbi   DDRD,5            ; LCD D5 = OUT
      sbi   DDRD,6             ; LCD D6 = OUT
      sbi   DDRD,7             ; LCD D7 = OUT
      cbi   PORTD,2              ; LDC RS = Low

      ; warte bis PowerUp
      ldi   r18,20
powerUp:   rcall   lcdWait
      dec   r18
      brne   powerUp              ; Power-Up Wartezyklus min 30 ms

      ; sende Resetsequenz kompatibel zu HD44780 Industriestandard
      ldi   r16,0b00110000         ; Reset-Sequenz Teil 1
      out   PORTD,r16
      rcall   lcdEnable         ; Enable-Impuls
      rcall   lcdWait
      ldi   r16,0b00110000         ; Reset-Sequenz Teil 2
      out   PORTD,r16
      rcall   lcdEnable         ; Enable-Impuls
      rcall   lcdWait
      ldi   r18,100            ; Wartezyklus bei RESET LCD min 100 µs
resetLCD:
      nop
      nop
      nop
      dec   r18
      brne   resetLCD
      ldi   r16,0b00110000         ; Reset-Sequenz Teil 3
      out   PORTD,r16
      rcall   lcdEnable         ; Enable-Impuls
      rcall   lcdWait

                ; sende init 1
      ldi   r16, 0b00100000           ; 4 Bit Modus aktivieren
      out   PORTD, r16
      rcall   lcdEnable          ; Enable-Impuls
      rcall   lcdWait
      ldi   r16, 0b00101000
      rcall   lcdCmd               ; Function Set 4 Bit, 2 Zeilen, 5x7
      rcall   lcdOff
      rcall   lcdClear
      ldi   r16, 0x06
      rcall   lcdCmd            ; Entry Mode Set, increase+shifted
      rcall   lcdOn
      ret
;----------------------------------------------------------------------- 
-
;----------------------------------------------------------------------- 
-
; Sendet einen mit 0x00 abgeschlossenen String von Flash an UART
; PE: Z=StartAdresse
;----------------------------------------------------------------------- 
-
lcdString:
   push   r16
   push   r30
   push   r31
lcdString1:
   lpm   r16,Z+
   cpi   r16,0
   breq   lcdString2
   rcall   lcdData
   rjmp   lcdString1
lcdString2:
   pop   r31
   pop   r30
   pop   r16
   ret
;----------------------------------------------------------------------- 
-
; wandelt ein Byte in die in die einzelnen Ziffern um (unsigned)
; PE:
;     r16   binärwert (0...255)
; PA:         FF   99   1
;     r1   Ergebnis (einer)   5   9   1
;     r2   Ergebnis (zehner)   5   9   0
;     r3   Ergebnis (hunderter)   2   0   0
;----------------------------------------------------------------------- 
-
lcdZahl:
   mov   r1,r16   ; Zwischenspeicher und Einer-Stelle
   clr   r3
   clr   r2
hundert:
   ldi   r16,100   ; solange die 100 in die Restzahl (r1) passt
   cp   r1,r16
   brlo   zehner
   sub   r1,r16   ; 100 abziehen und
   inc   r3   ; Hunderter-Stelle erhöhen
   rjmp   hundert
zehner:
   ldi   r16,10   ; wie bei den Hundertern
   cp   r1,r16
   brlo   einer
   sub   r1,r16
   inc   r2
   rjmp   zehner
einer:   ;   der_Rest_sind_die_Einer
   ldi   r21,0x30   ; 30 hex für Zahl
   mov   r16,r3
   add   r16,r21   ; hunderter + 30 hex
   rcall   lcdData
   mov   r16,r2
   add   r16,r21   ; zehner + 30 hex
   rcall   lcdData
   mov   r16,r1
   add   r16,r21   ; einer + 30 he
   rcall   lcdData
   ret
;----------------------------------------------------------------------- 
-

von Sebastian (Gast)


Lesenswert?

Mir ist ja nicht ganz klar, wie der Timer benutzt werden soll (Meine 
Vermutung: In der Interruptroutine müssen die Zählregister für den 
Durchflußwert erhöht werden), aber ich würde trotzdem empfehlen, zuerst 
eine manuelle Ausgabe auf das LCD einzubauen, um zu sehen, ob es richtig 
initialisiert wird. Das ist immer ein kritischer Punkt beim HD44780, und 
noch mehr bei seinen diversen "kompatiblen" Nachbauten.

von Bernd S. (kurtel)


Lesenswert?

Wie meinst du das. Mit einen Taster hochzählen? Der Timer soll 
eigentlich die Impulse v. Mengenmesser zählen u. auf LCD anzeigen.Die 
LCD Ausgabe an sich funktioniert. Das habe mich mir de "Hallo" 
ausprobiert. Ich hab ehrlich gesagt so gut wie keine Ahnung v.d. 
Programmiererei. Bis jetzt hab ich nur eine Temperaturmessung mit 
Auswertung hingegreigt. Nun wollt ich eben noch den Verbrauch messen u. 
anzeigen. Find auch nirgens ein passendes Programm
dazu.

von Sebastian (Gast)


Lesenswert?

Sehe ich das richtig, daß das Hauptprogramm nichts macht, außer den 
Watchdog zurücksetzen? Und dir Interrupr-Routine auch nicht?

Also, ein Ansatz von mir, in der Hoffnung, daß ich richtig rate: Der 
Counter ist so konfiguriert, daß Impulse am externen Zähleingang ihn 
hochzählen. Habe ich jetzt aber nicht nach Datenblatt kontrolliert. Wenn 
ein bestimmter Wert erreicht wird, wird der Interrupt ausgelöst. Nach 
tausend Impulsen, wenn ich das richtig sehe.
Nun aber muß in der Interruptroutine (ISR) "onTimer1Cmp" aber auch etwas 
passieren: Die niederwertigste Zählvariable muß erhöht werden, auf 
Überlauf geprüft und ggf. der Übertrag in die nächsthöhere Zählvariable 
für die Impulse ausgeführt. Dann muß eine Ausgabe auf dem Display 
erfolgen; dies macht man am Besten in der Hauptprogrammschleife (main 
loop), da zeitaufwendig. Schließlich muß der Zählerstand auch ausgegeben 
werden.

von Peter D. (peda)


Lesenswert?

Bernd S. wrote:
> Durchfluss-mengenmesser (2500/1Liter)der an PORTD3 angeschlossen ist.Das
> Programm hab ich mit den CodeWizzard erstellt.

Was fürn Wizzard?


> Leider kommt auf d. LCD
> nicht mal ne 1.

Kein Wunder, Du initialisierst nirgends das LCD und gibst auch nichts 
aus.
Es reicht nicht, Routinen irgendwo in den Flash zu knallen, man muß sie 
auch aufrufen.
Das LCD verhält sich also völlig korrekt und das Programmm auch.

Und nächstes Mal Quelltext als Anhang (*.asm).


Peter

von Bernd S. (kurtel)


Lesenswert?

Zum Wizzard-der ist beim Programm "myworkpadplus" v. MyAVR drin.
____________________________________________________________________
lcdInit:      sbi   DDRD,2              ; LCD RS = OUT
      sbi   DDRD,3              ; LCD E  = OUT
      sbi   DDRD,4            ; LCD D4 = OUT
      sbi   DDRD,5            ; LCD D5 = OUT
      sbi   DDRD,6             ; LCD D6 = OUT
      sbi   DDRD,7             ; LCD D7 = OUT
      cbi   PORTD,2              ; LDC RS = Low

Ist das denn nicht d. Initialisierung fürs LCD?

Es ist so. Ich hab mir das TWI-Projekt bei "MYAVR" gekauft 
(Hardware,Software usw). Der Beschreibung nach soll ja alles ganz 
einfach sein. Hab das Programm aus dem Lehrheft übernommen u. nach 
mehreren Startschwierigkeiten läuft das jetzt auch.Die Temp. wird in 
einstellbaren Abständen gemessen u. gespeichert. Ich lese dann die Daten 
aus und stell sie in einen Diagramm dar. Das ganze sollte zur Auswertung 
d. Heizung sein.
Nun nützt mir das aber alles nichts, wenn ich den Ölverbrauch nicht 
messen kann. Deshalb habe ich einen Mengenmesser beschafft, d. den 
Verbrauch anzeigen soll. Nur so kann ich Änderungen, z.B. durch 
elektronische Thermostate erkennen und auswerten.  Da ich wirklich keine 
Ahnung v. programmieren habe,hänge ich eben an der Mengenmessung. Hab 
nun überall nach einen Programm gesucht, bin aber nicht fündig geworden. 
Das Programm oben ist so ein kläglicher Vesuch. Ich habe auch nicht vor 
ein grosser Programmierer zu werden. Möchte eben nur dieses Projekt zu 
Ende bringen.

Wenn mir jemand so ein Programm erstellen könnte, würde ich auch gern 
dafür bezahlen. Das ist keine Faulheit von mir, nur Ahnungslosigkeit.

von Peter D. (peda)


Lesenswert?

Bernd S. wrote:

Bernd S. wrote:
> lcdInit:      sbi   DDRD,2              ; LCD RS = OUT
...
> Ist das denn nicht d. Initialisierung fürs LCD?

Schon, aber es wird nirgends aufgerufen, also ist es toter Code.

Dein PC führt ja auch nicht einfach sämtliche Programme aus, die auf 
Deiner Festplatte stehen.


> Hab
> nun überall nach einen Programm gesucht, bin aber nicht fündig geworden.

Programme sucht man nicht, man schreibt sie.

> Ich habe auch nicht vor
> ein grosser Programmierer zu werden. Möchte eben nur dieses Projekt zu
> Ende bringen.

Dann sag ich mal, Deine Chancen stehen schlecht.
Etwas Hilfe gibt man gerne mal, aber ein Programm für nen Ahnungslosen 
komplett zu schreiben, das ist richtig harte Arbeit. D.h. sowas wird 
richtig teuer.


Peter

von Karl H. (kbuchegg)


Lesenswert?

> Ich habe auch nicht vor
> ein grosser Programmierer zu werden. Möchte eben nur dieses Projekt zu
> Ende bringen.

Das Problem ist nicht ein grosser Programmierer zu werden.
Das Problem, so wie ich das sehe ist, das dir so ziemlich alle 
Grundlagen fehlen. Ein Wizard ist nun mal kein Ersatz für fehlendes 
Wissen.

Hast du einen C-Compiler für dein System?
Damit könnten wir uns dann erst mal von ein paar Details lösen, die man 
in der Assemblerprogrammierung können muss. Und die laufende Wartung für 
dich würde auch einfacher werden.


Frage an die Community:
Diese Durchflussmesser, wie funktionieren die?
Ich stell mir das so vor: da ist ein Schaufelrad drinnen und bei jeder 
Umdrehung wird ein mech. Kontakt geschlossen. Sprich: Mit Prellen ist zu 
rechnen.

von Sebastian (Gast)


Lesenswert?

> Diese Durchflussmesser, wie funktionieren die?
> Ich stell mir das so vor: da ist ein Schaufelrad drinnen und bei jeder
> Umdrehung wird ein mech. Kontakt geschlossen. Sprich: Mit Prellen ist zu
> rechnen.

Das ist typenabhängig. Es gibt auch optische Durchflußmesser mit 
integrierter Auswertelektronik, deren Signal ist im Allgemeinen 
prellfrei. Ein elektronikloser Durchflußmesser kann auch induktiv 
arbeiten und Spannungsimpulse liefern, wenn ein Magnet an einer Spule 
vorbeiläuft.

von Bernd S. (kurtel)


Angehängte Dateien:

Lesenswert?

Habe mich jetzt nochmal mit den Zähler versucht. Bei jeden Tastendruck
soll der Zähler 1 hochzählen und auf Lcd ausgeben. Leider kommt bei 
jeden
Tastendruck eine andere Ausgabe:
001
00101
00111
00101 usw.

Vielleicht hat einer mal Zeit und könnte den Code anschauen.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Bernd S. wrote:
> Habe mich jetzt nochmal mit den Zähler versucht. Bei jeden Tastendruck
> soll der Zähler 1 hochzählen und auf Lcd ausgeben. Leider kommt bei
> jeden Tastendruck eine andere Ausgabe:

Das würde ich erwarten, sonst wäre es ja kein Zähler.

> 001
> 00101
> 00111
> 00101 usw.

Sieht nach Zahlen im Dualsystem aus:
http://de.wikipedia.org/wiki/Dualsystem

Hast du die Zahlen richtig abgeschrieben, dann ist ein Problem im 
Programm. Wenn du allerdings nur irgendwelche 01 abgeschrieben hast, ist 
wahrscheinlich deine Frage erledigt.

von Bernd S. (kurtel)


Lesenswert?

Hab jetzt 3x "inc r16 " eingefügt. Da kommt in der Anzeige das gleiche
nur mit der Zahl 3

003
0033
00303
00333

Da nehm ich mal an das es Dezimalzahlen sind.

von Karl H. (kbuchegg)


Lesenswert?

3 Beobachtungen

* da deine Ausgabe eigentlich nur 3 Stellen umfassen sollte, dürfte 
irgendwas mit deine LcdLine1 Funktion nicht stimmen

* Wenn du keine weiteren Vorkehrungen triffst, ist ein externer 
Interrupt als Zähleingang für einen Taster denkbar ungeeignet. Tasten 
prellen: Du drückst einmal auf die Taste und die Taste generiert mehrere 
Pulse hintereinander.
Ich hab jetzt nicht genau analysiert, wie du den Interrupt 
freischaltest. Aber nur als Denkanstoss: Was denkst du, wie schnell wird 
wohl deine Interrupt Routine abgearbeitet und wie schnell kannst du die 
Taste wieder loslassen?

* Wenn du in der Interrupt Routine deinen 'Zähler' im R16 immer wieder 
auf 0 setzt, dann wird das mit dem Zählen wohl nicht so prickelnd 
funktionieren. Eigentlich sollte auf deinem Display ständig 001 stehen.

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.