KnightIgor 2 21 июля, 2014 Опубликовано 21 июля, 2014 (изменено) · Жалоба Здравствуйте, коллеги. Не побеждается ADC3. Имеем: - STM32F103VC на 24MHz (не 72 для уменьшения потребления, т.к. есть back up на аккумуляторе), - ADC3 в режиме сканирования трех каналов (11, 12 и 13), ADC тактируется 12MHz. - естественно DMA2, канал 5, который складывает оцифрованное в буфер U16 длиной 32 триплета - всего 96 слов; таких буферов, конечно, два: один заполняем, из другого читаем. - таймер 8, который с частотой 100Hz (замеряно) пихает ADC3 своим событием обновления (TRGO). - прерывание от завершения передач DMA (по флагу TC), которое устанавливает флаг готовности буфера, переключает буферы и перегружает DMA канал. В принципе, вся кухня работает. Но. После рестарта может случиться, что данные оказываются смещенными в буфере: например, на месте канала 11 будут данные из канала 12, на месте 12 - из 13, ну а на месте 13 - из 11. Может случиться и сдвиг более дальнего порядка - в 11 - из 13, ну и так далее, по кругу. Причем этот сдвиг случается один раз при старте системы, а в процессе работы все остается фиксированно, без перескоков. Я тщательно проанализировал последовательность инициализации и попытался в прерывании DMA сбрасывать бит STRT в ADC3->SR и "прочищать" ADC3->DR (чтением) перед тем как перегрузить DMA. Тем не менее в прерывании я мог всегда(!) поймать ситуацию при самом _первом_ вхождении, когда после перегрузки DMA его счетчик тут же уменьшался на единицу, словно ADC3 припрятал запрос к DMA несмотря на "прочистку". Последнее, что я сделал, - поднял приоритет DMA до "kill 'em all", а в прерывании - выключал бит DMA в ADC3->CR2, делал прочистку ADC и перегрузку DMA и включал ADC3->CR2 снова. На моей плате это вроде привело к успеху (счетчик DMA не уменьшался сразу), а вот у коллеги - нет. Идеи? Заранее благодарен. Изменено 21 июля, 2014 пользователем KnightIgor Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ViKo 1 21 июля, 2014 Опубликовано 21 июля, 2014 · Жалоба Еще бы таймер "прочистить", чтобы события лишнего не генерил. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
KnightIgor 2 21 июля, 2014 Опубликовано 21 июля, 2014 (изменено) · Жалоба Еще бы таймер "прочистить", чтобы события лишнего не генерил. Ну... это вряд ли поможет. Таймер стучит каждые 10мс - вечность. Оцифровка всех трех каналов занимает около 3х 85 циклов = 255 или ~22мкс (при 12MHz ADC). Пусть это был последний триплет, и сработало прерывание DMA. Прерывание из трех строк не зависает на 10мс. П.С. очистка таймера (каким образом?) наверняка приведет к изменению периода, то есть к шуму квантизации... Не кошерно как-то... Изменено 21 июля, 2014 пользователем KnightIgor Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AlHakim 0 21 июля, 2014 Опубликовано 21 июля, 2014 · Жалоба В принципе, вся кухня работает. Но. После рестарта может случиться, что данные оказываются смещенными в буфере: например, на месте канала 11 будут данные из канала 12, на месте 12 - из 13, ну а на месте 13 - из 11. Может случиться и сдвиг более дальнего порядка - в 11 - из 13, ну и так далее, по кругу. Причем этот сдвиг случается один раз при старте системы, а в процессе работы все остается фиксированно, без перескоков. вот недавно сам столкнулся с таким же. Нашел решение на этом же форуме. А именно сбрасывать регулярные каналы. И все заработал вот мой код //=== Прерывания DMA ADC1 ===// void DMA1_Channel1_IRQHandler(void) { ADC1->CR2 &= ~ADC_CR2_DMA;// | ADC_CR2_ADON); // выключаем DMA запросы АЦП ADC1->SR &= ~(ADC_SR_STRT); // сброс флага Overrun и старта преобразования ADC1->SQR1 &= ~ADC_SQR1_L; // сброс количества последовательных преобразований [сброс указателя канала] DMA1_Channel1->CCR &= ~ (DMA_CCR_EN |DMA_CCR_TCIE); //// деактивация 1го потока DMA1 if(DMA1->ISR & DMA_ISR_TCIF1) EventFlags.Bit.ADCConvFinish = 1; // проверяем прерывание по завершению передачи блока DMA1->IFCR=DMA_IFCR_CGIF1|DMA_IFCR_CTCIF1|DMA_IFCR_CHTIF1|DMA_IFCR_CTEIF1; //снимаем биты в DMA1->ISR (пока все) } //=== Прерывания TMR2 ===// void TIM2_IRQHandler(void){ // Период - 160 мксек при частоте сети 50 Гц. vu32 aa, cosa, cosb, cosc; vu8 i; TIM2->SR &= ~TIM_SR_UIF; // Снять флаг прерывания. DMA1_Channel1->CNDTR = 18; // указываем число пересылаемых данных 3 канала по 6-и перобразовании DMA1_Channel1->CCR |= DMA_CCR_EN | DMA_CCR_TCIE;// запустили очередное преобразование ADC (должны успеть,ааа) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ViKo 1 21 июля, 2014 Опубликовано 21 июля, 2014 · Жалоба Ну... это вряд ли поможет. Таймер стучит каждые 10мс - вечность. Оцифровка всех трех каналов занимает около 3х 85 циклов = 255 или ~22мкс (при 12MHz ADC). Пусть это был последний триплет, и сработало прерывание DMA. Прерывание из трех строк не зависает на 10мс. По таймеру все 3 канала преобразуются, значит? Группа. 8-битовые? А по 16 битов, может, без сбоев пойдет? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
KnightIgor 2 21 июля, 2014 Опубликовано 21 июля, 2014 · Жалоба По таймеру все 3 канала преобразуются, значит? Группа. Группа. 8-битовые? А по 16 битов, может, без сбоев пойдет? Собственно, 12-битовые (разрядность ADC), выровненные влево до 16-ти бит (разрядность DR), DMA забирает как U16. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 121 21 июля, 2014 Опубликовано 21 июля, 2014 · Жалоба После рестарта может случиться, что данные оказываются смещенными в буфере: например, на месте канала 11 будут данные из канала 12, на месте 12 - из 13, ну а на месте 13 - из 11.Значит на момент старта DMA АЦП уже успело сделать два измерения и результат первого для DMA оказался потерян. В какой последовательности вы запускаете всю эту кухню? По моим понятиям надо делать так: 1) таймер остановлен 2) Настраиваем АЦП, сбрасываем все готовности, которые могли остаться от предыдущей работы. 3) Настраиваем ПДП 4) запускаем таймер. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
KnightIgor 2 21 июля, 2014 Опубликовано 21 июля, 2014 (изменено) · Жалоба вот недавно сам столкнулся с таким же. Нашел решение на этом же форуме. А именно сбрасывать регулярные каналы. И все заработал вот мой код //=== Прерывания DMA ADC1 ===// void DMA1_Channel1_IRQHandler(void) { ADC1->CR2 &= ~ADC_CR2_DMA;// | ADC_CR2_ADON); // выключаем DMA запросы АЦП ADC1->SR &= ~(ADC_SR_STRT); // сброс флага Overrun и старта преобразования ADC1->SQR1 &= ~ADC_SQR1_L; // сброс количества последовательных преобразований [сброс указателя канала] DMA1_Channel1->CCR &= ~ (DMA_CCR_EN |DMA_CCR_TCIE); //// деактивация 1го потока DMA1 if(DMA1->ISR & DMA_ISR_TCIF1) EventFlags.Bit.ADCConvFinish = 1; // проверяем прерывание по завершению передачи блока DMA1->IFCR=DMA_IFCR_CGIF1|DMA_IFCR_CTCIF1|DMA_IFCR_CHTIF1|DMA_IFCR_CTEIF1; //снимаем биты в DMA1->ISR (пока все) } //=== Прерывания TMR2 ===// void TIM2_IRQHandler(void){ // Период - 160 мксек при частоте сети 50 Гц. vu32 aa, cosa, cosb, cosc; vu8 i; TIM2->SR &= ~TIM_SR_UIF; // Снять флаг прерывания. DMA1_Channel1->CNDTR = 18; // указываем число пересылаемых данных 3 канала по 6-и перобразовании DMA1_Channel1->CCR |= DMA_CCR_EN | DMA_CCR_TCIE;// запустили очередное преобразование ADC (должны успеть,ааа) Я тоже так пробую делать (сброс бита DMA в CR2 и STRT в SR, чтение DR три раза) за исключением сброса количества преобразований в SQR1, правда. Сейчас попробую и это, хотя на мой взгляд - как из пушки по воробьям. Кстати, в Вашем коде нет собственно "взвода" ADC на следующую операцию. Опущено здесь для иллюстрации? Еще для упрощения можно делать ADC1->SR = ~(ADC_SR_STRT), без &=, т.к. регистр SR есть rc/w0 - тип: пишешь прямо 0 в сбрасываемый бит. Значит на момент старта DMA АЦП уже успело сделать два измерения и результат первого для DMA оказался потерян. В какой последовательности вы запускаете всю эту кухню? По моим понятиям надо делать так: 1) таймер остановлен 2) Настраиваем АЦП, сбрасываем все готовности, которые могли остаться от предыдущей работы. 3) Настраиваем ПДП 4) запускаем таймер. Ладно, цитатнём код. Единократная инициализация: // TIM8 config: DBGMCU_Config(DBGMCU_TIM8_STOP, ENABLE); MIC_TIM_INIT(TIMx); // At this point TIM8 is still stopped; see MIC_Control(true) below. // Init DMA2 Channel5: DMA_DeInit(ADCx_DMA); DMA_INIT(ADCx_DMA, DMA_InitStruct); IRQ_INIT(NVIC_DMAInitStructure); DMA_TC_ENABLE(ADCx_DMA); // transmission complete interrupt enable // At this point DMA is still disabled; see MIC_Control(true) below. // Init ADC: ADCx_ClockConfig(ADCx, SystemCoreClock); ADC_DeInit(ADCx); ADC_INIT(ADCx, ADC_InitStructure); ADC_ConfigChannels(ADCx, MIC_ChannelRanks, sizeof(MIC_ChannelRanks)); ADC_ENABLE(ADCx); // wake up ADC_Calibrate(ADCx); ADCx->DR; // calibrate once, clear DR afterwards ADC_ExternalTrigConvCmd(ADCx, ENABLE); // enable by TIM8 TRGO // Start MIC sampling MIC_Control(true); Это объяснение для MIC_Control(true): // ----------------------------------------------------------------------------- // // Clears the data register n times resetting the scan mode // void ADC_ClearRegular(ADC_TypeDef* adc, int cnt) { adc->SR = ~ADC_SR_STRT; // rc/w0 register while (cnt--) adc->DR; } // ----------------------------------------------------------------------------- // // Starts/stops the ADC processing // void MIC_Control(bool action) { // Stop completely TIM_DISABLE_R(TIMx); ADC_DMACmd(ADCx, DISABLE); ADC_ClearRegular(ADCx, MIC_CHAN_COUNT); if (action) { memset(*ADC_MICx, MIC_INIT_VALUE, sizeof(*ADC_MICx)); MIC_DMA_Restart(bufnum = 0); ADC_DMACmd(ADCx, ENABLE); // use DMA // Restart completely MIC_Reload_Timer(MIC_SAMPLE_RATE); TIM_ENABLE_R(TIMx); // start the sample rate timer } } И самое интересное в прерывании: // ----------------------------------------------------------------------------- // // DMA2 Channel5 IRQ handler // void DMA2_Channel4_5_IRQHandler(void) { ADC_DMACmd(ADCx, DISABLE); ADC_ClearRegular(ADCx, MIC_CHAN_COUNT); MIC_DMA_Restart(bufnum = 1 - bufnum); ADC_DMACmd(ADCx, ENABLE); sample_ready.val = 1; } В коде много функций, которые на самом деле макросы, но из имен ясно, что они делают. Думаю, последовательность ясна. Изменено 21 июля, 2014 пользователем KnightIgor Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AlHakim 0 21 июля, 2014 Опубликовано 21 июля, 2014 (изменено) · Жалоба Кстати, в Вашем коде нет собственно "взвода" ADC на следующую операцию. Опущено здесь для иллюстрации? в принципе дальше в коде стоят ADC1->SQR1 |= (uint32_t)((6-1) << 20); ADC1->CR2 |= ADC_CR2_SWSTART | ADC_CR2_DMA; //ADC_CR2_ADON| //первый старт, н нужен, потому что как только разрешил DMA оно сразу стартует но вы не поверите. и без них работает, потому что ранее записано кол-во перобразований, осуществяемых через DMA и выставлено прерывание. А может быть это просто мистика, но оно работает :) DMA1_Channel1->CNDTR = 18; DMA1_Channel1->CCR |= DMA_CCR_EN | DMA_CCR_TCIE Изменено 21 июля, 2014 пользователем AlHakim Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 121 21 июля, 2014 Опубликовано 21 июля, 2014 · Жалоба Ой, сложно как все... Буферов два? Расположите их подряд. Пусть ПДП таскает в них данные в циклическом режиме. Без взяких перенастроек. "Пустил-забыл". ПДП дает два прерывания - "половину буфера заполнил" и "весь буфер запомнил". По первому флагу обрабатываем первый буфер, по второму - второй. В обработчике проверяем флаги ПДП, сбрасываем сработавший, запускаем обработку соответствующей половины "склеенного" буфера. Все. Больше ничего не трогаем. Ваш ADC_ClearRegular() делает совсем не то, что вы хотели. Он может вычитать результат максимум только одного преобразования. А может и вообще ни одного не вычитать. Потому что DR хранит результат одного (последнего) преобразования, никакого буфера типа FIFO на несколько результатов там нет. Если бы мне понадобилась подобная функция, я бы в ней выбросил параметр cnt, проверял STRT и если он выставлен (преобразование началось) - дожидался EOC, после чего сбрасывал бы EOC чтением DR, а STRT - записью в него 0. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
KnightIgor 2 21 июля, 2014 Опубликовано 21 июля, 2014 (изменено) · Жалоба Ой, сложно как все... Буферов два? Расположите их подряд. Пусть ПДП таскает в них данные в циклическом режиме. Без взяких перенастроек. "Пустил-забыл". ПДП дает два прерывания - "половину буфера заполнил" и "весь буфер запомнил". По первому флагу обрабатываем первый буфер, по второму - второй. В обработчике проверяем флаги ПДП, сбрасываем сработавший, запускаем обработку соответствующей половины "склеенного" буфера. Все. Больше ничего не трогаем. Ваш ADC_ClearRegular() делает совсем не то, что вы хотели. Он может вычитать результат максимум только одного преобразования. А может и вообще ни одного не вычитать. Потому что DR хранит результат одного (последнего) преобразования, никакого буфера типа FIFO на несколько результатов там нет. Если бы мне понадобилась подобная функция, я бы в ней выбросил параметр cnt, проверял STRT и если он выставлен (преобразование началось) - дожидался EOC, после чего сбрасывал бы EOC чтением DR, а STRT - записью в него 0. Хорошие идеи. Спасибо. И все же интересно, почему происходит сдвиг данных. P.S. Итак, я реализовал Вашу идею, все замечательно. Еще раз спасибо. И кажется, я обнаружил-таки, в чем была причина "лишних" неполных данных. В доке написано, что повторное разрешение ADC (то есть, запись "1" на уже "1" в CR2.ADON) приводит к запуску преобразования: Conversion starts when this bit holds a value of 1 and a 1 is written to it. Тут же далее курсивом упомянуто, что если в этом регистре одновременно меняются и другие биты (например CR2.DMA), то запуск преобразования не происходит: If any other bit in this register apart from ADON is changed at the same time, then conversion is not triggered. This is to prevent triggering an erroneous conversion. Так вот, враки это все! Этот "предохранитель" не работает, и преобразование запускается. Четко в отладчике видно. Тема для errata. Вот и получалось, что команда ADC_DMACmd(ADCx, DISABLE) запрещала передачу запросов в DMA, но запускала каждый раз преобразование. Оттуда и "лишние" данные и сдвиги. Изменено 22 июля, 2014 пользователем KnightIgor Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться