Stoppuhr

Ernst

Mitglied
04. Okt. 2009
61
9
8
nähe Regensburg
Sprachen
Ich grüße euch alle hier.
Hier möchte ich mein erstes Projekt mit dem Atmeg 8 vorstellen.
Eine Stoppuhr selber bauen ist zwar teuerer als eine für ein paar
Euro zu kaufen aber mir ging es darum sie selbst zu bauen. Die
Digitalanzeige und die Zusatzbeschaltung verwende ich als
Grundlage für etwaige andere Verwendungen wie Drehzahlmesser
oder so. Die blauen Drahtbrücken auf der Platine sehen natürlich
nicht so elegant aus eine Doppelseitige Platine wäre da besser gewesen
aber ich habe damit noch keine Erfahrung in der Herstellung damit.
Wenn sich jemand für die Schaltung, Programm oder andere Details
interessiert dann sende ich sie ihm gerne.
Gruß Ernst
P1110976.jpgP1110977.jpg
 
Hi Ernst,

Die blauen Drahtbrücken auf der Platine sehen natürlich
nicht so elegant aus eine Doppelseitige Platine wäre da besser gewesen
aber ich habe damit noch keine Erfahrung in der Herstellung damit.
ich würde mal sagen ... das Ergebnis zählt. Sieht doch nicht schlecht aus.
Bei 2x14Pin (PDIP28 Gehäuse) tippe ich mal auf Mega8,48,88,168,328. Die sind
pinkompatibel und einer von denen wird das wohl sein ;)

Wenn sich jemand für die Schaltung, Programm oder andere Details
interessiert dann sende ich sie ihm gerne.
Immer her damit ;) Interessant ist es immer wie andere ihre Aufgaben lösen.
Ich schau mir ganz gerne mal fremde Lösungen an um zu sehen wie ich es evtl
noch besser lösen könnte und was die anderen so für Ideen haben. Man lernt
ja nie aus ;)

Gruß
Dino
 
Grüß dich Dino
Zuerst einen Dank für dein Schreiben. Es handelt sich um einen Mega 8.
Mit anderen habe ich auch noch keine Erfahrung aber ich bin mit Ihm
voll zufrieden. Hier das Programm dafür. Sicher ist es nicht das gelbe
vom Ei aber es funktioniert. Als ich die Platine bzw. die Stoppuhr fertig
hatte da stellte ich fest dass ich die Anzeigenstellen von Rechts nach Links
vertauscht hatte. Ich meinte damit die Ansteuerung von den
6 Treibertransistoren die vom IC 74LS138 Pin 10 bis 15 sind vertauscht.
Die Zehntelsekunden befanden sich so auf der linken Anzeige und die
Stunden auf der Rechten Anzeige. Um nicht alles noch einmal Neu
zumachen habe ich das Programm entsprechend umgeschrieben.
Hier ist noch das Programm für meine Stoppuhr.
Gruß Ernst
Code:
;+----------------------------------------------------------------------
;| Title		: Stoppuhr für ATmega8
;+----------------------------------------------------------------------
;| Funktion		: ... Zeigt zehntel Sekunden, Sekunden , Minuten und Stunden an
;| Schaltung	: ... Taster an Port CO Start
;|           	: ... Taster an Port C1 Stop
;|           	: ... Anmerkung Programm geändert da auf Platine
;|           	: ... die Anzeigen 1 bis 6 von rechts nach links vertauscht sind
;+----------------------------------------------------------------------
;| Prozessor	: ATmega8
;| Takt		: 3,6864 MHz
;| Sprache       	: Assembler
;| Datum         	: ...
;| Version       	: ...
;| Autor         	: Enst
;+----------------------------------------------------------------------
.include	"AVR.H"
;------------------------------------------------------------------------
;Reset and Interrupt vector             ;VNr.  Beschreibung
	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
	reti		;7   TC1 Compare Match A
	reti		;8   TC1 Compare Match B
	reti		;9   TC1 Overflow
	rjmp	tim0_ovf	;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
;------------------------------------------------------------------------
main:
	ldi	r16,hi8(RAMEND)
	out	SPH,r16
	ldi	r16,lo8(RAMEND)
	out	SPL,r16
	ldi	r16,0b01111000
	out	DDRD,r16
	ldi	r16,0b00011111
	out	DDRB,r16
	ldi	r16,0b00000011
	out	DDRC,r16
	ldi	r16,0b00000011
	out	PORTC,r16
	ldi	r16,0b00000011
	out	TCCR0,r16
	ldi	r16,0b00000001
	out	TIMSK,r16
	ldi	r18,0x53
	out	TCNT0,r18
	ldi	r30,0x60
	ldi	r31,0x00
	ldi	r19,0x05
	sts	0x66,r19
	sei
;------------------------------------------------------------------------
mainloop:	ldi	r16,0x00
	sts	0x65,r16
	ldi	r17,0x10
	sts	0x64,r17
	ldi	r18,0x00
	sts	0x63,r18
	ldi	r19,0x10
	sts	0x62,r19
	ldi	r20,0x00
	sts	0x61,r20
	ldi	r21,0x10
	sts	0x60,r21
	sbic	PINC,0	; start Uhr
	rjmp	pause
	rjmp	tim0_ovf
