Skip to content

Commit

Permalink
feat: add Multi Channel ADC (#394)
Browse files Browse the repository at this point in the history
Add Multi Channel ADC

This ADC implementation supports multiple channels with DMA transfer and software trigger.
DMA transfer is one shot mode.
  • Loading branch information
fabiangottstein authored Sep 2, 2024
1 parent 75ece18 commit 19f2f6b
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 2 deletions.
90 changes: 90 additions & 0 deletions hal_st/stm32fxxx/AdcDmaMultiChannelStm.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#include "AdcDmaMultiChannelStm.hpp"
#include "infra/event/EventDispatcher.hpp"

namespace
{
constexpr std::array<uint32_t, hal::AdcDmaMultiChannelStmBase::MaxChannels> rank = {
ADC_REGULAR_RANK_1, ADC_REGULAR_RANK_2, ADC_REGULAR_RANK_3, ADC_REGULAR_RANK_4,
ADC_REGULAR_RANK_5, ADC_REGULAR_RANK_6, ADC_REGULAR_RANK_7, ADC_REGULAR_RANK_8,
#if defined(ADC_REGULAR_RANK_9)
ADC_REGULAR_RANK_9, ADC_REGULAR_RANK_10, ADC_REGULAR_RANK_11, ADC_REGULAR_RANK_12,
ADC_REGULAR_RANK_13, ADC_REGULAR_RANK_14, ADC_REGULAR_RANK_15, ADC_REGULAR_RANK_16
#endif
};
}

namespace hal
{
AdcDmaMultiChannelStmBase::AdcDmaMultiChannelStmBase(
infra::MemoryRange<uint16_t> buffer, infra::MemoryRange<AnalogPinStm> analogPins, AdcStm& adc,
DmaStm::ReceiveStream& receiveStream)
: buffer{ buffer }
, analogPins{ analogPins }
, adc{ adc }
, dmaStream{ receiveStream, &adc.Handle().Instance->DR, sizeof(uint16_t), [this]()
{
TransferDone();
} }
{

#ifdef ADC_SINGLE_ENDED
auto result = HAL_ADCEx_Calibration_Start(&adc.Handle(), ADC_SINGLE_ENDED);
assert(result == HAL_OK);
#endif

LL_ADC_REG_SetDMATransfer(adc.Handle().Instance, LL_ADC_REG_DMA_TRANSFER_LIMITED);
}

void AdcDmaMultiChannelStmBase::Measure(const infra::Function<void(infra::MemoryRange<uint16_t>)>& onDone)
{
this->onDone = onDone;

auto result = LL_ADC_REG_IsConversionOngoing(adc.Handle().Instance);
assert(result == 0);

result = ADC_Enable(&adc.Handle());
assert(result == HAL_OK);

__HAL_ADC_CLEAR_FLAG(&adc.Handle(), (ADC_FLAG_EOC | ADC_FLAG_EOS | ADC_FLAG_OVR));
dmaStream.StartReceive(buffer);
LL_ADC_REG_StartConversion(adc.Handle().Instance);
}

void AdcDmaMultiChannelStmBase::ConfigureChannels()
{
ADC_ChannelConfTypeDef channelConfig = { 0 };
#ifdef ADC_SMPR_SMP1
channelConfig.SamplingTime = ADC_SAMPLINGTIME_COMMON_1;
#else
channelConfig.SamplingTime = ADC_SAMPLETIME_47CYCLES_5;
#endif
#ifdef ADC_SINGLE_ENDED
channelConfig.SingleDiff = ADC_SINGLE_ENDED;
#endif
#ifdef ADC_OFFSET_NONE
channelConfig.OffsetNumber = ADC_OFFSET_NONE;
channelConfig.Offset = 0;
#endif

for (std::size_t i = 0; i != analogPins.size(); ++i)
{
channelConfig.Channel = adc.Channel(analogPins[i]);
channelConfig.Rank = rank[i];
auto result = HAL_ADC_ConfigChannel(&adc.Handle(), &channelConfig);
assert(result == HAL_OK);
}
LL_ADC_REG_SetSequencerLength(adc.Handle().Instance, analogPins.size() - 1);
}

void AdcDmaMultiChannelStmBase::TransferDone()
{
auto result = ADC_Disable(&adc.Handle());
assert(result == HAL_OK);

if (this->onDone)
infra::EventDispatcher::Instance().Schedule([this]()
{
onDone(buffer);
});
}
}
66 changes: 66 additions & 0 deletions hal_st/stm32fxxx/AdcDmaMultiChannelStm.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#ifndef HAL_ST_ADC_DMA_MULTI_CHANNEL_HPP
#define HAL_ST_ADC_DMA_MULTI_CHANNEL_HPP

#include "hal_st/stm32fxxx/AnalogToDigitalPinStm.hpp"
#include "hal_st/stm32fxxx/DmaStm.hpp"
#include "hal_st/stm32fxxx/GpioStm.hpp"
#include "infra/util/AutoResetFunction.hpp"

/// @brief This ADC implementation supports multiple channels with DMA transfer and software trigger.
/// DMA transfer is one shot mode.
namespace hal
{
class AdcDmaMultiChannelStmBase
{
public:
AdcDmaMultiChannelStmBase(
infra::MemoryRange<uint16_t> buffer, infra::MemoryRange<AnalogPinStm> analogPins, AdcStm& adc,
DmaStm::ReceiveStream& receiveStream);

void Measure(const infra::Function<void(infra::MemoryRange<uint16_t>)>& onDone);

constexpr static std::size_t MaxChannels{ 16 };

protected:
void ConfigureChannels();

private:
infra::MemoryRange<uint16_t> buffer;
infra::MemoryRange<AnalogPinStm> analogPins;
AdcStm& adc;
ReceiveDmaChannel dmaStream;
infra::AutoResetFunction<void(infra::MemoryRange<uint16_t>)> onDone;

void TransferDone();
};

template<std::size_t Channels>
class AdcDmaMultiChannelStm : public AdcDmaMultiChannelStmBase
{
public:
static_assert(Channels <= MaxChannels, "Number of Channels not supported");

template<class... Pins>
explicit AdcDmaMultiChannelStm(AdcStm& adc, DmaStm::ReceiveStream& receiveStream, Pins&&... pins);

private:
std::array<uint16_t, Channels> bufferStorage{};
std::array<AnalogPinStm, Channels> pinStorage;
};

//// Implementation ////

template<std::size_t Channels>
template<class... Pins>
AdcDmaMultiChannelStm<Channels>::AdcDmaMultiChannelStm(
AdcStm& adc, DmaStm::ReceiveStream& receiveStream, Pins&&... pins)
: AdcDmaMultiChannelStmBase{ bufferStorage, pinStorage, adc, receiveStream }
, pinStorage{ std::forward<Pins>(pins)... }
{
static_assert(sizeof...(Pins) == Channels, "Number of Pins and Channels not matching");
ConfigureChannels();
}

}

#endif
2 changes: 2 additions & 0 deletions hal_st/stm32fxxx/AnalogToDigitalPinStm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ namespace hal
};

