Full Version : Lam & Chiu Whack-a-Cap Game (ASM)
avr >>GAME & VIDEO PROJECTS >>Lam & Chiu Whack-a-Cap Game (ASM)


AVR_Admin- 04-22-2006
Whack-A-Cap: Miniature representation of a popular amusement game

by Gigi T Lam & Euborn Y Chiu

Introduction:

Our final project code calls for the implementation of an amusement game often bannered as "Test-Your-Strength" or less accurately (but more commonly) known as "Whack-a-Mole." Our machine is in essence a miniturized version of what can be found in most theme parks across the world. Its construction involves a spring load plastic cap to act as a surface for contact, a wooden mallet which the player uses to hit the plastic cap, and most importantly, a device, called an accelerometer, to measure the shock forces created by the impact of the mallet and the cap surface. A potential player would proceed to hit the plastic cap with the mallet provided, and depending on the level of force registered by the accelerator, a winning or losing result will be annouced visually through a liquid crystal display and light emitting diodes, as well as musically through the use of a speaker.


An 8535 development board was choosen for this project because we require an analog to digital conversion process to capture voltage values (as a function of g forces experienced) as they are registered by the accelerometer. Having decided on a chip to use for our project, we proceeded to the programming stage. Our approach was to design a state-machine with three states. The following paragraphs will explain the functions and reasoning behind each state.

Machine States:

Upon power up, our machine enters a welcome state. In this state, the following events occur: First, a series of tones are played through the speaker, and each note has an associated LED that lights up accordingly. The LCD displays a welcoming message, and the keypad is sensed for any button presses. At this point, the game operator has the option of setting game input sensitivity for a male or female player. A force of 49g and 40g is required to win in the male and female modes respectively.

Our program exits the welcome state when a keypad button is depressed, and proceed to the wait state where it loops until an acceptable input is received from the accelerometer. In this waiting state, a loop is used to monitor the output from the analog-to-digital conversion complete interrupt. We reject all motions of 20g or less to minimize noise, environmental vibrations, but most importantly mis-hits. As soon as a force greater than 20g is experienced, our program compares the value to the one set forth by the operator (i.e., male or female) and goes into a display state. In the display state, depending on results of the player's attempts, we either flash the LEDs rapidly (to indicate a win) or hold the LEDs at a certain position to represent relative effort (to indicate a loss). The display state does not end until the operator presses a soft-reset button, and when this happens we revert back to the welcome state.

Music:

Music is generated by creating square like waveforms to an output pin of the 8535 microprocessor. To create a note at, for example, 500Hz, we calculate that an output inversion each millisecond is needed. A output inversion at that rate produces a periodic signal of 2 milliseconds each cycle, which when fed to the speaker produces an audible note at 500 Hz. Using timer zero with a prescaler value of 64, we are able to create the range of sound desired by switching output values at each timer overflow interrupt. To obtain different notes we simply reloaded various values into TCNT0 at each overflow interrupt. This causes the interrupt to be called at different time frequencies. We then came up with a scheme to store the frequency and duration desired into memory. Each note is represented as a frequency-duration pair. For example, a value of (192,248) corresponds to a 500 Hz played for a quarter of a second. Using this convention, we programmed each note in our melody as frequency-duration pairs in memory. Whenever music is desired, timer zero is initialized, our machine then reads each value from memory sequentially, plays it for the duration required, and loads the next one. This process is repeated until a ending zero is reached. To acheive periods of silence (mute) in speaker output, it is decided that a value of (1,don't care) be used.

Frequency [Hz] Reload Value Duration Value for .25 Seconds

194 96 98
218 113 136
260 136 131
386 175 195
434 184 220
500 192 248
512 195 255

292 149 100
326 160 110
343 165 130

SILENCE 1 DONT CARE


Light Emitting Diodes:

Sychronized with the beats of music are sixteen light emitting diodes driven by two Radio Shack 74HCT138 demultiplexors. Seeing that the number of output pins on our microprocess are quickly being taken up by switches, LCD, accelerometer, and speaker, we see no way of driving sixteen LEDs without the use of a bus output to some sort of decoder. The Radio Shack decoder was choosen for its simplicity and ability to drive 20mA of current at each output pin. A pair of 74HCT138 takes output from pins b1 to b3 and lights up LEDs as required. Output to LEDs are always latched until the next timer zero overflow interrupt. This allows for easy sychronization with music when needed. If a LED is to maintain its value, then the same value can always be relatched into output register (LED).


Accelerometer:

The Analog Devices ADXL150AQC accelerometer is capable of recording shocks of up to 50g in both directions of one axis. The output of the accelerometer is dicated by the equation 2.5-(.038g) [Volts] where g is the force in g's experienced. We phyiscally mounted the accelerometer in such a way that the larger the g force, the small Vout becomes. In other words, a 49g impact will register as approximately 0.64 [Volts] whereas a 20g impact will cause 1.74 [Volts] at the output.

When input from the ADXL150AQC is needed, the ADC is allowed to cycle in free-run mode without noise cancelling, and the ADC complete interrupt is used to capture values from the ten-bit capture register. We used an Aref setting of 4.096 [Volts] so that, similiar to lab five, the least significant bit in the capture register accounts for about 4 [mV]. This becomes helpful when we scale the output value into a score. Since are mostly interesting in the range of activity between zero to 50g, we can simply translate the voltage received into a score between zero to one hundred by manipulating the ADC results by a factor of two.

Our accelerometer is mounted to the inside of a plastic cap with silicon rubber so that is aligned with the verticle axis. The plastic cap is then spring loaded with energy absorbing foam layers to help disperse excess energy as the cap is depressed with a mallet. The fact that our ADXL150AQC came in a surface mount package made it difficult to soldier but greatly simplified the mounting process by virtue of its small size.


Conclusion:

"Whack-A-Cap" is in effect a culumation of what we have learned this semester. It contains many bits and pieces from various labs, but we have improved on many things to make it a complete product as opposed to a congregation of modular code (that approach would have not worked anyhow). For example, we used the instruction lpm (something learned while studying LCD) to retrieve music material from memory, restructed the LCD code to use more relative branches and calls to save program memory, and used external circuitry like the decoder to ease internal programming requirments. We believe these improvements have made the final program possible in a very efficient, manageable way. The ADXL150AQC also works remarkably well, although with a better budget we would have opted for a version of larger range (perhaps on the scale of 250g) so that we may take harder hits before setting off a game-winner.

Link: http://instruct1.cit.cornell.edu/courses/e.../gigi/INDEX.HTM

CODE

;Gigi Lam
;Euborn Chiu
;EE476 Final Project, Sp99

.include "8535def.inc"

.device AT90S8535  ;specifies to the assembler which chip we are using

.def  AnaLo =r2
.def  AnaHi =r3
.def  reload    =r4  ;holds timer reload value
.def  duration  =r5  ;interrupt count for .25sec length per note
.def  charcnt   =r6  ;Char position on the display
.def  charcp    =r7  ;holds value to compare charcnt against (8)  
.def  b0    =r8
.def  b1    =r9
.def  b2    =r10
.def  winner    =r11;was last user a winner?
.def  Temp  =r16;temporary register
.def  iTemp =r17;interrupt temporary register
.def  save      =r18;SREG save location
.def  state     =r19;machine state for debounce subroutines
.def  compare   =r20;must exceed this value to win
;---LCD registers
.def  wreg  =r21;General use working register
.def  timeout   =r22;Timeout value passed to subroutine
.def  lcdstat   =r23;LCD busy/wait status
.def  longtime  =r24;Long timer for powerup
.def  chr   =r25;a variable character to print

.def  LED   =r26;LED lighting
.def  musicout  =r27;holds output to pin6
.def  Notenum   =r28;note number being played

;---equates
.equ  TCK1  =1
.equ  TCK8  =2
.equ  TCK64 =3  ;ck/64 prescalar
.equ  TCKSTOP   =0  ;ck stop
.equ  TEXF  =6  ;Timer/Counter runs from external falling edge
.equ  TEXR  =7  ;Timer/Counter runs from external rising edge
.equ  AZERO ='0'

.equ  lcdrs =PC6;LCD rs pin connected to PD6
.equ  lcdrw =PC5;LCD r/w pin connected to PD5
.equ  lcde  =PC4;LCD enable pin connected to PD4
.equ  true  =0xff
.equ  false =0x00

;---machine states
.equ  welcome   =0;state 0 =welcome display
.equ  waitforhit=1;state 1 =diplay for loser
.equ  display   =2;state 2 =wait for reset

.dseg
points: .byte   11      ;3 digit score + zero terminate

.cseg
.org $0000
       rjmp    RESET      ;reset entry vector
       reti            
       reti
       reti    
       reti
   reti
       reti  ;rjmp   T1match    ;timer1 matchA
       reti            
       rjmp    T1ovfl        ;timer1 overflow
       rjmp    T0Ovfl        ;timer0 overflow
       reti    
       reti    
       reti    
   reti
   rjmp    ADCdone        ;ADC done handler
   reti
   reti

;---messages for display
mnotes:
   .db 01,31,184,220,175,195,136,131,136,131,195,255,195,255,195,255,195,255
   .db 184,220,175,195,136,131,136,131,195,255,195,255,195,255,195,255
   .db 184,220,175,195,136,131,136,131,195,255,195,255,113,110,113,110
   .db 195,255,195,255,96,98,96,98,192,248,192,248,192,248,192,248
   .db 184,220,175,195,96,98,96,98,192,248,192,248,192,248,192,248
   .db 184,220,175,195,96,98,96,98,192,248,192,248,192,248,192,248
   .db 184,220,175,195,96,98,96,98,192,248,192,248,113,110,113,110
   .db 192,248,192,248,136,131,136,131,195,255,195,255,195,255,195,255
   .db 0x00
snotes:
   .db 136,80,149,100,160,110,165,130,175,155,184,170,192,180,0x00
fnotes:
   .db 195,230,192,180,184,170,175,155,165,130,160,110,149,100,136,80,0x00
dnotes:
   .db 240,255,240,255,240,255,240,255,240,255,01,31,240,255,240,255,240,255,240,255,240,255,01,31,0x00
welcome1:
   .db "Wack-a-Cap", 0x00
taunt:
   .db "Show Your Power!", 0x00
lost:
   .db "Sorry, Try Again", 0x00
won:
   .db "You're a Winner!", 0x00

;---reset vector
RESET:  cli
   ldi     temp, LOW(RAMEND)  ;setup stack pointer
   out     SPL, temp
   ldi     temp, HIGH(RAMEND)
   out     SPH, temp

   ldi temp, 0b00000101  ;T1OE on and OCI1EA on and T0OE on
   out TIMSK, temp
   ldi temp, TCK64
   out TCCR0, temp    ;set timer 0 clk / 64
   ldi temp, TCKSTOP
   out TCCR1B, temp      ;stop timer 1 (just in case)

   ldi temp, 0xFF    ;set port B output
   out DDRB, temp    ;bit0=music, bits4..1=LED encoded
   ldi temp, 0xFF
   out DDRC, temp    ;set port C output for LCD
   ldi temp, 0x00
   out DDRD, temp    ;set port D input for buttons

 ;---init LCD stuff
   clr notenum
   clr charcnt
   ldi temp, 8
   mov charcp, temp
   sei                        ;enable all interrupts  
                 ;power down the LCD
   ldi wreg, 0xff    ;LCD power connection
   out DDRC, wreg    ;power it down after reset
   ldi wreg, 0x00
   out PORTC,wreg
   ldi longtime,100      ;then Wait 1.5 second with LCD power off
   ldi timeout,0      ;Delay 15 mS
offwait:rcall   delay
   dec longtime
   brne    offwait
                 ;now power up LCD
   ldi wreg, 0xff
   out PORTC, wreg
   ldi longtime,100      ;Wait 1.5 second for LCD power up
   ldi timeout,0      ;Delay 15 mS  
puwait: rcall   delay
   dec longtime
   brne    puwait
                 ;hopefully by now the LCD has rebooted
main:   rcall   lcdinit        ;Initialize LCD module

softreset:
 ;---Init ADC stuff
   ldi temp, 0        
   out ADMUX, temp
   ldi temp, 0xFF  
   mov AnaLo, temp
   mov AnaHi, temp

 ;---init music stuff
   ldi ZH,high(mnotes*2)  ;first music note addr to Z-ptr
   ldi ZL,low(mnotes*2)    
   lpm            ;load first music note RELOAD value
   out TCNT0, r0      ;move value to timer 0 counter
   mov Reload, r0    ;save reload value
   adiw    ZL, 1
   lpm    
   mov duration, r0      ;load first music note duration value
   adiw    ZL, 1
   ldi musicout,0xFF      ;set music output to high

 ;---init LED stuff
   ldi LED, 0x00      ;disable all lights

   rcall   lcdclr        ;Clear LCD screen
   clr charcnt        ;zero the character count
   ldi ZH,high(welcome1*2);load first welcome message to Z pointer
   ldi ZL,low(welcome1*2)
   rcall   ZtoLCD        ;display msg

 ;---go on to welcome state
   ldi state, welcome      
   rjmp    _state

   clr winner

;---Welcome state:
;---Plays music and lights LED, waits for keypress
;---When keypress happens, goes to next state
_welcome:
   tst duration      ;test to see if we have overflowed duration
   brne    sensebut        
 ;---load next music note
   lpm            
   tst r0        ;Are we at last note? r0=0=yes
   brne    Noteok
   ldi ZH,high(mnotes*2)  ;first music note addr to Z-ptr
   ldi ZL,low(mnotes*2)    
   rjmp    sensebut        
Noteok:
   out TCNT0, r0      ;load next note values
   mov Reload, r0          
   adiw    ZL, 1
   lpm    
   mov duration, r0      ;load next duration value
   adiw    ZL, 1
 ;---load LED sequence accordingly
   ldi temp, 184
   cp  Reload, temp
   breq    _184
   ldi temp, 175
   cp  Reload, temp
   breq    _175
   ldi temp, 136
   cp  Reload, temp
   breq    _136
   ldi temp, 195
   cp  Reload, temp
   breq    _195
   ldi temp, 113
   cp  Reload, temp
   breq    _113
   ldi temp, 96
   cp  Reload, temp
   breq    _96
   ldi temp, 192
   cp  Reload, temp
   breq    _192

_184:   ldi LED, 0x00
   rjmp    sensebut
_175:   ldi LED, 0x01
   rjmp    sensebut
_136:   ldi LED, 0x04
   rjmp    sensebut
_195:   ldi LED, 0x07
   rjmp    sensebut
_113:   ldi LED, 0x03
   rjmp    sensebut
_96:    ldi LED, 0x02  
   rjmp    sensebut
_192:   ldi LED, 0x06
   rjmp    sensebut    

;---Check to see which machine state we are in
_state: cpi state, welcome
   breq    _welcome
   cpi state, waitforhit
   breq    _waitforhit
   cpi     state, display
   breq    _display
   rjmp    _state

sensebut:
   sbis    pind, 0
   rjmp    man
   sbis    pind, 1
   rjmp    woman
   rjmp    _state        ;no button pressed, remain in state
   
man:    ldi     compare, 0x15
   ldi state, waitforhit  
   rjmp    stopclk
woman:  ldi     compare, 0x18
   ldi state, waitforhit  
   rjmp    stopclk

stopclk:
 ;---button pressed, set up to exit state
   ldi temp, TCKSTOP
   out TCCR0, temp    ;stop timer
   ldi temp, 0b11101101  ;enable ADC, start conversion, free run, intr enabled
   out ADCSR, temp
 ;---exit state
   rjmp    _state

;---Wait for Hit state
;---Continuously senses ADC output
;---Advances only when ADC output is
_waitforhit:
                 ;moving values from ADC register
   ldi temp, 0x02
   cp  AnaHi, temp    ;compare with min value to disregard noise
   brsh    _waitforhit

   ldi temp, 0b01101101  ;disable ADC, leave everything else
   out ADCSR, temp

   ldi state, display
   rjmp    _state

;---Display State
;---Displays result of user's attempt
;---First Play Scale, then depending on ADC output value, play either winning or losing messages/music.
_display:
   ldi LED, 0xFF

   ldi temp, TCK64
   out TCCR0, temp    ;start timer 0 at clk/64

   ldi ZH,high(snotes*2)  ;first scale note addr to Z-ptr
   ldi ZL,low(snotes*2)    
playscale:
   lpm            ;load first scale note RELOAD value
   tst r0        ;if r0=0 then we are at the end of scale
   breq    donescale      ;we are done w/ this part then
   out TCNT0, r0      ;move value to timer 0 counter
   mov Reload, r0    ;save reload value
   adiw    ZL, 1
   lpm    
   mov duration, r0      ;load first scale note duration value
   adiw    ZL, 1
   inc LED        ;control LED lighting sequence
scaleloop:
   tst duration      ;test to see if we have overflowed duration
   brne    scaleloop      ;loop until we have played this note for entire duration
   rjmp    playscale      ;ready for next scale note

donescale:            ;done with scale here, ready for winning/losing msg
   lsr AnaHi
   ror AnaLo
   lsr AnaHi
   ror AnaLo
   lsr AnaHi
   ror AnaLo          ;div by 8

   cp  AnaLo, compare    ;lower result = bigger g force, player wins
   brlo    winmsg
   rjmp    losemsg

;---Winmsg: keeps top LED lit, plays ding-ding music
winmsg:
   ldi temp, 0xff  
   mov winner,temp    ;set winner bit
   add temp, AnaLo
   ldi LED, 0x07      ;light top LEDs
   ldi ZH,high(dnotes*2)  ;first ding-ding note addr to Z-ptr
   ldi ZL,low(dnotes*2)    
playding:
   lpm            ;load first ding note RELOAD value
   tst r0        ;if r0=0 then we are at the end of ding-ding sequence
   breq    doneding      ;we are done w/ this part then
   out TCNT0, r0      ;move value to timer 0 counter
   mov Reload, r0    ;save reload value
   adiw    ZL, 1
   lpm    
   mov duration, r0      ;load first dingding note duration value
   adiw    ZL, 1
dingloop:
   tst duration      ;cycle through all ding-ding notes
   brne    dingloop
   rjmp    playding
doneding:
   ldi ZH,high(won*2)    ;load winning message to Z pointer
   ldi ZL,low(won*2)
   rcall   ZtoLCD        ;go to LCD update
   rjmp    Waitreset

;---Losmsg: scrolls LEDs back down, and scale back down too.
losemsg:    
   ldi temp, 0x00  
   mov winner,temp    ;set loser bit
   ldi temp, 118      ;convert Vout to numerical value
   sub temp, AnaLo
   mov AnaLo, temp
   mov AnaHi, AnaLo      ;keep spare copy of AnaLo in AnaHi

   rcall   convertor      ;convert value to ASCII

   ldi     ZL, LOW(points)    ;ptr to RAM
       ldi     ZH, HIGH(points)
   rcall   ZrtoLCD        ;display points

   ldi LED, 0x08
   ldi ZH,high(fnotes*2)  ;first falling note addr to Z-ptr
   ldi ZL,low(fnotes*2)    
playfall:
   lpm            ;Z ptr already at first note of scale
   tst r0        ;if r0=0 then we are at the end of scale
   breq    donefall      ;we are done w/ this part then
   out TCNT0, r0      ;move value to timer 0 counter
   mov Reload, r0    ;save reload value
   adiw    ZL, 1
   lpm    
   mov duration, r0      ;load first scale note duration value
   adiw    ZL, 1
   dec LED        ;control LED lighting sequence
fallloop:
   tst duration      ;test to see if we have overflowed duration
   brne    fallloop      ;loop until we have played this note for entire duration
   rjmp    playfall      ;ready for next scale note

donefall:              ;done with scale here, ready for winning/losing msg
   ldi ZH,high(lost*2)    ;load losing msg to Z pointer
   ldi ZL,low(lost*2)
   rcall   ZtoLCD

;---Wait for operator reset
Waitreset:
   mov temp,Anahi    ;table for showing strength in LEDs
   cpi temp, 90
   brsh    _90s
   cpi temp, 80
   brsh    _80s
   cpi temp, 70
   brsh    _70s
   cpi temp, 60
   brsh    _60s
   cpi temp, 50
   brsh    _50s
_50s:   ldi LED, 3
   rjmp    fixclk
_60s:   ldi LED, 4
   rjmp    fixclk
_70s:   ldi LED, 5
   rjmp    fixclk
_80s:   ldi LED, 6
   rjmp    fixclk
_90s:   ldi LED, 7
   rjmp    fixclk

fixclk:
   ldi temp,TCK8
   out TCCR0, temp    ;set timer 0 at to/8
   ldi temp, 0x01  
   mov Reload, temp      ;mute
   ldi temp, 0x7A
   mov Duration, temp    ;for .5 second
   rjmp    LEDloop

LEDreset:
   clr LED
LEDloop:
   sbis    pind, 3        ;is reset key pressed by operator
   rjmp    Waitdone

   tst winner
   breq    LEDloop        ;loser
   tst Duration      ;Winner, flash LEDs
   brne    LEDloop
   ldi temp, 0x7A
   mov Duration, temp
   cpi LED, 0x07      ;reached last LED
   breq    LEDreset
   inc LED
   rjmp    LEDloop


Waitdone:
   ldi temp, TCK64
   out TCCR0, temp    ;set timer 0 clk / 64
   ldi state, welcome
   rjmp    softRESET
   

;----------Interrupts
;---Timer 0 overflow interrupt
;---Toggles music output register, reloads RELOAD value to counter
;---On portx: bit0=audio output, bits4..1=3bit to decoder for LED
;---Read LED and musicout registers, and switches output to PORTx accordingly
T0Ovfl:
   in  save, sreg
   ldi temp,0x01
   cp  Reload, temp      ;if Reload=0x01, then mute!
   breq    continue    
   com musicout      ;complement musicout
continue:
   in  itemp, portc
   tst musicout
   brne    lo_out
   ori itemp, 0x01
   rjmp    out_LED
lo_out:
   andi    itemp, 0xFE

out_LED:
   andi    itemp, 0xF1    ;zero out LED bits, leaves rest alone
   lsl LED        
   or  itemp, LED    ;include output to LED bits here
   lsr LED        ;shift back
   out portb, itemp

   dec duration      ;decrease duration counter to keep track of .25sec
   out TCNT0, Reload      ;reload counter
   out sreg, save
   reti
   
;---ADCdone interrupt
;---Called whenever ADC cycle is complete
ADCdone:
   in  save, sreg
   in  Analo, ADCL
   in  AnaHi, ADCH
   out sreg, save
   reti

;---LCD Stuff
;---ZtoLCD displays message from ROM
ZtoLCD: clr charcnt
   rcall   lcdclr
dispch:
   lpm
   tst     R0
   breq    endZtoLCD  ;If so, next message
   cp  charcnt,charcp;addressing changes at char #8!
   brne    wrtchr    ;at char 8, fix it
   ldi wreg,0xC0  ;Set address to last 8 chars
   rcall   lcdcmd  
wrtchr: mov wreg,R0    ;Send it to the LCD
   rcall   lcdput
   adiw    ZL,1      ;Increment Z-pointer
   inc charcnt    ;keep track of chars on display
   rjmp    dispch    ;Loop for more
endZtoLCD:
   ret

;---ZrtoLCD displays message from RAM
ZrtoLCD:
   clr charcnt    ;zero the character count
   rcall   lcdclr    ;Clear LCD screen
dispchr:
   ld  r0, Z      ;Get next chracter from RAM
   tst     R0            ;See if at end of message
       breq    endZrtoLCD    ;If so, next message
       cp      charcnt,charcp;addressing changes at char #8
       brne    wrtchrr        ;fix it
       ldi     wreg,0xC0      ;Set address to last 8 chars
       rcall   lcdcmd  
wrtchrr:
   mov     wreg,R0        ;Send it
       rcall   lcdput
       adiw    ZL,1          ;Increment Z-pointer
       inc     charcnt        ;keep track of chars
       rjmp    dispchr        ;Loop for more
endZrtoLCD:
   ret        ;done writing to LCD


;---Clear entire LCD and delay for a bit
lcdclr:
   ldi wreg,1    ;Clear LCD command
   rcall   lcdcmd
   ldi timeout,256;Delay 15 mS for clear command
   rcall   delay
   ret

;---Initialize LCD module
lcdinit:
   ldi wreg,0    ;Setup port pins
   out PORTC,wreg;Pull all pins low
   ldi wreg,0xff  ;All pins are outputs
   out DDRC,wreg
   ldi timeout,256;Wait at least 15 mS at power up
   rcall   delay

;   LCD specs call for 3 repetitions as follows
   ldi wreg,3    ;Function set
   out PORTC,wreg;to 8-bit mode
   nop        ;nop is data setup time
   sbi PORTC,lcde;Toggle enable line
   cbi PORTC,lcde

   ldi timeout,256;Wait at least 15 mS
   rcall   delay

   ldi wreg,3    ;Function set
   out PORTC,wreg
   nop
   sbi PORTC,lcde;Toggle enable line
   cbi PORTC,lcde

   ldi timeout,256;Wait at least 15 ms
   rcall   delay

   ldi wreg,3    ;Function set
   out PORTC,wreg
   nop
   sbi PORTC,lcde;Toggle enable line
   cbi PORTC,lcde

   ldi timeout,256;Wait at least 15 ms
   rcall   delay

   ldi wreg,2    ;Function set, 4 line interface
   out PORTC,wreg
   nop
   sbi PORTC,lcde;Toggle enable line
   cbi PORTC,lcde

   ldi wreg,0b11110000;Make 4 data lines inputs
   out DDRC,wreg
   
;   Finally,
;   At this point, the normal 4 wire command routine can be used

   ldi wreg,0b00100000;Function set, 4 wire, 1 line, 5x7 font
   rcall   lcdcmd

   ldi wreg,0b00001100;Display on, no cursor, no blink
   rcall   lcdcmd

   ldi wreg,0b00000110;Address increment, no scrolling
   rcall   lcdcmd
   ret

;---Wait for LCD to go unbusy
lcdwait:
   ldi wreg,0xF0  ;Make 4 data lines inputs
   out DDRC,wreg
   sbi PORTC,lcdrw;Set r/w pin to read
   cbi PORTC,lcdrs;Set register select to command
waitloop:
   sbi PORTC,lcde;Toggle enable line
   cbi PORTC,lcde
   in  lcdstat,PINC  ;Read busy flag
 ;Read, and ignore lower nibble
   sbi PORTC,lcde;Toggle enable line
   cbi PORTC,lcde

   sbrc    lcdstat,3  ;Loop until done
   rjmp    waitloop
   ret

;---Send command in wreg to LCD
lcdcmd:
   push    wreg      ;Save character
   rcall   lcdwait    ;Wait for LCD to be ready
   ldi wreg,0xFF  ;Make all port D pins outputs
   out DDRC,wreg
   pop wreg      ;Get character back
   push    wreg      ;Save another copy
   swap    wreg      ;Get upper nibble
   andi    wreg,0x0F  ;Strip off upper bits
   out PORTC,wreg;Put on port
   nop        ;wait for data setup time
   sbi PORTC,lcde;Toggle enable line
   cbi PORTC,lcde

   pop wreg      ;Recall character
   andi    wreg,0x0F  ;Strip off upper bits
   out PORTC,wreg;Put on port
   nop
   sbi PORTC,lcde;Toggle enable line
   cbi PORTC,lcde

   ldi wreg,0xF0  ;Make 4 data lines inputs
   out DDRC,wreg
   ret

;---Send character data in wreg to LCD
lcdput:
   push    wreg      ;Save character
   rcall   lcdwait    ;Wait for LCD to be ready
   ldi wreg,0xFF  ;Make all port D pins outputs
   out DDRC,wreg

   pop wreg      ;Get character back
   push    wreg      ;Save another copy
   swap    wreg      ;Get upper nibble
   andi    wreg,0x0F  ;Strip off upper bits
   out PORTC,wreg;Put on port
   sbi PORTC,lcdrs;Register select set for data
   nop
   sbi PORTC,lcde;Toggle enable line
   cbi PORTC,lcde

   pop wreg      ;Recall character
   andi    wreg,0x0F  ;Strip off upper bits
   out PORTC,wreg;Put on port
   sbi PORTC,lcdrs;Register select set for data
   nop
   sbi PORTC,lcde;Toggle enable line
   cbi PORTC,lcde

   ldi wreg,0xF0  ;Make 4 data lines inputs
   out DDRC,wreg
   ret

;---converts AnaLo to 3 digit decimal value
convertor:
   clr b0
   clr b1
   clr b2
begin_conv:
   tst AnaLo          ;test if resultL is zero
   breq    done          ;yes, we are done
dec_loop:
   ldi temp, 0x09    ;compare value
   dec AnaLo          ;decrement resultL
   cp  b0, temp      ;b0 >= 9?
   brsh    b0_yes      
   inc b0
   rjmp    begin_conv
b0_yes: cp  b1, temp      ;b0,b1>=9 too?
   brsh    b1_yes
   clr b0
   inc b1
   rjmp    begin_conv
b1_yes: clr b0
   clr b1
   inc b2
   rjmp    begin_conv
done:
   ldi     ZL, LOW(points)    ;ptr to RAM
       ldi     ZH, HIGH(points)
   ldi temp, azero    ;used to convert to ASCII
   add b0, temp      ;only 4 lsdigits needed
   add b1, temp
   ldi temp, 'S'
   st  Z, temp
   inc ZL
   ldi temp, 'c'
   st  Z, temp
   inc ZL
   ldi temp, 'o'
   st  Z, temp
   inc ZL
   ldi temp, 'r'
   st  Z, temp
   inc ZL
   ldi temp, 'e'
   st  Z, temp
   inc ZL
   ldi temp, ':'
   st  Z, temp
   inc ZL
   ldi temp, ' '
   st  Z, temp
   inc ZL
   st      Z, b1
       inc     ZL                  
       st      Z, b0
       inc     ZL              
   ldi temp, 0x00
   st  Z, temp
   ret

;---Timer 1 overflow interrupt handler
T1Ovfl:
   set        ;Set T flag
   ldi wreg,TCKSTOP  ;Timer 1 off
   out TCCR1B,wreg;Stop timer
   reti          ;Done, return

;---Delay n*64 microseconds using timer 0, delay time passed in timeout

Delay:  in  wreg,SREG  ;Save status register
   push    wreg
   ldi itemp, 0x00
   out TCNT1H, itemp
   out TCNT1L, timeout
   clt        ;Clear T
   ldi wreg,TCK1  ;Timer 1 prescaler, CK / 1
   out TCCR1B,wreg;Run timer
Dwait:  brtc    dwait      ;Wait for timer 1 interrupt to set T
   pop wreg      ;Restore status register
   out SREG,wreg
   ret

;--



Forumer™ is Voted #1 Free Forum Hosting provider
Build your own community today with the largest message board hosting company.