;------------------------------------------------------------------------
stelle0:	;	Anzeige1(links)
	sbis	PINC,1	; stoppt Uhr
	rjmp	pause
	sts	0x65,r16
	rcall	zeit
	inc	r16
	sts	0x65,r16
	cpi	r16,0x0a
	brne	stelle0
	rcall	stelle1
	cpi	r17,0x1a
	brne	stelle0
	rcall	stelle2
	cpi	r18,0x06
	brne	stelle0
	rcall	stelle3
	cpi	r19,0x1a
	brne	stelle0
	rcall	stelle4
	cpi	r20,0x06
	brne	stelle0
	rcall	stelle5
	cpi	r21,0x1a
	brne	stelle0
	rjmp	mainloop
;--------------------------------------------------
pause:	ldi	r16,0b00000111
	out	DDRC,r16
	out	PORTC,r16
	sbic	PINC,0
	rjmp	pause	; startet Zählvorgang erneut
	ldi	r16,0b00000011
	out	DDRC,r16
	out	PORTC,r16
	rjmp	stelle0
;------------------------------------------------------------------------
stelle1:	ldi	r16,0x00	; Anzeige 2  (links)
	sts	0x65,r16
	inc	r17
	sts	0x64,r17
	ret
;------------------------------------------------------------------------
stelle2:	ldi	r16,0x00	;Anzeige 3
	sts	0x65,r16
	ldi	r17,0x10
	sts	0x64,r17
	inc	r18
	sts	0x63,r18
	ret
;------------------------------------------------------------------------
stelle3:	ldi	r16,0x00	;Anzeige 4
	sts	0x65,r16
	ldi	r17,0x10
	sts	0x64,r17
	ldi	r18,0x00
	sts	0x63,r18
	inc	r19
	sts	0x62,r19
	ret
;------------------------------------------------------------------------
stelle4:	ldi	r16,0x0	;Anzeige 5
	sts	0x65,r16
	ldi	r17,0x10
	sts	0x64,r17
	ldi	r18,0x00
	sts	0x63,r18
	ldi	r19,0x10
	sts	0x62,r19
	inc	r20
	sts	0x61,r20
	ret
;------------------------------------------------------------------------
stelle5:	ldi	r16,0x00
	sts	0x65,r1	;Anzeige 6  (rechts)
	ldi	r17,0x10
	sts	0x64,r17
	ldi	r18,0x00
	sts	0x63,r18
	ldi	r19,0x10
	sts	0x62,r19
	ldi	r20,0x00
	sts	0x61,r20
	inc	r21
	sts	0x60,r21
	ret
;------------------------------------------------------------------------
tim0_ovf:
	push	r16
	push	r17
	push	r18
	push	r19
	in	r16,SREG
	ld	r18,z+
	lds	r19,0x66
	swap	r19
	out	PORTB,r18
	out	PORTD,r19
	swap	r19
	dec	r19
	sts	0x66,r19
	cpi	r19,0xff
	brne	tim0_ovf_1
	ldi	r30,0x60
	ldi	r31,0x00
	ldi	r19,0x05
	sts	0x66,r19
;------------------------------------------------------------------------
tim0_ovf_1:
	ldi	r18,0x53
	out	TCNT0,r18
	out	SREG,r16
	pop	r19
	pop	r18
	pop	r17
	pop	r16
	reti
	rjmp	mainloop
;--------------------------------------------------
zeit:
	push	r16
	push	r17
	push	r18
	in	r16,SREG
	push	r16
	ldi	r16,0x04	;15   ändert Geschwindigkeit der Uhr
zeit1:	ldi	r17,0xdc	;c2   ändert Geschwindigkeit der Uhr
zeit2:	ldi	r18,0x8a	;1d   ändert Geschwindigkeit der Uhr
zeit3:	dec	r18
	brne	zeit3
	dec	r17
	brne	zeit2
	dec	r16
	brne	zeit1
	pop	r16
	out	SREG,r16
	pop	r18
	pop	r17
	pop	r16
	ret
 
Hi Ernst,

ich hab mich mal erdreistet dein Programm in Code-Tags zu setzen. Dann kommen die Einrückungen auch besser raus.

Alles schön in Assembler ... fein, fein, fein ... :cool:

Assembler ist was feines. Wenn es denn bei der höheren Mathematik nicht so aufwendig wär :rolleyes:

Gruß
Dino
 
Hi
Endlich mal wieder jemand, der in Assembler spricht. Habe mal kurz übers Progamm geschaut. Bist du sicher, das du in 2 Monaten noch weißt, was du da gemacht hast ? Mal abgesehen davon, das du wenig Kommentarzeilen nutzt, du springst auch etwas gewöhnungsbedürftig im Code. Soll keine Kritik sein, denn jeder Programmierer hat da so seine eigene Vorstellung von "Perfektion". Im Prinzip aber gilt das Ergebnis. Nach dem Code wird nie wieder gefragt. Dennoch ist es für dich einfacher, auf Strukturen zu achten. Vielleicht schaust du dir mal unter FAQ meinen Beitrag "keine Angst vor Assembler an". Ich denke, dann wirst du verstehen, warum ich an deinem Programm "nörgel"... ;)
Ich bau grad einen Count-Down für "meine letzten Tage". Wenn ich damit fertig bin, werd ich mal das Programm und die Hardware vorstellen. Ziel ist Ferigstellung im nächsten Jahr. Dann wird im 1/10 Sekundentakt meine verbleibende "aktive" Zeit gezählt :flute: und natürlich auch am Arbeitsplatz positioniert. :hahaha:
Gruß oldmax

