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

feat: add Multi Channel ADC #394

Merged
merged 2 commits into from
Sep 2, 2024
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
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
Loading