Introduction Many of the simpler electronic games of the past decade can be easily programmed on the AVR microcontrollers we are using this semester, using only the lights and switches available on the evaluation boards. For our final project we programmed the game Simon using the AT90S8535 microcontroller chip and corresponding evaluation board. This is a simple computer game involving remembering a sequence of lights which are generated by the program, and repeating them back to the program in the correct order by pressing the correct sequence of buttons. Each time the player correctly repeats back the sequence, the sequence is increased by one element. This project, as well as giving us a chance to design a fun game, gave us the opportunity to use several pieces of programs which we had developed for previous labs throughout the semester. In particular, many features of the musical tone generator which we developed for lab 3 were very useful for this project. Using the 8535 also gave us an opportunity to use a new evaluation board, which we had not used in previous labs.
User Interface: Playing the Game After the program is started (either by turning on the evaluation board or pressing reset), the LED's all flash on and off, notifying the player that the game is starting, and a short starting melody is played. The game then turns on LED's 1 and 2, and waits until the player selects a level before starting the game. Level one has a constant playing speed while level two plays back the sequence of notes at an increasing rate as play continues. The game then randomly selects a number which is mapped to an LED which lights up while the corresponding tone is sounded. After playing the note, the game waits for the user to press the matching button. If the person presses the correct button, the program plays the previous string of lights and tones, selects the next random number, lights up the new LED while playing the corresponding tone, and waits for the player to press the correct sequence of buttons. As long as the player continues to press the correct sequence of switches without running out of time, the game will continue. Play stops when the playback sequence contains 40 numbers, which is the defined RAM storage size for the array of numbers, or the person looses. The player looses by either pressing the wrong button or taking longer then 2.1 seconds to press the correct button. When either of those things occur, the player looses the game, the level that the player was on, that is, the length of the stored sequence, is shown in binary on the LED's, and a short 'game over' song is played. A flow chart describing how our program works is provided below.
Figure 1. Simon Flow Chart . How the Game Actually Works The Random Number Generator Timer1 is used to as a random number generator and is prescaled to 1 at the start of the program. When the next number in the series is needed, the last three bits of timer1 are read into a register and then mapped, using a table, to a corresponding light and tone. As the time between successive reads on timer1 depends on the speed at which the player presses the buttons, this method was deemed adequate for generating random numbers, particularly as timer1 changes once every 0.25 mSec, whereas the player can press the buttons no faster then once every 20 msec. Lights Although we originally wished to use colored LED's with our program we were unable to make the external LED's work with our program. As a result, we chose to use only the LED's located on the evaluation board. Lookup tables are used to map the random number to an LED. The LED's are controlled through port B.
Figure 2. Steps for reading the buttons Switches Switches, or buttons, are located on the evaluation board and are controlled through port D. Lookup tables are used to map each switch to an LED and a musical tone which are activated when a person presses a button. A polling loop is used to determine which button was pressed and hence which LED to activate and which tone to play. The switches are debounced by calling a 0.46 second delay loop after reading the buttons. Notes The notes which are played when the buttons are pressed were taken from lab 3 and correspond to the octave which starts with middle C at 262 Hz (switch 7) and ends with the next highest C at 523Hz (switch 0). The note frequencies were declared as constants at the beginning of the program and the constant labels were used throughout the program. All tones were played through a speaker attached to port C. The notes are controlled using the timer0 overflow interrupt, sending the speaker signal out every time the interrupt is called. Commands in the main program turn on the timer0 overflow interrupt enable bit when a note is to be played and turn the bit off when the note is finished. A 0.66 second delay loop runs in the main program while the note is being played, with a 0.07 second delay loop running afterward to ensure that there is silence time between the notes. As well as the eight tones needed for the game, we also implemented two small melodies, one for starting the game, and one for when the game is over. A 'play song' subroutine is implemented for this part of the program, which loads the frequencies for each note in the song individually, and then calls a second 'play note' subroutine. The 'play note' subroutine then turns on and off the timer0 overflow interrupt enable bit and implements delay loops as in the main program for both the playing time and the silence time between notes. The timer0 overflow interrupt service routine controls the sending of the speaker output.
Figure 3. Formula to compute the delay time in the delay-subroutines. Storing the Sequence The sequence of three bit random numbers which map to the lights and tones, are stored in the chip SDRAM starting with location 0x060. We decided to limit the size of the sequence stored in memory to 40 numbers as we needed to exactly define the available memory space and assumed that no player will make it past 40 levels. The routine for storing data in RAM that was used in Lab 4 is used as a guideline for this part of our program: register 31 is used as a pointer to the current RAM location that is being accessed. A register (memlo) is used to keep track of how many numbers are in the stored sequence and is also used to determine if the game has filled up the allocated memory, that is, if the fortieth level has been reached. Additionally, this register is used to output the level of the game the player is on when they loose. Hardware The program is designed to work on the 8535 microcontroller and corresponding evaluation board. As mentioned earlier, the LED's are located on a breadboard and controlled through port B, the switches are controlled through port D, and port C controls the speaker. We chose not to implement other hardware features because hardware is very difficult to debug in the simulator programs, and most of what we needed was available on the evaluation boards. Results Our program worked rather well overall. During testing we established that the game does not play too fast and adequate time is allowed for the user to press the buttons without the player running out of time and loosing the game. We chose to debounce the buttons by implementing a 0.46 second delay loop between successive reads of the switches, instead of checking to verify that the switches had been depressed before starting the next read. If the player holds down a switch for a long time the switch will be read more then once and the player will loose the game. As a result, we had to be careful when calculating the timing of our program so that enough time is allowed for the player to press the buttons, but the game still runs fast enough that the player does not have to wait a long time between each action We did not have to worry about timing precision in our project. The times that we decided on when programming our delay loops were based on delay loops calculated for previous projects or on our experiences of playing the game during testing. The only timing that we did carefully calculate was the decrease in note playing time for level 2. We were more careful with this calculation because we needed to be able to decrease the note time in 40 equal steps, one step for each level, but the time that the notes are played had to remain long enough that the player was still able to hear the individual tones and see the LED's light up. We chose to decrease the note playing time by 0.75 m Sec for each level, so that there is a noticeable change in speed after some levels, but the game does not become too fast to be played. The game starts with a note playing time of 0.66 seconds and will finish with a note playing time of 0.35 seconds on the fortieth level. The amount of time that the game waits for the player to press a button before they loose the game remains constant.
Calculation of the delay loops was carried out by counting the number of instruction cycles within each loop and multiplying this by the instruction cycle time of 0.25 m Sec .
Although we originally wished to use colored LED's with our program we were unable to supply external LED's with enough power for them to work properly. We were unable to determine why our LED's were not working properly and only the first five LED's would light up. When we measured the voltages being supplied to the LED's, enough voltage was present on all pins, however, the last three LED's remained extremely dim. This problem could be due to either the external LED's not receiving enough current, or the evaluation board being damaged. Enough power is present to run all of the LED's on the evaluation board.
Future Improvements Although the program worked well in general, having all red LED's made it difficult to remember which LED was lit when the sequence was long. To fix this problem we attempted to use colored LED's, however, we were unable to make them work properly, possibly due to them not receiving enough current. A possible solution to this problem would involve using two different ports to drive the LED's, with each port driving only 4 of them. As this would get very complicated, we chose not to try this solution, and to simply use the LED's on the evaluation board. Another problem we noticed while testing the external LED's is that pressing the buttons when the LED's were on the breadboard was a bit awkward. It would probably be easier to play the game if the buttons and switches were a bit more spread out. As a result, a future improvement would be to either use external switches which could be positioned near the LED's on the breadboard, or to use LED's which are also switches themselves. Either of these features would make the program more user friendly.
A second improvement would be to change the method that is used to let the player select a level. Unless the player is familiar with the format of our game, although LED's 1 and 2 are turned on until a level is selected, there is no way the player will know that it is necessary to select a level to start the game. As a result, it would be a good idea to implement a separate switch which could be permanently set on a level and read only at the beginning of the game. Alternatively, it would be possible to attach a small LCD screen which would display a message telling the player to select a level of play, however, as this would require a lot of extra code, the first possibility would probably be better.
Other, small improvements which we could have made to our program were to add a winning song or a scoring method, however, we decided that these features were not necessary for our program.
A final improvement that we could make to this program is to change our method of debouncing the buttons. We chose to use delay loops between successive reads of the buttons, but it would speed up the game if we instead checked to see that the button was released before beginning another read of the switches. This would allow the player more control over the speed of the game, and would make the game more fun for people with faster reflexes.
;set up and turn on timer1 ldi wreg, 0x41 out TCCR1B, wreg
;set up speaker on Port C ser wreg out DDRC, wreg ldi spk, 0
;set up port D buttons as input ldi wreg, 0x00 out DDRD, wreg
;set up possible note time ldi wreg, 200 mov time, wreg
;set up pointer to highest RAM array location ldi wreg, 0x60 mov memlo, wreg
;prescale timer 0 ldi wreg, 3 ;prescale timer by clk/64 out TCCR0, wreg
;initialize text pointer before turning on interrupts ldi ZL, LOW(cntstr) ;ptr to RAM ldi ZH, HIGH(cntstr) sei ;turn on interrupts
;flash all LEDS tentatively assigned to portb main: ldi wreg, 0x00 out PORTB, wreg ;flash all LED's on rcall song1 rcall delay ;delay subroutine so player can see lights ldi wreg, 0xF9 out PORTB, wreg ;flash all LED's off, leaving on LED's 1 and 2 ;until player selects a level
cpi wreg, 2 brne sta1 set ;set T bit to indicate level=2.
sta1: ldi wreg, 0xFF ;turn of all LED's out PORTB, wreg
;random number generator start: in wreg, TCNT1L ;keep in temp2, TCNT1H ;discard andi wreg, 0x07 ;keep last three bits of this register to map to lights and tones mov temp1, memlo ;set up extra array pointer cpi temp1, 0x88 brne noreset win: rjmp win ;Spin loop when player wins
; load next digit in RAM noreset:mov ZL, memlo ldi ZH, 0x00 st Z, wreg ;load the button digit into RAM inc memlo
inc ZL ser wreg ;zero string terminator to RAM - we chose to use FF as 00 maps to a switch st Z, wreg
;start at beginning of RAM and play all the data ;now the pointer to the variable message in RAM ldi ZL, LOW(cntstr) ;ptr to RAM ldi ZH, HIGH(cntstr) plystr: ld r0,Z ;button is in r0 inc ZL mov wreg,r0 cpi wreg, 0xFF ;see if at end of message brne here rjmp end1 ;If so, jump to next part of program
; otherwise play note and set up lights ;poll for which note should be played here: cpi wreg, 7 brne one ldi tmer, do ldi lights, 0x7F rjmp outn one: cpi wreg, 6 brne two ldi tmer, re ldi lights, 0xBF rjmp outn two: cpi wreg, 5 brne three ldi lights, 0xDF ldi tmer, mi rjmp outn three: cpi wreg, 4 brne four ldi lights, 0xEF ldi tmer, fa rjmp outn four: cpi wreg, 3 brne five ldi lights, 0xF7 ldi tmer, sol rjmp outn five: cpi wreg, 2 brne six ldi lights, 0xFB ldi tmer, la rjmp outn six: cpi wreg, 1 brne seven ldi tmer, si ldi lights, 0xFD rjmp outn seven: cpi wreg, 0 ldi lights, 0xFE ldi tmer, do2 rjmp outn
;output LEDs outn: out PORTB, lights
;play note
;turn on timer ovfl interrupt timer0
ldi Reload, 0xFF ;load first freq into timer0 sub Reload, tmer out TCNT0, Reload
ldi wreg,0x01 ;enable timer interrupt out TIMSK, wreg
wait2: mov temp1, cntstr1 ; wait while note is played mov wreg, cntstr2 mov temp2, cntstr3 one2: nop dec temp1 brne one2 dec wreg brne one2 dec temp2 brne one2
;turn off timer0 ovfl interrupt ldi wreg, 0x00 out TIMSK, wreg ;turn off lights ser lights out PORTB, lights
;silence time ldi temp1, 0xFF ldi temp2, 0xFF one3: nop dec temp1 brne one3 dec temp2 brne one3
;loop back to get next note out of RAM rjmp plystr
;if done playing all notes ;wait for button pressed end1: ldi ZL, LOW(cntstr) ;ptr to RAM ldi ZH, HIGH(cntstr) end4: ld r0,Z inc ZL mov butime, r0 ;button is in r0 cpi butime, 0xFF ;See if at end of RAM array brne next rjmp end2
next: ldi temp1, 0xFF ;load a counter with the time allowed for player ldi temp3, 0xFF ;to press button before losing ldi count1, 0x03 end3: ldi temp2, 0xFF pressed. in wreg, PIND cpi wreg, 0x7F brne six1 ldi temp2, 0x07 ldi lights, 0x7F ldi tmer, do rjmp out1 six1: cpi wreg, 0xBF brne five1 ldi temp2, 0x06 ldi lights, 0xBF ldi tmer, re rjmp out1 five1: cpi wreg, 0xDF brne four1 ldi temp2, 0x05 ldi lights, 0xDF ldi tmer, mi rjmp out1 four1: cpi wreg, 0xEF brne three1 ldi temp2, 0x04 ldi tmer, fa ldi lights, 0xEF rjmp out1 three1: cpi wreg, 0xF7 brne two1 ldi temp2, 0x03 ldi tmer, sol ldi lights, 0xF7 rjmp out1 two1: cpi wreg, 0xFB brne one1 ldi temp2, 0x02 ldi lights, 0xFB ldi tmer, la rjmp out1 one1: cpi wreg, 0xFD brne zero1 ldi temp2, 0x01 ldi lights, 0xFD ldi tmer, si rjmp out1 zero1: cpi wreg, 0xFE brne out1 ldi temp2, 0x00 ldi tmer, do2 ldi lights, 0xFE rjmp out1 out1: cpi temp2, 0xFF ;if don't have button pressed check if out of time
brne gotit ;jump if have a valid number from RAM array
;updating timer to see if user is out of time dec temp1 ;else decrease counter cpi temp1, 0xFF brne down1 dec temp3 cpi temp3, 0xFF brne down1 dec count1 cpi count1, 0x00 breq error ;jump to game over if player is out of time down1: rjmp end3
;compare if same value from button as from RAM gotit: cp butime, temp2 ;If equal, get next value brne error ;if not equal go to end of game ;LED should not turn on if wrong button was pressed out PORTB, lights ;else output lights
;play note
;turn on timer ovfl interrupt timer0
ldi Reload, 0xFF ;load first freq into timer0 sub Reload, tmer out TCNT0, Reload ldi wreg,0x01 ;enable timer0 overflow interrupt out TIMSK, wreg
rcall delay ;delay subroutine before reading next button
;turn off timer0 ovfl interrupt ldi wreg, 0x00 out TIMSK, wreg
ldi temp2, 0xFF ;turn off lights out PORTB, temp2 rjmp end4 ;if match between RAM and pressed button repeat procedure.
end2: rcall delay ;call delay subroutine before beginning again brts chnge rjmp start ;compare if level=2 so variable clock chnge: ldi wreg, 0x10 ;if level=2, decrease count sub cntstr2, wreg ldi wreg, 0x00 sbc cntstr3, wreg rjmp start ;and go back to start of game, generating next random number
;error procedure error: mov wreg, memlo subi wreg, 0x60 ;display level player lost on com wreg out PORTB, wreg rcall song2 ;jump to play game over song display level rcall delay rjmp reset ;reset game
;******** Timer0 overflow interrupt service routine**********
timer: in savSREG, SREG ;save the status reg
com spk out PORTC, spk ;send out speaker value ldi Reload, 0xFF sub Reload, tmer out TCNT0, Reload out SREG, savSREG ;restore status reg reti
delay: ldi temp1, 0xF ldi wreg, 0x00 ldi temp3, 0x07 oned2: nop dec temp1 brne oned2 dec wreg brne oned2 dec temp3 brne oned2 ret ;*******************************************************************************
song1: ldi temp3,0x02 ;start song subroutine ldi tmer, do rcall play ldi temp3,0x02 ldi tmer, re rcall play ldi temp3,0x02 ldi tmer, mi rcall play ldi temp3,0x03 ldi tmer, fa rcall play ldi temp3,0x03 ldi tmer, mi rcall play ldi temp3,0x03 ldi tmer, fa rcall play reti
song2: ldi temp3,0x01 ;Game Over song subroutine ldi tmer, do2 rcall play ldi temp3,0x01 ldi tmer, si rcall play ldi temp3,0x01 ldi tmer, la rcall play ldi temp3,0x01 ldi tmer, sol rcall play ldi temp3,0x01 ldi tmer, fa rcall play ldi temp3,0x01 ldi tmer, mi rcall play ldi temp3,0x01 ldi tmer, re rcall play ldi temp3,0x07 ldi tmer, do rcall play reti
;*** subroutine to play a note *************************** play: ldi Reload, 0xFF ;load first freq into timer0 sub Reload, tmer out TCNT0, Reload ldi wreg,0x01 ;enable timer interrupt out TIMSK, wreg
ldi temp1, 0x00 ldi temp2, 0x00
dly1: nop ;time given by temp3 to play the note dec temp1 brne dly1 dec temp2 brne dly1 dec temp3 brne dly1
ldi wreg, 0x00 ;turn off timer0 ovfl interrupt out TIMSK, wreg
dly2: nop ;small silence time for between notes dec temp1 brne dly2 dec temp2 brne dly2
ret [/code]
Forumer™ is Voted #1 Free Forum Hosting provider
Build your own community today with the largest message board hosting company.