PS: ... ich kann ja so gemein sein ....
 
Hi,

Ich bau grad einen Count-Down für "meine letzten Tage".
...
Dann wird im 1/10 Sekundentakt meine verbleibende "aktive" Zeit gezählt :flute: und natürlich auch am Arbeitsplatz positioniert. :hahaha:
Mit oder ohne Alarmgeber für den Termin für den Ausstand ? :p
Damit die Kollegen auch wissen wann es das "große Fressen" gibt :cool:

Gruß
Dino
 
Hi Dino


Mit oder ohne Alarmgeber für den Termin für den Ausstand ?
Damit die Kollegen auch wissen wann es das "große Fressen" gibt

Keine schlechte Idee, vielleicht lass ich mir diesbezüglich noch was einfallen. Schließlich ist mein Atmega16 noch nicht ganz ausgelastet:yes4:
Warum ich diesen dicken Brocken eingebaut habe ? ... einfach weil ich ihn noch hatte...
(bevor diese Frage gestellt wird...)
OK, muss jetzt mal so langsam für's Abendessen sorgen. Sonst muss ich hungern, und ich bin doch im Wachstum ( aber nur in der Mitte :rolleyes:)
Also, bis demnächst.
Gruß oldmax
 
Grüß dich Dino und Oldmax
Ich danke das ihr mir geschrieben habt und das Dino mein Programm
formatiert hat. Beim rüber kopieren von my AVR Workpad da klappte
es bei mir nicht so wie ich wollte.
Ich danke dir für deine Kritik, ich lerne gerne dazu. Mir ist schon klar das
meine Programme nicht so professionell sind und das man sie sicher besser
bewerkstelligen könnte. Ich schaue mir deinen Beitrag
"keine Angst vor Assembler an" nachdem ich diesen Beitrag abgeschickt
habe gerne an. Zu deinem Count- Down Zähler für die letzten Tage habe
ich ein ähnliches Programm geschrieben. Port C3 habe ich als Schaltausgang
programmiert der bei erreichen der Anzeige von Null etwas schalten kann.
Vielleicht interessiert dich das Programm.
Gruß Ernst
Code:
;+----------------------------------------------------------------------
;| Title        : Multiplex Anzeige für ATmega8
;+----------------------------------------------------------------------
;| Funktion        : ... Zählt von einer eingegebenen Zahl an bis Null
;|                  : ... Schaltet Ausgang Port C3 Ein wenn Null erreicht
;|                  : ... zeigt Sekunden, Minuten, Stunden an
;|         : ... Taster an Port C1 startet Zählvorgeng
;| Schaltung    : ...
;+----------------------------------------------------------------------
;| Prozessor    : ATmega8
;| Takt        : 3,6864 MHz
;| Sprache           : Assembler
;| Datum             : ...
;| Version           : ...
;| Autor             : ...
;+----------------------------------------------------------------------
.include    "AVR.H"
;------------------------------------------------------------------------
;Reset and Interrupt vector             ;VNr.  Beschreibung
    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
    reti        ;7   TC1 Compare Match A
    reti        ;8   TC1 Compare Match B
    reti        ;9   TC1 Overflow
    rjmp    tim0_ovf    ;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
;------------------------------------------------------------------------
;Start, Power ON, Reset
main:
    ldi    r16,hi8(RAMEND)
    out    SPH,r16
    ldi    r16,lo8(RAMEND)
    out    SPL,r16
    ldi    r16,0b01110000
    out    DDRD,r16
    ldi    r16,0b00011111
    out    DDRB,r16
    ldi    r16,0b00000111
    out    DDRC,r16
    ldi    r16,0b00000011
    out    PORTC,r16
    ldi    r16,0b00000011
    out    TCCR0,r16
    ldi    r16,0b00000001
    out    TIMSK,r16
    ldi    r18,0x53
    out    TCNT0,r18
    ldi    r30,0x60
    ldi    r31,0x00
    ldi    r16,0x03
    sts    0x66,r16
;------------------------------------------------------------------------
; Hier Zeit für Zählvorgng eingeben
    ldi    r21,0x00    ;Zehner Stunden eingeben
    sts    0x65,r21
    ldi    r20,0x10    ;Einer Stunden eingeben
    sts    0x64,r20
    ldi    r19,0x01            ;Zehner Minuten eingeben
    sts    0x63,r19
    ldi    r18,0x10    ;Einer Minuten eingeben
    sts    0x62,r18
    ldi    r17,0x00    ;Zehner Sekunden eingeben
    sts    0x61,r17
    ldi    r16,0x00    ;Einer Sekunden eingeben
    sts    0x60,r16
    sei
;------------------------------------------------------------------------
mainloop:

; testet ob mindestens eine Stelle ist höher als 0 wenn ja dann Start
    cpi    r21,0x00
    brne    Z6
    cpi    r20,0x10
    brne    Z5
    cpi    r19,0x00
    brne    Z4
    cpi    r18,0x10
    brne    Z3
    cpi    r17,0x00
    brne    Z2
    cpi    r16,0x00
    brne    Z1
