Skip to content

Commit

Permalink
[stm32] Add DAC driver with DMA
Browse files Browse the repository at this point in the history
  • Loading branch information
chris-durand committed Jun 6, 2021
1 parent bbc8c4e commit a0bdef3
Show file tree
Hide file tree
Showing 3 changed files with 324 additions and 0 deletions.
164 changes: 164 additions & 0 deletions src/modm/platform/dac/stm32/dac_dma.hpp.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/*
* Copyright (c) 2021, 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_STM32_DAC_DMA{{ id }}_HPP
#define MODM_STM32_DAC_DMA{{ id }}_HPP

#include <cstdint>
#include "../device.hpp"
#include <modm/platform/dma/dma.hpp>
#include <modm/platform/gpio/connector.hpp>
#include <modm/platform/clock/rcc.hpp>
#include <modm/src/modm/math/units.hpp>

namespace modm::platform
{

/**
* Digital-to-Analog Converter with DMA (DAC{{ id }})
*
* @author Christopher Durand
* @ingroup modm_platform_dac{% if id %} modm_platform_dac_{{id}}{% endif %}
*/
class Dac{{ id }}Dma
{
public:
template< template<Peripheral _> class... Signals >
static void
connect()
{
using Connector = GpioConnector<Peripheral::Dac{{ id }}, Signals...>;
Connector::connect();
}

%% if target.family == "g4"
/**
* Initialize the D/A converter.
*
* Enables the RCC clock output for the DAC
*
* @tparam SystemClock System clock struct
*/
template<typename SystemClock>
static inline void
initialize();

%% else
/**
* Initialize the D/A converter.
*
* Enables the RCC clock output for the DAC
*/
static void
initialize();

%% endif

%% set max_channel = 2 if dual_channel else 1
%% for channel_id in range(1, max_channel + 1)
/**
* DAC output channel {{ channel_id }}
*
* @ingroup modm_platform_dac{% if id %} modm_platform_dac_{{id}}{% endif %}
*/
template<typename DmaChannel>
class Channel{{ channel_id }}
{
public:
%% if has_mcr
enum class Mode
{
%% set mode="DAC_MCR_MODE{}".format(channel_id)
ExternalWithBuffer = 0,
ExternalInternalWithBuffer = {{ mode }}_0,
ExternalWithoutBuffer = {{ mode }}_1,
Internal = {{ mode }}_0 | {{ mode }}_1,
Mask = {{ mode }}_0 | {{ mode }}_1 | {{ mode }}_2
};
%% endif

/// Enable the DAC channel
static void enableDacChannel();

/// Disable the DAC channel
static void disableDacChannel();

%% if has_mcr
/// Configure connection mode (internal, external, external with buffer)
static void setMode(Mode mode);
%% else
/// Enable the output buffer
static void setOutputBufferEnabled(bool enabled);
%% endif

/**
* Configure the external trigger source
*
* If DMA is used the external trigger mode is always active
*
* @param triggerSource trigger source id, please refer to the reference manual
*/
static void setTriggerSource(uint8_t triggerSource);
%% if has_sr
/**
* Returns true on DMA underrun
*
* @warning To reset the condition both the DAC channel and DMA channel
* have to be disabled and and the DMA channel must be reconfigured.
*/
static bool hasDmaUnderrun();
%% endif
/**
* Configure the DMA channel
*
* @warning this function must be called before enabling the DMA channel
* @pre the DMA channel must be disabled to be reconfigured
*
* @param data output data
* @param dataSize number of DMA transfers to be performed
* @param circularMode DMA circular mode setting
* @param priority DMA transfer priority
*/
static void configure(const void* data, size_t dataSize, DmaBase::CircularMode circularMode,
DmaBase::Priority priority = DmaBase::Priority::Medium);

/**
* Set data to transfer
*
* @pre the DMA channel must be disabled to call this function
*
* @param data output data
* @param dataSize number of DMA transfers to be performed
*/
static void setData(const void* data, size_t dataSize);

/**
* Start DMA transfers
* @pre before starting the transfer the channel must be configured
*/
static void startDma();

/// Stop DMA transfers
static void stopDma();
};
%% endfor
};


} // namespace modm::platform

%% if id == ""
#include "dac_dma_impl.hpp"
%% else
#include "dac_dma_{{ id }}_impl.hpp"
%% endif

#endif // MODM_STM32_DAC_DMA{{ id }}_HPP
154 changes: 154 additions & 0 deletions src/modm/platform/dac/stm32/dac_dma_impl.hpp.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*
* Copyright (c) 2021, 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_STM32_DAC_DMA{{ id }}_HPP
# error "Don't include this file directly, use 'spi_master_{{ id }}_dma.hpp' instead!"
#endif

