Skip to content

Commit

Permalink
[driver] Driver for PAT9125EL motion sensor
Browse files Browse the repository at this point in the history
  • Loading branch information
chris-durand committed Jan 3, 2019
1 parent 5176b1b commit d1b76f8
Show file tree
Hide file tree
Showing 5 changed files with 450 additions and 0 deletions.
152 changes: 152 additions & 0 deletions src/modm/driver/motion/pat9125el.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/*
* Copyright (c) 2018, Christopher Durand
*
* This file is part of the modm project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// ----------------------------------------------------------------------------

#ifndef MODM_PAT9125EL_HPP
#define MODM_PAT9125EL_HPP

#include <array>
#include <type_traits>
#include <modm/driver/motion/pat9125el_transport.hpp>
#include <modm/math/geometry/vector2.hpp>

namespace modm
{

/// @ingroup modm_driver_pat9125el
struct pat9125el
{
enum class Register : uint8_t
{
ProductId1 = 0x00,
ProductId2 = 0x01,
MotionStatus = 0x02,
DeltaXLow = 0x03,
DeltaYLow = 0x04,
OperationMode = 0x05,
Configuration = 0x06,
WriteProtect = 0x09,
Sleep1 = 0x0A,
Sleep2 = 0x0B,
ResolutionX = 0x0D,
ResolutionY = 0x0E,
DeltaXYHigh = 0x12,
ShutterTime = 0x14,
AvgBrightness = 0x17,
Orientation = 0x19
};

enum class MotionStatus : uint8_t
{
DataAvailable = (1 << 7)
};
MODM_FLAGS8(MotionStatus);

enum class WriteProtect : uint8_t
{
Enabled = 0,
Disabled = 0x5A
};
MODM_FLAGS8(WriteProtect);

static constexpr uint8_t ProductId1 = 0x31;

using Motion2D = modm::Vector<int16_t, 2>;
};

/**
* Driver for PAT9125EL motion sensor. Currently only the I2C version
* is supported.
*
* @tparam Transport Either I2C or SPi transport layer
* @see Pat9125elI2cTransport
*
* @tparam IntPin Optional interrupt pin. If pin is not connected, set to void.
*
* @todo implement SPI transport layer
* @todo add sensor sleep modes
*
* @author Christopher Durand
* @ingroup modm_driver_pat9125el
*/
template<typename Transport, typename IntPin = void>
class Pat9125el : public pat9125el, public Transport
{
public:
static constexpr bool UseInterruptPin = !std::is_void_v<IntPin>;

/**
* @tparam TransportArgs Arguments forwarded to transport layer
* @see Pat9125elI2cTransport
*/
template<typename... TransportArgs>
Pat9125el(TransportArgs&&... args);

/**
* Configure sensor resolution
*
* @param xResolution x resolution in unit of 5 counts per inch
* @param yResolution y resolution in unit of 5 counts per inch
*/
modm::ResumableResult<bool>
configure(uint8_t xResolution = 0x14, uint8_t yResolution = 0x14);

/**
* Check if the device is available
*/
modm::ResumableResult<bool>
ping();

/**
* Returns the last measured movement data read from the device
*/
Motion2D
getData() const;

/**
* Read movement measurement from the device
*/
modm::ResumableResult<bool>
readData();

/**
* Check if non-zero movement data has been read form the device
*/
bool
hasMoved() const;

void
resetMoved();

private:
modm::ResumableResult<bool>
writeRegister(Register reg, uint8_t data);

modm::ResumableResult<bool>
readRegister(Register reg, uint8_t& data);

modm::ResumableResult<bool>
readRegister(Register reg, uint8_t* data, size_t size);

void
updateData();

Motion2D data = {};
bool moved = false;
uint8_t status = 0;
bool success = true;
std::array<uint8_t, 3> readBuffer = {};
};

}

#include "pat9125el_impl.hpp"

#endif // MODM_PAT9125EL_HPP
33 changes: 33 additions & 0 deletions src/modm/driver/motion/pat9125el.lb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (c) 2018, Christopher Durand
#
# This file is part of the modm project.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# -----------------------------------------------------------------------------


def init(module):
module.parent = "driver"
module.name = "pat9125el"
module.description = "Driver for PAT9125EL motion sensor"

def prepare(module, options):
module.depends(
":architecture:delay",
":architecture:spi",
":architecture:i2c.device",
":math:geometry",
":debug")
return True

def build(env):
env.outbasepath = "modm/src/modm/driver/motion"
env.copy("pat9125el.hpp")
env.copy("pat9125el_impl.hpp")
env.copy("pat9125el_transport.hpp")
env.copy("pat9125el_transport_impl.hpp")
154 changes: 154 additions & 0 deletions src/modm/driver/motion/pat9125el_impl.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*
* Copyright (c) 2018, Christopher Durand
*
* This file is part of the modm project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// ----------------------------------------------------------------------------

#ifndef MODM_PAT9125EL_HPP
# error "Don't include this file directly, use 'pat9125el.hpp' instead!"
#endif

