Skip to content

Commit

Permalink
[rp2040] I2C implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
andryblack committed Feb 28, 2022
1 parent 72d35c7 commit 23b4c06
Show file tree
Hide file tree
Showing 8 changed files with 552 additions and 4 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
<td align="center">○</td>
<td align="center">○</td>
<td align="center">○</td>
<td align="center"></td>
<td align="center"></td>
<td align="center">✅</td>
<td align="center">✅</td>
<td align="center">✅</td>
Expand Down
12 changes: 9 additions & 3 deletions src/modm/platform/gpio/rp/base.hpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ struct Gpio
mA_8 = 2,
mA_12 = 3,
};
enum class
SlewRate : uint8_t
{
Slow = 0,
Fast = 1
};

enum class
Port
Expand Down Expand Up @@ -115,15 +121,15 @@ struct Gpio::PortRegs<Gpio::Port::{{ port | capitalize }}>
{
hw_write_masked(
&pads{{(pads_names[port] or port) | lower}}_hw->io[pin],
strength << PADS_BANK0_GPIO0_DRIVE_LSB,
uint32_t(strength) << PADS_BANK0_GPIO0_DRIVE_LSB,
PADS_BANK0_GPIO0_DRIVE_BITS
);
}
static void set_slewfast(uint8_t pin, bool fast)
static void set_slewrate(uint8_t pin, uint8_t rate)
{
hw_write_masked(
&pads{{(pads_names[port] or port) | lower}}_hw->io[pin],
(fast?1:0) << PADS_BANK0_GPIO0_SLEWFAST_LSB,
uint32_t(rate) << PADS_BANK0_GPIO0_SLEWFAST_LSB,
PADS_BANK0_GPIO0_SLEWFAST_BITS
);
}
Expand Down
5 changes: 5 additions & 0 deletions src/modm/platform/gpio/rp/set.hpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ public:
(PortRegs<Gpios::port>::set_pue_pde(Gpios::pin, type==InputType::PullUp, type==InputType::PullDown), ...);
}

static void setSlewRate(SlewRate rate)
{
(PortRegs<Gpios::port>::set_slewrate(Gpios::pin, uint8_t(rate)), ...);
}

