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

PCA9548A driver and I2C multiplexed master. #81

Merged
merged 3 commits into from
Dec 3, 2018
Merged
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
136 changes: 136 additions & 0 deletions examples/generic/i2c_multiplex/i2c_multiplex.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* Copyright (c) 2018, Sascha Schade
*
* 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/.
*/
#include <modm/board.hpp>
#include <modm/processing/timer.hpp>
#include <modm/processing/protothread.hpp>
#include <modm/architecture/interface/i2c_device.hpp>
#include <modm/architecture/interface/i2c_multiplexer.hpp>
#include <modm/driver/gpio/pca9548a.hpp>

using MyI2cMaster = modm::platform::I2cMaster1;
using Mpx = modm::Pca9548a<MyI2cMaster>;
using I2cMultiplexer = modm::I2cMultiplexer<MyI2cMaster, Mpx>;


#ifndef MODM_BOARD_HAS_LOGGER
#include <modm/debug.hpp>
// Add logger manually
using LoggerUsart = Usart2;
using LoggerUsartTx = modm::platform::GpioA2;
using LoggerUsartRx = modm::platform::GpioA3;
modm::IODeviceWrapper< LoggerUsart, modm::IOBuffer::BlockIfFull > loggerDevice;

// Set all four logger streams to use the UART
modm::log::Logger modm::log::debug(loggerDevice);
modm::log::Logger modm::log::info(loggerDevice);
modm::log::Logger modm::log::warning(loggerDevice);
modm::log::Logger modm::log::error(loggerDevice);
#endif

#undef MODM_LOG_LEVEL
#define MODM_LOG_LEVEL modm::log::DEBUG


namespace multiplexer
{
I2cMultiplexer i2cMultiplexer;

// Instances for each channel
using Ch0 = I2cMultiplexer::Ch0< i2cMultiplexer >;
using Ch1 = I2cMultiplexer::Ch1< i2cMultiplexer >;
using Ch2 = I2cMultiplexer::Ch2< i2cMultiplexer >;
using Ch3 = I2cMultiplexer::Ch3< i2cMultiplexer >;
using Ch4 = I2cMultiplexer::Ch4< i2cMultiplexer >;
using Ch5 = I2cMultiplexer::Ch5< i2cMultiplexer >;
using Ch6 = I2cMultiplexer::Ch6< i2cMultiplexer >;
using Ch7 = I2cMultiplexer::Ch7< i2cMultiplexer >;
}


class DeviceThread: public modm::pt::Protothread
{
public:
DeviceThread() : dev0(0x29), dev1(0x29), dev2(0x29), dev3(0x29)
{}

bool
update();

private:
modm::ShortTimeout timeout;

// Simple devices which are just pingable.
// Independent of real device. Any I2C device should be pingable at its address.
modm::I2cDevice<multiplexer::Ch1> dev0;
modm::I2cDevice<multiplexer::Ch2> dev1;
modm::I2cDevice<multiplexer::Ch3> dev2;
modm::I2cDevice<multiplexer::Ch7> dev3;
};

bool
DeviceThread::update()
{
PT_BEGIN();

MODM_LOG_DEBUG << MODM_FILE_INFO;
MODM_LOG_DEBUG << "Ping the Devices" << modm::endl;

// ping the devices repeatedly
while(true)
{
MODM_LOG_DEBUG.printf("[dev ] ping0\n");
MODM_LOG_DEBUG.printf("[dev ] ping0 res: %d\n", PT_CALL(dev0.ping()));
MODM_LOG_DEBUG.printf("[dev ] ping1\n");
MODM_LOG_DEBUG.printf("[dev ] ping1 res: %d\n", PT_CALL(dev1.ping()));
MODM_LOG_DEBUG.printf("[dev ] ping2\n");
MODM_LOG_DEBUG.printf("[dev ] ping2 res: %d\n", PT_CALL(dev2.ping()));
MODM_LOG_DEBUG.printf("[dev ] ping3\n");
MODM_LOG_DEBUG.printf("[dev ] ping3 res: %d\n", PT_CALL(dev3.ping()));
// Do again in 1s
this->timeout.restart(1000);
PT_WAIT_UNTIL(this->timeout.isExpired());
}

PT_END();
}

