1.
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.
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.
Now that we have the formalities out of the way lets take a closer look at the XL_STAR board.
The XL_Star Development Board arrived in a cute little white box.
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.
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.
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.
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.
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.
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.
If you are interested in the sample code we have included it below.
/************************************************************************** * 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 (); }
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.