; Ryan Castonia
; Dec 09
; 
; basestation code for weightlifting power meter.  See http://fab.cba.mit.edu/classes/MIT/863.09/people/ryan/index.htm.
;
.include "tn44def.inc"
;
; definitions
;
; LCD pins
.equ DB7 = PA0
.equ DB6 = PA1
.equ DB5 = PA2
.equ DB4 = PA3
.equ E = PA4
.equ RS = PA5

;button pins
.equ buttonPin1 = PB0
.equ buttonPin2 = PB1

;comm pin
.equ commPin = PB2

;
; registers
;
.def lcdbyte = R16
.def rxbyte = R17
.def temp = R18
.def temp1 = R19
.def temp2 = R20
.def conversion = R21
.def velocity = R22
.def weight = R23

.def	drem8u	=r15		;remainder
.def	dres8u	=r19		;result
.def	dd8u	=r19		;dividend
.def	dv8u	=r20		;divisor
.def	dcnt8u	=r24		;loop counter

.def	mc8u	=r16		;multiplicand
.def	mp8u	=r24		;multiplier
.def	m8uL	=r24		;result Low byte
.def	m8uH	=r25		;result High byte
.def	mcnt8u	=r26		;loop counter

.def	drem16uL=r14		; remainder
.def	drem16uH=r15		; "
.def	dres16uL=r24		; result
.def	dres16uH=r25		; "
.def	dd16uL	=r24		; dividend
.def	dd16uH	=r25		; "
.def	dv16uL	=r19		; divisor
.def	dv16uH	=r20		; "
.def	dcnt16u	=r16		;counter


.macro printreg ;prints register to LCD

	mov dd8u, @0; dividend
	ldi	dv8u, 100 ; divisor
	rcall div8u

;	clr lcdbyte
	mov lcdbyte, dres8u ; result
	add lcdbyte, conversion
	rcall lcd_putchar

	mov dd8u, drem8u ; remainder into dividend
	ldi	dv8u, 10 ; divisor
	rcall div8u

;	clr lcdbyte
	mov lcdbyte, dres8u ;result
	add lcdbyte, conversion
	rcall lcd_putchar

;	clr lcdbyte
	mov lcdbyte, drem8u ; remainder
	add lcdbyte, conversion
	rcall lcd_putchar

.endMacro

.macro printreg16 ;prints 16-bit register to LCD (@0=low @1=high)

; assumes number is <1000

	mov dd16uL, @0
	mov dd16uH, @1
	ldi dv16uL, 100
	ldi dv16uH, 0
	rcall div16u
	
	clr lcdbyte
	mov lcdbyte, dres16uL ; result
	add lcdbyte, conversion
	rcall lcd_putchar

    mov dd16uL, drem16uL ; remainder into dividend
	mov dd16uH, drem16uH
	ldi dv16uL, 10
	ldi dv16uH, 0
	rcall div16u

	clr lcdbyte
	mov lcdbyte, dres16uL ;result
	add lcdbyte, conversion
	rcall lcd_putchar

	clr lcdbyte
	mov lcdbyte, drem16uL ; remainder
	add lcdbyte, conversion
	rcall lcd_putchar

.endMacro

;
; code segment
;		
.cseg
.org 0
rjmp reset

.org 8 ; timer/counter1 overflow
  
  
   rcall clear

   ldi zl,low(ready*2)
   ldi zh,high(ready*2)
   rcall lcd_print

; button1 if ready to receive, button2 to edit the input weight
   waitloop:
   sbis PINB, buttonPin1
   rjmp receive
   sbic PINB, buttonPin2
   rjmp waitloop
   rjmp main

;get the velocity from the lightsensor
receive:
 rcall clear

;print labels
   ldi zl,low(labels*2)
   ldi zh,high(labels*2)
   rcall lcd_print

; move to second line of display
   ldi lcdbyte, (1 << DB7) + (1 << DB6)
   rcall lcd_putcmd
   clr lcdbyte
   rcall lcd_putcmd

