Assembler - Getränkeautomat

From XennisWiki
Jump to: navigation, search

Programmierung des Mikrocontrollers Atmel AVR - ATmega 16.

; Ablaufsteuerung fuer einen Verkaufsautomaten

.include "m16def.inc"

; benutzerdefinierte Konstanten
.equ	MaxMs	=	99				; Maximaler Geldbetrag (9,90 Euro), der eingewurfen werden kann
	; Eingangsmasken
.equ	E10C	=	0b00000001			; Einwurf 10 Cent
.equ	E20C	=	0b00000010			; Einwurf 20 Cent
.equ	E50C	=	0b00000100			; Einwurf 50 Cent
.equ	E1E	=	0b00001000			; Einwurf 1 Euro
.equ	AG1	=	0b00010000			; Auswahl Getränk "Cola"
.equ	AG2	=	0b00100000			; Auswahl Getränk "Sprite"
.equ	AG3	=	0b01000000			; Auswahl Getränk "Wasser"
.equ	EClear	=	0b10000000			; Rückgabetaste (CLEAR)
; Ende benutzerdefinierte Konstanten

.dseg
.org 0x0060						; Startadresse des Datenbereichs

; benutzerdefinierte Variablen
MS:		.byte	1				; Münzspeicher
PreisTab:	.byte	3				; Tabelle mit den Preisen für die Getränke					
AuswurfTab:	.byte	8				; Tabelle mit den Auswurfmasken	
; Ende benutzerdefinierte Variablen

.cseg
.org 	0x0000						; Startadresse des Codebereichs (Reset)
rjmp 	main
.org 	0x002a

main:		;Stackpointer initialisieren
		ldi	R16,	high(RAMEND)
		out	SPH,	R16
		ldi	R16,	low(RAMEND)
		out	SPL,	R16
		;Portdefinitionen
		ldi	R16,	0xFF			; PORTD (Ausgaben)
		out	DDRD,	R16
		ldi	R16,	0x00
		out	PORTD,	R16
		ldi	R16,	0x00			; PORTB (Eingaben)
		out 	DDRB,	R16
		ldi	R16,	0xFF
		out	PORTB,	R16

; benutzerdefinierter Programmteil
; -- Initialisierungsteil
		;Portdefinitionen
		ldi	R16,	0xFF			; PORTA (Ausgaben)
		out	DDRA,	R16
		ldi	R16,	0x00
		out	PORTA,	R16

		; Variablen initialisieren
		ldi	R16,	0x00
		sts	MS,	R16			; Münzspeicher = 0 Euro setzen

		; Tabellen initialisieren
		ldi	ZL,	low(AuswurfTab)		; Auswurf Tabelle
		ldi	ZH,	high(AuswurfTab)
		ldi	R16,	0
		st	Z+,	R16
		ldi	R16,	1
		st	Z+,	R16
		ldi	R16,	2
		st	Z+,	R16
		ldi	R16,	3
		st	Z+,	R16
		ldi	R16,	4
		st	Z+,	R16
		ldi	R16,	5
		st	Z+,	R16
		ldi	R16,	6
		st	Z+,	R16
		ldi	R16,	7
		st	Z+,	R16

		ldi	ZL,	low(PreisTab)		; Preistabelle
		ldi	ZH,	high(PreisTab)
		ldi	R16,	11
		st	Z+,	R16
		ldi	R16,	9
		st	Z+,	R16
		ldi	R16,	8
		st	Z+,	R16

