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

}




2 comments:

  1. #137 struct "" has no field "counter_measurement" timer.c
    #137 struct "" has no field "counter_measurement" ports.c
    #137 struct "" has no field "counter_measurement" main.c
    all three files (timer.c\ports.c\main.c) present " #include "counter.h" "
    Why dont work?

    ReplyDelete
    Replies
    1. Have you done the first modification shown in project.h?

      Delete

Note: Only a member of this blog may post a comment.