Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 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
33 changes: 33 additions & 0 deletions src/modm/architecture/interface/i2s.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2009-2012, Fabian Greif
* Copyright (c) 2010, Martin Rosekeit
* Copyright (c) 2012-2015, Niklas Hauser
* Copyright (c) 2013, Sascha Schade
* Copyright (c) 2021, Marton Ledneczki
*
* 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_INTERFACE_I2S_HPP
#define MODM_INTERFACE_I2S_HPP

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

namespace modm
{

/// @ingroup modm_architecture_i2s
struct I2s
{
/// The signature of the configuration function.
using ConfigurationHandler = void(*)();
};

} // namespace modm

#endif // MODM_INTERFACE_I2S_HPP
164 changes: 164 additions & 0 deletions src/modm/architecture/interface/i2s_master.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/*
* Copyright (c) 2009-2012, Fabian Greif
* Copyright (c) 2010, Martin Rosekeit
* Copyright (c) 2012-2017, Niklas Hauser
* Copyright (c) 2013, Sascha Schade
* Copyright (c) 2021, Marton Lednczki
*
* 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_INTERFACE_I2S_MASTER_HPP
#define MODM_INTERFACE_I2S_MASTER_HPP

#include <modm/processing/resumable.hpp>
#include "i2s.hpp"

namespace modm
{

/**
* Interface for a I2S Master
*
* @author Marton Ledneczki
* @ingroup modm_architecture_i2s
*/
class I2sMaster : public ::modm::PeripheralDriver, public I2s
{
#ifdef __DOXYGEN__
public:
/**
* Connect GPIOs to the peripheral and configure them.
*
* This configures the CK (serial clock), MCK (master clock),
* SD (serial data) and WS (word select) signals and connects them.
*
* @tparam Signals
* One CK, SD and WS signal are required
* and can be passed out-of-order.
*/
template< class... Signals >
static void
connect();

/**
* Initializes the hardware and sets the baudrate.
*
* @tparam SystemClock
* the currently active system clock
* @tparam baudrate
* the desired baudrate in Hz
* @tparam tolerance
* the allowed relative tolerance for the resulting baudrate
*/
template< class SystemClock, baudrate_t baudrate, percent_t tolerance=5_pct >
static void
initialize();

/// Sets a new data mode.
static void
setDataMode(DataMode mode);

/// Sets a new data order.
static void
setDataOrder(DataOrder order);

/**
* Request access to the spi master within a context.
* You may acquire the spi master multiple times within the same context.
*
* The configuration handler will only be called when aquiring the spi
* master for the first time (if it is not a `nullptr`).
*
* @warning Aquires must be balanced with releases of the **same** context!
* @warning Aquires are persistent even after calling `initialize()`!
*
* @return `0` if another context is using the spi master, otherwise
* `>0` as the number of times this context acquired the master.
*/
static uint8_t
acquire(void *ctx, ConfigurationHandler handler = nullptr);

/**
* Release access to the spi master within a context.
*
* @warning Releases must be balanced with acquires of the **same** context!
* @warning Releases are persistent even after calling `initialize()`!
*
* @return `0` if nothing can be released anymore (for any context)
* `>0` as the number of times this context can still release the master.
*/
static uint8_t
release(void *ctx);

/**
* Swap a single byte and wait for completion.
*
* @param data
* data to be sent
* @return received data
*/
static uint8_t
transferBlocking(uint8_t data);

/**
* Set the data buffers and length with options and starts a transfer.
* This may be hardware accelerated (DMA or Interrupt), but not guaranteed.
*
* @param[in] tx
* pointer to transmit buffer, set to `nullptr` to send dummy bytes
* @param[out] rx
* pointer to receive buffer, set to `nullptr` to discard received bytes
* @param length
* number of bytes to be shifted out
*/
static void
transferBlocking(const uint8_t *tx, uint8_t *rx, std::size_t length);

/**
* Swap a single byte and wait for completion non-blocking!.
*
* You must call this inside a Protothread or Resumable
* using `PT_CALL` or `RF_CALL` respectively.
* @warning These methods differ from Resumables by lacking context protection!
* You must ensure that only one driver is accessing this resumable function
* by using `acquire(ctx)` and `release(ctx)`.
*
* @param data
* data to be sent
* @return received data
*/
static modm::ResumableResult<uint8_t>
transfer(uint8_t data);

/**
* Set the data buffers and length with options and
* starts a non-blocking transfer.
* This may be hardware accelerated (DMA or Interrupt), but not guaranteed.
*
* You must call this inside a Protothread or Resumable
* using `PT_CALL` or `RF_CALL` respectively.
* @warning These methods differ from Resumables by lacking context protection!
* You must ensure that only one driver is accessing this resumable function
* by using `acquire(ctx)` and `release(ctx)`.
*
* @param[in] tx
* pointer to transmit buffer, set to `nullptr` to send dummy bytes
* @param[out] rx
* pointer to receive buffer, set to `nullptr` to discard received bytes
* @param length
* number of bytes to be shifted out
*/
static modm::ResumableResult<void>
transfer(const uint8_t *tx, uint8_t *rx, std::size_t length);
#endif
};

} // namespace modm

