Element14 and Freescale's XL-STAR- S08 Development Board.

Authored By: Charles Gantt on 08/29/2011
Review Type: Development Board
Manufacturer: Element14
Product Version: Rev. C

Element14 Newark STAR-XL-S08 Development Board

 

Newark Element14 Logo

 

A few weeks ago we announced on our Facebook Fan Page that we were teaming up with Newark.com to review some of the new products that they carry. After browsing their new products section we noticed a development board branded with Newark's Element14 brand. For those of you who have been living under a rock lately, Element14 is Newark / Farnel's community for DIY electronics enthusiast as well as professional electronic engineers.  Naturally after seeing this board we had to check it out first, so we requested the Element14 STAR-XL-S08 Development Board as our first review item. 

Introduction

 

Element 14 Newark Freescale STAR_XL Development Board

The XL_STAR board allows a user to develop, download and debug up to 64KBytes of 'C' or assembler object code for the MC9S08MM128 MCU using Freescale's CodeWarrior for MCUs V6.3 Special Edition, a professional IDE, for just the cost of the board. Connected to the MM128 MCU, via I2C, is the MMA8451Q a three axes accelerometer. The board is pre-programmed with firmware that uses the MM128 MCU and MMA8451Q accelerometer to drive a star array of LEDs.

The firmware demonstrates the various modes that the accelerometer can be operated in and the results are displayed via the LED array. The complete source code is available to users of the board. Supplied with the board are an installation CD and a USB cable, allowing users to connect the XL_STAR to a Windows PC for immediate development.

The XL_STAR is designed to allow both hardware and software engineers to work in parallel during project development. Once the CodeWarrior development software is loaded the software engineer can initiate either a new project or use the example project provided as a starting point.

For the hardware engineer all the signals from the MM128 MCU are brought out to 0.1” pitch plated through holes down two sides of the PCB. With the addition of 2x 32-way SIL sockets the hardware engineer can place the XL_STAR into a prototyping ‘bread board’ to add external circuitry before committing to prototype PCBs.

Once the prototype PCBs are ready, by removing two resistors from XL_STAR the board can be used, via an OSBDM (Open Source Background Debug Mode) connection, to debug the developers target hardware with the addition of a 6-way ribbon cable.

Applications

  • Portable designs – the MM128 MCU and MMA8451Q accelerometer have very low power modes, measured using jumpers and an ammeter on the XL_STAR board
  • Applications requiring movement detection and interpretation – the MMA8451Q allows free fall, shake, tap, transient and orientation detection for embedded applications
  • Embedded designs requiring USB connectivity – the MM128 MCU also contains a USB OTG on-chip module, which integrates the USB transceiver. Together with a certified free of charge USB OTG stack from Freescale, the XL_STAR offers fast time to market for embedded USB applications

 

Photos

Now that we have the formalities out of the way lets take a closer look at the XL_STAR board.

Element14 XL_Star Development Board. Freescale, Newark

The XL_Star Development Board arrived in a cute little white box.

Element14 Newark Freescale XL_Star Development Board

The cute little whte box includes the XL_Star Board, a USB cable, a coin cell battery holder and a rechargable litium ion coin cell battery.

 

Element14 XL_Star Development Board. Freescale Newark

The XL_STAR is based around a Freescale MM128 MCU which provides 128 KB of Flash, 12 KB of RAM, and a processor speed configurable up to 48 MHz.

 

Element14 XL_STAR Development Board

The XL_STAR board also features a Freescale MMA8451Q accelerometer, which detects both its orientation, through the static pull of gravity, and any movement of the board which causes acceleration or deceleration. Surrounding the accelerometer star pattern made up of 48 orange LEDs which can be addressed individually. The orange LEDs are arranged in an 8-pointed star. Each of the 8 ‘spokes’ of the star is connected to a GPIO pin. For example, LEDs D62, D54, D46, D38, D30 and D22 are all connected to Port D Pin 4 (PTD4) Similarly, each of the 6 concentric ‘circles’ is connected to another GPIO pin. For example the outer circle D56-D63 is connected to Port C Pin 5 (PTC5). Thus to illuminate D62 you would drive PTD4 low and PTC5 high.

 

Element14 XL_Star Development Board

Flipping the board over we find a secondary 8-bit microcontroller (a Freescale MC9S08JM60) which accepts debugging commands from the PC and forms the Open Source Background Debug Mode (OSBDM) interface. The JM60 then controls the main MCU on the top side of the board using its BDM interface. Also located on the back is the coin cell battery holder, (not soldered on in this photo) and the USB port used for programming the MM128 MCU on the front of the board.

 

Element14 XL_STAR Development Board

We had to solder the coin cell battery clip onto the XL-STAR Development board. It only took a few seconds with our Sparkfun 939 Soldering Station set to about 450c. The coin cell battery clip holds a lithium ion battery which is charged by the onboard Freescale MC34673 battery charger.

 

 

Using the Element 14 XL_STAR Preinstalled Demo Code

 

Element14 XL_Star

 

The XL_STAR comes preprogrammed with sample code which utilizes the accelerometer and the LED array. The accelerometer can be utilized to demo five different triggers. You can cycle through each demo by pressing SW3. Green LEDs D6-D10 will light up depending on which demo is selected. The accelerometer sensitivity can be changed by pressing button SW1, labelled G sel. This selects which multiple of normal gravity corresponds to a full-scale accelerometer reading. The options are 2g, 4g and 8g as indicated by the green LEDs D3-D5. Note that changing the sensitivity only affects the 'Orient' demo, because the other accelerometer functions used here don't depend on the sensitivity setting.

  1. Orient: Try tipping the board and watch the orange LEDs indicate the direction and magnitude of tip
  2. Shake: Try flicking the board in a direction, either up or down, left or right, or forward or back. You need to use a sharp flick, like striking a ball in a game of table tennis. The orange LEDs will point in the direction of the initial flick, re-setting after a couple of seconds.  If the initial flick is not fast enough, the accelerometer may instead detect the rebound event as your hand stops the board’s movement. In this case the reverse direction will be illuminated.
  3. Tap: Hold the board loosely and tap it once with your finger; the LEDs will show a 'pulse' pattern. Tap twice in quick succession (double tap) to get a different animated pattern.
  4. Freefall: Drop the board from the height of a few centimetres or more. The freefall event will be detected and displayed on the LEDs for a couple of seconds.
  5. Transient: The orange LEDs will all illuminate if the board is moving (strictly, if it is accelerating or decelerating) and turns off after a short while if the board is stationary.

If you are interested in the sample code we have included it below.

