Оцифровываю данные так: АЦП -> DMA -> прерывание half transfer и full transfer.
Если процессор всегда в активном режиме, все работает прекрасно.
Но как только в холостой основной цикл добавляю остановку процессора (WFI), то начинает твориться следующее:
DMA генерирует прерывания как положено, с той же частотой и вовремя, т.е. как и без WFI. Но как только активирую WFI, то в RAM, куда DMA должна сохранять результат оцифровки, данные не записываются.
Точнее, экспериментально определено, что в буфере DMA обновляется только та часть данных, которая оцифровалась в моменты времени, когда процессор был в активном режиме (т.е. вертелся в обработчиках прерываний). Определил это добавлением холостого цикла в конце обработчика прерываний. И от величины задержки зависит, какие значения запишутся в RAM.
При этом все счетчики контроллера DMA работают корректно, что в sleep mode, что без него, т.е. нужный канал АЦП записывается всегда по нужному адресу. Счетчики DMA считывал в обработчике DMA, и делаю вывод что прерывания DMA вызываются вовремя (без задержек).
Вероятно, я что-то упустил в настройках или еще где, но по работе DMA в sleep mode ничего интересного не нашел. В errata также ничего полезного нет.
Слабо верится что такой косяк до ST до сих пор не дошел.
Куски кода:
CODE
ADC1->CFGR1=ADC_CFGR1_AWDSGL|ADC_CFGR1_CONT|ADC_CFGR1_DMACFG|ADC_CFGR1_DMAEN;
ADC1->CFGR2=ADC_CFGR2_CKMODE_0;
ADC1->CHSELR=0x02FF|ADC_CHSELR_CHSEL16;
ADC1->SMPR=6;// скоростью оцифровки также игрался
ADC->CCR|=ADC_CCR_TSEN;
// ADC DMA
DMA1_Channel1->CCR&=~DMA_CCR_EN;
DMA1_Channel1->CCR=DMA_CCR_PL_0*3 | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0 | DMA_CCR_MINC | DMA_CCR_CIRC | DMA_CCR_HTIE | DMA_CCR_TCIE | DMA_CCR_TEIE;
DMA1_Channel1->CNDTR=20;
DMA1_Channel1->CPAR=(uint32_t)&(ADC1->DR);
DMA1_Channel1->CMAR=(uint32_t)&adcbuf;
DMA1_Channel1->CCR|=DMA_CCR_EN;
...
ADC1->CR=ADC_CR_ADSTART;
---------------- Основной цикл:
...
__enable_irq();
while (1) {
__WFI();// WFI cause DMA fill error in ADC bufer
};
------------------ И обработчик:
// Для фиксации позиции буфера DMA при обработке.
volatile int bp1, bp2;
volatile int bp1a, bp2a;
__irq void DMA1_Channel1_IRQHandler() {
int di=DMA1->ISR;
if (di & DMA_ISR_TEIF1) {
DMA1->IFCR=DMA_IFCR_CTEIF1;
}
if (di & DMA_ISR_HTIF1) {// нечетный вызов (первая половина буфера готова)
bp1=DMA1_Channel1->CNDTR;
DMA1->IFCR=DMA_IFCR_CHTIF1;
adc_new_data(&adcbuf[0]);
bp1a=DMA1_Channel1->CNDTR;
tc_cnt1++;
}
if (di & DMA_ISR_TCIF1) {// четный вызов (вторая половина буфера готова)
bp2=DMA1_Channel1->CNDTR;// Зафиксируем позицию DMA перед съемом данных
DMA1->IFCR=DMA_IFCR_CTCIF1;
adc_new_data(&adcbuf[1]);
bp2a=DMA1_Channel1->CNDTR;// Зафиксируем позицию DMA после съема данных
tc_cnt2++;
}
//for (int i=0; i<80; i++) __NOP();// Цикл, который влияет на количество байт, которые обновляются в буфере DMA
//(соответственно, появляется больше правильных выборок от начала буфера и от середины буфера)
}
ADC1->CFGR2=ADC_CFGR2_CKMODE_0;
ADC1->CHSELR=0x02FF|ADC_CHSELR_CHSEL16;
ADC1->SMPR=6;// скоростью оцифровки также игрался
ADC->CCR|=ADC_CCR_TSEN;
// ADC DMA
DMA1_Channel1->CCR&=~DMA_CCR_EN;
DMA1_Channel1->CCR=DMA_CCR_PL_0*3 | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0 | DMA_CCR_MINC | DMA_CCR_CIRC | DMA_CCR_HTIE | DMA_CCR_TCIE | DMA_CCR_TEIE;
DMA1_Channel1->CNDTR=20;
DMA1_Channel1->CPAR=(uint32_t)&(ADC1->DR);
DMA1_Channel1->CMAR=(uint32_t)&adcbuf;
DMA1_Channel1->CCR|=DMA_CCR_EN;
...
ADC1->CR=ADC_CR_ADSTART;
---------------- Основной цикл:
...
__enable_irq();
while (1) {
__WFI();// WFI cause DMA fill error in ADC bufer
};
------------------ И обработчик:
// Для фиксации позиции буфера DMA при обработке.
volatile int bp1, bp2;
volatile int bp1a, bp2a;
__irq void DMA1_Channel1_IRQHandler() {
int di=DMA1->ISR;
if (di & DMA_ISR_TEIF1) {
DMA1->IFCR=DMA_IFCR_CTEIF1;
}
if (di & DMA_ISR_HTIF1) {// нечетный вызов (первая половина буфера готова)
bp1=DMA1_Channel1->CNDTR;
DMA1->IFCR=DMA_IFCR_CHTIF1;
adc_new_data(&adcbuf[0]);
bp1a=DMA1_Channel1->CNDTR;
tc_cnt1++;
}
if (di & DMA_ISR_TCIF1) {// четный вызов (вторая половина буфера готова)
bp2=DMA1_Channel1->CNDTR;// Зафиксируем позицию DMA перед съемом данных
DMA1->IFCR=DMA_IFCR_CTCIF1;
adc_new_data(&adcbuf[1]);
bp2a=DMA1_Channel1->CNDTR;// Зафиксируем позицию DMA после съема данных
tc_cnt2++;
}
//for (int i=0; i<80; i++) __NOP();// Цикл, который влияет на количество байт, которые обновляются в буфере DMA
//(соответственно, появляется больше правильных выборок от начала буфера и от середины буфера)
}