Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ (drivers): Add CoreDACExpander #828

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions drivers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,6 @@ add_subdirectory(${DRIVERS_DIR}/FastLED)

# Touch drivers
add_subdirectory(${DRIVERS_DIR}/CoreIOExpander)
MMyster marked this conversation as resolved.
Show resolved Hide resolved
add_subdirectory(${DRIVERS_DIR}/CoreDACTouch)


28 changes: 28 additions & 0 deletions drivers/CoreDACTouch/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Leka - LekaOS
# Copyright 2022 APF France handicap
# SPDX-License-Identifier: Apache-2.0

add_library(CoreDACTouch STATIC)

target_include_directories(CoreDACTouch
PUBLIC
include
)

target_sources(CoreDACTouch
PRIVATE
source/CoreDACTouch.cpp
)

target_link_libraries(CoreDACTouch
mbed-os
CoreI2C
)

if (${CMAKE_PROJECT_NAME} STREQUAL "LekaOSUnitTests")

leka_unit_tests_sources(
tests/CoreDACTouch_test.cpp
)

endif()
36 changes: 36 additions & 0 deletions drivers/CoreDACTouch/include/CoreDACTouch.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Leka - LekaOS
// Copyright 2022 APF France handicap
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include <array>

#include "external/MCP4728.h"
#include "interface/drivers/DACTouch.h"
#include "interface/drivers/I2C.h"

namespace leka {

class CoreDACTouchMCP4728 : public interface::DACTouch
{
public:
CoreDACTouchMCP4728(interface::I2C &i2c, uint8_t address) : _i2c(i2c), _address(address) {};

void writeToAllInputRegister(std::array<uint8_t, 2> value) final;
void writeToSpecificInputRegister(uint8_t channel, std::array<uint8_t, 2> value) final;
void writeToMemoryRegisterUntilChannelD(uint8_t starting_channel, std::array<uint8_t, 2> value) final;
void writeToSpecificMemoryRegister(uint8_t channel, std::array<uint8_t, 2> value) final;

void writeVoltageReference(uint8_t voltageReference) final;
void writePowerMode(uint8_t powerMode) final;
void writeGain(uint8_t gain) final;

void readMemory(std::array<uint8_t, mcp4728::command::readMemory::size> &value) final;

private:
interface::I2C &_i2c;
uint8_t _address;
};

} // namespace leka
112 changes: 112 additions & 0 deletions drivers/CoreDACTouch/include/external/MCP4728.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Leka - LekaOS
// Copyright 2022 APF France handicap
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include "cstdint"

namespace leka::mcp4728 {

namespace command {

namespace mask {
inline constexpr auto writeFastMode = uint8_t {0xC0};
inline constexpr auto writeInputAndMemoryRegisters = uint8_t {0xF8};
inline constexpr auto writeSelectionBits = uint8_t {0xF0};

} // namespace mask

inline constexpr auto writeAllInputRegister = uint8_t {0x00};
inline constexpr auto writeSpecificInputRegister = uint8_t {0x40};
inline constexpr auto writeMemoryRegisterUntilChannelD = uint8_t {0x50};
inline constexpr auto writeSpecificMemoryRegister = uint8_t {0x60};
inline constexpr auto writeVoltageReference = uint8_t {0x80};
inline constexpr auto writePowerMode = uint8_t {0xA0};
inline constexpr auto writeGain = uint8_t {0xC0};

namespace readMemory {

inline constexpr auto size = uint8_t {24};
}

} // namespace command

namespace channel {

inline constexpr auto A = uint8_t {0x00};
inline constexpr auto B = uint8_t {0x01};
inline constexpr auto C = uint8_t {0x02};
inline constexpr auto D = uint8_t {0x03};

} // namespace channel

namespace data {

namespace voltage_reference {

inline constexpr auto Vdd = uint8_t {0x00};
namespace internal {
inline constexpr auto channel_A = uint8_t {0x08};
inline constexpr auto channel_B = uint8_t {0x04};
inline constexpr auto channel_C = uint8_t {0x02};
inline constexpr auto channel_D = uint8_t {0x01};
inline constexpr auto all_channel = uint8_t {0x0f};

} // namespace internal

} // namespace voltage_reference

namespace power_mode {

inline constexpr auto normal = uint8_t {0x00};

namespace channel_a {
inline constexpr auto normal = uint8_t {0x00};
inline constexpr auto powerDown1K = uint8_t {0x40};
inline constexpr auto powerDown100K = uint8_t {0x80};
inline constexpr auto powerDown500K = uint8_t {0xC0};
} // namespace channel_a

namespace channel_b {
inline constexpr auto normal = uint8_t {0x00};
inline constexpr auto powerDown1K = uint8_t {0x10};
inline constexpr auto powerDown100K = uint8_t {0x20};
inline constexpr auto powerDown500K = uint8_t {0x30};
} // namespace channel_b

namespace channel_c {
inline constexpr auto normal = uint8_t {0x00};
inline constexpr auto powerDown1K = uint8_t {0x04};
inline constexpr auto powerDown100K = uint8_t {0x08};
inline constexpr auto powerDown500K = uint8_t {0x0C};
} // namespace channel_c

namespace channel_d {
inline constexpr auto normal = uint8_t {0x00};
inline constexpr auto powerDown1K = uint8_t {0x01};
inline constexpr auto powerDown100K = uint8_t {0x02};
inline constexpr auto powerDown500K = uint8_t {0x03};
} // namespace channel_d

} // namespace power_mode

namespace gain {

namespace x1 {
inline constexpr auto all = uint8_t {0x00};
}

namespace x2 {
inline constexpr auto channel_A = uint8_t {0x08};
inline constexpr auto channel_B = uint8_t {0x04};
inline constexpr auto channel_C = uint8_t {0x02};
inline constexpr auto channel_D = uint8_t {0x01};
inline constexpr auto all = uint8_t {0x0f};
} // namespace x2

} // namespace gain

} // namespace data

} // namespace leka::mcp4728
82 changes: 82 additions & 0 deletions drivers/CoreDACTouch/source/CoreDACTouch.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Leka - LekaOS
// Copyright 2022 APF France handicap
// SPDX-License-Identifier: Apache-2.0

