ADC overrun的問題

334 views
Skip to first unread message

Fu Chiao Chang

unread,
Nov 28, 2013, 7:30:48 AM11/28/13
to stm...@googlegroups.com
我要做的是以DMA接收1024筆ADC的資料然後處理,接著再接收另外1024筆ADC的資料然後處理的循環,所以必須自行控制ADC和DMA的開始時間。
目前遇到的問題是,重新啟動ADC和DMA一定次數就會產生overrun。
因為接收的timing很重要,所以希望能夠直接避免overrun的產生。
以下是我程式的相關部份:
uint16_t AD1_VALUE[1024] = {0};
uint16_t AD2_VALUE[1024] = {0};
void initADC(void){
    ADC_InitTypeDef ADC_InitStructure;
    ADC_CommonInitTypeDef ADC_CommonInitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
    ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
    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 = DISABLE;
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
    ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfConversion = 1;
    ADC_Init(ADC3, &ADC_InitStructure);

    ADC_RegularChannelConfig(ADC3, ADC_Channel_3, 1, ADC_SampleTime_3Cycles);

    ADC_DMARequestAfterLastTransferCmd(ADC3, DISABLE);
    ADC_EOCOnEachRegularChannelCmd(ADC3, DISABLE);

    ADC_DMACmd(ADC3, ENABLE);

    ADC_Cmd(ADC3, ENABLE);

}

void initADC_DMA()
{
    DMA_InitTypeDef DMA_InitStructure;
    DMA_StructInit(&DMA_InitStructure);
    DMA_InitStructure.DMA_Channel = DMA_Channel_2;
    DMA_InitStructure.DMA_PeripheralBaseAddr = (unsigned int)(ADC3_BASE + 0x4c);
    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)AD1_VALUE;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
    DMA_InitStructure.DMA_BufferSize = 1024;
    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_Priority = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    DMA_DeInit(DMA2_Stream0);
    DMA_Init(DMA2_Stream0, &DMA_InitStructure);
    DMA_ITConfig(DMA2_Stream0, DMA_IT_TC, DISABLE);
    DMA_Cmd(DMA2_Stream0, ENABLE);
}

main()
{
    initADC();
    initADC_DMA();
    ADC_SoftwareStartConv(ADC3);
    while(1) {
    //do something
        if (DMA_GetCmdStatus(DMA2_Stream0) == DISABLE) {    //準備接收新的1024筆資料
            ADC_DMACmd(ADC3, DISABLE);
            while (ADC_GetFlagStatus(ADC3, ADC_FLAG_EOC) == RESET) ;
            while (ADC3->SR & ADC_FLAG_EOC)
                ADC_ClearFlag(ADC3, ADC_FLAG_EOC);
            while (DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0) == SET)
                DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);
            while (DMA_GetFlagStatus(DMA2_Stream0, DMA_FLAG_TCIF0) == SET)
                DMA_ClearFlag(DMA2_Stream0, DMA_FLAG_TCIF0);
            DMA_SetCurrDataCounter(DMA2_Stream0, 1024);
            DMA2_Stream0->M0AR = (i & 0x01 ? (uint32_t)AD1_VALUE: (uint32_t)AD2_VALUE);
            DMA_Cmd(DMA2_Stream0, ENABLE);
            ADC_DMACmd(ADC3, ENABLE);
            ADC_SoftwareStartConv(ADC3);
//overrun在這裡被發現
        }
    }
}

謝謝。

Bee

unread,
Nov 29, 2013, 12:23:44 AM11/29/13
to stm...@googlegroups.com
一包資料取完後,就先關閉DMA及ADC。
下次發動時,再開DMA及ADC。不過開啟前,先將DMA的半滿及全滿Flag清掉,ADC的overrun flag清掉,就可以正常啟動。
如果要連確實數目都要控制,還是用timer做為觸發ADC的來源。利用Repetition Counter做為計數。
但因為你計數大小超過256,所以還是有問題。
此時會想用DMA中斷去做次數控制,結果一定不行,我在上面死過。
最後使用timer串接才能做出來。
也就是使用另一個timer去計數,這樣才成功。

這是我寫了一年的娙驗。

Bee

unread,
Nov 29, 2013, 12:30:21 AM11/29/13
to stm...@googlegroups.com
忘了問你要取多快,我做到1.6MSPS

Fu Chiao Chang

unread,
Nov 29, 2013, 1:25:08 AM11/29/13
to stm...@googlegroups.com
後來發現是清除FLAG_EOC導致的,不去清除FLAG_EOC就正常了。

因為我的應用來說,CPU還有其他事情要做,能用DMA還是要儘量用。

每次取1024筆,這樣大概可以到2.8MSPS。

感謝你的回復。

Bee於 2013年11月29日星期五UTC+8下午1時30分21秒寫道:
忘了問你要取多快,我做到1.6MSPS

Bee

