Smb380

Could someone double-check this driver?


    /// <summary>
    /// Driver for the Bosch SMB380 Accelerometer
    /// </summary>
    public sealed class SMB380
    {
        #region Fields
        private const float G2Multiplier = 2.0f;
        private const float G4Multiplier = 4.0f;
        private const float G8Multiplier = 8.0f;

        private SPI spi;
        private byte[] read = new byte[2];
        private byte[] write = new byte[2];

        private float lowGThreshold = 0.3137255f;
        private float highGThreshold = 2.5098039f;
        private float anyMotionThreshold;
        private InterruptPort interruptPort;
        private float gMultiplier = G4Multiplier;
        private short bandwidthInHz = 1500;
        private bool areInterruptsEnabled = true;
        #endregion Fields

        #region Events
        /// <summary>
        /// Raised asynchronously when the Low G Threshold is reached.
        /// </summary>
        public event ThresholdReachedEventHandler ThresholdReached;
        #endregion Events

        #region Constructor
        static SMB380()
        {
            Instance = new SMB380();
        }

        private SMB380()
        {
            spi = new SPI(new SPI.Configuration((Cpu.Pin)FEZ_Pin.Digital.UEXT10, false, 0, 0, true, true, 200, SPI.SPI_module.SPI2));

            //Enabled latched interrupts
            WriteRegister(0x15, (byte)(ReadRegister(0x15) | 0x10));

            interruptPort = new InterruptPort((Cpu.Pin)FEZ_Pin.Interrupt.UEXT4, false, Port.ResistorMode.PullDown, Port.InterruptMode.InterruptEdgeHigh);
            interruptPort.OnInterrupt += OnInterrupt;
            interruptPort.EnableInterrupt();
        }
        #endregion Constructor

        #region Event Handlers
        private void OnInterrupt(uint data1, uint data2, DateTime time)
        {
            //LG
            byte register = ReadRegister(0x09);
            if ((register & (byte)0x08) > 0)
            {
                WriteRegister(0x09, (byte)(register & 0xF7));
                RaiseThresholdReached(time, ThresholdType.LowG);
            }

            //HG
            if ((register & (byte)0x04) > 0)
            {
                WriteRegister(0x09, (byte)(register & 0xFB));
                RaiseThresholdReached(time, ThresholdType.HighG);
            }

            //Clear the interrupt on the smb380
            WriteRegister(0x0A, (byte)(ReadRegister(0x0A) | 0x40));
        }

        private void RaiseThresholdReached(DateTime time, ThresholdType thresholdType)
        {
            Thread thread = new Thread(() =>
            {
                ThresholdReachedEventHandler localHandler = ThresholdReached;
                if (localHandler != null)
                {
                    localHandler(this, new ThresholdReachedEventArgs(thresholdType, time));
                }
                Dispatcher.Run();
            });
            thread.Start();
        }
        #endregion Event Handlers

        #region Properties
        /// <summary>
        /// Gets the current instance of the SMB380
        /// </summary>
        public static SMB380 Instance { get; private set; }

        /// <summary>
        /// Gets the Acceleration in g's in the X-direction
        /// </summary>
        public float AccelerationX
        {
            get
            {
                short lsb = (sbyte)ReadRegister(0x02); //LSB 6 7
                short msb = (sbyte)ReadRegister(0x03); //MSB 0 7
                return ExtractAcceleration(lsb, msb);
            }
        }

        /// <summary>
        /// Gets the Acceleration in g's in the Y-direction
        /// </summary>
        public float AccelerationY
        {
            get
            {
                short lsb = (sbyte)ReadRegister(0x04); //LSB 6 7
                short msb = (sbyte)ReadRegister(0x05); //MSB 0 7
                return ExtractAcceleration(lsb, msb);
            }
        }

        /// <summary>
        /// Gets the Acceleration in g's in the Z-direction
        /// </summary>
        public float AccelerationZ
        {
            get
            {
                short lsb = (sbyte)ReadRegister(0x06); //LSB 6 7
                short msb = (sbyte)ReadRegister(0x07); //MSB 0 7
                return ExtractAcceleration(lsb, msb);
            }
        }

        /// <summary>
        /// Gets or sets the range of the accelerometer.
        /// </summary>
        public GRange Range
        {
            get
            {
                return (GRange)((ReadRegister(0x14) & 0x18) >> 3);
            }
            set
            {
                byte val = ReadRegister(0x14);//xxx Range(2) Bandwidth(3)
                val &= (byte)(0xE7 | ((byte)value << 3));
                WriteRegister(0x14, val);
                gMultiplier = value == GRange.G8 ? G8Multiplier : value == GRange.G4 ? G4Multiplier : G2Multiplier;
            }
        }

        /// <summary>
        /// Gets or sets the filter bandwidth.
        /// </summary>
        public FilterBandwidth Bandwidth
        {
            get
            {
                return (FilterBandwidth)(ReadRegister(0x14) & 0x07);
            }
            set
            {
                if (value != Bandwidth)
                {
                    byte val = ReadRegister(0x14);//xxx Range(2) Bandwidth(3)
                    val &= (byte)(0xF8 | (byte)value);
                    WriteRegister(0x14, val);

                    switch (value)
                    {
                        case FilterBandwidth.Hz25:
                            bandwidthInHz = 25;
                            break;
                        case FilterBandwidth.Hz50:
                            bandwidthInHz = 50;
                            break;
                        case FilterBandwidth.Hz100:
                            bandwidthInHz = 100;
                            break;
                        case FilterBandwidth.Hz190:
                            bandwidthInHz = 190;
                            break;
                        case FilterBandwidth.Hz375:
                            bandwidthInHz = 375;
                            break;
                        case FilterBandwidth.Hz750:
                            bandwidthInHz = 750;
                            break;
                        default://case FilterBandwidth.Hz1500:
                            bandwidthInHz = 1500;
                            break;
                    }
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether or not an event should be raised when all 3 axes drop below the LowGThreshold.
        /// </summary>
        public bool IsLowGThresholdEnabled
        {
            get
            {
                return (ReadRegister(0x0B) & (byte)0x01) > 0;
            }
            set
            {
                WriteRegister(0x0B, (byte)((ReadRegister(0x0B) & 0xFE) | (byte)(value ? 0x01 : 0x00)));
                RefreshAreInterruptsEnabled();
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether or not an event should be raised when any of axes exceeds the HighGThreshold.
        /// </summary>
        public bool IsHighGThresholdEnabled
        {
            get
            {
                return (ReadRegister(0x0B) & (byte)0x10) > 0;
            }
            set
            {
                WriteRegister(0x0B, (byte)((ReadRegister(0x0B) & 0xFD) | (byte)(value ? 0x10 : 0x00)));
                RefreshAreInterruptsEnabled();
            }
        }

        /// <summary>
        /// Gets or sets the Low-G Threshold in g's.
        /// </summary>
        public float LowGThreshold
        {
            get
            {
                return lowGThreshold;
            }
            set
            {
                ValidateThresholdValue(value);
                lowGThreshold = value;
                WriteRegister(0x0C, ConvertThresholdFromGs(value));
            }
        }

        /// <summary>
        /// Gets or sets the duration, in ms, of any axis being below the Low-G threshold before an event is raised (if IsLowGThresholdEnabled.)
        /// </summary>
        public byte LowGDuration
        {
            get
            {
                return ReadRegister(0x0D);
            }
            set
            {
                WriteRegister(0x0D, value);
            }
        }

        /// <summary>
        /// Gets or sets the duration, in ms, of any axis being below the Low-G threshold before an event is raised (if IsHighGThresholdEnabled.)
        /// </summary>
        public byte HighGDuration
        {
            get
            {
                return ReadRegister(0x0F);
            }
            set
            {
                WriteRegister(0x0F, value);
            }
        }

        /// <summary>
        /// Gets or sets the High-G Threshold in g's
        /// </summary>
        public float HighGThreshold
        {
            get
            {
                return highGThreshold;
            }
            set
            {
                ValidateThresholdValue(value);
                highGThreshold = value;
                WriteRegister(0x0C, ConvertThresholdFromGs(value));
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether or not an event should be raised for any motion exceeding the AnyMotionThreshold.
        /// </summary>
        public bool IsAnyMotionThresholdEnabled
        {
            get
            {
                return (ReadRegister(0x0B) & (byte)0x40) > 0;
            }
            set
            {
                if (value != IsAnyMotionThresholdEnabled)
                {
                    if (value)
                    {
                        WriteRegister(0x15, (byte)(ReadRegister(0x15) | 0x40));//Enable Advanced Interrupts
                        WriteRegister(0x0B, (byte)(ReadRegister(0x0B) | 0x40));//Enable Any Motion Interrupt
                    }
                    else
                    {
                        WriteRegister(0x15, (byte)(ReadRegister(0x15) | 0xBF));//Disable Advanced Interrupts
                        WriteRegister(0x0B, (byte)(ReadRegister(0x0B) | 0xBF));//Disable Any Motion Interrupt
                    }
                    RefreshAreInterruptsEnabled();
                }
            }
        }

        /// <summary>
        /// Gets or sets the Any Motion Threshold in g's.
        /// </summary>
        public float AnyMotionThreshold
        {
            get
            {
                return anyMotionThreshold;
            }
            set
            {
                ValidateThresholdValue(value);
                anyMotionThreshold = value;
                WriteRegister(0x10, ConvertThresholdFromGs(value));
            }
        }

        /// <summary>
        /// Gets or sets the Any Motion Duration; this is in number of (consecutive) samples and the actual time will depend on bandwidth.  A time representation of the duration may be
        /// obtained through the AnyMotionDurationTime property.
        /// </summary>
        public AnyMotionDuration AnyMotionDuration
        {
            get
            {
                return (AnyMotionDuration)(ReadRegister(0x11) >> 6);
            }
            set
            {
                WriteRegister(0x11, (byte)((ReadRegister(0x11) & 0x3F) | ((byte)value << 6)));
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public TimeSpan AnyMotionDurationTime
        {
            get
            {
                return TimeSpan.FromTicks((long)((3.0f / (2.0f * (float)bandwidthInHz)) * TimeSpan.TicksPerSecond));
            }
        }
        #endregion Properties

        #region Methods
        /// <summary>
        /// Perform a soft reset of the smb380 - this is effectively "restore defaults" for all settings.
        /// </summary>
        public void SoftReset()
        {
            WriteRegister(0x0A, (byte)(ReadRegister(0x0A) | 0x02));
        }
        #endregion Methods

        #region Private Helpers
        private void RefreshAreInterruptsEnabled()
        {
            bool newAreInterruptsEnabled = IsHighGThresholdEnabled || IsLowGThresholdEnabled || IsAnyMotionThresholdEnabled;
            if (areInterruptsEnabled != newAreInterruptsEnabled)
            {
                if (areInterruptsEnabled = newAreInterruptsEnabled)
                {
                    WriteRegister(0x15, (byte)(ReadRegister(0x15) | 0x10));//Enable latched interrupts
                    interruptPort.EnableInterrupt();
                }
                else
                {
                    interruptPort.DisableInterrupt();
                    WriteRegister(0x15, (byte)(ReadRegister(0x15) & 0xEF));//Disable latched interrupts
                    WriteRegister(0x0A, (byte)(ReadRegister(0x0A) & 0xBF));//Reset any current interrupt
                }
            }
        }

        private byte ReadRegister(byte address)
        {
            write[0] = (byte)(0x80 | address);
            write[1] = 0xFF;
            spi.WriteRead(write, read);
            return read[1];
        }

        private void WriteRegister(byte address, byte value)
        {
            write[0] = address;
            write[1] = value;
            spi.WriteRead(write, read);
        }

        private float ExtractAcceleration(short lsb, short msb)
        {
            return (float)(((((short)((msb << 2) | (lsb >> 6))) << 4) >> 4) / 512.0f) * gMultiplier;
        }

        private void ValidateThresholdValue(float value)
        {
            GRange range = Range;
            if (value < 0)
            {
                throw new ArgumentOutOfRangeException("value", "Threshold values must be positive; the absolute value of acceleration is used for threshold comparisons");
            }
            else if (value > 8.0f)
            {
                throw new ArgumentOutOfRangeException("value", "Threshold must be less than 8.0g's and greater than -8.0g's");
            }
            else if (range != GRange.G8 && value > 4.0f)
            {
                throw new ArgumentOutOfRangeException("value", "Threshold must be less than 4.0g's and greater than -4.0g's when Range is not G8");
            }
            else if (range == GRange.G2 && value > 2.0f)
            {
                throw new ArgumentOutOfRangeException("value", "Threshold must be less than 2.0g's and greater than -2.0g's when Range is G2");
            }
        }

        private byte ConvertThresholdFromGs(float value)
        {
            return (byte)((255.0f * value) / gMultiplier);
        }
        #endregion
    }

I’ve set my range to +/- 2g and I still get a good 1g on my z-axis and -.5 on my x. The y never strays more than .002g’s from 0 on a 25Hz bandwidth setting. I also don’t seem to get anything after resetting memory until I shake the thing really hard…which seems weird…though my years with Nintendo have taught me that occasionally you have to weird things with technology to make them work right :stuck_out_tongue:

[quote]Could someone double-check this driver?

[/quote]

I guess not…

Most members are not going to put in the hours necessary to get the specifications for the device, read them, and then review your code.

You will get responses if you ask a specific question.

I thought the bottom of the post was specific about the parts I’d like others who have the device to check but…

I’ve set my range to +/- 2g and I still get a solid 1g on my z-axis and -.5 on my x. The y never strays more than .002g’s from 0 on a 25Hz bandwidth setting. Is there anything in this driver that would cause that?