Code:
/**************************************************************************
* MAIN.C  
* Top level routines for XL_STAR board accelerometer demo
*
* Copyright (c) 2011 Freescale Semiconductor
* All Rights Reserved
*
*************************************************************************** 
*
* THIS SOFTWARE IS PROVIDED BY FREESCALE "AS IS" AND ANY EXPRESSED 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 FREESCALE OR ITS 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.
*
**************************************************************************
* Comments:
*       Developed by MicroAPL Ltd on behalf of Freescale Semiconductor
*
* Revision History
*       06/01/11 SDM           Initial version
*       14/01/11 SDM (1.0.0)   First release
*
* HOW TO USE THE DEMO PROGRAM
* ---------------------------
* This program is designed to run on Freescale's XL_STAR board, and
* shows how to use the MMA8451Q accelerometer.
*
* When first powered on, the board will go into an 'orientation' demo mode.
* Try picking the board up and tipping it in different directions. You should
* see that the orange LEDs illuminate to indicate the direction of the tip
* and its magnitude.
*
* You can select between five different demos by repeatedly pressing button SW3,
* labelled 'Chan'. The current demo mode will be indicated by the green LEDs
* D6-D10. The available demo modes are:
*
*     Orient    - Try tipping the board and watch the orange LEDs indicate
*                 the direction and magnitude of tip
*
*     Shake     - Try flicking the board in a direction, either up or down,
*                 left or right, or forward or back, like striking a ball in
*                 a game of table tennis. The orange LEDs will point in 
*                 the direction of the initial flick, re-setting after a 
*                 couple of seconds.
*
*     Tap       - Hold the board loosely and tap it once with your finger;
*                 the LEDs will show a 'pulse' pattern.
*                 Tap twice in quick succession (double tap) to get a
*                 different animated pattern.
*
*     Freefall  - Drop the board from the height of a few centimetres or more. 
*                 The freefall event will be detected and displayed on the LEDs
*                 for a couple of seconds.
*
*     Transient - The orange LEDs will all illuminate if the board is moving,
*                 (strictly, if it is accelerating or decelerating) and turn  
*                 off after a short while if you stand still. 
*
* The accelerometer sensitivity can be changed by pressing button SW1, labelled 
* 'G sel'. This selects which multiple of normal gravity corresponds to a
* full-scale accelerometer reading. The options are 2g, 4g and 8g as indicated
* by the green LEDs D3-D5.
*
* The accelerometer can return 8-bit or 14-bit data to the main CPU. Applications 
* requiring more accuracy can use 14-bit data; otherwise it's faster to read 8-bit 
* data. You can select between the two options by pressing the SW2 button, labelled
* 'DataW'. The current selection is shown on the green LEDs D1-D2.
*
* Note that changing the sensitivity only affects the 'Orient' demo, because
* the other accelerometer functions used here don't depend on the sensitivity setting.
*
* Changing between 8-bit and 14-bit data also only affects the 'Orient' demo, but
* although the data width setting is honoured you probably won't notice the
* difference.
*
* When the accelerometer doesn't detect any interesting events for around 20 seconds,
* software will put the board into a power-saving mode. So that you can see this  
* happening, an 'X' pattern is displayed briefly before almost all the LEDs turn off.
*
* To wake the board up, just pick it. A '+' pattern is displayed briefly to indicate 
* that the board is awake again, and the current demo mode is then resumed.
*
*
* SOME NOTES ON PROGRAM STRUCTURE
* -------------------------------
* The following notes might be helpful for getting an understanding of the demo code.
*
* (1) On power-on reset, execution starts at label '_Startup' in Start08.c. This file
*     is common to many CodeWarrior HCS08 programs. Its main purpose is to set up the
*     runtime environment of the C program (stack, initialised data, etc). 
*
* (2) Control then transfers to our 'main' routine below. This sets up the hardware
*     and then enters an endless loop.
*
*     Within the loop, everything is interrupt driven. The microprocessor executes
*     the WAIT instruction in order to sleep when there's no new information from 
*     the accelerometer. It is woken from sleep by an interrupt, either from the
*     accelerometer or caused by a button press. The interrupt service routines
*     are 'accelerometer_isr' and 'button_press_isr.'
*
* (3) The accelerometer is configured differently each time a new demo mode is
*     selected. It would be possible to detect multiple events at once - e.g. single 
*     and double taps, orientation changes *and* freefall - by careful configuration.
*     To make it easier to understand the code, each demo mode only configures one
*     type of event.
*
* (4) After some seconds of inactivity the accelerometer will transition from
*     Wake to Sleep mode, and this will be indicated by an interrupt. The software
*     transfers control to the routine 'wait_for_movement' which reconfigures the
*     accelerometer to detect any movement and then puts both the accelerometer and
*     the board into low power modes.
* 
* (5) There are a number of routines with names like 'leds_circle' which are used
*     to display different patterns on the orange LEDs. These are all straightforward
*     except 'leds_show_tilt'. For added interest this uses the fact that the GPIO
*     pins driving the LEDs can also be reconfigured as outputs of the Timer/Pulse 
*     Width Modulator modules (TPMs). By driving the pins with a pulse-width modulated 
*     signal the brightness of the LEDs can be controlled.
*
* (6) This demo program is unusual in that it makes use of the ANSI C 'double' floating  
*     point data type, which is not typically the case for embedded software.
*     Floating point code is needed here to calculate tilt angles in the 'Orient' 
*     demo, and requires the use of a version of the ANSI library which includes maths
*     routines like 'cos' and 'atan2'. 
*
*     The library which includes floating point support is called 'ansifs.lib'. 
*     For most embedded applications you can make use of 'ansiis.lib', a simpler library 
*     which leads to slightly smaller code sizes. See the compiler documentation for 
*     more details.
* 
*/

#include           
#include "derivative.h"     
#include 
#include "mma845x.h"
#include "iic.h"

/* Private function prototypes */
static void initialise_hardware (void);
static void initialise_accelerometer (void);
static void read_accelerometer_info (void); 
static void update_green_leds (Bool active);
static void configure_LEDs_for_PWM (void);
static void configure_LEDs_for_GPIO (void);
static void millisecond_delay (int delay);
static void leds_pulse (int delay);
static void leds_rotate (int count, int delay);
static void leds_show_tilt (byte *values);
static void leds_circle (int circle_number);
static void leds_cross (void);
static void leds_plus (void);
static void leds_all_on (void);
static void leds_all_off (void);
static void leds_show_shake (byte src);
static void wait_for_movement (void);

/* Accelerometer I2C slave address */                                   
#define SlaveAddressIIC       (0x1d << 1)      /* Address = 1D in top 7 bits of byte */

/* Demo modes */
enum {
    DEMO_ORIENTATION,
    DEMO_SHAKE,
    DEMO_TAP,
    DEMO_FREEFALL,
    DEMO_TRANSIENT,
    NUM_DEMOS
};

/* Accelerometer can return 8-bit or 14-bit samples */
enum {
    READ_MODE_8BITS,
    READ_MODE_14BITS
};

/* Maximum value to use for PWM in PWM-controlled LED modes */
#define MAX_PWM_VALUE   500

/* 100ms debounce delay used for SW1 - SW3 push buttons */
#define DEBOUNCE_DELAY  100                      

/* Global variables */
static byte CurrentDemo;                       /* Current demo, e.g. DEMO_FREEFALL */
static byte NewDemo;                           /* New demo requested */

static byte CurrentRange;                      /* Current range setting, e.g. FULL_SCALE_2G */
static byte NewRange;                          /* New range setting requested */

static byte CurrentReadMode;                   /* Current read size, e.g. READ_MODE_14BITS */
static byte NewReadMode;                       /* New read size requested */

static byte last_int_source_reg;               /* Last value of INT_SOURCE_REG as read by 
                                                  read_accelerometer_info() 
                                               */ 
static byte last_sysmod_reg;                   /* Last value of SYSMOD_REG */
static byte last_f_status_reg;                 /* Last value of F_STATUS_REG */
static byte last_transient_src_reg;            /* Last value of TRANSIENT_SRC_REG */
static byte last_pl_status_reg;                /* Last value of PL_STATUS_REG */
static byte last_pulse_src_reg;                /* Last value of PULSE_SRC_REG */
static byte last_ff_mt_src_1_reg;              /* Last value of FF_MT_SRC_1_REG */
static byte last_xyz_values [6];               /* Last values of X, Y and Z */ 

/************************************************************************
*       main - Program main entry point                                 *
*************************************************************************/
void main(void) 
{
    int     count, ring;
    
    /* Initialise system clocks, GPIO, etc */           
    initialise_hardware ();
  
    /* Delay about 2 seconds to indicate that board is OK */
    leds_all_on ();
    millisecond_delay (2000);
      
    /* Show that we're live */    
    leds_pulse (60);

    /* Force main loop to initially select DEMO_ORIENTATION, 14-bit accuracy, 2g sensitivity */
    NewDemo = DEMO_ORIENTATION;
    NewReadMode = READ_MODE_14BITS;
    NewRange = FULL_SCALE_2G;
    
    CurrentDemo = ~NewDemo;
    
    /* Loop forever to run demos */ 
    for (;;) {
        /* ---- Check for button press events ---- */
    
        /* Did user press one of the buttons SW1 - SW3 ?
                Requested new sensitivity by pressing "g Sel" button SW1 ?
                Requested new read mode by pressing "DataW" button SW2 ?  
                Requested new demo mode by pressing "Chan" button SW3 ? 
        */
        if (NewRange != CurrentRange || NewReadMode != CurrentReadMode || NewDemo != CurrentDemo) {
            
             /* Yes. Orange LEDs all off */
            leds_all_off ();

            /* Short delay to debounce key press */
            millisecond_delay (DEBOUNCE_DELAY);    
           
            /* Reconfigure accelerometer */ 
            CurrentRange = NewRange;
            CurrentReadMode = NewReadMode; 
            CurrentDemo = NewDemo;
            initialise_accelerometer ();
            
            /* Update the green indicator LEDs to show current demo mode, sensitivity, etc */
            update_green_leds (TRUE);
        }
        
        /* ---- Check for accelerometer event(s) ---- */
        read_accelerometer_info (); 
        
        /* ---- Handle any transition from accelerometer Wake -> Sleep mode ---- */
        if (last_int_source_reg & SRC_ASLP_MASK) {
            if ((last_sysmod_reg & SYSMOD_MASK) == 0x02) {
                /* Accelerometer has gone into sleep mode because no activity has been detected
                   in the last 20 seconds. We put the main CPU to sleep too, to save maximum power.
                */
                wait_for_movement ();               /*  Routine doesn't return until 
                                                        accelerometer is awake again */
                                                        
                /* Force reconfiguration now we're awake again */ 
                CurrentDemo = ~NewDemo;
                continue;
            }
        }
       
        /* ---- Update LEDs if accelerometer event occurred ---- */
        switch (CurrentDemo) {
        case DEMO_ORIENTATION:
            /* New XYZ tilt data ready? */
            if (last_int_source_reg & SRC_DRDY_MASK) {            

                /* Temporarily disable interrupts so 'last_xyz_values' data is not overwritten */
                DisableInterrupts;
                
                /* Make a copy of the data, unpacked into 14-bit format if necessary */
                if (CurrentReadMode == READ_MODE_14BITS) {
                    /* Already read 14-bit data into last_xyz_values[] */
                }
                else {
                    /* Unpack 8-bit data into 14 bit format */
                    last_xyz_values [5] = 0;
                    last_xyz_values [4] = last_xyz_values [2];
                    last_xyz_values [3] = 0;
                    last_xyz_values [2] = last_xyz_values [1];
                    last_xyz_values [1] = 0;
                } 
                EnableInterrupts;   
                leds_show_tilt (&last_xyz_values[0]);
            }
            break;
            
        case DEMO_SHAKE:
            /* Shake detected ? */
            if (last_int_source_reg & SRC_TRANS_MASK) {            
                /* Indicate that we detected shake */
                leds_show_shake (last_transient_src_reg);

                /* Throw away any stale shake event, typically caused in opposite
                   direction to initial shake, as hand slows the board down
                */
                (void) IIC_RegRead(SlaveAddressIIC, TRANSIENT_SRC_REG);
            }
            break;
            
        case DEMO_TAP:
            /* Tap event detected ? */
            if (last_int_source_reg & SRC_PULSE_MASK) {
                /* Single or double tap detected ? */
                if (last_pulse_src_reg & PDPE_MASK)
                    leds_rotate (2, 50);         /* Double tap */
                else
                    leds_pulse (60);             /* Single tap */
            }
            break;
            
        case DEMO_FREEFALL:
            /* Freefall detected ? */
            if (last_int_source_reg & SRC_FF_MT_1_MASK) {            
                /* Indicate that we detected freefall by showing decreasing circle pattern on LEDs */
                for (count = 0; count < 5; count++) {
                    for (ring = 5; ring >= 0; ring--) {
                        leds_circle (ring);
                        millisecond_delay (60);
                    }
                }
                leds_all_off ();
                
                /* Throw away any stale freefall event, typically caused because board
                   fell some distance
                */
                (void) IIC_RegRead(SlaveAddressIIC, FF_MT_SRC_1_REG);
            }
            break;
            
        case DEMO_TRANSIENT:
            /* Motion detected ? */
            if (last_int_source_reg & SRC_TRANS_MASK) {            
                /* Indicate that we detected motion by animating the LEDs for a few seconds */
                leds_rotate (2, 50);
            }
            break;
            
        default:
            break;
        }

        /* Wait for interrupt signalling accelerometer event or button press,
           unless another event has already occurred
        */
        DisableInterrupts;
        last_int_source_reg = IIC_RegRead(SlaveAddressIIC, INT_SOURCE_REG);   
        if (last_int_source_reg == 0 && NewRange == CurrentRange && NewReadMode == CurrentReadMode && NewDemo == CurrentDemo)  
            asm "wait";
        EnableInterrupts;

    } /* Loop forever */


    /* Program should never reach here ! */
}

