Sunday, August 5, 2012

Free MSP430 Compiler Resource

TI CCS Studio 4 comes with eZ430 watch is limited edition compiler.  The max size is 16K.  It is difficult to explore different project without breaking the limit.

Other option for the compiler is
http://www.bipom.com/products/us/3180265.html

http://gnutoolchains.com/msp430/
which can use with VisualGDB plug in in Visual Studio.


Most Popular Unrestricted IDEs and Compilers


Thursday, August 2, 2012

eZ430 Pedometer with Internal Sensor

We can use the ACC sensor to implement pedometer.  I modify the previous 'Counter' project and look up the source code of acceleration.c to implement the pedometer function. All modified source code can be download from here .

Algorithm
   I am working on pedometer using all ACC x, y, z sensor data.  I measure the value of
sum = abs(x) + abs(y) + abs(z) and detect the highest and lowest value.  If change direction found (from rising to falling or from falling to rising), I can mark the highest and lowest value.
To filter out noise, the difference of highest and lowest value must over certain threshold to add the count.  The threshold value define the sensitivity of the pedometer.  The walk mode and run mode use different sensitivity value to prevent noise.

Function key: 
* key: long press to switch walk mode or run mode. Walk mode show down arrow.  Run mode show up arrow.
^ key: reset counter value

To be done
   There are still lots of task can be done to prevent noise step or miss step.

Modification

  • File project.h,



// Set of request flags
typedef union
{
  struct
  {
    u16 temperature_measurement : 1;    // 1 = Measure temperature
    u16 voltage_measurement     : 1;    // 1 = Measure voltage
    u16 altitude_measurement     : 1;    // 1 = Measure air pressure
    u16 acceleration_measurement : 1; // 1 = Measure acceleration
    u16 buzzer       : 1;    // 1 = Output buzzer
    u16 counter_measurement         : 1;    // 1 = measure counter from acceleration
  } flag;
  u16 all_flags;            // Shortcut to all display flags (for reset)
} s_request_flags;



  • File main.c,


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);

if ( request.flag.counter_measurement ) do_counter_measurement();
// Reset request flag
request.all_flags = 0;
}


  • File timer.c,


__interrupt void TIMER0_A0_ISR(void)
{
// 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;

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

// Exit from LPM3 on RETI
_BIC_SR_IRQ(LPM3_bits);  
return;
}

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

// 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) as_stop();

// If DRDY is (still) high, request data again
if ((AS_INT_IN & AS_INT_PIN) == AS_INT_PIN) request.flag.acceleration_measurement = 1;
}
if ( is_counter_measurement()) 
{
if ((AS_INT_IN & AS_INT_PIN) == AS_INT_PIN) request.flag.counter_measurement = 1; 
}
// If BlueRobin transmitter is connected, get data from API
if (is_bluerobin()) get_bluerobin_data();

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

// -------------------------------------------------------------------
// Detect continuous M1/M2 button high state
if (BUTTON_M1_IS_PRESSED)
{
sButton.m1_timeout++;

// Check if button was held low for more than M_BUTTON_LONG_TIME seconds
if (sButton.m1_timeout > M_BUTTON_LONG_TIME)
{
button.flag.m1_long = 1;
sButton.m1_timeout = 0;

// Ignore next M1 button event
sys.flag.mask_m1_button = 1;
}
}
else
{
sButton.m1_timeout = 0;
}

if (BUTTON_M2_IS_PRESSED)
{
sButton.m2_timeout++;

// Check if button was held low for more than M_BUTTON_LONG_TIME seconds
if (sButton.m2_timeout > M_BUTTON_LONG_TIME)
{
button.flag.m2_long = 1;
sButton.m2_timeout = 0;

// Ignore next M2 button event
sys.flag.mask_m2_button = 1;
}
}
else
{
sButton.m2_timeout = 0;
}

// Exit from LPM3 on RETI
_BIC_SR_IRQ(LPM3_bits);            
}


  • File ports.c, 