int
main()
{
Board::initialize();

#ifndef MODM_BOARD_HAS_LOGGER
LoggerUsart::connect<LoggerUsartTx::Tx, LoggerUsartRx::Rx>();
LoggerUsart::initialize<Board::systemClock, modm::Uart::Baudrate::B115200>(12);
#endif
strongly-typed marked this conversation as resolved.
Show resolved Hide resolved

modm::platform::I2cMaster1::connect<modm::platform::GpioB7::Sda, modm::platform::GpioB6::Scl>();
modm::platform::I2cMaster1::initialize<Board::systemClock, modm::platform::I2cMaster1::Baudrate::Standard>();

constexpr uint32_t rate = 1; // Hz
constexpr float interval = 1000.0 / rate; // msec
modm::ShortPeriodicTimer heartbeat(interval);

// Main loop
DeviceThread deviceThread;

uint32_t counter(0);
while (true)
{
deviceThread.update();

if (heartbeat.execute()) {
Board::Leds::toggle();
MODM_LOG_INFO << "Loop counter: " << (counter++) << modm::endl;
}
}

return 0;
}
23 changes: 23 additions & 0 deletions examples/generic/i2c_multiplex/project.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version='1.0' encoding='UTF-8'?>
<library>
<extends>../../../src/modm/board/nucleo_f303k8/board.xml</extends>
<options>
<option name=":target">stm32f303k8t</option>
<!-- <option name=":target">stm32f103c8t</option> -->
<option name=":platform:uart:2:buffer.tx">2048</option>
<option name=":platform:uart:2:buffer.rx">2048</option>
<option name=":build:build.path">../../../build/generic/i2c_multiplex</option>
</options>
<modules>
<module>:debug</module>
<module>:platform:gpio</module>
<module>:driver</module>
<module>:driver:pca9548a</module>
<module>:platform:i2c:1</module>
<module>:platform:uart:2</module>
<module>:processing:protothread</module>
<module>:processing:resumable</module>
<module>:processing:timer</module>
<module>:build:scons</module>
</modules>
</library>
2 changes: 1 addition & 1 deletion src/modm/architecture/interface/gpio_expander.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ class GpioExpander
/// Returns the input bits: 0 for low, 1 for high
Pins
getInputs() const;
#endif
#endif // __DOXYGEN__
};

/**
Expand Down
117 changes: 117 additions & 0 deletions src/modm/architecture/interface/i2c_multiplexer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* Copyright (c) 2018, Sascha Schade
*
* 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_I2C_MULTIPLEXER_HPP
#define MODM_I2C_MULTIPLEXER_HPP

#include <modm/processing/resumable.hpp>

namespace modm
{

/**
* Interface of an I2C Multiplexer.
*
* All I2C multiplexer driver should implement this interface.
* @ingroup modm_architecture_i2c_multiplexer
*/
template <
auto &multiplexer,
typename std::remove_reference_t<decltype(multiplexer)>::Channel channel >
class I2cMultiplexerChannel : public modm::I2cMaster
{
public:
static constexpr auto &i2cMultiplexer = multiplexer;

public:
static bool
start(I2cTransaction *transaction, ConfigurationHandler handler = nullptr);
};

/// @ingroup modm_architecture_i2c_multiplexer
struct i2cMultiplexer
{
public:
enum class
Channel : uint8_t
{
Ch0 = 0,
Ch1 = 1,
Ch2 = 2,
Ch3 = 3,
Ch4 = 4,
Ch5 = 5,
Ch6 = 6,
Ch7 = 7
};
}; // struct i2cMultiplexer


