|
ADC+DMA STM32F407, Как определить время преобразования? |
|
|
|
Jan 13 2014, 08:40
|
Частый гость
 
Группа: Участник
Сообщений: 90
Регистрация: 12-12-13
Пользователь №: 79 587

|
Необходимо измерить сигнал с нескольких каналов последовательно, сохраняя данные в память через DMA. Измерять надо крайне быстро. Действую таким образом: выбираю канал, запускаю АЦП. Вот кусок кода: Код if(ModeStruct.Discreteness_hall) { if ((step_counter % ModeStruct.Discreteness_hall) == 0) { ADC1->SQR3 = ADC_Channel_0; ADC1->CR2 |= ADC_CR2_SWSTART; //Start ADC } } if (ModeStruct.Discreteness_l) { if ((step_counter % ModeStruct.Discreteness_l) == 0) { ADC1->SQR3 = ADC_Channel_1; ADC1->CR2 |= ADC_CR2_SWSTART; //Start ADC } } if (ModeStruct.Discreteness_diff) { if ((step_counter % ModeStruct.Discreteness_diff) == 0) { ADC1->SQR3 = ADC_Channel_2; ADC1->CR2 |= ADC_CR2_SWSTART; //Start ADC } } Вот инициализация (тоже кусок): Код ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b; ADC_InitStruct.ADC_ScanConvMode = DISABLE; ADC_InitStruct.ADC_ContinuousConvMode = DISABLE; ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStruct.ADC_NbrOfConversion = 1; ADC_Init(ADC1, &ADC_InitStruct); Столкнулся с такой проблемой, что в какой-то момент очередное преобразование АЦП запускается до того, как завершилось предыдущее, после чего отключается DMA. Подскажите, как максимально быстро можно проверить, завершилось ли предыдущее преобразование, можно ли запускать следующее?Пробовал проверять таким вот образом: Код ADC1->SQR3 = ADC_Channel_0; ADC1->CR2 |= ADC_CR2_SWSTART; //Start ADC while(!(ADC1->SR & (uint32_t)ADC_FLAG_EOC)) {} ADC1->SR &= ~((uint32_t)ADC_FLAG_EOC); Без DMA такой способ работает. Но когда включаю DMA, то флаг окончания преобразования не поднимается, видимо его DMA раньше перехватывает и сам сбрасывает. Была мысль делать небольшую задержку после запуска преобразования, но что-то я запутался, как рассчитать время, необходимое для завершения перобразования. А конкретно, как определить часосту тактирования АЦП? Частота ядра в моем случае 168МГц, соответстсвенно шина APB2 работает на частоте 84МГц. Значит и частота АЦП 84МГц, или есть еще какие-то предделители? Вводит в заблуждение предделитель в ADC common control register. Учитывается ли этот регистр при работе с одним АЦП или используется только когда запускаю 2 или 3 АЦП одновременно?
|
|
|
|
|
Jan 16 2014, 02:36
|

Гуру
     
Группа: Свой
Сообщений: 2 015
Регистрация: 23-01-07
Из: Москва
Пользователь №: 24 702

|
Вы неправильно подходите к задаче. 1)Зачем запускать преобразование по каждому каналу отдельно ? Нужно выбрать группу(цепочку) каналов, и запускать преобразование всей группы, тогда они и будут преобразовываться с максимальной скоростью . 2)После преобразования, если уж оно запущено как у Вас вручную, нужно ждать когда выставится бит того что оно завершено. В вашем коде этого нет. 3)Зачем вообще запусать преобразование вручную ? Надо выбрать гдеппу(цепочку) каналов, запускать преобразование по триггеру от таймера, по завершению преобразования автоматический запрос ДМА. А потом уже разбирать то что ДМА в память написал. Вот Вам два моих кода. Первый настраивает цепочку из двух каналов для преобразования по запуску вручную и чтения результата вручную. Второй настраивает цепочку из 1 канала(но из комментариев понятно, как сделать больше) для преобразования по триггеру от таймера и чтения по ДМА. Они для STM32L151, но на вид у нас АЦП почти одинаковые. Код ADC1->CR2&=~ADC_CR2_ADON; //вЫключили АЦП ADC1->CR1|=ADC_CR1_PDI; //power down между преобразованиями ADC1->CR1|=ADC_CR1_PDD; //power down между пачками преобразований //ADC1->SMPR2|=(7<<3); //канал 11 время выборки 384 клока //ADC1->SMPR2|=(7<<12); //канал 14 время выборки 384 клока
ADC1->CR1|=ADC_CR1_SCAN; //включаем Scan mode , что бы преобразовывалась вся цепочка за раз
//ADC1->JSQR|=ADC_JSQR_JL_1; //3 преобразования в цепочке injectid каналов ADC1->JSQR|=ADC_JSQR_JL_0; //2 преобразования в цепочке injectid каналов
ADC1->JSQR|=(11<<15)|(14<<10); //injected каналы 11 и 14
ADC->CCR&=~ADC_CCR_ADCPRE; //частота АЦП= HSI
ADC1->CR1&=~ADC_CR1_RES_0; ADC1->CR1&=~ADC_CR1_RES_1; // 12 бит // ADC1->CR2|=ADC_CR2_ALIGN; // левый сдвиг результата в регистре данных. Что бы правитльно прочитать 12и битовый результат из 16и битового резистра
ADC1->CR2|=ADC_CR2_ADON; //включили АЦП while(!(ADC1->SR & ADC_SR_ADONS)){} //ждём пока включится while(ADC1->SR & ADC_SR_JCNR){} //ждём пока injected каналы станут готовы ADC1->SR&=~ADC_SR_JEOC; ADC1->CR2|=ADC_CR2_JSWSTART; //запускаем преобразование injectid каналов while(!(ADC1->SR & ADC_SR_JEOC)){}//ждём пока преобразование завершится Код ADC1->CR2&=~ADC_CR2_ADON; //вЫключили АЦП
ADC1->SMPR1=0x0; ADC1->SMPR2=0x0; ADC1->SMPR3=0x0; //время выборки минимальное ADC->CCR&=~ADC_CCR_ADCPRE; //частота АЦП= HSI //ADC->CCR|=ADC_CCR_TSVREFE; //включили внутренний источник опорного напряжения ////////////////////////////// ADC1->CR1|=ADC_CR1_PDI; //power down между преобразованиями //ADC1->CR1|=ADC_CR1_PDD; //power down во время задержки //ADC1->CR1|=ADC_CR2_DELS; // задержка 255 клоков APB
ADC1->CR1&=~ADC_CR1_RES_0; ADC1->CR1&=~ADC_CR1_RES_1; // 12 бит ADC1->CR2|=ADC_CR2_ALIGN; // левый сдвиг результата в регистре данных. Что бы правитльно прочитать 12и битовый результат из 16и битового резистра
ADC1->CR2|=ADC_CR2_EXTEN_1; // преобразование по переднему фронту внешнего сигнала////////////////////////////// ADC1->CR2|=ADC_CR2_EXTSEL_0; //преобразование по TIM9_TRGO event ADC1->SQR5|=(1<<0); //1 преобразование в цепочке 1 канал ADC1->CR2|=ADC_CR2_DDS; // Запросы ДМА не прекращаются после последней передачи ADC1->CR2|=ADC_CR2_DMA; // Включаем запрос ДМА
ADC1->CR2|=ADC_CR2_ADON; //включили АЦП while(!(ADC1->SR & ADC_SR_ADONS)){} // ждём пока включится
--------------------
Если у Вас нет практического опыта в данной теме- не вступайте в дискуссию и не пишите никаких теоретических рассуждений! Заранее спасибо !
|
|
|
|
|
Jan 16 2014, 11:54
|
Частый гость
 
