| /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 }