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:
- Yellow — first tone
- Green — second tone
- Orange — third tone
- Red — fourth tone
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:
- Press RB4+RB5 together to start a new melody
- Press RB6+RB7 together to change rhythm (4 speed settings)
Circuit Schematic
MemoSound circuit schematic
- Battery operated with LP2950CZ-5.0 voltage regulator
- No main switch — uses PIC sleep mode for power savings
- Four switches on PORTB high nibble (RB4-RB7)
- Internal weak pull-ups enabled on PORTB
- RB0 drives the piezo speaker
- PORTA drives four color LEDs
- 8 MHz crystal oscillator
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):