You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OpenChronos/ezchronos.c

752 lines
22 KiB
C

// *************************************************************************************************
//
// Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
//
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the
// distribution.
//
// Neither the name of Texas Instruments Incorporated nor the names of
// its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// *************************************************************************************************
// Initialization and control of application.
// *************************************************************************************************
// *************************************************************************************************
// Include section
// system
#include "project.h"
#include <string.h>
// driver
#include "clock.h"
#include "display.h"
#include "vti_as.h"
#include "vti_ps.h"
#include "radio.h"
#include "buzzer.h"
#include "ports.h"
#include "timer.h"
#include "pmm.h"
#include "rf1a.h"
// logic
#include "menu.h"
#include "date.h"
#include "alarm.h"
#include "stopwatch.h"
#include "battery.h"
#include "temperature.h"
#include "altitude.h"
#include "battery.h"
#include "acceleration.h"
//pfs
#ifndef ELIMINATE_BLUEROBIN
#include "bluerobin.h"
#endif
#include "rfsimpliciti.h"
#include "simpliciti.h"
#ifdef CONFIG_TEST
#include "test.h"
#endif
#ifdef CONFIG_EGGTIMER
#include "eggtimer.h"
#endif
#ifdef CONFIG_PHASE_CLOCK
#include "phase_clock.h"
#endif
#include "mrfi.h"
#include "nwk_types.h"
// *************************************************************************************************
// Prototypes section
void init_application(void);
void init_global_variables(void);
void wakeup_event(void);
void process_requests(void);
void display_update(void);
void idle_loop(void);
void configure_ports(void);
void read_calibration_values(void);
// *************************************************************************************************
// Defines section
// Number of calibration data bytes in INFOA memory
#define CALIBRATION_DATA_LENGTH (13u)
// *************************************************************************************************
// Global Variable section
// Variable holding system internal flags
volatile s_system_flags sys;
// Variable holding flags set by logic modules
volatile s_request_flags request;
// Variable holding message flags
volatile s_message_flags message;
// Global radio frequency offset taken from calibration memory
// Compensates crystal deviation from 26MHz nominal value
u8 rf_frequoffset;
// Function pointers for LINE1 and LINE2 display function
void (*fptr_lcd_function_line1)(u8 line, u8 update);
void (*fptr_lcd_function_line2)(u8 line, u8 update);
// *************************************************************************************************
// Extern section
extern void start_simpliciti_sync(void);
extern u16 ps_read_register(u8 address, u8 mode);
extern u8 ps_write_register(u8 address, u8 data);
// rf hardware address
static const addr_t sMyROMAddress = {THIS_DEVICE_ADDRESS};
// *************************************************************************************************
// @fn main
// @brief Main routine
// @param none
// @return none
// *************************************************************************************************
int main(void)
{
// Init MCU
init_application();
// Assign initial value to global variables
init_global_variables();
#if CONFIG_TEST
// Branch to welcome screen
test_mode();
#else
display_all_off();
#endif
// Main control loop: wait in low power mode until some event needs to be processed
while(1)
{
// When idle go to LPM3
idle_loop();
// Process wake-up events
if (button.all_flags || sys.all_flags) wakeup_event();
// Process actions requested by logic modules
if (request.all_flags) process_requests();
// Before going to LPM3, update display
if (display.all_flags) display_update();
}
}
// *************************************************************************************************
// @fn init_application
// @brief Initialize the microcontroller.
// @param none
// @return none
// *************************************************************************************************
void init_application(void)
{
volatile unsigned char *ptr;
// ---------------------------------------------------------------------
// Enable watchdog
// Watchdog triggers after 16 seconds when not cleared
#ifdef USE_WATCHDOG
WDTCTL = WDTPW + WDTIS__512K + WDTSSEL__ACLK;
#else
WDTCTL = WDTPW + WDTHOLD;
#endif
// ---------------------------------------------------------------------
// Configure PMM
SetVCore(3);
// Set global high power request enable
PMMCTL0_H = 0xA5;
PMMCTL0_L |= PMMHPMRE;
PMMCTL0_H = 0x00;
// ---------------------------------------------------------------------
// Enable 32kHz ACLK
P5SEL |= 0x03; // Select XIN, XOUT on P5.0 and P5.1
UCSCTL6 &= ~XT1OFF; // XT1 On, Highest drive strength
UCSCTL6 |= XCAP_3; // Internal load cap
UCSCTL3 = SELA__XT1CLK; // Select XT1 as FLL reference
UCSCTL4 = SELA__XT1CLK | SELS__DCOCLKDIV | SELM__DCOCLKDIV;
// ---------------------------------------------------------------------
// Configure CPU clock for 12MHz
_BIS_SR(SCG0); // Disable the FLL control loop
UCSCTL0 = 0x0000; // Set lowest possible DCOx, MODx
UCSCTL1 = DCORSEL_5; // Select suitable range
UCSCTL2 = FLLD_1 + 0x16E; // Set DCO Multiplier
_BIC_SR(SCG0); // Enable the FLL control loop
// Worst-case settling time for the DCO when the DCO range bits have been
// changed is n x 32 x 32 x f_MCLK / f_FLL_reference. See UCS chapter in 5xx
// UG for optimization.
// 32 x 32 x 8 MHz / 32,768 Hz = 250000 = MCLK cycles for DCO to settle
__delay_cycles(250000);
// Loop until XT1 & DCO stabilizes, use do-while to insure that
// body is executed at least once
do
{
UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + XT1HFOFFG + DCOFFG);
SFRIFG1 &= ~OFIFG; // Clear fault flags
} while ((SFRIFG1 & OFIFG));
// ---------------------------------------------------------------------
// Configure port mapping
// Disable all interrupts
__disable_interrupt();
// Get write-access to port mapping registers:
PMAPPWD = 0x02D52;
// Allow reconfiguration during runtime:
PMAPCTL = PMAPRECFG;
// P2.7 = TA0CCR1A or TA1CCR0A output (buzzer output)
ptr = &P2MAP0;
*(ptr+7) = PM_TA1CCR0A;
P2OUT &= ~BIT7;
P2DIR |= BIT7;
// P1.5 = SPI MISO input
ptr = &P1MAP0;
*(ptr+5) = PM_UCA0SOMI;
// P1.6 = SPI MOSI output
*(ptr+6) = PM_UCA0SIMO;
// P1.7 = SPI CLK output
*(ptr+7) = PM_UCA0CLK;
// Disable write-access to port mapping registers:
PMAPPWD = 0;
// Re-enable all interrupts
__enable_interrupt();
// ---------------------------------------------------------------------
// Configure ports
// ---------------------------------------------------------------------
// Reset radio core
radio_reset();
radio_powerdown();
// ---------------------------------------------------------------------
// Init acceleration sensor
as_init();
// ---------------------------------------------------------------------
// Init LCD
lcd_init();
// ---------------------------------------------------------------------
// Init buttons
init_buttons();
// ---------------------------------------------------------------------
// Configure Timer0 for use by the clock and delay functions
Timer0_Init();
// ---------------------------------------------------------------------
// Init pressure sensor
ps_init();
}
// *************************************************************************************************
// @fn init_global_variables
// @brief Initialize global variables.
// @param none
// @return none
// *************************************************************************************************
void init_global_variables(void)
{
// --------------------------------------------
// Apply default settings
// set menu pointers to default menu items
ptrMenu_L1 = &menu_L1_Time;
// ptrMenu_L1 = &menu_L1_Alarm;
// ptrMenu_L1 = &menu_L1_Heartrate;
// ptrMenu_L1 = &menu_L1_Speed;
// ptrMenu_L1 = &menu_L1_Temperature;
// ptrMenu_L1 = &menu_L1_Altitude;
// ptrMenu_L1 = &menu_L1_Acceleration;
ptrMenu_L2 = &menu_L2_Date;
// ptrMenu_L2 = &menu_L2_Stopwatch;
// ptrMenu_L2 = &menu_L2_Rf;
// ptrMenu_L2 = &menu_L2_Ppt;
// ptrMenu_L2 = &menu_L2_Sync;
// ptrMenu_L2 = &menu_L2_Distance;
// ptrMenu_L2 = &menu_L2_Calories;
// ptrMenu_L2 = &menu_L2_Battery;
// Assign LINE1 and LINE2 display functions
fptr_lcd_function_line1 = ptrMenu_L1->display_function;
fptr_lcd_function_line2 = ptrMenu_L2->display_function;
// Init system flags
button.all_flags = 0;
sys.all_flags = 0;
request.all_flags = 0;
display.all_flags = 0;
message.all_flags = 0;
// Force full display update when starting up
display.flag.full_update = 1;
#ifndef ISM_US
// Use metric units when displaying values
sys.flag.use_metric_units = 1;
#else
#ifdef CONFIG_METRIC_ONLY
sys.flag.use_metric_units = 1;
#endif
#endif
// Read calibration values from info memory
read_calibration_values();
// Set system time to default value
reset_clock();
// Set date to default value
reset_date();
// Set alarm time to default value
reset_alarm();
// Set buzzer to default value
reset_buzzer();
// Reset stopwatch
reset_stopwatch();
// Reset altitude measurement
reset_altitude_measurement();
// Reset acceleration measurement
reset_acceleration();
// Reset BlueRobin stack
//pfs
#ifndef ELIMINATE_BLUEROBIN
reset_bluerobin();
#endif
#ifdef CONFIG_EGGTIMER
//Set Eggtimer to a 5 minute default
memcpy(seggtimer.defaultTime, "00010000", sizeof(seggtimer.time));
reset_eggtimer();
#endif
#ifdef CONFIG_PHASE_CLOCK
// default program
sPhase.program = 0;
#endif
// Reset SimpliciTI stack
reset_rf();
// Reset temperature measurement
reset_temp_measurement();
// Reset battery measurement
reset_batt_measurement();
battery_measurement();
}
// *************************************************************************************************
// @fn wakeup_event
// @brief Process external / internal wakeup events.
// @param none
// @return none
// *************************************************************************************************
void wakeup_event(void)
{
// Enable idle timeout
sys.flag.idle_timeout_enabled = 1;
// If buttons are locked, only display "buttons are locked" message
if (button.all_flags && sys.flag.lock_buttons)
{
// Show "buttons are locked" message synchronously with next second tick
if (!(BUTTON_NUM_IS_PRESSED && BUTTON_DOWN_IS_PRESSED))
{
message.flag.prepare = 1;
message.flag.type_locked = 1;
}
// Clear buttons
button.all_flags = 0;
}
// Process long button press event (while button is held)
else if (button.flag.star_long)
{
// Clear button event
button.flag.star_long = 0;
// Call sub menu function
ptrMenu_L1->mx_function(LINE1);
// Set display update flag
display.flag.full_update = 1;
}
else if (button.flag.num_long)
{
// Clear button event
button.flag.num_long = 0;
// Call sub menu function
ptrMenu_L2->mx_function(LINE2);
// Set display update flag
display.flag.full_update = 1;
}
// Process single button press event (after button was released)
else if (button.all_flags)
{
// M1 button event ---------------------------------------------------------------------
// (Short) Advance to next menu item
if(button.flag.star)
{
// Clean up display before activating next menu item
fptr_lcd_function_line1(LINE1, DISPLAY_LINE_CLEAR);
// Go to next menu entry
ptrMenu_L1 = ptrMenu_L1->next;
// Assign new display function
fptr_lcd_function_line1 = ptrMenu_L1->display_function;
// Set Line1 display update flag
display.flag.line1_full_update = 1;
// Clear button flag
button.flag.star = 0;
}
// NUM button event ---------------------------------------------------------------------
// (Short) Advance to next menu item
else if(button.flag.num)
{
// Clean up display before activating next menu item
fptr_lcd_function_line2(LINE2, DISPLAY_LINE_CLEAR);
// Go to next menu entry
ptrMenu_L2 = ptrMenu_L2->next;
// Assign new display function
fptr_lcd_function_line2 = ptrMenu_L2->display_function;
// Set Line2 display update flag
display.flag.line2_full_update = 1;
// Clear button flag
button.flag.num = 0;
}
// UP button event ---------------------------------------------------------------------
// Activate user function for Line1 menu item
else if(button.flag.up)
{
// Call direct function
ptrMenu_L1->sx_function(LINE1);
// Set Line1 display update flag
display.flag.line1_full_update = 1;
// Clear button flag
button.flag.up = 0;
}
// DOWN button event ---------------------------------------------------------------------
// Activate user function for Line2 menu item
else if(button.flag.down)
{
// Call direct function
ptrMenu_L2->sx_function(LINE2);
// Set Line1 display update flag
display.flag.line2_full_update = 1;
// Clear button flag
button.flag.down = 0;
}
}
// Process internal events
if (sys.all_flags)
{
// Idle timeout ---------------------------------------------------------------------
if (sys.flag.idle_timeout)
{
// Clear timeout flag
sys.flag.idle_timeout = 0;
// Clear display
clear_display();
// Set display update flags
display.flag.full_update = 1;
}
}
// Disable idle timeout
sys.flag.idle_timeout_enabled = 0;
}
// *************************************************************************************************
// @fn process_requests
// @brief Process requested actions outside ISR context.
// @param none
// @return none
// *************************************************************************************************
void process_requests(void)
{
// Do temperature measurement
if (request.flag.temperature_measurement) temperature_measurement(FILTER_ON);
// Do pressure measurement
if (request.flag.altitude_measurement) do_altitude_measurement(FILTER_ON);
// Do acceleration measurement
if (request.flag.acceleration_measurement) do_acceleration_measurement();
// Do voltage measurement
if (request.flag.voltage_measurement) battery_measurement();
// Generate alarm (two signals every second)
if (request.flag.buzzer) start_buzzer(2, BUZZER_ON_TICKS, BUZZER_OFF_TICKS);
// Reset request flag
request.all_flags = 0;
}
// *************************************************************************************************
// @fn display_update
// @brief Process display flags and call LCD update routines.
// @param none
// @return none
// *************************************************************************************************
void display_update(void)
{
u8 line;
u8 string[8];
// ---------------------------------------------------------------------
// Call Line1 display function
if (display.flag.full_update || display.flag.line1_full_update)
{
clear_line(LINE1);
fptr_lcd_function_line1(LINE1, DISPLAY_LINE_UPDATE_FULL);
}
else if (ptrMenu_L1->display_update())
{
// Update line1 only when new data is available
fptr_lcd_function_line1(LINE1, DISPLAY_LINE_UPDATE_PARTIAL);
}
// ---------------------------------------------------------------------
// If message text should be displayed on Line2, skip normal update
if (message.flag.show)
{
line = LINE2;
// Select message to display
if (message.flag.type_locked) memcpy(string, " LO?T", 6);
else if (message.flag.type_unlocked) memcpy(string, " OPEN", 6);
else if (message.flag.type_lobatt) memcpy(string, "LOBATT", 6);
else if (message.flag.type_no_beep_on) memcpy(string, " SILNC", 6);
else if (message.flag.type_no_beep_off) memcpy(string, " BEEP", 6);
else if (message.flag.type_alarm_on)
{
memcpy(string, " ON", 4);
line = LINE1;
}
else if (message.flag.type_alarm_off)
{
memcpy(string, " OFF", 4);
line = LINE1;
}
// Clear previous content
clear_line(line);
fptr_lcd_function_line2(line, DISPLAY_LINE_CLEAR);
if (line == LINE2) display_chars(LCD_SEG_L2_5_0, string, SEG_ON);
else display_chars(LCD_SEG_L1_3_0, string, SEG_ON);
// Next second tick erases message and repaints original screen content
message.all_flags = 0;
message.flag.erase = 1;
}
// ---------------------------------------------------------------------
// Call Line2 display function
else if (display.flag.full_update || display.flag.line2_full_update)
{
clear_line(LINE2);
fptr_lcd_function_line2(LINE2, DISPLAY_LINE_UPDATE_FULL);
}
else if (ptrMenu_L2->display_update() && !message.all_flags)
{
// Update line2 only when new data is available
fptr_lcd_function_line2(LINE2, DISPLAY_LINE_UPDATE_PARTIAL);
}
// ---------------------------------------------------------------------
// Restore blinking icons (blinking memory is cleared when calling set_value)
if (display.flag.full_update)
{
//pfs
#ifndef ELIMINATE_BLUEROBIN
if (is_bluerobin() == BLUEROBIN_CONNECTED)
{
// Turn on beeper icon to show activity
display_symbol(LCD_ICON_BEEPER1, SEG_ON_BLINK_OFF);
display_symbol(LCD_ICON_BEEPER2, SEG_ON_BLINK_OFF);
display_symbol(LCD_ICON_BEEPER3, SEG_ON_BLINK_OFF);
}
#endif
}
// Clear display flag
display.all_flags = 0;
}
// *************************************************************************************************
// @fn to_lpm
// @brief Go to LPM0/3.
// @param none
// @return none
// *************************************************************************************************
void to_lpm(void)
{
// Go to LPM3
_BIS_SR(LPM3_bits + GIE);
__no_operation();
}
// *************************************************************************************************
// @fn idle_loop
// @brief Go to LPM. Service watchdog timer when waking up.
// @param none
// @return none
// *************************************************************************************************
void idle_loop(void)
{
// To low power mode
to_lpm();
#ifdef USE_WATCHDOG
// Service watchdog
WDTCTL = WDTPW + WDTIS__512K + WDTSSEL__ACLK + WDTCNTCL;
#endif
}
// *************************************************************************************************
// @fn read_calibration_values
// @brief Read calibration values for temperature measurement, voltage measurement
// and radio from INFO memory.
// @param none
// @return none
// *************************************************************************************************
void read_calibration_values(void)
{
u8 cal_data[CALIBRATION_DATA_LENGTH]; // Temporary storage for constants
u8 i;
u8 * flash_mem; // Memory pointer
// Read calibration data from Info D memory
flash_mem = (u8 *)0x1800;
for (i=0; i<CALIBRATION_DATA_LENGTH; i++)
{
cal_data[i] = *flash_mem++;
}
if (cal_data[0] == 0xFF)
{
// If no values are available (i.e. INFO D memory has been erased by user), assign experimentally derived values
rf_frequoffset = 4;
sTemp.offset = -250;
sBatt.offset = -10;
simpliciti_ed_address[0] = sMyROMAddress.addr[0];
simpliciti_ed_address[1] = sMyROMAddress.addr[1];
simpliciti_ed_address[2] = sMyROMAddress.addr[2];
simpliciti_ed_address[3] = sMyROMAddress.addr[3];
sAlt.altitude_offset = 0;
}
else
{
// Assign calibration data to global variables
rf_frequoffset = cal_data[1];
// Range check for calibrated FREQEST value (-20 .. + 20 is ok, else use default value)
if ((rf_frequoffset > 20) && (rf_frequoffset < (256-20)))
{
rf_frequoffset = 0;
}
sTemp.offset = (s16)((cal_data[2] << 8) + cal_data[3]);
sBatt.offset = (s16)((cal_data[4] << 8) + cal_data[5]);
simpliciti_ed_address[0] = cal_data[6];
simpliciti_ed_address[1] = cal_data[7];
simpliciti_ed_address[2] = cal_data[8];
simpliciti_ed_address[3] = cal_data[9];
// S/W version byte set during calibration?
if (cal_data[12] != 0xFF)
{
sAlt.altitude_offset = (s16)((cal_data[10] << 8) + cal_data[11]);;
}
else
{
sAlt.altitude_offset = 0;
}
}
}