Skip to content

Commit

Permalink
feat: add ADC triggered by timer using DMA (#238)
Browse files Browse the repository at this point in the history
* add class for timer only for wb55

* change const to ref to an object

* enum classes fixed

* ADC class refactored in order to be more generic

* New class added: adc triggered by timer using dma

* add functions to start and stop timer

* DMA class extended to be able to configure data size

* add dma and timer to the new adc class

* fix build

* fix build

* fix build

* dma fixed

* struct to configure timing created

* adc triggered by timer with dma fixed

* ADC triggered by dma fixed

* Apply suggestions from code review

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Richard Peters <richard.peters@philips.com>

* dma refactored

* interface refactored

* integrate new interface from embedded-infra-lib

* changes based on interface

* buffer name fixed

* code adjusted based on the refactored interface

* interface has to be public in order to be used in the application

* do not invoke onDone callback inside dma interrupt, instead, schedule it

* remove really assert to check if instance is set

* really assert in case of trying to use a reserved DMA stream

* new class added to support internal temperature conversions

* adc disable functions replaced by adc stop

* changes to support G4 devices

* macro to convert digital data into temperature fixed

* build fixed for m7

* convertion to celsius degrees extracted to a helper class

* helper class refactored

* Update hal_st/stm32fxxx/CMakeLists.txt

Co-authored-by: Richard Peters <richard.peters@philips.com>

* remove include related to wb in timer stm cpp file

* struct initialization in timer stm fixed

* unnecessary includes removed. Callback being scheduled instead of being called on interrupt context

* Update hal_st/stm32fxxx/AdcDmaStm.hpp

Co-authored-by: Richard Peters <richard.peters@philips.com>

* interface connector removed from adc with dma class

* PR comments processed

* Apply suggestions from code review

* if micro is wb then calibrate before usage

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Richard Peters <richard.peters@philips.com>
Co-authored-by: André Jordan <3933856+andjordan@users.noreply.github.com>
  • Loading branch information
4 people committed Mar 15, 2024
1 parent 5e5f05f commit 31adf4e
Show file tree
Hide file tree
Showing 10 changed files with 391 additions and 22 deletions.
101 changes: 101 additions & 0 deletions hal_st/stm32fxxx/AdcDmaStm.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#include "hal_st/stm32fxxx/AdcDmaStm.hpp"
#include "infra/event/EventDispatcher.hpp"

namespace hal
{
AdcTriggeredByTimerWithDma::AdcTriggeredByTimerWithDma(infra::MemoryRange<uint16_t> buffer, AdcStm& adc, DmaStm::ReceiveStream& receiveStream, TimerBaseStm::Timing timing, hal::GpioPinStm& pin)
: adc(adc)
, buffer(buffer)
, dmaStream(receiveStream, &adc.Handle().Instance->DR, sizeof(uint16_t), [this]()
{
TransferDone();
})
, analogPin(pin)
#if defined(STM32G0)
, timer(3, timing, { TimerBaseStm::CounterMode::up, infra::MakeOptional<TimerBaseStm::Trigger>({ TimerBaseStm::Trigger::TriggerOutput::update, false }) })
#else
, timer(2, timing, { TimerBaseStm::CounterMode::up, infra::MakeOptional<TimerBaseStm::Trigger>({ TimerBaseStm::Trigger::TriggerOutput::update, false }) })
#endif
{
ADC_ChannelConfTypeDef channelConfig;
channelConfig.Channel = adc.Channel(analogPin);
#if !defined(STM32WB)
channelConfig.Rank = 1;
#else
channelConfig.Rank = ADC_REGULAR_RANK_1;
#endif
#if defined(STM32F0) || defined(STM32F3)
channelConfig.SamplingTime = ADC_SAMPLETIME_7CYCLES_5;
#elif defined(STM32WB)
channelConfig.SingleDiff = ADC_SINGLE_ENDED;
channelConfig.SamplingTime = ADC_SAMPLETIME_92CYCLES_5;
channelConfig.Offset = 0;
channelConfig.OffsetNumber = ADC_OFFSET_NONE;
#elif defined(STM32G0)
channelConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES_5;
#elif defined(STM32G4)
channelConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES_5;
channelConfig.Offset = 0;
#else
channelConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
channelConfig.Offset = 0;
#endif
ReconfigureTrigger();

auto result = HAL_ADC_ConfigChannel(&adc.Handle(), &channelConfig);
assert(result == HAL_OK);

result = HAL_ADCEx_Calibration_Start(&adc.Handle(), ADC_SINGLE_ENDED);
assert(result == HAL_OK);
}

void AdcTriggeredByTimerWithDma::Measure(std::size_t numberOfSamples, const infra::Function<void(infra::MemoryRange<uint16_t>)>& onDone)
{
this->onDone = onDone;

timer.Start();
Configure();
dmaStream.StartReceive(buffer);
LL_ADC_REG_StartConversion(adc.Handle().Instance);
}

void AdcTriggeredByTimerWithDma::ReconfigureTrigger()
{
HAL_ADC_Stop(&adc.Handle());

#if defined(STM32F4) || defined(STM32F7)
LL_ADC_REG_SetTriggerSource(adc.Handle().Instance, ADC_EXTERNALTRIGCONV_T2_TRGO);
#elif defined(STM32G0)
LL_ADC_REG_SetTriggerSource(adc.Handle().Instance, ADC_EXTERNALTRIG_T3_TRGO);
#else
LL_ADC_REG_SetTriggerSource(adc.Handle().Instance, ADC_EXTERNALTRIG_T2_TRGO);
#endif
LL_ADC_REG_SetTriggerEdge(adc.Handle().Instance, LL_ADC_REG_TRIG_EXT_RISING);
}

void AdcTriggeredByTimerWithDma::Configure()
{
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));
LL_ADC_REG_SetDMATransfer(adc.Handle().Instance, LL_ADC_REG_DMA_TRANSFER_LIMITED);
}

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

