/*
Copyright 2003/2004, Freie Universitaet Berlin. All rights reserved.

These sources were developed at the Freie Universität Berlin, Computer
Systems and Telematics group.

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 Freie Universitaet Berlin (FUB) 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 FUB and the contributors on an "as is"
basis, without any representations or warranties of any kind, express
or implied including, but not limited to, representations or
warranties of non-infringement, merchantability or fitness for a
particular purpose. In no event shall FUB 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.

This implementation was developed by the CST group at the FUB.
Contributors: Bjoern Lichtblau

For documentation and questions please use the web site
http://www.scatterweb.net and the mailinglist
scatterweb@lists.spline.inf.fu-berlin.de (subscription via the Website).
Berlin, 2003/2004/2005
*/

/** \mainpage

<h1>1. Overview</h1>

As the name already implies, sensor networks need sensors, typically lots of sensors, 
to collect data from their environment. All sensor nodes may form an ad-hoc network with 
some nodes acting as data sources, some as relays, and some as data sinks collecting data. 
Nodes may act in all three roles at the same time. 

The ESBs developed by the CST Group forms a sound basis for research in sensor networks, 
the development of university level courses in CS/EE based on real hardware, 
and prototyping of sensor net applications. 

The ESBs are part of the scatterweb approach comprising a whole family of 
mbedded web servers, sensors and actors. Typical communication scenarios of 
sensor networks based on the ESBs are:

- ESBs communicate via the serial port with a standard PC for application development.
- ESBs communicate with mobile phones via the serial port to connect to wide-area mobile 
  phone networks. This enables remote configuration of ESBs via short messages (SMS) 
  as well as reception of sensor data on arbitrary mobile phones world-wide.
- ESBs communicate via their radio interface with other ESBs or an embedded web server (EWS) 
  to form a truly embedded, highly flexible sensor network solution.

Main components of the ESB are the sensors, a micro controller for processing of all data, 
and a transceiver handling communication with other nodes. 

Software for the following features is already available for the ESB 430:

- Configuration and analysis of all sensor data: vibration, noise, movement, temperature, infrared, (luminosity).
- Control of peripherals: timer, transceiver, serial port, EEPROM, IR-send/receive, LEDs, beeper, switches.
- Sending and receiving of data packets via the radio interface, transmission of sensor data, communication 
  with other systems using the same radio interface (ESBs, ESWs).
- Sending and receiving of short messages (SMS) via connected mobile phone (serial port). Bluetooth connection 
  available upon request (standard for the EWS). Just send the ESB an SMS triggering temperature monitoring every 
  Tuesday morning from 7:00 a.m. until 11:43 a.m., if and only if there is movement in the room but no light - 
  it's possible with a single SMS!
- Sending (e.g. to control home entertainment devices) and receiving (e.g. from conventional remote controls) 
  of RC5 packets via infrared.
- Periodic polling of sensor data and (depending on settings) automatic transmission via 
  the radio interface, the serial port (terminal or mobile phone).
- Simple and easy configuration and control of the ESB via terminal commands over 
  the serial interface or radio.

\image html esb.jpg "ESB: Embedded Sensor Board"
\image html stick.jpg "USB430Stick: ScatterFlasher"


<h1>2. Software Documentation</h1>

The software of the sensor nodes was first seperated into two parts 
(firmware&application) to provide in-field programming via radio. 
This has recently changed because that reprogramming strategy created 
several problems. 
Now the whole thing (except the reprogramming code, which should not
change anymore, see progamming.c) is reprogrammable. Anyway the seperation
between firmware and application is kept because it prooved good
as hardware abstraction layer and to provide some os-like concepts.

The \ref Firmware provides abstract functions to 
interface and use the hardware, contains the main execution loop + interrupts 
and provides some os-like concepts. 
The firmware supports all ScatterWeb/MSP430 devices: 
ESB (and EYE), USB430Stick, EWS430Stick, ECR430, THM430.
The application (userapp, program) is built on top of the firmware.

<h3>Directory tree</h3>
- apps: Contains application directories with PSPad project files.
- firmware: Contains firmware sources and binaries.
- doc: Contains pictures, documentation, doxygen configuration file and generated documentation.
- attic: Contains code, which was removed from the project for several reason, 
         but still may be interesting for other development.

<h3>Documentation</h3>
<ul>
<li> \ref Firmware
<li> \ref scatternode
<li> \ref scattergate
</ul>

Read also:
<ul>
<li> \ref Commands
<li> \ref ehandler.c "Sensor processing"
<li> \ref flasher.c "Over the air reprogramming"
<li> \ref lpm "Power saving"
</ul>

See homepage of scatterweb project: http://www.scatterweb.de<br>
Ask questions to our mailing list: scatterweb@lists.spline.inf.fu-berlin.de (subscription at homepage).

<br><br>

<h1> 3. Major Changes </h1>

- Firmware and userapp are not that strictly separated anymore. 
  This made reprogramming easier and all restrictions from the old firmware<->userapp seperation
  are gone. Reprogramming code is now in programming.c, flasher.c contains the radio transfer of
  code images.

- For simplicity all 3 firmwares from ESB, USB/EWS430 and ECR430 have been integrated into one
  with the use of #ifdef at some hardware dependent places.
  When compiling the command line one must always define one of: ESB, USB430, EWS430, ECR430, EYE, THM430.
  This is done in the makefiles with the variable DEVICE.
  The makefiles are now taking every source file they see in the directories
  and don't need to be changed for new/removed files anymore
  (Look into the makefiles and at new directory structure).

- Radio software has changed. Sending is now also done interrupt driven and comes with
  a ring buffer to store several packets to send. Additional new features are crc16 checksums
  and automatic acknowledgements  (except broadcasts) with feedback to the sender of every packet. 
  It contains also experimental code for synchronizing the clock (See radio.c).
  
- The terminal software has been moved to the firmware as many commands between devices were the same. 
  Command creation has changed and most commands are executable from a remote device (with reply).
  Commands can now be placed where they are needed, Macros and a special section in flash
  build a table to automatically search all commands (See terminal.c).

- Light sensor software has been removed due to complex handling. Timer slots are also
  needed for constant dco checking&adjusting (timer.c) and a software RTC (time.c) as the RTC is mostly abandoned
  because of its big current usage, it's only used as an well calibrated temperature sensor (clock.c).

- time.c represents the new software RTC (see ::currentTime). 
  clock.c has been completly cleared, only the function for reading the temperature and 
  ensuring RTC 32 khz output at initialization are left.
  fiveMsTimer is replaced through ::currentTime or use of timers (timer.c), its still left for internal timer use.
  With ::SYNCH_PACKET, ::radio_queue_synch and ::doSynch you can add synchronization now.
  
- ! See attic directory for removed code !

- timer.c module has been added, no need to poll for an elapsed time anymore. 
  dco.c includes DCO checking, so DCO is recalibrated the whole time, not at startup only!

- Other modules have been more seperated (config.c (was in eeprom.c), programming.c (was in flasher.c)).

- watchdog.c has been rewritten. It's now correct and possible to see where a reset came from.
  Additionaly a feature has beend added to watch stack growth and detect a stack overflow.
  For bad conditions like that a panic function has been added. The firmware can disable the application
  if a panic occured.
  
- An abstract printf like function has been implemented for string outputting: ::abstract_write.
  This is used by the functions ::write (to write a string to memory), ::print (to write normal
  output to the serial port) and ::log (to write debug output to the serial port). No other methods
  should be used for writing to the serial port, sendRS232 is not public anymore.
  This is because serial port output is now controlled via ::serial_logLevel. It allows several 
  log levels, some debug bits for special debugging and total disabling of all output (see serial.c).

- A cooperative scheduler exists to create simple task functions: scos.c 
  For example look at scosex.c or rewritten flasher.c.

- The main superloop goes to sleep when everything is processed, all
  things that need to be handled are interrupt driven (see ::main). Every
  interrupt which needs processing from the superloop wakes the processor up
  with WAKEUPLPM1, which also sets a flag in runModule. When all flags in
  runModule are reset (after processing from superloop tasks) the loop goes
  to LPM1 again. If a function which needs calls as often as possible is added
  to the superloop you have to comment out the lines about sleeping! 

- Typedefs for UINT8, UINT16, UINT32 have been introduced because it is better style
  and much shorter.

<h1>4. Important things to know</h1>

- All routines should return fast. If a routine occupies
  the processor for a long time (for example outputting a long
  text which works with the blocking sendRS232Char() function)
  it should call startWatchdog() repeatedly (normally done in main())
  to avoid a watchdog reset. If a watchdog reset occurs the
  firmware will disable the programm if PANIC_DISABLES_USERAPP is set.
  
- You don't need to change the makefiles anymore. Makefiles in firmware
  and application automatically compile every .c file in the src directories and
  look into every .c and .h file for changes.

- No intialization of globals is done. You can write "UINT16 var = 49;"
  but var will never be initialized, do initialization in code!
  (Initialization may work again if the standard startup routine is used.)
  
- Don't use dynamic memomry allocation (malloc/free) !!!!!<br>
  Dynamic memory allocation bears the following problems on MCUS:<br>
  - Much harder to debug.
  - Unknown behaviour at compile time.
  - Memory fragmentation and no way (missing MMU) to defragment it.
  - No memory protection between stack and heap which grow towards each other.
  - Additionally current msp430-gcc malloc implementation seems to be buggy.
  
  Sometimes an embedded engineer said:
  "Think small on MCUs. If you are planning to use dynamic memory allocation,
   you are thinking much too BIG."
   
- If weird things happen look into linker map "out.map" or output listing 
  (assembler intermixed with c source code) "out.lst".
  
- Sometimes compilation fails or hangs with existing objects files from another machine.
  Do a "make clean" or simply delete all object (*.o) files in firmware and userapp 
  directories in this situation!
  
<h1> 5. Links </h1>

<ul>
<li>ScatterWeb:<br>
<a href="http://www.scatterweb.de">Homepage of ScatterWeb project: http://www.scatterweb.de</a><br>
<a href="mailto:scatterweb@lists.spline.inf.fu-berlin.de">ScatterWeb Mailinglist: scatterweb@lists.spline.inf.fu-berlin.de</a><br>

<li>MSP430:<br>
<a href="http://www.ti.com/sc/msp430/">TIs MSP430 Website</a>
<a href="http://groups.yahoo.com/group/msp430/">MSP430 Yahoo Group</a><br>
<a href="http://mspgcc.sourceforge.net">msp430-gcc Homepage</a><br>
<a href="http://www.mikrocontroller.net/wiki/MSPGCC">A wiki for the MSP430</a><br> 
<a href="http://groups.yahoo.com/group/e-embedded/">A more general Yahoo Group for embedded things.</a><br>
<a href="http://mspgcc.sourceforge.net/baudrate.html">Baudrate calculator</a><br>
<a href="http://www.mikrocontroller.net/">Mikrocontroller.net (several microcontrollers, in german, very nice link section) </a><br>

<li>Utilities/Other related:<br>    
<a href="http://www.wlug.org.nz/MakefileHowto">A good HowTo about Makefiles</a><br>
<a href="http://developer.apple.com/documentation/DeveloperTools/gcc-3.3/cpp/Macros.html#Macros">Another one</a><br>
<a href="http://www.delorie.com/gnu/docs/binutils/ld_24.html">Binutils (especially linker ld) documentation</a><br>
<a href="http://www.8052.com/tutintel.phtml">Intel Hex File Documentation 1</a><br>
<a href="http://www.pjrc.com/tech/8051/pm2_docs/intel-hex.html">Intel Hex File Documentation 2</a><br>
<a href="http://www.pspad.com/">PSPad Homepage</a>
<a href="http://www.cs.cf.ac.uk/Dave/C/node13.html#SECTION001300000000000000000">A nice guide to bit manipulation</a>

<li>Protocols:<br>
<a href="http://mikrocontroller.cco-ev.de/de/IR-Protokolle.htm">Several IR protocols (in german)</a><br>
<a href="http://users.pandora.be/davshomepage/rc5.htm">Phillips RC5 infrared remote protocol page</a><br>
<a href="http://perso.club-internet.fr/mbouget/i2c-faq.html">I2C FAQ</a><br>

<li>Eclipse supports mspgcc, looks worth a try<br>
<a href="http://homepage.hispeed.ch/py430/mspgcc/">MSP430 and Eclipse</a><br>
<a href="http://www.eclipse.org/cdt/">CDT (C/C++ Development Tools) Project providing a fully functional C and C++ IDE for the Eclipse platform.  </a><br>

<li>
Free terminal programs:
<a href="http://hp.vector.co.jp/authors/VA002416/teraterm.html">Tera Term Pro 2.3 Program and Source</a>
<a href="http://www.ayera.com/teraterm/">TeraTerm Pro Web 3.1.3 - Enhanced Telnet/SSH2 Client (modified by Ayera)</a>
<a href="http://realterm.sourceforge.net/">Realterm</a><br>
</ul>

*/

