mvvm 发表于 2019-6-25 15:48:17

STM32F4ADC采集的数据好坏和调用的某个函数有关

本帖最后由 mvvm 于 2019-6-25 17:21 编辑

   提的这个问题我自己也认为ADC采样不可能因为你调用某个函数导致采样异常,但是我调试就是遇到了调的都崩溃了这问题太莫名其妙了。所以希望熟悉ADC采样的帮我分析分析哪里的问题。
    问题描述:
我要实现的功能是采集输入的一个正弦信号(频率50hz),设置的采样率是1k,每完成一个周波(20ms也就是20个采样点)计算一下均方根值,然后进行校准。
现在我程序打算采用的方式有两种:
第一种:ADC+TIM通过定时器定时触发ADC,然后开启转换完成中断(EOC),再调用ADC_EOCOnEachRegularChannelCmd函数使得ADC每次完成中断都触发一次中断,然后中断函数中将数据读取到缓存。缓存使用的是双缓存
第二种:ADC+TIM+DMA通过定时器触发ADC,然后通过中断将数据存储到缓存中,DMA单次传输20个数据,开启双缓存模式,开启传输完成中断。

经测试以上两种方式都能实现1k的采样率,并且能将数据存放到内存中,我认为正确是我将采集到的数据一个一个打印出来。当输入只有偏置电压的时候换算成的电压值大致和偏置电压相等。当输入一个正弦信号时,我将采集到的数据用excel绘成图波形也是一个很标准的正弦曲线。
单独一个一个打印出来的数据我觉得是没有问题的,但是我在测试均方根值得时候却发现当输入只有偏置电压得时候算出得均方值特别大(按理应该接近0),然后我调试发现数据抖动的特别厉害,怪不得均方根值特别大。然后我就很奇怪了我的均方根计算函数不复杂呀。168M的主频做20个点的计算应该没有一点问题呀。怎么就一调用这函数就出问题,我不调用调试看数据都是好的(基本没有波动)。这到底什么情况啊!!!!

以上问题不管我用那种方式都存在这个问题。

相关代码:
void Test_Fun(void)
{
uint8_t i;
uint16_t* ptr;
uint16_t avg;
uint32_t sum=0;

if(Device_Status.Full_Buff_Node == 0)
{
ptr=Device_Status.Cur_Sampval;
}
else
{
ptr=Device_Status.Cur_Sampval;
}
for(i=0; i<20; i++)
{
sum += ptr;
}
avg=sum/20;   
#if DEBUG
printf("%d\n", avg);
#endif
}

int main(void)
{
Control_GPIO_Init();//初始化要用到的GPIO
#if DEBUG
USART1_Init(115200);
#endif
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
Adc_Config();//ADC的配置
ADC_Start();//启动转换
while(1)
{
    if(Device_Status.Rms_Calculate_Flag==1)//20点采样完成标志
    {
      Device_Status.Rms_Calculate_Flag=0;
      Test_Fun();   //用来计算均方值屏蔽此函数采样值波动特别小,加上波动特别大。
    }
}
}

void Adc_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3, ENABLE);
/* PF7->ADC_BAT->IN5, PF10->ADC_I->IN8 */
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_7|GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_25MHz;
GPIO_Init(GPIOF, &GPIO_InitStructure);
ADC_CommonInitStructure.ADC_Mode=ADC_Mode_Independent;
ADC_CommonInitStructure.ADC_Prescaler=ADC_Prescaler_Div6;
ADC_CommonInitStructure.ADC_DMAAccessMode=ADC_DMAAccessMode_Disabled;
ADC_CommonInitStructure.ADC_TwoSamplingDelay=ADC_TwoSamplingDelay_10Cycles;
ADC_CommonInit(&ADC_CommonInitStructure);
ADC_InitStructure.ADC_Resolution=ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode=ENABLE;
ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;
ADC_InitStructure.ADC_ContinuousConvMode=DISABLE;
ADC_InitStructure.ADC_NbrOfConversion=1;
ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_T8_TRGO;//定时器8触发
ADC_InitStructure.ADC_ExternalTrigConvEdge=ADC_ExternalTrigConvEdge_Rising;
ADC_Init(ADC3, &ADC_InitStructure);
/* 规则通道选择 */
ADC_RegularChannelConfig(ADC3, ADC_Channel_8, 1, ADC_SampleTime_480Cycles);

#if USE_ADC_DMA
ADC_DMARequestAfterLastTransferCmd(ADC3, ENABLE);
ADC_DMA_Config((uint32_t*)Device_Status.Cur_Sampval, (uint32_t*)Device_Status.Cur_Sampval, 20);
ADC_DMACmd(ADC3, ENABLE);
#else
ADC_EOCOnEachRegularChannelCmd(ADC3, ENABLE);//每次采样完成生成中断
ADC_ITConfig(ADC3, ADC_IT_EOC, ENABLE);//开启EOC中断
#endif
ADC_NVIC_Config();//设置优先级
ADC_Cmd(ADC3, DISABLE);
Adc_SampleFre_Config();
}
//DMA配置
void ADC_DMA_Config(uint32_t* buff0, uint32_t* buff1, uint16_t size)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
DMA_DeInit(DMA2_Stream0);
while (DMA_GetCmdStatus(DMA2_Stream0) != DISABLE);
DMA_InitStructure.DMA_Channel = DMA_Channel_2;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC3->DR;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)buff0;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = 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_Disable;         
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream0, &DMA_InitStructure);
DMA_ITConfig(DMA2_Stream0, DMA_IT_TC, ENABLE);
DMA_DoubleBufferModeConfig(DMA2_Stream0, (uint32_t)buff1, DMA_Memory_0);
DMA_DoubleBufferModeCmd(DMA2_Stream0, ENABLE);
DMA_Cmd(DMA2_Stream0, DISABLE);
}
//采样频率设置
void Adc_SampleFre_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);
TIM_TimeBaseInitStructure.TIM_Period=999;
TIM_TimeBaseInitStructure.TIM_Prescaler=167; /* 1000k */
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM8, &TIM_TimeBaseInitStructure);

