; file	mt.asm
;
; auth:	Raphael Holzer, Dr.
;	Swiss Federal Insitute of Technology
; 	1015 Lausanne, Switzerland
;
; date:	17 April 2002

; this program is testing some of the functions of the Olimex AVR-MT board


; =================
;	definitions
; =================

.include "2313def.inc"

; === constant definitions ===
; PORT B
.equ	BUZZER	= 4
.equ	BUTTON1	= 5
.equ	BUTTON2 = 6
.equ	BUTTON3	= 7

; PORTD
.equ	TX	= 0
.equ	RX	= 1
.equ	DALLAS	= 2
.equ	RELAY	= 3
.equ	RS	= 4
.equ	FREQ	= 5
.equ	E	= 6

.set	clock	= 10000000

; === register definitions ===
.def	w	= r16		; working register
.def	_w	= r17		; working register for interrupts
.def	a0	= r18		; argument for subroutines

; ================== 
; 	main program
; ==================

reset:	ldi	w, RAMEND	; load stack pointer SP
	out	SPL,w		
	sbi	DDRD,RELAY	; RELAY = output
	rcall	lcd_init	; initialize LCD module
	rcall	beep		; short beep
	rjmp	main

str:	.db	"bc ",0		; ASCII string constant
	
main:	ldi	a0,'a'		; display a single character
	rcall	lcd_putc
	
	ldi	zl, low(str*2)	; load address of "string" into z
	ldi	zh,high(str*2)
	rcall	lcd_puts	; display "string"	
	
	rcall	lcd_putsi	; display "string" immediate
.db	"123",0	; 

loop:	sbi	PORTD,RELAY	; turn on relay and LED
	rcall	wait_500ms
	cbi	PORTD,RELAY	; turn off relay and LED
	rcall	wait_500ms
	rjmp	loop		; infinite loop


; ============
; 	buzzer
; ============

beep:	ldi	a0,250		; load loop counter
	sbi	DDRB,BUZZER	; make output
	sbi	PORTB,BUZZER	; buzzer high
	rcall	wait_1000us
	cbi	PORTB,BUZZER	; buzzer low
	rcall	wait_1000us
	dec	a0		; decrement loop count
	brne	beep+2		; loop back if not zero
	ret		

	
; ==================
; 	LCD routines
; ==================

lcd_init:
	sbi	DDRD,E		; make E output
	sbi	DDRD,RS		; make RS output
	in	w,DDRB
	ori	w,0x0f
	out	DDRB,w		; make LCD data output

	cbi	PORTD,RS	; address IR
	ldi	w,100
	rcall	wait_ms		; wait 100ms
	
	ldi	a0, 0b0011	; 8-bit interface
	rcall	lcd_put_nibble	
	ldi	w,100
	rcall	wait_ms		; wait 100ms

	ldi	a0, 0b0011	; 8-bit interface
	rcall	lcd_put_nibble
	ldi	w,100
	rcall	wait_ms		; wait 100ms

	ldi	a0, 0b0010	; 4-bit interface
	rcall	lcd_put_nibble
	ldi	w,2
	rcall	wait_ms		; wait 2ms

; 	this displays 8chars ($00..$07) followed by 8chars ($40..$47)
	ldi	a0,0b00101000	; 4-bits, 2-line 5x8-dots
	rcall	lcd_ir

	ldi	a0,0b00001111	; disply=1, cursor=1, blink=1
	rcall	lcd_ir
		
	ldi	a0,0b00000001	; display clear
	rcall	lcd_ir	
	ldi	w,2
	rcall	wait_ms		; wait 2ms
	ret

lcd_ir:
; in	a0	data byte to write to IR
	cbi	PORTD,RS	; address IR
	rjmp	lcd_put_byte

lcd_putc:
; in	a0	character to put to LCD
	sbi	PORTD,RS	; address DR

lcd_put_byte:
; in	a0	data to put to LCD 4-bit interface
	push	a0		; temporary save a0
	swap	a0		; get high nibble (D4..D7)
	rcall	lcd_put_nibble	; put high nibble (D4..D7)
	pop	a0		; restore a0
				; put low nibble (D0..D3)
; (fall through)
lcd_put_nibble:
; in	a0 (lower nibble)

	in	w,PORTB		; read whole portB
	andi	a0,0x0f		; extract low nibble
	andi	w, 0xf0		; extract high nibble
	or	w, a0		; combine the two
	out	PORTB,w		; update portB
	
	sbi	PORTD,E		; pulse E=1
	ldi	w,100
	rcall	wait_us		; wait 100us
	cbi	PORTD,E		; pulse E=0
	ldi	w,100
	rcall	wait_us		; wait 100us
	ret


; === LCD put string ===

; 	displays the null-terminated "string" constant pointed by z

lcd_puts:
	lpm			; read character pointed by Z
	tst	r0		; test for 0
	breq	lcd_puts_done
	mov	a0,r0		
	rcall	lcd_putc	; display character
	adiw	zl,1		; increment character pointer
	rjmp	lcd_puts	; loop back
lcd_puts_done:
	ret


; === LCD put string immediate ===

; 	displays the null-terminated "string" constant 
; 	following immediately the function call "lcd_putsi"
	
lcd_putsi:
	pop	zh		; load "return address" into z
	pop	zl		; it's the begin of the "string"
	lsl	zl		; multiply z by 2
	rol	zh		; to transform word->byte pointer
	rcall	lcd_puts	; display the "string"
	adiw	zl,1		; increment z pointer
	lsr	zh		; divide z by 2
	ror	zl		; to transform byte->word pointer
	ijmp			; indirect jump to address after "string"


; =================== 
; 	wait routines
; ===================

wait_1000ms:			; wait 1000 miliseconds (ms)
	rcall	wait_500ms
wait_500ms:			; wait  500 miliseconds (ms)
	rcall	wait_250ms
wait_250ms:			; wait  250 miliseconds (ms)
	ldi	w,250
	rcall	wait_ms
	ret
wait_ms:			; wait  "w" miliseconds (ms)
	push	w
	rcall	wait_1000us
	pop	w
	dec	w
	brne	PC-4
	ret

wait_1000us:			; wait 1000 microseconds (us)
	rcall	wait_500us
wait_500us:			; wait  500 microseconds (us)
	rcall	wait_250us
wait_250us:			; wait  250 microseconds (us)
	ldi	w,249
	rcall	wait_us
	ret
wait_us:			; wait  "w" microseconds (us)
; in	w
	dec	w
	rcall	wait7
	brne	PC-2
	ret

wait7:	ret			; wait 7 cycles