Did you find this site useful ? Please
to help www.micro-examples.com

If you need a coder or a freelance programmer, submit your project to me


PIC16F84A MemoSound Game

Printer-friendly version | Forums | FAQs |

PIC16F84A MemoSound Game System


This example will show you how to build a PIC based simple memorization game system.

With only a PIC16F84A and a few cheap components, you will train your brain first to understand a few basics about PICs, second to play to a not that easy memory game !

PIC Sleep Mode and Wake-Up from PORTB Change are the keys of the software.

 

 

Game rules

You will have to memorize a melody, made of up to 62 steps.
A step is one of the four tones available in the game system.
In order to help you, each tone is associated to a color LED (yellow, green, orange, red) which lights each time the tone is played.
The game system plays the melody, then you have to repeat it correctly by pressing the button of the tone's LED. At the beginning, the melody has only one step.
If you fail, an error melody is played, the melody is played again and you can try again to repeat it.
If you succeed, a new tone is added to the melody.
The longest melody is 62 step long, will you be able to learn it by heart ?

If you get bored with one melody, press the RB4 and RB5 buttons at the same time, the game system will create a new melody.

If you want to modify the melody rythme, press the RB6 and RB7 buttons at the same time, and select a new rythme by pressing a key when the LED is on :
RA0 : very fast
RA1 : fast
RA2 : slow
RA3 : very slow (default)

Circuit Schematic

The game is battery operated, and the circuit is powered with a LP2950CZ-5.0 regulator with some decoupling capacitors. There is no main switch, because the circuit is in sleep mode when nothing happens. A standard 9V battery should work for weeks.
Four switches are connected to the PORTB high nibble to allow wake-up on PORT change of the PIC when the player press a button. There is no pull-up resistors, because internal weak pull-up of the PIC is used.
The RB0 pin of the PIC drives directly a little piezzo speaker.
The PORTA low nibble drives fours LEDs, through current limitation resistors.
A cheap 8Mhz crystal is used to clock the PIC, you could add the two 15pf capacitors from crystal to ground to follow Microchip recommendation, but the PIC works very well without, just do it if you have a lazy PIC that does not start oscillator.

PIC16F84A Memsound circuit schematic

 

 

Click to enlarge schematic

 

 

 

 

 

 

PIC C Source Code

This project is made for a PIC16F84A MCU, but could be changed to any other PIC MCU with only a very few adjustments.
As this project fits within the 2k demo limit of the mikroC compiler, you can use it for free, download it from :
http://www.mikroe.com/en/compilers/mikroc/pic/

/*
 * Project : memosound
 * Author : Bruno Gavand
 * Date : August, 2006
 * Description : Sound & Light memorization game
 *
 * Target : PIC16F84A @ 8 MHz
 * Flags : HS oscillator, no watchdog, no power up timer
 *
 * IDE : mikroC V6.0
 *
 * the player has to memorize and play back a melody :
 * the PIC creates a new melody for each new game
 * a melody is up to 63 steps
 * each step is a tone corresponding to a color
 * there are 4 tones (A, B, C, D) and then 4 LEDs (red, green , orange, yellow)
 *
 * start-up : the PIC plays a welcome melody and secretly creates the new melody
 * the PIC plays the first step of the melody, and the player have to play it back
 * if it is the good one, the PIC plays the melody from the beginnig with one more step
 * the player have to repeat the same melody
 * if the player fails, an error melody is played and the PIC plays the same melody again
 * when all 63 steps of the melody are correctly played back by the player,
 * the PIC plays a win melody and creates a new one.
 * at any time, the player can press two buttons at a time to give up with the current melody
 * and start with a new one.
 *
 * RA0...RA3 : LEDs
 * RB0 : Piezzo buzzer
 * RB4...RB7 : Buttons
 *
 * see more details on http://www.micro-examples.com/
 *
 */

/*
 * macro definitions
 */
#define NBFREQ  7                       // size of frequency table
#define NBCYCLES(freq)  (2000/f)        // duration of a sound

#define EEPROM_SIZE     64              // size of P16F84A EEPROM : steps are stored in EEPROM
#define NBSTEPS         (EEPROM_SIZE - 2)       // maximum number of steps
#define ADDR_NBSTEPS    (EEPROM_SIZE - 2)       // address in EEPROM of the step counter
#define ADDR_SPEED      (EEPROM_SIZE - 1)       // address in EEPROM of the game speed

