Page MenuHomec4science

adc.c
No OneTemporary

File Metadata

Created
Wed, May 1, 20:53
#include "adc.h"
#include "../lib/basic_filter.h"
#include "../lib/utils.h"
volatile uint16_t adc_currentValuesBuffer[ADC_BUFFER_SIZE];
bfilt_BasicFilter adc_currentFilter;
uint16_t adc_currentSensOffset = 2048;
void adc_DmaInit(void);
/**
* @brief Initialize the ADC converter (2 analog inputs + current sense).
*/
void adc_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
adc_DmaInit();
// Periph clock enable
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1|RCC_APB2Periph_ADC2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3,ENABLE);
// GPIO configuration
GPIO_InitStructure.GPIO_Pin = ADC1_Input_Pin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(ADC1_Input_Port, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = ADC2_Input_Pin;
GPIO_Init(ADC2_Input_Port, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = ADC_Current_Sens_Pin;
GPIO_Init(ADC_Current_Sens_Port, &GPIO_InitStructure);
// ADC Common configuration
ADC_DeInit();
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; // Useless here (only used in dual or triple interleaved modes).
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;
ADC_CommonInit(&ADC_CommonInitStructure);
// ADC regular channel configuration
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_ExternalTrigConv = 0;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Init(ADC2, &ADC_InitStructure);
// ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
// ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T8_TRGO;
// ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
// Synchronous sampling with the PWM replaced by a high-frequency sampling at ~300kHz (over-sampling), because of the commutation noise of the current measuring amplifier at ~120kHz.
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_Init(ADC3, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC1_Input_Chan, 1, ADC_SampleTime_56Cycles);
ADC_Cmd(ADC1, ENABLE);
ADC_SoftwareStartConv(ADC1);
ADC_RegularChannelConfig(ADC2, ADC2_Input_Chan, 1, ADC_SampleTime_56Cycles);
ADC_Cmd(ADC2, ENABLE);
ADC_SoftwareStartConv(ADC2);
ADC_RegularChannelConfig(ADC3, ADC_Current_Sens_Chan, 1, ADC_SampleTime_28Cycles);
ADC_DMARequestAfterLastTransferCmd(ADC3, ENABLE);
ADC_DMACmd(ADC3, ENABLE);
ADC_Cmd(ADC3, ENABLE);
ADC_SoftwareStartConv(ADC3);
// Setup the filter of the ADC current samples over time.
bfilt_Init(&adc_currentFilter, 0.2f, 0.0f);
}
/**
* @brief Setup the DMA that copies the current ADC samples to the RAM.
*/
void adc_DmaInit(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&adc_currentValuesBuffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = ADC_BUFFER_SIZE;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_InitStructure.DMA_Channel = DMA_Channel_2;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC3->DR;
DMA_Init(DMA2_Stream1, &DMA_InitStructure);
DMA_Cmd(DMA2_Stream1, ENABLE);
}
/**
* @brief Compute the current sense offset.
* @note Run before enabling current regulation.
*/
void adc_CalibrateCurrentSens(void)
{
int32_t i=0;
for(i=0; i<1000; i++)
{
utils_DelayUs(400);
if(adc_currentFilter.filteredValue > 0.0f)
adc_currentSensOffset++;
else
adc_currentSensOffset--;
}
}
/**
* @brief Compute the current sense offset.
* @retval The measured current in [mA].
*/
float32_t adc_GetCurrent(void)
{
int32_t motorCurrentSum;
float32_t motorCurrentMean;
int32_t i;
// Compute the average of all the ADC values in the buffer.
motorCurrentSum = 0; // [ADC raw value].
for(i=0; i<ADC_BUFFER_SIZE; i++)
motorCurrentSum += (int32_t)(adc_currentValuesBuffer[i]);
motorCurrentMean = (float32_t)motorCurrentSum / (float32_t)ADC_BUFFER_SIZE;
// Add the offset obtained during the sense resistor calibration.
motorCurrentMean -= (float32_t) adc_currentSensOffset;
// Convert from ADC increments to mA.
motorCurrentMean /= CURRENT_SCALE;
// Use low-pass filtering to reduce the noise. This is currently only used
// during the calibration.
bfilt_Step(&adc_currentFilter, motorCurrentMean);
//return motorCurrentMean;
return adc_currentFilter.filteredValue;
}
/**
* @brief Start the ADC conversion of the selected channel.
*/
void adc_StartConversion(AdcChannel channel)
{
if(channel == ANIN1)
ADC1->CR2 |= (uint32_t)ADC_CR2_SWSTART;
else if(channel == ANIN2)
ADC2->CR2 |= (uint32_t)ADC_CR2_SWSTART;
}
/**
* @brief Get the ADC conversion status of the selected channel.
* @retval 1 if the conversion is finished, 0 otherwise.
*/
uint8_t adc_ConversionIsFinished(AdcChannel channel)
{
if(channel == ANIN1)
return (ADC1->SR & ADC_SR_EOC) != 0;
else if(channel == ANIN2)
return (ADC2->SR & ADC_SR_EOC) != 0;
else
return 0;
}
/**
* @brief Get the voltage measured by the selected ADC channel.
* @param channel: the channel of the ADC.
* @param scale: the full scale, depends of the switches position.
* @return the measured voltage, in [V], or 0.0f if the channel is invalid.
* @note make sure that the conversion is finished before calling.
*/
float32_t adc_GetConversionResult(AdcChannel channel, float32_t scale)
{
float32_t adcRawVal, voltage;
if(channel == ANIN1)
adcRawVal = (float32_t)ADC1->DR;
else if(channel == ANIN2)
adcRawVal = (float32_t)ADC2->DR;
else
adcRawVal = 0.0f;
voltage = (1.0f - (adcRawVal / (float32_t)ADC_MAX) * 2.0f) * scale;
return voltage;
}
/**
* @brief Get the voltage measured by the selected ADC channel.
* @param channel: the channel of the ADC.
* @param scale: the full scale, depends of the switches position.
* @return the measured voltage, in [V], or 0.0f if the channel is invalid.
* @note make sure that the conversion is finished before calling.
*/
float32_t adc_GetChannelVoltage(AdcChannel channel, float32_t scale)
{
int32_t conversionTime = 0;
adc_StartConversion(channel);
while(!adc_ConversionIsFinished(channel))
{
if(conversionTime < ADC_MAX_CONVERSION_TIME)
conversionTime++;
else
break;
}
return adc_GetConversionResult(channel, scale);
}

Event Timeline