PIC16F84A MemoSound Game
A PIC-based memorization game system
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
- 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):