; Zeigt Fehlerhafte Eingabe an
; bei Eingabe 000000 erscheinen 6 Decimalpunkte
; Programm wird beendet
    ldi    r21,0x1a
    sts    0x60,r21
    sts    0x61,r21
    sts    0x62,r21
    sts    0x63,r21
    sts    0x64,r21
    sts    0x65,r21
    rjmp    ende
Z6:    rjmp    Zahl1
Z5:    rjmp    Zahl1
Z4:    rjmp    Zahl1
Z3:    rjmp    Zahl1
Z2:    rjmp    Zahl1
Z1:    rjmp    Zahl1
;------------------------------------------------------------------------
Zahl1:    sbic    PINC,0
    rjmp    Zahl1
    rjmp    Start


Start:    cpi    r16,0x00
    breq    Zahl2
    rcall    loop1
Zahl2:    cpi    r17,0x00
    breq    Zahl3
    rcall    loop2
Zahl3:    cpi    r18,0x10
    breq    Zahl4
    rcall    loop3
Zahl4:    cpi    r19,0x00
    breq    Zahl5
    rcall    loop4
Zahl5:    cpi    r20,0x10
    breq    Zahl6
    rcall    loop5
Zahl6:    cpi    r21,0x00
    breq    Zahl7
    rcall    loop6
Zahl7:    ldi    r16,0b00000100
    out    PORTC,r16

    rjmp    ende
;------------------------------------------------------------------------
;    Ende des Zählvorgangs
ende:    rjmp    ende
;------------------------------------------------------------------------
;------------------------------------------------------------------------
loop1:    rcall    zeit
    dec    r16
    sts    0x60,r16
    cpi    r16,0x00
    brne    loop1
    ret
;------------------------------------------------------------------------
;------------------------------------------------------------------------
loop2:
    cpi    r16,0x00
    breq    loop2x
    dec    r17
    sts    0x61,r17
    rcall    loop1
    cpi    r17,0x00
    brne    loop2
    cpi    r16,0x00
    brne    loop1
    ret
loop2x:    rcall    zeit
    ldi    r16,0x09
    sts    0x60,r16
    rjmp    loop2
;------------------------------------------------------------------------
;------------------------------------------------------------------------
loop3:
    cpi    r17,0x00
    breq    loop3x
    rcall    loop2
    cpi    r18,0x10
    brne    loop3x
    ret
loop3x:    rcall    zeit
    ldi    r16,0x09
    ldi    r17,0x06
    sts    0x60,r16
    sts    0x61,r17
    dec    r18
    sts    0x62,r18
    rjmp    loop3
;------------------------------------------------------------------------
;------------------------------------------------------------------------
loop4:    cpi    r18,0x10
    breq    loop4x
    rcall    loop3
    cpi    r19,0x00
    brne    loop4x
    ret
loop4x:    rcall    zeit
    ldi    r16,0x09
    ldi    r17,0x06
    ldi    r18,0x19
    sts    0x60,r16
    sts    0x61,r17
    sts    0x62,r18
    dec    r19
    sts    0x63,r19
    rjmp    loop4

;------------------------------------------------------------------------
;------------------------------------------------------------------------
loop5:    cpi    r19,0x00
    breq    loop5x
    rcall    loop4
    cpi    r20,0x10
    brne    loop5x
    ret
loop5x:    rcall    zeit
    ldi    r16,0x09
    ldi    r17,0x06
    ldi    r18,0x19
    ldi    r19,0x05
    sts    0x60,r16
    sts    0x61,r17
    sts    0x62,r18
    sts    0x63,r19
    dec    r20
    sts    0x64,r20
    rjmp    loop5
;------------------------------------------------------------------------
;------------------------------------------------------------------------
loop6:    cpi    r20,0x10
    breq    loop6x
    rcall    loop5
    cpi    r21,0x00
    brne    loop6x
    ret
loop6x:    rcall    zeit
    ldi    r16,0x09
    ldi    r17,0x06
    ldi    r18,0x19
    ldi    r19,0x05
    sts    0x60,r16
    ldi    r20,0x19
    sts    0x61,r17
    sts    0x62,r18
    sts    0x63,r19
    sts    0x64,r20
    dec    r21
    sts    0x65,r21
    rjmp    loop6


;------------------------------------------------------------------------
;------------------------------------------------------------------------
tim0_ovf:
    push    r16
    push    r17
    push    r18
    push    r19
    in    r16,SREG
    ld    r18,z+
    lds    r19,0x66
    swap    r19
    out    PORTB,r18
    out    PORTD,r19
    swap    r19
    dec    r19
    sts    0x66,r19
    cpi    r19,0xff
    brne    tim0_ovf_1
    ldi    r30,0x60
    ldi    r31,0x00
    ldi    r19,0x05
    sts    0x66,r19
tim0_ovf_1:    ldi    r18,0x53
    out    TCNT0,r18
    out    SREG,r16
    pop    r19
    pop    r18
    pop    r17
    pop    r16
    reti
    rjmp    mainloop
;--------------------------------------------------
zeit:
    push    r16
    push    r17
    push    r18
    in    r16,SREG
    push    r16
    ldi    r16,0xcc    ; 5f  ändert Geschwindigkeit des Zählers  0x64 cc