#include <stdarg.h> // important for variable argument lists: function(x, y, ...)
#include "ScatterWeb.System.h"

#define RESET 			(WDTCTL = 0x0000)
#define WDSTATE   (*((UINT16*)0x09fe))      ///< 2 bytes are reserved at main

#if defined(ESB) || defined(EYE)
#define SDA_HIGH  (P5OUT |= 0x01) ///< RTC data line high
#define SDA_LOW   (P5OUT &= 0xFE) ///< RTC data line low
#define SCL_HIGH  (P5OUT |= 0x02) ///< RTC clock line high 
#define SCL_LOW   (P5OUT &= 0xFD) ///< RTC clock line low
#define BUS_READ          0x9F
#define BUS_WRITE         0x9E
#define ACC_CSR           0xAC      ///< Access Configuration/Control Register
#define ACC_CLOCK         0xC0      ///< Access Clock Register
#define ACC_CLOCK_ALARM   0xC7      ///< Access Clock Alarm Register
#define ACC_TH            0xA1      ///< Access Thermostat Setpoint High
#define ACC_TL            0xA2      ///< Access Thermostat Setpoint Low
#define ACC_CSRAM         0x17      ///< Access Clock 32 byte SRAM
#define ACC_RT            0xAA      ///< Access Read Temperatur Register
#define CSR_OS1          (0x80)      
#define CSR_OS0          (0x40)      
#define CSR_A1           (0x20)      
#define CSR_A0           (0x10)    
#define CSR_CNV          (0x04)      
#define CSR_POL          (0x02)     
#define CSR_1SH          (0x01)      
#define CSR_DEFAULT     (CSR_OS1 + CSR_OS0 + CSR_A1 + CSR_CNV + CSR_1SH + CSR_POL)
#endif

