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.
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 :
Don't hesitate to leave comments & suggestions, and also report bugs in my forums.
Enjoy !
|