;****************************************************************************************
;*	Am Beispiel des AT-Tiny	2313 wird ein 16-Segment Display in Betrieb genommen.		*
;*	Bauanleitung bei http://www.avr-projekte.de											*
;*	Autor: Jürgen Woetzel																*
;*	Version vom 16.08.09																*
;****************************************************************************************

.include "tn2313def.inc"
.device attiny2313

;-------------------------------------------------------------------------------------
.def	S		=r5		;SRegister
.def	temp	=r16	;Trash
.def	temp1	=r17	;
.def	temp2	=r18	;
.def	itemp	=r19	;Bit-Zähler für Digits (Multiplex 16Seg)
.def	tick	=r25	;Zähler für div. Timings
;-------------------------------------------------------------------------------------
;Schieberegister 74HC595 für 16Seg.-Display

.equ S_Port		= PORTB	;Port des 74HC595
.equ S_DRR		= DDRB	;Datenrichtung
.equ RClock		= 2		;74HC595 Schieberegister -> Latch (steigende Flanke)
.equ Clock		= 1		;74HC595 Takt (steigende Flanke)
.equ SerD		= 0		;74HC595 Daten
;-------------------------------------------------------------------------------------
.cseg
.org 0
		rjmp	Reset
;-------------------------------------------------------------------------------------
;ISR

.org OVF0addr
		in		S,sreg				;Verschiedene Register sichern
		push	temp				
		push	temp1
		push	temp2
		push	ZH
		push	ZL
		inc		tick				;Variable für Warteschleifen
		inc		itemp				;Digitzähler +1, zählt immer nur von 0..3 ...
		cbr		itemp,0b11111100	;... und wird als Index auf das aktive Digit benutzt.

;************************************************************************************************
;*	Multiplexen der 16Segment Anzeige															*
;*	alle 2 ms wird eine der 4 Anzeigen (gem. Anode, Treiber low Aktiv) eingeschaltet (welche 	*
;*	steht in itemp).																			*
;*	Der entsprechende ASC-Code steht im SRam Dig1..4. Die ISR holt den zugehörenden ASC-Code	*
;*	und ermittelt damit den passenden 16Seg.-Code den er in die Schieberegister taktet.			*
;*  Im Mainprg. wird nur noch der ASC-Code nach dig1..4 geschrieben, den Rest macht die ISR. 	*
;************************************************************************************************
;Erstes Byte in den 595 shiften. Bit0..3 =Digit(0=Digit ein), Bit7 =0 für DP ein.

		mov		temp1,itemp			
		ser		temp				;temp = -1
		clc							;Null an die richtige Pos. schieben
nxtr:	rol		temp				;Anhand von itemp das richtige..
		dec		temp1				;Digit schalten
		brpl	nxtr

;>> in temp steht jetzt für das richtige Digit eine Null

;Wenn nötig, DP (Dezimalpunkt) einschalten
		mov		temp1,temp
		com		temp1
		lds		temp2,dpmem			;ein 0Bit im unteren Nibble schaltet den DP
;		com		temp2				;ein 1Bit schaltet DP wenn nicht auskommentiert
		and		temp1,temp2
		brne	noDP
		cbr		temp,$80			;DP ein
noDP:	rcall	HC595				;Erstes Byte in den HC595 takten
		ldi		YH,high(Dig1)		;Zeiger auf Display SRAM
		ldi		YL,low(Dig1)		;Y zeigt nun auf 1.Digit
		add		YL,itemp			;aktuelles Digit addieren
		ld		temp,Y				;in temp steht nun der ASCii Code des aktuellen Digit
		lsl		temp				;;mal 2, pro Char sind 2 Bytes in der ASCTab.
		ldi		ZH,high(ASCTab<<1)	;Zeiger auf ASCTab
		ldi		ZL,low(ASCTab<<1)
		add		ZL,temp				;+ offset
		lpm		temp,Z+				;1. Bitmuster -> Temp
		rcall	hc595				;Temp raustakten
		lpm		temp,Z				;2.Bitmuster
		rcall	hc595				;und raus
		cbi   S_Port,RClock			;Palim Palem,Steigende Flanke an RClock..
		sbi   S_Port,RClock			;..schreibt das Schieberegister in die Latches

TIM0_OVF_exit:						;ISR Ende
		pop		ZL					;Register Zurücklesen
		pop		ZH
		pop		temp2
		pop		temp1
		pop		temp
		out		sreg,S
		reti						

;----------------------------------------------------------------------
;Taktet das Byte temp in das Schieberegister, vllt. noch als Makro machen,spart 3* rcall+ret

HC595:	sec							;Carry für Schleifenende
		rol		temp				;erstes Datenbit ->C, C(Schleifenende) -> Temp (Bit0=1)
		rjmp	shift				;Erstes Datenbit an Port legen
nxbit:	lsl		temp				;Datenbit raus, Null rein
		breq	ex1					;das letzte Carry war meins und geht nichtmehr raus
shift:	brcc	bit0				;ist Datenbit =0?
		sbi		S_Port,SerD			;nein, es ist 1
		rjmp	bit1				;an Clock Läuten