#include "CoreDACTouch.h"
#include <array>

#include "MemoryUtils.h"

using namespace leka;

void CoreDACTouchMCP4728::writeToAllInputRegister(std::array<uint8_t, 2> value)
MMyster marked this conversation as resolved.
Show resolved Hide resolved
{
auto command = std::array<uint8_t, 2> {};
command.at(0) =
static_cast<uint8_t>((mcp4728::command::mask::writeFastMode & mcp4728::command::writeAllInputRegister) |
(~mcp4728::command::mask::writeFastMode & value.at(0)));
command.at(1) = value.at(1);
_i2c.write(_address, command.data(), command.size(), false);
}

void CoreDACTouchMCP4728::writeToSpecificInputRegister(uint8_t channel, std::array<uint8_t, 2> value)
{
auto command = std::array<uint8_t, 3> {};
command.at(0) = static_cast<uint8_t>(
(mcp4728::command::mask::writeInputAndMemoryRegisters & mcp4728::command::writeSpecificInputRegister) |
(~mcp4728::command::mask::writeInputAndMemoryRegisters & ((0x03 & channel) << 1)));
std::copy(value.begin(), value.end(), command.begin() + 1);
_i2c.write(_address, command.data(), command.size(), false);
}

void CoreDACTouchMCP4728::writeToMemoryRegisterUntilChannelD(uint8_t starting_channel, std::array<uint8_t, 2> value)
{
auto command = std::array<uint8_t, 3> {};
command.at(0) = static_cast<uint8_t>(
(mcp4728::command::mask::writeInputAndMemoryRegisters & mcp4728::command::writeMemoryRegisterUntilChannelD) |
(~mcp4728::command::mask::writeInputAndMemoryRegisters & ((0x03 & starting_channel) << 1)));
std::copy(value.begin(), value.end(), command.begin() + 1);
_i2c.write(_address, command.data(), command.size(), false);
}

void CoreDACTouchMCP4728::writeToSpecificMemoryRegister(uint8_t channel, std::array<uint8_t, 2> value)
{
auto command = std::array<uint8_t, 3> {};
command.at(0) = static_cast<uint8_t>(
(mcp4728::command::mask::writeInputAndMemoryRegisters & mcp4728::command::writeSpecificMemoryRegister) |
(~mcp4728::command::mask::writeInputAndMemoryRegisters & ((0x03 & channel) << 1)));
std::copy(value.begin(), value.end(), command.begin() + 1);

_i2c.write(_address, command.data(), command.size(), false);
}

void CoreDACTouchMCP4728::writeVoltageReference(uint8_t voltageReference)
{
auto command =
static_cast<uint8_t>((mcp4728::command::mask::writeSelectionBits & mcp4728::command::writeVoltageReference) |
(~mcp4728::command::mask::writeSelectionBits & voltageReference));
_i2c.write(_address, &command, 1, false);
}

void CoreDACTouchMCP4728::writePowerMode(uint8_t powerMode)
{
auto command = std::array<uint8_t, 2> {};
command.at(0) =
static_cast<uint8_t>((mcp4728::command::mask::writeSelectionBits & mcp4728::command::writePowerMode) |
(~mcp4728::command::mask::writeSelectionBits & (0xF0 & powerMode) >> 4));
command.at(1) = static_cast<uint8_t>((0x0F & powerMode) << 4);

_i2c.write(_address, command.data(), command.size(), false);
}

