Время выполнения команды
- Войдите на сайт для отправки комментариев
Пнд, 05/08/2013 - 15:51
Вопрос такой: почему время выполнения команды в ардуино разное при тех же условиях?
Например вот такой скетч
#include <analogComp.h> #include <CyberLib.h> #include <LiquidCrystal.h> LiquidCrystal lcd(14,15,16,17,18,19); unsigned int t_1 = 0; // переменная для хранения времении работы программы в мкс unsigned int t_2 = 0; // переменная для хранения времении работы программы в мкс unsigned int R = 3607; // сопротивление резистора заряда конденсатора в kОмах volatile float T = 0.0; // время заряда конд. плюс входная емкость плюс время виполнения команд volatile float C = 0.0; // вычесленное значение емкости void setup() { lcd.begin(16,2); } void loop() { D8_Out; // назначаем D8 на выход D8_Low; // на D8 устанавливаем 0 (минус конденсатора) D9_Out; // назначаем D9 на выход D9_Low; // на D9 устанавливаем 0 (разряд конд. через резистор 100 Ом) delay(500); // ждем пока розрядится конд. D9_In; // делаем D9 входом, что б не мишать измерениям delayMicroseconds(100); // небольшея пауза t_1 = micros(); // засекаем время t_1 if (analogComparator.waitComp(0)) // если на компараторе напражение више установленого... { t_2 = micros(); // засекаем время t_2 } T = (t_2 - t_1); // время заряда конденсатора, показывает разные значения (без конд. 12 - 14 мкС) C = ((T - 12.0)/R*1000); // вычисление емкости в пф. (12.0 - см. выше) lcd.clear(); lcd.setCursor(0,0); lcd.print ("Capacitance: "); lcd.setCursor(0,1); lcd.print (C); // вывод значения емкости на экран lcd.print (" pF "); }
Вот схема. Это измеритель емкости. Меряет время заряда конд. резистором 3.6М.
Таким образом, значение емкости постоянно прыгают. Как с этим бороться? Подскажите пожалуйста.
Принцип взят отсюда http://easyelectronics.ru/kondensator-i-rc-cepochka.html
функция micros() дает результат кратный 4мкс, на малых емкостях скакать тока в путь...
Спасибо dtvims! Это объясняет многое. А как тогда засечь 1 мкс? Таймер-то 16 битный, межет есть какие-то другие способы? TimerOne например. Я новичок в контроллерах, поетому спрашиваю у профи на форуме, если такие есть, конечно :-)
работайте с таймером непосредственно через его регистры.
micros() использует счетчик от WatchDog, вернее его прерывание, в котором инкрементирует 32-х разрядный счетчик (переменную типа unsigned long). Если чаще вызывать прерывание, то дуинка тупо будет сидеть в обработчике данного прерывания. см. исходники arduino IDE.
Можно попробовать 2 решения: или использовать непосредственно 8-ми битный счетчик (перед началом измерения сбрасывать его, а в конце читать, прерывание не включать), Вам же надо не так много мкс отсчитать; или делать свои задержки из расчета 16 тактов на 1мкс (если у Вас кварц на 16MHz), но при этом не забудте отключить WatchDog, иначе он своим прерыванием, раз в 4мкс, будет вносить доп. погрешность, а чтение портов необходимо делать напрямую, НЕ используя Arduino IDE, иначе опять получите задержку до 4 мкс (чет накрутили они там, чтобы сделать сплошную нумерацию пинов).
Да! Варианты есть. Но авторы Дуины их не предусмотрели! Я только начинаю в этом разбираться. Хорошо-бы примеры какие-то увидеть с коментариями. Буду признателен!
micros() использует счетчик от WatchDog, вернее его прерывание, ...
Разве???
...
не забудте отключить WatchDog, иначе он своим прерыванием, раз в 4мкс
...
WatchDog'у, работающему от собственного 128-килогерцового осциллятора, до микросекундной скорострельности как до Парижу раком:
Щас сам покапался в исходниках. Теперь думаю, где же я видел перенастроенный WatchDog?
Не суть, значит надо таймер 0 отключить
И речь тут не о самой скорострельности, а о том, что обработка этого прерывания как раз около 1 мкс, если не больше, посчитайте сколько тактов на вход в прерывание, сохранить регистры, обработка и выход.
Уважаемые step962 и dtvims! Вы, наверное, понимаете друг друга, а я вас нет! Как мне использовать это в моей программе? Я схемотехник и только-только начинаю программировать, ПАМАГИТЕ пожалуйста!!!
Для меня это темный лес! А если использовать библиотеку TimerOne https://code.google.com/p/arduino-timerone/downloads/list, тока я не знаю как, нету примера подходящего. Может ви подскажите примером или ссилкой на него.
Нашел пример, но как его переделать что б в мою прогу вставить??? http://justforduino.blogspot.com/2013/05/arduino-2.html
По последней Вашей ссылке отличный пример, только Вам и одного таймера будет достаточно я думаю, собственно автор там и использует таймер1. А вот зачем он инициализировал таймер 0, я что-то не понял.
Да, я вижу, что это почти то что надо, но мозгов не хватает что б использовать в моей программе! Помогииииииииите! Пожалуйста!
"TCCR1B = 0;" - выключает таймер
"TCNT1 = 0;" - сброс счетчика
в общем цикле он забирает значение TCNT1 - это и есть счетчик таймера 1. Поскольку настройки все по нулям, он по сути должен отсчитывать такты, т.е. 1мкс = 16 тактов.
"#include <util/delay.h>" - стандартная билиотека AVR, где есть функция "_delay_ms(800); // wait 800 ms", которая реализует простой цикл с известным числом тактов, т.е. Если какие-то прерывания не прервут его работу, то отсчет будет точным.
более детально про настройку таймера тут http://www.atmel.com/Images/doc8161.pdf , а по русски вот тут неплохо http://mainloop.ru/avr-atmega/avr-timer-counter.html
Спасибо! Буду разбираться, то есть учиться!
Новый код:
надо смотреть, что у Вас в "analogComp.h", в стандартных ее не нашел :(
похорошему, действительно лучше самому читать и писать в порт. Для цифровых портов: "DDRD" - регистр режима порта "D", если соответсвующий бит выставляете 0, то вход, если 1 выход. "PORTD" - что на выходе, а через "PIND" читать состояние. Тут детально http://easyelectronics.ru/avr-uchebnyj-kurs-ustrojstvo-i-rabota-portov-vvoda-vyvoda.html
С аналоговым входом не помню деталей :( Вот тут нашел детально http://dfe.petrsu.ru/koi/posob/avrlab/mega16adc.html, где, кстати, описана серьезная задержка в 13-25 тактов на преобразование.
Можно дожидаться, когда когда на цифровом входе будет логический переход с 0 на 1 или наоборот, может пошустрее и по точнее выйдет, тут, кстати, и на прерывание можно повесить такой переход (int0), т.е. сработало прерывание, в нем и замер времени сделали, чтобы не делать постоянные замеры в цикле, от чего и результат вероятно плавает.
Чтобы учитывать переполнение, т.е. измерять большую емкость, придется таки использовать прерывание или уменьшать резистор :)
вот тут есть инфа по таймеру http://dfe.petrsu.ru/koi/posob/avrlab/mega16tcnt1.html
UPD, а я понял зачем в том примере инициализировали таймер 0 - это его выключили :) дабавте туже функцию к себе в "setup()", возможно уже чуть меньше скакать будет.
В дополнение. В случае с измерениями емкости, где-то читал, что все-равно скакать будет. Для точных имерений, схемка нужна по сложнее, чтобы стабилизировать процес. Можно делать несколько измерений, запоминать их, а потом выдавать среднее значение как результат :)
Вообще analogComp и использует ацп в чистом виде :) причем для ожидания она использует отсчет millis(), где, если максимальное время ожидания не задано, используется 5 секунд. Т.е. при отключении тамера 0, работать перестанет. Правда там можно задать некую userFunction(), которая будет срабатывать по прерыванию. Собственно, погрешность во времени измерения от такого использования по любому в 1.5мкс есть.
Мне кажется, что если Вы это все аккуратно под свою задачу сами напишите, уже будет точнее, т.к. вы не будете вызывать никакие доп. функции, один только вызов, которых Вам будет стоить по 1мкс.
Можно еще какой-нибудь фильтр наложить типа Калмана, на пачку измерений :) Может Еще увеличить время разряда?
А как же аппаратный компаратор? Выводы AIN0 и AIN1, они же Digital pin 6 и Digital pin 7, они же 12 и13 ноги Dip корпуса Atmega.
Он, вроде, независимо от АЦП работает.
Вот ище нашел http://www.rigexpert.com/index?s=articles&f=cmeter&l=ru Этот прибор так же работает, только контроллер ATmega16 и дисплей простой. Вот его код (длинний зараза). Может одтуда отковырять часть таймер-компаратор.
AIN - Analog Input, т.е. аналоговый вход, т.е. и есть АЦП
Вам проще добавить в "setup()" вызов "Timer0_Init();" и "analogComparator.setOn(AIN0, AIN1);", а вместо "
if
(analogComparator.waitComp(0)) " используйте "while((ACSR && (1<<ACO)) == 0){}"
посути тоже самое но погрешность уменьшиться.
UPD. А вообще именно что-то типа этой схемы, что найдена Вами в последний раз, я и имел ввиду, про посложнее. Там в основе частотомер, а поскольку он, насколько это возможно, стабилизирует колебания, то и результат получается более точный. Можно брать данный вариант почти без переделки, только отображение на дисплей поменять и поотлючать Дуиновские ненужности типа таймера 0.
Вам проще добавить в "setup()" вызов "Timer0_Init();" и "analogComparator.setOn(AIN0, AIN1);", а вместо "
if
(analogComparator.waitComp(0)) " используйте "while((ACSR && (1<<ACO)) == 0){}"
Сделал, не работает!!! Не реагирует на изменение напряжения на входе AIN0.
"while((ACSR && (1<<ACO)) == 0){}" - это должно быть вместо "analogComparator.waitComp(0)", а "if" просто не нужнымм
после "while((ACSR && (1<<ACO)) == 0){}; Ваш под итог с расчетом времени строго после данного цикла.
Если он зависает до или в этом цикле, то значит я что-то не учел, а в остальном это тоже самое, что и "analogComparator.waitComp(0)", если конечно инициалзацию компоратора не забыли "analogComparator.setOn(AIN0, AIN1);", которая выполняется в "analogComparator.waitComp(0)", если не сделана ранее.
UPD. Хотя судя по "_initialized = 0;" инициализация происходит перед началом каждого измерения, тогда надо так:
Да я все правильно понял и сделал. На счет analogComparator.setOn(AIN0, AIN1) - в моем предыдущем коде ее не было и работало, как только вставил в сетап - перестало, хотя больше ничего не менял. Ваш последний вариант тоже не работает, где-то что-то еще спряталось.
П.С. судя по даташиту, компаратор это отдельная схема не зависимая от АЦП, хотя я может не правильно понял.
П.П.С. а зачем Timer0_Init()? Все делает 1-й таймер.
Заработало!!!
Оказивается надо еще и analogComparator.setOff() вставить! Методом втыка получилось вот что
Все равно результат прыгает, хоть и не так сильно.
Надо-бы выкинуть analogComp.h, ее исползуют только analogComparator.setOn и analogComparator.setOff .
Mожно же и без нее обойтись? По моему ето она все портит.
Timer0_Init() присутсвует только зануление всех регистров связанных с таймером, т.е. его отключает.
analogComparator.setOn(AIN0, AIN1), ранее выполнялся в analogComparator.waitComp(0), поэтому нужен отдельно. Вот она:
По сути - это тоже самое, что оставил там я. В этой функции только инициализация setOn и ожидание, когда бит ACO примет значение 1, что соответсвует "
while
((ACSR && (1<<ACO)) == 0){};
" - ждать, крутить в цикле, пока не станет бит ACO единицей. Т.е. убрано все лишнее. Т.е. как минимум должен остаться прежний функционал, только таймаут в 5 секунд пропал. Можно поставить "while
((ACSR && (1<<ACO)) == 1){};
", хотя, если так заработает, то странно, но возможно :)Я посмотрел детально Вашу схему (Хорошо, Компаратор не совсем АЦП в чистом виде :), по крайней мере, на нем можно его реализовать, но не суть). Вам, по хорошему, между D7 и землей нужен дополнительный конденсатор емкостью около 0.1 мкФ, причем поближе к контактам Контроллера - это уменьшит шумы опорного напряжения.
analogComp.h, Вам нужен, чтобы использовать analogComparator.setOn(AIN0, AIN1). Поставте на опорное конденсатор. И можно попробовать уменьшить/увеличить время разряда измеряемого конденсатора, может хоть повторяемость эксперимента увеличит.
Да, а таймер 0 надо выключить, т.к. он включается одной из стандартных библиотек ардуины, Вам лишние помехи при измерении не нужны, как и сам таймер 0.
от сюда http://easyelectronics.ru/avr-uchebnyj-kurs-ispolzovanie-analogovogo-komparatora.html#more-54
При этом наблюдается мерзкий эффект — когда сравниваемые напряжения на входах компаратора очень близки, то возникает дребезг. Т.е. мельчайшие помехи уже начинают играть роль и перевешивают чашу весов компаратора то в одну то в другую сторону. Возникает жуткий дребезг. Этот дребезг надо подавлять программно. Скажем игнорировать изменения сигнала если он чаще чем раз в несколько миллисекунд.
вот Вам и разный результат :(
Да! Но как? Непойму. Кстати, кондюк не помог.
А в данном случае никак этого "дребезга не избежать". В описании, он идет несколько МИЛИсекунд, а у Вас исчисляется все МИКРО секундами.
А сильно скачет-то сейчас? Какой разброс?
На 100 пФ прыгает +- 1пФ (6913 - 7017 тиков таймера). Разница в 6,5 мкс!
Ну +-1пФ - это супер результат :) у многих и такого достигнуть не получается! По моему, на такой простой схеме результат достойный, можно патентовать :)
Не!!! Негодится!!! 1% прыгает плюс 1- 2% погрешность на все остальное. Буду делать чужую схему, покупать контролер, дисплей и все остальное :(
Я еще выкинул <util/delay.h> , а задержки сделал на том же таймере - не помогло. Иногда зависает.
Вот последний код, может чот пропустили?
Я так понял, что большего не следует ожидать от этой схеми и Arduino IDE.
поотлючать Дуиновские ненужности типа таймера 0.
А есть ли че еще поотключать? АЦП, например. Если да, то как?
Таймер 0 все-таки выключите :)
А более навороченная схема врятли лучше результат даст. Где-то читал, что простой схемой можно мерять только приблизительно емкости. Сам заморочился как-то этим вопросом, т.к. на чип-кондерах емкость не пишится из-за конструктивных особенностей, а не нужных плат-доноров полно. Вот и заинтересовался, что у Вас получится :) Чтобы точно мерять, нужны уже ресурсы по круче чем AVR`ка :(
Урррра! Все работает и причем точно меряет!!! Я поотключал все лишнее и скачки в измерениях стали предсказуемими, если вывести среднее значение из 1000 измерений то результат стабильный и поразительно точный. Мерял 2%-ные кондюки - правильно показывает!
Чуть подправил схему и прогу, с резистором 4.3М меряет от 1 до 900 пФ. Планирую доделать еще 2 или 3 предела измерений, но это потом, так как мне сейчас нужно пико фарады мерять. Вот че получилось:
#include <analogComp.h> #include <CyberLib.h> #include <LiquidCrystal.h> LiquidCrystal lcd(14,15,16,17,18,19); unsigned int Rp = 4274; // сопротивление резистора заряда конденсатора в kОмах volatile unsigned int T = 0; // время заряда конд. плюс входная емкость плюс время виполнения команд volatile float C = 0.0; // вычесленное значение емкости volatile float T2 = 0.0; // среднее значение времени заряда конд. void Timer1_Init( void ) // Timer/Counter 1 initialization { TCNT1 = 0; TCCR1A = 0; // Bits: COM1A1 COM1A0 COM1B1 COM1B0 - - WGM11 WGM10 TCCR1B = 0; // Bits: ICNC1 ICES1 - WGM13 WGM12 CS12 CS11 CS10 TCCR1C = 0; // Bits: FOC1A FOC1B - - - - - - TIMSK1 = 0; // Bits: - - ICIE1 - - OCIE1B OCIE1A TOIE1 TIFR1 = 0; // Bits: – – ICF1 – - OCF1B OCF1A TOV1 } void setup() { lcd.begin(16,2); D12_Out; // назначаем D12 на выход D12_High; // на D12 устанавливаем 1 (плюс конденсатора) D8_Out; // назначаем D8 на выход D8_Low; // на D8 устанавливаем 0 (минус конденсатора) cli(); // Global disable interrupts Timer1_Init(); // Timer/Counter 1 initialization TIMSK0 &= ~(1 << TOIE0); // Disable Timer 0. TIMSK2 &= ~(1 << TOIE2); // Disable Timer 2. sei(); // Global anable interrupts ADCSRA = 0; // Disable ADC PRR = B10010111; // Disable USART0, TWI, SPI } void loop() { for (int i=0; i<1000; i++) { D9_Out; // назначаем D9 на выход D9_Low; // на D9 устанавливаем 0 (разряд конд. через резистор 100 Ом) // задержка на разряд конд. TCCR1B = (1<<CS10); // запускаем таймер while( TCNT1 < 32000 ){} TCCR1B = 0; //останавливаем таймер TCNT1 = 0; // сброс счетчика D9_In; // делаем D9 входом, что б не мишать измерениям // маленкая пауза TCCR1B = (1<<CS10); // запускаем таймер. while( TCNT1 < 1840 ){} TCCR1B = 0; //останавливаем таймер TCNT1 = 0; // сброс счетчика analogComparator.setOn(AIN0, AIN1); // компаратор вкл. TCCR1B = (1<<CS10); // запускаем таймер while((ACSR && (1<<ACO)) == 0){} TCCR1B = 0; //останавливаем таймер analogComparator.setOff(); // компаратор откл. T = TCNT1; // забираем значение таймера TCNT1 = 0; // сброс счетчика T2 = T2 + T; } T2 = T2/1000; // считаем среднее C = ((T2 - 5.0)/Rp*1000) / 16; // вычисление емкости в пф. // на дисплей lcd.clear(); lcd.setCursor(0,0); lcd.print("Capacitance: "); lcd.setCursor(0,1); lcd.print(C); lcd.print(" pF"); }
Подстроечником надо выставить на D7 63.5% от напряжения на D12 относительно D8. D8 - земля, D12 - Vcc. Это для того что бы анулирорать влияние падения напряжения на транзисторах.
dtvims , вам отдельное спасибо!
Вам, спасибо. Вы собрали очень простую схему, что я как раз и искал для измерения емкости :). Будет время, соберу себе такой приборчик, только пределаю чисто под AVR (без arduino), компактнее получиться.
Именно в электронике я не силен, потому ищу готовые схемы, а вот код под себя подправить без проблем :)
А у меня все наоборот! :) Я и буду делать отдельную плату и корпус. На ATmaga8 под smd.
http://www.ebay.com/itm/New-Transistor-Tester-Capacitor-ESR-Inductance-R...
Делайте только корпус- дешевле выйдет.
Кроме кондёров меряет всё!
На борту МЕГА328, smd
Если без ESR, старый вариант на ATmaga8 и выводных резисторах, ещё дешевле.
Где-то на форумах народ прошивки пишет.
С таким же успехом, можно купить и дешевенький мультиметр с такой функцией (всего в 2 раза дороже предложенной китайской поделки). Только, мультиметр как правило уже есть, но без данной функции. Во-вторых, сделать самостоятельно по данной схеме обойдется не более 300р, если не покупать детали в чип и дипе. И наконец, гораздо приятнее сделать это самому.