你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

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

[复制链接]
Love_ST 提问时间:2019-6-25 15:48 /
本帖最后由 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[1];
}
else
{
  ptr=Device_Status.Cur_Sampval[0];
}
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[0], (uint32_t*)Device_Status.Cur_Sampval[1], 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[Device_Status.Full_Buff_Node][Device_Status.Sample_Num++]=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;
      }
    }
}
}


采样波动小.png
采样波动大.png
采样到的波形.png
收藏 评论28 发布时间:2019-6-25 15:48

举报

28个回答
Love_ST 回答时间:2019-7-1 13:49:41
/* 完整的ADC配置 */
void Adc_Config1(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);  
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3, ENABLE);
/* PF4->IN14 */
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_4;
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_50MHz;
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_5Cycles;
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;
ADC_InitStructure.ADC_ExternalTrigConvEdge=ADC_ExternalTrigConvEdge_Rising;
ADC_Init(ADC3, &ADC_InitStructure);  
  
  ADC_RegularChannelConfig(ADC3, ADC_Channel_14, 1, ADC_SampleTime_480Cycles);
  
  ADC_EOCOnEachRegularChannelCmd(ADC3, ENABLE);
ADC_ITConfig(ADC3, ADC_IT_EOC, ENABLE);
  
NVIC_InitStructure.NVIC_IRQChannel=ADC_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_Init(&NVIC_InitStructure);
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);
  
  ADC_Cmd(ADC3, ENABLE);
  TIM_Cmd(TIM8, ENABLE);
}
/* ADC中断 */
void ADC_IRQHandler(void)
{
static uint8_t Num=0;
if(ADC_GetITStatus(ADC3, ADC_IT_EOC) != RESET)
{
  Test_Var.Databuf[Test_Var.Sample_Buff_Node][Test_Var.Sample_Buff_Num++]=ADC3->DR;
  if(Test_Var.Sample_Buff_Num == 20)
  {
   Test_Var.Sample_Buff_Num=0;
   Test_Var.Sample_Finish_Flag=SET;
      if(Test_Var.Sample_Buff_Node==0)
      {
        Test_Var.Sample_Buff_Node=1;
      }
      else
      {
        Test_Var.Sample_Buff_Node=0;
      }
  }
}
}
现在就想证明ADC配置是否正确,正确的话,就说明是硬件的问题了
waiman 回答时间:2019-6-26 14:26:18
我一般的做法是让DMA直接读取ADC,控制ADC的时钟频率和采集时间,使得ADC接近你想要的采样周期,然后可以连续采集,的DMA完成中断里,切换接收缓存重新开始采集。

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

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

评分

参与人数 1蝴蝶豆 +3 收起 理由
STMCU + 3

查看全部评分

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

你好用printf打印的,波特率115200,中断第一种方式下只有一个ADC转换完成中断,第二种方式下只有DMA传输完成中断,中断基本没干什么只是置位一些标志位。
打印就是这条代码:printf("%d\n", data)
Love_ST 回答时间:2019-6-25 15:48:57
Love_ST 回答时间:2019-6-25 15:49:52
调了一天了,调的我都都不知道如何找问题了
疯de_恒 回答时间:2019-6-25 16:22:32
有好几个地方会出问题。先反问你几个问题。你的打印数据函数呢?用什么打印?打印数据量多少?打印一次数据需要多少时间?有中断服务函数吗??中断里还干了些什么耗时的动作??
你回答完我这几个问题,估计你自己也就能找到问题了。

评分

参与人数 1蝴蝶豆 +3 收起 理由
STMCU + 3

查看全部评分

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

你好,我刚才又补充了一下,麻烦你再看看
edmundlee 回答时间:2019-6-25 23:13:09
有很多原因都可以引起这样的现象, 我偏向认为这是硬件的问题
如果说你是用USB 2 USART的转换板, 就把USART的线(包括地线)全都拔掉, 如果说拔了后就正常, 就是你的地线有问题

评分

参与人数 1蝴蝶豆 +2 收起 理由
STMCU + 2

查看全部评分

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

感谢回答,昨天我把函数重新修改一下(功能一样本来是为了看是这个函数的某个地方导致,然后就一点 一点的加)莫名其妙又好了。
Love_ST 回答时间:2019-6-29 13:49:38
waiman-156411 发表于 2019-6-26 14:26
我一般的做法是让DMA直接读取ADC,控制ADC的时钟频率和采集时间,使得ADC接近你想要的采样周期,然后可以连 ...

感谢回答,我由于要考虑功耗,所以不想用DMA,而且也就一个通道数据,采样率也不高,完全足够
waiman 回答时间:2019-6-30 23:58:00
mvvm 发表于 2019-6-29 13:49
感谢回答,我由于要考虑功耗,所以不想用DMA,而且也就一个通道数据,采样率也不高,完全足够 ...

功耗?DMA的功耗对比CPU而言不大,你可以在DMA采数据的时候,CPU进入Sleep,等DMA完成中断唤醒CPU,这样功耗更低。
不信你可以用cubemx计算一下
专注于你 回答时间:2019-7-1 09:05:18
可以看一下采集得部分

评分

参与人数 1蝴蝶豆 +1 收起 理由
STMCU + 1

查看全部评分

Love_ST 回答时间:2019-7-1 09:58:43
waiman-156411 发表于 2019-6-30 23:58
功耗?DMA的功耗对比CPU而言不大,你可以在DMA采数据的时候,CPU进入Sleep,等DMA完成中断唤醒CPU,这样 ...

cubemx不会用,暂时可以不考虑功耗问题,现在问题主要是采集到的数据异常跳动问题。
Love_ST 回答时间:2019-7-1 09:59:53
专注于你 发表于 2019-7-1 09:05
可以看一下采集得部分

采集部分?我有贴出来
安静ing 回答时间:2019-7-1 11:00:17
捕获.PNG
我是tim+adc+dma  DMA中断数据满了直接读就行 跟串口没关系 这是我200点的电网采样

评分

参与人数 1蝴蝶豆 +2 收起 理由
STMCU + 2

查看全部评分

12下一页
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版