zeit1:    ldi    r17,0xc8    ; c8
zeit2:    ldi    r18,0x1d    ; 1d
zeit3:    dec    r18
    brne    zeit3
    dec    r17
    brne    zeit2
    dec    r16
    brne    zeit1
    pop    r16
    out    SREG,r16
    pop    r18
    pop    r17
    pop    r16
    ret


;--------------------------------------------------
 
Hi Ernst,

Mir ist schon klar das
meine Programme nicht so professionell sind und das man sie sicher besser
bewerkstelligen könnte.

Naja professionell oder nicht. Es ist eher wichtig ein paar mehr Bemerkungen reinzumachen damit ...

1. Andere bei der Fehlersuche (Hilfestellung) besser in das Programm reinfinden. Sonst wird dir notfalls nicht wirklich einer helfen weil die Einarbeitung zu aufwändig ist.

2. Du nach 1-2 Monaten mit dem Code auf noch was anfangen kannst und nicht dastehst wie nen Ochs vorm Scheunentor (wofür war das nochmal, warum hab ich das an der Stelle reingesetzt ?)

Dein Programmteil für ne Warteschleife ...
Code:
;------------------------------------------------------------------------
;------------------------------------------------------------------------
loop2:
    cpi    r16,0x00
    breq    loop2x
    dec    r17
    sts    0x61,r17
    rcall    loop1
    cpi    r17,0x00
    brne    loop2
    cpi    r16,0x00
    brne    loop1
    ret
loop2x:    rcall    zeit
    ldi    r16,0x09
    sts    0x60,r16
    rjmp    loop2
;------------------------------------------------------------------------
;------------------------------------------------------------------------

Mein Programmteil für ne Warteschleife ...
Code:
; ==================================================================
; ===== 3ms warten =================================================
; ==================================================================
;	rcall wait3m	; (3)     Warteschleife aufrufen
; ================>	; 59758 Cyclen (incl Call+Ret) => ~3ms@20MHz
wait3m: push r20	; (2)     r20 auf Stack retten (1tes)
	push r21	; (2)     r21 auf Stack retten (2tes)
	clr r20		; (1)     r20+r21 ergeben zusammen
	ldi r21,116	; (1)     einen 2-Byte-Zaehler (47913Cycl)
			;
			;                    2__________59758__
			;                    |    1__513__     |
			;                    |    |       |    |
			;         12 Cycle = 116*((256*2+1)+2)+1+17
loop3m: dec r20		; (1)     || niedrigstes Byte -1
	brne loop3m	; (1f,2t)_/| 0 erreicht? nein -> Schleife
	dec r21		; (1)      | mittleres Byte -1
	brne loop3m	; (1f,2t)__/ 0 erreicht? nein -> Schleife

	pop r21		; (2)     r21 zurueckholen (2tes)
	pop r20		; (2)     r20 zurueckholen (1tes)
	ret		; (4)     Schleifenende, Rueckkehr

Wie man sieht ist bei mir ein nicht unerheblicher Anteil des Codes Bemerkungen und Infos. (etwa die Hälfte bis dreiviertel).

Die Zahlen in den Klammern sind die verbrauchten Taktzyklen der Befehle. Normalerweise braucht man das nicht. Das ist bei Warteschleifen für eine genaue Zeit jedoch wichtig alles mitzurechnen. Ich habe auch die beiden Schleifen (Cycle1 und Cycle2) mit den verbrauchten Taktzyklen aufgerechnet. Man sieht auch wofür welche Zeile da ist. Man könnte sich jetzt streiten ob es wirklich so ausführlich sein muß. Es hilft mir aber nach ein paar Monaten enorm mich wieder in den Code einzuarbeiten.

Das ist beim Programmieren erstmal das wichtigste. Man muß den Code ohne viel Aufwand schnell nachvollziehen können.

Gruß
Dino
 
Grüß dich Dino
Zuerst danke ich dir dass du mir dein Programm zeigst. Ich kann daraus lernen.
Mit den Kommentaren gebe ich dir recht bei komplexeren Programmen da werde
ich mehr Randbemerkungen als Notizen dazu schreiben. Zu den Zeitschleifen da gebe
ich zu das ich da nichts gerechnet habe. Ich habe die Uhr einfach 10 Stunden laufen
lassen und sie mit einer Funkuhr verglichen wenn sie vor- oder nach ging da habe ich
die Zeitschleife einfach ein paar Takte verlängert oder verkürzt bis sie genau ging.
Gruß Ernst
 
Hi Ernst

Zuerst danke ich dir dass du mir dein Programm zeigst. Ich kann daraus lernen.
Im Projekte-Bereich ist noch mehr Assembler-Code von mir. Sieh mal in die folgenden Projekte ...
ATmega32-Board für Steueraufgaben und Experimente
ASCII-Keyboard mit 4Bit-LCD und Tiny2313
Analyzer für I2C/TWI und 1-Wire/microLAN
Wie alles begann - oder - 4Kanal-PWM mit AT90S2313 für einen Magierstab

Wobei mein Assemblercode auch nicht unbedingt das gelbe vom Ei ist. Der ist bestimmt noch um einiges verbesserungswürdig.

Mit den Kommentaren gebe ich dir recht bei komplexeren Programmen da werde
ich mehr Randbemerkungen als Notizen dazu schreiben.
In meinen Quellcodes wirst du ne Menge an Zusatzinfos finden. Zum Beispiel Teile aus Datenblättern wenn es darum geht für einen Baustein ein bestimmtes Timing einzuhalten.

