打印
[STM32F0]

STM32F030 ADC切换通道后采集到的数据不对可能是什么原因?

[复制链接]
1984|13
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
GZZXB|  楼主 | 2019-10-21 21:28 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
假设通道2 的ad值是200左右,如果不改变通道那么测的值一直都在200左右。 当采集完通道2 -->切换到通道1-->采集完通道1-->切换到通道2    。。。  这样切换时 通道1的值就变为600多了。 通道1是一个1.6V左右的直流电平。
搞了两天了没有找到原因,贴上代码:
volatile uint8_t  TimerTrgAdcOkFlag=0;
//------------------------------------   DMA 初始化 ----------------------------------------------------------------
void DmaInit(void)
{
        DMA_InitTypeDef    DMA_InitStructure;
        NVIC_InitTypeDef   NVIC_InitStructure;
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 , ENABLE);                           //使能DMA时钟
        DMA_DeInit(DMA1_Channel1);
        DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC1_DR_Address;         //DMA外设ADC基地址
        DMA_InitStructure.DMA_MemoryBaseAddr =(uint32_t)&AdResults;                   //DMA内存基地址*/
        DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                            //方向:外设为源地址 即外设-》内存
        DMA_InitStructure.DMA_BufferSize = ADC_SAMP_CNT;                              //搬运数量
        DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;              //DMA外设地址不自加
        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_Mode = DMA_Mode_Normal;                                 //搬运完DMA_BufferSize 后不再搬运
        DMA_InitStructure.DMA_Priority = DMA_Priority_High;
        DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
        DMA_Init(DMA1_Channel1, &DMA_InitStructure);
        NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;                      // 配置DMA中断源
        NVIC_InitStructure.NVIC_IRQChannelPriority = 1;                    
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
        DMA_ClearITPendingBit(DMA_IT_TC);                                             //清除一次DMA中断标志
        DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);                               //使能DMA传输完成中断
        DMA_ClearITPendingBit(DMA_IT_TC);                                             //清除一次DMA中断标志
        DMA_Cmd(DMA1_Channel1, ENABLE);                                               //使能DMA1
}


//配置ADC触发  = 定时器TIM15,用更新事件触发
void TimTrgInit(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
     //(1)选择TIM15 Update事件作为触发源(计数器溢出)
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM15, ENABLE);
        TIM_DeInit(TIM15);                                                            //配置基础
        TIM_TimeBaseStructure.TIM_ClockDivision=0;                                    //48M
        TIM_TimeBaseStructure.TIM_Prescaler=48-1;//48000;                               //48k 1Tick=1ms
        TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
        TIM_TimeBaseStructure.TIM_Period=20-1;//1000*1;                                //50K
        TIM_TimeBaseStructure.TIM_Period=25-1;//1000*1;                                //40K
        TIM_TimeBaseStructure.TIM_Period=50-1;//1000*1;                                //20K
        TIM_TimeBaseStructure.TIM_Period=FS_PRE-1;
        TIM_TimeBaseStructure.TIM_RepetitionCounter=0;                                //发生RepetitionCounter+1次溢出事件后中断
        TIM_TimeBaseInit(TIM15,&TIM_TimeBaseStructure);
        TIM_SelectOutputTrigger(TIM15,TIM_TRGOSource_Update);                         //TIM15的Update事件作为外部TRGO
        TIM_ClearFlag(TIM15,TIM_FLAG_Update);                                         //防止一上电进入中断 貌似可以不用??
        TIM_Cmd(TIM15,ENABLE);                                                        //使能TIM15
        ADC_StartOfConversion(ADC1);                                                  //如果是软件触发将立即开始转换,
                                                                                      //如果是定时器触发则等待定时触发信号来到启动。
        NVIC_InitStructure.NVIC_IRQChannel = TIM15_IRQn;                              // 配置DMA中断源
        NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
        TIM_ITConfig(TIM15,TIM_IT_Update,ENABLE);                   //使能中断用来调试,
}

