Game Show Buzzer (PIC16F887, MPLAB, Hi-Tech C)

Discussion in 'Embedded Systems and Microcontrollers' started by Recon, Jan 10, 2012.

  1. Recon

    Thread Starter Member

    Oct 16, 2009
    30
    0
    I've already posted a few threads in this forum for help with some basics, but this is the project that I am working towards. The code below works but I think it'll make most people here cringe! :eek:

    I have 4 input switches (RA0 to RA3) and 4 output LEDs (RB0 to RB3). The game starts with an LED countdown. Once the countdown is over, the first player to hit the switch lights their corresponding LED. I added a basic check to prevent cheating (by holding the switch during the countdown).

    I would really appreciate any feedback as to how I can improve this program. At the moment, the winning LED remains lit as it is stuck in a never-ending loop. I tried to come up with a better solution but I'm not sure how. I'd also like to add a buzzer and perhaps some chasing LED effects.

    Thanks in advance. :)

    Code ( (Unknown Language)):
    1. #include "htc.h"
    2. #define _XTAL_FREQ 4000000
    3. #define delay_s(T) {unsigned char i; for (i=0; i<T*10; i++) __delay_ms(100);}
    4.  
    5. __CONFIG(FOSC_INTRC_NOCLKOUT & WDTE_OFF & PWRTE_ON & MCLRE_ON & CP_OFF & CPD_OFF & BOREN_OFF & IESO_OFF & FCMEN_OFF & LVP_OFF & DEBUG_OFF & BOR4V_BOR40V & WRT_OFF);
    6.  
    7. char team0_ok, team1_ok, team2_ok, team3_ok;
    8.  
    9. int main(void) {
    10.  
    11.     TRISA = 0b00001111; // RA0 to RA3 are inputs, RA4 to RA7 are outputs
    12.     TRISB = 0b11110000; // RB0 to RB3 are outputs, RB4 to RB7 are inputs
    13.     ANSEL = 0; // configure PORTA analog channels as a digital inputs
    14.  
    15.     // initial LED "countdown"
    16.     PORTB = 0b00000001;
    17.     delay_s(1);
    18.     PORTB = 0b00000011;
    19.     delay_s(1);
    20.     PORTB = 0b00000111;
    21.     delay_s(1);
    22.     PORTB = 0b00001111;
    23.     delay_s(3);
    24.     PORTB = 0; // no output LEDs
    25.  
    26.     // reset team_ok values
    27.     team0_ok = 1;
    28.     team1_ok = 1;
    29.     team2_ok = 1;
    30.     team3_ok = 1;
    31.    
    32.     // check whether an input is already high (cheating!)
    33.     if(RA0 == 1){
    34.         team0_ok = 0;
    35.     }
    36.     if(RA1 == 1){
    37.         team1_ok = 0;
    38.     }
    39.     if(RA2 == 1){
    40.         team2_ok = 0;
    41.     }
    42.     if(RA3 == 1){
    43.         team3_ok = 0;
    44.     }
    45.  
    46.     // loop to detect an input and light the corresponding LED
    47.     for(;;) {
    48.         if((RA0 == 1)&&(team0_ok == 1)){
    49.             for(;;){RB0 = 1;}
    50.         }
    51.         else if((RA1 == 1)&&(team1_ok == 1)){
    52.             for(;;){RB1 = 1;}
    53.         }
    54.         else if((RA2 == 1)&&(team2_ok == 1)){
    55.             for(;;){RB2 = 1;}
    56.         }
    57.         else if((RA3 == 1)&&(team3_ok == 1)){
    58.             for(;;){RB3 = 1;}
    59.         }
    60.         else {
    61.             // nothing -- is this ok?
    62.         }
    63.     }
    64. }
     
    Last edited: Jan 10, 2012
  2. t06afre

    AAC Fanatic!

    May 11, 2009
    5,939
    1,222
    How do you design your switch setup. Do you use pull-up or pull-down resistors
     
  3. Recon

    Thread Starter Member

    Oct 16, 2009
    30
    0
    I use pull-up resistors with push-to-break switches at the moment.
     
  4. t06afre

    AAC Fanatic!

    May 11, 2009
    5,939
    1,222
    Just to be sure. Can you make a sketch of your switch setup. Nothing fancy is needed. just use paint, or a hand drawing will do fine. You are aware of that then digital inputs are not connect they will see a high level. As a rule of thumb. But in worst case the digital input left open or floating. May flicker between high and low level. So digital inputs should never be left floating but connected to either a high proper high or low level.
     
    Last edited: Jan 10, 2012
  5. Recon

    Thread Starter Member

    Oct 16, 2009
    30
    0
    I have since extended the program to support 8 switches and added a buzzer on RC7. The program below works perfectly, but I'm still interested in hearing feedback on how I could improve it. I've attached the schematic.

    PORTA = switches
    PORTB = LEDs
    PORTC = 7 segment display (+ buzzer on RC7)

    Code ( (Unknown Language)):
    1. #include "htc.h"
    2. #define _XTAL_FREQ 4000000
    3. #define delay_s(T) {unsigned char i; for (i=0; i<T; i++) __delay_ms(100);}
    4.  
    5. __CONFIG(FOSC_INTRC_NOCLKOUT & WDTE_OFF & PWRTE_ON & MCLRE_ON & CP_OFF & CPD_OFF & BOREN_OFF & IESO_OFF & FCMEN_OFF & LVP_OFF & DEBUG_OFF & BOR4V_BOR40V & WRT_OFF);
    6.  
    7. void chase(void) {
    8.     unsigned char i;
    9.    
    10.     PORTB = 0b00011111;
    11.     for(;;){
    12.         for(i=0; i<7; i++){
    13.             PORTB = (PORTB >> 1)|(PORTB << (sizeof(PORTB)*8 - 1)); //bitwise rotate
    14.             __delay_ms(75);
    15.         }
    16.     }
    17. }
    18.  
    19. void beep(unsigned char beeps) {
    20.     unsigned char i;
    21.    
    22.     for(i=0; i<beeps; i++){
    23.         RC7 = 0;
    24.         __delay_ms(100);
    25.         RC7 = 1;
    26.         __delay_ms(100);
    27.     }
    28. }      
    29.  
    30. int main(void) {
    31.     unsigned char team0_ok, team1_ok, team2_ok, team3_ok, team4_ok, team5_ok, team6_ok, team7_ok;
    32.    
    33.     TRISA = 1; // PORTA inputs (switches)
    34.     TRISB = 0; // PORTB outputs (LEDs)
    35.     TRISC = 0; // PORTC outputs (7 seg)
    36.     ANSEL = 0; // configure PORTA analog channels as digital inputs
    37.     ANSELH = 0;
    38.    
    39.     //reset
    40.     PORTA = 0b11111111;
    41.     PORTB = 0b11111111;
    42.     PORTC = 0b11111111;
    43.    
    44.     // initial LED "countdown"
    45.     beep(4);
    46.     PORTB = 0b01111111;
    47.     delay_s(5);
    48.     PORTB = 0b00111111;
    49.     delay_s(5);
    50.     PORTB = 0b00011111;
    51.     delay_s(5);
    52.     PORTB = 0b00001111;
    53.     delay_s(5);
    54.     PORTB = 0b00000111;
    55.     delay_s(5);
    56.     PORTB = 0b00000011;
    57.     delay_s(5);
    58.     PORTB = 0b00000001;
    59.     delay_s(5);
    60.     PORTB = 0b00000001;
    61.     delay_s(30);
    62.     PORTB = 0b11111111; // no output LEDs
    63.    
    64.     // reset team_ok values
    65.     team0_ok = 1;
    66.     team1_ok = 1;
    67.     team2_ok = 1;
    68.     team3_ok = 1;
    69.     team4_ok = 1;
    70.     team5_ok = 1;
    71.     team6_ok = 1;
    72.     team7_ok = 1;
    73.    
    74.     // check whether a switch is being pressed (cheating!)
    75.     if(RA0 == 0){
    76.         team0_ok = 0;
    77.     }
    78.     if(RA1 == 0){
    79.         team1_ok = 0;
    80.     }
    81.     if(RA2 == 0){
    82.         team2_ok = 0;
    83.     }
    84.     if(RA3 == 0){
    85.         team3_ok = 0;
    86.     }
    87.     if(RA4 == 0){
    88.         team4_ok = 0;
    89.     }
    90.     if(RA5 == 0){
    91.         team5_ok = 0;
    92.     }
    93.     if(RA6 == 0){
    94.         team6_ok = 0;
    95.     }
    96.     if(RA7 == 0){
    97.         team7_ok = 0;
    98.     }
    99.  
    100.     // loop to detect an input
    101.     for(;;) {
    102.         if((RA0 == 0)&&(team0_ok == 1)){
    103.             for(;;){
    104.                 PORTC = 0b10000000; //8
    105.                 beep(2);
    106.                 chase();
    107.             }
    108.         }
    109.         else if((RA1 == 0)&&(team1_ok == 1)){
    110.             for(;;){
    111.                 PORTC = 0b11111000; //7
    112.                 beep(2);
    113.                 chase();
    114.             }
    115.         }
    116.         else if((RA2 == 0)&&(team2_ok == 1)){
    117.             for(;;){
    118.                 PORTC = 0b10000010; //6
    119.                 beep(2);
    120.                 chase();
    121.             }
    122.         }
    123.         else if((RA3 == 0)&&(team3_ok == 1)){
    124.             for(;;){
    125.                 PORTC = 0b10010010; //5
    126.                 beep(2);
    127.                 chase();
    128.             }
    129.         }
    130.         else if((RA4 == 0)&&(team4_ok == 1)){
    131.             for(;;){
    132.                 PORTC = 0b10011001; //4
    133.                 beep(2);
    134.                 chase();
    135.             }
    136.         }
    137.         else if((RA5 == 0)&&(team5_ok == 1)){
    138.             for(;;){
    139.                 PORTC = 0b10110000; //3
    140.                 beep(2);
    141.                 chase();
    142.             }
    143.         }
    144.         else if((RA6 == 0)&&(team6_ok == 1)){
    145.             for(;;){
    146.                 PORTC = 0b10100100; //2
    147.                 beep(2);
    148.                 chase();
    149.             }
    150.         }
    151.         else if((RA7 == 0)&&(team7_ok == 1)){
    152.             for(;;){
    153.                 PORTC = 0b11111001; //1
    154.                 beep(2);
    155.                 chase();
    156.             }                  
    157.         }
    158.         else {
    159.             // nothing
    160.         }
    161.     }
    162. }
    163.  
     
  6. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    7,394
    1,606
    Well if it works then... SHIP IT !!! <grin>

    One thing I noticed in your first post is in your "loop to detect an input" you read an individual pin then move to the next one. Thus there is a (admittedly very small) chance that either two buttons are pressed at the same time (within one loop) or one is pressed just after it was sensed but still slightly before another button is pressed as it is being sensed.

    Ugh, I mean it is possible to not detect ties, and possible to miss a true winner.

    To solve that, inside the "loop to detect an input" I would read the pins once and store the value, then do the button test against the stored value. That eliminated the race to give a fair result.

    That does nothing to solve the issue of ties.

    Also, you need to press the reset button to get out of your loop. That makes it hard to make a total of many tries.
     
    Recon likes this.
  7. Recon

    Thread Starter Member

    Oct 16, 2009
    30
    0
    That makes sense. I'll give it a try.

    Just out of interest, approximately how quickly will the PIC cycle through the entire "loop to detect an input"? Are we talking tens/hundreds/thousands of times per second?
     
  8. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    7,394
    1,606
    That is a very good point for you to determine using the MPLAB simulator running your code, with the instruction clock set to match your hardware.

    Drop in a breakpoint at the start of the loop, run to it, reset the stopwatch, and run till it again stops.

    The stopwatch will tell you the whole story.
     
  9. Recon

    Thread Starter Member

    Oct 16, 2009
    30
    0
    Thanks. According to MPLAB SIM, it cycles through in just 9.6 uSecs.
     
Loading...