Zu den Zeitschleifen da gebe ich zu das ich da nichts gerechnet habe. Ich habe die Uhr einfach 10 Stunden laufen lassen und sie mit einer Funkuhr verglichen wenn sie vor- oder nach ging da habe ich die Zeitschleife einfach ein paar Takte verlängert oder verkürzt bis sie genau ging.
Stell dir mal vor, du erzeugst mit einem Timer alle 100µs einen Interrupt (also mit 10kHz). Wenn du nun einfach vor dich hinprogrammierst, dann kann es passieren das auf einmal dein Programm in den Interruptroutinen mehr Zeit verbraucht als es bis zum nächsten Interrupt zur Verfügung hat. Oder du bekommst mehr Daten zusammen als du in der Zwischenzeit verarbeiten (zB über UART senden) kannst. Dann bekommen die Puffer für die Daten auf einmal dicke Backen. Wie du siehst gehört eine saubere Zeitplanung immer dazu. Einfach rumprobieren geht zwar bei einfachen Sachen meißt gut, kann aber auch schnell in die Hose gehen.

Eine gute Planung und Vorarbeit ist bei der Programmierung genauso wichtig wie bei allen anderen Projekten. Wenn man sich Gedanken macht was man eigentlich machen will und wie man es am besten umsetzen kann dann ist schon die meißte Arbeit getan. Dazu gehören natürlich auch Berechnungen von Ausführungszeiten wenn es darauf ankommt.

Mit Assembler kann man die schnellsten Programme schreiben die gegenüber einer Hochsprache das letzte aus der Hardware herausquetschen können. Das heißt aber auch das man sich wesentlich weiter in die internen Abläufe der Hardware einarbeiten muß. Im schlimmsten Fall wird das Programm damit auf einem Controllertyp angebunden und ist nicht mehr auf andere portierbar. Man muß da immer einen goldenen Mittelweg finden.

Ich wünsche dir auf jeden Fall viel Spaß mit Assembler. Nur hier lernst du den Controller wirklich kennen. Aber vergiß nicht die nervenden Sachen einfach mal einer Hochsprache zu überlassen. Ich habe mich dazu durchgerungen die nervigen Aufgaben durch Bascom ausführen zu lassen und nur die zeitkritischen Aufgaben oder ästhetischen Lösungen durch Assembler auszuführen. Wobei ich im Moment bei den selbstgeschriebenen Bibliotheken noch gaaanz am Anfang bin.

Gruß
Dino
 
Hi
Nun, ich bin aus meinem Kurzurlaub zurück und hab ein wenig mal in den Posts geblättert. Ernst, wenn du meinst, bei einem kleinen Programm brauchts keine Struktur... ääähhh falsch. Es geht eigentlich gar nicht so sehr um eine Programmgröße, sondern um Gewohnheiten. Und noch etwas. Du willst doch nicht immer wieder den gleichen Code schreiben, womöglich jedesmal darüber nachdenken, wie eine Parametrierung durchzuführen ist. Deshalb empfehle ich, auch gegen den ein oder anderen Einwand, Module zu schreiben.
Auf die Gefahr hin, das ich mich hier zum xten Mal wiederhole, ein Programm besteht bei mir aus einer übersichtlichen Schleife mit Aufrufen von Unterprogrammen. Dabei beginne ich innerhalb der Schleife mit dem Einlesen der Eingänge und lege diese in der richtigen Logik ab. Also, ein betätigter Taster ist eine "1". In der Regel ist ein Taster aber gegen GND geschaltet, so das der Pegel genau verkehrt ist. Das wird in der Einleseroutine gleich "richtig" gestellt. Auch ist es in den seltesten Fällen nicht notwendig, Kontaktbehaftete Eingänge in einer Interruptroutine einzulesen. Selbst bei einem 1MHz-Takt und umfangreichen Programm ist es eine Kunst, den Taster so kurz zu betätigen, das es der Controller nicht mitbekommt. Wenn das so ist, dann sind es Verzögerungsblöcke, die den Controller für eine Zeit im Kreis laufen lassen. Eine scheußliche Unart.....
Doch wie realisiert man dann eine Verzögerung ? Und wie hält man die Programmschleife "klein" ?
Das ist nicht mit ein paar Worten erklärt, aber trotzdem versuche ich es mal. Denk mal an "Signalbits". Also an Bits, die wenn gesetzt, eine Aktion auslösen und danach wieder gelöscht werden. Die packe ich in Bytes mit dem Präfix "_Ctrl" für Control. Also, Prg_Ctrl (Programmkontrollbits), Read_Ctrl (Empfangskontrollbits), Time_Ctrl (Zeitkontrollbits) etc..
Nehmen wir einmal die Time_Ctrl - Bits. Diese werden in der Timer-ISR gesetzt, z.B. Bit 0 alle Millisekunden, Bit 1 alle Sekunden usw. Schon habe ich in meinem Hauptprogramm eine Möglichkeit, dieses Bit zu prüfen, die entsprechende Bearbeitung zu erledigen und das Bit zu löschen. Ob nun exakt im mSek-Takt die Bearbeitung erfolgt, oder 10 oder 20µSek. später ist doch völlig wurscht. Wichtig ist, das, wenn ich die Ereignisse zähle nach 1 Sekunde 1000 Ereignisse erfolgt sind und nicht 978 oder so. Das bedeutet, das du dir im Klaren sein mußt, wenn du ein Bit im mSek. Takt im Programm prüfst und bearbeiten willst, du immer eine Zykluszeit < 1 mSek. haben mußt. Daher sind solch kleine Zeiten für Controllbits nicht besonders geeignet, aber eine 1/10 Sekunde passt da schon prima. So könnte der Aufruf aus der Programmschleife erfolgen :
Code:
     ......
     RCall   Chk_Time
     ......