/*
 * frequency table : periods in thousands of ms
 */
const unsigned char     t_period[NBFREQ] =
        {
        132,
        148,
        165,
        176,
        198,
        220,
        247
        } ;

unsigned char   speed ;         // game speed

/*
 * delays are made as function instead of macro
 * to save ROM space
 *
 * 1 second delay
 */
void    delay1000ms()
        {
        Delay_ms(1000) ;
        }

/*
 * 10 ms delay
 */
void    delay10ms()
        {
        Delay_ms(10) ;
        }

/*
 * play one sound with LED
 *
 * s : index of the sound in frequency table
 * d : duration of the sound in numbers of periods
 * t : duration of silence after the sound (to be multiplied by 10 ms)
 */
void    playSound(unsigned char s, unsigned char d, unsigned char n, unsigned char t)
        {
        unsigned char c ;
        
        c = t_period[s] ;
        PORTA = 1 << (s & 3) ;          // light LED corresponding to the sound index
        while(n--)
                {
                while(d--)
                        {
                        unsigned char cc ;
                        
                        PORTB.F0 ^= 1 ;
                        for(cc = 0 ; cc < c ; cc++) Delay_us(1)  ;
                        }
                }
        PORTA = 0 ;                     // turn LED off
        PORTB.F0 = 0 ;                  // turn loudspeaker off
        while(t--) delay10ms() ;        // do the silence if needed
        }

/*
 * play the error melody, all tones in descending order
 */
void    playError()
        {
        unsigned char i ;

        for(i = 0 ; i < NBFREQ ; i++)
                {
                playSound(i, 50, 1, 0) ;
                }
        delay1000ms() ;
        }

/*
 * play the welcome melody, all tones in ascending order
 */
void    playWelcome()
        {
        unsigned char i ;

        for(i = NBFREQ ; i > 0 ; i--)
                {
                playSound(i - 1, 50, 1, 0) ;
                }
        }

/*
 * fill EEPROM with a new melody
 */
void    fillEEPROM()
        {
        unsigned char   i ;

        for(i = 0 ; i < NBSTEPS ; i++)          // for all melody steps
                {
                EEPROM_Write(i, rand() % 4) ;   // writes a random index from 0 to 3 in EEPROM location
                }
        EEPROM_Write(ADDR_NBSTEPS, 0) ;         // reset the current step number
        }

/*
 * increment melody step number
 *
 * return 0 if the player won
 * return 1 otherwise
 */
unsigned char   nextStep()
        {
        unsigned char   s ;

        s = EEPROM_Read(ADDR_NBSTEPS) ;         // read current step number from EEPROM
        s++ ;                                   // next one
        if(s == NBSTEPS)                        // is it the maximum step number ?
                {
                return(0) ;                     // yes, player has won
                }

        delay10ms() ;                           // waits a little bit

        INTCON.RBIE = 0 ;                       // disable portb interrupt
        INTCON.RBIF = 0 ;                       // clear pending interrupt
        EEPROM_Write(ADDR_NBSTEPS, s) ;         // no, write new step

        delay10ms() ;                           // waits a little bit

        return(1) ;                             // not won yet
        }

/*
 * play a step
 *
 * a : index in EEPROM of the step to play
 */
void    playEEPROM(unsigned char a)
        {
        unsigned char   n ;

        n = EEPROM_Read(a) ;                                    // read frequency index from EEPROM
        playSound(n, t_period[NBFREQ - 1 - n], speed, 10) ;     // play corresponding sound
        }

/*
 * most important routine,
 * verify if player's entry matches melody stored in EEPROM
 */