/************************************************************************
*       initialise_hardware - Initialise clock, GPIO, etc               *
*************************************************************************
; This routine initialises the main microprocessor - everything we need
; except the accelerometer which is done separately
*/
static void initialise_hardware (void) 
{
    /* Configure write-once System Options Register (SOPT)
            STOPE=1   : Stop Mode enabled 
            COPE=0    : Disable watchdog     
            BKGDPE=1  : Background Debug Mode Pin Enable
    */
    SOPT1 = 0x23;                   
    
    /* Configure SIM Clock Set and Select Register (SIMCO)
            CS=0      : Don't drive CLKOUT pin 
    */
    SIMCO &= ~0x07;                 
  
    /* Configure System Power Management Status and Control 1 Register (SPMSC1)  
            LVDIE=0   : No interrupt on low voltage detected
            LVDRE=1   : Generate reset on low voltage detected instead [write-once bit]
            LVDSE=1   : Low-voltage detect is enabled in STOP modes
            LVDE=1    : Low-voltage detect is enabled [write-once bit]
    */
    SPMSC1 = 0x1c;                                       

    /* Configure System Power Management Status and Control 2 Register (SPMSC2)  
            PPDE=1    : Partial power down enabled [write-once bit]
            PPDC=0    : STOP3 low power mode enabled
    */
    SPMSC2 = 0x02;                                      

    /* Configure System Power Management Status and Control 3 Register (SPMSC3)  
            LVDV=0    : Low trip point selected for low-voltage detect 
            LVWV=0    : Low trip point selected for low-voltage warning 
            LVWIE=0   : Interrupt on low voltage warning is disabled
    */
    SPMSC3 = 0x00;    
                 
    /* Currently using internal clock. Any trim value stored in flash ? */   
    if (*(unsigned char *) 0xffaf != 0xff) {
        /* Yes. Initialise trim */  
        MCGTRM = * (unsigned char *) 0xffaf;    /* Factory MCGTRM value stored in flash at address 0xFFAF */
        MCGSC = * (unsigned char *) 0xffae;     /* Factory FTRIM value stored in flash at address 0xFFAE */
    }

    /* System clock initialization... 
       We configure the system to use the clock derived from the 16MHz external crystal 
       connected to XOSC2.
       
       System is placed into FEE mode, so
       
            f_MCGOUT = (f_EXT * F) / (R * B)
                     = (16MHz * 512) / (512 * 2) = 8MHz processor clock
                     
            f_BUS = f_MCGOUT / 2 = 4MHz bus clock
    */

    /* Multipurpose Clock Generator Control Register 2 (MCGC2)
            BDIV=01   : Bus clock = selected clock / 2
            RANGE=1
            HGO=1
            LP=0
            EREFS=1   : External clock source requested
            ERCLKEN=0 : External reference clock to ADC inactive
            EREFSTEN=0: External clock disabled in Stop mode
            
        NB: Write to MCGC2 before MCGC1. This sets the multiplier before 
            enabling the FLL
    */
    MCGC2 = 0x74;                          
  
    /* Multipurpose Clock Generator Control Register 3 (MCGC3)
            LOLIE=0
            PLLS=0
            CME=0
            DIV32=1
            VDIV=0001 
    */
    MCGC3 = 0x11;                           
  
    /* Multipurpose Clock Generator Control Register 1 (MCGC1)
            CLKS=00   : Output of FLL is selected as system clock source
            RDIV=100  : Divide-by-512 since RANGE=1 and DIV32=1
            IREFS=0   : External reference clock selected
            IRCLKEN=0 : MCGIRCLK inactive
            IREFSTEN=0   
    */
    MCGC1 = 0x20;                           
  
    /* Wait until external reference is selected */
    while (MCGSC_IREFST)
        ;
                        
    /* Multipurpose Clock Generator Control Register 4 (MCGC4)
        DMX32=0      
        DRST_DRS=0  (Together these give F = 512)
    */
    MCGC4 = 0x00;                           
  
    /* Wait until FLL is locked */
    while(!MCGSC_LOCK)                      
        ;
  
    /* Wait until FLL clock is selected as a bus clock reference */
    while (MCGSC & 0x0c)
        ;
        
    /* Enable clocks to all peripherals...
       Note: To conserve power, clocks to unused peripherals should be disabled.
             However, since this software is likely to be used by programmers
             new to the S08, we enable all the clocks here to make it easier
             for the programmer to get things working
    */
    
    /* SCGC1: CMT=1,TPM2=1,TPM1=1,ADC=1,DAC=1,IIC=1,SCI2=1,SCI1=1 */
    SCGC1 = 0xff;                                      
    
    /* SCGC2: USB=1,PDB=1,IRQ=1,KBI=1,PRACMP=1,TOD=1,SPI2=1,SPI1=1 */
    SCGC2 = 0xff;                                      
    
    /* SCGC3: VREF=1,CRC=1,FLS1=1,TRIAMP2=1,TRIAMP1=1,GPOA2=1,GPOA1=1 */
    SCGC3 = 0xff;                                      
  
    /* Disable TPMs */
    TPM1SC = 0;
    TPM2SC = 0;                    
  
    /* Port A GPIO 

        A7  : Output connected to green 'Orientation' LED, initially 1 [off]
        A6  : Output connected to green 'Transient' LED, initially 1 [off]
        A5  : Output connected to green 'Freefall' LED, initially 1 [off]
        A4  : Output connected to green '14-bit data' LED , initially 1 [off]
        A3  : Input connected to 'Channel Select' button (KBI1P2)
        A2  : Input connected to 'Data 8/14bit' button   (KBI1P1)
        A1  : Input connected to 'g-Range Select' button (KBI1P0)
        A0  : Output connected to green '8-bit data' LED, initially 1 [off]
    */
    PTAPE = ~0xf1;                  /* Pull-ups on inputs */                                               
    PTAIFE = ~0xf1;                 /* Low-pass filter on inputs */                           
    PTASE = 0xf1;                                      
    PTADS = 0xf1;                   
    PTAD = 0xf1;                   
    PTADD = 0xf1;                  

    /* Port B GPIO 
    
        B7  : Not used
        B6  : Not used
        B5  : XTAL2
        B4  : EXTAL2
        B3  : XTAL1
        B2  : EXTAL1
        B1  : Output connected to green 'Tap' LED, initially 1 [off]
        B0  : Output connected to green 'Shake' LED, initially 1 [off]
    */
    PTBPE = ~0x03;                  /* Pull-ups on inputs and unused pins */
    PTBIFE = ~0x03;                 /* Low-pass filter on inputs */
    PTBSE = 0x03;                                      
    PTBDS = 0x03;                                             
    PTBD = 0x03;            
    PTBDD = 0x03;           
                          
    /* Port C GPIO  
    
        C7  : Not used
        C6  : Not used
        C5  : Output connected to orange LED ring, initially 0 [off]
        C4  : Output connected to orange LED ring, initially 0 [off]
        C3  : Output connected to orange LED ring, initially 0 [off]
        C2  : Output connected to orange LED ring, initially 0 [off]
        C1  : Output connected to orange LED ring, initially 0 [off]
        C0  : Output connected to orange LED ring, initially 0 [off]
    */
    PTCPE = ~0x3f;                  /* Pull-ups on inputs and unused pins */ 
    PTCIFE = ~0x3f;                 /* Low-pass filter on inputs  */
    PTCSE = 0x3f;                                      
    PTCDS = 0x3f;                                                  
    PTCD = 0x00;                 
    PTCDD = 0x3f;                
  
    /* Port D GPIO  
    
        D7  : RX1
        D6  : TX1
        D5  : Output connected to orange LED spoke, initially 1 [off]
        D4  : Output connected to orange LED spoke, initially 1 [off]
        D3  : Output connected to orange LED spoke, initially 1 [off]
        D2  : Output connected to orange LED spoke, initially 1 [off]
        D1  : MCU_RESET
        D0  : MCU_BKGD
    */
    PTDIFE = 0x82;                  /* Low pass filter on RX1 and MCU_RESET */
    PTDSE = 0x3c;                                      
    PTDDS = 0x3c;                                            
    PTDD = 0x3c;               
    PTDDD = 0x3c;           
                            
    /* Port E GPIO  

        E7  : Output connected to orange LED spoke, initially 1 [off]
        E6  : MCU_RXD
        E5  : MCU_TXD
        E4  : Not used
        E3  : Not used
        E2  : Not used
        E1  : MMA_INT1 (KBI2P4 input)
        E0  : MMA_INT2 (KBI2P3 input)
    */
    PTEPE = ~0x80;                  /* Pull-ups on inputs and unused pins */ 
    PTEIFE = ~0x80;                 /* Low pass filter on inputs */
    PTESE = 0x80;                                      
    PTEDS = 0x80;           
    PTED = 0x80;               
    PTEDD = 0x80;           

    /* Port F GPIO 
     
        F7  : Output connected to green '4g' LED, initially 1 [off]
        F6  : Output connected to green '2g' LED, initially 1 [off]
        F5  : Not used
        F4  : MMA_SDA
        F3  : MMA_SCL
        F2  : Output connected to orange LED spoke, initially 1 [off]
        F1  : Output connected to orange LED spoke, initially 1 [off]
        F0  : Output connected to orange LED spoke, initially 1 [off]
    */
    PTFPE = ~0xc7;                  /* Pull-ups on inputs and unused pins */      
    PTFIFE = ~0xc7;                 /* Low pass filter on inputs */     
    PTFSE = 0xc7;                                      
    PTFDS = 0xc7;                
    PTFD = 0xc7;                 
    PTFDD = 0xc7;                

    /* Port G GPIO 
    
        G7-1: Not bonded out
        G0  : Output connected to green '8g' LED, initially 1 [off]
    
    
    */  
    PTGPE = ~0x01;                  /* Pull-ups on unused pins */                      
    PTGIFE = ~0x01;                 /* Low pass filter on inputs */                     
    PTGSE = 0x01;                                      
    PTGDS = 0x01;             
    PTGD = 0x01;              
    PTGDD = 0x01;             


    /* Initialize Inter-Integrated Circuit (IIC) used to talk to accelerometer */    
    SOPT3_IICPS = 1;                /* PTF3/PTF4 used for IIC comms */
    
    IICF = 0x02;                    /* IIC Frequency Divider Register
                                            MULT = 0b00 : mul = 1
                                            ICR  = 0b000010 : scl = 24
                                      
                                       IIC Frequency = Bus clock x mul / scl
                                                     = 4 MHz x 1 / 24
                                                     = 167kHz
                                    */
    
    IICC1 = 0x80;                   /* IIC Control Register 1 : Enable IIC module
                                         RSTA     =0  : Not generating repeat start
                                    */

    /* Enable keyboard interrupt from push buttons
       labelled 'g-Range Select', 'Data 8/14bit' and 'Channel Select'
    */
    KBI1SC = 0;                     /* Mask keyboard interrupts */
    KBI1ES = 0;                     /* Detect falling edge */
    PTAPE_PTAPE1 = 1;               /* Enable pull up on KBI1P0 (Port A pin 1) */
    PTAPE_PTAPE2 = 1;               /* Enable pull up on KBI1P1 (Port A pin 2) */
    PTAPE_PTAPE3 = 1;               /* Enable pull up on KBI1P2 (Port A pin 3) */

    KBI1PE = 0x07;                  /* Enable the KBI pins */
    KBI1SC_KB1ACK = 1;              /* Clear any false interrupt */
    KBI1SC_KB1IE = 1;               /* Enable keyboard interrupts */


   /* Configure interrupts from accelerometer:
          MMA_INT1 (KBI2P4 input, Port E pin 1)
          MMA_INT2 (KBI2P3 input, Port E pin 0)
    */
    KBI2SC = 0;                     /* Mask keyboard interrupts */
    KBI2ES = 0;                     /* Detect falling edge */
    PTEPE_PTEPE1 = 1;               /* Enable pull up on KBI2P4 (Port E pin 1) */
    PTEPE_PTEPE0 = 1;               /* Enable pull up on KBI2P3 (Port E pin 0) */

    KBI2PE = 0x18;                  /* Enable the KBI pins */
    KBI2SC_KB2ACK = 1;              /* Clear any false interrupt */
    KBI2SC_KB2IE = 0;               /* Disable MMA_INTx interrupts initially */

}