und so wird dann das Event bearbeitet:
Code:
;******************************************************
;* Diese Routine bearbeitet die Ereignisbits, die in der TImer-ISR   *
;* gesetzt werden. Im Byte time_Ctrl sind folgende Bits definiert:   *
;*     Bit 0 = 1/10 Sekunde                                                          *
;*     Bit 1 = 1 Sekunde                                                               *
;* Weitere Bits können jeden beliebigen Zeitraum bedeuten           *
;******************************************************
Chk_Time:
   LDS   Reg_A, Time_Ctrl             ; Bits werden in Timer ISR gesetzt. 
   ANDI Reg_A, 0b00000001         ; Bit 0 (1/10 Sekunde) prüfen
   BREQ Chk_Sekunde                  ; wenn nicht gesetzt, dann prüf auf Sekundentakt
   LDS   Reg_A, Time_Ctrl             ; nochmal laden
   ANDI Reg_A, 0b11111110         ; Bit löschen ( Event quittieren)
   STS   Time_Ctrl, Reg_A             ; und Signalbits zurückschreiben
   ;----------------------------------------------------------------------
   ; hier erfolgt nun die Bearbeitung, oder ein RCALL zu einer
   ; entsprechenden Routine
   ;----------------------------------------------------------------------
Chk_Sekunde:
    LDS   Reg_A, Time_Ctrl             ; Bits werden in Timer ISR gesetzt. 
    ANDI Reg_A, 0b00000010         ; Bit 0 (1 Sekunde) prüfen
    BREQ End_Chk_Time                 ; wenn nicht gesetzt, dann beende
    LDS   Reg_A, Time_Ctrl             ; nochmal laden
    ANDI Reg_A, 0b11111101         ; Bit löschen ( Event quittieren)
    STS   Time_Ctrl, Reg_A             ; und Signalbits zurückschreiben
   ;----------------------------------------------------------------------
   ; hier erfolgt nun die Bearbeitung, oder ein RCALL zu einer
   ; entsprechenden Routine
   ;----------------------------------------------------------------------
End_Chk_Time
Mit dieser Subroutine hast du nun schon ein Tool, welches du, so wie es ist, in ein anderes Programm einfügen kannst. Lediglich die Bearbeitungen der einzelnen Abschnitte werden angepasst. So kannst du dir Verzögerungen bauen, die dein Programm in der Zykluszeit (fast) überhaupt nicht belasten. Mehr Hinweise findest du in den Beiträgen im FAQ-Bereich. Und jetzt viel Spaß
Gruß oldmax
PS: Die Formatierung der Kommentarzeilen bitte ich zu entschuldigen, aber mein Editor entspricht nicht der versendeten Darstellung....
 
Grüßt euch Dino und Oldmax
Danke für die Tipps und den Programmcode von euch. Ich finde es eine gute Idee
Programmteile (Tasterabfrage oder so) zu schreiben, sie abzuspeichern und dann
die Teile so wie man sie benötigt in das eigentliche Programm einsetzen.
Zum Assembler und den Hochsprachen. Mein Workpad - Programm verträgt nur
Assembler und C / C++. Bei C / C++ da steht in dem Begleitheft das bei meinem
Board mit dabei gewesen nicht viel drinnen. Ich habe dann auch schon das
Ponyprogramm oder Studioprogramm auf den Rechner installiert gehabt aber die
erkennen mein Bord nicht. Obwohl es passend angeschlossen und die
Programmierung mit meinem AVR Workpad – Programm klappt. Nachdem ich
nächtelang ohne Erfolg darüber gesessen bin die Einstellungen von den Programmen
so zu ändern dass es mit dem Programmieren klappt habe ich es aufgegeben diese
Programme zu verwenden. Was soll es, ich bin mit dem zufrieden was ich habe.
Zu den Zeitschleifen. Ich finde es auch nicht gut wenn man den Prozessor im
Hauptprogramm mit einer Zählschleife lähmt um eine gewünschte Verzögerungszeit
zu realisieren. Im Begleitheft da ist ein Programmbeispiel mit Timern darinnen wie man
aus den verwendeten 3686400 MHZ Quarz 440 Herz macht. Wie ist es aber wenn man
wesendlich längere Zeiten haben möchte die im Sekunden, Minuten oder vielleicht sogar
im Stundenbereich liegen. Ich habe das noch nicht ganz herausgefunden aber es ist ja
kein Meister vom Himmel gefallen. Zum Schluss noch einmal einen Dank das ihr euch so
einsetzt für meine Probleme.
Gruß Ernst
 
