; 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