void CoreDACTouchMCP4728::writeGain(uint8_t gain)
{
auto command = static_cast<uint8_t>((mcp4728::command::mask::writeSelectionBits & mcp4728::command::writeGain) |
(utils::memory::getLowByte(gain)));
_i2c.write(_address, &command, 1, false);
}

void CoreDACTouchMCP4728::readMemory(std::array<uint8_t, mcp4728::command::readMemory::size> &value)
{
_i2c.read(_address, value.data(), value.size(), false);
}
113 changes: 113 additions & 0 deletions drivers/CoreDACTouch/tests/CoreDACTouch_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Leka - LekaOS
// Copyright 2022 APF France handicap
// SPDX-License-Identifier: Apache-2.0

#include "CoreDACTouch.h"

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "mocks/leka/CoreI2C.h"

using namespace leka;

using ::testing::Args;
using ::testing::DoAll;
using ::testing::ElementsAre;
using ::testing::Return;
using ::testing::SetArrayArgument;

class CoreDACTouchTest : public ::testing::Test

{
protected:
// void SetUp() override {}
// void TearDown() override {}

const uint8_t i2c_address {0xC0};
mock::CoreI2C mocki2c;
CoreDACTouchMCP4728 dac {mocki2c, i2c_address};
};

TEST_F(CoreDACTouchTest, initializationDefault)
{
auto new_dac = CoreDACTouchMCP4728 {mocki2c, i2c_address};
ASSERT_NE(&new_dac, nullptr);
}

TEST_F(CoreDACTouchTest, writeToAllInputRegister)
{
auto value_to_write = std::array<uint8_t, 2> {0x0F, 0xA8};

const auto data = ElementsAre(0x0F, 0xA8);
EXPECT_CALL(mocki2c, write).With(Args<1, 2>(data));

dac.writeToAllInputRegister(value_to_write);
}

TEST_F(CoreDACTouchTest, writeToSpecificInputRegister)
{
auto value_to_write = std::array<uint8_t, 2> {0x0F, 0xA8};

const auto data = ElementsAre(0x46, 0x0F, 0xA8);
EXPECT_CALL(mocki2c, write).With(Args<1, 2>(data));

dac.writeToSpecificInputRegister(mcp4728::channel::D, value_to_write);
}

TEST_F(CoreDACTouchTest, writeToMemoryRegisterUntilChannelD)
{
auto value_to_write = std::array<uint8_t, 2> {0xFF, 0xAA};

const auto data = ElementsAre(0x50, 0xFF, 0xAA);
EXPECT_CALL(mocki2c, write).With(Args<1, 2>(data));

dac.writeToMemoryRegisterUntilChannelD(mcp4728::channel::A, value_to_write);
}

TEST_F(CoreDACTouchTest, writeToSpecificMemoryRegister)
{
auto value_to_write = std::array<uint8_t, 2> {0xFF, 0xAA};

const auto data = ElementsAre(0x62, 0xFF, 0xAA);
EXPECT_CALL(mocki2c, write).With(Args<1, 2>(data));

dac.writeToSpecificMemoryRegister(mcp4728::channel::B, value_to_write);
}

TEST_F(CoreDACTouchTest, readMemory)
{
auto data = std::array<uint8_t, 24> {};
auto expected_data = std::array<uint8_t, 24> {0x01, 0x02, 0x03, 0x04};

EXPECT_CALL(mocki2c, read)
.WillOnce(DoAll(SetArrayArgument<1>(begin(expected_data), end(expected_data)), Return(0)));

dac.readMemory(data);

ASSERT_EQ(data, expected_data);
}

TEST_F(CoreDACTouchTest, writeVoltageReference)
{
const auto data = ElementsAre(0x80);
EXPECT_CALL(mocki2c, write).With(Args<1, 2>(data));

dac.writeVoltageReference(mcp4728::data::voltage_reference::Vdd);
}

TEST_F(CoreDACTouchTest, writePowerMode)
{
const auto data = ElementsAre(0xAC, 0x40);
EXPECT_CALL(mocki2c, write).With(Args<1, 2>(data));

dac.writePowerMode(mcp4728::data::power_mode::channel_a::powerDown500K |
mcp4728::data::power_mode::channel_c::powerDown1K);
}

TEST_F(CoreDACTouchTest, writeGain)
{
const auto data = ElementsAre(0xCF);
EXPECT_CALL(mocki2c, write).With(Args<1, 2>(data));

dac.writeGain(mcp4728::data::gain::x2::all);
}
Loading