#endif // MODM_INTERFACE_SPI_MASTER_HPP
16 changes: 16 additions & 0 deletions src/modm/architecture/module.lb
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,21 @@ class I2cMultiplexer(Module):
env.copy("interface/i2c_multiplexer.hpp")
# -----------------------------------------------------------------------------

class I2s(Module):
def init(self, module):
module.name = "i2s"
module.description = "Inter-IC Sound (I2S)"

def prepare(self, module, options):
module.depends(":processing:resumable")
return True

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

class Interrupt(Module):
def init(self, module):
module.name = "interrupt"
Expand Down Expand Up @@ -381,6 +396,7 @@ def prepare(module, options):
module.add_submodule(I2c())
module.add_submodule(I2cDevice())
module.add_submodule(I2cMultiplexer())
module.add_submodule(I2s())
module.add_submodule(Interrupt())
module.add_submodule(Memory())
module.add_submodule(OneWire())
Expand Down
9 changes: 7 additions & 2 deletions src/modm/board/disco_f407vg/board.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,17 @@ struct SystemClock {
{
Rcc::enableExternalCrystal(); // 8MHz
const Rcc::PllFactors pllFactors{
.pllM = 4, // 8MHz / M=4 -> 2MHz
.pllN = 168, // 2MHz * N=168 -> 336MHz
.pllM = 8, // 8MHz / M=8 -> 1MHz
.pllN = 336, // 1MHz * N=336 -> 336MHz
.pllP = 2, // 336MHz / P=2 -> 168MHz = F_cpu
.pllQ = 7 // 336MHz / Q=7 -> 48MHz = F_usb
};
Rcc::enablePll(Rcc::PllSource::ExternalCrystal, pllFactors);
const Rcc::PllI2sFactors pllI2sFactors{
.pllN = 258, // 1 MHz * N=258 -> 258 MHz
.pllR = 3 // 258 MHz / R=3 -> 86 MHz
};
Rcc::enablePllI2s(Rcc::PllSource::ExternalCrystal, pllI2sFactors, 2048);
// set flash latency for 168MHz
Rcc::setFlashLatency<Frequency>();
// switch system clock to PLL output
Expand Down
1 change: 1 addition & 0 deletions src/modm/board/disco_f407vg/module.lb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def prepare(module, options):
":platform:gpio",
":platform:i2c:1",
":platform:spi:1",
":platform:i2s:3",
":platform:usb:fs")
return True

Expand Down
23 changes: 23 additions & 0 deletions src/modm/platform/clock/stm32/rcc.cpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,29 @@ modm::platform::Rcc::enablePll(PllSource source, const PllFactors& pllFactors, u
%%endif
}

%% if target["family"] == "f4"
bool
modm::platform::Rcc::enablePllI2s(PllSource, const PllI2sFactors& pllFactors, uint32_t waitCycles)
{
// Read reserved and don't care values and clear all other values
uint32_t tmp = RCC->PLLI2SCFGR & ~(RCC_PLLI2SCFGR_PLLI2SN | RCC_PLLI2SCFGR_PLLI2SR);
// set PLL divider and multiplier
tmp |= (((uint32_t) pllFactors.pllN) << RCC_PLLI2SCFGR_PLLI2SN_Pos) & RCC_PLLI2SCFGR_PLLI2SN;
tmp |= (((uint32_t) pllFactors.pllR) << RCC_PLLI2SCFGR_PLLI2SR_Pos) & RCC_PLLI2SCFGR_PLLI2SR;
RCC->PLLI2SCFGR = tmp;

// enable I2S PLL
RCC->CR |= RCC_CR_PLLI2SON;
// wait till I2S PLL gets ready or time is up
while ((RCC->CR & RCC_CR_PLLI2SRDY) == 0)
{
if (not --waitCycles)
return false;
}
return true;
}
%% endif

%% if pllsai_p_usb
bool
modm::platform::Rcc::enablePllSai(const PllSaiFactors& pllFactors, uint32_t waitCycles)
Expand Down
26 changes: 26 additions & 0 deletions src/modm/platform/clock/stm32/rcc.hpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,32 @@ public:
setHsiPredivider4Enabled(bool divideBy4, uint32_t waitCycles = 2048);
%% endif

%% if target["family"] == "f4"
/** TODO: comments
* input clock is the HSE or HSI divided by M
* f(VCO clock) = f(PLLI2S clock input) × (PLLI2SN / PLLM)
* f(PLL I2S clock output) = f(VCO clock) / PLLI2SR
*
* \param pllN
* PLLI2S multiplication factor for VCO
* 50 <= PLLI2SN <= 432
*
* \param pllR
* PLLI2S division factor for I2S clocks
* Caution: The I2Ss requires a frequency lower than or equal to 192 MHz to work correctly.
* 2 <= PLLR <= 7
*/
struct PllI2sFactors
{
const uint16_t pllN;
const uint16_t pllR;
};

/* TODO: comments */
static bool
enablePllI2s(PllSource, const PllI2sFactors& pllFactors, uint32_t waitCycles);
%% endif

// sinks
static bool
enableSystemClock(SystemClockSource src, uint32_t waitCycles = 2048);
Expand Down
74 changes: 74 additions & 0 deletions src/modm/platform/i2s/stm32/i2s_master.hpp.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2009-2011, Fabian Greif
* Copyright (c) 2010, Martin Rosekeit
* Copyright (c) 2011-2017, Niklas Hauser
* Copyright (c) 2012, Georgi Grinshpun
* Copyright (c) 2013, Kevin Läufer
* Copyright (c) 2014, Sascha Schade
* Copyright (C) 2021, Marton Ledneczki
*
* 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_STM32_I2S_MASTER{{ id }}_HPP
#define MODM_STM32_I2S_MASTER{{ id }}_HPP

#include <modm/architecture/interface/i2s_master.hpp>
#include <modm/platform/gpio/connector.hpp>
#include <modm/math/algorithm/prescaler.hpp>
//#include "i2s_hal_{{ id }}.hpp" TODO

namespace modm
{

namespace platform
{

/**
* Inter-IC Sound (I2C{{ id }}).
*
* TODO: say something about the nature of implementation
*
* @author Marton Ledneczki
* @ingroup modm_platform_i2s modm_platform_i2s_{{id}}
*/
class I2sMaster{{ id }} : public modm::I2sMaster
{
public:
//using Hal = I2sHal{{ id }}; TODO

//using DataSize = Hal::DataSize; TODO

public:
template< template<Peripheral _> class... Signals >
static void
connect()
{
using Connector = GpioConnector<Peripheral::I2s{{ id }}, Signals...>;
using Ck = typename Connector::template GetSignal<Gpio::Signal::Ck>;
using Mck = typename Connector::template GetSignal<Gpio::Signal::Mck>;
using Sd = typename Connector::template GetSignal<Gpio::Signal::Sd>;
using Ws = typename Connector::template GetSignal<Gpio::Signal::Ws>;

// Connector::disconnect();
Ck::setOutput(Gpio::OutputType::PushPull);
Mck::setOutput(Gpio::OutputType::PushPull);
Sd::setOutput(Gpio::OutputType::PushPull);
Ws::setOutput(Gpio::OutputType::PushPull);
Connector::connect();
}


// end documentation inherited
};

} // namespace platform

} // namespace modm

#endif // MODM_STM32_I2S_MASTER{{ id }}_HPP
Loading