Build a simple memorization game system with a PIC16F84A and a few cheap components.

Overview

Build a simple memorization game system. With a PIC16F84A and a few cheap components, train your brain. PIC Sleep Mode and Wake-Up from PORTB Change are the keys to battery-efficient operation.

Game Rules

Memorize a melody of up to 62 steps. Each tone has a corresponding color LED:

The game plays a melody sequence, then you must repeat it by pressing the corresponding buttons. If you fail, an error melody plays and you can retry. If you succeed, a new tone is added to the sequence.

Special button combinations:

Circuit Schematic

MemoSound circuit schematic
MemoSound circuit schematic

C Source Code

memosound.c
/*
 * Project : memosound
 * 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 :
 * up to 63 steps, each step is one of 4 tones (A, B, C, D)
 * each tone is associated to a color LED (yellow, green, orange, red)
 *
 * RA0...RA3 : LEDs
 * RB0 : Piezo buzzer
 * RB4...RB7 : Buttons
 *
 *******************************************************************************
 */

#define NBFREQ  7
#define NBCYCLES(freq)  (2000/f)

#define EEPROM_SIZE     64
#define NBSTEPS         (EEPROM_SIZE - 2)
#define ADDR_NBSTEPS    (EEPROM_SIZE - 2)
#define ADDR_SPEED      (EEPROM_SIZE - 1)

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

unsigned char   speed ;

void    delay1000ms()
        {
        Delay_ms(1000) ;
        }

void    delay10ms()
        {
        Delay_ms(10) ;
        }

/*
 * play one sound with LED
 * s : sound index, d : duration, n : repeat, t : silence after (x10ms)
 */
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
        while(n--)
                {
                while(d--)
                        {
                        unsigned char cc ;
                        PORTB.F0 ^= 1 ;
                        for(cc = 0 ; cc < c ; cc++) Delay_us(1) ;
                        }
                }
        PORTA = 0 ;
        PORTB.F0 = 0 ;
        while(t--) delay10ms() ;
        }

/* play error melody: all tones descending */
void    playError()
        {
        unsigned char i ;
        for(i = 0 ; i < NBFREQ ; i++)
                {
                playSound(i, 50, 1, 0) ;
                }
        delay1000ms() ;
        }

/* play welcome melody: all tones ascending */
void    playWelcome()
        {
        unsigned char i ;
        for(i = NBFREQ ; i > 0 ; i--)
                {
                playSound(i - 1, 50, 1, 0) ;
                }
        }

/* fill EEPROM with a new random melody */
void    fillEEPROM()
        {
        unsigned char   i ;
        for(i = 0 ; i < NBSTEPS ; i++)
                {
                EEPROM_Write(i, rand() % 4) ;
                }
        EEPROM_Write(ADDR_NBSTEPS, 0) ;
        }

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

        s = EEPROM_Read(ADDR_NBSTEPS) ;
        s++ ;
        if(s == NBSTEPS)
                {
                return(0) ;
                }

        delay10ms() ;
        INTCON.RBIE = 0 ;
        INTCON.RBIF = 0 ;
        EEPROM_Write(ADDR_NBSTEPS, s) ;
        delay10ms() ;

        return(1) ;
        }

/* play a step from EEPROM */
void    playEEPROM(unsigned char a)
        {
        unsigned char   n ;
        n = EEPROM_Read(a) ;
        playSound(n, t_period[NBFREQ - 1 - n], speed, 10) ;
        }

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

        s = EEPROM_Read(ADDR_NBSTEPS) ;
        for(i = 0 ; i < s ; i++)
                {
                INTCON.RBIF = 0 ;
                INTCON.RBIE = 1 ;
                INTCON.GIE = 0 ;
                asm { sleep } ;                 // sleep until key press

                v = EEPROM_Read(i) ;

                while((PORTB & 0xf0) != 0xf0)
                        {
                        b = PORTB & 0xf0 ;
                        if(b == 0b01110000) b = 0 ;
                        else if(b == 0b10110000) b = 1 ;
                        else if(b == 0b11010000) b = 2 ;
                        else if(b == 0b11100000) b = 3 ;
                        else if(b == 0b11000000)        // speed mode
                                {
                                delay1000ms() ;
                                for(;;)
                                        {
                                        for(b = 0 ; b < 4 ; b++)
                                                {
                                                PORTA = 1 << (b & 3) ;
                                                for(s = 0 ; s < 200 ; s++)
                                                        {
                                                        if((PORTB & 0xf0) != 0xf0)
                                                                {
                                                                PORTA = 0 ;
                                                                speed = b + 1 ;
                                                                EEPROM_Write(ADDR_SPEED, speed) ;
                                                                delay1000ms() ;
                                                                return(0) ;
                                                                }
                                                        delay10ms() ;
                                                        }
                                                }
                                        }
                                }
                        else if(b == 0b00110000)        // melody reset
                                {
                                PORTA = 0xf ;
                                delay1000ms() ;
                                PORTA = 0 ;
                                return(2) ;
                                }

                        playSound(b, t_period[NBFREQ - 1 - b], 1, 0) ;
                        }

                delay10ms() ;
                rand() ;                        // more unpredictable melodies

                if(b != v)
                        {
                        playError() ;
                        return(0) ;
                        }
                }

        return(1) ;
        }

/* play all melody according to player's level */
void    playMusic()
        {
        unsigned char   i, s ;

        s = EEPROM_Read(ADDR_NBSTEPS) ;
        for(i = 0 ; i < s ; i++)
                {
                playEEPROM(i) ;
                }
        }

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

        TRISA = 0 ;
        PORTA = 0 ;
        TRISB = 0xf0 ;          // high nibble input, low nibble output
        PORTB = 0 ;

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

        speed = EEPROM_Read(ADDR_SPEED) ;

        PlayWelcome() ;
        delay1000ms() ;

        for(;;)
                {
                fillEEPROM() ;                  // new melody

                while(nextStep())               // next step
                        {
                        do
                                {
                                playMusic() ;   // play melody
                                }
                        while((r = verifEEPROM()) == 0) ;

                        if(r == 2)              // new melody requested
                                {
                                break ;
                                }

                        delay1000ms() ;
                        }

                if(r == 1)                      // player won!
                        {
                        PlayWelcome() ;
                        PlayWelcome() ;
                        PlayWelcome() ;
                        PlayWelcome() ;
                        delay1000ms() ;
                        }
                }
        }

Download

MemoSound HEX file

Pre-compiled HEX file ready to program into your PIC16F84A (8 MHz crystal):

092-MEMOSOUND-memosound_pic16F84A_8Mhz.hex