/************************************************************************
*       initialise_accelerometer - Perform demo-specific initialisation *
*************************************************************************
; This routine configures the accelerometer, the TPMs and the LEDs in
; preparation for the selected demo mode
*/
static void initialise_accelerometer (void)  
{ 
    byte    n, int_mask;
   
    /* Disable MMA_INTx interrupts so there's no danger of a spurious interrupt
       during reconfiguration 
    */               
    KBI2SC_KB2IE = 0;               

    /* Put MMA845xQ into Standby Mode */
    IIC_RegWrite(SlaveAddressIIC, CTRL_REG1, (IIC_RegRead(SlaveAddressIIC, CTRL_REG1) & ~ACTIVE_MASK));
    
    /* Configure sensor for:
         Sleep Mode Poll Rate of 1.56Hz (640ms) for maximum power saving 
         System Output Data Rate (ODR) of 200Hz (5ms)
         User-specified read mode (8-bit or 14-bit data)
    */
    n = ASLP_RATE_640MS + DATA_RATE_5MS;          
    if (CurrentReadMode == READ_MODE_8BITS)
        n |= FREAD_MASK;
    IIC_RegWrite(SlaveAddressIIC, CTRL_REG1, n);  
  
    /* Select user-specified sensitivity, 2g/4g/8g */
    IIC_RegWrite(SlaveAddressIIC, XYZ_DATA_CFG_REG, CurrentRange);
    
    /* Configure Sleep/Wake modes 
        SMODS1:0   : 1 1    Low power mode when asleep
        SLPE       : 1      Sleep enabled
        MODS1:0    : 0 0    Normal mode when awake
    */
    IIC_RegWrite(SlaveAddressIIC, CTRL_REG2, 0x1c);          
    IIC_RegWrite(SlaveAddressIIC, ASLP_COUNT_REG, 62);      /* Sleep after 20 seconds of inactivity */         

    /* Disable the FIFO */
    IIC_RegWrite(SlaveAddressIIC, F_SETUP_REG, 0x00);

    switch (CurrentDemo) {
    case DEMO_TRANSIENT:
    case DEMO_SHAKE:
    case DEMO_ORIENTATION:
        /* In all three of these demo modes we configure the accelerometer to detect
           movement:
           
                DEMO_TRANSIENT   - We configure the accelerometer to detect small movements
                                   of > 0.063g                   
                                   
                DEMO_SHAKE       - We configure the accelerometer to detect movements
                                   of > 0.5g
                                                      
                DEMO_ORIENTATION - We don't care about the movement data itself, but
                                   we use transient detection so that the accelerometer
                                   can tell us when the board isn't being used. When
                                   it transitions to Sleep mode, we can put the main
                                   processor to sleep too.
                                   
          By using the high-pass filter we can remove the constant effect of gravity,
           and only detect changes in acceleration. See Application Note AN4071.
        */

        /* ELE = 1     : Event latch enabled 
           ZTEFE = 1   : Raise event on any of Z, Y, X axes
           YTEFE = 1
           XTEFE = 1
           HBF_BYP = 0 : High-pass filter enabled
        */
        IIC_RegWrite(SlaveAddressIIC, TRANSIENT_CFG_REG, 0x1e);
        
        /* Set High-pass filter cut-off frequency for best sensitivity */
        IIC_RegWrite(SlaveAddressIIC, HP_FILTER_CUTOFF_REG, 0x03);   

        if (CurrentDemo == DEMO_SHAKE) {
            /* Transient is indicated when acceleration on one of the axes
               is above threshold 8 x 0.063g = 0.5g 
             */   
             IIC_RegWrite(SlaveAddressIIC, TRANSIENT_THS_REG, 8);
        } 
        else {
            /* Transient is indicated when acceleration on one of the axes
               is above threshold 1 x 0.063g - i.e. a small movement 
            */   
            IIC_RegWrite(SlaveAddressIIC, TRANSIENT_THS_REG, 1);
        }

        /* Internal debounce counter. For an ODR of 200Hz in Normal mode,
           motion is indicated after 5 x 1 = 5ms. 
        */
        IIC_RegWrite(SlaveAddressIIC, TRANSIENT_COUNT_REG, 1);

        /* Interrupt signalled on INT1 when transient detected */
        int_mask = INT_EN_TRANS_MASK;

        if (CurrentDemo == DEMO_ORIENTATION) {
            /* Interrupt also signalled when new data is ready */
             int_mask |= INT_EN_DRDY_MASK;
         
            /* Set up TPMs to produce edge-aligned PWM signals */
            configure_LEDs_for_PWM ();    
        }
        break;
        
    case DEMO_TAP:
        /* Configure the accelerometer to detect single and double taps... 
           (See Application Note AN4072)
        */
 
        /* Z configured for Single Tap and Double Tap with Latch enabled */
        IIC_RegWrite(SlaveAddressIIC, PULSE_CFG_REG, 0x70);    
        
        /* Enable low-pass filter */
        IIC_RegWrite(SlaveAddressIIC, HP_FILTER_CUTOFF_REG, PULSE_LPF_EN_MASK);   

        /* Set Z Threshold to 16 x 0.063g = 1.0g 
           
           Note: In a more sophisticated application we could dynamically
           track the orientation and configure X, Y Z with changing thresholds
        */
        IIC_RegWrite(SlaveAddressIIC, PULSE_THSZ_REG, 16);  

        /* Set the Pulse Time Limit for 30 ms at 200 Hz ODR in Normal Mode with the LPF Enabled  
           30 ms/5 ms = 6 counts
        */
        IIC_RegWrite(SlaveAddressIIC, PULSE_TMLT_REG, 6);
        
        /* Set the Pulse Latency Timer to 100 ms, 200 Hz ODR Normal Mode, LPF Enabled  
           100 ms/10 ms = 10 counts
        */
        IIC_RegWrite(SlaveAddressIIC, PULSE_LTCY_REG, 10);
        
        /* Set the Pulse window to 400 ms, 200 Hz Normal Mode, LPF Enabled
           400 ms/10 ms = 50 counts
        */
        IIC_RegWrite(SlaveAddressIIC, PULSE_WIND_REG, 50);

        /* Interrupt signalled on INT1 when pulse detected */
        int_mask = INT_EN_PULSE_MASK;
        break;
        
    case DEMO_FREEFALL:
        /* Configure accelerometer for linear freefall detection... 
           [Note that the accelerometer can also detect tumbling, as
            described in Application Note AN4070]
        */
        
        /* ELE = 1   : Event latch enabled 
           OAE = 0   : Detect Freefall
           ZEFE = 1  : Raise event on any of Z, Y, X axes
           YEFE = 1
           XEFE = 1
        */
        IIC_RegWrite(SlaveAddressIIC, FF_MT_CFG_1_REG, 0xb8);   
        
        /* Freefall is indicated when acceleration on all three axes
           falls below threshold 3 x 0.063g = 0.19g 
        */   
        IIC_RegWrite(SlaveAddressIIC, FT_MT_THS_1_REG, 3);
        
        /* Internal debounce counter. For an ODR of 200Hz in Normal mode,
           freefall is indicated after 5 x 20 = 100ms of falling - a drop
           of a few centimetres - See Application Note AN4070
        */
        IIC_RegWrite(SlaveAddressIIC, FF_MT_COUNT_1_REG, 20);

        /* Interrupt signalled on INT1 when Freefall detected */
        int_mask = INT_EN_FF_MT_1_MASK;
        break;
    }
   
    /* Configure interrupts */
    int_mask |= INT_EN_ASLP_MASK;                           /* Also generate interrupt on 
                                                               Sleep <--> Wake transition 
                                                            */
    IIC_RegWrite(SlaveAddressIIC, CTRL_REG4, int_mask);
    IIC_RegWrite(SlaveAddressIIC, CTRL_REG5, 0xfd);         /* All interrupts mapped to MMA_INT1 */
    IIC_RegWrite(SlaveAddressIIC, CTRL_REG3, 0x78);         /* Active-low interrupts, all functions 
                                                               wake system 
                                                            */  

    /* Throw away any stale interrupt info */
    last_int_source_reg = 0;
    
    /* Put MMA845xQ into Active Mode */
    IIC_RegWrite(SlaveAddressIIC, CTRL_REG1, (IIC_RegRead(SlaveAddressIIC, CTRL_REG1) | ACTIVE_MASK));

    /* Enable MMA_INTx interrupts */
    KBI2SC_KB2IE = 1;               
}