namespace modm
{

template<typename Transport, typename IntPin>
template<typename... TransportParams>
Pat9125el<Transport, IntPin>::Pat9125el(TransportParams&&... params)
: Transport(std::forward<TransportParams>(params)...)
{
}

template<typename Transport, typename IntPin>
modm::ResumableResult<bool>
Pat9125el<Transport, IntPin>::configure(uint8_t xResolution, uint8_t yResolution)
{
RF_BEGIN();

success = RF_CALL(writeRegister(Register::WriteProtect, uint8_t(WriteProtect::Disabled)));
if(!success) {
RF_RETURN(false);
}

success = RF_CALL(writeRegister(Register::ResolutionX, xResolution));
if(!success) {
RF_RETURN(false);
}

success = RF_CALL(writeRegister(Register::ResolutionY, yResolution));
if(!success) {
RF_RETURN(false);
}

RF_END_RETURN_CALL(writeRegister(Register::WriteProtect, uint8_t(WriteProtect::Enabled)));
}

template<typename Transport, typename IntPin>
modm::ResumableResult<bool>
Pat9125el<Transport, IntPin>::ping()
{
RF_BEGIN();
success = RF_CALL(readRegister(Register::ProductId1, readBuffer[0]));
if(!success) {
RF_RETURN(false);
}

RF_END_RETURN(readBuffer[0] == ProductId1);
}

template<typename Transport, typename IntPin>
pat9125el::Motion2D
Pat9125el<Transport, IntPin>::getData() const
{
return data;
}

template<typename Transport, typename IntPin>
modm::ResumableResult<bool>
Pat9125el<Transport, IntPin>::readData()
{
RF_BEGIN();

if constexpr(UseInterruptPin) {
if(IntPin::read()) {
RF_RETURN(false);
}
}

success = RF_CALL(readRegister(Register::MotionStatus, status));
if(!success) {
RF_RETURN(false);
}

if(MotionStatus_t{status} & MotionStatus::DataAvailable) {
// read x and y low data registers
success = RF_CALL(readRegister(Register::DeltaXLow, &readBuffer[0], 2));
if(!success) {
RF_RETURN(false);
}

// read x/y high data register
success = RF_CALL(readRegister(Register::DeltaXYHigh, &readBuffer[2], 1));
if(!success) {
RF_RETURN(false);
}

updateData();
}

RF_END_RETURN(true);
}

template<typename Transport, typename IntPin>
void
Pat9125el<Transport, IntPin>::updateData()
{
const int16_t x = (readBuffer[0] | ((readBuffer[2] & 0b11110000) << 4));
const int16_t y = (readBuffer[1] | ((readBuffer[2] & 0b00001111) << 8));

// convert 12-bit 2's complement data to 16-bit signed int
data.x = static_cast<int16_t>(x << 4) >> 4;
data.y = static_cast<int16_t>(y << 4) >> 4;
moved = true;
}

template<typename Transport, typename IntPin>
bool
Pat9125el<Transport, IntPin>::hasMoved() const
{
return moved;
}

template<typename Transport, typename IntPin>
void
Pat9125el<Transport, IntPin>::resetMoved()
{
moved = false;
}

template<typename Transport, typename IntPin>
modm::ResumableResult<bool>
Pat9125el<Transport, IntPin>::writeRegister(Register reg, uint8_t data)
{
return this->write(static_cast<uint8_t>(reg), data);
}

template<typename Transport, typename IntPin>
modm::ResumableResult<bool>
Pat9125el<Transport, IntPin>::readRegister(Register reg, uint8_t& data)
{
return this->read(static_cast<uint8_t>(reg), data);
}

template<typename Transport, typename IntPin>
modm::ResumableResult<bool>
Pat9125el<Transport, IntPin>::readRegister(Register reg, uint8_t* data, size_t size)
{
return this->read(static_cast<uint8_t>(reg), data, size);
}

}
53 changes: 53 additions & 0 deletions src/modm/driver/motion/pat9125el_transport.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2018, Christopher Durand
*
* This file is part of the modm project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// ----------------------------------------------------------------------------

#ifndef MODM_PAT9125EL_TRANSPORT_HPP
#define MODM_PAT9125EL_TRANSPORT_HPP

#include <modm/architecture/interface/i2c_device.hpp>
#include <modm/processing/resumable.hpp>
#include <array>

namespace modm
{

/**
* PAT9125EL I2C transport layer
*
* @author Christopher Durand
* @ingroup modm_driver_pat9125el
*/
template<class I2cMaster>
class Pat9125elI2cTransport : public I2cDevice<I2cMaster, 4>
{
protected:
Pat9125elI2cTransport(uint8_t address);

modm::ResumableResult<bool>
write(uint8_t reg, uint8_t value);

modm::ResumableResult<bool>
read(uint8_t reg, uint8_t& value);

modm::ResumableResult<bool>
read(uint8_t reg, uint8_t* buffer, uint8_t length);

private:
std::array<uint8_t, 2> buffer;
};

// TODO: implement SPI transport layer

}

#include "pat9125el_transport_impl.hpp"

#endif // MODM_PAT9125EL_TRANSPORT_HPP
Loading

0 comments on commit d1b76f8

Please sign in to comment.