unsigned char   verifEEPROM()
        {
        unsigned char   i, s, v, b ;

        s = EEPROM_Read(ADDR_NBSTEPS) ;         // read current player's level
        for(i = 0 ; i < s ; i++)                // for each level
                {
                INTCON.RBIF = 0 ;               // clear PORTB on change interrupt flag
                INTCON.RBIE = 1 ;               // enables PORTB on change interrupt
                INTCON.GIE = 0 ;                // do not call interrupt routine at wake up
                asm { sleep } ;                 // go to sleep mode, and low power consomption

                /*
                 * when user press at least one key, the PIC wakes up
                 * and continues in sequence here
                 */
                v = EEPROM_Read(i) ;            // get next tone of the melody

                while((PORTB & 0xf0) != 0xf0)   // while a key is still pressed
                        {
                        b = PORTB & 0xf0 ;                      // get keys status
                        if(b == 0b01110000) b = 0 ;             // decode keys, and get tone number
                        else if(b == 0b10110000) b = 1 ;
                        else if(b == 0b11010000) b = 2 ;
                        else if(b == 0b11100000) b = 3 ;
                        else if(b == 0b11000000)                // is speed mode selected ?
                                {
                                delay1000ms() ;                 // debounce with delay
                                for(;;)                         // enter forever selection loop
                                        {
                                        for(b = 0 ; b < 4 ; b++)        // for all LEDS
                                                {
                                                PORTA = 1 << (b & 3) ;          // light LED
                                                for(s = 0 ; s < 200 ; s++)      // for a little while
                                                        {
                                                        if((PORTB & 0xf0) != 0xf0) // if a key is pressed
                                                                {
                                                                PORTA = 0 ;             // clear LEDs
                                                                speed = b + 1 ;         // compute speed
                                                                EEPROM_Write(ADDR_SPEED, speed) ; // store speed in EEPROM

                                                                delay1000ms() ;         // wait a little bit

                                                                return(0) ;             // resume melody
                                                                }
                                                        delay10ms() ;
                                                        }
                                                }
                                        }
                                }
                        else if(b == 0b00110000)                // is melody reset selected ?
                                {
                                PORTA = 0xf ;                   // understood, light all LEDs
                                delay1000ms() ;                 // wait one second
                                PORTA = 0 ;                     // clear all LEDs

                                return(2) ;                     // code 2 to prevent error melody to be played
                                }

                        playSound(b, t_period[NBFREQ - 1 - b], 1, 0) ; // play the sound corresponding to the key pressed by the player
                        }

                delay10ms() ;                   // debounce keys
                rand() ;                        // usefull to get more unpredictable melodies

                if(b != v)                      // did the player gave the wrong tone ?
                        {
                        playError() ;           // yes, play error melody
                        return(0) ;             // code 2 for player's memory failure about this tone
                        }
                }

        return(1) ;                             // code 1 for correct player's memory for this tone
        }

/*
 * play all melody according to player's level
 */

void    playMusic()
        {
        unsigned char   i, s ;

        s = EEPROM_Read(ADDR_NBSTEPS) ;         // get player's level
        for(i = 0 ; i < s ; i++)                // for each level
                {
                playEEPROM(i) ;                 // play the tone of the melody
                }
        }

/*
 * program entry
 */
void main()
        {
        unsigned char r ;

        TRISA = 0 ;     // PORTA is output
        PORTA = 0 ;     // clear output

        TRISB = 0xf0 ;  // high nibble is input for keys, low nibble is output
        PORTB = 0 ;     // clear output

        OPTION_REG &= 0b01111111 ;      // weak pull-up on PORTB

        speed = EEPROM_Read(ADDR_SPEED) ;       // get game speed

        PlayWelcome() ;                         // play welcome melody
        delay1000ms() ;                         // 1 second delay before game start

        for(;;)                                 // forerver
                {
                fillEEPROM() ;                  // fill EEPROM with new melody

                while(nextStep())               // next step in melody
                        {
                        do
                                {
                                playMusic() ;   // play melody to current step
                                }
                        while((r = verifEEPROM()) == 0) ;       // until player repeat it correctly

                        if(r == 2)              // player asked for new melody
                                {
                                break ;
                                }

                        delay1000ms() ;         // 1 second delay before next step
                        }

                if(r == 1)                      // player repeated all steps correctly
                        {
                        PlayWelcome() ;         // play win melody
                        PlayWelcome() ;
                        PlayWelcome() ;
                        PlayWelcome() ;

                        delay1000ms() ;         // 1 second delay befor new game
                        }
                }
        }


EEPROM Definition :

This project uses the internal PIC EEPROM, you have to add this EEPROM definition to your project :

0x00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
0x10 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
0x20 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
0x30 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 04

You can download the .HEX file for PIC16F84A 8 Mhz crystal here :

http://www.micro-examples.com/pics/092-MEMOSOUND-memosound_pic16F84A_8Mhz.hex

 

Prototype

Here is the picture of the prototype I built with VeroBoard :

PIC16F84A MemoSound Game Prototype

Don't hesitate to leave comments & suggestions, and also report bugs in my forums.

Enjoy !

 

All trademarks and registered trademarks are the property of their respective owners