static void set()
{
%% for port, id in ports.items()
Expand Down
1 change: 1 addition & 0 deletions src/modm/platform/gpio/rp/static.hpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public:
static void setInput() { PinSet::setInput(); }
static void setInput(InputType type) { PinSet::setInput(type); }
static void configure(InputType type) { PinSet::configure(type); }
static void setSlewRate(SlewRate rate) { PinSet::setSlewRate(rate); }

static bool read() { return Regs::sio_in() & mask; }

Expand Down
125 changes: 125 additions & 0 deletions src/modm/platform/i2c/rp/i2c_master.cpp.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* Copyright (c) 2022, Andrey Kunitsyn
*
* 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 "i2c_master_{{ id }}.hpp"
#include <modm/platform/core/resets.hpp>


namespace
{
struct SimpleWait
{
using State = uint_fast32_t;
static State start() {
return 100'000;
}
static bool check(State& s) {
return --s == 0;
}
};
}
// ----------------------------------------------------------------------------

void modm::platform::I2cMaster{{ id }}::hwReset()
{
Resets::reset(RESETS_RESET_I2C{{ id }}_BITS);
}

void modm::platform::I2cMaster{{ id }}::hwUnReset()
{
Resets::unresetWait(RESETS_RESET_I2C{{ id }}_BITS);
}

void
modm::platform::I2cMaster{{ id }}::reset()
{
errorState = Error::SoftwareReset;
restartOnNext = false;
}

bool
modm::platform::I2cMaster{{ id }}::start(I2cTransaction *transaction, ConfigurationHandler handler)
{
if (!transaction)
{
return true;
}
if (not transaction->attaching())
{
transaction->detaching(modm::I2c::DetachCause::FailedToAttach);
// return false; // done at the end of the function
}
else
{
// reset error state
errorState = Error::NoError;
// call the configuration function
if (handler and configuration != handler) {
configuration = handler;
configuration();
}

// ask the transaction object about address and next operation.
auto starting = transaction->starting();
uint8_t address = (starting.address & 0xfe) >> 1;

hw().enable = 0;
hw().tar = address;
hw().enable = 1;

auto nextOperation = static_cast<modm::I2c::Operation>(starting.next);

do
{
switch (nextOperation)
{
case modm::I2c::Operation::Write:
{
auto writing = transaction->writing();
// what next?
nextOperation = static_cast<modm::I2c::Operation>(writing.next);
doWrite<SimpleWait>(writing.buffer,writing.length,nextOperation!=I2c::Operation::Stop);
} break;

case I2c::Operation::Read:
{
auto reading = transaction->reading();
nextOperation = static_cast<modm::I2c::Operation>(reading.next);
doRead<SimpleWait>(reading.buffer,reading.length,nextOperation!=I2c::Operation::Stop);
break;
}

case I2c::Operation::Restart:
starting = transaction->starting();
nextOperation = static_cast<modm::I2c::Operation>(starting.next);
break;

default:
case I2c::Operation::Stop:
transaction->detaching(modm::I2c::DetachCause::NormalStop);
return true;
}
if (errorState != Error::NoError)
{
transaction->detaching(modm::I2c::DetachCause::ErrorCondition);
return true;
}
}
while (true);
}
return false;
}

modm::I2cMaster::Error
modm::platform::I2cMaster{{ id }}::getErrorState()
{
return errorState;
}
106 changes: 106 additions & 0 deletions src/modm/platform/i2c/rp/i2c_master.hpp.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Copyright (c) 2022, Andrey Kunitsyn
*
* 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/.
*/
// ----------------------------------------------------------------------------

#pragma once

#include "../device.hpp"
#include <modm/platform/gpio/connector.hpp>
#include <modm/architecture/interface/i2c_master.hpp>
#include <modm/math/algorithm/prescaler.hpp>
#include <hardware/structs/i2c.h>

namespace modm::platform
{

/**
* I2cMaster implementation of I2C{{ id }} module.
*
* @author Andrey Kunitsyn
* @ingroup modm_platform_i2c modm_platform_i2c_{{id}}
*/
class I2cMaster{{ id }} : public ::modm::I2cMaster
{
public:
static inline i2c_hw_t& hw() { return *i2c{{ id }}_hw; }
public:
template<class... Signals, ResetDevices reset = ResetDevices::Standard>
static void
connect(PullUps pullups = PullUps::External)
{
using Connector = GpioConnector<Peripheral::I2c{{ id }}, Signals...>;
using Scl = typename Connector::template GetSignal<Gpio::Signal::Scl>;
using Sda = typename Connector::template GetSignal<Gpio::Signal::Sda>;
static_assert(sizeof...(Signals) == 2 and
Connector::template IsValid<Scl> and Connector::template IsValid<Sda>,
"I2cMaster{{id}}::connect() requires one Scl and one Sda signal!");
const Gpio::InputType input =
(pullups == PullUps::Internal) ? Gpio::InputType::PullUp : Gpio::InputType::Floating;

Connector::disconnect();
Scl::configure(input);
Scl::setSlewRate(Gpio::SlewRate::Slow);
Sda::configure(input);
Sda::setSlewRate(Gpio::SlewRate::Slow);
if (reset != ResetDevices::NoReset) resetDevices<Scl, uint32_t(reset)>();
Connector::connect();
}

static void hwReset();
static void hwUnReset();

/**
* Set up the I2C module for master operation.
*
* @param rate
* `Standard` or `Fast` or `Fast+`, `High` datarate is not supported
*/
template<class SystemClock, baudrate_t baudrate=kBd(100), percent_t tolerance=pct(5)>
static void
initialize();

template< class SystemClock, baudrate_t baudrate, percent_t tolerance=pct(5)>
static uint32_t
setBaudrate();

// start documentation inherited
static bool
start(I2cTransaction *transaction, ConfigurationHandler handler = nullptr);

static Error
getErrorState();

static void
reset();
// end documentation inherited

template <typename Wait>
static Error transfer(uint8_t addr,const uint8_t* write,size_t writeLen,
uint8_t* read, size_t readLen);

private:
template <typename Wait>
static void doWrite(const uint8_t* write,size_t writeLen,bool nostop);
template <typename Wait>
static void doRead(uint8_t* read,size_t readLen,bool nostop);
static bool isReadAvailable() {
return hw().rxflr;
}
static bool isWriteAvailable() {
constexpr size_t IC_TX_BUFFER_DEPTH = 16;
return IC_TX_BUFFER_DEPTH - hw().txflr;
}
static inline Error errorState{Error::NoError};
static inline I2c::ConfigurationHandler configuration{nullptr};
static inline bool restartOnNext{false};
};

} // namespace modm::platform
#include "i2c_master_impl_{{id}}.hpp"
Loading

0 comments on commit 23b4c06

Please sign in to comment.