/// @ingroup modm_architecture_i2c_multiplexer
template < class I2cMaster, class I2cMultiplexerDevice >
class I2cMultiplexer : public i2cMultiplexer, public modm::I2c
{
public:
/// Constructor, sets address to default of 0x70
I2cMultiplexer();

static bool
start(I2cTransaction *transaction, ConfigurationHandler handler = nullptr)
{
return I2cMaster::start(transaction, handler);
}

public:
/// Alias-templates for simpler use of the Channels
/// @{
template < auto &object >
using Ch0 = I2cMultiplexerChannel< object, Channel::Ch0 >;
template < auto &object >
using Ch1 = I2cMultiplexerChannel< object, Channel::Ch1 >;
template < auto &object >
using Ch2 = I2cMultiplexerChannel< object, Channel::Ch2 >;
template < auto &object >
using Ch3 = I2cMultiplexerChannel< object, Channel::Ch3 >;
template < auto &object >
using Ch4 = I2cMultiplexerChannel< object, Channel::Ch4 >;
template < auto &object >
using Ch5 = I2cMultiplexerChannel< object, Channel::Ch5 >;
template < auto &object >
using Ch6 = I2cMultiplexerChannel< object, Channel::Ch6 >;
template < auto &object >
using Ch7 = I2cMultiplexerChannel< object, Channel::Ch7 >;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to automatically populate object with this? So you can do using Ch0 = object.Ch0;?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No idea, probably @chris-durand comes up with a suggestion. I can live with that code.

/// @}

I2cMultiplexerDevice multiplexerDevice;
};

} // modm namespace

template < class I2cMaster, class I2cMultiplexerDevice >
modm::I2cMultiplexer<I2cMaster, I2cMultiplexerDevice >::I2cMultiplexer()
{
}

template <
auto &multiplexer,
typename std::remove_reference_t<decltype(multiplexer)>::Channel channel >
bool
modm::I2cMultiplexerChannel<multiplexer, channel>::start(modm::I2cTransaction *transaction, ConfigurationHandler handler)
{
// If call to multiplexer failed, return without doing the actual transaction
if (RF_CALL_BLOCKING(multiplexer.multiplexerDevice.setActiveChannel(static_cast<uint8_t>(channel))) == false) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I need to introduce stackful fibers, so async doesn't modify the function signature.

return false;
}

return multiplexer.start(transaction, handler);
}

#endif // MODM_I2C_MULTIPLEXER_HPP
15 changes: 15 additions & 0 deletions src/modm/architecture/module.lb
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,20 @@ class I2cDevice(Module):
env.copy("interface/i2c_device.hpp")
# -----------------------------------------------------------------------------

class I2cMultiplexer(Module):
def init(self, module):
module.name = "i2c.multiplexer"
module.description = "I²C Multiplexer"

def prepare(self, module, options):
module.depends(":architecture:i2c", ":architecture:register", ":processing:resumable", ":math:utils")
return True

def build(self, env):
env.outbasepath = "modm/src/modm/architecture"
env.copy("interface/i2c_multiplexer.hpp")
# -----------------------------------------------------------------------------

class Interrupt(Module):
def init(self, module):
module.name = "interrupt"
Expand Down Expand Up @@ -342,6 +356,7 @@ def prepare(module, options):
module.add_submodule(Heap())
module.add_submodule(I2c())
module.add_submodule(I2cDevice())
module.add_submodule(I2cMultiplexer())
module.add_submodule(Interrupt())
module.add_submodule(Memory())
module.add_submodule(OneWire())
Expand Down
56 changes: 56 additions & 0 deletions src/modm/driver/gpio/pca9548a.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// coding: utf-8
/*
* Copyright (c) 2018 Sascha Schade
*
* 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_PCA9548A_HPP
#define MODM_PCA9548A_HPP

#include <modm/architecture/interface/i2c_device.hpp>

namespace modm
{

/**
* PCA9548A/TCA9548A: 8-channel I2C switch with reset
*
* This bidirectional 8-channel switch can be used to multiplex I2C busses.
*
* @ingroup driver_gpio
*
* @author Sascha Schade
*/
template < typename I2cMaster >
class Pca9548a : public modm::I2cDevice< I2cMaster, 2 >
{
public:
Pca9548a(uint8_t address=0b1110000);

modm::ResumableResult<bool>
setActiveChannel(uint8_t channel);

modm::ResumableResult<bool>
readCommandRegister(uint8_t &command_register);

modm::ResumableResult<bool>
writeCommandRegister(uint8_t command_register);

private:
uint8_t buffer[1];
uint8_t current_command_register = 0;

static constexpr uint8_t CHANNELS = 8;
};

} // modm namespace

#include "pca9548a_impl.hpp"

#endif // MODM_PCA9548A_HPP
Loading