• Pic® Basic


  • MMA7260 Accelerometer Interface

    Accelerometers are fascinating devices, combining tiny moving mechanical parts with electronics to produce truly amazing results. They are part of the semiconductor family named MEMS (Microelectromechanical systems). The freescale MMA7260Q is a 3-axis accelerometer, meaning it can detect a gravity force in X, Y and Z axes.

    Accelerometers are invariably surface mount devices, making prototyping difficult, however, there are many, so called, breakout boards that contain the accelerometer chip and any surrounding circuitry required. These are normally brought to a more useable format, such as 0.1" (2.54mm) headers.

    This is the type of board I used, and it was purchased from ebay for less that I could purchase the actual accelerometer chip itself. The board is made by Sure electronics and is shown below, along with it's axis orientation:


    The datasheet for the MMA7260 accelerometer chip can be downloaded from here: MMA7260QT datasheet.pdf.

    The PCB is a little misleading, in that it has two rows of 0.1" headers, one row contains the texts that pertain to a serial SPI device (
    J4), and the other row contains the texts that pertain to the accelerometer outputs (J1). The serial text row (left hand side from the above illustration, named J4) pins are not connected to any serial conversion device, and the MMA7260 only produces analogue outputs. The only connected pin is the 5 volt one, which connects to a regulator chip.

    As the Amicus18 operates at 3.3 Volts, and so does the MMA7260 device, this row can be totally ignored, as most pins serve no purpose in any case, and all interfacing is performed with header J1.


    As usual, I used the trusty companion shield to implement the interface, and it's layout is shown below:


    Click here for a larger image

    A top down layout is shown below for more clarity:


    Click here for a larger image


    The MMA7260 has analogue outputs that vary in voltage according to the gravity force measured. Unusually, it also has four selections of force sensitivity through the use of it's SEL1 and SEL2 inputs:


    SEL2 Input
    SEL1 Input
    g-Range
    Sensitivity
    0 0 1.5g 800mV per g
    0 1 2g 600mV per g
    1 0 4g 300mV per g
    1 1 6g 200mV per g


    The analogue outputs for X, Y and Z can easily be interfaced to three of the microcontroller's ADC peripheral inputs, each producing a value from 0 to 1023 depending on the movement of a particular axis. This can then be converted to a voltage value.

    That is exactly what the program below does. However, it also incorporates a median filter so it can smooth out the ADC values somewhat. The program can also be downloaded from here: MMA7260_Accelerometer_Simple.bas.

    Code:
    '
    ' MMA7260 Accelerometer interface
    '
    ' Display the X, Y and Z axis values on the serial terminal
    ' According to the position of the accelerometer
    '
    ' Accelerometer connections
    '
    ' X output connects to AN0 (PORTA.0)
    ' Y output connects to AN1 (PORTA.1)
    ' Z output connects to AN2 (PORTA.2)
    ' Sel1 input connects to PORTB.5
    ' Sel2 input connects to PORTB.0
    '
        Include "Amicus18.inc"          ' Configure the compiler for a PIC18F25K20 at 64MHz. i.e. An Amicus18 board
        Include "Amicus18_ADC.inc"      ' Load the PIC18F25K20 ADC macro's into the program
        
        Symbol tSel1 = PORTB.5          ' Accelerometer Sel1 pin connection
        Symbol tSel2 = PORTB.0          ' Accelerometer Sel2 pin connection
        
        Symbol cQuanta = 3.3 / 1023     ' ADC to voltage conversion (VDD / ADC resolution)
          
        Dim wXoutput As Word            ' Holds the filtered 10-Bit result from the ADC
        Dim wYoutput As Word            ' Holds the filtered 10-Bit result from the ADC
        Dim wZoutput As Word            ' Holds the filtered 10-Bit result from the ADC
        
        Dim fXVoltage As Float          ' Holds the voltage output from the channel
        Dim fYVoltage As Float          ' Holds the voltage output from the channel
        Dim fZVoltage As Float          ' Holds the voltage output from the channel
    '
    ' Variables used by the bubble sort routine
    '    
        Dim bSortIndex As Byte
        Dim tSwapOccured As Bit
        Dim wSwapTemp As Word
        Dim wSortResult As wSwapTemp
        
        Symbol cSamplesToTake = 9       ' The amount of samples to take for the bubble sort
        Dim wSampleArray[cSamplesToTake] As Word
        
    '--------------------------------------------------------------------    
        GoTo Main                       ' Jump over the subroutines
    '-------------------------------------------------------------------- 
    '
    ' Select the sensitivity of the MMA7620
    '
    $define Set_1_5g() Low tSel1 : Low tSel2
    $define Set_2g() High tSel1 : Low tSel2
    $define Set_4g() Low tSel1 : High tSel2
    $define Set_6g() High tSel1 : High tSel2
    
    '--------------------------------------------------------------------        
    ' Implement a bubble sorting median filter
    ' 
    ' Input     : wSampleArray holds the values to sort
    ' Output    : wSortResult holds the median of the values
    ' Notes     : None
    ' 
    MedianFilter: 
        Repeat
            tSwapOccured = 0                                                    ' Clear flag that indicates swap.
            bSortIndex = 0
            Repeat                                                              ' For each cell of the array...
                If wSampleArray[bSortIndex] > wSampleArray[bSortIndex + 1] Then ' Move larger values up. 
                    wSwapTemp = wSampleArray[bSortIndex]                        ' ..by swapping them. 
                    wSampleArray[bSortIndex] = wSampleArray[bSortIndex + 1]
                    wSampleArray[bSortIndex + 1] = wSwapTemp
                    tSwapOccured = 1                                            ' Set bit if swap occurred.  
                EndIf
                Inc bSortIndex
            Until bSortIndex > cSamplesToTake                                   ' Check next cell of the array. 
        Until tSwapOccured = 0                                                  ' Keep sorting until no more swaps. 
        wSortResult = wSampleArray[cSamplesToTake / 2]                          ' Extract the middle (median) value
        Return
       
    '--------------------------------------------------------------------
    ' Read the X output from the accelerometer (Forward-Backward)
    '
    ' Input     : None
    ' Output    : wXoutput holds the filtered 10-bit result from the ADC
    '             fXVoltage holds the voltage output from the channel
    ' Notes     : None
    ReadChannel_X:
        For bSortIndex = (cSamplesToTake - 1) To 0 Step -1  ' Create a loop for the amount of samples to take
            wSampleArray[bSortIndex] = ReadADC(ADC_CH0)     ' Read the channel into the filter array
            DelayUS 2                                       ' A small delay to allow the ADC's capacitor to re-charge
        Next                                                ' Close the loop
        GoSub MedianFilter                                  ' Perform a median filter on the samples
        wXoutput = wSortResult                              ' Transfer the result of the filter
        fXVoltage = wXoutput * cQuanta                      ' Convert to a voltage
        Return
    
    '--------------------------------------------------------------------
    ' Read the Y output from the accelerometer (Side to Side)
    '
    ' Input     : None
    ' Output    : wYoutput holds the filtered 10-bit result from the ADC
    '             fYVoltage holds the voltage output from the channel
    ' Notes     : None
    ReadChannel_Y:
        For bSortIndex = (cSamplesToTake - 1) To 0 Step -1  ' Create a loop for the amount of samples to take
            wSampleArray[bSortIndex] = ReadADC(ADC_CH1)     ' Read the channel into the filter array
            DelayUS 2                                       ' A small delay to allow the ADC's capacitor to re-charge
        Next                                                ' Close the loop
        GoSub MedianFilter                                  ' Perform a median filter on the samples
        wYoutput = wSortResult                              ' Transfer the result of the filter
        fYVoltage = wYoutput * cQuanta                      ' Convert to a voltage
        Return
    
    '--------------------------------------------------------------------
    ' Read the Z output from the accelerometer (Up and Down)
    '
    ' Input     : None
    ' Output    : wZoutput holds the filtered 10-bit result from the ADC
    '             fZVoltage holds the voltage output from the channel
    ' Notes     : None
    ReadChannel_Z:
        For bSortIndex = (cSamplesToTake - 1) To 0 Step -1  ' Create a loop for the amount of samples to take
            wSampleArray[bSortIndex] = ReadADC(ADC_CH2)     ' Read the channel into the filter array
            DelayUS 2                                       ' A small delay to allow the ADC's capacitor to re-charge
        Next                                                ' Close the loop
        GoSub MedianFilter                                  ' Perform a median filter on the samples
        wZoutput = wSortResult                              ' Transfer the result of the filter
        fZVoltage = wZoutput * cQuanta                      ' Convert to a voltage
        Return
     
    '--------------------------------------------------------------------    
    ' The main program loop starts here
    Main: 
        Set_1_5g()                                          ' Set accelerometer sensitivity
    '
    ' Open the ADC and make AN0, AN1, and AN2 analogue inputs
    '   
        OpenADC(ADC_FOSC_64 & ADC_RIGHT_JUST & ADC_2_TAD, ADC_REF_VDD_VSS, ADC_1ANA & ADC_2ANA & ADC_3ANA)
    '
    ' Display the accelerometer values on the serial terminal
    '           
       While                                    ' Create an infinite loop     
            GoSub ReadChannel_X                 ' Read the X channel
            GoSub ReadChannel_Y                 ' Read the Y channel
            GoSub ReadChannel_Z                 ' Read the Z channel
                   
            HRSOut 1                            ' Clear the serial terminal's window
            HRSOut "X axis (Forward-Backward): ", Dec wXoutput, " : ", Dec2 fXVoltage, " Volts\r"  ' Display the X channel's value
            HRSOut "Y axis (Side to Side)    : ", Dec wYoutput, " : ", Dec2 fYVoltage, " Volts\r"  ' Display the Y channel's value
            HRSOut "Z axis (Up and Down)     : ", Dec wZoutput, " : ", Dec2 fZVoltage, " Volts\r"  ' Display the Z channel's value
            
            DelayMS 300 
        Wend                                    ' Do it forever

    The above code will transmit the raw ADC values as well as the voltage values to the serial terminal, as shown below:


    We could leave it at that, as we have enough information for a lot of applications, however, we still haven't retrieved the actual g force measured by the device, so we'll continue.

    Measuring g forces and inclinations.
    When thinking about accelerometers it is often useful to image a cube with a ball inside it:
    If we take this box to a location with no gravitation fields, or for that matter with no other fields that might affect the ball's position, the ball will simply float in the middle of the box. You can imagine the box as a space craft orbiting around the planet where everything is in a weightless state . From the illustration above you can see that we assign to each axis a pair of walls (the Y+ wall has been removed so we can see inside the box).

    Imagine that each wall is pressure sensitive. If we move the box to the left (we accelerate it with acceleration 1g = 9.8m/s^2), the ball will hit the wall X-. We then measure the pressure force that the ball applies to the wall and output a value of -1g on the X axis:
    Note that the accelerometer will actually detect a force that is directed in the opposite direction from the acceleration vector. This force is often named Inertial Force or Fictitious Force . One thing that should be learn from this is that an accelerometer measures acceleration indirectly through a force that is applied to one of it's walls (it may be a spring in our box analogy, or semiconductor tie in a real accelerometer). This force can be caused by the acceleration , but as we'll see in the next example it is not always caused by acceleration.

    If we take our model and put it on Earth, the ball will fall on the Z- wall and will apply a force of 1g on the bottom wall, as shown in the illustration below:
    In this case the box isn't moving but we still get a reading of -1g on the Z axis. The pressure that the ball has applied on the wall was caused by a gravitation force. In theory, it could be a different type of force; for example, if you imagine that our ball is metallic, placing a magnet next to the box could move the ball so it hits another wall. This illustrates that in essence, an accelerometer measures force not acceleration. It just happens that acceleration causes an inertial force that is captured by the force detection mechanism of the accelerometer.

    So far we've looked at the accelerometer output on a single axis and this is all that's available with a single axis accelerometer. The real value of triaxial accelerometers comes from the fact that they can detect inertial forces on all three axes. Let's go back to our box model, and rotate it 45 degrees to the right. The ball will touch two walls now: Z- and X- as shown in the illustration below:
    The values of 0.71 are not arbitrary, they are actually an approximation for sqr(1/2). This will become more clear as we introduce our next model for the accelerometer.

    In the previous model we've fixed the gravitation force and rotated our imaginary box. In the last two examples we've looked at the output in two different box positions, while the force vector remained constant. While this was useful in understanding how the accelerometer interacts with outside forces, it's more practical to perform calculations if we fix the coordinate system to the axes of the accelerometer and imagine that the force vector rotates around us.
    Looking at the illustration above, imagine that each axis in the model is perpendicular to the respective faces of the box in the previous illustrations. The vector R is the force vector that the accelerometer is measuring (it could be either the gravitation force or the inertial force from the examples above or a combination of both). Rx, Ry, Rz are a projection of the R vector on the X, Y, and Z axes. Note the following relation:

    Equation 1.

    R^2 = Rx^2 + Ry^2 + Rz^2

    This is essentially the equivalent of the Pythagorean theorem, but in 3D.

    Remember that the values of sqr(1/2) ~ 0.71 are not random. If they are inserted into the formula above, after recalling that our gravitation force was 1 g we can verify that:

    1^2 = (-sqr(1/2) )^2 + 0 ^2 + (-sqr(1/2))^2

    simply by substituting R=1, Rx = -sqr(1/2), Ry = 0 , Rz = -sqr(1/2) in equation 1

    The values Rx, Ry, and Rz are actually linearly related to the values that a real accelerometer will output and are in a form that can be used for performing various calculations.

    We’ll move on with a simple example. On my desk, the 10-bit ADC of the Amicus18 board gave the following values for the three accelerometer channels (axes), when the board was tilted to the right and forward:

    AdcRx = 513 485
    AdcRy = 541 480
    AdcRz = 687 502


    In order to convert a 10-bit ADC value to a voltage we use the following formula:

    VoltsRx = AdcRx * 3.3 / 1023


    Applying this formula to all three channels we get:

    VoltsRx = 513 * (3.3V / 1023) = ~1.65V (we round all results to 2 decimal points)
    VoltsRy = 540 * (3.3V / 1023) = ~1.74V
    VoltsRz = 687 * (3.3V / 1023) = ~2.21V


    The accelerometer has a zero-g voltage level, which can be found in the datasheet, or better still use the “MMA7260_Accelerometer_Simple.bas” program and note the values from the serial terminal when the companion shield is sitting horizontal on a level desk. these are the voltages that correspond to 0g. The companion shield will need to be moved on to it’s right side in order to take the 0g reading of the Z axis correctly.

    In experiments, the 0g values for a sensitivity setting of 1.5 were:

    VoltsRx = 1.57V
    VoltsRy = 1.55V
    VoltsRz = 1.65V


    To get a signed voltage value we need to calculate the shift from these levels. For example, if our 0g voltage level is 1.57V. We calculate the voltage shifts from the zero-g voltage as follows:

    DeltaVoltsRx = 1.65V - 1.57V = 0.08V
    DeltaVoltsRy = 1.74V - 1.55V = 0.19V
    DeltaVoltsRz = 2.21V – 1.65V = 0.56V


    We now have our accelerometer readings in Volts, but it's still not in g (9.8 m/s^2), to do the final conversion we apply the accelerometer’s sensitivity, usually expressed in mV/g. Sensitivity values can be found in the accelerometer's datasheet, and are selectable with the MMA7260, i.e. 800mV/g, 600mV/g, 300mV/g, and 200mV/g.

    To get the final force values expressed in g we use the following formula:

    Rx = DeltaVoltsRx / Sensitivity

    Rx = 0.08V / 0.6V/g = ~0.13g
    Ry = 0.19V / 0.6V/g = ~0.31g
    Rz = 0.56V / 0.6V/g = ~0.93g


    We now have all three values that define our inertial force vector. If we need to calculate inclination of the device relative to the ground we can calculate the angle between these vector and the Z axis.

    If we were interested in a per-axis direction of inclination we could split this result into two components. i.e. inclination on the X and Y axis that can be calculated as the angle between gravitation vector and X / Y axes.

    Calculating these angles is simpler than you may imagine, now that we’ve calculated the values for Rx, Ry and Rz.


    The angles that we’re interested in are the angles between the X, Y, and Z axes and the force vector R. We'll define these angles as Axr, Ayr, Azr. The illustration above shows a right angle triangle formed by R and Rx:

    cos(Axr) = Rx / R

    and similarly :

    cos(Ayr) = Ry / R
    cos(Azr) = Rz / R


    We can deduce from the earlier equation 1 that R = sqr( Rx^2 + Ry^2 + Rz^2). So we can now calculate the angles by using the compiler's acos() function (the inverse cos() function):

    Axr = acos(Rx/R)
    Ayr = acos(Ry/R)
    Azr = acos(Rz/R)


    The angles are still in radians, so a conversion to degrees is called for. This is simply a multiplication by the constant 180 / 3.14:

    Axd = Axr * (180 / 3.14)
    Ayd = Ayr * (180 / 3.14)
    Azd = Azr * (180 / 3.14)


    The above theory is made practical in the program listed below. It will display the g forces and the angles on the serial terminal. The program can also be downloaded from here:MMA7260_Accelerometer_Gravity.bas.


    Code:
    '
    ' MMA7260 Accelerometer interface
    '
    ' Display the X, Y and Z axis g-force values and angles
    '
    ' Accelerometer connections
    '
    ' X output connects to AN0 (PORTA.0)
    ' Y output connects to AN1 (PORTA.1)
    ' Z output connects to AN2 (PORTA.2)
    ' Sel1 input connects to PORTB.5
    ' Sel2 input connects to PORTB.0
    ' Sleep connects to VDD
    '
        Include "Amicus18.inc"          ' Configure the compiler for a PIC18F25K20 at 64MHz. i.e. An Amicus18 board
        Include "Amicus18_ADC.inc"      ' Load the PIC18F25K20 ADC macro's into the program
        
        Symbol tSel1 = PORTB.5          ' Accelerometer's Sel1 pin connection
        Symbol tSel2 = PORTB.0          ' Accelerometer's Sel2 pin connection
        
        Symbol cQuanta = 3.3 / 1023     ' ADC to voltage conversion (VDD / ADC resolution)
        Symbol cDegreeConversion = 180.0 / 3.14159265 ' Conversion from radians to degrees
          
        Dim wXoutput As Word            ' Holds the filtered 10-Bit result from the ADC
        Dim wYoutput As Word            ' Holds the filtered 10-Bit result from the ADC
        Dim wZoutput As Word            ' Holds the filtered 10-Bit result from the ADC
        
        Dim fXVoltage As Float          ' Holds the voltage output from the channel
        Dim fYVoltage As Float          ' Holds the voltage output from the channel
        Dim fZVoltage As Float          ' Holds the voltage output from the channel
       
        Dim R As Float                  ' Holds the result of the multiple sqr
        
        Dim Rx As Float                 ' Holds the result of fGravity_X squared
        Dim Ry As Float                 ' Holds the result of fGravity_Y squared
        Dim Rz As Float                 ' Holds the result of fGravity_Z squared
        
        Dim bAngleX As Byte             ' Holds the inclination angle of the X axis
        Dim bAngleY As Byte             ' Holds the inclination angle of the Y axis
        
        Dim fGravity_X As Float         ' Holds the result of the X axis g-force calculation
        Dim fGravity_Y As Float         ' Holds the result of the Y axis g-force calculation
        Dim fGravity_Z As Float         ' Holds the result of the Z axis g-force calculation
    
        Symbol Zero_G_X = 1.57          ' Zero g voltage level for X axis, as measured
        Symbol Zero_G_Y = 1.55          ' Zero g voltage level for Y axis, as measured
        Symbol Zero_G_Z = 1.65          ' Zero g voltage level for Z axis, as measured
    '
    ' Variables used by the bubble sort routine
    '    
        Dim bSortIndex As Byte
        Dim tSwapOccured As Bit
        Dim wSwapTemp As Word
        Dim wSortResult As wSwapTemp
        
        Symbol cSamplesToTake = 19      ' The amount of samples to take for the bubble sort
        Dim wSampleArray[cSamplesToTake] As Word
        
    '--------------------------------------------------------------------      
        GoTo Main                       ' Jump  over the subroutines
    '-------------------------------------------------------------------- 
    ' Select the sensitivity of the MMA7620
    ' Also creates a sensitivity constant for each g selection
    '
    $define Set_1_5g()  '
        Symbol cSensitivity = 0.8 '
        Low tSel1       '
        Low tSel2
        
    $define Set_2g()    '
        Symbol cSensitivity = 0.6 '
        High tSel1      '
        Low tSel2
        
    $define Set_4g()    '
        Symbol cSensitivity = 0.3 '
        Low tSel1       '
        High tSel2
        
    $define Set_6g()    '
        Symbol cSensitivity = 0.2 '
        High tSel1      '
        High tSel2
    
    '--------------------------------------------------------------------         
    ' Implement a bubble sorting median filter
    ' Input     : wSampleArray holds the values to sort
    ' Output    : wSortResult holds the median of the values
    ' Notes     : None
    ' 
    MedianFilter: 
        Repeat
            tSwapOccured = 0                                                     ' Clear flag that indicates swap.                
            bSortIndex = 0
            Repeat                                                               ' For each cell of the array...
                If wSampleArray[bSortIndex] > wSampleArray[bSortIndex + 1] Then  ' Move larger values up. 
                    wSwapTemp = wSampleArray[bSortIndex]                         ' ..by swapping them. 
                    wSampleArray[bSortIndex] = wSampleArray[bSortIndex + 1]
                    wSampleArray[bSortIndex + 1] = wSwapTemp
                    tSwapOccured = 1                                             ' Set bit if swap occurred.  
                EndIf
                Inc bSortIndex
            Until bSortIndex > cSamplesToTake                                    ' Check next cell of the array. 
        Until tSwapOccured = 0                                                   ' Keep sorting  until no more swaps. 
        wSortResult = wSampleArray[cSamplesToTake / 2]                           ' Extract the middle (median) value
        Return
       
    '--------------------------------------------------------------------
    ' Read the X output from the accelerometer (Forward-Backward)
    ' Input     : None
    ' Output    : wXoutput holds the filtered 10-bit result from the ADC
    '             fXVoltage holds the voltage output from the channel
    ' Notes     : None
    ReadChannel_X:
        For bSortIndex = (cSamplesToTake - 1) To 0 Step -1  ' Create a loop for the amount of samples to take
            wSampleArray[bSortIndex] = ReadADC(ADC_CH0)     ' Read the channel into the filter  array
            DelayUS 2                                       ' A small delay to allow the ADC's capacitor to re-charge
        Next                                                ' Close the loop
        GoSub MedianFilter                                  ' Perform a median filter on the samples
        wXoutput = wSortResult                              ' Transfer the result of the filter
        fXVoltage = wXoutput * cQuanta                      ' Convert to a voltage
        Return
    
    '--------------------------------------------------------------------
    ' Read the Y output from the accelerometer (Side to Side)
    ' Input     : None
    ' Output    : wYoutput holds the filtered 10-bit result from the ADC
    '             fYVoltage holds the voltage output from the channel
    ' Notes     : None
    ReadChannel_Y:
        For bSortIndex = (cSamplesToTake - 1) To 0 Step -1  ' Create a loop for the amount of samples to take
            wSampleArray[bSortIndex] = ReadADC(ADC_CH1)     ' Read the channel into the filter array
            DelayUS 2                                       ' A small delay to allow the ADC's capacitor to re-charge
        Next                                                ' Close the loop
        GoSub MedianFilter                                  ' Perform a median filter on the samples
        wYoutput = wSortResult                              ' Transfer the result of the filter
        fYVoltage = wYoutput * cQuanta                      ' Convert to a voltage
        Return
    
    '--------------------------------------------------------------------
    ' Read the Z output from the accelerometer (Up and Down)
    ' Input     : None
    ' Output    : wZoutput holds the filtered 10-bit result from the ADC
    '             fZVoltage holds the voltage output from the channel
    ' Notes     : None
    ReadChannel_Z:
        For bSortIndex = (cSamplesToTake - 1) To 0 Step -1  ' Create a loop for the amount of samples to take
            wSampleArray[bSortIndex] = ReadADC(ADC_CH2)     ' Read the channel into the filter array
            DelayUS 2                                       ' A small delay to allow the ADC's capacitor to re-charge
        Next                                                ' Close the loop
        GoSub MedianFilter                                  ' Perform a median filter on the samples
        wZoutput = wSortResult                              ' Transfer the result of the filter
        fZVoltage = wZoutput * cQuanta                      ' Convert to a voltage
        Return
     
    '--------------------------------------------------------------------     
    ' The main program loop starts here
    Main: 
        Set_2g()                                          ' Set accelerometer sensitivity
    '
    ' Open the ADC and make AN0, AN1, and AN2 analogue inputs
    '   
        OpenADC(ADC_FOSC_32 & ADC_RIGHT_JUST & ADC_0_TAD, ADC_REF_VDD_VSS, ADC_1ANA & ADC_2ANA & ADC_3ANA)
    '
    ' Display the accelerometer forces and angles on the serial terminal
    '           
       While                                    ' Create an infinite loop     
            GoSub ReadChannel_X                 ' Read the X channel
            GoSub ReadChannel_Y                 ' Read the Y channel
            GoSub ReadChannel_Z                 ' Read the Z channel
            '
            ' Calculate the gravity measurement of the axis
            ' 
            fGravity_X = (fXVoltage - Zero_G_X) / cSensitivity 
            fGravity_Y = (fYVoltage - Zero_G_Y) / cSensitivity 
            fGravity_Z = (fZVoltage - Zero_G_Z) / cSensitivity
            '
            ' Convert the gravity measurements to angles and convert to degrees        
            '
            Rx = fGravity_X * fGravity_X
            Ry = fGravity_Y * fGravity_Y
            Rz = fGravity_Z * fGravity_Z
            
            R = Sqr(Rx + Ry + Rz)
       
            bAngleX = (ACos(fGravity_X / R)) * cDegreeConversion
            bAngleY = (ACos(fGravity_Y / R)) * cDegreeConversion     
                
            HRSOut 1                            ' Clear the serial terminal's window       
            HRSOut "X axis (Forward-Backward): ", Dec2 fGravity_X, " g\r"     ' Display the X channel's gravity value
            HRSOut "Y axis (Side to Side)    : ", Dec2 fGravity_Y, " g\r"     ' Display the Y channel's gravity value       
            HRSOut "\r"      
            HRSOut "X axis (Forward-Backward): ", Dec bAngleX, " degrees\r"   ' Display the X channel's angle
            HRSOut "Y axis (Side to Side)    : ", Dec bAngleY, " degrees\r"   ' Display the Y channel's angle              
            DelayMS 300 
        Wend                                    '  Do it forever

    Once the program is loaded into the Amicus18 board, the serial terminal will display the values, as shown below:
















    Les Johnson.