class AdcTriggeredByTimerWithDma;
class AdcDmaMultiChannelStmBase;

class AdcStm
{
Expand All @@ -87,6 +88,7 @@ namespace hal
friend class AnalogToDigitalPinImplStm;
friend class AnalogToDigitalInternalTemperatureStm;
friend class AdcTriggeredByTimerWithDma;
friend class AdcDmaMultiChannelStmBase;

uint8_t index;
ADC_HandleTypeDef handle{};
Expand Down
12 changes: 10 additions & 2 deletions hal_st/stm32fxxx/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ target_link_libraries(hal_st.stm32fxxx PUBLIC
)

target_sources(hal_st.stm32fxxx PRIVATE
$<$<OR:$<STREQUAL:${TARGET_MCU},stm32wb55>,$<STREQUAL:${TARGET_MCU_FAMILY},stm32wbaxx>>:AdcDmaStm.cpp>
$<$<OR:$<STREQUAL:${TARGET_MCU},stm32wb55>,$<STREQUAL:${TARGET_MCU_FAMILY},stm32wbaxx>>:AdcDmaStm.hpp>
AnalogToDigitalPinStm.cpp
AnalogToDigitalPinStm.hpp
BackupRamStm.cpp
Expand Down Expand Up @@ -101,6 +99,16 @@ target_sources(hal_st.stm32fxxx PRIVATE
WatchDogStm.hpp
)

if (TARGET_MCU STREQUAL stm32wb55
OR TARGET_MCU_FAMILY STREQUAL stm32wbaxx
OR TARGET_MCU_FAMILY STREQUAL stm32h5xx)
target_sources(hal_st.stm32fxxx PRIVATE AdcDmaStm.cpp
AdcDmaStm.hpp
AdcDmaMultiChannelStm.cpp
AdcDmaMultiChannelStm.hpp
)
endif()

if (TARGET_MCU_VENDOR STREQUAL st)
if (HALST_XML_GPIO AND HALST_XML_STM32)
set(gpio_xml "${CMAKE_SOURCE_DIR}/${HALST_XML_GPIO}")
Expand Down

0 comments on commit 19f2f6b

Please sign in to comment.