bit0:	cbi		S_Port,SerD			;Datenbit =0
bit1:	cbi		S_Port,Clock		;Steigende Flanke an Clock..
 		sbi		S_Port,Clock		;.. schiebt das Bit in den HC595
 		rjmp	nxbit				;Das ganze nochmal
ex1:	ret	

;--------------------------------------------------------------------

;--------------------------------------------------------------------
;Init


Reset:	ldi  	temp,RAMEND			;Stack Init
		out		SPL,temp			;Wie immer ans Ende des SRAM
									;Timer Init
		ldi		temp,1<<CS00|1<<CS01;Timer/Counter0, Vorteiler = Systemtakt/64
		out		TCCR0,temp			;alle 256*64 Taktzyklen (2,048 mS @ 8MHz) ein Timerinterrupt	
		ldi		temp,1<<TOIE0		;Timer0 Einschalten
		out		TIMSK,temp
									;Ports Init
		ldi		temp,1<<SerD|1<<Clock|1<<RClock
		out		S_DRR,temp			;Die 3 Signale zum HC595 als Ausgang, der Rest ist Eingang
		sei							;ISR Los ...

		ldi		temp,0b1111			;DP aus,
		sts		DPmem,temp
;--------------------------------------------------------------------
;Schreibt den Text "DEMO" auf das Display und Wartet ca 5 Sekunden
;
main:	ldi		temp,'d'
		sts		Dig1,temp
		ldi		temp,'e'
		sts		Dig2,temp
		ldi		temp,'m'
		sts		Dig3,temp
		ldi		temp,'o'
		sts		Dig4,temp
		rcall	wait5
;--------------------------------------------------------------------
;Schreibt "1234" auf das Display
;
		ldi		temp,1
		sts		Dig1,temp
		inc		temp
		sts		Dig2,temp
		inc		temp
		sts		Dig3,temp
		inc		temp
		sts		Dig4,temp
;--------------------------------------------------------------------
;Lässt den DP 10 mal Blinken
;
		ldi 	temp1,10
blink:	ldi		temp,0b1101			;DP an die 2.stelle
		sts		DPmem,temp			;DP Ein
		rcall	wait05				;Zeit verplempern
		ldi		temp,0b1111			;DP Aus
		sts		DPmem,temp
		rcall	wait05				;kurz warten
		dec		temp1				;das ganze 10 mal
		brne	blink

;---------------------------------------------------------------------
;Text Scrollen

		ldi		ZH,high(Text<<1)	;Zeiger auf den String
		ldi		ZL,low(Text<<1)
txloop:	lpm		temp,Z+				;Die ersten 4 Chars aufs Display
		sts		Dig1,temp
		lpm		temp,Z+
		sts		Dig2,temp
		lpm		temp,Z+
		sts		Dig3,temp
		lpm		temp,Z+
		tst		temp				;Stringende? (lpm setzt das Z-Flag nicht)
		breq	main				;Wenn ja Sprung
		sts		Dig4,temp
		sbiw	Z,3					;3 vom Stringzeiger abziehen (es wurde ja um 4 erhöht)
		rcall	waitx				;ne 1/4 Sek. warten
		rjmp	txloop				;die nächsten 4 Chars


;-------------------------------------------------------------------------------------
;Warteschleifen

wait05:	rcall	waitx				;0,5 Sekunde vertrödeln

waitx:	clr		tick				;Der Zähler wird in der ISR alle 2,048 mS erhöht.
waitx1:	cpi		tick,128			;Es werden (128*2,048) 0,262 Sekunden vertrödelt
		brne	waitx1				;Um die Scrollgeschwindigkeit zu ändern kanm man mit dem Wert 128 spielen
		ret

wait5:	ldi		temp,20				;5 Sekunden vertrödeln
wait5_2:rcall	waitx
		dec		temp
		brne	wait5_2
		ret		

;-------------------------------------------------------------------------------------
;Text (Null-Terminiert) der Gescrollt werden soll

Text:	.db		"   ***  www.avr-projekte.de  ***  ein tiny2313 schiebt diesen text durch das 16-segment-display  *** Sonderzeichen §$%&/()=?><"
		.db		16,17,18,19,20,21,22,23,24,"      ",0		

;------------------------------------------------------------------------------------------
;16 Segment ASCTabelle, das erste Byte enthält sie Segmente K..U, Byte2 A..F
;Low aktiv! 0=Segment ein 1=Segment aus
;Da man die Codes 0..15 eh nicht Darstellen kann habe ich 0..9 und A..F in die
;ersten 16 Tabellenpos. geschrieben. Das spart etwas Umrechnungszeit bei Dez - Hex Routinen
;------------------------------------------------------------------------------------------

.org $300	;An gerade Adresse setzen, das spart der ISR Zeit da ZH nicht geprüft werden muss

ASCTab:

.db	0b11111111,0b00000000	;0
.db	0b11011111,0b11001111	;1
.db	0b11101110,0b00010001	;2
.db	0b11101111,0b00000011	;3
.db	0b11101110,0b11001110	;4
.db	0b11101110,0b00100010	;5
.db	0b11101110,0b00100000	;6
.db	0b11011011,0b00111111	;7
.db	0b11101110,0b00000000	;8
.db	0b11101110,0b00000010	;9
.db	0b11101110,0b00001100	;A
.db	0b10101011,0b00000011	;B
.db	0b11111111,0b00110000	;C
.db	0b10111011,0b00000011	;D
.db	0b11101110,0b00110000	;E
.db	0b11111110,0b00111100	;F
;meine Sonderzeichen
.db	0b10111011,0b01011000	;0 Grad
.db	0b11111010,0b01011010	;5 Grad
.db	0b10101111,0b10011111	;Grad
.db	0b11111111,0b11110011	;Cursor
.db	0b11110011,0b11101101	;IN (für Innentemperatur)
.db	0b11111010,0b111111001	;OUT (für Aussen)
.db	0b01110111,0b00000000	;0 mit Schrägstrich
.db	0b01111101,0b11111100	;$18 Rechts
.db	0b11010111,0b11001111	;$19 Links

;ASCii
.org	ASCTab+32
.db	0b11111111,0b11111111	;Space
.db	0b11111111,0b11001111	;!
.db	0b10111111,0b11111110	;"
.db	0b10101010,0b11000011	;#
.db	0b10101010,0b00100010	;$
.db	0b10001000,0b01100110	;%
.db	0b10011001,0b10100011	;&
.db	0b11011111,0b11111111	;'
.db	0b10111011,0b10110111	;(
.db	0b10111011,0b01111011	;)
.db	0b00000000,0b11111111	;*
.db	0b10101010,0b11111111	;+
.db	0b11111101,0b11111111	;,
.db	0b11101110,0b11111111	;-
.db	0b11111010,0b11111001	;.
.db	0b11011101,0b11111111	;/
.db	0b11111111,0b00000000	;0
.db	0b11011111,0b11001111	;1
.db	0b11101110,0b00010001	;2
.db	0b11101111,0b00000011	;3
.db	0b11101110,0b11001110	;4
.db	0b11101110,0b00100010	;5
.db	0b11101110,0b00100000	;6
.db	0b11011011,0b00111111	;7
.db	0b11101110,0b00000000	;8
.db	0b11101110,0b00000010	;9
.org	ASCTab+65			;ATM kein Bock auf Sonderzeichen
.db	0b11101110,0b00001100	;A
.db	0b10101011,0b00000011	;B
.db	0b11111111,0b00110000	;C
.db	0b10111011,0b00000011	;D
.db	0b11101110,0b00110000	;E
.db	0b11111110,0b00111100	;F
.db	0b11101111,0b00100000	;G
.db	0b11101110,0b11001100	;H
.db	0b10111011,0b11111111	;I
.db	0b11111111,0b10000001	;J
.db	0b11001110,0b11101100	;K
.db	0b11111111,0b11110000	;L
.db	0b01011111,0b11001100	;M
.db	0b01110111,0b11001100	;N
.db	0b11111111,0b00000000	;O
.db	0b11101110,0b00011100	;P
.db	0b11110111,0b00000000	;Q
.db	0b11100110,0b00011100	;R
.db	0b11101110,0b00100010	;S
.db	0b10111011,0b00111111	;T
.db	0b11111111,0b11000000	;U
.db	0b11011101,0b11111100	;V
.db	0b11110101,0b11001100	;W
.db	0b01010101,0b11111111	;X
.db	0b01011011,0b11111111	;Y
.db	0b11011101,0b00110011	;Z
.org	ASCTab+97
;Die Kleinbuchstaben will ich mir ATM net geben, also vorerst alles in Grossschrift

.db	0b11101110,0b00001100	;A
.db	0b10101011,0b00000011	;B
.db	0b11111111,0b00110000	;C
.db	0b10111011,0b00000011	;D
.db	0b11101110,0b00110000	;E
.db	0b11111110,0b00111100	;F
.db	0b11101111,0b00100000	;G
.db	0b11101110,0b11001100	;H
.db	0b10111011,0b11111111	;I
.db	0b11111111,0b10000001	;J
.db	0b11001110,0b11101100	;K
.db	0b11111111,0b11110000	;L
.db	0b01011111,0b11001100	;M
.db	0b01110111,0b11001100	;N
.db	0b11111111,0b00000000	;O
.db	0b11101110,0b00011100	;P
.db	0b11110111,0b00000000	;Q
.db	0b11100110,0b00011100	;R
.db	0b11101110,0b00100010	;S
.db	0b10111011,0b00111111	;T
.db	0b11111111,0b11000000	;U
.db	0b11011101,0b11111100	;V
.db	0b11110101,0b11001100	;W
.db	0b01010101,0b11111111	;X
.db	0b01011011,0b11111111	;Y
.db	0b11011101,0b00110011	;Z
;---------------------------------------------------------------------------------------

.dseg
.org	SRAM_START

Dig1:	.byte	1
Dig2:	.byte	1
Dig3:	.byte	1
Dig4:	.byte	1
DPmem:	.byte	1