/************************************************************************
*       read_accelerometer_info - Read any state change info            *
*************************************************************************
; This routine reads the accelerometer's INT_SOURCE_REG to check whether
; any events have occured. It then reads any event-specific data in order
; to clear the interrupts.
;
; Values are returned in the following global variables:
;
;       last_int_source_reg       - INT_SOURCE_REG
;       last_sysmod_reg           - SYSMOD_REG                     
;       last_f_status_reg         - F_STATUS_REG                    
;       last_transient_src_reg    - TRANSIENT_SRC_REG               
;       last_pl_status_reg        - PL_STATUS_REG                   
;       last_pulse_src_reg        - PULSE_SRC_REG                   
;       last_ff_mt_src_1_reg      - FF_MT_SRC_1_REG                 
;       last_xyz_values           - OUT_X_MSB_REG, etc          
;
; See the comments in the routine 'accelerometer_isr' for more information 
*/
static void read_accelerometer_info (void)
{
    /* Which source caused an interrupt, if any ? */
    last_int_source_reg = IIC_RegRead(SlaveAddressIIC, INT_SOURCE_REG);

    /* Sleep <--> Wake transition detected ? */
    if (last_int_source_reg & SRC_ASLP_MASK) {            
        /* Yes - Clear the event */
        last_sysmod_reg = IIC_RegRead(SlaveAddressIIC, SYSMOD_REG);
    } 
    
    /* FIFO event detected ? */
    if (last_int_source_reg & SRC_FIFO_MASK) {            
        /* Yes - Clear the event 
           Note that our demos don't use this event, so no further processing is required
        */
        last_f_status_reg = IIC_RegRead(SlaveAddressIIC, F_STATUS_REG);
    } 
    
    /* Transient detected ? */
    if (last_int_source_reg & SRC_TRANS_MASK) {            
        /* Yes - Clear the transient event */
        last_transient_src_reg = IIC_RegRead(SlaveAddressIIC, TRANSIENT_SRC_REG);
    } 
    
    /* Landscape/portrait orientation change detected ? */
    if (last_int_source_reg & SRC_LNDPRT_MASK) {            
        /* Yes - Clear the event 
           Note that our demos don't use this event, so no further processing is required
        */
        last_pl_status_reg = IIC_RegRead(SlaveAddressIIC, PL_STATUS_REG);
    } 
    
    /* Tap/pulse event detected ? */
    if (last_int_source_reg & SRC_PULSE_MASK) {
        /* Yes - Clear the pulse event */
        last_pulse_src_reg = IIC_RegRead(SlaveAddressIIC, PULSE_SRC_REG);
    }
    
    /* Freefall detected ? */
    if (last_int_source_reg & SRC_FF_MT_1_MASK) {            
        /* Yes - Clear the freefall event */
        last_ff_mt_src_1_reg = IIC_RegRead(SlaveAddressIIC, FF_MT_SRC_1_REG);
    }             

    /* Freefall detected ? */
    if (last_int_source_reg & SRC_DRDY_MASK) {            
        /* Yes - read the XYZ data to clear the event */
         if (CurrentReadMode == READ_MODE_14BITS) {
             /* Read 14-bit XYZ results using a 6 byte IIC access */
             IIC_RegReadN(SlaveAddressIIC, OUT_X_MSB_REG, 6, (byte *) &last_xyz_values[0]);
         }
         else {
             /* Read 8-bit XYZ results using a 3 byte IIC access */
             IIC_RegReadN(SlaveAddressIIC, OUT_X_MSB_REG, 3, (byte *) &last_xyz_values[0]);
         }
    }      
}

/************************************************************************
*       update_green_leds - Update status of green indicator LEDs       *
*************************************************************************
; The green LEDs are used to indicate what mode the board's in:
;
;       LEDs D1-D2 indicate the accelerometer read mode 
;       LEDs D3-D5 indicate the accelerometer sensitivity 
;       LEDs D6-D10 indicate the current demo mode
;
; Arguments:
;   Bool       active        - Specifies whether the board is active 
;                              or asleep (turn all green LEDs off) 
;                           
*/
static void update_green_leds (Bool active)
{
    /* Green LEDs all off initially */
    PTAD_PTAD0 = 1;                             /* 8-bit/14-bit data LEDs */
    PTAD_PTAD4 = 1;             

    PTFD_PTFD6 = 1;                             /* 2g/4g/8g LEDs */
    PTFD_PTFD7 = 1;
    PTGD_PTGD0 = 1;
    
    PTAD_PTAD5 = 1;                             /* Channel select LEDs */
    PTAD_PTAD6 = 1;
    PTAD_PTAD7 = 1;
    PTBD_PTBD0 = 1;
    PTBD_PTBD1 = 1;

    if (active) {
        /* Use green LEDs D1-D2 to indicate current accelerometer read mode. 
           NB: Internally the accelerometer always uses 14 bits, but it can be configured to
           only return 8 bits to achieve a faster IIC transfer speed)
        */
        if (CurrentReadMode == READ_MODE_8BITS) 
            PTAD_PTAD0 = 0;             /* Turn on '8-bit data' LED */
        else
            PTAD_PTAD4 = 0;             /* Turn on '14-bit data' LED */
            
        /* Use green LEDs D3-D5 to indicate current accelerometer sensitivity - 2g, 4g or 8g */
        switch (CurrentRange) {
        case FULL_SCALE_2G:
            /* Turn on '2g' green LED indicator */
            PTFD_PTFD6 = 0;
            break;
             
        case FULL_SCALE_4G:     
            /* Turn on '4g' green LED indicator */
            PTFD_PTFD7 = 0;
            break;
             
        case FULL_SCALE_8G:
            /* Turn on '8g' green LED indicator */
            PTGD_PTGD0 = 0;
            break;
        }
             
        /* Use green LEDs D6-D10 to indicate current demo mode */   
        switch (CurrentDemo) {
        case DEMO_ORIENTATION:
            /* Turn 'Orientation' LED on */
            PTAD_PTAD7 = 0;
            break;
                
        case DEMO_SHAKE:
            /* Turn 'Shake' LED on */
            PTBD_PTBD0 = 0;
            break;
                
        case DEMO_TAP:
            /* Turn 'Tap' LED on */
            PTBD_PTBD1 = 0;
            break;
                
        case DEMO_FREEFALL:
            /* Turn 'Freefall' LED on */
            PTAD_PTAD5 = 0;
            break;
                
        case DEMO_TRANSIENT:
            /* Turn 'Transient' LED on */
            PTAD_PTAD6 = 0;
            break;
                
        }
    }
}

