本帖最后由 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; } } } } |
盘古UE-STM32F407工控板原理图
STM32F407 定时器触发DMA 求助大神
【MCU实战经验】基于STM32F407的音频播放器设计
【STM32F429心得\疑问】+STM32F4之FSMC和FMC
STM32F429读取IO口传输的数据速率
STM32F407ZGT6 手摸芯片背部重启
STM32F4 SPI 动作时,软件片选信号被拉高,IO口程序逻辑失控
STM32F401RE NUCLEO求助,串口一直不能进中断
读取STM32F407内部温度传感器值错误
STM32F429多路内部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配置是否正确,正确的话,就说明是硬件的问题了
这种方法可以释放MCU的资源,在DMA采集过程中,MCU可以对上一次的数据进行处理,甚至用DMA负责把数据从串口Printf出去。
但这样对于多个ADC连续转换,要求时序很快精度很高,或者采样周期很长的情况下不适合。
评分
查看全部评分
你好用printf打印的,波特率115200,中断第一种方式下只有一个ADC转换完成中断,第二种方式下只有DMA传输完成中断,中断基本没干什么只是置位一些标志位。
打印就是这条代码:printf("%d\n", data)
你回答完我这几个问题,估计你自己也就能找到问题了。
评分
查看全部评分
你好,我刚才又补充了一下,麻烦你再看看
如果说你是用USB 2 USART的转换板, 就把USART的线(包括地线)全都拔掉, 如果说拔了后就正常, 就是你的地线有问题
评分
查看全部评分
感谢回答,昨天我把函数重新修改一下(功能一样本来是为了看是这个函数的某个地方导致,然后就一点 一点的加)莫名其妙又好了。
感谢回答,我由于要考虑功耗,所以不想用DMA,而且也就一个通道数据,采样率也不高,完全足够
功耗?DMA的功耗对比CPU而言不大,你可以在DMA采数据的时候,CPU进入Sleep,等DMA完成中断唤醒CPU,这样功耗更低。
不信你可以用cubemx计算一下
评分
查看全部评分
cubemx不会用,暂时可以不考虑功耗问题,现在问题主要是采集到的数据异常跳动问题。
采集部分?我有贴出来
我是tim+adc+dma DMA中断数据满了直接读就行 跟串口没关系 这是我200点的电网采样
评分
查看全部评分