Catch the Light PIC Game

Catch the Light PIC Game

This project is a simple PIC game where LEDs go on randomly one at a time. The objective is to “catch” the LED that’s on by pressing the corresponding button. For every catch, the score, displayed on a seven segment display, is incremented. The speed of the LEDs increase every time the score goes beyond multiples of 5 (10, 15, 20).

Introduction

This a good PIC project for beginners. All that is needed is four LEDs, four buttons and a dual 7 segment display. This project is coded using XC8. Here’s a video of the project simulated on Proteus ISIS:


Making the LEDs go random is done through the rand() command and using TMR1’s value as seed. Each LED has a corresponding button. If the user presses a button, the RB change interrupt is triggered and then the PORTB (buttons) value is compared to the PORTC (LEDs) values. If a match occurs, the score is incremented.

Materials Needed

  • PIC16F877A
  • Dual Seven-segment Display - Common Cathode
  • 74LS48
  • 5 x SPST Pushbuttons
  • 4 x LEDs
  • 5 x 10k ohm, 1/4 W Resistors
  • 5 x 200 ohm, 1/4 W Resistors
  • 4 MHz Crystal
  • 2 x 22pF Ceramic Capacitor

Schematic Diagram

Catch the Light! PIC Project Game

I wanted to use RB change interrupt for faster reaction time. And since this interrupt triggers via four pins (RB4 to RB7), this project is limited to four buttons and, consequently, four LEDs. More LEDs and more buttons is possible but you can’t use interrupts anymore.

Code

/*
 * File:   catch_the_light.c
 * Author: Roland
 * 
 * Catch the Light Game
 * 
 * 
 * Created on April 19, 2018, 1:02 PM
 */

#pragma config FOSC = XT        // Oscillator Selection bits (XT oscillator)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF        // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF        // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

#define _XTAL_FREQ 4000000
#include <xc.h>

#include <stdlib.h>
#include <stdio.h>
#include <time.h>

int rand_num;
int old_rand_num;
int score;
int score_tens;
int score_ones;
int shifter;
int delay;

//a function that varies the delay. Because passing variables to __delay_ms() doesn't work
void someDelay(){
    switch(delay){
        case 500:
            __delay_ms(500);
            break;
        case 200:
            __delay_ms(200);
            break;
        case 100:
            __delay_ms(100);
            break;
        case 50:
            __delay_ms(50);
            break;
    }
    return;
}
void main(void) {
    
    T1CON = 0b00000001;             //turn on TMR1
    INTCON = 0b10101000;            //use global, TMR0 and RB change interrupts
    OPTION_REG = 0b00000100;        //use 1:32 prescale for TMR0
    TRISB = 0xF0;                   //inputs on RB4 to RB7, outputs for RB0 to RB3
    TRISC = 0;                      //all PORTC are outputs
    TRISD = 0;                      //all PORTD are outputs
    
    delay = 500;                    //default delay
   
    loop:
    while(1){
        srand(TMR1);                //use TMR1 value as seed for random
        rand_num = rand() % 9;      //generate random numbers from 0 to 9
        //filter out consecutive numbers, 0 and those that has at least two bits high (011 = 3, 101 = 5, 110 = 6, 111 = 7)
        if(rand_num == old_rand_num || rand_num == 0 || rand_num == 3 || rand_num == 5 || rand_num == 6 || rand_num == 7){ 
            goto loop;
        }
        PORTD = rand_num;           
        old_rand_num = rand_num;    //
        someDelay();                //a function that varies the delay. Because passing variables to __delay_ms() doesn't work 
    }
    
    return;
}

void interrupt isr(void){
    if(TMR0IF){
         //switch digits for every interrupt. Read about POV for better understanding
            shifter++;        
            switch(shifter){
                case 1:
                    PORTB = 0b11111110;
                    PORTC = score_ones;
                    break;
                case 2:
                    PORTB = 0b11111101;
                    PORTC = score_tens;
                    break;
                case 6:
                    shifter = 0;
                    break;
            }
            TMR0IF = 0;     //manual clearing of interrupt flag
    }
    if(RBIF){
        int dummy = PORTB;          //read PORTB to clear mismatch condition
        int masked = (PORTB & 0xF0) >> 4;   //acquire pressed buttons
        if(~masked == (PORTD + 0xFFF0)){
            score++;
            score_tens = score/10;
            score_ones = score - score_tens*10;
            //adjust speed according to score
            if(score <= 5){ 
                delay = 500;
            }else if(score > 5 && score <= 10){
                delay = 200;
            }else if(score > 10 && score <= 20){
                delay = 100;
            }else if(score > 20 && score <= 30){
                delay = 50;
            }else if(score > 99){
                score = 99;
            }else{
                delay = 500;
            }
        }
        RBIF = 0;
    }
}

 

Leave a Reply

Your email address will not be published. Required fields are marked *