Ускорение функции micros()
- Войдите на сайт для отправки комментариев
Вс, 17/01/2016 - 08:54
Micros()
Возвращает количество микросекунд с момента начала выполнения текущей программы на плате Arduino. Значение переполняется и сбрасывается на ноль, приблизительно через 70 минут. На 16MHz платах Ардуино функция micros() имеет разрешение 4 микросекунды (возвращаемое значение всегда кратно 4). На 8MHz платах разрешение функции 8 микросекунд.
Как можно улучшить разрешение функции micros() до 2 или 1 микросекунды?
ну, очевидно же - 32MHz!
Ну следуя логике - частотой процессора - на 32 или 64МГц будет 2 или 1 мкс
Нет 32MHz! не правильный ответ. Железо Arduino nano 16 MHz!
Мне нужен программный способ.
Micros()
Как можно улучшить разрешение функции micros() до 2 или 1 микросекунды?
Написать свою через прерывание от таймера.
Только, надеюсь, Вы понимаете для чего Вам это нужно и как Вы собираетесь этим пользоваться, т.к. микросекунда - это всего лишь 16 команд процессора. Т.е. Ваша программа неминуемо будет вносить искажения. Если нужно точно, то надо считать количество команд и вносить соответсвующие поправки.
Мне нужно точно измерить частоту в диапазоне от 2000 до 10000 Гц. Желательно с долями герца. Я беру 100 полных тактов частоты и считаю время нужное для этого. Потом время делю на количество тактов. Получаем частоту.
Код пока такой.
//---------------------------------------------------------------------------------------------------------------------------------//
#include <CyberLib.h> //
#include <Wire.h> //
#include <LiquidCrystal_I2C.h> //
//---------------------------------------------------------------------------------------------------------------------------------//
LiquidCrystal_I2C lcd(0x27, 16, 2); // Для экрана 16х2 (двухстрочный) //
//---------------------------------------------------------------------------------------------------------------------------------//
unsigned long per1 =100000; // //
unsigned long interval_LCD = 1000; //Интервал смены показаний на LCD в миллисекундах //
unsigned long lastTime_LCD = 0; //Время последнего изменения состояния для LCD в миллисекундах //
unsigned long DeltaTime_LCD = 0; //Время с момента последнего вывода на LCD в миллисекундах //
unsigned long currentTime = 0; //Текущее время в милисекундах //
//---------------------------------------------------------------------------------------------------------------------------------//
boolean sig_new,sig_old,triger_new,triger_old; //
unsigned int t4,p1; //
unsigned long time_1,time_2; //
float f; //
//---------------------------------------------------------------------------------------------------------------------------------//
void setup() { //
pinMode(10, INPUT); //Назначим вывод 10 на ввод сигналов //
digitalWrite(10, HIGH); //Включаем подтягивающий резистор //
D10_In; //
lcd.init(); //Задаем размерность LCD дисплея //
lcd.backlight(); //Включаем подсветку экрана //
lcd.clear(); //Очистка экрана //
} //
//---------------------------------------------------------------------------------------------------------------------------------//
void loop() //
{ //
//===================================================================================================================================
p1=0; //
label_a : //
sig_old=sig_new; //
triger_old=triger_new; //
sig_new=D10_Read; //
if (sig_old==0 && sig_new==1 ) {triger_new=1;t4=p1;p1=p1+1;} //
if (sig_old==0 && sig_new==1 && abs(time_2-time_1)>per1 ) {triger_new=0;p1=0;} //
if (triger_old==0 ) time_1=micros(); //
if (triger_old==1 && triger_new==0 ) {;} else {time_2=micros();goto label_a;} //
//---Условия для вывода показаний на LCD-------------------------------------------------------------------------------------------//
currentTime=millis(); //
DeltaTime_LCD =abs(currentTime - lastTime_LCD); //Время с момента последнего вывода на LCD //
if (DeltaTime_LCD >= interval_LCD ) //Условия вывода на LCD //
{ //
//---------------------------------------------------------------------------------------------------------------------------------//
f=1000000.0/(abs(time_2-time_1)/(t4*1.0)); //
lcd.setCursor(0, 0);lcd.print(t4); //
lcd.setCursor(8, 0);lcd.print(abs(time_2-time_1)); //
lcd.setCursor(0, 1);lcd.print(f,2); //
//---------------------------------------------------------------------------------------------------------------------------------//
lastTime_LCD = currentTime; //
} //
//---------------------------------------------------------------------------------------------------------------------------------//
} //
Забудьте тогда про micros()
Берете timer1 16 битный с предделителем 1, настраиваете его на режим ctc и заносите в ocr1a=32000
Настраиваете прерывание по сравнению и там увеличиваете своб переменную счетчика unsigned int
Таким образом получаете 32 разрядный счетчик с тактом счета 1/16000000 секунд, т.е.
Для большей точности счетчик можно запускать в начале полупериода измеряемой частоты, а в конце полупериода брать значение нашего 32бииного счетчика и пересчитывать его в частоту, потом усреднять на цикле из нескольких измерений
За ответ спасибо. Но проблема в том, что я еще не изучал как программировать таймеры. Поэтому и сделал таймер на программном коде. Буду теперь разбираться в таймерах.
Буду теперь разбираться в таймерах.
Правильный источник для этого - http://www.atmel.com/images/doc2505.pdf
Ну и даташит на ATMega328 никто не отменял, конечно.
Попробовал поменять параметры 0 таймера.
Добавил код:
Все поменялось. Как сделать время 2 или 1 мкс?
Все научился. надо было добавить для стандартных настроек строку:
TCCR0B = TCCR0B & 0b11111000 | 0x03;
И еще глупый вопрос по таймерам.
Я хочу на таймере 2 сделать делитель с переменным коэффициентом деления от 1 до 255. Коэффициент деления должен меняться не в Setup , а в Loop(). таймер должен входную частоту делить и выдавать на выход. Как мне задать в программе номера входных и выходных пинов?
В разделе проекты вроде же был частотомер на базе ардуино. Не подходит или не смотрели?
Единственное, что нашел стояшее
http://www.cqham.ru/forum/showthread.php?20109-%D7%E0%F1%F2%EE%F2%EE%EC%...
У остальных низкая точность на низких частотах.
Может чем поможет http://www.gammon.com.au/timers
Может чем поможет http://www.gammon.com.au/timers
Спасибо. Здесь хоть на английском, но думаю можно разобраться. Именно то , что хотел
Мне нужно точно измерить частоту в диапазоне от 2000 до 10000 Гц. Желательно с долями герца. Я беру 100 полных тактов частоты и считаю время нужное для этого. Потом время делю на количество тактов. Получаем частоту.
То, что вы способны придумать свой способ измерения -это конечно хорошо. Но есть общепринятый способ измерения, точнее и удобнее. Один 16-битный таймер тактируется от измеряемого сигнала и считает кол-во своих тактов. Второй таймер отмеряет промежуток времени в 1 секунду. Кол-во пойманных тактов за 1 секунду = частота. Способ многократно обсуждался здесь, приводились готовые решение, давались ссылки на готовые библиотеки. Зачем изобретать велосипед?
То, что вы способны придумать свой способ измерения -это конечно хорошо. Но есть общепринятый способ измерения, точнее и удобнее. Один 16-битный таймер тактируется от измеряемого сигнала и считает кол-во своих тактов. Второй таймер отмеряет промежуток времени в 1 секунду. Кол-во пойманных тактов за 1 секунду = частота. Способ многократно обсуждался здесь, приводились готовые решение, давались ссылки на готовые библиотеки. Зачем изобретать велосипед?
А как этим способом померить частоту с точностью 0.01 Гц? За интервал времени 1 секунда.
Buldakov, Измерять 100 секунд. И то не факт, что будет точно. Копеечная ардуина не заменит профессиональный частотомер. У неё нет на это ни технических возможностей ни эталонного генератора, частота кварца на ней гуляет на сотню килогерц, какая тут точность.
Buldakov, Измерять 100 секунд. И то не факт, что будет точно. Копеечная ардуина не заменит профессиональный частотомер. У неё нет на это ни технических возможностей ни эталонного генератора, частота кварца на ней гуляет на сотню килогерц, какая тут точность.
Ну по поводу кварца. У него стабильность 10-6 что при 16 мгц составит десятки герц.(реально меньше) Сотни кгц - это стабильность очень плохого мультивибратора если специально постараться.
Теперь задачка, которую надо решить (специально для вас). Нужен металлоискатель. Он собран на RL генераторе с частотой генерации 7000 гц. При подносе металла частота генератора становится 7001 гц. (и это максимальное значение) При этом такое изменение частоты действует меньше 1 секунды. Надо измерить такое динамическое изменение частоты и отфильтровать его. Ваши предложения. Задачка простая.
Есть правда еще вариант сделать аналоговую систему автоподстройки частоты с ФАП с выделением сигнала рассогласования фазы. (у меня было так) Порядка 20 корпусов ОУ (счетверенных)
Теперь задачка, которую надо решить (специально для вас).
Ещё один экзаменатор нашёлся. Кому надо - пусть тот и решает.
Buldakov, В металлоискателях не силён, но очевидно что бы отличить 7000 гц от 7001 не на грани погрешности -потребуется очень серьёзная схемотехника, так что 20 корпусов ОУ -весьма похоже на правду.
Хорошо пусть будет не маталлоискатель. Но суть дела это не меняет.
Дана частота 7000 гц. При максимальном уровне измеряемой величины частота будет 7001 гц. Как измерить этот 1 Гц разницы с высокой точностью? типа 0.001 гц на интервале времени измерения меньше 1 секунды.
Это для нормальных кварцов. У дунек, китайцы лепят нечто, что действительно плавает по частоте, если и не на сотни герц, то сравнимо с мультивибратором.
Измерять "плавающим измерителем" с точностью в третьем порядке .. получится - расскажите обязательно. Очень любопытно.
Дана частота 7000 гц. При максимальном уровне измеряемой величины частота будет 7001 гц. Как измерить этот 1 Гц разницы с высокой точностью? типа 0.001 гц на интервале времени измерения меньше 1 секунды.
По крайней мере для стандартной UNO/NANO никак не измерить . Можно конечно получить после запятой какие-то цифры, но они будут плавать и скакать непредсказуемым образом. Начинать разговор о возможности чего-то вразумительного после запятой можно при наличии прецизионного генератора 16МГц, от которого затактировать МК ардуино.
Дана частота 7000 гц. При максимальном уровне измеряемой величины частота будет 7001 гц. Как измерить этот 1 Гц разницы с высокой точностью? типа 0.001 гц на интервале времени измерения меньше 1 секунды.
Припустим за 1С происходит 7000 тактов. Счетчик насчитает 7000 +/- 1 такт за 1 секунду. Итого точнее не получится. Нужно использовать аналоговый вход для определения позиции по фронту на последнем измерении.
По поводу стабильности кварца. На вход arduino подаю сигнал с высокостабильного генератора на 32 мгц с делителем на 555ие19 на выходе получаю частоту 15625 гц. Ее и измеряю. Жду прихода полных 15626 импульсов и определяю время этих импульсов. Время плавает в пределах 1000024 1000032 мкс. Частота плавает в пределе +/- 0.06 гц. Поскольку частотомер чисто программный - то эту погрешность можно отнести к временем между опросами цифрового входа. Опрос происходит с частотой около 450000 раз в секунду. Большей частоты опроса (программным способом) у меня не получилось. Поэтому нестабильность пока ищу сдесь. Другое дело, что долговременная стабильность а arduino плавает. С этим полностью согласен. При включении и прогреве частота уверенно побежала. В данном случае это совсем не важно. Мне не нужно эталонное измеренное значение частоты с высокой стабильностью.
А по поводу того, что частота плавает есть специальные методы для борьбы с этим. например:
https://ru.wikipedia.org/wiki/%D0%90%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%B0%D1%8F_%D1%81%D0%BA%D0%BE%D0%BB%D1%8C%D0%B7%D1%8F%D1%89%D0%B0%D1%8F_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D1%8F%D1%8F_%D0%9A%D0%B0%D1%83%D1%84%D0%BC%D0%B0%D0%BD%D0%B0
http://konkop.narod.ru/Files/7_74_79.pdf
Buldakov, я не пойму, к чему вы все эти речи произносите. Есть готовая библа, которая измеряет, усредняет, и прочее. Вы её пробовали? Пример двух цифр после запятой там прямо на картинке.
Buldakov, я не пойму, к чему вы все эти речи произносите. Есть готовая библа, которая измеряет, усредняет, и прочее. Вы её пробовали? Пример двух цифр после запятой там прямо на картинке.
Я описываю, что у меня получилось. Если вам не интересно - можете не читать.
По поводу данной библиотеки ее я пробовал. При компиляции выдается ошибка и все на этом. и при том, что она только до 1000 гц. Другая библиотека от 1000 гц и измеряет только целые частоты. Ни одна из них не подходит. Думаете мне охота что то придумывать если можно найти готовое. Так нет же ничего.
Buldakov, и что, раз у вас не скомпилировалось, значит будем ставить крест? Или всё таки разбираться? У меня не выдаёт ошибок. И по поводу 1000 Герц -это не предел, это рекомендация. Он измеряет и выше в данном режиме. Пробуйте, тестируйте. У меня нет генератора с разрешением менее герца что бы проверить как он измеряет.
А в принципе зачем разбираться в чужом коде. Уже написал свой. Полностью программный. меня устраивает. Если не устроит куплю железку более мощную. Цена вопроса 200 руб. Буду лучше изучать таймеры.
Уважаемый ТС.
Бросьте попытки использовать всякие библиотеки, там писать не много, а понимания работы будет значительно больше.
Попробуйте выход генератора забросить на вход прерывания: функция attachInterrupt 0 (на digital pin 2) или 1 (на digital pin 3).
После первого начинаем считать их например до 100, на последнем смотрим таймер и делим на 100.
Для более точного определения времени попробуйте использовать таймер с предделителем 001 где какую то N увеличивайте по кругу N++. Вам не надо знать полное время выполнения 100 циклов. Вам достаточно узнать тот хвостик которйы останется после отсчитывания вашего N++, если N типа int то может намотать несколько кругов.
Не используйте в качестве счетчика переменный типа long и функцию micros() - она медленная, а операции с long еще медленнее.
Не парьтесь над стабильностью кварца. Вам достаточно измерить время за секунду до обнаружения металла и при обнаружении. Калибровочное время всегда перерасчитывайте и постоянно поправляйте, тогда не будет проблем на соленых грунтах.
Тут ссылка на то, что вы пытаетесь реализовать. Я такой спаял - прекрасно работает. Идеальная игрушка для ребенка кладоискателя или чернометальщика.
http://md4u.ru/viewtopic.php?t=3977
Ну хороше, есть у Вас на входе сигнал из диапазона 7000-7001Гц, и Вы хотите его померить именно в этом диапазоне. Так это просто! Вы опрашиваете нужный пин с частотой 13998Гц. И в результате опроса получаете сигнал с частотой 1-2Гц. А эту уже сильно проще. Далее меряете период и.. а в общем то пересчитывать в частоту для ваших дальнейших целей не нужно. Частота 13998Гц не догма, можна подобрать и такую, чтоб разность сразу на наушники.
Последний вариант кода, того что получился выкладываю ниже.
Просьба помочь это реализовать того, кто разбирается в таймерах. Пока научился только работать с регистрами в setup. и то только в теории по книгам.
Первый этап. Пока не знаю как это реализовать. Но примерный план такой. Настраиваю триггер 2 на предделитель с коэффициентом 1 или 8. как делитель частоты 16 М. Запуск этого триггера осушествляется по условию: triger_old==0 && triger_new==1. остановка триггера 2 по условию: triger_old==1 && triger_new==0. Считаем сколько раз триггер 2 переполнялся и текущее значение в регистре счетном. Потом проделываю тоже самое, что и сейчас, только вместо micros() беру значение из таймера 2. Данным действием мы сможем убрать неточность вызванную функцией micros(). Неточность вызванную длительностью сигнала triger_new=1. пока не трогаем.
Частота 13998Гц не догма, можна подобрать и такую, чтоб разность сразу на наушники.
Изменение частоты в 1 Гц - на наушники?
Это бесполезно. На совсем низкой частоте (до субконтроктавы) - да, будет разница в полтона, но здесь и дифференциальная чувствительность уха никакая, и наушники такую частоту, как правило, воспроизвести не могут. Если брать что-то удобное типа 440 Гц, то там 12 Гц - это тоже на самом предел различимости.
Просьба помочь с таймером. В Setup() прописываю такой код.
Хочу запрограэто на выммировать таймер 2 на выдачу частоты в 1024 раза меньше частоты кварца и выдать это на выход 11 Arduino.
Там ничего нет. Просьба подправить, что у меня неправильно.
//Пример программирования 2 таймера 8 бит
pinMode(11, OUTPUT); //Назначим вывод 11 на вывод сигналов
TCNT2 =0b00000000; //Сбрасываем счетный регистр таймера.
TCCR2A=0b00000000; //Сброс настроек таймера
OCR2A =0b00000000; //Регистр сравнения. Сюда мы записываем то, с чем надо сравнить.
TCCR2B=0b00000100;
TIMSK2=0b00000000;
ASSR =0b00000000;
Buldakov, На выходе имеем 16000000/1024=15625Hz:
Частота 13998Гц не догма, можна подобрать и такую, чтоб разность сразу на наушники.
Изменение частоты в 1 Гц - на наушники?
Это бесполезно. На совсем низкой частоте (до субконтроктавы) - да, будет разница в полтона, но здесь и дифференциальная чувствительность уха никакая, и наушники такую частоту, как правило, воспроизвести не могут. Если брать что-то удобное типа 440 Гц, то там 12 Гц - это тоже на самом предел различимости.
Выделил шрифтом.
Да спасибо за код. Все работает. теперь посмотрю что я делал неправильно.
Выделил шрифтом.
А толку было выделять, если ПРИ ЛЮБОЙ частоте разница в наушниках будет малоразличима.
Дуругими словами, невозможно подобрать частоту, чтобы добиться в наушниках ощутимого эффекта.
(Выделил на всякий случай)
Buldakov, На выходе имеем 16000000/1024=15625Hz:
Не понял какие биты перевести в 1
И частота почему то не 16000000/1024 а 8000000/1024
Buldakov, это аналог записи TCCR2A=B01000010, советую вам пользоваться буквенными названиями бит, так нагляднее. Второй вопрос не точно понял. Но смысл кажется уловил. После делителя таймер работает с частотой 16000000/делитель, но за один свой такт он может только 1 раз изменить состояние выхода. Соответссно что-бы получить частоту в 1024 раз меньше тактовой нужно разделить на 1024 и ещё раз на 2. В моём скетче это деление происходит 2 раза. Первый раз прескалером на 32, второй раз регистром сравнения на 16 .
По поводу частоты. я беру 16000000 делю на 1024 (предделитель) и у меня должна получиться частота 15625, а получается частота 7822 гц. Как будто еще где то вкрался делитель на 2. (есть подозрение что предделитель переключает выход таймера из 0 в 1 - поэтому частота в 2 раза меньше планируемой)
По поводу TCCR2A=B01000010
в даташите wgm21 вроде 3 бит. А com2A0 вроде 4 бит. поэтому я и писал B00011000 и у меня ничего не работало.
Buldakov, есть подозрение что предделитель переключает выход таймера из 0 в 1 - поэтому частота в 2 раза меньше планируемой)
Я же вам в предыдущем сообшении написал почему так происходит
в даташите wgm21 вроде 3 бит. А com2A0 вроде 4 бит.
Видимо вы не в тот даташит смотрели.
Может быть не тот даташит.
Теперь другой вопрос.
Как мне прочитать состояние регистра триггера? (типа r1=TCNT2;) Нужно ли на время чтения регистра останавливать счетчик, а потом запускать?
и как посчитать сколько раз триггер сбрасывался до нуля?
Buldakov, не понял, что вы хотите конкретно? До скольки должен считать счётный регистр TCNT2? Например в моём скетче значение TCNT2 никогда не превысит 15. Что-бы считать кол-во переходов через ноль нужно создать прерывание по соответвующему регистру, и в прерывании инкременировать какую-нибудь переменную.
В том то и дело что TCNT2 должен быть не более 15. Но у меня при чтении r1=TCNT2; переменная r1 может принимать значение и 70 и 80?
Buldakov, в скетче из #36 это невозможно. Либо вы что-то изменили уже у себя.
Сколько не смотрел различные даташиты ничего не понял. биты wgm21 и com2a1 находятся во всех даташитах на другом месте. И непонятно откуда взялось TCCR2A и TCCR2B. Во всех даташитах только TCCR2. Но в программе забиваю TCCR2 - и выдает ошибку.
Сколько не смотрел различные даташиты ничего не понял. биты wgm21 и com2a1 находятся во всех даташитах на другом месте. И непонятно откуда взялось TCCR2A и TCCR2B. Во всех даташитах только TCCR2. Но в программе забиваю TCCR2 - и выдает ошибку.
Какие же это даташиты Вы смотрите?
Открываем даташит на ATMega328P и на стр.143 читаем "The counting sequence is determined by the setting of the WGM21 and WGM20 bits located in the Timer/Counter Control Register (TCCR2A) and the WGM22 located in the Timer/Counter Control Register B (TCCR2B)."
Далее, в п. 18.11 (со стр. 153) оба регистра TCCR2A и TCCR2B подробно описаны.
А Вы какой даташит смотрели?
По вашей ссылке все правильно. Я сммотрю этот даташит. на atmega32 по ссылке ниже. Там на стр. 122 регистр TCCR2.
http://www.gaw.ru/pdf/Atmel/AVR/atmega32.pdf
А при чём здесь Atmega32? В Arduino Nano стоит Atmega328P. Это разные микроконтроллеры с разными регистрами. Atmega32 это предыдущее поколение.