cbi PINB, commPin

;get the velocity
rcall getchar
	; needs to be multiplied by 2 to be in cm/sec
	mov mc8u, rxbyte
	ldi mp8u, 2
	rcall mpy8u
	mov velocity, m8uL

;calculate power and print it
rcall power
   
   ldi zl,low(watts*2)
   ldi zh,high(watts*2)
   rcall lcd_print

;print velocity in m/s         
mov dd8u, velocity ; dividend
ldi	dv8u, 100 ; divisor
rcall div8u

clr lcdbyte
mov lcdbyte, dres8u ; result
add lcdbyte, conversion
rcall lcd_putchar

clr lcdbyte
ldi lcdbyte, 46 ; prints a decimal point
rcall lcd_putchar

mov dd8u, drem8u ; remainder into dividend
ldi	dv8u, 10 ; divisor
rcall div8u

clr lcdbyte
mov lcdbyte, dres8u ;result
add lcdbyte, conversion
rcall lcd_putchar

clr lcdbyte
mov lcdbyte, drem8u ; remainder
add lcdbyte, conversion
rcall lcd_putchar

   ldi zl,low(vel*2)
   ldi zh,high(vel*2)
   rcall lcd_print

waitloop2:
sbis PINB, buttonPin1
rjmp receive
sbic PINB, buttonPin2
rjmp waitloop2
rjmp main


;;  INTERRUPT COMPLETE

power:

	mov mc8u, rxbyte
	mov mp8u, weight
	rcall mpy8u

	mov dd16uL, m8uL
	mov dd16uH, m8uH
	ldi dv16uL, 22   ; constant needed to get power in correct units (watts)
	ldi dv16uH, 0
	rcall div16u

	printreg16 dres16uL, dres16uH

ret

; half_bit_delay
; serial half bit delay
;
half_bit_delay:
   ldi temp, 100
   half_bit_delay_loop:
      dec temp
      brne half_bit_delay_loop
   ret
