打印
[STM32F4]

DMA双缓冲模式

[复制链接]
2819|14
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
我使用STM32F4的ADC DMA方式,DMA采用双缓冲模式,采样数据为6通道正弦波形,当DMA在切换双缓冲的地址时,会导致ADC数据不准确,我对采样数据做递推平均值算法,在地址转换的位置会存在尖峰,我是每25个周期转换一次。
沙发
zzxueyan|  楼主 | 2017-7-11 09:10 | 只看该作者
做平均值递推算法后的情况,所采样波形为50HZ,25个周期为500ms,地址切换的时刻在1600点,

new.jpg (47.12 KB )

new.jpg

new2.jpg (148.7 KB )

new2.jpg

使用特权

评论回复
板凳
mmuuss586| | 2017-7-11 09:58 | 只看该作者
切换的时候不准也正常,最好将这段数据滤出掉;

6通道,采样的数据频率也不高,可以用DMA+中断啊;

使用特权

评论回复
地板
zzxueyan|  楼主 | 2017-7-11 10:13 | 只看该作者
mmuuss586 发表于 2017-7-11 09:58
切换的时候不准也正常,最好将这段数据滤出掉;

6通道,采样的数据频率也不高,可以用DMA+中断啊; ...

我需要把采样数据通过以太网发送道上位机软件,因为数据量比较大,所以采用了双缓冲的模式

使用特权

评论回复
5
mmuuss586| | 2017-7-11 10:22 | 只看该作者
zzxueyan 发表于 2017-7-11 10:13
我需要把采样数据通过以太网发送道上位机软件,因为数据量比较大,所以采用了双缓冲的模式 ...

哦,没用过这个模式;也有可能ST本身的BUG吧;


使用特权

评论回复
6
Xflyan| | 2017-7-11 14:01 | 只看该作者
用一个缓冲就可以了,长度设为两倍,不需要切换地址,用 DMA 半传输中断和完全传输中断,半中断时取前半部分数据,完全中断时取后半部分数据

使用特权

评论回复
7
zzxueyan|  楼主 | 2017-7-12 09:12 | 只看该作者
Xflyan 发表于 2017-7-11 14:01
用一个缓冲就可以了,长度设为两倍,不需要切换地址,用 DMA 半传输中断和完全传输中断,半中断时取前半部 ...

用一个缓冲试了以下,也是有这个问题,缓冲区满切换到首地址是,也会发生,可以看一下图上,第3200点的数据,很明显有偏差了。

new3.jpg (127.07 KB )

new3.jpg

使用特权

评论回复
8
Xflyan| | 2017-7-12 09:54 | 只看该作者
zzxueyan 发表于 2017-7-12 09:12
用一个缓冲试了以下,也是有这个问题,缓冲区满切换到首地址是,也会发生,可以看一下图上,第3200点的数 ...

你这个波形没看出来哪里有明显偏差了,ADC采样存在一定偏差是肯定的,要画出漂亮的波形,你还得再进行一下FIR或IIR滤波了

使用特权

评论回复
9
icecut| | 2017-7-12 10:13 | 只看该作者
你可以假定一下别的地方出错?
芯片的设计是很严谨的。只要能用都是专家级经过深思熟虑的。
不要首选怀疑芯片。你应该首先验证代码。其次是分析硬件。

最简单就是先不用双缓冲,看看是不是还有问题,或者把双缓冲的长度变成2/3看误差是不是还对应的上。

使用特权

评论回复
10
zzxueyan|  楼主 | 2017-7-12 11:07 | 只看该作者
icecut 发表于 2017-7-12 10:13
你可以假定一下别的地方出错?
芯片的设计是很严谨的。只要能用都是专家级经过深思熟虑的。
不要首选怀疑芯 ...

用单缓冲试了试,长度改为原来的2倍,出现尖峰的地方也随着改变了,都是在DMA缓存满后,切换地址的地方有问题,比如我从3200个点的地方缓存满了,从第3200个点之前取64个点做平均,或者从3200之后取64点做平均,值都很正常,就是从3200点之前取一部分采样数据,之后取一部分数据做平均,值就会有很明显的偏差,我用的是递推平均值算法,每次错后一点,做64点(每周期)平均值,和用FFT做有效值得出的波形是一样的,为了查找问题方便,用的求平均值。

使用特权