timer.Stop();

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

#include "hal/interfaces/AnalogToDigitalPin.hpp"
#include "hal_st/stm32fxxx/AnalogToDigitalPinStm.hpp"
#include "hal_st/stm32fxxx/DmaStm.hpp"
#include "hal_st/stm32fxxx/TimerStm.hpp"
#include "infra/util/AutoResetFunction.hpp"

namespace hal
{
class AdcTriggeredByTimerWithDma
: public AnalogToDigitalPinImplBase<uint16_t>
{
public:
template<std::size_t Max>
using WithNumberOfSamples = infra::WithStorage<AdcTriggeredByTimerWithDma, std::array<uint16_t, Max>>;

explicit AdcTriggeredByTimerWithDma(infra::MemoryRange<uint16_t> buffer, AdcStm& adc, DmaStm::ReceiveStream& receiveStream, TimerBaseStm::Timing timing, hal::GpioPinStm& pin);

void Measure(std::size_t numberOfSamples, const infra::Function<void(infra::MemoryRange<uint16_t>)>& onDone) override;

private:
void ReconfigureTrigger();
void TransferDone();
void Configure();

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

#endif
88 changes: 80 additions & 8 deletions hal_st/stm32fxxx/AnalogToDigitalPinStm.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
#include "hal_st/stm32fxxx/AnalogToDigitalPinStm.hpp"
#include "generated/stm32fxxx/PeripheralTable.hpp"
#include <array>
#include <cassert>
#include "infra/event/EventDispatcher.hpp"
#include "infra/util/MemoryRange.hpp"
#if defined(STM32F7)
extern "C"
{
#include "stm32f7xx_ll_adc.h"
}
#endif

namespace
{
Expand Down Expand Up @@ -33,12 +39,15 @@ namespace hal
AnalogToDigitalPinImplStm::AnalogToDigitalPinImplStm(hal::GpioPinStm& pin, AdcStm& adc)
: analogPin(pin)
, adc(adc)
{}
{
HAL_ADC_Stop(&adc.Handle());
LL_ADC_REG_SetTriggerSource(adc.Handle().Instance, ADC_SOFTWARE_START);
}

void AnalogToDigitalPinImplStm::Measure(std::size_t numberOfSamples, const infra::Function<void(infra::MemoryRange<uint16_t>)>& onDone)
{
ADC_ChannelConfTypeDef channelConfig;
channelConfig.Channel = adcChannel[analogPin.AdcChannel(adc.index + 1)];
channelConfig.Channel = adc.Channel(analogPin);
#if defined(STM32WB) || defined(STM32G0) || defined(STM32G4)
channelConfig.Rank = ADC_REGULAR_RANK_1;
#else
Expand All @@ -57,15 +66,56 @@ namespace hal
channelConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
channelConfig.Offset = 0;
#endif
HAL_StatusTypeDef result = HAL_ADC_ConfigChannel(&adc.handle, &channelConfig);
HAL_StatusTypeDef result = HAL_ADC_ConfigChannel(&adc.Handle(), &channelConfig);
assert(result == HAL_OK);

adc.Measure(onDone);
}

AnalogToDigitalInternalTemperatureStm::AnalogToDigitalInternalTemperatureStm(AdcStm& adc)
: adc(adc)
{
HAL_ADC_Stop(&adc.Handle());
LL_ADC_REG_SetTriggerSource(adc.Handle().Instance, ADC_SOFTWARE_START);
}

void AnalogToDigitalInternalTemperatureStm::Measure(std::size_t numberOfSamples, const infra::Function<void(infra::MemoryRange<uint16_t>)>& onDone)
{
ADC_ChannelConfTypeDef channelConfig;
#if !defined(STM32G4)
channelConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
#else
if (adc.index == 1)
channelConfig.Channel = ADC_CHANNEL_TEMPSENSOR_ADC1;
else
channelConfig.Channel = ADC_CHANNEL_TEMPSENSOR_ADC5;
#endif
#if defined(STM32WB) || defined(STM32G0) || defined(STM32G4)
channelConfig.Rank = ADC_REGULAR_RANK_1;
#else
channelConfig.Rank = 1;
#endif
#if defined(STM32F0) || defined(STM32F3)
channelConfig.SamplingTime = ADC_SAMPLETIME_7CYCLES_5;
#elif defined(STM32WB) || defined(STM32G4)
channelConfig.SamplingTime = ADC_SAMPLETIME_2CYCLES_5;
channelConfig.Offset = 0;
channelConfig.OffsetNumber = ADC_OFFSET_NONE;
channelConfig.SingleDiff = ADC_SINGLE_ENDED;
#elif defined(STM32G0)
channelConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES_5;
#else
channelConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
channelConfig.Offset = 0;
#endif
HAL_StatusTypeDef result = HAL_ADC_ConfigChannel(&adc.Handle(), &channelConfig);
assert(result == HAL_OK);

adc.Measure(onDone);
}

AdcStm::AdcStm(uint8_t oneBasedIndex)
: index(oneBasedIndex - 1)
, handle()
#if defined(STM32WB) || defined(STM32G0)
, interruptHandler(ADC1_IRQn, [this]()
#elif defined(STM32G4)
Expand Down Expand Up @@ -99,11 +149,18 @@ namespace hal
#endif
#if defined(STM32WB) || defined(STM32G0) || defined(STM32G4)
handle.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
handle.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
handle.Init.OversamplingMode = DISABLE;
#elif !defined(STM32F3)
handle.Init.EOCSelection = DISABLE;
#endif
HAL_StatusTypeDef result = HAL_ADC_Init(&handle);
assert(result == HAL_OK);

#if defined(STM32WB)
result = HAL_ADCEx_Calibration_Start(&handle, ADC_SINGLE_ENDED);
assert(result == HAL_OK);
#endif
}

AdcStm::~AdcStm()
Expand All @@ -119,6 +176,16 @@ namespace hal
assert(result == HAL_OK);
}

uint32_t AdcStm::Channel(const hal::AnalogPinStm& pin) const
{
return adcChannel[pin.AdcChannel(index + 1)];
}

ADC_HandleTypeDef& AdcStm::Handle()
{
return handle;
}

void AdcStm::MeasurementDone()
{
assert(onDone != nullptr);
Expand All @@ -128,7 +195,12 @@ namespace hal
handle.Instance->SR &= ~ADC_SR_EOC;
#endif
interruptHandler.ClearPending();
sample = HAL_ADC_GetValue(&handle);
onDone(infra::MakeRangeFromSingleObject(sample));
sample = static_cast<uint16_t>(HAL_ADC_GetValue(&handle));

if (onDone)
infra::EventDispatcher::Instance().Schedule([this]()
{
onDone(infra::MakeRangeFromSingleObject(sample));
});
}
}
26 changes: 23 additions & 3 deletions hal_st/stm32fxxx/AnalogToDigitalPinStm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ namespace hal
class AdcStm;

class AnalogToDigitalPinImplStm
: protected AnalogToDigitalPinImplBase<uint16_t>
: public AnalogToDigitalPinImplBase<uint16_t>
{
public:
AnalogToDigitalPinImplStm(hal::GpioPinStm& pin, AdcStm& adc);
explicit AnalogToDigitalPinImplStm(hal::GpioPinStm& pin, AdcStm& adc);

void Measure(std::size_t numberOfSamples, const infra::Function<void(infra::MemoryRange<uint16_t>)>& onDone) override;

Expand All @@ -24,18 +24,38 @@ namespace hal
AdcStm& adc;
};

class AnalogToDigitalInternalTemperatureStm
: public AnalogToDigitalPinImplBase<uint16_t>
{
public:
explicit AnalogToDigitalInternalTemperatureStm(AdcStm& adc);

void Measure(std::size_t numberOfSamples, const infra::Function<void(infra::MemoryRange<uint16_t>)>& onDone) override;

private:
AdcStm& adc;
};

class AdcTriggeredByTimerWithDma;

class AdcStm
{
public:
AdcStm(uint8_t oneBasedIndex);
explicit AdcStm(uint8_t adcIndex);
~AdcStm();

protected:
uint32_t Channel(const hal::AnalogPinStm& pin) const;
ADC_HandleTypeDef& Handle();

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

private:
friend class AnalogToDigitalPinImplStm;
friend class AnalogToDigitalInternalTemperatureStm;
friend class AdcTriggeredByTimerWithDma;

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

target_sources(hal_st.stm32fxxx PRIVATE
$<$<STREQUAL:${TARGET_MCU},stm32wb55>:AdcDmaStm.cpp>
$<$<STREQUAL:${TARGET_MCU},stm32wb55>:AdcDmaStm.hpp>
AnalogToDigitalPinStm.cpp
AnalogToDigitalPinStm.hpp
BackupRamStm.cpp
Expand Down Expand Up @@ -77,6 +79,8 @@ target_sources(hal_st.stm32fxxx PRIVATE
SystemTickStm.hpp
SystemTickTimerService.cpp
SystemTickTimerService.hpp
TimerStm.cpp
TimerStm.hpp
$<$<NOT:$<STREQUAL:${TARGET_MCU_FAMILY},stm32g0xx>>:UartStm.cpp>
$<$<NOT:$<STREQUAL:${TARGET_MCU_FAMILY},stm32g0xx>>:UartStm.hpp>
$<$<NOT:$<STREQUAL:${TARGET_MCU_FAMILY},stm32g0xx>>:UartStmDma.cpp>
Expand Down
2 changes: 1 addition & 1 deletion hal_st/stm32fxxx/DmaStm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ namespace hal

void DmaStm::ReserveStream(uint8_t dmaIndex, uint8_t streamIndex)
{
assert((streamAllocation[dmaIndex] & (1 << streamIndex)) == 0);
really_assert((streamAllocation[dmaIndex] & (1 << streamIndex)) == 0);
streamAllocation[dmaIndex] |= 1 << streamIndex;
}

Expand Down
Loading

0 comments on commit 31adf4e

Please sign in to comment.