#if defined(ECR)
#define CS_LOW    (P2OUT &= ~0x08)
#define CS_HIGH   (P2OUT |= 0x08)
#define SDA_HIGH  (P5OUT |= 0x01)   ///< RTC data line high
#define SDA_LOW   (P5OUT &= 0xFE)   ///< RTC data line low
#define SDA       (P5IN & 0x01)     ///< Test SDA.
#define SCL_HIGH  (P5OUT |= 0x02)   ///< RTC clock line high 
#define SCL_LOW   (P5OUT &= 0xFD)   ///< RTC clock line low
#define NOPS    do{ _NOP(); _NOP(); _NOP(); _NOP(); _NOP(); _NOP(); _NOP(); _NOP(); _NOP(); _NOP();}while(0)
#endif

UINT16 wd_startup;

#if defined(ESB) || defined(EYE)
/// Puts the start condition on bus.
void cl_start(void){
  P5DIR |= 0x03; // ensure: P50(SDA), P51(SCL) output
  SCL_LOW;  _NOP(); _NOP();
  SDA_HIGH; _NOP(); _NOP();
  SCL_HIGH; _NOP(); _NOP();
  SDA_LOW;  _NOP(); _NOP();
  SCL_LOW;  _NOP(); _NOP();
}

/// Puts the stop condition on bus.
void cl_stop(void){
  SCL_LOW;  _NOP(); _NOP();
  SDA_LOW;  _NOP(); _NOP();
  SCL_HIGH; _NOP(); _NOP();
  SDA_HIGH; _NOP(); _NOP();
  SCL_LOW;  _NOP(); _NOP();
  P5DIR &= ~0x03;
}

/// Writes a byte on the bus, returns the acknowledge bit.
UINT16 cl_writeOnBus(UINT8 byte) {
  UINT16 i, ack;
  for(i=0;i<8;i++) {
    if(byte & 0x80) SDA_HIGH; else SDA_LOW;
    SCL_HIGH;
    byte = byte << 1;
    SCL_LOW;
  }
  // check ack
  P5DIR &= 0xFE;                              // P50(SDA) input
  SCL_HIGH;
  if(P5IN & 0x01) ack = 0; else ack = 1;      // test if ack=0, else error
  SCL_LOW;
  P5DIR |= 0x01;                              // P50(SDA) output
  return ack;
}

/// Reads one byte from the bus.
/** \param ack If set, the ackbit after the received byte will be set. */
UINT8 cl_readFromBus(UINT16 ack) {
  UINT16 i;
  UINT8 byte = 0;
  P5DIR &= 0xFE;                              // P50(SDA) input
  for(i=0;i<8;i++) {
    byte = byte << 1;                
    SCL_HIGH;
    if(P5IN & 0x01) byte |= 0x01; else byte &= 0xFE;
    SCL_LOW;
  }
  P5DIR |= 0x01;                              // P50(SDA) output 
  if(ack) SDA_LOW; else SDA_HIGH;
  SCL_HIGH;
  SCL_LOW;
  return byte;
}

/// Reads out all bits of a 16 bit register (possible access: Configuration Status Register, Temperature, TH, TL)
UINT16 getReg16bit(UINT8 acc,UINT16 bitmask){
  UINT16 config = 0;
  do cl_start();
  while(!cl_writeOnBus(BUS_WRITE)); 
  cl_writeOnBus(acc);
  cl_start(); 
  cl_writeOnBus(BUS_READ);
  config = cl_readFromBus(1);
  config = config << 8;
  config += cl_readFromBus(0);
  cl_stop();
  config &= bitmask;
  _NOP();
  _NOP();
  return config;
}

/// Only first 8 bit of Configuration Status Register can be set
void setCSReg(UINT8 setting){
  do cl_start();
  while(!cl_writeOnBus(BUS_WRITE));               
  cl_writeOnBus(ACC_CSR);                         
  cl_writeOnBus(setting);
  cl_stop();
  _NOP();
  _NOP();
  _NOP();
  _NOP();
}

/// RTC initialization. Initializes RTC with ::CSR_DEFAULT.
void initClock(void){
  UINT8 csr = getReg16bit(ACC_CSR,0xFF00) >> 8;
  if(csr!=CSR_DEFAULT) setCSReg(CSR_DEFAULT); // if desired config isnt in clock => set it
  /// IMPORTANT: Ensure quartz is generating 32768 Hz 
  /// (sometimes CH bit gets set when clock is read while reset)
  do cl_start();                    /// Do start until BUS_WRITE is acked.
  while(!cl_writeOnBus(BUS_WRITE)); /// Send control byte
  cl_writeOnBus(ACC_CLOCK);         /// Send command byte ::ACC_CLOCK.
  cl_writeOnBus(0x00);              /// Send starting address 0x00.

  cl_writeOnBus(0x00); /// Set CH to 0, tseconds and seconds will also be reset!
  cl_stop();           /// Stop condition.   
}

/** \short Starts a single temperature conversion.
 **
 ** Must be done shortly before reading the temperature or simply
 ** called repeatedly. Consumes much more power when doing many conversion.
 **/   
void cl_startConversion(){
  do cl_start();                    // do start until BUS_WRITE is acked
  while(!cl_writeOnBus(BUS_WRITE)); // control byte
  cl_writeOnBus(0xEE);              // start conversion
  cl_stop();  	
}
#endif

