// Jonathan Go (jtg28), Ryan Connell (rbc24) // ECE 476 Lab 5 #include "main.h" //timer 0 compare match ISR //ONE ms resolution, i.e. interrupt is called every ms. interrupt [TIM0_COMP] void timer0_cmp(void){ if(bouncetime > 0) bouncetime--; if(updatetime > 0) updatetime--; if(revtime > 0) revtime--; if(wirelesstime > 0) wirelesstime--; if(beeptime > 0) beeptime--; } //timer 2 compare match ISR //This is used to generate square wave frequencies for sound generation interrupt [TIM2_COMP] void timer2_cmp(void){ if (alarm) { alarm_output = alarm_output ^ 0xff; SOUND_PORT = alarm_output; } } /** * Checks the buttons by looking at the state of the debouncer * as well as the variable PushedButton. * This function is called every UPDATETIME ms. */ void update(void){ updatetime = UPDATETIME; // Toggles the blink flag to make the curDigit either appear or disappear. if (--blinkcount == 0) { blink = blink ^ 1; blinkcount = BLINKCOUNT; } if (timerOn) { // Handle timer stuff float curTime; if (++tenths == 10) { tenths = 0; if (++seconds == 60) { seconds = 0; if (++minutes == 60) { minutes = 0; hours++; } } } } /** * Checks if the wireless has been triggered already. * Will not enter this if wireless has been activated * within the past 5 seconds. */ if (wirelesstime == 0 && (0x02 & ~WIRELESS_SENSOR_PIN)) { wirelesstime = FIVESECONDS; // Locks if it's not in the LOCKED state, beep 3 times if (curState != LOCKED) { curState = LOCKED; numBeepToggles = 6; // since it's div by 2 + 1 } // Unlocks, beeps twice else { curState = DISPLAYTIME; numBeepToggles = 4; } } // Want to check this continuously if (PushFlag && (PushedButton & SEAT) == SEAT) { switch (curState) { case LOCKED: fastbeep = 1; numBeepToggles = 2; break; } } if(PushFlag && Unchecked){ // means a button has been pressed if (verifyButton(OK)) { switch (curState) { case LOCKED: if (arrayToInt(password_try) == arrayToInt(u.password)) { curState = DISPLAYTIME; // clear the password try password_try[0] = 0; password_try[1] = 0; password_try[2] = 0; password_try[3] = 0; } break; case DISPLAYTIME: timerOn = timerOn ^ 1; if (timerOn == 1) resetTimer(); break; case ACTIVATELOCK: curState = LOCKED; break; case DISPLAYWEIGHT: if (curAction == VIEW) curAction = MODIFY; else curAction = VIEW; break; case DISPLAYPASSWORD: if (curAction == VIEW) curAction = MODIFY; else curAction = VIEW; break; } } else { switch (curState) { case LOCKED: updateDigit(password_try); break; case DISPLAYWEIGHT: if (curAction == MODIFY) { updateDigit(u.weight); } else updateState(); break; case DISPLAYPASSWORD: if (curAction == MODIFY) { updateDigit(u.password); } else updateState(); break; default: updateState(); break; } } } if(curState == LOCKED && locked_sensor_state != curSensor) { // if it hasn't already tripped, trip the alarm now if(!alarmOn) { fastbeep = 1; numBeepToggles = 50; locked_sensor_state = curSensor; } } printState(); } /** * This function is called every BEEPTIME ms. * This toggles the alarm, depending on numBeepToggles. * Number of actual beeps = (numBeepToggles / 2) + 1 */ void beep(void) { if (fastbeep) beeptime = FASTBEEPTIME; else beeptime = BEEPTIME; if(numBeepToggles > 0) { alarm = alarm ^ 0xff; numBeepToggles--; alarmOn = 1; } else { alarm = 0x00; SOUND_PORT = 0x00; fastbeep = 0; alarmOn = 0; } } /** * Verifies that the button has been pressed. */ char verifyButton(char b) { Unchecked = 0; return (PushedButton & b); } /** * Updates the state of the global state machine. * Called if a button has been pushed to change state * and the state is not LOCKED. * See state diagram for the state transitions. */ void updateState(void) { if(verifyButton(RIGHT) && ++curState >= NUMSTATES){ curState = 1; // 0 is reserved for locked state } else if(verifyButton(LEFT) && --curState < 1){ curState = NUMSTATES - 1; } } /** * Called manually when a pulse is received from the magnetic sensor. */ void updateParams(void){ int delta; delta = TENSECONDS - revtime; if (delta < 10000) { ready = 0; curDistance += circInMiles; //(3600 sec/hr)*(81.75 in/rev)*(1/63360 mi/in) curVelocity = velocityMultiplierMPH/delta; // Only calculate calories burned if timer is on! if (timerOn) { float velocityms; velocityms = velocityMultiplierMS/delta; // 4.4 accounts for the 1/2 and the conversion to kg curCalories += arrayToInt(u.weight) * velocityms * velocityms * calorieMultiplier; } // Updates the maximum velocity so far if (curVelocity > curMaxVelocity) curMaxVelocity = curVelocity; } else curVelocity = 0; printState(); ready = 1; revtime = TENSECONDS; } // Converts an array of length 4 into an int for printing int arrayToInt(char num[]) { int ret = 0; ret += (int)num[0] * 1000; ret += (int)num[1] * 100; ret += (int)num[2] * 10; ret += num[3]; return ret; } /** * Updates the array passed to it. * Should only be called if the button pressed is not OK. */ void updateDigit(char num[]) { if(verifyButton(LEFT) && --curDigit < 0) { curDigit = MAXDIGITS-1; } else if(verifyButton(RIGHT) && ++curDigit >= MAXDIGITS) { curDigit = 0; } else if(verifyButton(UP) && ++num[curDigit] > 9) { num[curDigit] = 0; } else if(verifyButton(DOWN) && --num[curDigit] < 0) { num[curDigit] = 9; } } // The debouncer state machine, run every DEBOUNCETIME ms. void debounce(void){ bouncetime = BOUNCETIME; // reset the task timer switch (PushState){ case NOPUSH: if (((~BUTTON_PIN)& 0b11111010) != 0x00){ // Check if any buttons have been pushed PushState = MAYBEPUSH; PushedButton = ~BUTTON_PIN; // Save which buttons are pressed } else PushState = NOPUSH; break; case MAYBEPUSH: if (~BUTTON_PIN == PushedButton){ // Check if the same buttons are pressed PushState = PUSHED; PushFlag = 1; } else PushState = NOPUSH; break; case PUSHED: if (~BUTTON_PIN == PushedButton){ // Check if the same buttons are pressed PushState = PUSHED; } else PushState = MAYBENOPUSH; // Signal is bouncing? break; case MAYBENOPUSH: if (~BUTTON_PIN == PushedButton){ // Check if the same buttons are pressed PushState = PUSHED; // Signal bounced back up } else{ PushState = NOPUSH; PushFlag = 0; Unchecked = 1; // Just released the button, so the button is not checked } break; } } // Prints the current state void printState(void){ blink = blink ^ 1; lcd_clear(); lcd_gotoxy(0,0); switch (curState){ case LOCKED: lcd_putsf("*****LOCKED*****"); sprintf(lcd_buffer,"Password? %04d",arrayToInt(password_try)); if (blink) { // blinks the current digit by blanking it out every other time the // state is printed chartoblink = curDigit + 10; lcd_buffer[chartoblink] = ' '; } lcd_puts(lcd_buffer); break; case DISPLAYSPEED: sprintf(lcd_buffer,"Speed(mph):%2.4f",curVelocity); lcd_puts(lcd_buffer); lcd_gotoxy(0,1); sprintf(lcd_buffer,"Max:%2.4f",curMaxVelocity); lcd_puts(lcd_buffer); break; case DISPLAYDIST: lcd_putsf("Distance"); lcd_gotoxy(0,1); sprintf(lcd_buffer,"%3.3f mi",curDistance); lcd_puts(lcd_buffer); break; case DISPLAYTIME: lcd_putsf("Time"); lcd_gotoxy(0,1); sprintf(lcd_buffer,"%02d:%02d:%02d:%d",hours,minutes,seconds,tenths); lcd_puts(lcd_buffer); break; case DISPLAYCALORIES: lcd_putsf("Calories Burned"); lcd_gotoxy(0,1); sprintf(lcd_buffer,"%3.3f Calories",curCalories); lcd_puts(lcd_buffer); break; case DISPLAYWEIGHT: if (curAction == MODIFY) lcd_putsf("Edit Weight"); else lcd_putsf("Weight"); lcd_gotoxy(0,1); sprintf(lcd_buffer,"%04d lbs",arrayToInt(u.weight)); if (curAction == MODIFY && blink) { // blinks the current digit by blanking it out every other time the // state is printed lcd_buffer[curDigit] = ' '; } lcd_puts(lcd_buffer); break; case DISPLAYPASSWORD: if (curAction == MODIFY) lcd_putsf("Edit Password"); else lcd_putsf("Password"); lcd_gotoxy(0,1); sprintf(lcd_buffer,"%04d",arrayToInt(u.password)); if (curAction == MODIFY && blink) { // blinks the current digit by blanking it out every other time the // state is printed lcd_buffer[curDigit] = ' '; } lcd_puts(lcd_buffer); break; case ACTIVATELOCK: lcd_putsf("Press OK to LOCK"); break; default: lcd_putsf("Undefined state"); break; } } // Resets the timer and associated values that are dependant on timer. void resetTimer(void) { hours = 0; minutes = 0; seconds = 0; tenths = 0; curDistance = 0; curVelocity = 0; curCalories = 0; } // General initialization routines void initialize(void){ //set up the input ports BUTTON_DDR = 0x00; WIRELESS_SENSOR_DDR = 0x00; //set up the output ports SOUND_DDR = 0xff; DISPLAY_DDR = 0xff; //set up timers TIMSK = 0b10000010; // timer 0 cmp ISR, timer 2 cmp ISR TIFR = 0b10000000; //set up timer 0 TCNT0 = 0; TCCR0 = 0b00001011; //prescalar = 64 with clear on match OCR0 = 249; // 250 ticks = 1 ms //set up timer 2 TCNT2 = 0; TCCR2 = 0b00001011; // prescalar = 8 with clear on match OCR2 = 99; // generates 2.5kHz // task timers bouncetime = BOUNCETIME; updatetime = UPDATETIME; revtime = TENSECONDS; beeptime = BEEPTIME; // initialize the debouncer PushState = NOPUSH; PushedButton = 0x00; // initialize the magnetic sensor variables curSensor = 0x01 & ~WIRELESS_SENSOR_PIN; prevSensor = 0x01 & ~WIRELESS_SENSOR_PIN; curVelocity = 0; curMaxVelocity = 0; curDistance = 0; ready = 1; // initialize the general state machine curState = LOCKED; curAction = VIEW; locked_sensor_state = curSensor; // Blinking variables curDigit = 0; blink = 0; chartoblink = 0; blinkcount = BLINKCOUNT; u.password[0] = 1; u.password[1] = 0; u.password[2] = 0; u.password[3] = 0; u.weight[0] = 0; u.weight[1] = 1; u.weight[2] = 5; u.weight[3] = 0; password_try[0] = 0; password_try[1] = 0; password_try[2] = 0; password_try[3] = 0; // Initialize alarm variables alarm = 0; alarmOn = 0; alarm_output = 0x00; fastbeep = 0; numBeepToggles = 0; wirelesstime = 0; // Timer related hours = 0; minutes = 0; seconds = 0; tenths = 0; timerOn = 0; lcd_init(LCDWIDTH); printState(); } int main(void){ initialize(); // Start handling interrupts #asm sei #endasm while(1){ if(bouncetime == 0) debounce(); if(updatetime == 0) update(); curSensor = 0x01 & ~WIRELESS_SENSOR_PIN; if(prevSensor != curSensor) { prevSensor = curSensor; if(ready && (curSensor == 0x01)) updateParams(); } if(ready && revtime == 0) updateParams(); if(beeptime == 0) beep(); } return 0; }