262 lines
9.1 KiB
C++

/*
* ADC.cpp
*
* Created on: Jul 1, 2023
* Author: Carst
*/
#include <cstdint>
#include <stm32g0xx_ll_adc.h>
#include <stm32g0xx_ll_dma.h>
#include <stm32g0xx_ll_gpio.h>
#include <stm32g0xx_ll_tim.h>
#include "ADC.hpp"
#include <stdio.h>
typedef struct
{
std::uint16_t current;
std::uint16_t voltage;
std::uint16_t temperature;
}ADC_Samples_t;
static ADC_Samples_t samples[10];
static std::uint16_t current;
static std::uint16_t voltage;
static std::uint16_t temperature;
extern "C" void DMA1_Channel1_IRQHandler(void) /* DMA1 Channel 1 */
{
uint32_t tmp_current = 0U;
uint32_t tmp_temp = 0U;
uint32_t tmp_volt = 0U;
if(LL_DMA_IsActiveFlag_HT1(DMA1))
{
LL_DMA_ClearFlag_HT1(DMA1);
for(uint32_t i = 0U; i < (sizeof(samples) / sizeof(samples[0])) / 2U; i++)
{
tmp_current += samples[i].current;
tmp_temp += samples[i].temperature;
tmp_volt += samples[i].voltage;
}
}
else if(LL_DMA_IsActiveFlag_TC1(DMA1))
{
LL_DMA_ClearFlag_TC1(DMA1);
for(uint32_t i = (sizeof(samples)/sizeof(samples[0])) / 2U; i < (sizeof(samples)/sizeof(samples[0])); i++)
{
tmp_current += samples[i].current;
tmp_temp += samples[i].temperature;
tmp_volt += samples[i].voltage;
}
}
else
{
/* error */
__asm("bkpt #0");
}
tmp_current /= ((sizeof(samples)/sizeof(samples[0])) / 2U);
tmp_temp /= ((sizeof(samples)/sizeof(samples[0])) / 2U);
tmp_volt /= ((sizeof(samples)/sizeof(samples[0])) / 2U);
current = (current + tmp_current) / 2U;
temperature = (temperature + tmp_temp) / 2U;
voltage = (voltage + tmp_volt) / 2U;
}
namespace ElektronischeLast
{
void iADC::init(float voltage_gain, float current_gain)
{
this->voltage_gain = voltage_gain;
this->current_gain = current_gain;
/**ADC1 GPIO Configuration
PA0 ------> ADC1_IN0
PA1 ------> ADC1_IN1
*/
LL_GPIO_InitTypeDef GPIO_InitStruct =
{
.Pin = LL_GPIO_PIN_0,
.Mode = LL_GPIO_MODE_ANALOG,
.Pull = LL_GPIO_PULL_NO,
};
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = LL_GPIO_PIN_1;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = LL_GPIO_PIN_6;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* Enable ADC internal voltage regulator */
LL_ADC_EnableInternalRegulator(ADC1);
/* Delay for ADC internal voltage regulator stabilization. */
/* Compute number of CPU cycles to wait for, from delay in us. */
/* Note: Variable divided by 2 to compensate partially */
/* CPU processing cycles (depends on compilation optimization). */
/* Note: If system core clock frequency is below 200kHz, wait time */
/* is only a few CPU processing cycles. */
uint32_t wait_loop_index;
wait_loop_index = ((LL_ADC_DELAY_INTERNAL_REGUL_STAB_US * (SystemCoreClock / (100000 * 2))) / 10);
while(wait_loop_index != 0)
{
wait_loop_index--;
}
/* Kalibrierung muss vor dem DMA gemacht werden, da dieser sonst den Kalibrierwert
* als ersten Datentransfer durchführt und alle weiteren einen Offset haben. */
LL_ADC_StartCalibration(ADC1);
while(LL_ADC_IsCalibrationOnGoing(ADC1))
{
}
LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_1, LL_DMAMUX_REQ_ADC1);
LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_1, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PRIORITY_HIGH);
LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MODE_CIRCULAR);
LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PDATAALIGN_HALFWORD);
LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_HALFWORD);
LL_DMA_SetPeriphAddress(DMA1, LL_DMA_CHANNEL_1, reinterpret_cast<std::uint32_t>(&ADC1->DR));
/* Select ADC as DMA transfer request */
LL_DMAMUX_SetRequestID(DMAMUX1, LL_DMAMUX_CHANNEL_0, LL_DMAMUX_REQ_ADC1);
/* Set DMA transfer addresses of source and destination */
LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_1,
LL_ADC_DMA_GetRegAddr(ADC1, LL_ADC_DMA_REG_REGULAR_DATA),
(uint32_t)&samples, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
/* Set DMA transfer size */
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, sizeof(samples)/(sizeof(uint16_t)));
/* Enable DMA transfer interruption: transfer complete */
LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1);
/* Enable DMA transfer interruption: half transfer */
LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_1);
/* Enable DMA transfer interruption: transfer error */
LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_1);
/* Enable the DMA transfer */
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
LL_ADC_InitTypeDef ADC_InitStruct =
{
.Clock = LL_ADC_CLOCK_SYNC_PCLK_DIV2,
.Resolution = LL_ADC_RESOLUTION_12B,
.DataAlignment = LL_ADC_DATA_ALIGN_RIGHT,
.LowPowerMode = LL_ADC_LP_MODE_NONE,
};
LL_ADC_Init(ADC1, &ADC_InitStruct);
LL_ADC_REG_SetSequencerConfigurable(ADC1, LL_ADC_REG_SEQ_CONFIGURABLE);
/* Poll for ADC channel configuration ready */
while (LL_ADC_IsActiveFlag_CCRDY(ADC1) == 0)
{
}
/* Clear flag ADC channel configuration ready */
LL_ADC_ClearFlag_CCRDY(ADC1);
LL_ADC_REG_InitTypeDef ADC_REG_InitStruct =
{
.TriggerSource = LL_ADC_REG_TRIG_EXT_TIM1_TRGO2,
.SequencerLength = LL_ADC_REG_SEQ_SCAN_ENABLE_3RANKS,
.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE,
.ContinuousMode = LL_ADC_REG_CONV_SINGLE,
.DMATransfer = LL_ADC_REG_DMA_TRANSFER_UNLIMITED,
.Overrun = LL_ADC_REG_OVR_DATA_PRESERVED,
};
LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);
LL_ADC_SetOverSamplingScope(ADC1, LL_ADC_OVS_DISABLE);
LL_ADC_SetTriggerFrequencyMode(ADC1, LL_ADC_CLOCK_FREQ_MODE_HIGH);
LL_ADC_REG_SetTriggerEdge(ADC1, LL_ADC_REG_TRIG_EXT_RISING);
LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_1, LL_ADC_SAMPLINGTIME_160CYCLES_5);
/** Configure Regular Channel
*/
LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_1, LL_ADC_CHANNEL_0);
LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_0, LL_ADC_SAMPLINGTIME_COMMON_1);
/** Configure Regular Channel
*/
LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_2, LL_ADC_CHANNEL_1);
LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_1, LL_ADC_SAMPLINGTIME_COMMON_1);
/** Configure Regular Channel
*/
LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_3, LL_ADC_CHANNEL_6);
LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_2, LL_ADC_SAMPLINGTIME_COMMON_1);
/* Poll for ADC channel configuration ready */
while (LL_ADC_IsActiveFlag_CCRDY(ADC1) == 0)
{
}
/* Clear flag ADC channel configuration ready */
LL_ADC_ClearFlag_CCRDY(ADC1);
LL_TIM_InitTypeDef TIM_InitStruct =
{
.Prescaler = 63U, /* =1MHz Timer-Input */
.CounterMode = LL_TIM_COUNTERMODE_UP,
.Autoreload = 199UL,
.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1,
.RepetitionCounter = 0UL,
};
LL_TIM_Init(TIM1, &TIM_InitStruct);
LL_TIM_DisableARRPreload(TIM1);
LL_TIM_SetClockSource(TIM1, LL_TIM_CLOCKSOURCE_INTERNAL);
LL_TIM_SetTriggerOutput(TIM1, LL_TIM_TRGO_RESET);
LL_TIM_SetTriggerOutput2(TIM1, LL_TIM_TRGO2_UPDATE);
LL_TIM_DisableMasterSlaveMode(TIM1);
LL_ADC_Enable(ADC1);
LL_ADC_REG_StartConversion(ADC1);
LL_TIM_EnableCounter(TIM1);
/* DMA1_Channel1_IRQn interrupt configuration */
NVIC_SetPriority(DMA1_Channel1_IRQn, 0);
NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}
/**
* @details Die Auflösung beträgt 0,8mV pro Bit.
* Rmess = 1Ohm => 1mv pro 1mA
* Imess = Imess1 + Imess2 + Imess3 + Imess 4
* @return Strom in mA
*/
std::uint32_t iADC::get_current(void)
{
const float factor = 3.3f / 4096.0f * 1000.0f * this->current_gain;
return factor * current;
}
/**
* MCP9701
* Vout = TC * TA + V0°C => Tc = (Vout - V0°C) / Tc
* Tc=19,5mV/°C, V0°C=0,4V
*/
std::uint32_t iADC::get_temperature(void)
{
return ((temperature * 3.3f / 4096.0f) - 0.4f) / 0.019f;
}
/**
* @brief Gemessene Spannung der zu belastenden Quelle.
* @details Die Auflösung beträgt 0,8mV pro Bit.
* Uin = (R1 + R2)/ R2 * Uout => (100k + 4,7k) / 4,7k * Uout
* Uout = ADC * 0,8mV
* @return Spannung in mV
*/
std::uint32_t iADC::get_voltage(void)
{
const float factor = this->voltage_gain * 3.3f / 4096.0f * 1000.0f;
return factor * voltage;
}
std::uint32_t iADC::get_current_raw(void)
{
return current;
}
std::uint32_t iADC::get_temperature_raw(void)
{
return temperature;
}
std::uint32_t iADC::get_voltage_raw(void)
{
return voltage;
}
}