namespace modm::platform
{

%% if target.family == "g4"
template <typename SystemClock>
%% endif
void
Dac{{ id }}Dma::initialize()
{
Rcc::enable<Peripheral::Dac{{ id }}>();
%% if dual_channel
DAC{{ id }}->CR = DAC_CR_TEN1 | DAC_CR_TEN2;
%% else
DAC{{ id }}->CR = DAC_CR_TEN1;
%% endif
%% if has_mcr
DAC{{ id }}->MCR = 0;
%% endif

%% if target.family == "g4"
if constexpr(SystemClock::Dac{{ id }} > MHz(160)) {
DAC{{ id }}->MCR = DAC_MCR_HFSEL_1;
} else if constexpr(SystemClock::Dac{{ id }} > MHz(80)) {
DAC{{ id }}->MCR = DAC_MCR_HFSEL_0;
}
%% endif
}

%% set max_channel = 2 if dual_channel else 1
%% for channel_id in range(1, max_channel + 1)
%% set channel="Dac{}Dma::Channel{}<DmaChannel>".format(id, channel_id)

template<typename DmaChannel>
void
{{ channel }}::enableDacChannel()
{
DAC{{ id }}->CR |= DAC_CR_DMAEN{{ channel_id }} | DAC_CR_EN{{ channel_id }};
}

template<typename DmaChannel>
void
{{ channel }}::disableDacChannel()
{
%% if has_sr
// clear underrun flag
DAC{{ id }}->SR = DAC_SR_DMAUDR{{ channel_id }};
%% endif
DAC{{ id }}->CR &= ~(DAC_CR_DMAEN{{ channel_id }} | DAC_CR_EN{{ channel_id }});
}

%% if has_mcr
template<typename DmaChannel>
void
{{ channel }}::setMode(Mode mode)
{
disableDacChannel();
DAC{{ id }}->MCR = (DAC{{ id }}->MCR & DAC_MCR_MODE{{ channel_id }}) | uint32_t(mode);
}

%% else
template<typename DmaChannel>
void
{{ channel }}::setOutputBufferEnabled(bool enabled)
{
if (enabled) {
DAC{{ id }}->CR |= DAC_CR_BOFF{{ channel_id }};
} else {
DAC{{ id }}->CR &= ~DAC_CR_BOFF{{ channel_id }};
}
}

%% endif

template<typename DmaChannel>
void
{{ channel }}::setTriggerSource(uint8_t triggerSource)
{
DAC{{ id }}->CR = (DAC{{ id }}->CR & ~DAC_CR_TSEL{{ channel_id }})
| ((uint32_t(triggerSource) << DAC_CR_TSEL{{ channel_id }}_Pos) & DAC_CR_TSEL{{ channel_id }}_Msk);
}

%% if has_sr
template<typename DmaChannel>
bool
{{ channel }}::hasDmaUnderrun()
{
return (DAC{{ id }}->SR & DAC_SR_DMAUDR{{ channel_id }});
}
%% endif

template<typename DmaChannel>
void
{{ channel }}::configure(const void* data, size_t dataLength, DmaBase::CircularMode circularMode,
DmaBase::Priority priority)
{
using RequestMapping = typename DmaChannel::RequestMapping<Peripheral::Dac{{ id }}, DmaBase::Signal::Ch{{ channel_id }}>;
constexpr auto request = RequestMapping::Request;

DmaChannel::configure(
DmaBase::DataTransferDirection::MemoryToPeripheral,
DmaBase::MemoryDataSize::HalfWord,
DmaBase::PeripheralDataSize::Word,
DmaBase::MemoryIncrementMode::Increment,
DmaBase::PeripheralIncrementMode::Fixed,
priority,
circularMode
);

DmaChannel::template setPeripheralRequest<request>();

DmaChannel::setMemoryAddress(uintptr_t(data));
DmaChannel::setPeripheralAddress(uintptr_t(&(DAC{{ id }}->DHR12R{{ channel_id }})));
DmaChannel::setDataLength(dataLength);
}

template<typename DmaChannel>
void
{{ channel }}::setData(const void* data, size_t dataLength)
{
DmaChannel::setMemoryAddress(uintptr_t(data));
DmaChannel::setDataLength(dataLength);
}

template<typename DmaChannel>
void
{{ channel }}::startDma()
{
DmaChannel::start();
}

template<typename DmaChannel>
void
{{ channel }}::stopDma()
{
DmaChannel::stop();
}
%% endfor

} // namespace modm::platform
6 changes: 6 additions & 0 deletions src/modm/platform/dac/stm32/module.lb
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ class Instance(Module):

env.template("dac.hpp.in", "dac_{}.hpp".format(self.instance))
env.template("dac.cpp.in", "dac_{}.cpp".format(self.instance))
if env.has_module(":platform:dma"):
env.template("dac_dma.hpp.in", "dac_dma_{}.hpp".format(self.instance))
env.template("dac_dma_impl.hpp.in", "dac_dma_{}_impl.hpp".format(self.instance))


def init(module):
Expand Down Expand Up @@ -98,3 +101,6 @@ def build(env):
if "instance" not in driver:
env.template("dac.hpp.in")
env.template("dac.cpp.in")
if env.has_module(":platform:dma"):
env.template("dac_dma.hpp.in")
env.template("dac_dma_impl.hpp.in")

0 comments on commit a0bdef3

Please sign in to comment.