; -- Hauptprogramm
a:		ldi	R16,	EClear			; Eingangserfassung von CLEAR
		call	eingangserfassung
		brne	callGeldrueckgabe		; 	Falls CLEAR gedrückt => springe

		; Eingangserfassung von 10 Ct. bis 1 Euro
		ldi	R16,	E10C			; Eingangserfassung von 10 Ct.
		call	eingangserfassung
		ldi	R16,	0x01				; entspricht 10 Ct.
		brne	erhoeheMuenzspeicher
		ldi	R16,	E20C			; Eingangserfassung von 20 Ct.
		call	eingangserfassung
		ldi	R16,	0x02				; entspricht 20 Ct.
		brne	erhoeheMuenzspeicher
		ldi	R16,	E50C			; Eingangserfassung von 50 Ct.
		call	eingangserfassung
		ldi	R16,	0x05				; entspricht 50 Ct.
		brne	erhoeheMuenzspeicher
		ldi	R16,	E1E				; Eingangserfassung von 1 Euro
		call	eingangserfassung
		ldi	R16,	0x0A				; entspricht 1 Euro
		brne	erhoeheMuenzspeicher

		; Münzspeicher > 9,90 Euro ?
b:		lds	R16,	MS			; Münzspeicher laden
		ldi	R17,	MaxMs			; Maximalen Betrag laden
		cp	R17,	R16			; Maximaler Betrag - Münzspeicher (R17-R16)
		brmi	callGeldrueckgabe		; Wenn Münzspeicher > Maximaler Betrag springe (d.h. wenn n=1)

		; Eingangserfassung von Getränk 1 bis Getränk 3
		ldi	R16,	AG1			; Eingangserfassung von Getränk 1
		call	eingangserfassung		; Parameter "Eingabemaske" in R16
		ldi	R16,	1
		brne	muenzspeicherAG
		ldi	R16,	AG2			; Eingangserfassung von Getränk 2
		call	eingangserfassung		; Parameter "Eingabemaske" in R16
		ldi	R16,	2
		brne	muenzspeicherAG
		ldi	R16,	AG3			; Eingangserfassung von Getränk 3
		call	eingangserfassung		; Parameter "Eingabemaske" in R16
		ldi	R16,	3
		brne	muenzspeicherAG

		; Ausgabe von Münzspeicher auf Display
		lds	R16,	MS			; Münzspeicher laden		
		out	PORTA,	R16			; Displayanzeige aktualisieren

		rjmp	a				; Springe an den Anfang

; Ruft das Unterprogramm "geldrueckgabe" auf
callGeldrueckgabe:
		lds	R16,	MS			; Parameter "Münzspeicher" für das Unterprogramm
		call	geldrueckgabe
		ldi	R16,	0x00
		sts	MS,	R16			; Lösche Münzspeicher nach Geldrückgabe
		rjmp	a

; Erhöht den Münzspeicher um ein Vielfaches von 10 Cent
; Parameter:	R16: Erhöhung um Vielfaches von 10 Cent (z.B. R16=3 => Erhöhung um 3*10 Cent)
erhoeheMuenzspeicher:
		lds	R17,	MS			; Lade Münzspeicher
erhoeheLoop:
		inc	R17				; ... und erhöhe dieses um 10 Ct.
		dec	R16				; Dekrementiere Schleifenzähler
		brne	erhoeheLoop			; Wenn Schleifenzähler != 0 => springe an den Anfang
		sts	MS,	R17			; Speichere Münzspeicher
		rjmp	b

; Getränkepreise mit dem eingeworfenen Betrag vergleichen
; Parameter:	R16 Getränknummer
; Rückgabe:	R18 Auswurfmaske
muenzspeicherAG:

		; Auswurfmaske des Getränks (R18)
		ldi	ZL,	low(AuswurfTab)		; Z mit Adresse der Auswurf-Tabelle initialisieren
		ldi	ZH,	high(AuswurfTab)
		ldi	R17,	0
		add	ZL,	R16			; Die Nummer des Getränks zu ZL hinzuaddieren 
		adc	ZH,	R17			; Ggf. den Übertrag zu ZH hinzuaddieren
		ld	R18,	Z			; Auswurfmaske speichern
		; Preis des Getränks (R16)
		ldi	ZL,	low(PreisTab)		; Z mit Adresse der Preis Tabelle initialisieren
		ldi	ZH,	high(PreisTab)
		ldi	R17,	0
		add	ZL,	R16			; Die Nummer des Getränks zu ZL hinzuaddieren 
		adc	ZH,	R17			; Ggf. den Übertrag zu ZH hinzuaddieren
		ld	R17,	-Z			; Preis des Getränks speichern ("-Z" da Preis des ersten
							; Getränks an Stelle Null gespeichert ist)

		; Münzspeicher und Preis vergleichen
		lds	R16,	MS			; Lade Münzspeicher
		cp	R17,	R16			; Getränkepreis - Münzspeicher
		brmi	erniedriegeMuenzspeicher	; Münzspeicher > Getränkepreis Euro => springe
		breq	erniedriegeMuenzspeicher	; Münzspeicher = Getränkepreis Euro => springe
		rjmp	a				; Münzspeicher < Getränkepreis Euro


