реклама на сайте
подробности

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> ADC+DMA STM32F407, Как определить время преобразования?
Haamu
сообщение Jan 13 2014, 08:40
Сообщение #1


Частый гость
**

Группа: Участник
Сообщений: 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 АЦП одновременно?
Go to the top of the page
 
+Quote Post
MiklPolikov
сообщение Jan 16 2014, 02:36
Сообщение #2


Гуру
******

Группа: Свой
Сообщений: 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)){} // ждём пока включится


--------------------
Если у Вас нет практического опыта в данной теме- не вступайте в дискуссию и не пишите никаких теоретических рассуждений! Заранее спасибо !
Go to the top of the page
 
+Quote Post
Haamu
сообщение Jan 16 2014, 11:54
Сообщение #3


Частый гость
**

Группа: Участник
Сообщений: 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 ничего не вышло.
Go to the top of the page
 
+Quote Post
MiklPolikov
сообщение Jan 16 2014, 12:35
Сообщение #4


Гуру
******

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



Цитата(Haamu @ Jan 16 2014, 15:54) *
Какой это бит? Как я уже писал ранее, с битом EOC ничего не вышло.


Я вручную запускаю только injected каналы, и поэтому проверяю бит ADC_SR_JEOC


--------------------
Если у Вас нет практического опыта в данной теме- не вступайте в дискуссию и не пишите никаких теоретических рассуждений! Заранее спасибо !
Go to the top of the page
 
+Quote Post
HardEgor
сообщение Jan 16 2014, 16:29
Сообщение #5


Гуру
******

Группа: Свой
Сообщений: 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 мегасэмплов.
А ДМА не ошибается, возможно как раз была превышена частота.
Go to the top of the page
 
+Quote Post
Haamu
сообщение Jan 17 2014, 11:42
Сообщение #6


Частый гость
**

Группа: Участник
Сообщений: 90
Регистрация: 12-12-13
Пользователь №: 79 587



HardEgor, подскажите пожалуйста, где именно в даташите Вы нашли такиее цифры? Подозреваю, что мы с Вами о разных контроллерах говорим.
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Jan 17 2014, 12:16
Сообщение #7


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



Цитата(Haamu @ Jan 16 2014, 13:54) *
Я рассматривал такой вариант, но отказался от него, т.к. каждый раз число каналов может быть разным (в зависимости от значения параметра дискретности).
И что вам мешает при смене параметра дискретности заново перенастроить количество каналов в настройках АЦП и счетчик пересылок DMA на нужное количество каналов?


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
HardEgor
сообщение Jan 17 2014, 18:24
Сообщение #8


Гуру
******

Группа: Свой
Сообщений: 2 223
Регистрация: 3-03-06
Из: Tomsk
Пользователь №: 14 925



Product Specifications Table 67. ADC characteristics и Reference Manual с сайта

Go to the top of the page
 
+Quote Post
Haamu
сообщение Jan 20 2014, 12:55
Сообщение #9


Частый гость
**

Группа: Участник
Сообщений: 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М выборки в секунду... Маловато...
Go to the top of the page
 
+Quote Post
HardEgor
сообщение Jan 20 2014, 15:34
Сообщение #10


Гуру
******

Группа: Свой
Сообщений: 2 223
Регистрация: 3-03-06
Из: Tomsk
Пользователь №: 14 925



Ну так можно снизить системную частоту и подобрать комбинацию из частоты и предделителя, чтобы на АЦП максимум приходил.
Go to the top of the page
 
+Quote Post
Falkon_99
сообщение Feb 22 2014, 20:59
Сообщение #11


Частый гость
**

Группа: Участник
Сообщений: 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, через внутренний канал АЦП ?
Go to the top of the page
 
+Quote Post
ViKo
сообщение Feb 23 2014, 15:31
Сообщение #12


Универсальный солдатик
******

Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362



Цитата(Falkon_99 @ Feb 22 2014, 23:59) *
Настроил преобразование регулярных каналов, с минимальной частотой. Требуется точность а не быстродействие.
...
Что я могу делать не так???

А вы уверены, что минимальная частота даст максимальную точность? Все же, заряды "перетрахиваются".
Попробуйте на максимальной, посмотрим, что получится.
Go to the top of the page
 
+Quote Post
Falkon_99
сообщение Feb 23 2014, 17:08
Сообщение #13


Частый гость
**

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



я прозрел! От частоты АЦП зависят результвты причём значительно, особенно по каналу измерения температуры.
минимальные скачки показаний получаются при 15MHz. (ADCPRE = 4).
Но в пересчете по каналу Vbat, ничего не меняется.
Скорее всего нужно откалибровать один раз его, и забыть.

Еще датчик температуры себя странно ведет. При прогреве от 30 до 80 С, показания в регистре увеличиваются всего лишь на 20 пунктов. Ото при установленном 12bit АЦП.

В общем толи я туплю гдето, толи АЦП в STM32 полный отстой.
Go to the top of the page
 
+Quote Post
scifi
сообщение Feb 23 2014, 18:00
Сообщение #14


Гуру
******

Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136



Цитата(Falkon_99 @ Feb 23 2014, 21:08) *
В общем толи я туплю гдето, толи АЦП в STM32 полный отстой.

Тупите, как мне кажется :-)
В даташите приведено минимальное необходимое время выборки для датчика температуры. Вы его соблюдаете?
Go to the top of the page
 
+Quote Post
Falkon_99
сообщение Feb 24 2014, 07:52
Сообщение #15


Частый гость
**

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



TS_temp ADC sampling time when reading the temperature 1°C accuracy 10 µs

у меня данные с регистра читаются 5 раз в секунду, выводятся на экран, пока без всяких пеерсчетов.
Может что то с чипом, попробую на другой плате


Напишите, кто знает, приблизительный предел изменения данных в регистре, при повышении температуры на 50С, при услови что АЦП 12bit (0...4095 уе)

Go to the top of the page
 
+Quote Post

2 страниц V   1 2 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 9th July 2025 - 22:42
Рейтинг@Mail.ru


Страница сгенерированна за 0.0153 секунд с 7
ELECTRONIX ©2004-2016