;
; getchar
; assumes no line driver (doesn't invert bits)
;
getchar:
   ldi temp1, 9; 8 data + 1 stop bit
   getchar1:
      sbis PINB, commPin; wait for start bit
      rjmp getchar1
   rcall half_bit_delay; delay to middle of bit
   getchar2:
      rcall half_bit_delay; bit delay
      rcall half_bit_delay; "
      clc; clear carry
      sbis PINB, commPin; if RX pin high skip
         sec; otherwise set carry
      dec temp1
      breq getchar3; return if all bits read
      ror rxbyte; otherwise shift bit into receive byte
      rjmp getchar2; go get next bit
   getchar3:
      ret


;
; lcd_delay
; delay between commands
;
.equ lcd_delay_time = 50 ; this can be changed
lcd_delay:
   ldi temp, lcd_delay_time    
   lcd_delay1:                
      ldi temp1, lcd_delay_time
      lcd_delay2:
         dec temp1
         brne lcd_delay2
      dec temp
      brne lcd_delay1
   ret

;
; strings to print
;
start_line:
   .db "Enter Weight-lbs",0
labels:
   .db "Power   MaxVel",0
lbs:
   .db " lbs",0
watts:
   .db "w    ",0
vel:
   .db "m/s",0
ready:
   .db "Ready?",0
waiting:
   .db "Waiting...",0

	  
startloop:	 
      ;go to zero position
      clr lcdbyte
      rcall lcd_putcmd
      ldi lcdbyte, (1 << DB5)
      rcall lcd_putcmd      ;
      ; print first line      ;
      ldi zl,low(start_line*2)
      ldi zh,high(start_line*2)
      rcall lcd_print  
  		
		;initialize values (can change weight if needed)
	    ldi conversion, 48
    	ldi temp, 0
		ldi weight, 135
      ;
      ret

;
; button1 to increment weight and prints the updated value
;
button1:

inc weight
rcall clear
printreg weight
      ldi zl,low(lbs*2)
      ldi zh,high(lbs*2)
      rcall lcd_print

ret


; button2 to decrement weight and print the updated value
;
button2:

sei
dec weight
rcall clear
printreg weight
      ldi zl,low(lbs*2)
      ldi zh,high(lbs*2)
      rcall lcd_print
ret
;
; reset routine
;
reset:
   ;
   ; set stack pointer to top of RAM
   ;
   ldi temp, high(RAMEND)
   out SPH, temp
   ldi temp, low(RAMEND)
   out SPL, temp
   ;
   ; init I/O pins
   ;
   cbi PORTA, DB7
   sbi DDRA, DB7
   cbi PORTA, DB6
   sbi DDRA, DB6
   cbi PORTA, DB5
   sbi DDRA, DB5
   cbi PORTA, DB4
   sbi DDRA, DB4
   cbi PORTA, E
   sbi DDRA, E
   cbi PORTA, RS
   sbi DDRA, RS

; init button pins and turn on pull-up resistors
	cbi DDRB, buttonPin1
		sbi PORTB, buttonPin1
	cbi DDRB, buttonPin2
		sbi PORTB, buttonPin2

; init comm pin, no pull-up resistor
 	cbi DDRB, commPin
		cbi PORTB, commPin

; set conversion to 1 so that startloop runs in the main loop
    ldi conversion, 1
   ;
   ; init LCD
   ;
   rcall lcd_init

   ; allow timer/counter interrupt
    sei
	ldi temp, (1 << TOIE1)
	out TIMSK1, temp
 
  ; set up 16bit counter1 with clock/256
   ldi temp1, (1 << CS12) | (0 << CS11) | (0 << CS10)
   out TCCR1B, temp1

   ;
   ; main loop
   ;
   main:
		sbrc conversion, 0 ; conversion starts at 0 and then becomes 48 after the startloop runs one time
		rcall startloop
   		
		;look for buttons being pressed
		sbis PINB, buttonPin1
		rcall button1
		sbis PINB, buttonPin2
		rcall button2
		rjmp main
;
; lcd_putchar
; put character in lcdbyte
;
lcd_putchar:
   ; set RS for data 
   sbi PORTA, RS
   ; output high nibble
   cbi PORTA, DB7
   sbrc lcdbyte, 7
      sbi PORTA, DB7
   cbi PORTA, DB6
   sbrc lcdbyte, 6
      sbi PORTA, DB6
   cbi PORTA, DB5
   sbrc lcdbyte, 5
      sbi PORTA, DB5
   cbi PORTA, DB4
   sbrc lcdbyte, 4
      sbi PORTA, DB4
   ; strobe E
   nop
   sbi PORTA, E
   nop
   cbi PORTA, E
   ; wait
   rcall lcd_delay
   ; output low nibble
   cbi PORTA, DB7
   sbrc lcdbyte, 3
      sbi PORTA, DB7
   cbi PORTA, DB6
   sbrc lcdbyte, 2
      sbi PORTA, DB6
   cbi PORTA, DB5
   sbrc lcdbyte, 1
      sbi PORTA, DB5
   cbi PORTA, DB4
   sbrc lcdbyte, 0
      sbi PORTA, DB4
   ; strobe E
   nop
   sbi PORTA, E
   nop
   cbi PORTA, E
   ; wait and return
   rcall lcd_delay ; can be shorter
   ret
;
; lcd_putcmd
; put command in lcdbyte
;
lcd_putcmd:
   ; clear RS for command
   cbi PORTA, RS
   ; output command bits
   out PORTA, lcdbyte
   ; strobe E
   nop
   sbi PORTA, E
   nop
   cbi PORTA, E
   ; wait and return
   rcall lcd_delay
   ret
;
; lcd_init
; initialize the LCD
;
lcd_init:
   ; power-up delay
   rcall lcd_delay
   ; initialization sequence
   ldi lcdbyte, (1 << DB5) + (1 << DB4)
   rcall lcd_putcmd
   ldi lcdbyte, (1 << DB5) + (1 << DB4)
   rcall lcd_putcmd
   ldi lcdbyte, (1 << DB5) + (1 << DB4)
   rcall lcd_putcmd
   ; 4-bit interface
   ldi lcdbyte, (1 << DB5)
   rcall lcd_putcmd
   ; two lines, 5x7 font
   ldi lcdbyte, (1 << DB5)
   rcall lcd_putcmd
   ldi lcdbyte, (1 << DB7)
   rcall lcd_putcmd
   ; display on
   clr lcdbyte
   rcall lcd_putcmd
   ldi lcdbyte, (1 << DB7) + (1 << DB6) + (1 << DB5)
   rcall lcd_putcmd
   ; entry mode
   clr lcdbyte
   rcall lcd_putcmd
   ldi lcdbyte, (1 << DB6) + (1 << DB5)
   rcall lcd_putcmd
   ret
;
; lcd_print
; print a null-terminated string
;
lcd_print:
   lcd_print_loop:
      lpm
      mov lcdbyte, R0
      cpi lcdbyte, 0
         breq return
      rcall lcd_putchar
      adiw zl, 1
      rjmp lcd_print_loop
   return:
      ret

;
; clear display and move to zero position
;
      
clear:	  
	  clr lcdbyte
      rcall lcd_putcmd
      ldi lcdbyte, (1 << DB4)
      rcall lcd_putcmd

      clr lcdbyte
      rcall lcd_putcmd
      ldi lcdbyte, (1 << DB5)
      rcall lcd_putcmd
ret


;
; 8x8 unsigned division
;
div8u:	
    sub	drem8u,drem8u	;clear remainder and carry
	ldi	dcnt8u,9	;init loop counter
d8u_1:	rol	dd8u		;shift left dividend
	dec	dcnt8u		;decrement counter
	brne	d8u_2		;if done
	ret			;    return
d8u_2:	rol	drem8u		;shift dividend into remainder
	sub	drem8u,dv8u	;remainder = remainder - divisor
	brcc	d8u_3		;if result negative
	add	drem8u,dv8u	;    restore remainder
	clc			;    clear carry to be shifted into result
	rjmp	d8u_1		;else
d8u_3:	sec			;    set carry to be shifted into result
	rjmp	d8u_1

;
; 8x8 unsigned multiplication
;
mpy8u:	clr	m8uH		;clear result High byte
	ldi	mcnt8u,8	;init loop counter
	lsr	mp8u		;rotate multiplier
	
m8u_1:	brcc	m8u_2		;carry set 
	add 	m8uH,mc8u	;   add multiplicand to result High byte
m8u_2:	ror	m8uH		;rotate right result High byte
	ror	m8uL		;rotate right result L byte and multiplier
	dec	mcnt8u		;decrement loop counter
	brne	m8u_1		;if not done, loop more
	ret

;
; 16x16 unsigned division
;
div16u:	clr	drem16uL	;clear remainder Low byte
	sub	drem16uH,drem16uH;clear remainder High byte and carry
	ldi	dcnt16u,17	;init loop counter
d16u_1:	rol	dd16uL		;shift left dividend
	rol	dd16uH
	dec	dcnt16u		;decrement counter
	brne	d16u_2		;if done
	ret			;    return
d16u_2:	rol	drem16uL	;shift dividend into remainder
	rol	drem16uH
	sub	drem16uL,dv16uL	;remainder = remainder - divisor
	sbc	drem16uH,dv16uH	;
	brcc	d16u_3		;if result negative
	add	drem16uL,dv16uL	;    restore remainder
	adc	drem16uH,dv16uH
	clc			;    clear carry to be shifted into result
	rjmp	d16u_1		;else
d16u_3:	sec			;    set carry to be shifted into result
	rjmp	d16u_1