Hi
Nun, das mit dem Timer ist gar nicht so schwer. In meinem Beitrag "Keine Angst vor Assembler" habe ich in Kapitel 3.3 kurz den Timer gestreift. Vielleicht zu kurz, deshalb hier noch ein paar bemerkungen.
Der Timer ist eigentlich ein Zähler, der direkt die Takte vom System zählt. Da ein Takt eine bestimmte Zeit benötigt, kann man bei einer Vorgabe von Takten eine Zeit bestimmen. Beispiel: Dein µC läuft mit 1 MHz internem Takt. Um nun 1 Sekunde abzuleiten, müsste man 1 Mio. Takte zählen. Das ist aber nicht nötig. Ein Timer hat ein 16 Bit-Register, welches man durchaus auf 1000 setzen kann und dann vergleicht, ob diese 1000 erreicht wurden. Natürlich macht man dies nicht mit einem Programm, da dann ja auch Zeit verschwendet würde, sondern "parametriert" lediglich den Timer. Wird nun der Vergleichswert erreich, dann löst eine est eingebaute Kogik einen Interrupt aus und springt an die zugehörige Adresse in der Interrupt Vektor Tabelle ( IVT ) Dort hast du nun die Möglichkeit, ein RETI (Return from Interrupt) hinzuschreiben und nix zu tun, oder einen Sprung zu einer von dir vorgegebenen Adresse vorzunehmen. Adressen werden mit symbolischen Namen versehen wie "My_Timer_ISR:" und einem Doppelpunkt. Der Compiler generiert daraus die Adresse im Befehlsspeicher oder im Fachjargon "Codesegment".
Hier mal ein Beispiel, wie eine solche Interrupt Service Routine ( ISR ) für einen Timer aussieht:
Code:
My_Timer_ISR:                                 ; Einsprungsadresse in die ISR
    Push     Reg_A                             ; Zuerst die benutzten Register retten
    In         Reg_A, SREG                    ; nun noch das Statusregister
    Push     Reg_A                             ; retten
    LDS      Reg_A, MilliSekunden          ; eine vorher definierte Variable zum zählen der Interrupts
    Inc       Reg_A                             ; erhöhen
    STS      MilliSekunde, Reg_A           ; und erst mal abspeichern
    CPI       Reg_A, 100                      ; 99 überschritten?
    BRLO     End_Timer_Isr                  ; wenn nicht, dann Ende 
    CLR       Reg_A                            ; und wieder bei 0 anfangen
    STS      Millisekunde, Reg_A           ; und abspeichern
    LDS      Reg_A, Timer_Ctrl              ; Kontrollbyte laden
    ORI      Reg_A, 0b00000001           ;  Bit 0 als Timer-Event für 1/10 Sekunde setzen
    STS      Timer_Ctrl, Reg_A             ; und abspeichern , Bit wird im Hauptprogramm behandelt
    LDS      Reg_A, Sek_10tel              ; Variable für 10tel Sekunden
    INC      Reg_A                             ; erhöhen und
    STS     Sek_10tel, Reg_A              ; ablegen
    CPI      Reg_A, 10                        ; größer 9 ? 
    BRLo    End_Timer_ISR                  ; wenn nicht, dann beende die ISR
    CLR      Reg_A                             ; und wieder bei 0 anfangen
    STS     Sek_10tel, Reg_A
    LDS      Reg_A, Timer_Ctrl              ; Kontrollbyte laden
    ORI      Reg_A, 0b00000010           ;  Bit 1 als Timer-Event für 1Sekunde setzen
    STS     Timer_Ctrl, Reg_A              ; und abspeichern , Bit wird im Hauptprogramm behandelt
End_Timer_ISR:
    Pop     Reg_A                             ; Register zurückholen
    Out     SREG, Reg_A                    ; Zuerst das Statusregister
    POP     Reg_A                             ; und dann Register A wieder herstellen
RETI                                             ; hier ist jetzt die ISR beendet.
Wie du siehst, sind nun Kontrollbits vorhanden, die du im Hauptprogramm wie in einem bereits geschildertem Verfahren bearbeiten und auswerten kannst. Ob du da wieder in eine Variable zählst und dir so Minuten und Stunden zusammenbastelst, ist völlig unerheblich. Auch, das die Bearbeitung evtl. mal eine tausendstel Sekunde später stattfindet, wirst du nicht merken. Lediglich die Genauigkeit des internen Taktgebers ist nicht so doll und daher empfiehlt es sich, mit einem externen Quarz zu arbeiten. Dann hast du noch die Möglichkeit, einen Vorteiler einzusetzen, der die eingehenden Takte vor dem "Timer" herunter teilt - z. B. durch 8. Damit bist du wieder bei einem 8 MHz Quarz bei 1 MHz Zählfrequenz. Liegst du bei 16 MHZ, dann setzt du statt 100 im 1. Vergleich 200 ein. So bekommst du auch die richtige Zeitbasis.
Gruß oldmax
 
Grüßt dich Oldmax
Ich danke dir für die Erklärung und das Programm. Ich habe es bei mir auf den Board ausprobiert und es klappt super. An der Genauigkeit von dem Sekundentakt habe ich noch nicht gearbeitet aber das ist mir momentan nicht so wichtig. Mir ging es ja in erster Linie daran eine längere Verzögerungszeit zu realisieren ohne dass ich den Prozessor mit Zählvorgängen blockiere.
Gruß Ernst
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)