unread,
Nov 29, 2013, 4:21:47 AM11/29/13
to stm...@googlegroups.com
2.8MSPS? 不可能,除非你不是用12位元。
因為要使用到2.4MSPS,還是將主頻改為72MHz的倍率。這個會影響軟體執行能力。
不過因為IO是系統最慢的,所以還是要做。

但,我公司要我做到4.0MSPS。還要使用ADC串接間隔取樣。
我的應用至少要開10bit以上的解析度。

Bee

unread,
Nov 29, 2013, 5:07:12 AM11/29/13
to stm...@googlegroups.com
看了一下您的程式,發現我的系統比您做的事還多,因為多架了RTOS。
另外依您的寫法,還可以使用DMA半軟體式FIFO,這樣取樣到一半就可以做事。
若是使用Double Buffer的能力,可以將資料收取和資料處理分在二個bank的記憶體上面。

我是拿來做影像處理的。內部RAM完全不足,已經外加2MB的SRAM仍不足。
目前已經考慮往STM32F427/429來使用,希望使用SDRAM的容量來解決問題。

Fu Chiao Chang

unread,
Nov 29, 2013, 5:07:23 AM11/29/13
to stm...@googlegroups.com
2.74(KHz)*1024(Samples),應該就差不多2.8MSPS吧。

我是用12bit的。

主頻我沒有用72MHz的倍率,我也不是很懂你說要用那個速率的理由。

沒弄錯的話,我應該是用168MHz。

我量測的方法是用DMA結束的interrupt去重開DMA和ADC並且toggle GPIO,這樣就可以用示波器去量GPIO頻率。

這樣應該沒錯吧。

另外,我用的開發板是STM32F4 Discovery。

Bee於 2013年11月29日星期五UTC+8下午5時21分47秒寫道:

Bee

unread,
Nov 29, 2013, 5:12:03 AM11/29/13
to stm...@googlegroups.com
168MHz主頻被我放棄。因為ADC只能工作在36MHz,超過就會非線性,在手冊中有寫。
168MHz的ADC只能使用除8,就會變成21MHz,遠低於36MHz。
所以主頻改用144MHz,這樣ADC可以使用除4,以36Mhz工作。

Bee

unread,
Nov 29, 2013, 5:14:17 AM11/29/13
to stm...@googlegroups.com
主頻144Mhz,APB1就會是72MHz,ADC前除最小為2。所以為除4。

Fu Chiao Chang

unread,
Nov 29, 2013, 6:07:37 AM11/29/13
to stm...@googlegroups.com
可是我的ADC是在APB2底下,APB2是84MHZ,APB1則是42MHz。

你提到ADC前還要再除二的部份,我還沒在我手邊的文件看到,能不能麻煩提供一下文件的編號。


我在程式裡這樣設定 ADC_RegularChannelConfig(ADC3, ADC_Channel_3, 1, ADC_SampleTime_3Cycles);

依據設定,42MHz/2/3(Cycles per sample),這樣也有7MSPS,顯然還是和文件上的2.4MSPS差很多。

應該是還差conversion time吧?目前還沒翻到文件說conversion time要多長,話說回來,ADC sampling rate是這樣算的嗎?

Bee於 2013年11月29日星期五UTC+8下午6時14分17秒寫道:
主頻144Mhz,APB1就會是72MHz,ADC前除最小為2。所以為除4。

Bee

unread,
Nov 29, 2013, 9:12:15 PM11/29/13
to stm...@googlegroups.com
APB2才是72MHz,手上無程式,寫錯。
Sample time是取樣時間,最小為3,使用12位元還要加12個轉換時間。因為ADC Clock為36MHz,36MHz/15=2.4MHz。是這樣來的。
ADC使用電容電荷分配的方式進行數位轉換,若是沒有給足夠時間給它進行電荷平衡,轉出結果就無法保證其線性度。
ST有文件說明其ADC轉換原理,就會了解為何要有取樣時間及轉換時間。看了一段時間,也忘了在那。
你的需求若是2.8MHz,使用10位元可以達成,36MHz/(3+10)=2.77MHz,差不多是你要的。
取樣時間也可以當成濾波的方法,因為原理為電容儲存電荷的方式,也就是突波可以被電容平均掉,所以加長取樣時間有濾波效果。
又因是電容取樣電荷方式,所以取樣的電阻值會因取樣時間而改變。若是外部訊號給的電流值太小,是要加長取樣時間,才能保證取得足夠電荷進行ADC轉換工作。

Bee

unread,
Nov 29, 2013, 9:49:12 PM11/29/13
to stm...@googlegroups.com
你的設定為42MHz/(3+12)=2.8MHz。
真的是用在超頻的設定。

Fu Chiao Chang

unread,
Nov 30, 2013, 11:54:25 AM11/30/13
to stm...@googlegroups.com
喔喔喔,原來如此。

受益良多,感激不盡。

Bee於 2013年11月30日星期六UTC+8上午10時12分15秒寫道:
Reply all
Reply to author
Forward
0 new messages