/* * ADC.cpp * * Created on: Jul 1, 2023 * Author: Carst */ #include #include #include #include #include #include "ADC.hpp" #include 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 { iADC::iADC(void) { /**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(&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); } iADC::~iADC(void) { } /** * @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 * 4.0f; return factor * current; } std::uint32_t iADC::get_temperature(void) { return temperature; } /** * @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 = (100000.0f + 4700.0f) / 4700.0f * 3.3f / 4096.0f * 1000.0f; return factor * voltage; } }