Группа: Участник
Сообщений: 90
Регистрация: 12-12-13
Пользователь №: 79 587

|
Цитата(MiklPolikov @ Jan 16 2014, 06:36)  1)Зачем запускать преобразование по каждому каналу отдельно ? Нужно выбрать группу(цепочку) каналов, и запускать преобразование всей группы Я рассматривал такой вариант, но отказался от него, т.к. каждый раз число каналов может быть разным (в зависимости от значения параметра дискретности). Для понятности, вот полностью обработчик прерывания таймера: Код void TIM3_IRQHandler(void) //limit - 560 ticks { if (TIM3->SR & TIM_IT_Update) { if (step_counter < 10000) { //step_counter = 0 - 9999 DAC->DHR12R2 = ModeStruct.SignalData[step_counter] + 0x7FF; DAC->SWTRIGR |= DAC_SWTRIGR_SWTRIG2; __asm("mov r0, #10 \n" "cycle00: subs r0, #1 \n" " bhi cycle00"); if (ModeStruct.Discreteness_hall) { if ((step_counter % ModeStruct.Discreteness_hall) == 0) { ADC1->SQR3 = ADC_Channel_0; ADC1->CR2 |= ADC_CR2_SWSTART; __asm("mov r0, #12 \n" "cycle01: subs r0, #1 \n" " bhi cycle01"); } } if (ModeStruct.Discreteness_l) { if ((step_counter % ModeStruct.Discreteness_l) == 0) { ADC1->SQR3 = ADC_Channel_1; ADC1->CR2 |= ADC_CR2_SWSTART; __asm("mov r0, #12 \n" "cycle02: subs r0, #1 \n" " bhi cycle02"); } } if (ModeStruct.Discreteness_diff) { if ((step_counter % ModeStruct.Discreteness_diff) == 0) { ADC1->SQR3 = ADC_Channel_2; ADC1->CR2 |= ADC_CR2_SWSTART; __asm("mov r0, #12 \n" "cycle03: subs r0, #1 \n" " bhi cycle03"); } } if (ModeStruct.Discreteness_b_hall) { if ((step_counter % ModeStruct.Discreteness_b_hall) == 0) { ADC1->SQR3 = ADC_Channel_10; ADC1->CR2 |= ADC_CR2_SWSTART; __asm("mov r0, #12 \n" "cycle04: subs r0, #1 \n" " bhi cycle04"); } } if (ModeStruct.Discreteness_b_l) { if ((step_counter % ModeStruct.Discreteness_b_l) == 0) { ADC1->SQR3 = ADC_Channel_11; ADC1->CR2 |= ADC_CR2_SWSTART; __asm("mov r0, #12 \n" "cycle05: subs r0, #1 \n" " bhi cycle05"); } } step_counter ++; } else { step_counter = 0; period_counter --; } if (period_counter == 0) { DAC_SetChannel2Data(DAC_Align_12b_R, 0x0000); DAC_SoftwareTriggerCmd(DAC_Channel_2, ENABLE); timer_stop(); } TIM3->SR &= ~TIM_IT_Update; } } Цитата(MiklPolikov @ Jan 16 2014, 06:36)  2)После преобразования, если уж оно запущено как у Вас вручную, нужно ждать когда выставится бит того что оно завершено. В вашем коде этого нет. Какой это бит? Как я уже писал ранее, с битом EOC ничего не вышло.
|
|
|
|
|
Jan 16 2014, 16:29
|
Гуру
     