temp_t System_getTemperature(){ 
  #if defined(ESB) || defined(EYE)
  temp_t temperature;
  temperature.u = getReg16bit(ACC_RT,0xFFFF); 
  temperature.u >>= 1; // Shift one down
  if(temperature.u & 0x4000) temperature.u |= 0x8000; // extend sign
  return temperature;
  #elif defined(ECR)
    UINT16 i;
  UINT16 devid = 0;
  temp_t temperature;
  temperature.u = 0;
  SCL_LOW; // Ensure clock is low
  NOPS;
  CS_LOW; // Start communication 
  NOPS;
  // Read temperature
  for(i=0; i<16; i++){
    SCL_HIGH;
    NOPS;
    if(SDA) temperature.u++; // Set 1 in LSB
    if(i<15) temperature.u <<= 1; // Shift one digit left
    SCL_LOW;
    NOPS;
  }
  // Write comamnd 0x1111 = Shutdown
  P5DIR |= 0x01; // SDA is output
  for(i=0; i<16; i++){
    SDA_HIGH; // write all 1s
    SCL_HIGH;
    NOPS;
    SCL_LOW;
    NOPS;
  }
  P5DIR &= ~0x01; // SDA input again
  // Now read device id
  for(i=0; i<16; i++){
    SCL_HIGH;
    NOPS;
    if(SDA) devid++; // Set 1 in LSB
    if(i<15) devid <<= 1; // Shift one digit left
    SCL_LOW;
    NOPS;
  }  
  // Write comamnd 0x0000 = continous conversion
  P5DIR |= 0x01; // SDA is output
  for(i=0; i<16; i++){
    SDA_LOW; // write all 1s
    SCL_HIGH;
    NOPS;
    SCL_LOW;
    NOPS;
  }
  P5DIR &= ~0x01; // SDA input again
  //End of communication
  CS_HIGH;
  temperature.u &= 0xFFFC; // Clear last two bits as they are always 1.
  return temperature;    
  #else
  temp_t temperature;
  return temperature;
  #endif
}

////////////////////////////////////////////////////////////////////////////////
///\name Terminal Commands: Temperature (ESB, EYE, THM430)
///\ingroup Commands
//@{
 
/// rtt <br> Temperature Sensor: Reads temperature value of the temperature sensor. 
COMMAND(rtt, 0){
#if defined(ESB) || defined(EYE) || defined(ECR) 
#if defined(ESB) || defined(EYE)
  cl_startConversion(); 
#endif
  String_write(term_reply, "%T", System_getTemperature()); 
#else
  String_write(term_reply, "NOT SUPPORTED: %s (wrong platform)", str);
#endif
}                 

//@}

volatile UINT16 rxpTemp;          ///< ::ADC12ISR() stores the current receive power here.
volatile UINT16 rxpValue;         ///< If a packet start is detected by ::ADC12ISR() the receive power of the start is written here.

#if defined(ESB) || defined(EYE)
volatile UINT8 MIC; ///< 0x01 is like IFG, 0x02 like IE and will set sensorFlags.MIC
#endif

/// \name ADU
//@{
  
UINT16 _skipADC;
  
/** \brief ISR for ADU.
 **
 ** 4 mhz : 18 us, every 120 us
 ** 2,4 mhz : 30 us every 120 us                                                                    */ 
interrupt(ADC_VECTOR) ADC12ISR (void){
  rxpTemp = ADC12MEM5; // Important as this will reset the IFG.
  if(_skipADC) _skipADC--;
  else {
    // Test if CarrierDetect and not already inside a packet.
    if(rxpTemp > Configuration.rxReceiveLimit && cdCounter==0){
      // Packet start should be detected within next 13 bytes.
      // (TURNON_LENGTH+(PREAMBLE_LENGTH+1)+3[FF, 01, 7F]+2 bytes, else it was noise.
      cdCounter = 13;  
      rxpValue = rxpTemp;
    } 
#if defined(ESB) || defined(EYE)  
    // Microphone
    extern UINT16 _micDiff;
    if( ((UINT16)abs(2048 - ADC12MEM2) > _micDiff) && (!(MIC & MICIFG))){
      MIC |= MICIFG;
      if(MIC & MICIE){
        sensorFlags |= SENSOR_MICROPHONE; //WAKEUP   
        WAKEUPLPM1(ADC12ISR, MF_SENSORS);  
      } 
    }
#endif
  }
}

/** \brief Init function for ADU.
 ** Inits the AD to contionously calc the 4 values (extern and battery voltage,
 ** receivepower and microphone) and generate an interrupt at the
 ** end of this sequence, the interrupt routine then copies out all values into ...Value  
 ** ECR: 60 - 63 Adwandler herausgeführt
 ** ECR: 64 Goldcap
 */
void _initADU(void){
  ADC12CTL0 = SHT0_6+SHT1_6+MSC;  // Setup ADC12, ref., sampling time
#if defined(ESB) || defined(EYE)
  ADC12CTL1 = SHP+CONSEQ_3+CSTARTADD_2;   // Use sampling timer, repeat-sequence-of-channels
  // ausserdem p61 p62 pir analog und digital?!
  ADC12MCTL2 = (INCH_0 + SREF_0); // mikrofon (P60)             
  ADC12MCTL3 = (INCH_3 + SREF_0); // externe Spannung (P63)            
  ADC12MCTL4 = (INCH_4 + SREF_0); // Batteriespannung (P64);           
  ADC12MCTL5 = (INCH_5 + SREF_0) + EOS;  // Funk Empfangsleistung (P65)     
  { extern UINT16 _micDiff; _micDiff = 0xFFFF; }
  MIC = 0x00;
#elif defined(ECR)
  ADC12CTL1 = SHP+CONSEQ_3+CSTARTADD_4;   // Use sampling timer, repeat-sequence-of-channels
  ADC12MCTL4 = (INCH_4 + SREF_0);        // Batteriespannung (P64);           
  ADC12MCTL5 = (INCH_5 + SREF_0) + EOS;  // Funk Empfangsleistung (P65)    
#elif defined(eGate)
  ADC12CTL1 = SHP+CONSEQ_3+CSTARTADD_5;   // Use sampling timer, repeat-sequence-of-channels     
  ADC12MCTL5 = (INCH_5 + SREF_0) + EOS;   // Funk Empfangsleistung (P65)     
#endif
  _skipADC = 0; 
}

/// Enables and starts ADU.
void enableADU(){
  _initADU();
  ADC12CTL0 |= ADC12ON;
  ADC12IE = 0x0020;                     // Enable ADC12IFG.5, Interrupt when sequence is
                                        // completed
  System_wait(600);                            // Delay for reference start-up
  ADC12CTL0 |= ENC;                     // enable conversion
  ADC12CTL0 |= ADC12SC;                 // Start conversion
  _skipADC = 100;
}

/// Stops and disables ADU.
void disableADU(){
  ADC12CTL0 &= ~ENC;                 // Stop conversion   
  System_wait(600);                        // Wait, TODO exchange with test for ADC12BUSY &test
  ADC12CTL0 &= ~(ADC12ON+REFON);     // turn off ADC12
}
//@}