; Erniedrigt den Münzspeicher
; Parameter:	R16: Münzspeicher
;		R17: Getränkepreis 
erniedriegeMuenzspeicher:
		sub	R16,	R17			; Münzspeicher - Getränkepreis
		sts	MS,	R16			; Speicher Münzspeicher
		call	warenausgabe			; Parameter ist immer noch in R18 gespeichert
		rjmp	a

; -- Unterprogramme

; Unterprogramm "eingangserfassung"
; Parameter:	R16: Maske zu Erkennung des Eingangs
; Rückgabe:	Z-Flag (Z=0 Eingang war gesetzt, sonst Z=1)
eingangserfassung:
		push	R16				; Register sichern
		push	R17

		in	R17,	PINB			; PIN B einlesen
		and	R17,	R16
		breq	endEinfangserfassung		; Wenn Taste NICHT gedrückt wurde => Springe zum Ende
tasteIstGedrueckt:					; Taste wurde gedrückt ...
		in	R17,	PINB			; ... solange warten, bis Taste nicht mehr gedürkt wird
		and	R17,	R16
		brne	tasteIstGedrueckt		; Ist Taste immer noch gedrückt => wiederhole

		and	R16,	R16			; Z=0 setzen, da Taste gedrückt wurde (Rückgabeparameter)

endEinfangserfassung:
		pop	R17
		pop	R16				; Register wiederherstellen
		ret

; Unterprogramm "warenausgabe"
; Parameter: R18: Auswurfmakse
warenausgabe:
		push	R16				; Register sichern
		push	R17
		push	R18

		out	PORTD,	R18			; Getränk ausgeben
		ldi	R17,	20			; Schleifenzähler für "callWarte"
		ldi	R16,	0xFF			; Parameter für Unterprogramm "warte"
callWarte:						; 0,5 Sekunden = 500.000 Mikrosekunden (us)
		call	warte				; 	255 (0xFF) * 100us = 25.500us pro Aufruf "warte"
		dec	R17				;	500.000us / 25.500us = 20 (0x14) Aufrufe
		brne	callWarte			; Schleifenzähler != 0 => wiederhole

		ldi	R16,	0x00
		out	PORTD,	R16			; Ausgabe wieder auf Null setzen

		lds	R16,	MS			; Parameter für Unterprogramm "geldrueckgabe"
		call	geldrueckgabe
		ldi	R16,	0x00
		sts	MS,	R16			; Lösche Münzspeicher nach Geldrückgabe

		pop	R18
		pop	R17
		pop	R16				; Register wiederherstellen
		ret

; Ende benutzerdefinierter Programmteil

; Vorgegebenes Unterprogramm "geldrueckgabe"
; Parameter:	R16: Münzspeicher
geldrueckgabe:
		nop
		ret

; Vorgegebenes Unterprogramm "warte":
; Routine zur Verzoegerung des Programmablaufs
; Parameter: R16: Verzoegerungszeit in 100us
warte:		push	R16				; Register sichern
		push	R17
warte1:		ldi	R17,	32
		nop
warte2:		dec	R17				; innere Schleife (Laufzeit 100us)
		brne	warte2				; die dann R16 mal abgearbeitet wird
		dec		R16
		brne	warte1
		pop		R17
		pop		R16			; Register wiederherstellen
		ret