评论回复
11
zzxueyan|  楼主 | 2017-7-12 11:14 | 只看该作者
/* ÉùÃ÷TIMµÄ¼Ä´æÆ÷½á¹¹Ìå*/
    ADC_InitTypeDef       ADC_InitStruct;
    ADC_CommonInitTypeDef ADC_CommonInitStruct;
          DMA_InitTypeDef DMA_InitStruct;
          GPIO_InitTypeDef      GPIO_InitStructure;

          RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOF, ENABLE);
       
    /* ÉèÖÃAD²ÉÑù¶Ë¿Ú:Ä£ÄâÊäÈë*/   
    /* DMA2 channel2 configuration*/
    DMA_InitStruct.DMA_Channel = DMA_Channel_2;        //»Ö¸´Ä¬ÈÏÖµ¸Ã²Ù×÷ҪעÒâ²»ÒªÖظ´²Ù×÷Ôì³É¼Ä´æÆ÷״̬»ìÂÒ
    DMA_InitStruct.DMA_PeripheralBaseAddr = ADC3_DR_Address;     //ÍâÉèµØÖ·        32λµØÖ·
    DMA_InitStruct.DMA_Memory0BaseAddr = (u32)&Tx_Buffer[2];      //ÄÚ´æµØÖ·£¬Êý×é½á¹¹À´´æ´¢²É¼¯µÄADÁ¿ ¼Ó(u32) ÒòΪ´æ´¢Æ÷µØÖ·¼Ä´æÆ÷ÊÇ32λµÄ
    DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory;              //dma´«Êä·½Ïòµ¥Ïò ÍâÉèΪԴ¡£
                             
          DMA_InitStruct.DMA_BufferSize = sampling_dot*chn*25*2;        //ÉèÖÃDMAÔÚ´«Êäʱ»º³åÇøµÄ³¤¶È word 3¸öͨµÀ ÿ¸öͨµÀ32µã ¹²´æ96µãÖµ
    DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//ÉèÖÃDMAµÄÍâÉèµÝÔöģʽ£¬Ò»¸öÍâÉè
    DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;         //ÉèÖÃDMAµÄÄÚ´æµÝÔöģʽ£¬
    DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//ÍâÉèÊý¾Ý×Ö³¤
    DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//ÄÚ´æÊý¾Ý×Ö³¤
    DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;                 //ÉèÖÃDMAµÄ´«Êäģʽ£ºÁ¬Ðø²»¶ÏµÄÑ­»·Ä£Ê½
    DMA_InitStruct.DMA_Priority = DMA_Priority_High;             //ÉèÖÃDMAµÄÓÅÏȼ¶±ðΪ¸ß
    DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;
    DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
    DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;                    //
    DMA_Init(DMA2_Stream0, &DMA_InitStruct);

    /*Enable DMA1 channel1*/
    DMA_Cmd(DMA2_Stream0, ENABLE);       
                DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);
                DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_HTIF0);
                DMA_ITConfig(DMA2_Stream0, DMA_IT_HT, ENABLE);  
    DMA_ITConfig(DMA2_Stream0, DMA_IT_TC, ENABLE);

    /* Configure PC.03 (ADC Channel13) as analog input -------------------------*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
                GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
    GPIO_Init(GPIOF, &GPIO_InitStructure);
               
                RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3, ENABLE);
    /* Common configuration (applicable for the three ADCs) ******/
    /* Single ADC mode */
    ADC_CommonInitStruct.ADC_Mode = ADC_Mode_Independent;
    /* ADCCLK = PCLK2/2 */

                ADC_CommonInitStruct.ADC_Prescaler = ADC_Prescaler_Div6;
                ADC_CommonInitStruct.ADC_Prescaler = ADC_Prescaler_Div8;
    /* Available only for multi ADC mode */
//     ADC_CommonInitStruct.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
               
                ADC_CommonInitStruct.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
    /* Delay between 2 sampling phases */

                ADC_CommonInitStruct.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles;
    ADC_CommonInit(&ADC_CommonInitStruct);
               

    /* Configure ADC1 to convert continously channel/2/3 ***********/
    ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b;
    ADC_InitStruct.ADC_ScanConvMode = ENABLE ;
                ADC_InitStruct.ADC_ContinuousConvMode = DISABLE;
//    ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;
    ADC_InitStruct.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
    ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStruct.ADC_NbrOfConversion = chn;
    ADC_Init(ADC3, &ADC_InitStruct);


    /* ADC1 regular channels configuration [¹æÔòģʽͨµÀÅäÖÃ]*/
    ADC_RegularChannelConfig(ADC3, ADC_Channel_5 , 6, ADC_SampleTime_480Cycles);//ͨµÀ1£¬UA
    ADC_RegularChannelConfig(ADC3, ADC_Channel_10, 5, ADC_SampleTime_480Cycles);//ͨµÀ2£¬IA
    ADC_RegularChannelConfig(ADC3, ADC_Channel_6 , 4, ADC_SampleTime_480Cycles);//ͨµÀ3£¬UB
                ADC_RegularChannelConfig(ADC3, ADC_Channel_4 , 3, ADC_SampleTime_480Cycles);//ͨµÀ4£¬IB
                ADC_RegularChannelConfig(ADC3, ADC_Channel_7 , 2, ADC_SampleTime_480Cycles);//ͨµÀ5£¬UC
                ADC_RegularChannelConfig(ADC3, ADC_Channel_13 ,1, ADC_SampleTime_480Cycles);//ͨµÀ6£¬IC

                ADC_DMARequestAfterLastTransferCmd(ADC3, ENABLE);
               
    /*  Ê¹ÄÜADC1 DMA*/
    ADC_DMACmd(ADC3, ENABLE);
      
    /* Enable ADC1 ʹÄÜADC1*/
//    ADC_ITConfig(ADC3, ADC_IT_EOC, ENABLE);
         
    /* Enable ADC1 ʹÄÜADC1*/
    ADC_Cmd(ADC3, ENABLE);   
     

    ADC_SoftwareStartConv(ADC3);

使用特权

评论回复
12
icecut| | 2017-7-12 13:37 | 只看该作者
你不用dma,把3200点缓存里初始化成正确的数据,然后用你的函数去处理。看看有没有问题。

使用特权

评论回复
13
zzxueyan|  楼主 | 2017-7-12 15:52 | 只看该作者
icecut 发表于 2017-7-12 13:37
你不用dma,把3200点缓存里初始化成正确的数据,然后用你的函数去处理。看看有没有问题。 ...

在定时中断里只做启动AD转换的操作,就可以了,以前的程序里对采样次数做了累加,应该是条件限制造成了采样的延后。

使用特权

评论回复
14
icecut| | 2017-7-12 16:11 | 只看该作者
zzxueyan 发表于 2017-7-12 15:52
在定时中断里只做启动AD转换的操作,就可以了,以前的程序里对采样次数做了累加,应该是条件限制造成了采 ...

那应该没什么问题了吧。呵呵。st不用背黑锅了

使用特权

评论回复
15
请问楼主用以太网传数据的话不会应为速度的问题而舍弃掉某些数据吗

使用特权

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

本版积分规则

2

主题

9

帖子

0

粉丝