/// \name Initalizing & Controlling the DCO (Digitally Controlled Oscillator)
//@{

volatile UINT16 dcoCap;        ///< Contains always last TBCCR6 Value (Capture on ACLK)
volatile UINT16 dcoDiff;       ///< Evreytime dcoDiff==0 TIMER_B1 interrupt writes a new DCO diff measure into it.
volatile UINT8  checkDCOState; ///< DCO will only be changed if two times low or high in a row to avoid peaks. Implemented with this variable.

bool System_setDCO(){
#if defined(DCO_2458KHZ)
#define DELTA  600                      ///< Target DCO = DELTA*(4096) = 2457600 */
#elif defined(DCO_4506KHZ)
#define DELTA  1100                      ///< Target DCO = DELTA*(4096Hz) , DELTA=550 => 4505600 Hz  */
#else
#error No DCO frequency in Set_DCO.
#endif
#define UP		 1                        ///< Temporary.
#define DOWN     2                      ///< Temporary.
  UINT16 Compare, Oldcapture = 0, i, direction = 0;
  //BCSCTL1 = 0x87; // ACLK not is devided. RSEL=6 no division for MCLK and SSMCLK. XT2 is off.
  BCSCTL1 = 0xB7; // ACLK is devided by 8 = 4096 Hz. RSEL=6 no division for MCLK and SSMCLK. XT2 is off.
  BCSCTL2 = 0x00; // Init FLL to desired frequency using the 32762Hz crystal DCO frquenzy = 2,4576 MHz   
  DCOCTL = 0xFF;
  for (i = 0xFFFF; i > 0; i--);         // Delay for XTAL to settle  
  CCTL2 = CCIS0 + CM0 + CAP;            // Define CCR2, CAP, ACLK
  TACTL = TASSEL1 + TACLR + MC1;        // SMCLK, continous mode
  //
  while (1){
    CCTL2 &= ~(CCIFG + COV);             // clear IFG and possible overflow
                                        // 1. capture
    while (!(CCTL2 & CCIFG));           // Wait until capture occured!    
    CCTL2 &= ~CCIFG;                    // Capture occured, clear flag
    Oldcapture = CCR2;                  // Save current captured SMCLK
                                        // 2. - 5. capture, "software divider" so aclk stays untouched and 2400 bps are working!
    //for(i=0; i<4; i++){
      while (!(CCTL2 & CCIFG));         // Wait until capture occured!    
      CCTL2 &= ~CCIFG;                  // Capture occured, clear flag
      Compare = CCR2;
    //}
    Compare = Compare - Oldcapture;     // SMCLK difference
    if (DELTA == Compare){  
      break;        // if equal, leave "while(1)"
    }
    else if (DELTA < Compare){           // DCO is too fast, slow it down
      if(direction == UP) break;          // stop if we were going up (jumped over exact delta)
      if(!direction) direction = DOWN;    // if starting set direction
      DCOCTL--;
      if (DCOCTL == 0xFF){
         if(!((BCSCTL1 & 0x07) == 0x00)){
           WDTCTL = 0xDEAD;
         }      
         BCSCTL1--;     // Did DCO role under? -> Select next lower RSEL
      }
    }    
    else {                      
      if(direction == DOWN) break;          // stop if we were going down (jumped over exact delta)
      if(!direction) direction = UP;       // if starting set direction
      DCOCTL++;
      if (DCOCTL == 0x00){
         if((BCSCTL1 & 0x07) == 0x07){
           WDTCTL = 0xDEAD;
         }      
         BCSCTL1++;     // Did DCO role over? // -> Select next higher RSEL
      }                                               
    }
  }  
  CCTL2 = 0;                              // Stop CCR2 function
  TACTL = 0;                              // Stop Timer_A 
  dcoDiff = 0;
  checkDCOState = 0; 
  return True;
}

#define DCOLOW      (1)                   ///< Symbolic name, DCO was last measure too low.
#define DCOHIGH     (2)                   ///< Symbolic name, DCO was last measure too high.
#define DCODIFF     (3)                   ///< Difference of dcoDiff to DELTA when DCO measure is taken as too high/low. 

void checkDCO(){
  if(dcoDiff<(DELTA-DCODIFF)){ // frequency too low
    if(checkDCOState != DCOLOW){ checkDCOState = DCOLOW; }
    else {
      if(Comm_logLevel & DEBUG_DCO) Comm_print("DCO++");
      DCOCTL++;
      if (DCOCTL == 0x00){
         if((BCSCTL1 & 0x07) == 0x07){
           Comm_print("Frequency too high!!!\r\n");
           WDTCTL = 0xDEAD;
         }      
         BCSCTL1++;     // Did DCO role over? // -> Select next higher RSEL
      }
      if(Comm_logLevel & DEBUG_DCO) Comm_print(" %b %b >%i\r\n", DCOCTL, BCSCTL1, dcoDiff);        
    }
  }
  else if(dcoDiff>(DELTA+DCODIFF)){ // frequency too high
    if(checkDCOState != DCOHIGH){ checkDCOState = DCOHIGH; }
    else {
      if(Comm_logLevel & DEBUG_DCO) Comm_print("DCO--");
      DCOCTL--;
      if (DCOCTL == 0xFF){
         if(!((BCSCTL1 & 0x07) == 0x00)){
           Comm_print("Frequency too low!!!\r\n");
           WDTCTL = 0xDEAD;
         }      
         BCSCTL1--;     // Did DCO role under? -> Select next lower RSEL
      }    
      if(Comm_logLevel & DEBUG_DCO) Comm_print(" %b %b >%i\r\n", DCOCTL, BCSCTL1, dcoDiff);        
    }
  }
  else { checkDCOState = 0; } // freqency was ok 
  Timers_add(100, checkDCO, 0xFFFF);
}

/** \brief DCO Checker. Interrupt occurs every ~250us. Continously updates ::dcoDiff. */
interrupt(TIMERB1_VECTOR) Timer_B1 (void){
  dcoDiff = TBCCR6 - dcoCap;
  dcoCap = TBCCR6;            
  TBCCTL6 &= ~(CCIFG);
}

//@}

bool System_startWatchdog() {
  do { WDTCTL = WDT_ARST_1000; TACCR1 = TAR + 4096; TACCTL1 = CCIE; }while(0);
  return True;
}

bool System_stopWatchdog() {
  do { WDTCTL = WDTPW + WDTHOLD; TACCTL1 &= ~CCIE; }while(0);
  return True;
}

bool System_reset() {
  WDSTATE = WDS_SWRESET;
  FCTL3 = 0xDEAD;
  return True;
}