#pragma vector=PORT2_VECTOR
__interrupt void PORT2_ISR(void)
{
......


// ---------------------------------------------------
// Acceleration sensor IRQ
if (IRQ_TRIGGERED(int_flag, AS_INT_PIN))
{
// Get data from sensor
if ( is_acceleration_measurement())
  request.flag.acceleration_measurement = 1;
if ( is_counter_measurement()) {
  request.flag.counter_measurement = 1;
}
  }
  ......


  • File counter.h,


#ifndef COUNTER_H_
#define COUNTER_H_

// *************************************************************************************************
// Include section


// *************************************************************************************************
// Prototypes section

// internal functions
extern void reset_counter(void);

// menu functions
extern void mx_counter(u8 line);
extern void sx_counter(u8 line);
extern void display_counter(u8 line, u8 update);
extern u8 is_counter_measurement(void);
extern void do_counter_measurement(void);


// *************************************************************************************************
// Defines section


// *************************************************************************************************
// Global Variable section
struct counter
{
// MENU_ITEM_NOT_VISIBLE, MENU_ITEM_VISIBLE
menu_t state;
s16 count;
// Sensor raw data
u8 xyz[3];
// Acceleration data in 10 * mgrav
u16 data[3];
u16         sum, low, high; // total data
u8 rise_state; // 0: init, 1: rise, 2: fall
u8 style; // 0: walk, 1: run
};
extern struct counter sCounter;


#endif /*COUNTER_H_*/

  • File counter.c,

// system
#include "project.h"
// driver
#include "counter.h"
#include "ports.h"
#include "display.h"
#include "vti_as.h"

//#include "adc12.h"
//#include "timer.h"

// logic
#include "user.h"
// Global Variable section
struct counter sCounter;
void reset_counter(void)
{
  sCounter.state = MENU_ITEM_NOT_VISIBLE; 
sCounter.count = 0;
sCounter.style = 0;
}

void mx_counter(u8 line)
{
// change walk mode or run mode
display.flag.update_counter = 1;
sCounter.style = !sCounter.style;
}
void sx_counter(u8 line)
{
// reset counter
sCounter.count = 0; 
display.flag.update_counter = 1;
}
void display_counter(u8 line, u8 update)
{
u8 * str;
// Redraw line
switch( update ) {
case DISPLAY_LINE_UPDATE_FULL:
    sCounter.data[0] = sCounter.data[1] = sCounter.data[2] = 0;
sCounter.sum = 0;
sCounter.rise_state = 0;
// Start sensor
as_start();
// Set icon

// Menu item is visible
sCounter.state = MENU_ITEM_VISIBLE; 
// Display result in xx.x format
case DISPLAY_LINE_UPDATE_PARTIAL:
// Display result in xx.x format
str = itoa(sCounter.count, 4, 0);
display_chars(LCD_SEG_L1_3_0, str, SEG_ON);
display.flag.update_counter = 0;
if ( sCounter.style ) { 
  display_symbol(LCD_SYMB_ARROW_UP, SEG_ON);
  display_symbol(LCD_SYMB_ARROW_DOWN, SEG_OFF);   
} else {
  display_symbol(LCD_SYMB_ARROW_DOWN, SEG_ON);
        display_symbol(LCD_SYMB_ARROW_UP, SEG_OFF);
}
break;
case DISPLAY_LINE_CLEAR:
as_stop();
// Menu item is not visible
sCounter.state = MENU_ITEM_NOT_VISIBLE;
// Clear function-specific symbols
display_symbol(LCD_SYMB_ARROW_UP, SEG_OFF);
display_symbol(LCD_SYMB_ARROW_DOWN, SEG_OFF);
break;
}
}
u8 is_counter_measurement(void)
{
   return sCounter.state == MENU_ITEM_VISIBLE;
}
#define WALK_COUNT_THRESHOLD  400
#define RUN_COUNT_THRESHOLD  300
void do_count(void)
{
u16 threshold = (sCounter.style ? RUN_COUNT_THRESHOLD: WALK_COUNT_THRESHOLD );
if (( sCounter.high - sCounter.low) > threshold ) {
sCounter.count ++;  
display.flag.update_counter = 1;
}
}
void do_counter_measurement(void)
{
extern u16 convert_acceleration_value_to_mgrav(u8 value);
u8 i;
u16 accel_data, sum1;
// Get data from sensor
as_get_data(sCounter.xyz);
for ( i = 0, sum1 = 0; i < 3; i ++ ) {
accel_data = convert_acceleration_value_to_mgrav(sCounter.xyz[i]);
// Filter acceleration
#if 0
accel_data = (u16)((accel_data * 0.2) + (sCounter.data[i] * 0.8));
#endif
accel_data = (u16)((accel_data * 0.1) + (sCounter.data[i] * 0.9));
sum1 += accel_data;
// Store average acceleration
sCounter.data[i] = accel_data;
}
if ( sCounter.sum == 0 ) {
  sCounter.low = sum1;
  sCounter.high = sum1; 
} else { 
  switch ( sCounter.rise_state) { // init state
  case 0:
    if ( sum1 > sCounter.sum ) {
      sCounter.rise_state = 1;
      sCounter.low = sCounter.sum;
    } else {
      sCounter.rise_state = 2;
      sCounter.high = sCounter.sum;
    }
    break;
  case 1: 
 if ( sum1 < sCounter.sum ) { // change direction
   sCounter.high = sCounter.sum;
   sCounter.rise_state = 2;
 }
 break;
  case 2:
    if ( sum1 > sCounter.sum ) {
     sCounter.low = sCounter.sum;
     sCounter.rise_state = 1; 
           do_count();
    } 
    break;
  }  
    }
sCounter.sum = sum1;
// Set display update flag
// display.flag.update_counter = 1;
}

  • If source file compile error, maybe need to include "counter.h"
  • If not modify from previous example, make sure to modify menu.c
  1. include "counter.h"
  2. add menu item and change menu of time function
// Line 1 - Counter
const struct menu menu_L1_Counter = 
{
   FUNCTION( sx_counter ),
   FUNCTION( mx_counter ),
   FUNCTION( display_counter ),
   FUNCTION( update_counter ),
   &menu_L1_Alarm,
};
// Line1 - Time

const struct menu menu_L1_Time =
{
FUNCTION(sx_time), // direct function
FUNCTION(mx_time), // sub menu function
FUNCTION(display_time), // display function
FUNCTION(update_time), // new display data
// &menu_L1_Alarm,
&menu_L1_Counter,
};
// Line1 - Alarm
  • in main.c, include "counter.h" and add 
void init_global_variables(void)
{
...
       // Read calibration values from info memory
read_calibration_values();
// reset counter
reset_counter();

}




Saturday, July 21, 2012

Menu and New Function

Counter Function Description

I'd like to add a "Counter" function into the watch.

  1.    Press "Up" key, counter add 1.  
  2.    Long press "*" key, counter reset to zero.
I'd like to add counter function after Time function.

File "counter.h"


#ifndef COUNTER_H_
#define COUNTER_H_

// **************************************************************
// Include section


//***************************************************************
// Prototypes section

// internal functions
extern void reset_counter(void);

// menu functions
extern void mx_counter(u8 line);
extern void sx_counter(u8 line);
extern void display_counter(u8 line, u8 update);


//***************************************************************
// Defines section


//***************************************************************
// Global Variable section
struct counter
{
// MENU_ITEM_NOT_VISIBLE, MENU_ITEM_VISIBLE
menu_t state;
s16 count;
};
extern struct counter sCounter;


#endif /*COUNTER_H_*/

File "counter.c"


// system
#include "project.h"
// driver
#include "counter.h"
#include "ports.h"
#include "display.h"
//#include "adc12.h"
//#include "timer.h"

// logic
#include "user.h"
// Global Variable section
struct counter sCounter;
void reset_counter(void)
{
  sCounter.state = MENU_ITEM_NOT_VISIBLE; 
sCounter.count = 0;
}

void mx_counter(u8 line)
{
sCounter.count = 0;
display.flag.update_counter = 1;
}
void sx_counter(u8 line)
{
sCounter.count ++;
display.flag.update_counter = 1;
}
void display_counter(u8 line, u8 update)
{
u8 * str;

// Redraw line
if (update == DISPLAY_LINE_UPDATE_FULL)
{
// Set battery and V icon
display_symbol(LCD_SYMB_ARROW_UP, SEG_ON);

// Menu item is visible
sCounter.state = MENU_ITEM_VISIBLE; 

// Display result in xxxx format
str = itoa(sCounter.count, 4, 0);

display_chars(LCD_SEG_L1_3_0, str, SEG_ON);
}
else if (update == DISPLAY_LINE_UPDATE_PARTIAL)
{
// Display result in xxxx format
str = itoa(sCounter.count, 4, 0);

display_chars(LCD_SEG_L1_3_0, str, SEG_ON);

display.flag.update_counter = 0;
}
else if (update == DISPLAY_LINE_CLEAR)
{
// Menu item is not visible
sCounter.state = MENU_ITEM_NOT_VISIBLE;
// Clear function-specific symbols
display_symbol(LCD_SYMB_ARROW_UP, SEG_OFF);
}
}

In file "main.c"



// 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"
#include "bluerobin.h"
#include "rfsimpliciti.h"
#include "simpliciti.h"
#include "test.h"
#include "counter.h"
....
void init_global_variables(void)
{
.....
// Read calibration values from info memory
read_calibration_values();
// reset counter
reset_counter();
}

In file "display.h", add another flag "update_counter"

typedef union
{
  struct
  {
  // Line1 + Line2 + Icons
    u16 full_update       : 1;    // 1 = Redraw all content
    u16 partial_update       : 1;    // 1 = Update changes
 
  // Line only
    u16 line1_full_update     : 1;    // 1 = Redraw Line1 content
    u16 line2_full_update     : 1;    // 1 = Redraw Line2 content

// Logic module data update flags
    u16 update_time       : 1;    // 1 = Time was updated 
    u16 update_stopwatch     : 1;    // 1 = Stopwatch was updated
    u16 update_temperature   : 1;    // 1 = Temperature was updated
    u16 update_battery_voltage : 1;    // 1 = Battery voltage was updated
    u16 update_date       : 1;    // 1 = Date was updated
    u16 update_alarm       : 1;    // 1 = Alarm time was updated
    u16 update_acceleration : 1; // 1 = Acceleration data was updated
    u16 update_counter : 1;    // 1 = counter was update
  } flag;
  u16 all_flags;            // Shortcut to all display flags (for reset)
} s_display_flags;


Menu Manipulation 

Add a menu after "Time" function.

File "menu.c"
In file menu.c, add a function first,


u8 update_counter(void)
{
return (display.flag.update_counter);
}

Then add counter menu

// Line 1 - Counter
const struct menu menu_L1_Counter = 
{
   FUNCTION( sx_counter ),
   FUNCTION( mx_counter ),
   FUNCTION( display_counter ),
   FUNCTION( update_counter ),
   &menu_L1_Alarm,
};

Change the Time menu next menu pointer,
// Line1 - Time

const struct menu menu_L1_Time =
{
FUNCTION(sx_time), // direct function
FUNCTION(mx_time), // sub menu function
FUNCTION(display_time), // display function
FUNCTION(update_time), // new display data
// &menu_L1_Alarm,
&menu_L1_Counter,
};

That's it.


Wednesday, July 18, 2012

Start Here

I got this eZ430-Chronos, the TI watch development tool, about 3 years ago (2009).  It was intended to inspire my son Alvin for embedded system development.  Turn out we both don't have time then.

Start ez430 wiki

Installation

   Install the CD for all the drivers and CCS4 development IDE.  After install CCS, run it and got update notification and proceed to update.

Wireless Update ?

Since 2010, ez430-Chronos got the capability for wireless update.  Our kit need update to do that.

Watch Firmware Update

   Get the eZ430-Chronos Firmware Update Tools to update my watch (915Mhz version).  However, to make it work, the RF Access Point also need to update.

RF Access Point Update

We need another tool CC-DEBUGER and connect it to RF board to update.  Since we don't have CC-DEBUGGER, just forget about it for now.

Working Test

Plug in RF board and run Chronos Control Center.
  1. Start Access Point (Chronos Control Center)
  2. Watch # key to Acc mode.  Press down to start (and stop) sending ACC data.  
  3. X Y Z show the wave of data collected.
  4. Watch # key to PPT mode. Press down to start (and stop) ppt mode.
  5. Watch up, #, * key pressed show on Chronos Control Center)

Compile and Debug

  1. Start CCS
  2. Project->Import Existing CCS Eclipse Project (Select existing project from installed folder)
  3. Select ez-430 as active project.  Target (right click select 915 limited).
  4. Select temperature.c
  5. find "display_temperatture() function.  Add first line : sys.flag.use_metric_units = 1
  6. Build 
  7. Debug
  8. Set break point at display_temperature function
  9. Press * to temperature mode and program stop
  10. continue run and all temperature display is using metric_unit (Celcus).
My display is 30.9C now.  It can't be right.  It is not that hot here.  Maybe battery is low or need calibration now. Battery voltage is 29.1V (according to reading from watch).  From multi-meter, battery is 2.96V(without load).