TIM_SelectOutputTrigger(TIM8, TIM_TRGOSource_Update);
TIM_Cmd(TIM8, DISABLE);
}
void ADC_Start(void)
{
Triode_Control(VDD_ADC, ENABLE);
#if USE_ADC_DMA
//ADC_DMA_Config();
DMA2->LIFCR = ((uint32_t)0x0000007D);
while(DMA_GetCmdStatus(DMA2_Stream0)!= DISABLE);
DMA2_Stream0->CR |= (uint32_t)DMA_SxCR_EN; /* 使能DMA2_Stream0 */
#endif
ADC3->CR2 |= (uint32_t)ADC_CR2_ADON; /* 使能ADC3 */
TIM8->CR1 |= TIM_CR1_CEN;            /* 使能定时器8 */
Device_Status.Adc_Start_Flag=1;
}
//ADC中断函数
void ADC_IRQHandler(void)
{
if(ADC_GetITStatus(ADC3, ADC_IT_EOC) != RESET)
{
    Device_Status.Cur_Sampval=ADC3->DR;
    if(Device_Status.Sample_Num >= 20)
    {
      Device_Status.Sample_Num=0;
      Device_Status.Rms_Calculate_Flag=1;
      if(Device_Status.Full_Buff_Node == 1)
      {
      Device_Status.Full_Buff_Node=0;
      }
      else
      {
      Device_Status.Full_Buff_Node=1;
      }
    }
}
}


mvvm 发表于 2019-6-25 15:48:57

:'(:'(:'(:'(

mvvm 发表于 2019-6-25 15:49:52

调了一天了,调的我都都不知道如何找问题了

す疯Ⅱ恒す 发表于 2019-6-25 16:22:32

有好几个地方会出问题。先反问你几个问题。你的打印数据函数呢?用什么打印?打印数据量多少?打印一次数据需要多少时间?有中断服务函数吗??中断里还干了些什么耗时的动作??
你回答完我这几个问题,估计你自己也就能找到问题了。

mvvm 发表于 2019-6-25 17:17:22

す疯Ⅱ恒す 发表于 2019-6-25 16:22
有好几个地方会出问题。先反问你几个问题。你的打印数据函数呢?用什么打印?打印数据量多少?打印一次数据 ...

你好用printf打印的,波特率115200,中断第一种方式下只有一个ADC转换完成中断,第二种方式下只有DMA传输完成中断,中断基本没干什么只是置位一些标志位。
打印就是这条代码:printf("%d\n", data)

mvvm 发表于 2019-6-25 17:21:54

す疯Ⅱ恒す 发表于 2019-6-25 16:22
有好几个地方会出问题。先反问你几个问题。你的打印数据函数呢?用什么打印?打印数据量多少?打印一次数据 ...

你好,我刚才又补充了一下,麻烦你再看看:)

edmundlee 发表于 2019-6-25 23:13:09

有很多原因都可以引起这样的现象, 我偏向认为这是硬件的问题
如果说你是用USB 2 USART的转换板, 就把USART的线(包括地线)全都拔掉, 如果说拔了后就正常, 就是你的地线有问题

mvvm 发表于 2019-6-26 08:08:05

edmundlee 发表于 2019-6-25 23:13
有很多原因都可以引起这样的现象, 我偏向认为这是硬件的问题
如果说你是用USB 2 USART的转换板, 就把USAR ...

感谢回答,昨天我把函数重新修改一下(功能一样本来是为了看是这个函数的某个地方导致,然后就一点 一点的加)莫名其妙又好了。

waiman-156411 发表于 2019-6-26 14:26:18

我一般的做法是让DMA直接读取ADC,控制ADC的时钟频率和采集时间,使得ADC接近你想要的采样周期,然后可以连续采集,的DMA完成中断里,切换接收缓存重新开始采集。

这种方法可以释放MCU的资源,在DMA采集过程中,MCU可以对上一次的数据进行处理,甚至用DMA负责把数据从串口Printf出去。

但这样对于多个ADC连续转换,要求时序很快精度很高,或者采样周期很长的情况下不适合。

mvvm 发表于 2019-6-29 13:49:38

waiman-156411 发表于 2019-6-26 14:26
我一般的做法是让DMA直接读取ADC,控制ADC的时钟频率和采集时间,使得ADC接近你想要的采样周期,然后可以连 ...

感谢回答,我由于要考虑功耗,所以不想用DMA,而且也就一个通道数据,采样率也不高,完全足够
页: [1] 2 3
查看完整版本: STM32F4ADC采集的数据好坏和调用的某个函数有关