void wd_init(){
    // It was a POR/PUC.
    if(TACTL == 0x0000) wd_startup = STARTUP_POR;    
    // PUC only: look further.
    else {                                      
      if(FCTL3 & KEYV){ // PUC: Was it a flash key violation?
        FCTL3 = 0xA500; // Clear KEYV Bit in FCTL3, check if wanted or by accident.
        if(WDSTATE==WDS_SWRESET) wd_startup = STARTUP_SWRESET;
        else if(WDSTATE==WDS_PANIC) wd_startup = STARTUP_PANIC;
        else if(WDSTATE==WDS_REPROGRAM) wd_startup = STARTUP_REPROGRAM;
        else                      wd_startup = STARTUP_KEYV;
      }
      else if(IFG1 & WDTIFG){ // PUC: Was it the watchdog?
        IFG1 &= ~WDTIFG; // Clear WDTIFG Bit. 
        wd_startup = STARTUP_WATCHDOG;
        // WDSTATE could be further evaluated here, where occured the wdt expiry?
      }
      else { // PUC only but no watchdog? 
        wd_startup = STARTUP_UNKNOWN;
      }
    }
    WDSTATE = WDS_RUNNING;        //
}

void wd_stackCheck(){
  extern UINT16 __noinit_end;
  UINT16* p = & __noinit_end;
  if(p[0]!=0xABCD || p[1]!=0xABCD || p[2]!=0xABCD || p[3]!=0xABCD){
    if(Configuration.panicFlags & PANIC_ON_STACK_OVERFLOW) System_panic("Stack LOW");
    else System_reset();
  }
  Timers_add(1024, wd_stackCheck, 0xFFFF);    
}

void wd_init2(){
  wd_stackCheck();
  if((Configuration.panicFlags & PANIC_ON_BAD_RESET) && (wd_startup > STARTUP_PANIC)){
    System_panic("RESET[%s]", System_resetReason());
  }
}

const UINT8 reason_strings[7][10] = { "POR", "SWRESET", "REPROGRAM", "PANIC", 
                                             "WATCHDOG", "KEYV", "UNKNOWN" };

const UINT8* System_resetReason(){ return reason_strings[wd_startup]; }

/** Clears userappControl in infomem. */
bool System_panicClear(){
  UINT8 ie1 = IE1;
  ///////////////////////////// disable all interrupts
  IFG1=0;                    // clear interrupt flag1 
  WDTCTL=0x5A80;             // stop WD 
                             // DCO(SMCLK) is 2,4576MHz, /6 = 409600 Hz 
  FCTL2=0xA5C5;              // select SMCLK for flash timing, divider 5+1 
  _DINT();                   // disable all interrupts to protect CPU 
                             // during programming from system crash 
  IE1=0x00;                  // disable all NMI-Interrupt sources
  /////////////////////////////
  FCTL3 = 0xA500;               // Lock = 0   
  while(FCTL3 & 0x0001) nop();  // Wait for BUSY = 0, not needed unless run from RAM
  FCTL1 = 0xA502;               // ERASE = 1 
  *((UINT16*)0x1080) = 0xFFFF;            // erase 2. Flash info segment 
  FCTL1 = 0xA500;               // ERASE = 0 automatically done?!
  FCTL3 = 0xA510;               // Lock = 1 
  ///////////////////////////// 
  System_startWatchdog();           // 
  _EINT();                   // 
  IFG1 = 0x00;               // 
  IE1= ie1;                  // 
  /////////////////////////////
  return True;
}

UINT8* panic_dest;
/** Writes something to userappControl */
void panic_w(UINT8 c){
  if(panic_dest < (UINT8*)0x1080 || 
     panic_dest > (UINT8*)0x10FF) return;
  if(panic_dest == (UINT8*)0x10FF) c = 0x00;
  ///////////////////////////// disable all interrupts
  IFG1=0;                    // clear interrupt flag1 
  WDTCTL=0x5A80;             // stop WD 
                             // DCO(SMCLK) is 2,4576MHz, /6 = 409600 Hz 
  FCTL2=0xA5C5;              // select SMCLK for flash timing, divider 5+1 
  _DINT();                   // disable all interrupts to protect CPU 
                             // during programming from system crash 
  IE1=0x00;                  // disable all NMI-Interrupt sources
  /////////////////////////////  
  FCTL3 = 0xA500;              // Lock = 0 
  while(FCTL3 & 0x0001) nop(); // Wait for BUSY = 0, not needed unless run from RAM
  FCTL1 = 0xA540;              // WRT = 1 
  *panic_dest =  c;
  panic_dest++;    
  FCTL1 = 0xA500;              // WRT = 0 
  FCTL3 = 0xA510;              // Lock = 1 
  ///////////////////////////// Dotn need to reenable watchdog or ints as reset will follow
}

interrupt(WDT_VECTOR) wdt_isr (void){
  //_BIC_SR_IRQ(LPM3_bits); // clear bits of SR on stack, produces warning
  WAKEUPLPM1(wdt_isr, 0x01);
}

// Possible callers: wd_stackCheck/watchdog.c
//                     Timers_add/timers.c
//                     
bool System_panic(const char *fmt, ...){
  extern volatile time_t currentTime; // interrupts are disabled so access direct
  va_list argp;
  btime_t btime;
  dint();
  IFG1 = 0x00;
  panic_dest = (UINT8*) 0x1080;
  while(*panic_dest != 0xFF && panic_dest < (UINT8*)0x10FF) panic_dest++;
  Time_convertSystemTime((time_t*)&currentTime, &btime);
  Time_writeTime(panic_w, &btime);  
  panic_w(' ');
  va_start(argp, fmt);
  String_abstractWrite(panic_w, fmt, argp);  
  va_end(argp);
  panic_w(0x00);
  WDSTATE = WDS_PANIC;
  _NOP();
  FCTL3 = 0xDEAD;
  return True;
}

UINT8* System_panicReason(UINT16 n){
  UINT8* p = (UINT8*)0x1080;
  UINT16 i;
  for(i=0; i<n; i++){
    while(*p != 0x00) p++;
    p++;
  }
  if(*p != 0xFF && p < (UINT8*)0x10FF) return p;
  else           return NULL;
}

UINT16 System_reprogram(){
  extern UINT16 prog_reprogram();
  UINT16 result;
  if(!(Configuration.firmwareFlags & FF_PROGRAMMABLE)){
    Comm_log(LOW, "ERROR Not reprogrammable!\r\n");
    return 1000;
  }
  WDSTATE = WDS_REPROGRAM;
  result = prog_reprogram();
  if(result==ERROR_NOIMAGE) Comm_log(LOW, "ERROR No image!\r\n");
  else if(result==ERROR_SIZEMISMATCH) Comm_log(LOW, "ERROR Size mismatch (changed programming.c?!?)\r\n");
  else if(result==ERROR_WRONGDEVICE) Comm_log(LOW, "ERROR Wrong device!\r\n");
  else if(result==ERROR_WRONGADDRESS) Comm_log(LOW, "ERROR Wrong address!\r\n");
  else Comm_log(LOW, "ERROR Unknown error=%i\r\n", result);
  WDSTATE = WDS_RUNNING;
  return result;
}

