/Users/michael/Documents/Venturers/JOTA:JOTI/Circuits/Move it Carefully/Published documents/main.c
  1 /*
  2  * File:   main.c
  3  * Author: Michael Gross
  4  * Move it Carefully project, Countdown timer with switches and level indicators
  5  * Scouts have to unplug and move the box between 2 places
  6  * and hold down all 6 switches and keep the box level before the countdown timer expires.
  7  * Runs on a 4 digit 7 segment display
  8  * Can be set to start from 1 to 15 minutes.
  9  * Last Update Sun 14th April, 2024, 11:26am
 10  */
 11 
 12 
 13 // 'C' source line config statements
 14 
 15 // CONFIG
 16 
 17 #include <xc.h>                // Compiler library
 18 #include <pic16f877a.h>        // Include library for PIC16F877A
 19 
 20                                // Setting the PIC16F877A configuration bits
 21 #pragma config FOSC = HS       // External oscillator in HS (High Speed) mode
 22 #pragma config WDTE = OFF      // Watchdog Timer desable
 23 #pragma config PWRTE = OFF     // Power-up Timer desable
 24 #pragma config BOREN = ON      // Brown-out Reset enable
 25 #pragma config LVP = OFF       // Disabled low voltage programming (high Vpp)
 26 #pragma config CPD = OFF        // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
 27 #pragma config WRT = OFF        // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
 28 #pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)
 29 
 30 #define _XTAL_FREQ 4000000     // Oscillator frequency (4 MHz)
 31 # define TRUE 1 
 32 
 33 //Define Variables
 34 unsigned short i, Digit1, Digit2, Digit3, Digit4, Blank_digit_3, Blank_digit_4; //Define digits 1-4 and Blanking test values (minutes only))
 35 unsigned int Minutes, Seconds, Switches, Switches_catch, Tilt, Tilt_catch, Pass_Fail; //Define Minute and seconds integers
 36 
 37 
 38 
 39 //------ Function to Return mask for common cathode 7-seg. display
 40 unsigned short mask(unsigned short num) {
 41  switch (num) {
 42  case 0 : return 0x3F;
 43  case 1 : return 0x06;
 44  case 2 : return 0x5B;
 45  case 3 : return 0x4F;
 46  case 4 : return 0x66;
 47  case 5 : return 0x6D;
 48  case 6 : return 0x7C;
 49  case 7 : return 0x07;
 50  case 8 : return 0x7F;
 51  case 9 : return 0x67;
 52  } //case end
 53 }
 54 
 55 void main() {
 56   ADCON0 = 0b10000000; //This turns the ADC off
 57   ADCON1 = 0b10000110; //This turns the ADC off
 58   CMCON = 7;    //Disable compararator.
 59   TRISA = 0x00; // Set PORTA direction to be output. Port A drives Transistors for Leds and Beeper.
 60   PORTA = 0x00;  // Turn OFF  PORTA, by making them low
 61   TRISC = 0xFF; // Set PORTC direction to be input. Port C is connected to the dip switches and is used for the no of start minutes.
 62   TRISD = 0x00; // Set PORTD direction to be outputs. Port D drives the segments
 63   PORTD = 0x00; // Turn OFF LEDs on PORTD, by making them low
 64   TRISE = 0b10000111; // Set PORTE direction to be inputs. Port E is connected to the XLR plug that completes the game.
 65  
 66 
 67   Seconds=00;   // Initial Value of Seconds, as we start with whole minutes
 68   Minutes=0;   // Initial Value of Minutes
 69   Switches=0;   // Initial Value of Switches - to count the number of times the 6 side switches have been let go.
 70   Switches_catch=0; //Initial value - used to check for switches that are let go during display cycle.
 71   Tilt_catch=0; //Initial value - used to check for tilt triggered during display cycle.
 72   Tilt=0;   // Initial Value of Tilt - to count the number of times they have been triggered. 
 73   Pass_Fail=0;   //Used to determine whether they have succeeded or not.
 74    
 75   // Set Start Minutes, value selected and added in Binary on dip switches.
 76         {if (RC0==1) Minutes=Minutes+1;}
 77         {if (RC1==1) Minutes=Minutes+2;}
 78         {if (RC2==1) Minutes=Minutes+4;}
 79         {if (RC3==1) Minutes=Minutes+8;}
 80         {if (RC0==0&&RC1==0&&RC2==0&&RC3==0) Minutes=1;} //If all switches are off then just start at 1 minute.
 81   
 82   //Extract single digits
 83   do {
 84         Digit1 = Seconds%10;  // Extract Seconds Ones Digit
 85         Digit1 = mask(Digit1);
 86         Digit2 = (Seconds/10)%10; // Extract Seconds Tens Digit
 87         Digit2 = mask(Digit2);
 88         Digit3 = Minutes%10; // Extract Minutes Digit
 89         Blank_digit_3 = Digit3;  // to check if zero for digit blanking
 90         Digit3 = mask(Digit3);
 91         Digit4 = (Minutes/10);// Extract Minutes Tens Digit
 92         Blank_digit_4 = Digit4;  // to check if zero for digit blanking
 93         Digit4 = mask(Digit4);
 94   
 95         // Wait 20ms, this is required to allow R6 and R7 enough time to charge C5 so that RC6 is set to 1. 
 96         //Without this RC6 would be 0 until about the 10th iteration of the 1st display cycle, and register as a tilt, until it has charged sufficiently.
 97             __delay_ms(20); //Wait
 98  
 99 // Display values on the displays - approx 125 times = 1 second
100         for (i = 0; i<=125; i++) {
101             
102               if (RC6==0) { // Check tilt  and increment tilt count if triggered. 0=triggered, 1=not triggered
103                 Tilt_catch=1; 
104             } 
105               
106             if (RC7==1) { // Check switches and increment switch count if let go. - 1=switch let go, 0=switch not let go
107                 Switches_catch=1; 
108             } 
109 
110             
111                 if (i==1){    //Start buzzer at start of display cycle, and turn off later after 5 cycles to simulate ticking sound..
112                   RA5=1;  //Turn Buzzer on
113                 }
114 
115 
116                  if (Blank_digit_4==0) {  // If the 4th digit is blank, then it will mean 1 less delay, so timer will speed up, this adds the delay back in to keep the timer pace the same
117                      RA2 = 0;
118                      __delay_ms(1);
119 
120                  }
121                  if  ((Blank_digit_3==0) && (Blank_digit_4==0)){  //Same as above but if 3 is blank as well we add in another delay
122                      RA1 = 0;
123                      __delay_ms(1);
124                              }
125 
126                 PORTD = Digit1;
127                 RA0 = 1;      // Select single seconds digit
128                 RA1 = 0;
129                 RA2 = 0;
130                 RA3 = 0;
131                 __delay_ms(3); //Wait
132 
133 
134                 PORTD = Digit2;
135                 RA0 = 0;
136                 RA1 = 1;     // Select 10's seconds Digit
137                 RA2 = 0;
138                 RA3 = 0;
139                 __delay_ms(3); //Wait
140                 
141               
142              //If switches or tilt are triggered, keep buzzer going (may have been turned off after 5 cycles))
143                  if (((i>5) && (RC6==0) || (RC7==1))) {  //check to see if anything is triggered
144                   RA5=1; //Turn buzzer on 
145                     }
146              //End of tick cycle, if nothing is triggered.
147                 else if ((i==5) && (RC6==1) &&(RC7==0)) {//Turn Buzzer off after 5 out of the 50 display cycles so the beep length is short, , unless tilt switches (RC6) or switches (RC7) are triggered, then leave it on.
148                   RA5=0; //Turn buzzer off 
149                     }
150     
151                 if  ((Blank_digit_3==0) && (Blank_digit_4==0)){       //If both digit 3 and 4 are blank, turn off Digit 2 after displaying it so the delay time is the same otherwise the digit will be brighter.
152                      RA1 = 0;
153                              }
154 
155                 if  ((Blank_digit_3>0) || ((Blank_digit_4>0) && (Blank_digit_3==0))){     // Ensure digit 3 turns on, if not 0, or if digit 3 is zero, but digit 4 is greater than 1 (e.g 10)
156                 PORTD = Digit3;
157                 RA0 = 0;
158                 RA1 = 0;
159                 RA2 = 1;     // Select single minutes Digit
160                 RA3 = 0;
161                 __delay_ms(3); //Wait
162                 }
163 
164 
165                 if (Blank_digit_4>0 && RC6==1 && RC7==0) {        // If Digit 4 is not blank, and Switches and Tilt have not been triggered and then display it.
166                   PORTD   = Digit4;
167                   RA0 = 0;
168                   RA1 = 0;
169                   RA2 = 0;
170                   RA3 = 1;     // Select 10's minutes Digit
171                   __delay_ms(3); //Wait
172                      }
173             }
174 
175             //Set seconds.
176                   if (Seconds==0) {   //if seconds are zero, decrease minutes and change seconds to 60
177                       Minutes--;
178                       Seconds=60;
179                   }
180 
181                     Seconds--;    // decrease seconds by 1 (if they were zero above, then we set them to 60 above, and subtract 1 to start at 59))
182                               // otherwise 0 seconds would not display.
183 
184         // Check to see if anything has been triggered, if so change 1st digit and turn buzzer on.
185               if ((Tilt_catch==1) && (Switches_catch==0)) {      //If only the tilt switch has been triggered, increase the count, and display t
186                Tilt++;
187                PORTD = 0x78;       //Display T
188                RA0 = 0;      // Select 10's Minutes  digit
189                RA1 = 0;
190                RA2 = 0;
191                RA3 = 1;
192                RA5=1;  //Turn Buzzer on
193                __delay_ms(15); //Wait
194               }
195 
196               if ((Tilt_catch==0) && (Switches_catch==1)) {     //If only the switches have been let go then increase the count and display S
197                 Switches++;
198                 PORTD = 0x6D;       // Display S  
199                 RA0 = 0;      // Select 10's Minutes digit
200                 RA1 = 0;
201                 RA2 = 0;
202                 RA3 = 1;
203                 RA5=1;  //Turn Buzzer on
204                 __delay_ms(15); //Wait
205               }
206                     
207                if ((Tilt_catch==1) && (Switches_catch==1)) {     //If the switches have been let go and tilt triggered, show a b for both
208                 Switches++;
209                 Tilt++;     
210                 PORTD = 0x7C;       // Display b for both  
211                 RA0 = 0;      // Select 10's Minutes digit
212                 RA1 = 0;
213                 RA2 = 0;
214                 RA3 = 1;
215                 RA5=1;  //Turn Buzzer on
216                 __delay_ms(15); //Wait
217               }
218                 // Reset catch counts
219                   Tilt_catch=0;
220                   Switches_catch=0;
221 
222             } 
223  
224 
225     while((Seconds>0 || Minutes>0) && RE1==1);    // endless loop, unless seconds and minutes are zero (time has run out), or the finish plug has been put in.
226  
227 
228   
229   
230 // Finish section, either timer has expired or finish plug has been put in.
231     
232     //Ensure that the buzzer is off.
233         RA5=0;
234         
235    //Turn the displays off
236         RA0 = 0;     
237         RA1 = 0;
238         RA2 = 0;
239         RA3 = 0;
240     
241 // Display pass or fail and scores
242         
243   // Do this 1st, so they can not put the plug in after the timer expires and change the pass/fail status.
244        if (RE1==1) {   
245             Pass_Fail=0;    //Set Display to show Fail
246                  
247         } else {
248             Pass_Fail=1;   //Change to pass if time has not run out and finish plug is connected.
249 
250         }    
251     
252 // Beep three times to indicate end of program
253     for (i = 0; i<=3; i++) { 
254                  RA5=1;
255                  __delay_ms(150);    //Wait 
256                  RA5=0;
257                  __delay_ms(150);    //Wait 
258                  
259     }
260     
261  //Loop to display Pass/Fail and scores continuously
262     do {
263             if (Pass_Fail==0) {   //Set Display to show Fail
264                 Digit1=0x38;  //L
265                 Digit2=0x06;  //I
266                 Digit3=0x77;  //A
267                 Digit4=0x71;  //F
268             } else {
269                 Digit1=0x6D;  //S   //Change to pass if time has not run out and finish plug is connected.
270                 Digit2=0x6D;  //S
271                 Digit3=0x77;  //A
272                 Digit4=0x73;  //P   
273             } 
274             
275                Display_digits();    //Display pass or fail for 1 second.           
276   
277             __delay_ms(200);    //Wait 1/2 second
278        
279   // Extract Switch score and Display  for 1 seconds
280             if (Switches<100) {
281                 Digit1 = Switches%10;  // Extract Switch Ones Digit
282                 Digit1 = mask(Digit1);
283                 Digit2 = (Switches/10)%10; // Extract Switch Tens Digit
284                 Digit2 = mask(Digit2);
285                 Digit3 = 0x40;
286                }
287        
288             else {
289                 Digit1 = 0x06;
290                 Digit2 = 0x76;
291                 Digit3 = 0x40;
292                 }
293        
294                 Digit4 = 0x6D;        //Display S
295                 Display_digits();     //Display the scores
296                 RA1 = 0;             //Turn off digit 4           
297                 __delay_ms(100);    //Wait 1/2 second
298 
299        // Extract Tilt score and Display  for 1 seconds 
300                 if (Tilt<100) {
301                 Digit1 = Tilt%10;  // Extract Tilt Ones Digit
302                 Digit1 = mask(Digit1);
303                 Digit2 = (Tilt/10)%10; // Extract Tilt Tens Digit
304                 Digit2 = mask(Digit2);
305                 Digit3 = 0x40;
306                 }
307 
308             else {
309                 Digit1 = 0x06;
310                 Digit2 = 0x76;
311                 Digit3 = 0x40;
312                 }
313                 
314                 Digit4 = 0x78;       //Display T
315                 Display_digits();               //Display the scores
316 }
317        
318    while(1) ;
319     
320 }
321 
322 
323 
324 Display_digits()
325 {
326            for (i = 0; i<=150; i++) {     
327                 PORTD = Digit4;       // Display S  
328                       RA0 = 0;      // Select 10's Minutes digit
329                       RA1 = 0;
330                       RA2 = 0;
331                       RA3 = 1;
332                       __delay_ms(3); //Wait  
333                     
334                 PORTD = Digit3;       
335                       RA0 = 0;      // Select 10's Minutes digit
336                       RA1 = 0;
337                       RA2 = 1;
338                       RA3 = 0;
339                       __delay_ms(3); //Wait  
340                       
341                  PORTD = Digit2;
342                       RA0 = 0;      // Select single seconds digit
343                       RA1 = 1;
344                       RA2 = 0;
345                       RA3 = 0;
346                       __delay_ms(3); //Wait
347 
348                  PORTD = Digit1;
349                        RA0 = 1;
350                        RA1 = 0;     // Select 10's seconds Digit
351                        RA2 = 0;
352                        RA3 = 0;
353                         __delay_ms(3); //Wait   
354                  RA0 = 0;         //Turn off digit 1 
355             }
356  
357 }