/************************************************************************
*       millisecond_delay - Pause execution for specified time          *
*************************************************************************
; NB: Millisecond timings are approximate only. 
;
; Arguments:
;   int        delay        - Approximate number of milliseconds to delay
*/
static void millisecond_delay (int delay)
{
    unsigned short count;
    
    /* Configure TPM1 as a free-running counter with approximately 32 microsecond tick... */  
    
    while (delay > 0) {
        if (delay > (0xffffUL/32)) {
            /* Need to loop multiple times to achieve full delay because a value 
               of 2048 or more would overflow 16 bits in "count = (count << 5) - count" below 
            */
            count = (0xffffUL/32);
            delay -= (int) (0xffffUL/32);
        }
        else {
            /* Do the whole thing in one hit */  
            count = (unsigned short) delay;
            delay = 0;
        }
        
        /* Multiply by 31, since 31 ticks = 31 x 32 microseconds = 0.992 ms,
           or approximately 1 ms. We don't care about the slight inaccuracy 
        */   
        count = (count << 5) - count;  
            
        TPM1SC = 0;                     /* Stop the timer */  
        TPM1SC_TOF = 0;                 /* Clear the TOF flag */  
             
        TPM1C0SC = 0;                   /* Configure the timer */  
        TPM1MOD = count;           
        TPM1CNT = 0;
        TPM1SC = 0x0f;                  /* Restart the timer: CLKS = 01 (Use BUSCLK), PS=111 (Divide-by-128)
                                           Frequency = fbus / 128, so period = 128 / 4 MHz = 32 microseconds 
                                         */
        
        /* Wait for timer to expire */  
        while (!_TPM1SC.Bits.TOF)  
            ;
    }
}


/************************************************************************
*       leds_show_tilt - Use LEDs to indicate how board is tilted       *
*************************************************************************
; This routine uses the X and Y values to show how the board is tilted
;
; (a) The LEDs are illuminated to indicate the direction of gravity
;     using a PWM technique to illuminate most brightly the 'spoke'
;     that's closest in direction, and dimmly illuminate the others
;
; (b) The number of LEDs illuminated along a spoke indicates how much the
;     board is tipped. (This is also affected by the 2g/4g/8g button)
;      
; The following diagram shows the orientation of the board when held 
; vertically with screen-printed writing the right way up - i.e. "On"
; switch at the top.
;
; This orients the accelerometer in the so-called Portrait Down (PD) position
;
;
;             PTD5            PTD4           PTD3          X
;                  *           *           *           <-------+ 
;                    *         *         *                     |
;                      *       *       *                       | Y
;                        *     *     *                         |
;                          *   *   *                           V
;                            * * *                              
;            PTF2  * * * * * *   * * * * * *  PTD2             
;                            * * *                             | 
;                          *   *   *                           | Gravity
;                        *     *     *                         |
;                      *       *       *                       V
;                    *         *         *
;                  *           *           * 
;             PTF1            PTF0           PTE7
;
;                "FREESCALE"        "Debug S08 Rev.A"
;
;
; The PTxx pins are actually configurable as outputs of the two 
; Timer/Pulse Width Modulator modules (TPMs):
;
;       PTD4 : TPM1CH2
;       PTD3 : TPM1CH1
;       PTD2 : TPM1CH0
;       PTE7 : TPM2CH3
;       PTF0 : TPM2CH2
;       PTF1 : TPM2CH1
;       PTF2 : TPM2CH0
;       PTD5 : TPM1CH3
;
; We first calculate the direction in which gravity is pulling, relative to the board.
; This is then used to calculate PWM duty cycles for the LEDs, so that they illuminate 
; with different brightnesses to show which way is Down.
;
; Note that it's not necessary to go to all this effort if the software just wants to
; detect a change in orientation between Portait and Landscape mode. For this simple
; case, the accelerometer can detect the change automatically, without polling. See 
; Application Note AN4068 for details.
;
; Arguments:
;   byte        *values - Array of 3 pairs of values for X, Y and Z
;                         Each pair is in 14-bit binary form
*/
static void leds_show_tilt (byte *values)
{
    int     x, y, g, r, pwm, spoke; 
    double  theta, angle, brightness, x2, y2, radius;
    
    /* Get X and Y values */
    x = (values [0] << 8) | values [1];
    y = (values [2] << 8) | values [3];
    
    /* What reading would correspond to normal gravity (at FULL_SCALE_2G) ? */
    if (CurrentReadMode == READ_MODE_8BITS)
        g = 0x3f00;                         
    else
        g = 0x3ffc;
        
    /* Indicate how much the board is tipped by turning on more LEDs
       along a spoke as the board is tipped further over
    */
    x2 = (double) x / g; 
    y2 = (double) y / g;
    radius = sqrt (x2 * x2 + y2 * y2); 
    
    PTCD &= ~0x3f;
    for (r = 0; r < 6; r++) {
        if (radius > r / 6.0)
            PTCD |= 1 << r;
    }  
        
    /* Calculate direction in which gravity is pulling. We'll use this to choose
       which spokes to illuminate, and with what duty cycles.
       
       With reference to the diagram above, 0 degrees is at 12 o'clock
       and angles increase clockwise
    */ 
    if (x == 0 && y == 0)
        theta = 0.0;
    else {    
        theta = atan2(-x, -y);
        if (theta < 0.0)
            theta +=  2.0 * _M_PI;
    }
      
    /* Loop to illuminate spokes */
    for (spoke = 0; spoke < 8; spoke++) {
        /* Calculate angle of spoke in radians */
        angle = spoke * 45.0 * _M_PI / 180.0; 
       
        /* Calculate desired brightness */
        angle = fabs (theta - angle);
        if (angle >= _M_PI / 2.0 && angle <= 3 * _M_PI / 2.0) {
            /* More than 90 degress between gravity and this spoke, so always off */
            pwm = 0;
        } else {
            /* Calculate brightness based on angle between spoke and gravity */
            brightness = cos (angle);

            /* Since the eye's response to brightness is non-linear, 
               use a logarithmic dimming curve as used by DALI lighting
            */
            brightness = pow (10.0, 3.0 * (brightness - 1.0));
        
            /* Calculate PWM value */
            pwm = (int) (MAX_PWM_VALUE * brightness);
        }
       
        switch (spoke) {
        case 0: TPM1C2V = pwm;  break;
        case 1: TPM1C1V = pwm;  break;
        case 2: TPM1C0V = pwm;  break;
        case 3: TPM2C3V = pwm;  break;
        case 4: TPM2C2V = pwm;  break;
        case 5: TPM2C1V = pwm;  break;
        case 6: TPM2C0V = pwm;  break;
        case 7: TPM1C3V = pwm;  break;
        }
    }
}

/************************************************************************
*       configure_LEDs_for_PWM - Set up TPM1 and TPM2                   *
*************************************************************************
; Since the LEDs are connected to the Timer/Pulse Width Modulator modules (TPMs),
; we can configure the TPMs to generate an edge-aligned PWM signal. This
; means that the brightness of a 'spoke' of LEDs can be varied by changing
; the PWM duty cycle
*/
static void configure_LEDs_for_PWM (void)
{
    /* Configure TPM1 to generate PWM signals */
    TPM1SC = 0;                                 /* Stop the timer */ 

    /* Configure duty cycles of each channel
     
       NB: We choose a timer frequency = fbus / 16, so a period of 500 (MAX_PWM_VALUE)
       means that an LED will flash
       
            4 x 10E6 / (500 * 16) = 500 flashes/second 
            
       ...i.e. too fast to be detected by the human eye
    
    */
    TPM1MOD = MAX_PWM_VALUE; 
    
    /* Configure TPMxCnSC for each channel as follows
        CHnF    = 0  Not used
        CHnIE   = 0  No interrupt
        MSnB:A  = 10 Edge-aligned PWM
        ELSnB:A = 01 Active-low pulse
    */   
    TPM1C0SC = 0x24;       
    TPM1C1SC = 0x24;       
    TPM1C2SC = 0x24;       
    TPM1C3SC = 0x24;       
                                    
    TPM1SC = 0x0c;                  /* Restart the timer: CLKS = 01 (Use BUSCLK), PS=100 (Divide-by-16) */

    /* Repeat for TPM2 */
    TPM2SC = 0;      
    TPM2MOD = MAX_PWM_VALUE;
     
    TPM2C0SC = 0x24;       
    TPM2C1SC = 0x24;       
    TPM2C2SC = 0x24;       
    TPM2C3SC = 0x24;       
    
    TPM2SC = 0x0c;     
}