//-----------配置ADC为TIM15触发--------------
void AdcConfig(void)
{
    GPIO_InitTypeDef        GPIO_InitStructure;
    ADC_InitTypeDef         ADC_InitStructure;
    //DMA_InitTypeDef         DMA_InitStructure;
    //TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    //NVIC_InitTypeDef NVIC_InitStructure;
    //----gpio----
        RCC_APB2PeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_5;                        //Configure PA.0 PA.1  as analog input
    //GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;                        //Configure PA.0 PA.1  as analog input
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
        //-----adc-----
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
        RCC_ADCCLKConfig(RCC_ADCCLK_PCLK_Div4);        //ADC时钟不能大于14M
        ADC_DeInit(ADC1);
        ADC_StructInit(&ADC_InitStructure);
        ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;                          //右对齐
        ADC_InitStructure.ADC_ExternalTrigConvEdge=ADC_ExternalTrigConvEdge_Rising;   //上升沿触发
        ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_T15_TRGO;         //TIM15触发
        // ADC_InitStructure.ADC_ExternalTrigConv =  ADC_ExternalTrigConv_T1_CC4;//选择TIM1 Compare&Capture4 作为触发源
        ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;                        //12位精度
        ADC_InitStructure.ADC_ContinuousConvMode=DISABLE;                             //连续模式禁止 要由定时器触发
        ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Upward;               //CH0-18
        ADC_Init(ADC1, &ADC_InitStructure);
        ADC_OverrunModeCmd(ADC1,ENABLE);                                              //数据覆盖方式,只保留最新的转换数据
        //ADC_ChannelConfig(ADC1,ADC_Channel_0|ADC_Channel_1,ADC_SampleTime_1_5Cycles);
        ADC_ChannelConfig(ADC1,ADC_Channel_1,ADC_SampleTime_1_5Cycles);
        ADC_GetCalibrationFactor(ADC1);                                               // 开始ADC校准
        ADC_Cmd(ADC1,ENABLE);
        while(ADC_GetFlagStatus(ADC1, ADC_FLAG_ADEN)==RESET);                         //等待ADC准备好
        ADC_DMACmd(ADC1,ENABLE);                                                      //使能ADC
        ADC_DMARequestModeConfig(ADC1,ADC_DMAMode_Circular);                          //ADC模式
        //ADC_DMARequestModeConfig(ADC1,ADC_DMAMode_OneShot);                          //DMA循环模式
        //------dma-----------
        DmaInit();
        //-------trg timer------
        TimTrgInit();
}


//传输完512个数据后,进入中断。在中断中我关闭触发定时器 并TimerTrgAdcOkFlag=1 声明采集传送完毕。
void DMA1_Channel1_IRQHandler()  
{  
    //DMA_InitTypeDef         DMA_InitStructure;
        if(DMA_GetITStatus(DMA1_IT_TC1) != RESET)                                     //判断DMA传输完成中断                       
        {
                TimerTrgAdcOkFlag = 1;
                //toggle(GPIOC,GPIO_Pin_13);
                TIM_Cmd(TIM15,DISABLE);                                                   //关闭触发用的定时器
                DMA_ClearITPendingBit(DMA1_IT_TC1);                                           //清除DMA中断标志位  
        }      
}  

//-----------------------至此某个通道的一组数据就已经成功搬运存储完毕, 然后我有个timer3 是不停的在跑的并开启了溢出中断。在中断检测 当 TimerTrgAdcOkFlag  为0时就会再次初始化DMA和触发定时器。  这样就会继续采集搬运了

void TIM3_IRQHandler()
{
    //static uint8_t cnt=0;
    if(TIM_GetITStatus(TIM3, TIM_IT_Update)!= RESET)            //判断发生update事件中断  
    {  
        tick++;
        TIM_ClearITPendingBit(TIM3, TIM_IT_Update);     //清除update事件中断标志  
            //toggle(GPIOC,GPIO_Pin_13);
            if(TimerTrgAdcOkFlag==0)                                                //上次数据处理完毕
            {
                 if(!(TIM15->CR1 & TIM_CR1_CEN))                                    //且TIM15定时器 在关闭状态
                 {
                      DmaInit();
                     TimTrgInit();                                     //开启下一笔数据采集
                 }
            }
    }
   
}

//-----------------------  当我要切换通道时调用下面这个函数-----------------------------
void SetAdcChannel(uint32_t ADC_Channel)
{
    while(0==TimerTrgAdcOkFlag);              //等待上一次数据采集完成
        ADC1->CR &= (uint32_t)(~ADC_CR_ADSTART);  //设置START=0 才可以修改通道
        ADC_ChannelConfig(ADC1,ADC_Channel,ADC_SampleTime_1_5Cycles);
        ADC1->CHSELR = (uint32_t)ADC_Channel;     //修改通道
        while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_ADRDY)){};
        DelayMs(5);
        TimerTrgAdcOkFlag=0;                      //申明上一次数据处理完毕 , timer3中断里如果判断为0 就开始下次采集
              //ADC1->CR |= (uint32_t)ADC_CR_ADSTART;     //使能触发转换
        //while(0==TimerTrgAdcOkFlag);              //等待上一次数据采集完成
        //TimerTrgAdcOkFlag=0;   
}

SetAdcChannel(ADC_Channel_2);  等待TimerTrgAdcOkFlag=1 ----》查看结果  ---》SetAdcChannel(ADC_Channel_2);  等待TimerTrgAdcOkFlag=1 ----》查看结果 。。。  这样ADC_Channel_2数据是对的
SetAdcChannel(ADC_Channel_2);  等待TimerTrgAdcOkFlag=1 ----》查看结果  ---》SetAdcChannel(ADC_Channel_1);  等待TimerTrgAdcOkFlag=1 ----》查看结果   .。。   这样ADC_Channel_2数据是错的

使用特权