Группа: Свой
Сообщений: 2 223
Регистрация: 3-03-06
Из: Tomsk
Пользователь №: 14 925

|
Цитата(Haamu @ Jan 13 2014, 15:40)  Была мысль делать небольшую задержку после запуска преобразования, но что-то я запутался, как рассчитать время, необходимое для завершения перобразования. А конкретно, как определить часосту тактирования АЦП? Частота ядра в моем случае 168МГц, соответстсвенно шина APB2 работает на частоте 84МГц. Значит и частота АЦП 84МГц, или есть еще какие-то предделители? Вводит в заблуждение предделитель в ADC common control register. Учитывается ли этот регистр при работе с одним АЦП или используется только когда запускаю 2 или 3 АЦП одновременно? Вы немного не понимаете как всё работает, смотрим в даташит и видим что максимальная частота работы АЦП 36МГц при питании выше 2.4В, большую частоту на него подавать нельзя - никто не гарантирует что он будет правильно работать. Следовательно предделитель синхронизации АЦП надо выставлять так чтобы частота не была превышена. Т.е. если на APB2 84MHz, тогда надо ставить предделитель 3 и получаем 28МГц, а если поставим 2 - получим 42МГц - превышение. Дальше, в даташите написано что максимальное количество выборок которое сможет сделать АЦП - 2 мегасэмпла.Вот это ваш предел оцифровки, с такой скоростью идут данные от АЦП. Еще есть вариант когда включается Interleave Dual или Triple, тогда можно достичь 6 мегасэмплов. А ДМА не ошибается, возможно как раз была превышена частота.
|
|
|
|
|
Jan 20 2014, 12:55
|
Частый гость
 
Группа: Участник
Сообщений: 90
Регистрация: 12-12-13
Пользователь №: 79 587

|
Цитата(HardEgor @ Jan 17 2014, 22:24)  Product Specifications Table 67. ADC characteristics и Reference Manual с сайта Теперь увидел. Спасибо. Мде... Учитывая то, что предделитель может быть только 2, 4, 6 или 8, а с предделителем 2 получается превышение частоты, то в моем случае максимальная частота тактирования АЦП получается всего 21МГц, то есть всего 1,4М выборки в секунду... Маловато...
|
|
|
|
|
Feb 22 2014, 20:59
|

Частый гость
 
Группа: Участник
Сообщений: 169
Регистрация: 26-03-12
Из: Харьков
Пользователь №: 71 010

|
В тему про АЦП, у меня STM32F207
Настроил преобразование регулярных каналов, с минимальной частотой. Требуется точность а не быстродействие.
1 ch - напряжение питание через делитель. 2 ch - встроенный датчик температуры. 3 ch - канал опорного напряжения. 4 ch - напряжение Vbat.
Поскольку на вывод AVDD приходит нестабилизированное питание, просто +3V, пересчет делаю с учетом опорного напряжения. Точность полный отстой +/- 20 единиц в регистре ADC1->DR. Но дело в другом:
По первому каналу пересчет получается (по здравому смыслу), около 3В А вот канал Vbat очень занижает показания... чтобы не запутатся в пересчетах, смотрим в "попугаях" регистра ADC1->DR
Vref = 0x0682 (По даташиту 1.21В) Vbat = 0x0643 (По даташиту необходимо увеличить значение в 2 раза) => 0x0C86
далее просто 0x0C86 * 1.21 / 0x0682 = 2,33 В (Поверьте реально на батарейке 2,98 В)
Что я могу делать не так???
Кто нибудь вообще сталкивался с измерением напряжения Vbat, через внутренний канал АЦП ?
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|