// software wd
interrupt(TIMERA1_VECTOR) timera1_isr (void){
  UINT16 oldpc = * ( ((UINT16*)GET_FRAME_ADDR(timera1_isr)) + 1);
  UINT16 sp = READ_SP;
  System_panic("SW-WD(%x,%x)", oldpc, sp);
  TACCTL1 = 0x0000; // Ensure timer is off. clrs IE&IFG
  System_reset();
}

/** A simple looping waiting function.
 ** Rough estimate at 2,4576 MHz: w=1 => 18us, w=10 => 62us, w=100 => 500us  w=400 => 4ms.<br>
 ** This method has two big disadvantages: it blocks and it's not 
 ** accurate, timers are better. */
bool System_wait(UINT32 w){
  volatile UINT32 i; 
  for(i=0;i<w;i++) nop(); 
  return True;
}

/// Another waiting functions which is more precise than wait because it uses TimerA to produce its interval, also blocking.
bool System_waitA(UINT16 i){
  UINT16 tar = TAR;
  while(TAR - tar < i) System_startWatchdog();
  return True;
}

/// Another waiting function in assembler found somewhere on the MSP430 Yahoo discussion group.
static void __inline__ pause(register UINT16 n) {
    __asm__ __volatile__ (
		"1: \n"
		" dec	%[n] \n"
		" jne	1b \n"
        : [n] "+r"(n));
}
//@}

UINT16 const ccitt_crc16_table[256] =
 {
 	0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
 	0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
 	0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
 	0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
 	0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
 	0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
 	0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
 	0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
 	0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
 	0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
 	0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
 	0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
 	0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
 	0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
 	0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
 	0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
 	0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
 	0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
 	0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
 	0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
 	0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
 	0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
 	0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
 	0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
 	0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
 	0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
 	0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
 	0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
 	0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
 	0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
 	0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
 	0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
};
 
/// Recompute the FCS with one more character appended.
UINT16 System_ccittFcs(UINT16 fcs, UINT8 c){
 	return (((fcs) >> 8) ^ ccitt_crc16_table[((fcs) ^ (c)) & 0xff]);
}
 
/// Recompute the FCS with len bytes appended.
UINT16 System_crcCalc(UINT16 fcs, UINT8 const *buf, UINT16 len) {
	while (len--) fcs = System_ccittFcs(fcs, *buf++);
	return fcs;
}

//@}

NAKED(_reset_vector__){
  extern UINT16 __noinit_end; // End of data segment in RAM.
  UINT16* p = & __noinit_end; // Held in a register because only 
                                    // var in this procedure!
  while(p < (UINT16*)0x09FE){ // Fill rest of RAM with 0xABCD.
    *p = 0xABCD;
    p++;    
  }
  __asm__ __volatile__("br #main"::); // Branch to main.
}

//@{
void _unexpected_ () { }                                   ///< unsed ISR
interrupt(UART1TX_VECTOR)     uart1_tx (void){ }            ///< unsed ISR
interrupt(COMPARATORA_VECTOR) compa_isr (void){ }           ///< unsed ISR
interrupt(NMI_VECTOR) nmi_isr (void){  }

#if defined(eGate)
interrupt(PORT2_VECTOR) p2_irq (void){ }
interrupt(PORT1_VECTOR) p1_irq (void){ }
#endif
//@}

/** \brief Performs initialization of all ports.
 **
 ** Sets SELECTION, DIRECTION and OUT registers for P1 to P6, see code for details. 
 */
void initPorts(void){
  #if defined(ESB) || defined(EYE)
  ////////// Port 1 ////
  P1SEL = 0x00;
  P1DIR = 0x81;       // Outputs: P10=IRSend, P17=RS232RTS
                      // Inputs: P11=Light, P12=IRRec, P13=PIR, P14=Vibration, 
                      //         P15=Clockalarm, P16=RS232CTS
  P1OUT = 0x00;
  ////////// Port 2 ////
  P2SEL = 0x00;       // Select for beeper, it is controlled via TimerA1
  P2DIR = 0x7F;       // Outpus: P20..P23=Leds+Beeper, P24..P26=Poti
                      // Inputs: P27=Taster
  P2OUT = 0x77;
  ////////// Port 3 ////
  P3SEL = 0xE0;       // Sels for P34..P37 to activate UART, 
  P3DIR = 0x5F;       // Inputs: P30..P33=CON4, P35/P37=RXD Transceiver/RS232
                      // Outputs: P34/P36=TXD Transceiver/RS232
  P3OUT = 0x00;       // Output a Zero on P34(TXD Transceiver) and turn SELECT off when receiving!!!
  ////////// Port 4 ////
  P4SEL = 0x00;       // CON5 Stecker
  P4DIR = 0xFF;
  P4OUT = 0x00;
  ////////// Port 5 ////
  P5SEL = 0x00;       // P50/P51= Clock SDA/SCL, P52/P53/P54=EEPROM SDA/SCL/WP
  P5DIR = 0xF0;       // P56/P57=Transceiver CNTRL0/1, P55=Sensors ON/OFF (0 ON, 1 OFF)
  P5OUT = 0x2F;
  ////////// Port 6 //// 
  P6SEL = 0x00;       // P60=Microphone, P61=PIR digital (same as P13), P62=PIR analog
  P6DIR = 0x00;       // P63=extern voltage, P64=battery voltage, P65=Receive power
  P6OUT = 0x00;
  #elif defined(ECR)
  ////////// Port 1 ////
  P1SEL = 0x00;       // Free: alle ausser P14
  P1DIR = 0xEF;       // Outputs: None
                      // Inputs: P14=Vibration, 
  P1OUT = 0x00;
  ////////// Port 2 ////
  P2SEL = 0x00;       // free: P21, P22, P27
  P2DIR = 0xFF;       // Outpus: P20=Red Led, P23=LM71 /CS, P24..P26=Poti
  P2OUT = 0x79;       // Output all 1s for POTI, LM71 CS and 
  ////////// Port 3 //// Free: P30..P33
  P3SEL = 0xE0;       // Sels for P34..P37 to activate UART, 
  P3DIR = 0x5F;       // Inputs: P35/P37=RXD Transceiver/RS232
                      // Outputs: P34/P36=TXD Transceiver/RS232
  P3OUT = 0x00;       // Output a Zero on P34(TXD Transceiver) and turn SELECT off when receiving!!!
  ////////// Port 4 //// Free: all
  P4SEL = 0x00;       // CON1 / CON2 Stecker
  P4DIR = 0xFF;
  P4OUT = 0x00;
  ////////// Port 5 ////
  P5SEL = 0x00;       // P50[In]/P51[Out]= LM71 SDA/SCL, P52/P53/P54=EEPROM SDA/SCL/WP, 
  P5DIR = 0xF2;       // P56/P57=Transceiver CNTRL0/1, P55=[LM71, EEPROM, Con1, Con2] ON/OFF (0 ON, 1 OFF)
  P5OUT = 0x0C; // Power fro LM71,EEPROM,Con1,Con2 is on at initialization to read config
  ////////// Port 6 //// 
  P6SEL = 0x30;       // Free: P60 P61 P62 P63 P66 P67
  P6DIR = 0xCF;       // P64=battery voltage, P65=Receive power
  P6OUT = 0x00;
  #elif defined (eGate)
  ////////// Port 1 ////
  P1SEL = 0x00;       // No Sels
  P1DIR = 0x02;      // Inputs P10..P16 USB RTS/CTS/DTR/DSR/DCO/RI/SLEEP, Outputs CTS 
  P1OUT = 0x00;       // all
  ////////// Port 2 ////
  P2SEL = 0x00;       // No Sels
  P2DIR = 0x70;       // Outputs: P24/P25/P26 DS1804 /INC U/D /CS
  P2OUT = 0x70;
  ////////// Port 3 ////
  P3SEL = 0xE0;       // Sels for P34..P37 to activate UART

  P3DIR = 0x5F;       // Inputs: P30..P33=CON4, P35/P37=RXD Transceiver/RS232  
  //P3DIR = 0x1F;       // Inputs: P35/P37=RXD RF/USB
                      // Outputs: P34/P36=TXD RF/USB
  P3OUT = 0xE0;       // Output a Zero on P34(TXD Transceiver) and turn SELECT off when receiving!!!
  //P3OUT = 0xFF;       // Output
  ////////// Port 4 ////
  P4SEL = 0x00;       // CON4
  P4DIR = 0xFF;       // Outputs: (JTAG-Interface zur Weiterprogrammierung)
  P4OUT = 0xFF;
  ////////// Port 5 ////
  P5SEL = 0x00;       // P55=LED P52/P53/P54=EEPROM SDA/SCL/WP P56/P57=Transceiver CNTRL0/1
  P5DIR = 0xD1;       // Outputs: P50 LED  P54 WP, P56/57 CNTRL0/1
  P5OUT = 0x01;       // Inputs: P52/P53, LED IS ON
  ////////// Port 6 ////
  P6SEL = 0x00;       // No Sels
  P6DIR = 0x00;       // Input: P65 BBOut,CMPIn
  P6OUT = 0x00;
  #else
#error No device for initPorts()!
#endif
}