/************************************************************************
*       configure_LEDs_for_GPIO - Stop TPMs from driving LEDs           *
*************************************************************************
; Modifies the TPM channel configurations so that the TPMs no longer drive
; the LEDs. Control over the LEDs reverts to the GPIO method
*/
static void configure_LEDs_for_GPIO (void)
{
    /* Stop the TPMs to conserve power */
    TPM1SC = 0;                                  
    TPM2SC = 0; 
                                    
    /* ELSnB:A = 00 : Pin reverts to GPIO */ 
    TPM1C0SC = 0x00;       
    TPM1C1SC = 0x00;       
    TPM1C2SC = 0x00;       
    TPM1C3SC = 0x00;       

    TPM2C0SC = 0x00;       
    TPM2C1SC = 0x00;       
    TPM2C2SC = 0x00;       
    TPM2C3SC = 0x00;       
}

/************************************************************************
*       leds_circle - Turn on specified ring of LEDs                    *
*************************************************************************
; Turns on specified ring of LEDs, where 0 = inner ring, 5 = outer ring
;
; Arguments:
;   int        circle_number        - Ring to turn on
*/
static void leds_circle (int circle_number)
{
    byte    ptc_val;
 
    configure_LEDs_for_GPIO ();
    
    /* All LED 'spokes' active */
    PTDD &= ~0x3c;                  
    PTED &= ~0x80;
    PTFD &= ~0x07;

    /* Turn on requested circle; turn off all others */
    ptc_val = PTCD & ~0x3f;
    ptc_val |= 1 << circle_number;
    PTCD = ptc_val;           
}

/************************************************************************
*       leds_pulse - Show 'pulse' on LEDs                               *
*************************************************************************
; Displays an animated 'pulse' of light on the LEDs
;
; Arguments:
;   int        delay        - Delay between each stage
*/
static void leds_pulse (int delay)
{
    byte    ring, dir;

    configure_LEDs_for_GPIO ();

    ring = 0;                       /* Start off by illuminating inner ring */
    dir = 1;                        /* Start off with pulse spreading outwards */
    
    for (;;) { 
        /* Turn on current 'ring' of LEDs */
        leds_circle (ring);
        millisecond_delay (delay);
        
        /* Figure out which ring to turn on next */
        ring += dir;
        if (ring == 6) {
            /* Reached outer ring, so reverse direction */
            dir = -1;
            ring = 5;
        }
        else if (ring == 0) {
            /* All done. Turn all LEDs off */
            leds_all_off ();
            break;
        }
        
        /* If user has changed demo mode by pressing SW3, exit early
           to improve responsiveness
        */
        if (NewDemo != CurrentDemo)
            break;
    }
}

/************************************************************************
*       leds_cross - Turn on LEDs to form a "x" cross                   *
*************************************************************************
;
;             PTD5            PTD4           PTD3     
;                  *                       *          
;                    *                   *            
;                      *               *              
;                        *           *                
;                          *       *                  
;                            *   *                    
;            PTF2                            PTD2     
;                            *   *                    
;                          *       *                  
;                        *           *                
;                      *               *              
;                    *                   *
;                  *                       * 
;             PTF1            PTF0           PTE7
;
*/
static void leds_cross (void)
{
    configure_LEDs_for_GPIO ();
    
    /* Set active spokes */
    PTDD |= 0x3c; 
    PTDD &= ~(1 << 3);          /* PTD3 on */
    PTDD &= ~(1 << 5);          /* PTD5 on */
                     
    PTED &= ~(1 << 7);          /* PTE7 on */
    
    PTFD |= 0x07;
    PTFD &= ~(1 << 1);          /* PTF1 on */
    
    /* Turn on all LEDs in active spokes */
    PTCD |= 0x3f;         
}

/************************************************************************
*       leds_plus - Turn on LEDs to form a "+" plus sign                *
*************************************************************************
;
;             PTD5            PTD4           PTD3     
;                              *                      
;                              *                      
;                              *                      
;                              *                      
;                              *                      
;            PTF2  * * * * * *   * * * * * * PTD2     
;                              *                      
;                              *                      
;                              *                      
;                              *                      
;                              *          
;                              *             
;             PTF1            PTF0           PTE7
;
*/
static void leds_plus (void)
{
    configure_LEDs_for_GPIO ();
    
    /* Set active spokes */
    PTDD |= 0x3c; 
    PTDD &= ~(1 << 2);          /* PTD2 on */
    PTDD &= ~(1 << 4);          /* PTD4 on */
                     
    PTED |= 0x80;
    
    PTFD |= 0x07;
    PTFD &= ~(1 << 0);          /* PTF0 on */
    PTFD &= ~(1 << 2);          /* PTF2 on */
    
    /* Turn on all LEDs in active spokes */
    PTCD |= 0x3f;         
}

/************************************************************************
*       leds_rotate - Show rotating 'spoke' pattern on LEDs             *
*************************************************************************
; Displays a rotating spoke pattern on the LEDs
;
; Arguments:
;   int        count        - Number of complete rotations to do
;   int        delay        - Delay between each stage
*/
static void leds_rotate (int count, int delay)
{
    int     i;
  
    for (i = 0; i < count * 8; i++) {
        if (i & 0x1)
            leds_cross ();
        else
            leds_plus ();
        
        millisecond_delay (delay);
        
        /* If user has changed demo mode by pressing SW3, exit early
           to improve responsiveness
        */
        if (NewDemo != CurrentDemo)
            break;
    }
    
    /* All spokes off */
    leds_all_off ();
}   

/************************************************************************
*       leds_all_on - Turn all LEDs on                                  *
*************************************************************************/
static void leds_all_on (void)
{
    configure_LEDs_for_GPIO ();
    PTDD &= ~0x3c;
    PTED &= ~0x80;
    PTFD &= ~0x07;
    PTCD |= 0x3f;
}

/************************************************************************
*       leds_all_off - Turn all LEDs off                                *
*************************************************************************/
static void leds_all_off (void)
{
    configure_LEDs_for_GPIO ();
    PTCD &= ~0x3f;
}

/************************************************************************
*       leds_show_shake - Show directional shake                        *
*************************************************************************
; This routine takes 'shake' data as returned in the accelerometer's
; Transient Source Register, and illuminates the LEDs to indicate the
; direction of the initial flick
;
; Reminder: With the board oriented so that the writing is the right way up
; and the On/Off switch is at the top, the accelerometer's X and Y
; directions are as follows:
;
; 
;             PTD5            PTD4           PTD3          X
;                  .           .           .           <-------+ 
;                    .         .         .                     |
;                      .       .       .                       | Y
;                        .     .     .                         |
;                          .   .   .                           V
;                            . . .                              
;            PTF2  . . . . . .   . . . . . .  PTD2             
;                            . . .                        
;                          .   .   .                      
;                        .     .     .                    
;                      .       .       .                  
;                    .         .         .
;                  .           .           . 
;             PTF1            PTF0           PTE7
;
;
; Note that more sophisticated directional shake detection would
; require analysis of the pattern of transients before and after
; the shake event...
;
; When the hand starts the shake, the board will experience 
; acceleration in a certain direction. A short while later the hand will
; stop the shake, causing deceleration. Usually the hand will slightly
; over-correct, so that the board 'bounces' and briefly accelerates
; in the opposite direction. Depending on the selected threshold
; programmed into TRANSIENT_THS_REG, the accelerometer may miss the
; initial shake and only detect the bounce. 
;
; See Application Note AN4071
;
*/
static void leds_show_shake (byte src)
{
    byte    i;
    
    /* All LEDs off */
    configure_LEDs_for_GPIO ();

    PTDD |= 0x3c;
    PTED |= 0x80;
    PTFD |= 0x07;
    PTCD |= 0x3f;

	/* Turn on LEDs to illustrate shake direction */
    if (src & ZTRANSE_MASK) {
        /* Shake in Z axis detected */
        if (src & ZTRANSEPOL_MASK)  
            leds_circle (5);
        else
            leds_circle (0);
    }
    else if (src & YTRANSE_MASK) {
        /* Shake in Y axis detected */
        if (src & YTRANSEPOL_MASK)  
            PTFD &= ~(1 << 0);
        else  
            PTDD &= ~(1 << 4);
   }
    else if (src & XTRANSE_MASK) {
        /* Shake in X axis detected */
        if (src & XTRANSEPOL_MASK)  
            PTFD &= ~(1 << 2);
        else  
            PTDD &= ~(1 << 2);
   }
    
    /* Hold LED pattern for 2 seconds */
    for (i = 0; i < 100; i++) {
        millisecond_delay (20);
        
        /* If user has changed demo mode by pressing SW3, exit early
           to improve responsiveness
        */
        if (NewDemo != CurrentDemo)
            break;
    }
    leds_all_off ();
}