评论回复
沙发
caoenq| | 2019-10-22 11:04 | 只看该作者
这就是STM32的dam+ADC数据串道的问题,我跟香主反应过这个问题,后来也是没有答案。最直接的办法:不要使用DMA了。

使用特权

评论回复
板凳
香水城| | 2019-10-22 12:26 | 只看该作者
caoenq 发表于 2019-10-22 11:04
这就是STM32的dam+ADC数据串道的问题,我跟香主反应过这个问题,后来也是没有答案。最直接的办法:不要使用D ...

不知你啥时问的,应该时代码处理上哪里没处理好。

不要DMA多可惜啊。

使用特权

评论回复
地板
香水城| | 2019-10-22 12:28 | 只看该作者
caoenq 发表于 2019-10-22 11:04
这就是STM32的dam+ADC数据串道的问题,我跟香主反应过这个问题,后来也是没有答案。最直接的办法:不要使用D ...

我看你DMA配置在 循环模式。 ADC是通过TIMER触发,那在切换通道过程中,是否可能发生ADC转换呢?

使用特权

评论回复
5
GZZXB|  楼主 | 2019-10-22 13:20 | 只看该作者
香水城 发表于 2019-10-22 12:28
我看你DMA配置在 循环模式。 ADC是通过TIMER触发,那在切换通道过程中,是否可能发生ADC转换呢?
...

  不存在啊,我在中断里已经把触发定时器给关闭了。  切换完通道后,TimerTrgAdcOkFlag=0;  然后下一个TIM3_IRQHandler()  判断为0,才再次开启触发转换啊。

使用特权

评论回复
6
香水城| | 2019-10-22 14:02 | 只看该作者
GZZXB 发表于 2019-10-22 13:20
不存在啊,我在中断里已经把触发定时器给关闭了。  切换完通道后,TimerTrgAdcOkFlag=0;  然后下一个TIM ...


你说的情况还有点没看得很清楚。

你是先第1个通道触发转换一批数据,然后做通道切换后对第2个通道转换一批数据,之后又切换到第一个通道进行转换,是这样吗? 如果这样的话,切换通道过程若没有及时停止定时器,切换前很可能发生过前一通道的转换。

另外,你注意下中断的优先级的合理安排。比方说,你那个TIM3的中断不停在跑,是否可能跟DMA中断有冲突或干扰的地方。


使用特权

评论回复
7
GZZXB|  楼主 | 2019-10-22 14:32 | 只看该作者
香水城 发表于 2019-10-22 14:02
你说的情况还有点没看得很清楚。

你是先第1个通道触发转换一批数据,然后做通道切换后对第2个通道转换 ...

   是的就是两个通道切换采集。
切换通道函数第一句代码  while(0==TimerTrgAdcOkFlag);              //等待上一次数据采集完成
就是等待DMA中断完成后才执行下一步。  TimerTrgAdcOkFlag 只在DMA传输完成中断里置1,一进入DMA中断就立即关闭定时器。
    也许在产生DMA中断到执行关闭定时器这个代码段时间,可能还会让定时器触发ad一次,但是我是将整个buf做滤波来观察的,如果错一个应该完全没有影响才对吧。
    TIM3中断优先级应该没有影响吧,因为在TIM3中断里有检测触发定时器是否关闭,只有在关闭时才会启动下一次采集。  而触发定时器在DMA中断里才会关闭。

使用特权

评论回复
8
GZZXB|  楼主 | 2019-10-22 14:37 | 只看该作者
香水城 发表于 2019-10-22 14:02
你说的情况还有点没看得很清楚。

你是先第1个通道触发转换一批数据,然后做通道切换后对第2个通道转换 ...

    验证了下你说的优先级可能的问题,将TIM3中断优先级设为1 DMA中断优先级设置为0 ,现象还是存在。

使用特权

评论回复
9
香水城| | 2019-10-23 12:18 | 只看该作者
我就你的问题做了测试,TIM15触发,使用DMA的循环模式传输数据。
通道1转换一批数据后,切换到另一通道。 测试正常。

你注意下,在通道切换时先要将ADC暂时停掉,然后修改完通道后在开启。
当然,ADC转换依然保持定时器的触发转换。

使用特权

评论回复
10
yaosongjin2018| | 2019-10-24 15:33 | 只看该作者
F030设置通道库函数有问题,你可以百度一下,记得是里面有一句或运算的,要把或运算符去掉

使用特权

评论回复
11
internally| | 2019-10-24 16:06 | 只看该作者
应该就是香水城版主说的问题

使用特权

评论回复
12
liaotian001| | 2019-11-5 15:12 | 只看该作者
我看你使用了较多的MCU类型,关于这个问题,看一下我的帖子,对你有所帮助。

使用特权

评论回复
13
keaibukelian| | 2019-11-18 11:47 | 只看该作者
读取的数据寄存器有没有进行更改

使用特权

评论回复
14
chip1008| | 2020-3-27 13:57 | 只看该作者
不知道楼主的问题是否已经解决了。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

96

主题

331

帖子

10

粉丝