2.03.2015

Akcelerometr LIS302DL

Jak już mamy podpięty bluetooth, to aż się prosi, aby wysyłać przez port com jakieś ciekawe dane. Tylko jakie? Na płytce STM32F4Discovery znajduje się akcelerometr LIS302DL. Dzięki niemu możemy uzyskać dane np. o położeniu płytki w przestrzeni (pochylenie i przekręcenie). Wizualizacja takich danych może być bardzo interesującym doświadczeniem.
LIS302DL .NET Micro framework
Ale od początku. Akcelerometr mierzy przyspieszenie. Taki akcelerometr można zobrazować sobie jako kulkę, pośrodku pudełka, zawieszoną z każdej strony na sprężynach. Z akcelerometru otrzymujemy informację o ugięciu sprężyn w osiach x, y i z. Jak to wygląda na obrazkach można pooglądać tutaj: The Accelerometer Tutorial. Na płytce STM32F4Discovery akcelerometr LIS302DL podłączony jest do portu SPI1. Wybranie akcelerometru do komunikacji (chipselect) następuje poprzez pin PE3.

Teraz kot. Najpierw kilka funkcji pomocniczych. Dzięki nim komunikacja z akcelerometrem będzie bardzo prosta.

private byte[] Read(byte startAddress, byte count)
{
    startAddress |= 0x80;
    if (count > 1)
        startAddress |= 0x40;

    var writeBuffer = new byte[1 + count];
    writeBuffer[0] = startAddress;

    var readBuffer = new byte[1 + count];
    _spi.WriteRead(writeBuffer, readBuffer);

    byte[] result = Utility.ExtractRangeFromArray(readBuffer, 1, count);
    return result;
}

private void Write(byte address, byte value)
{
    byte[] writeBuffer = { address, value};
    _spi.Write(writeBuffer);
}

Funkcja Read służy do odczytu danych ze wskazanego rejestru akcelerometru, a Write do zapisu. W funkcji Read zastosowałem pewną właściwość portu SPI do odczytu kolejnych wartości. Cały pomysł sprowadza się do specjalnego przygotowania tablicy writeBuffer. Na pierwszej pozycji tablicy występuje adres rejestru, natomiast na kolejnych pozycjach są zera. Pozycji z zerami jest tyle, ile chcemy odczytać kolejnych bajtów. Taka technika przyda się np. podczas odczytu wartości z rejestrów x, y i z. Zamiast odczytywać je pojedynczo, za jednym razem odczytamy wszystkie, ponieważ rejestry te występują po sobie. Tak więc klasa do obsługi akcelerometru (bez ww funkcji) będzie wyglądała tak:
public class Lis302Dl : IDisposable
{
    private readonly SPI _spi;
    private double _currentSensitivity;

    private const byte WhoAmiReg = 0x0F;
    private const byte CtrlReg1 = 0x20;
    private const byte CtrlReg2 = 0x21;
    private const byte OutXReg = 0x29;

    public enum Scale { Full2K3, Full9K2 }

    public Lis302Dl(SPI.SPI_module spiModule, Cpu.Pin chipSelect)
    {
        var spiCfg = new SPI.Configuration(chipSelect, false,
                                            0, 0, //5,8
                                            true, true,
                                            5000, spiModule);

        _spi = new SPI(spiCfg);

        byte[] whoAmI = Read(WhoAmiReg, 1);
        if(whoAmI[0] != 0x3B)
            throw new InvalidOperationException("LIS302DL not available");

        Write(CtrlReg1, 0x47);
        _currentSensitivity = ToSensitivity(Scale.Full2K3);
    }

    private double ToSensitivity(Scale scale)
    {
        return scale == Scale.Full2K3 ? 0.018 : 0.072;
    }

    public void GetRaw(out sbyte x, out sbyte y, out sbyte z)
    {
        byte[] register = Read(OutXReg, 5);
        x = (sbyte)register[0];
        y = (sbyte)register[2];
        z = (sbyte)register[4];
    }

    public void GetAcc(out double x, out double y, out double z)
    {
        byte[] register = Read(OutXReg, 5);
        x = _currentSensitivity*(sbyte) register[0];
        y = _currentSensitivity*(sbyte) register[2];
        z = _currentSensitivity*(sbyte) register[4];
    }

    public void Dispose()
    {
        _spi.Dispose();
    }
}

Teraz trzeba tylko przygotować krótki programik główny, który pobierze dane z akcelerometru i wyśle na port COM2. Przyspieszenie wysyłane jest przetworzone na m/s^2. Wysyłane są od razu 3 wartości: x, y i z rozdzielone średnikiem.

public static void Main()
{
    const double g = 9.80665;

    var serial = new SerialPort("COM2");
    serial.Open();

    var mems = new Lis302Dl(Stm32F4Discovery.SpiDevices.SPI1,
                            Stm32F4Discovery.Pins.PE3);
    for (;;)
    {
        double x, y, z;
        mems.GetAcc(out x, out y, out z);
                
        x = x*g;
        y = y*g;
        z = z*g;

        string sendStr = x.ToString("F3") + ";" 
                         + y.ToString("F3") + ";" 
                         + z.ToString("F3") + "\r\n";

        byte[] sendBuffer = Encoding.UTF8.GetBytes(sendStr);
        serial.Write(sendBuffer, 0, sendBuffer.Length);
        Thread.Sleep(50);
    }
}

Pozostała wizualizacja danych. Świetnie się do tego celu nadaje program Processing. Jest bardzo prosty w obsłudze i nauce, a możliwości ma przeogromne. Na przykład takim programikiem: ProcessingPlot2D można wyrysować wartości x, y i z na wykresie:


Można też spróbować sił w 3D. Takim programikiem: ProcessingPlot3D uzyskamy coś takiego:


Cały kot programu: DemoLIS302DL.