__interrupt #endif void TIMER0_A0_ISR(void) { static u8 button_lock_counter = 0; // Disable IE TA0CCTL0 &= ~CCIE; // Reset IRQ flag TA0CCTL0 &= ~CCIFG; // Add 1 sec to TACCR0 register (IRQ will be asserted at 0x7FFF and 0xFFFF = 1 sec intervals) TA0CCR0 += 32768; // Enable IE TA0CCTL0 |= CCIE; // Add 1 second to global time clock_tick(); // Set clock update flag display.flag.update_time = 1; #ifdef BLUEROBIN // While SimpliciTI stack operates or BlueRobin searches, freeze system state if (is_rf() || is_bluerobin_searching()) { // SimpliciTI automatic timeout if (sRFsmpl.timeout == 0) { simpliciti_flag |= SIMPLICITI_TRIGGER_STOP; } else { sRFsmpl.timeout--; } // switch message after received packet if (sRFsmpl.mode == SIMPLICITI_SYNC) { if (sRFsmpl.display_sync_done == 0) { display_chars(LCD_SEG_L2_5_0, (u8 *) " SYNC", SEG_ON); } else { sRFsmpl.display_sync_done--; } } // Exit from LPM3 on RETI _BIC_SR_IRQ(LPM3_bits); return; } #endif // ------------------------------------------------------------------- // Service modules that require 1/min processing if (sTime.drawFlag >= 2) { // Measure battery voltage to keep track of remaining battery life request.flag.voltage_measurement = 1; // Check if alarm needs to be turned on check_alarm(); } // ------------------------------------------------------------------- // Service active modules that require 1/s processing // Generate alarm signal if (sAlarm.state == ALARM_ON) { // Decrement alarm duration counter if (sAlarm.duration-- > 0) { request.flag.buzzer = 1; } else { sAlarm.duration = ALARM_ON_DURATION; stop_alarm(); } } // Do a temperature measurement each second while menu item is active if (is_temp_measurement()) request.flag.temperature_measurement = 1; // Do a pressure measurement each second while menu item is active if (is_altitude_measurement()) { // Countdown altitude measurement timeout while menu item is active sAlt.timeout--; // Stop measurement when timeout has elapsed if (sAlt.timeout == 0) { stop_altitude_measurement(); // Show ---- m/ft display_chars(LCD_SEG_L1_3_0, (u8 *) "----", SEG_ON); // Clear up/down arrow display_symbol(LCD_SYMB_ARROW_UP, SEG_OFF); display_symbol(LCD_SYMB_ARROW_DOWN, SEG_OFF); } else { if (bmp_used) { bmp_ps_start(); } } // In case we missed the IRQ due to debouncing, get data now if ((PS_INT_IN & PS_INT_PIN) == PS_INT_PIN) request.flag.altitude_measurement = 1; } // Count down timeout if (is_acceleration_measurement()) { // Countdown acceleration measurement timeout sAccel.timeout--; // Stop measurement when timeout has elapsed if (sAccel.timeout == 0) { if (bmp_used) { bmp_as_stop(); } else { cma_as_stop(); } // Show ---- display_chars(LCD_SEG_L1_3_0, (u8 *) "----", SEG_ON); // Clear up/down arrow display_symbol(LCD_SYMB_ARROW_UP, SEG_OFF); display_symbol(LCD_SYMB_ARROW_DOWN, SEG_OFF); display_symbol(LCD_SEG_L1_DP1, SEG_OFF); } // If DRDY is (still) high, request data again if ((AS_INT_IN & AS_INT_PIN) == AS_INT_PIN) request.flag.acceleration_measurement = 1; } #ifdef BLUEROBIN // If BlueRobin transmitter is connected, get data from API if (is_bluerobin()) get_bluerobin_data(); #endif // If battery is low, decrement display counter if (sys.flag.low_battery) { if (sBatt.lobatt_display-- == 0) { message.flag.prepare = 1; message.flag.type_lobatt = 1; sBatt.lobatt_display = BATTERY_LOW_MESSAGE_CYCLE; } } // If a message has to be displayed, set display flag if (message.all_flags) { if (message.flag.prepare) { message.flag.prepare = 0; message.flag.show = 1; } else if (message.flag.erase) // message cycle is over, so erase it { message.flag.erase = 0; display.flag.full_update = 1; } } // ------------------------------------------------------------------- // Check idle timeout, set timeout flag if (sys.flag.idle_timeout_enabled) { if (sTime.system_time - sTime.last_activity > INACTIVITY_TIME) sys.flag.idle_timeout = 1; //setFlag(sysFlag_g, SYS_TIMEOUT_IDLE); } // ------------------------------------------------------------------- // Turn the Backlight off after timeout if (sButton.backlight_status == 1) { if (sButton.backlight_timeout > BACKLIGHT_TIME_ON) { //turn off Backlight P2OUT &= ~BUTTON_BACKLIGHT_PIN; P2DIR &= ~BUTTON_BACKLIGHT_PIN; sButton.backlight_timeout = 0; sButton.backlight_status = 0; } else { sButton.backlight_timeout++; } } // ------------------------------------------------------------------- // Detect continuous button high states // Trying to lock/unlock buttons? if (BUTTON_NUM_IS_PRESSED && BUTTON_DOWN_IS_PRESSED) { if (button_lock_counter++ > LEFT_BUTTON_LONG_TIME) { // Toggle lock / unlock buttons flag sys.flag.lock_buttons = ~sys.flag.lock_buttons; // Show "buttons are locked/unlocked" message synchronously with next second tick message.flag.prepare = 1; if (sys.flag.lock_buttons) message.flag.type_locked = 1; else message.flag.type_unlocked = 1; // Reset button lock counter button_lock_counter = 0; } } else // Trying to create a long button press? { // Reset button lock counter button_lock_counter = 0; if (BUTTON_STAR_IS_PRESSED) { sButton.star_timeout++; // Check if button was held low for some seconds if (sButton.star_timeout > LEFT_BUTTON_LONG_TIME) { button.flag.star_long = 1; button.flag.star_not_long = 0; sButton.star_timeout = 0; // Return interrupt edge to normal value BUTTONS_IES &= ~BUTTON_STAR_PIN; } } else // there was a button press not long enough { sButton.star_timeout = 0; } if (BUTTON_NUM_IS_PRESSED) { sButton.num_timeout++; // Check if button was held low for some seconds if (sButton.num_timeout > LEFT_BUTTON_LONG_TIME) { button.flag.num_long = 1; button.flag.num_not_long = 0; sButton.num_timeout = 0; // Return interrupt edge to normal value BUTTONS_IES &= ~BUTTON_NUM_PIN; } } else // there was a button press not long enough { sButton.num_timeout = 0; } } // Exit from LPM3 on RETI _BIC_SR_IRQ(LPM3_bits); }
// ************************************************************************************************* // @fn test_mode // @brief Manual test mode. Activated by holding buttons STAR and UP simultaneously. // Cancelled by any other button press. // @param none // @return none // ************************************************************************************************* void test_mode(void) { u8 test_step, start_next_test; u8 *str; u8 i; // Disable timer - no need for a clock tick Timer0_Stop(); // Disable LCD charge pump while in standby mode // This reduces current consumption by ca. 5µA to ca. 10µA LCDBVCTL = 0; // Show welcome screen display_chars(LCD_SEG_L1_3_0, (u8 *) "TONY", SEG_ON); display_chars(LCD_SEG_L2_4_0, (u8 *) "MAGLA", SEG_ON); display_symbol(LCD_SEG_L1_COL, SEG_ON); display_symbol(LCD_ICON_HEART, SEG_ON); display_symbol(LCD_ICON_STOPWATCH, SEG_ON); display_symbol(LCD_ICON_RECORD, SEG_ON); display_symbol(LCD_ICON_ALARM, SEG_ON); display_symbol(LCD_ICON_BEEPER1, SEG_ON); display_symbol(LCD_ICON_BEEPER2, SEG_ON); display_symbol(LCD_ICON_BEEPER3, SEG_ON); display_symbol(LCD_SYMB_ARROW_UP, SEG_ON); display_symbol(LCD_SYMB_ARROW_DOWN, SEG_ON); display_symbol(LCD_SYMB_AM, SEG_ON); // Hold watchdog WDTCTL = WDTPW + WDTHOLD; // Wait for button press _BIS_SR(LPM3_bits + GIE); __no_operation(); // Clear display display_all_off(); #ifdef USE_LCD_CHARGE_PUMP // Charge pump voltage generated internally, internal bias (V2-V4) generation // This ensures that the contrast and LCD control is constant for the whole battery lifetime LCDBVCTL = LCDCPEN | VLCD_2_72; #endif // Renenable timer Timer0_Start(); // Debounce button press Timer0_A4_Delay(CONV_MS_TO_TICKS(100)); while (1) { // Check button event if (BUTTON_STAR_IS_PRESSED && BUTTON_UP_IS_PRESSED) { // Start with test #0 test_step = 0; start_next_test = 1; while (1) { if (start_next_test) { // Clean up previous test display display_all_off(); start_next_test = 0; switch (test_step) { case 0: // All LCD segments on display_all_on(); // Wait until buttons are off while (BUTTON_STAR_IS_PRESSED && BUTTON_UP_IS_PRESSED) ; break; case 1: // Altitude measurement display_altitude(LINE1, DISPLAY_LINE_UPDATE_FULL); for (i = 0; i < 2; i++) { while ((PS_INT_IN & PS_INT_PIN) == 0) ; do_altitude_measurement(FILTER_OFF); display_altitude(LINE1, DISPLAY_LINE_UPDATE_PARTIAL); } stop_altitude_measurement(); break; case 2: // Temperature measurement display_temperature(LINE1, DISPLAY_LINE_UPDATE_FULL); for (i = 0; i < 4; i++) { Timer0_A4_Delay(CONV_MS_TO_TICKS(250)); temperature_measurement(FILTER_OFF); display_temperature(LINE1, DISPLAY_LINE_UPDATE_PARTIAL); } break; case 3: // Acceleration measurement if (bmp_used) { bmp_as_start(); } else { cma_as_start(); } for (i = 0; i < 4; i++) { Timer0_A4_Delay(CONV_MS_TO_TICKS(250)); if (bmp_used) { bmp_as_get_data(sAccel.xyz); } else { cma_as_get_data(sAccel.xyz); } str = int_to_array(sAccel.xyz[0], 3, 0); display_chars(LCD_SEG_L1_2_0, str, SEG_ON); str = int_to_array(sAccel.xyz[2], 3, 0); display_chars(LCD_SEG_L2_2_0, str, SEG_ON); } if (bmp_used) { bmp_as_stop(); } else { cma_as_stop(); } break; case 4: // BlueRobin test button.flag.up = 1; sx_bluerobin(LINE1); Timer0_A4_Delay(CONV_MS_TO_TICKS(100)); get_bluerobin_data(); display_heartrate(LINE1, DISPLAY_LINE_UPDATE_FULL); stop_bluerobin(); break; } // Debounce button Timer0_A4_Delay(CONV_MS_TO_TICKS(200)); } // Check button event if (BUTTON_STAR_IS_PRESSED) { test_step = 1; start_next_test = 1; } else if (BUTTON_NUM_IS_PRESSED) { test_step = 2; start_next_test = 1; } else if (BUTTON_UP_IS_PRESSED) { test_step = 3; start_next_test = 1; } else if (BUTTON_DOWN_IS_PRESSED) { test_step = 4; start_next_test = 1; } else if (BUTTON_BACKLIGHT_IS_PRESSED) { // Wait until button has been released (avoid restart) while (BUTTON_BACKLIGHT_IS_PRESSED) ; // Disable LCD and LCD charge pump LCDBCTL0 &= ~BIT0; LCDBVCTL = 0; // Debounce button press Timer0_A4_Delay(CONV_MS_TO_TICKS(500)); // Disable timer - no need for a clock tick Timer0_Stop(); // Hold watchdog WDTCTL = WDTPW + WDTHOLD; // Sleep until button is pressed (ca. 4µA current consumption) _BIS_SR(LPM4_bits + GIE); __no_operation(); // Force watchdog reset for a clean restart WDTCTL = 1; } #ifdef USE_WATCHDOG // Service watchdog WDTCTL = WDTPW + WDTIS__512K + WDTSSEL__ACLK + WDTCNTCL; #endif // To LPM3 _BIS_SR(LPM3_bits + GIE); __no_operation(); } } else { // Debounce button Timer0_A4_Delay(CONV_MS_TO_TICKS(100)); button.all_flags = 0; // Turn off backlight P2OUT &= ~BUTTON_BACKLIGHT_PIN; P2DIR &= ~BUTTON_BACKLIGHT_PIN; break; } } }
// ************************************************************************************************* // @fn start_simpliciti_sync // @brief Start SimpliciTI (sync mode). // @param none // @return none // ************************************************************************************************* void start_simpliciti_sync(void) { // Clear LINE1 clear_line(LINE1); fptr_lcd_function_line1(LINE1, DISPLAY_LINE_CLEAR); // Stop acceleration sensor if (bmp_used) { bmp_as_stop(); } else { cma_as_stop(); } // Get updated altitude start_altitude_measurement(); stop_altitude_measurement(); // Get updated temperature temperature_measurement(FILTER_OFF); // Turn on beeper icon to show activity display_symbol(LCD_ICON_BEEPER1, SEG_ON_BLINK_ON); display_symbol(LCD_ICON_BEEPER2, SEG_ON_BLINK_ON); display_symbol(LCD_ICON_BEEPER3, SEG_ON_BLINK_ON); // Debounce button event Timer0_A4_Delay(CONV_MS_TO_TICKS(BUTTONS_DEBOUNCE_TIME_OUT)); // Prepare radio for RF communication open_radio(); // Set SimpliciTI mode sRFsmpl.mode = SIMPLICITI_SYNC; // Set SimpliciTI timeout to save battery power sRFsmpl.timeout = SIMPLICITI_TIMEOUT; // Start SimpliciTI stack. Try to link to access point. // Exit with timeout or by a button DOWN press. if (simpliciti_link()) { // Enter sync routine. This will send ready-to-receive packets at regular intervals to the // access point. // The access point replies with a command (NOP if no other command is set) simpliciti_main_sync(); } // Set SimpliciTI state to OFF sRFsmpl.mode = SIMPLICITI_OFF; // Powerdown radio close_radio(); // Clear last button events Timer0_A4_Delay(CONV_MS_TO_TICKS(BUTTONS_DEBOUNCE_TIME_OUT)); BUTTONS_IFG = 0x00; button.all_flags = 0; // Clear icons display_symbol(LCD_ICON_BEEPER1, SEG_OFF_BLINK_OFF); display_symbol(LCD_ICON_BEEPER2, SEG_OFF_BLINK_OFF); display_symbol(LCD_ICON_BEEPER3, SEG_OFF_BLINK_OFF); // Force full display update display.flag.full_update = 1; }
// ************************************************************************************************* // @fn display_acceleration // @brief Display routine. // @param u8 line LINE1 // u8 update DISPLAY_LINE_UPDATE_FULL, DISPLAY_LINE_CLEAR // @return none // ************************************************************************************************* void display_acceleration(u8 line, u8 update) { u8 *str; u8 raw_data; u16 accel_data; // Show warning if acceleration sensor was not initialised properly if (!as_ok) { display_chars(LCD_SEG_L1_2_0, (u8 *) "ERR", SEG_ON); } else { // Redraw whole screen if (update == DISPLAY_LINE_UPDATE_FULL) { { // Start acceleration sensor if (!is_acceleration_measurement()) { // Clear previous acceleration value sAccel.data = 0; // Start sensor cma_as_start(); // Set timeout counter sAccel.timeout = ACCEL_MEASUREMENT_TIMEOUT; // Set mode sAccel.mode = ACCEL_MODE_ON; // Start with Y-axis values sAccel.view_style = DISPLAY_ACCEL_Y; } // Display decimal point display_symbol(LCD_SEG_L1_DP1, SEG_ON); } } else if (update == DISPLAY_LINE_UPDATE_PARTIAL) { // Convert X/Y/Z values to mg switch (sAccel.view_style) { case DISPLAY_ACCEL_X: raw_data = sAccel.xyz[0]; display_char(LCD_SEG_L1_3, 'X', SEG_ON); break; case DISPLAY_ACCEL_Y: raw_data = sAccel.xyz[1]; display_char(LCD_SEG_L1_3, 'Y', SEG_ON); break; default: raw_data = sAccel.xyz[2]; display_char(LCD_SEG_L1_3, 'Z', SEG_ON); break; } accel_data = convert_acceleration_value_to_mgrav(raw_data) / 10; // Filter acceleration accel_data = (u16) ((accel_data * 0.2) + (sAccel.data * 0.8)); // Store average acceleration sAccel.data = accel_data; // Display acceleration in x.xx format str = int_to_array(accel_data, 3, 0); display_chars(LCD_SEG_L1_2_0, str, SEG_ON); // Display sign if (acceleration_value_is_positive(raw_data)) { display_symbol(LCD_SYMB_ARROW_UP, SEG_ON); display_symbol(LCD_SYMB_ARROW_DOWN, SEG_OFF); } else { display_symbol(LCD_SYMB_ARROW_UP, SEG_OFF); display_symbol(LCD_SYMB_ARROW_DOWN, SEG_ON); } } else if (update == DISPLAY_LINE_CLEAR) { // Stop acceleration sensor cma_as_stop(); // Clear mode sAccel.mode = ACCEL_MODE_OFF; // Clean up display display_symbol(LCD_SEG_L1_DP1, SEG_OFF); display_symbol(LCD_SYMB_ARROW_UP, SEG_OFF); display_symbol(LCD_SYMB_ARROW_DOWN, SEG_OFF); } } }
// ************************************************************************************************* // @fn start_simpliciti_tx_only // @brief Start SimpliciTI (tx only). // @param simpliciti_state_t SIMPLICITI_ACCELERATION, SIMPLICITI_BUTTONS // @return none // ************************************************************************************************* void start_simpliciti_tx_only(simpliciti_mode_t mode) { // Display time in line 1 clear_line(LINE1); fptr_lcd_function_line1(LINE1, DISPLAY_LINE_CLEAR); display_time(LINE1, DISPLAY_LINE_UPDATE_FULL); // Preset simpliciti_data with mode (key or mouse click) and clear other data bytes if (mode == SIMPLICITI_ACCELERATION) { simpliciti_data[0] = SIMPLICITI_MOUSE_EVENTS; } else { simpliciti_data[0] = SIMPLICITI_KEY_EVENTS; } simpliciti_data[1] = 0; simpliciti_data[2] = 0; simpliciti_data[3] = 0; // Turn on beeper icon to show activity display_symbol(LCD_ICON_BEEPER1, SEG_ON_BLINK_ON); display_symbol(LCD_ICON_BEEPER2, SEG_ON_BLINK_ON); display_symbol(LCD_ICON_BEEPER3, SEG_ON_BLINK_ON); // Debounce button event Timer0_A4_Delay(CONV_MS_TO_TICKS(BUTTONS_DEBOUNCE_TIME_OUT)); // Prepare radio for RF communication open_radio(); // Set SimpliciTI mode sRFsmpl.mode = mode; // Set SimpliciTI timeout to save battery power sRFsmpl.timeout = SIMPLICITI_TIMEOUT; // Start SimpliciTI stack. Try to link to access point. // Exit with timeout or by a button DOWN press. if (simpliciti_link()) { if (mode == SIMPLICITI_ACCELERATION) { // Start acceleration sensor if (bmp_used) { bmp_as_start(); } else { cma_as_start(); } } // Enter TX only routine. This will transfer button events and/or acceleration data to // access point. simpliciti_main_tx_only(); } // Set SimpliciTI state to OFF sRFsmpl.mode = SIMPLICITI_OFF; // Stop acceleration sensor if (bmp_used) { bmp_as_stop(); } else { cma_as_stop(); } // Powerdown radio close_radio(); // Clear last button events Timer0_A4_Delay(CONV_MS_TO_TICKS(BUTTONS_DEBOUNCE_TIME_OUT)); BUTTONS_IFG = 0x00; button.all_flags = 0; // Clear icons display_symbol(LCD_ICON_BEEPER1, SEG_OFF_BLINK_OFF); display_symbol(LCD_ICON_BEEPER2, SEG_OFF_BLINK_OFF); display_symbol(LCD_ICON_BEEPER3, SEG_OFF_BLINK_OFF); // Clean up line 1 clear_line(LINE1); display_time(LINE1, DISPLAY_LINE_CLEAR); // Force full display update display.flag.full_update = 1; }