/************************************************************************
*       button_press_isr - Interrupt handler for push buttons           *
*************************************************************************
; Interrupt handler for push buttons SW1-SW3 as follows:
;
;       g-Range Select (SW1) : Select 2g, 4g or 8g mode
;       Data 8/14bit   (SW2) : Configure accelerometer to return 8- or 14-bit data 
;       Channel Select (SW3) : Select a new demo
;
; In each case we just update a global variable, leaving it upto non-ISR
; code to change the configuration at a convenient time. Note that we can't
; directly change the accelerometer here, because a transition from 
; STANDBY -> ACTIVE mode will cause certain registers to be clobbered
*/
interrupt void button_press_isr (void)
{
    byte    val;
    
    /* Acknowledge the interrupt */  
    val = PTAD;
    KBI1SC_KB1ACK = 1;
    
    /* Which button was pressed ?  */
    val = ~val;                         /* Active low */

    if (val & (1 << 1)) {
        /* User pressed 'g-Range Select' button (SW1) 
           Request switch to next range setting
         */
         if (NewRange == CurrentRange) {
            switch (CurrentRange) {
            case FULL_SCALE_2G:
                NewRange = FULL_SCALE_4G;
                break;
            case FULL_SCALE_4G:
                NewRange = FULL_SCALE_8G;
                break;
            case FULL_SCALE_8G:
            default:
                NewRange = FULL_SCALE_2G;
                break;
            }
        }      
        else {
            /* Ignore key bounce. Main loop hasn't yet seen and responded
               to previous key press
            */
        }
    }

    if (val & (1 << 2)) {
        /* User pressed 'Data 8/14bit' button (SW2) 
           Request toggle current mode
        */
        if (NewReadMode == CurrentReadMode) {
            if (CurrentReadMode == READ_MODE_14BITS)  
                NewReadMode = READ_MODE_8BITS;    
            else    
                NewReadMode = READ_MODE_14BITS;            
        }
        else {
            /* Ignore key bounce. Main loop hasn't yet seen and responded
               to previous key press
            */
        }
    }
    
    if (val & (1 << 3)) {
        /* User pressed 'Channel Select' button (SW3)
           Request new demo
        */
        if (NewDemo == CurrentDemo) {
            /* Bump and wrap */
            NewDemo = CurrentDemo + 1;
            if (NewDemo == NUM_DEMOS)
                NewDemo = 0;
        }
        else {
            /* Ignore key bounce. Main loop hasn't yet seen and responded
               to previous key press
            */
        }
    } 
}

/************************************************************************
*       accelerometer_isr - Interrupt handler for accelerometer         *
*************************************************************************
; The accelerometer signals an interrupt by driving MMA_INT1 low
; low, which causes an edge-triggered interrupt via KBI2. The interrupt will
; wake the main processor up if it's sleeping.
;
; To tell the accelerometer that we've seen the interrupt, so that it stops
; driving MMA_INT1 low, it's necessary to read an event-specific register.  
; For example, to clear a 'Transient' interrupt you would read the TRANS_SRC 
; register.
; 
; However, this requires a bit of thought to achieve the best design:
;
; (a) It's good practice to keep ISRs as short as possible. We don't want to
;     perform IIC reads here if we can avoid it
;
; (b) Reading a register like TRANS_SRC also has another effect besides 
;     clearing MMA_INT1. Normally the bits in TRANS_SRC are latched to 
;     indicate which transient occurred. Reading TRANS_SRC un-freezes the bits 
;     and starts the next accelerometer transient detection, so that re-reading
;     it wouldn't return the same result.
; 
; The solution relies on recognising that although the accelerometer will 
; continue to drive MMA_INT1 low until we've cleared the interrupt, this won't
; cause a new falling edge on KBI2.
;
; Thus, provided we acknowledge the KBI2 interrupt here, the processor
; isn't immediately interrupted again. We can leave it up to non-interrupt 
; code in the routine 'read_accelerometer_info' to read TRANS_SRC, etc
*/
interrupt void accelerometer_isr (void)
{
    /* Acknowledge KBI2 interrupt */
    KBI2SC_KB2ACK = 1;
}

/************************************************************************
*       wait_for_movement - Wait until accelerometer indicates movement *
*************************************************************************
; This routine is called when the accelerometer indicates that no event of 
; the current type has occurred for 20 seconds. (For example if we're
; detecting 'taps', the routine is called if the user hasn't tapped the
; board for 20 seconds).
;
; We reconfigure the accelerometer to detect any movement, then put the
; CPU into a low power 'STOP 3' mode
;
; The subroutine doesn't return until the accelerometer indicates
; movement - e.g. the user has picked the board up.
*/
static void wait_for_movement (void)
{
    /* Configure accelerometer for motion detection in low power mode... */
      
    /* Disable MMA_INTx interrupts so there's no danger of a spurious interrupt
       during reconfiguration 
    */               
    DisableInterrupts;
    KBI2SC_KB2IE = 0;                  

    /* Put MMA845xQ into Standby Mode */
    IIC_RegWrite(SlaveAddressIIC, CTRL_REG1, (IIC_RegRead(SlaveAddressIIC, CTRL_REG1) & ~ACTIVE_MASK));
    
    /* ELE = 1     : Event latch enabled 
       ZTEFE = 1   : Raise event on any of Z, Y, X axes
       YTEFE = 1
       XTEFE = 1
       HBF_BYP = 0 : High-pass filter enabled
    */
    IIC_RegWrite(SlaveAddressIIC, TRANSIENT_CFG_REG, 0x1e);
    IIC_RegWrite(SlaveAddressIIC, HP_FILTER_CUTOFF_REG, 0x00);   
    
    /* Transient is indicated when acceleration on one of the axes
       is above threshold 1 x 0.063g  
    */   
    IIC_RegWrite(SlaveAddressIIC, TRANSIENT_THS_REG, 1);
    IIC_RegWrite(SlaveAddressIIC, TRANSIENT_COUNT_REG, 1);

    /* Interrupt signalled on INT1 when transient detected, or on Sleep <--> Wake transition */
    IIC_RegWrite(SlaveAddressIIC, CTRL_REG4, (INT_EN_TRANS_MASK | INT_EN_ASLP_MASK));
    
    /* Since reconfiguring the accelerometer here will wake it up again,
       tell it to go back to sleep as soon as possible
    */ 
    IIC_RegWrite(SlaveAddressIIC, ASLP_COUNT_REG, 1);              

    /* Put MMA845xQ back into Active Mode */
    IIC_RegWrite(SlaveAddressIIC, CTRL_REG1, (IIC_RegRead(SlaveAddressIIC, CTRL_REG1) | ACTIVE_MASK));

    /* Enable MMA_INTx interrupts */
    last_int_source_reg = 0;
    KBI2SC_KB2IE = 1;               

    /* Indicate that we're in sleep mode */
    leds_cross ();
    millisecond_delay (2000);
    leds_all_off ();
    
    /* Turn green LEDs off too */
    update_green_leds (FALSE);

    /* Sleep until accelerometer wakes up */
    for (;;) {
    
        /* Wait until we have event from accelerometer or push button */
        DisableInterrupts;
        last_int_source_reg = IIC_RegRead(SlaveAddressIIC, INT_SOURCE_REG);   
        if (last_int_source_reg == 0)  
            asm "stop";
        read_accelerometer_info ();
        EnableInterrupts;

        /* Accelerometer wake-up event? */
        if (last_int_source_reg & SRC_ASLP_MASK) {
            if ((last_sysmod_reg & SYSMOD_MASK) != 0x02) {
                /* Yes */  
                break;
            }
        }
       
        /* Did user press one of the buttons SW1 - SW3 ?
           If so, return to active mode
        */
        if (NewRange != CurrentRange || NewReadMode != CurrentReadMode || NewDemo != CurrentDemo) 
            break;
   }

    /* Indicate that we're awake again */
    leds_plus ();      
    millisecond_delay (2000);    
    leds_all_off ();
}             

 

Conclusion

We really like the XL_Star board and we are going to continue playing with it over the next few weeks and hopefully provide a tutorial on using it as a debugger. This first offering from Elelment 14 is proving to be a big hit across the electronics enthusiast community and we can not wait to see what they have instore next. If you are looking for a compact Freescale S08 Development Platform that can be used after the development stage we highly reccomend this board.

The Element14 XL_STAR board is avaliable from Newark.com and is a bargan for about $32.00 USD.
If you are outside of the USA and would like to purchase the XL_STAR it can be found on Farnell.com for £19.99.

Element14 has a community page for the XL_STAR development board so hop in and join the discussion.

 

This product was provided free of charge by its manufacturer or retailer for the purpose of review.

TheMakersWorkbench.com  strives to provide non biased reviews and thus does not accept any form of monitary payment for the reviews appearing on its pages. If you would like to have your product evaluated, showcased, or reviewed on TheMakersWorkbench.com please feel free to contact us by clicking on the contact us tab at the top of the page. 

Other Articles You Might Like

Review: Lulzbot AO-100 3D Printer part 1Lulzbot AO-100 / AO-101 reviewed

Tutorial: DIY Telescope to PC CableDIY Telescope to PC Cable

Project: LED Strip Bench LightingDIY RGB LED Strip Bench lighting

Review: ISO-TIP Portable  Soldering KitISO-Tip Portable Soldering Kit Review

Article: Arduino Due Overview

Project: 3D Printed Balloon Powered CarPrint a Balloon powered Jet Car with your 3D printer