/** The callbacks set via ::registerCallback are stored here. */
fp_vp callbacks[NUMBER_CALLBACKS];

/** Use this function to register a callback. */
//void registerCallback( UINT8 type, void(*fp)() ){
bool System_registerCallback( UINT8 type, fp_t fp ){
  callbacks[type] = fp;
  return True;
}

extern void Process_init();  ///< The only function in userapp that can be called from firmware (except callbacks).

volatile UINT16 runModule;

/** \brief Firmware main loop.
**
 **  Runs the bootsequence and then loops endlessly, calling callbacks and doing
 **  internal stuff. */
__attribute__((reserve(2))) int main(void) {
  // ---------------------------------------------------------------------------
  // --------------- Hardware initialization. ----------------------------------
  // ---------------------------------------------------------------------------
  WDTCTL = WDT_ARST_1000;
  System_stopWatchdog();
  // Port initialization, important
  initPorts();
  wd_init();
  runModule = 0;
#if defined(eGate)
  while(! P1IN & 0x40); // Wait until FTDI enables power.
  System_wait(10000);
#endif
  enableADU();
  // TODO: get one advalue and hang here sleepin until voltage is high enough
#if defined(ESB) || defined(EYE) 
  initClock();   // Basic clock set up, important!!! 
#endif
  Timers_init();
  eint();
  Configuration_init();
  Comm_init(BPS_115200);
  Net_init();
  Net_on();
#if defined(ESB) || defined(EYE) || defined(ECR)
  Data_init();
  Data_on();
#endif
#if defined(ESB) || defined(EYE)
  System_wait(150000); // Needed for microphone.
#endif
  // ---------------------------------------------------------------------------
  // --------------- Firmware internal software initialization. ----------------
  // ---------------------------------------------------------------------------
  wd_init2();
  flasher_init();
  Configuration_print();
  // ---------------------------------------------------------------------------
  // --------------- Application initialization. -------------------------------
  // ---------------------------------------------------------------------------
  /* clear callbacks */
  {  volatile UINT16 i;
     for(i=0; i<NUMBER_CALLBACKS; i++) callbacks[i] = 0;  }
  // Standard is the terminal, so terminal will also work if application is disabled.
  System_registerCallback(C_SERIAL, Messaging_serialHandler);         
  Threading_init();
  /* If PANIC_DISABLES_APP is set and a panic occured dont intialize userapp. */
  if((Configuration.panicFlags & PANIC_DISABLES_APP) && (System_panicReason(0) != NULL)){
    Comm_log(HIGH, "Info: Userapp disabled.\r\n");
  }
  else {
    Comm_log(HIGH, "Info: Userapp enabled.\r\n");
    Process_init();
  }
  // ---------------------------------------------------------------------------
  // --------------- Superloop -------------------------------------------------
  // ---------------------------------------------------------------------------  
  for(;;){  
    System_startWatchdog(); // Starts&resets the watchdog, long procedures should call
                     // stop watchdog, else MSP will reset.
    //print("WAKEUP %x\r\n", runModule);
    if(runModule & MF_SCOS) Threading_eventHandler();
    //// Radio tasks.
    if(runModule & MF_RADIO_RX) Net_rxHandler();  
    if(runModule & MF_RADIO_TX) Net_txHandler();
    if(runModule & MF_TIMER) Timers_eventHandler();  
#if !defined(EYE)
    //// Callback for serial line
    if(runModule & MF_SERIAL_RX){
      extern volatile UINT8* serial_line;
      if(serial_line != 0){
      	if(callbacks[C_SERIAL]) callbacks[C_SERIAL]((void*)serial_line);
      }
    }
#endif
#if defined(ESB) || defined(EYE) || defined(ECR)
    if(runModule & MF_SENSORS) if(sensorFlags != 0x00) Data_sensorHandler();
#endif
#if defined(ESB) || defined(EYE)   
    if(runModule & MF_RC5) Data_RC5ReceiveHandler();
#endif
    dint(); 
    nop();
    if(runModule==0){
      // enter lpm3 and enable GIE at once to not loose an interrupt
      System_stopWatchdog();
      eint();
      LPM1;
    } else eint();
  }  
} // END main()
