dimax, а почему не оформить в виде обработчика прерывания по захвату таймера?
Я думал. Если сделать обычное прерывание, то компилятор сначала напихает туда команд сохранения всех рабочих регистров в стек, это сразу 15-20 тактов МК коту под хвост. Этого нельзя допустить. Значит надо выполнить ISR_NAKED (), сохранить по самому минимуму в стек, затем сохранить куда-то 16 бит данных из регистра ICR, перепрограммировать таймер на ловлю противоположного ипульса, восттановить сохраннёные регистры из стека и можно выходить. Просто на обычной ассемблерной вставке это скорее всего не сделать, придёться цеплять внешний ассемблерный файл, который подключать к проекту. В общем довольно муторно, конечно была бы какая то серьёзная надобность, можно б было и написать..
ua6em пишет:
И как его применять???
Подать на вход ICP (пин8) импульс, функция выплюнет его длительность в тактах Мк
Ну я так и делал через ISR_NAKED, в нем асм-вставками, как и у вас ловил прерывания в миниум регистров. Да, есть ещё регистры GPIO аж 3шт .. :) Понятно что последовательным кодом и ручной проверкой флагов можно ловить меньшие интервалы, но у меня стояла задача исключить долгие ожидания типового pulseIn().
ua6em, ну да. Обычной операцией присваивания. И название функции нужно исправить на uint16_t asm_func(), видимо "u" куда-то пропало при вставлении кода.
ua6em, ну да. Обычной операцией присваивания. И название функции нужно исправить на uint16_t asm_func(), видимо "u" куда-то пропало при вставлении кода.
Показывает 0, длина импульсов 1000мксек и 2000мксек
ua6em, функция точно рабочая. Если не работает значит у тебя что-то не так. Версия IDE та что советовал? КОмпилятор любит выкидывать непонятные ему куски кода. Может помочь указывание volatile перед функцией. Если не поможет, то карму чистить нужно..))
ua6em, функция точно рабочая. Если не работает значит у тебя что-то не так. Версия IDE та что советовал? КОмпилятор любит выкидывать непонятные ему куски кода. Может помочь указывание volatile перед функцией. Если не поможет, то карму чистить нужно..))
Скомпилировал оба примера в IDE 1.6.8 в первом сигнал брал с 12 пина во вотором с 8, в обоих случаях показывает 0.00 и 0 ))) Сигнал подаю с приемника радиоуправления FS-IA6...
Код второго:
Взял другой твой скетч, откомпилировался, залился, работает, частоту показывает 50 дути 5 при ширине импульса 1000 мксек и 10 при ширине импульса 2000мксек )))
Дело было не в бобине )))
ua6em, ну вот вынудил проверить. Всё работает. Импульс сгенерил таймером2 с этой же дуни .
Для проверки генерю 2-м таймером импульс периодом 510 тактов (Phase Correct PWM Mode) , из которых 2 такта лог"0" и 508 тактов лог "1". 11 пин выхода таймера2 соединён с 8 пином входа ICP. Как видно по скриншоту определяет астрономически точно, но это потому что сигналы синхронны :)
В принципе я конечно не спорю, - моя недоработка что работает не везде, но раньше у меня не хватало опыта что б понять причину. Сейчас думаю хватит, но так неохота.. :) Мне вот интересней тема с объединенением таймеров в стм32 для измерения имульса, но страшно браться, я ж уже один раз не смог, вдруг опять не смогу :(
ua6em, ну вот вынудил проверить. Всё работает. Импульс сгенерил таймером2 с этой же дуни .
Для проверки генерю 2-м таймером импульс периодом 510 тактов (Phase Correct PWM Mode) , из которых 2 такта лог"0" и 508 тактов лог "1". 11 пин выхода таймера2 соединён с 8 пином входа ICP. Как видно по скриншоту определяет астрономически точно, но это потому что сигналы синхронны :)
В принципе я конечно не спорю, - моя недоработка что работает не везде, но раньше у меня не хватало опыта что б понять причину. Сейчас думаю хватит, но так неохота.. :) Мне вот интересней тема с объединенением таймеров в стм32 для измерения имульса, но страшно браться, я ж уже один раз не смог, вдруг опять не смогу :(
Залил! Выходит дело не в скетче и с 11 выхода и с приемника гонит 0 )))
Значит дело в самой ардуине
По объединению таймеров - может народ, что подскажет если не получится...
Идея достойная жеж
Залил! Выходит дело не в скетче и с 11 выхода и с приемника гонит 0 )))
Значит дело в самой ардуине
Фантастика какая-то, не может такого быть. Там и без источника импульса , если просто воткнуть проводок в 8 пин, то в сериал мониторе начинает мусор сыпаться. Импульс с 11 ноги кстати ваш осциллограф может и не схватить, там всего 120нС в ноле.
PS: Кстати, а залил что, хекс или скомпилил у себя? Если второе, то доверия меньше))
Залил! Выходит дело не в скетче и с 11 выхода и с приемника гонит 0 )))
Значит дело в самой ардуине
Фантастика какая-то, не может такого быть. Там и без источника импульса , если просто воткнуть проводок в 8 пин, то в сериал мониторе начинает мусор сыпаться. Импульс с 11 ноги кстати ваш осциллограф может и не схватить, там всего 120нС в ноле.
PS: Кстати, а залил что, хекс или скомпилил у себя? Если второе, то доверия меньше))
Залил твой HEX вот так - C:\Users\User\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino14/bin/avrdude -CC:\Users\User\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino14/etc/avrdude.conf -v -patmega328p -carduino -PCOM3 -b57600 -D -Uflash:w:C:\ARDUINO\hex\icp_asm.ino.hex:i
Да и скомпилил, тот же баг, ясно жеж что в ардуине какой то косяк...
Залил в другую ардуино, тот же эффект...
Чудеса всё чудесатее и чудесатее при том, что твой код, что был в том же разделе выше работает на ура
ua6em, нашел косячёчек, компилятор не понимал что я что-то вывожу из функции (но в 1.6.8 -понимал! ) Как грится век живи -век учись ! Проверил, работает в 1.8.5
ua6em, нашел косячёчек, компилятор не понимал что я что-то вывожу из функции (но в 1.6.8 -понимал! ) Как грится век живи -век учись ! Проверил, работает в 1.8.5
А я уж постеснялся озвучивать, что и на UNO не пошло ни через компиляцию ни через загрузку HEX )))
Надеюсь, что исследование было полезным.
заработало, компилил в 1.8.7
Функция правда получается блокирующая, но не суть, интересно, как она отрабатывает с библиотекой servo.h
И сам отвечу, конфликтует! Не измеряет однако. Оно и не удивительно, используют же один таймер
Коллега Arhat109-2 обнадёжил, что asm-вставки нормально работают в прерывании, поэтому решил воплотить старую задумку -измерить как быстро после появления стороннего сигнала на входе сработает прерывание INT0/1 Подал сигнал от внешнего генератора на INT0, в прерывании дёргаю ногу PB4(Пин12). Опция ISR_NAKED отключает вмешательство компилятора, поэтому нога поднимается уже спустя 2 такта после входа прерывание. Посколько я не использовал рабочие регистры, то сохранять в стек ничего не нужно. Команда sbi не изменяет флагов, поэтому регистр SREG тоже нет нужды бэкапить. В общем прерывание реально пустое. Nop-ов добавил что б немного подзатянуть импульс.
И вот что получилось - На осциллограф выведено жёлтый луч -внешний сигнал пришедший на вход INT0 . Бирюзовый - сигнал снятый с 12 ноги.
Как видно по статистике дельта фронтов прыгает. В среднем от появления сигнала на входе до выполнения прерывания - около 700nS без учёта двух тактов. В реальной программе с сохранением регистров в стек добавится ещё куча команд. Счёт уже на микросекунды пойдёт.
И второй эксперимент - просто циклический опрос ноги.
int main (){
DDRB|=1<<4;// pin12=output
sei();
while(1){
asm volatile (
// пройти если единица -> первый импульс
"wait_start:" "\n\t"
"sbis 0x9,2" "\n\t" //Есть 1?
"rjmp wait_start" "\n\t" // тогда пропускаем
"sbi 0x05, 4" "\n\t" // PINB|=1<<4
"nop" "\n\t"
"nop" "\n\t"
"nop" "\n\t"
"nop" "\n\t"
"nop" "\n\t"
"cbi 0x05, 4" "\n\t" // PINB&=~1<<4
"wait_low:" "\n\t"
"sbic 0x09,2" "\n\t" // ноль пришёл?
"rjmp wait_low" "\n\t" // тогда уходим
);
};//end while
}
Тут прога ждёт единицу, выдаёт импульс, затем снова ждёт ноль. Скриншот выкладывать не буду, он такой-же только цифры другие - min290, max480, avg -387. Однако в 2 раза быстрее реагирует, хотя ожидал цифр ещё меньше. Но блокирующая. Но зато никаких потенциальных накладных расходов на сохранение в стек. Думайте сами, решайте сами, иметь или не иметь :)
По первому коду: вроде всё согласно даташита, ну или я не увидел косяков. Цена деления 200нсек.
1. Вход в naked перывание: 2 пуша адреса возврата (по 2 такта - SRAM!) + выборка 2 слов из таблицы векторов прерываний + исполнение команды jump (там лежит именно она). Итого 7 тактов.
2. Нога поднимается гарантировано ещё через 2 такта, уже имеем 9 тактов между фронтами;
3. Схема реагирования на входной сигнал по даташиту в среднем 2 такта (для INTx и 4 такта для PCINT, как понял) - вот ни 11 тактов между фронтами в среднем. Там жеж и писано что +- 1 такт, смотря когда придет сигнал .. полностью подтверждается вышим средним от 10 до 12 тактов.
4. Ширина импульса - это 5 тактов нопов + 2 такта на команду (задержан результат как первой так и второй ОДИНАКОВО) .. 7 тактов. Хорошо согласуется с вашей осцилограммой.
.. разве что похоже частота в 16Мгц несколько "гуляет" и слегка понижена. :)
P.S. Когда давал рекомендацию использовать таймер для определения длительностей импульсов (и не в одной теме сразу!), именно это и имел ввиду (пройдено уже давно) и даже где-то приятно что столько человек бросилось сразу же разбираться с режимом захвата таймера.
Он не даром сделан, ибо точнее. :)
P.P.S. уточнил по даташиту на Мега2560 - выделил жирным. Собственно, отсутствие на типовой меге2560 ножек ICP для режима "захват таймера" и подвигло отрисовать свою версию. Там ещё и PCINT далеко не все и третий UART отсутствует.
Замер длительности импульса ещё очень удобен для датчика цвета TCS3200, лучше которого мне пока повстречать не довелось. Видеть цвет при общей освещенности в 6-10лк .. это лучше чем "кошка на страже". :)
ua6em, нашел косячёчек, компилятор не понимал что я что-то вывожу из функции (но в 1.6.8 -понимал! ) Как грится век живи -век учись ! Проверил, работает в 1.8.5
Здравствуйте. Подскажите пожалуйста, как из вашей функции вывести не только длительность логической '1', а еще и длительность логического '0', который следует за '1'? Или даже лучше длительность импульса в сумме ('1' + '0'). Потом мне нужно будет просуммировать например 20 импульсов ('1' + '0') и уже исходя из этого посчитать частоту. Если со вторым я справлюсь, то с первым прошу у вас помощи.
Попробовал его - в симуляторе "Proteus 7" частота 5000 Гц, подаваемая на вход со скважностью 50%, скачет от 4600 Гц до 5000 Гц примерно, скважность скачет от 46% до 58% соответственно. Не могу понять в чем косяк.
А помоему с ICP проще, и точнее, нежели с Асм вставками. Тут уже люди говорили по этому поводу. И измеряемая длительность не ограничивается 16-ю разрядами.
Да согласен, для измерения коротких импульсов прерывания не подходят.
Как там любят говорить некоторые - кал мамонта?)
Тут ещё и не с таким приходится работать.(
Green, так я и сделал без прерываний. Но ICP умеет ловить только один фронт, что не позволяет измерить длительность импульса. Поэтому пришлось на асме дописать ожидание флага, быстро перепрограммировать таймер на ловлю другого фронта. Или есть какие то ещё варианты?
Здравствуйте. Подскажите пожалуйста, как из вашей функции вывести не только длительность логической '1', а еще и длительность логического '0', который следует за '1'? Или даже лучше длительность импульса в сумме ('1' + '0'). Потом мне нужно будет просуммировать например 20 импульсов ('1' + '0') и уже исходя из этого посчитать частоту. Если со вторым я справлюсь, то с первым прошу у вас помощи.
Сдаюсь. В общем, со вторым я тоже не справился. :(
dimax, подскажите пожалуйста, как с помощью вашей ассемблерной вставки http://arduino.ru/forum/programmirovanie/schityvanie-impulsov#comment-218898 просуммировать например 20 первых импульсов для последующего расчета частоты (для уменьшения погрешности расчета) исходя уже из длины этих 20 импульсов, а не одного отдельно взятого импульса?
RuleZZZZ, ну и запускайте 20 раз функцию. Но это не имеет смысла, на низких частотах будет 20 раз одно и тоже число. А для измерения высоких этот алгоритм не подходит.
Частоты нужны от 3 кГц до 30 кГц примерно. Запускать функцию - это значит при появлении единицы на 12 порту запускать? И так 20 раз? Разве не будет задержки от считывания состояния пина?
RuleZZZZ, от 3кГц есть смысл использовать другой алгоритм -измерение кол-ва периодов за секунду, точность будет существенно выше. Есть стандартная библа для этого.
В том и дело, что мне не надо за секунду. А нужны всего-лишь 10-20 первых импульсов от начала их подачи, причем, что на 3 кГц, что на 30 кГц. При 3 кГц 10 имп. будут длиной около 3.3 мс, а при 30 кГц соответственно 330 мкс, то есть периоды в каждом случае будут разные и очень короткие относительно 1 секунды.
В том и дело, что мне не надо за секунду. А нужны всего-лишь 10-20 первых импульсов от начала их подачи, причем, что на 3 кГц, что на 30 кГц. При 3 кГц 10 имп. будут длиной около 3.3 мс, а при 30 кГц соответственно 330 мкс, то есть периоды в каждом случае будут разные и очень короткие относительно 1 секунды.
Спасибо за ссылку. Попробовал эту библиотеку - к сожалению, она измеряет только ширину импульса, когда на ноге '1' (либо я не разобрался), а мне нужно измерять общую ширину '1' + '0', чтобы потом рассчитать частоту.
Почитав еще несколько тем по прерываниям и таймерам, нарыл код от dimax, немного его допилил под свои нужны и получилось не то, что хотелось. Может кто наведет на путь истинный и подскажет в чем проблема и почему переменная 'alltic' вместо положенных просуммированных 64000 тиков за 20 периодов при 5 кГц (3200 тиков за 1 период * 20 периодов) выводит какое-то непонятное число 168341, после чего неправильно считает частоту сигнала? Если убрать из захвата прерываний переменные 'n' и 'alltic', и просто выводить переменную 'tic' как это было в первоначальном варианте, то все отлично выводится - 3200 (количество тиков за 1 период) и правильно считается частота - 5 кГц. Понимаю, что ошибка где-то в логике, но не могу понять, где именно.
#include <LiquidCrystal.h>
LiquidCrystal lcd(4, 5, 6, 7, 10, 9);
volatile unsigned int int_tic;
volatile unsigned long tic;
volatile unsigned long n, alltic = 0;
unsigned int freq;
void setup()
{
lcd.begin(16, 2);
lcd.clear();
pinMode (8,INPUT); //вход ICP
TCCR1B = 0; TCCR1A = 0; TCNT1 = 0;
TIMSK1 = (1<<ICIE1)|(1<<TOIE1);//создавать прерывание от сигнала на пине ICP1
TCCR1B = (1<<ICNC1)|(1<<ICES1)|(1<<CS10);//div 1
}
ISR (TIMER1_CAPT_vect) //прерывание захвата сигнала на входе ICP1
{
tic = ((uint32_t)int_tic<<16)|TCNT1; //подсчёт тиков
alltic += tic;
n++;
ICR1=0; int_tic=0; TCNT1=0;
}
ISR (TIMER1_OVF_vect) { //прерывание для счёта по переполнению uint
int_tic++; //считать переполнения через 65536 тактов
if (int_tic > (F_CPU/65536)) {tic=0; int_tic=0;} //если на входе пусто более секунды
} //то обнулить счётчики
void loop()
{
if ( n == 20 )
{
n = 0;
freq = (float(16E6/alltic))/20;
lcd.clear();
lcd.setCursor(0,0);
lcd.print(alltic);
lcd.setCursor(0,1);
lcd.println(freq);
lcd.setCursor(6,1);
lcd.println("Hz");
delay(100);
}
}
RuleZZZZ, а зачем вам суммировать 20 отсчётов? Там не должно быть разброса значений при хорошем сигнале. По поводу вопроса - перед выполнением арифметических операций с volatile-переменными размерностью >8бит нужно запретить прерывания.
Сигнал будет с компаратора LM339. Суммированием хочу добиться лучшей точности, а также сдвига этих 20 периодов ближе-дальше по времени. То есть например считать не с первого импульса, а с 10-го или 20-го.
По поводу арифметических операций - то есть 'n' можно сделать 8бит и использовать внутри прерывания, а подсчет 'alltic' вывести в loop() с запретом прерываний в лупе во время этого счета?
Попробовал так сделать, но теперь переменная 'alltic' просто накапливает тики каждого периода и выводится на дисплей, то есть идет по нарастающей - 3200, потом 6400 и т.д. Частота при этом постоянная - '0'. В коде подсветил строки, которые поменял||добавил.
#include <LiquidCrystal.h>
LiquidCrystal lcd(4, 5, 6, 7, 10, 9);
volatile unsigned int int_tic;
volatile unsigned long tic;
volatile byte n, flag = 0;
volatile unsigned long alltic = 0;
unsigned int freq;
void setup()
{
lcd.begin(16, 2);
lcd.clear();
pinMode (8,INPUT); //вход ICP
TCCR1B = 0; TCCR1A = 0; TCNT1 = 0;
TIMSK1 = (1<<ICIE1)|(1<<TOIE1);//создавать прерывание от сигнала на пине ICP1
TCCR1B = (1<<ICNC1)|(1<<ICES1)|(1<<CS10);//div 1
}
ISR (TIMER1_CAPT_vect) //прерывание захвата сигнала на входе ICP1
{
tic = ((uint32_t)int_tic<<16)|TCNT1; //подсчёт тиков
n++; flag = 1;
ICR1=0; int_tic=0; TCNT1=0;
}
ISR (TIMER1_OVF_vect) { //прерывание для счёта по переполнению uint
int_tic++; //считать переполнения через 65536 тактов
if (int_tic > (F_CPU/65536)) {tic=0; int_tic=0;} //если на входе пусто более секунды
} //то обнулить счётчики
void loop()
{
cli();
if ( flag == 1 )
{
alltic += tic;
flag = 0;
}
else if ( n == 20 )
{
n = 0;
freq = (float(16E6/alltic))/20;
alltic = 0;
}
sei();
lcd.clear();
lcd.setCursor(0,0);
lcd.print(alltic);
lcd.setCursor(0,1);
lcd.println(freq);
lcd.setCursor(6,1);
lcd.println("Hz");
delay(100);
}
}
В общем, если кому интересно, то вроде бы удалось победить свое ТЗ. Напоминаю, что нужно узнать время в тиках, которое необходимо посчитать за N-нное количество периодов сигнала, чтобы в дальнейшем уже высчитать частоту этого сигнала. Причем сигнал может быть с разной скважностью. Также необходимо, чтобы была возможность сдвига чтения сигналов по времени, то есть чтобы можно было сделать выборку N-нного количества импульсов не с первого, а например с 10-го, или 20-го импульса. Частоты нужны примерно от 3 кГц до 30 кГц, если больше 30 кГц, то лишним не будет.
Так вот, надыбал еще один код в интернете, на основании которого сделал то, что мне нужно, и кажысь оно работает, по крайней мере в симуляторе Протеуса. УРА, товарищи! Выкладываю, вдруг кому будет еще интересно. И возможно кто-то сможет подсказать, какие "подводные" камни могут приключиться в реальной работе? Ведь в "ISR (TIMER1_CAPT_vect)" слишком много операций производится.
#include <LiquidCrystal.h>
LiquidCrystal lcd(4, 5, 6, 7, 10, 9);
volatile boolean first;
volatile boolean triggered;
volatile unsigned long overflowCount;
volatile unsigned long startTime;
volatile unsigned long finishTime;
unsigned long allTime = 0;
byte n = 0;
unsigned int freq;
// timer overflows (every 65536 counts)
ISR (TIMER1_OVF_vect)
{
overflowCount++;
} // end of TIMER1_OVF_vect
ISR (TIMER1_CAPT_vect)
{
// n++;
// grab counter value before it changes any more
unsigned int timer1CounterValue;
timer1CounterValue = ICR1; // see datasheet, page 117 (accessing 16-bit registers)
unsigned long overflowCopy = overflowCount;
// if just missed an overflow
if ((TIFR1 & bit (TOV1)) && timer1CounterValue < 0x7FFF)
overflowCopy++;
// wait until we noticed last one
if (triggered)
return;
if (first)
{
startTime = (overflowCopy << 16) + timer1CounterValue;
first = false;
return;
}
finishTime = (overflowCopy << 16) + timer1CounterValue;
triggered = true;
TIMSK1 = 0; // no more interrupts for now
} // end of TIMER1_CAPT_vect
void prepareForInterrupts ()
{
noInterrupts (); // protected code
first = true;
triggered = false; // re-arm for next time
// reset Timer 1
TCCR1A = 0;
TCCR1B = 0;
TIFR1 = bit (ICF1) | bit (TOV1); // clear flags so we don't get a bogus interrupt
TCNT1 = 0; // Counter to zero
overflowCount = 0; // Therefore no overflows yet
// Timer 1 - counts clock pulses
TIMSK1 = bit (TOIE1) | bit (ICIE1); // interrupt on Timer 1 overflow and input capture
// start Timer 1, no prescaler
TCCR1B = bit (CS10) | bit (ICES1); // plus Input Capture Edge Select (rising on D8)
interrupts ();
} // end of prepareForInterrupts
void setup ()
{
lcd.begin(16, 2); //количество столбцов и строк дисплея
lcd.clear(); // clear the screen
// set up for interrupts
prepareForInterrupts ();
} // end of setup
void loop ()
{
// wait till we have a reading
if (!triggered)
return;
n++;
unsigned long elapsedTime = finishTime - startTime;
allTime += elapsedTime;
if ( n == 20 )
{
// period is elapsed time
//unsigned long elapsedTime = finishTime - startTime;
// frequency is inverse of period, adjusted for clock period
float freq = (F_CPU*n / float (allTime)); // each tick is 62.5 ns at 16 MHz
lcd.clear();
lcd.setCursor(0,0);
lcd.print(allTime);
lcd.setCursor(0,1);
lcd.println(freq);
lcd.setCursor(6,1);
lcd.println("Hz");
allTime = 0;
n = 0;
// so we can read it
delay (100);
}
prepareForInterrupts ();
} // end of loop
Вы в курсе куда пойдёт программа, если случайно выполнится 82 строка?
Неа, не в курсе, подскажите пожалуйста. А что не так со строкой этой? То, что мне надо, программа выполняет - при подаче импульсов выводит на экран частоту, при прекращении подачи импульсов оставляет на экране последнее значение частоты. При повторной подаче импульсов очищает экран и снова выводит нужную частоту.
Вы в курсе куда пойдёт программа, если случайно выполнится 82 строка?
Неа, не в курсе, подскажите пожалуйста.
Перейдет к строке 79. ;)) Тут все нормально. В lcd не нужно println писать, просто это бессмысленно, а так - не перживай, если и есть пара недочетов, то не критичных. Только с "n" ты ничего не делаешь, случайно закомментил?
Спасибо за комментарий. 'n' в 21 строке - забыл убрать оттуда, так как пытался сразу там сделать подсчет количества периодов. Но потом сделал в лупе, так как понял, что триггер "щелкает" после захвата фронта импульса и идет дальнейшее выполнение программы. Думаю этого мне и не хватало, я никак не мог прикрутить "флаг", при котором программа в основном цикле будет видеть, что пришел фронт импульса.
dimax, а почему не оформить в виде обработчика прерывания по захвату таймера?
Я думал. Если сделать обычное прерывание, то компилятор сначала напихает туда команд сохранения всех рабочих регистров в стек, это сразу 15-20 тактов МК коту под хвост. Этого нельзя допустить. Значит надо выполнить ISR_NAKED (), сохранить по самому минимуму в стек, затем сохранить куда-то 16 бит данных из регистра ICR, перепрограммировать таймер на ловлю противоположного ипульса, восттановить сохраннёные регистры из стека и можно выходить. Просто на обычной ассемблерной вставке это скорее всего не сделать, придёться цеплять внешний ассемблерный файл, который подключать к проекту. В общем довольно муторно, конечно была бы какая то серьёзная надобность, можно б было и написать..
И как его применять???
Подать на вход ICP (пин8) импульс, функция выплюнет его длительность в тактах Мк
ua6em, ну да. Обычной операцией присваивания. И название функции нужно исправить на uint16_t asm_func(), видимо "u" куда-то пропало при вставлении кода.
ua6em, ну да. Обычной операцией присваивания. И название функции нужно исправить на uint16_t asm_func(), видимо "u" куда-то пропало при вставлении кода.
Показывает 0, длина импульсов 1000мксек и 2000мксек
ua6em, функция точно рабочая. Если не работает значит у тебя что-то не так. Версия IDE та что советовал? КОмпилятор любит выкидывать непонятные ему куски кода. Может помочь указывание volatile перед функцией. Если не поможет, то карму чистить нужно..))
ua6em, функция точно рабочая. Если не работает значит у тебя что-то не так. Версия IDE та что советовал? КОмпилятор любит выкидывать непонятные ему куски кода. Может помочь указывание volatile перед функцией. Если не поможет, то карму чистить нужно..))
Скомпилировал оба примера в IDE 1.6.8 в первом сигнал брал с 12 пина во вотором с 8, в обоих случаях показывает 0.00 и 0 ))) Сигнал подаю с приемника радиоуправления FS-IA6...
Код второго:
Можешь скинуть сюда бинарник, сравню
Взял другой твой скетч, откомпилировался, залился, работает, частоту показывает 50 дути 5 при ширине импульса 1000 мксек и 10 при ширине импульса 2000мксек )))
Дело было не в бобине )))
ua6em, ну вот вынудил проверить. Всё работает. Импульс сгенерил таймером2 с этой же дуни .
Для проверки генерю 2-м таймером импульс периодом 510 тактов (Phase Correct PWM Mode) , из которых 2 такта лог"0" и 508 тактов лог "1". 11 пин выхода таймера2 соединён с 8 пином входа ICP. Как видно по скриншоту определяет астрономически точно, но это потому что сигналы синхронны :)
Скетч. и готовый hex
В принципе я конечно не спорю, - моя недоработка что работает не везде, но раньше у меня не хватало опыта что б понять причину. Сейчас думаю хватит, но так неохота.. :) Мне вот интересней тема с объединенением таймеров в стм32 для измерения имульса, но страшно браться, я ж уже один раз не смог, вдруг опять не смогу :(
ua6em, ну вот вынудил проверить. Всё работает. Импульс сгенерил таймером2 с этой же дуни .
Для проверки генерю 2-м таймером импульс периодом 510 тактов (Phase Correct PWM Mode) , из которых 2 такта лог"0" и 508 тактов лог "1". 11 пин выхода таймера2 соединён с 8 пином входа ICP. Как видно по скриншоту определяет астрономически точно, но это потому что сигналы синхронны :)
Скетч. и готовый hex
В принципе я конечно не спорю, - моя недоработка что работает не везде, но раньше у меня не хватало опыта что б понять причину. Сейчас думаю хватит, но так неохота.. :) Мне вот интересней тема с объединенением таймеров в стм32 для измерения имульса, но страшно браться, я ж уже один раз не смог, вдруг опять не смогу :(
Залил! Выходит дело не в скетче и с 11 выхода и с приемника гонит 0 )))
Значит дело в самой ардуине
По объединению таймеров - может народ, что подскажет если не получится...
Идея достойная жеж
Залил! Выходит дело не в скетче и с 11 выхода и с приемника гонит 0 )))
Значит дело в самой ардуине
Фантастика какая-то, не может такого быть. Там и без источника импульса , если просто воткнуть проводок в 8 пин, то в сериал мониторе начинает мусор сыпаться. Импульс с 11 ноги кстати ваш осциллограф может и не схватить, там всего 120нС в ноле.
PS: Кстати, а залил что, хекс или скомпилил у себя? Если второе, то доверия меньше))
Залил! Выходит дело не в скетче и с 11 выхода и с приемника гонит 0 )))
Значит дело в самой ардуине
Фантастика какая-то, не может такого быть. Там и без источника импульса , если просто воткнуть проводок в 8 пин, то в сериал мониторе начинает мусор сыпаться. Импульс с 11 ноги кстати ваш осциллограф может и не схватить, там всего 120нС в ноле.
PS: Кстати, а залил что, хекс или скомпилил у себя? Если второе, то доверия меньше))
Залил твой HEX вот так - C:\Users\User\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino14/bin/avrdude -CC:\Users\User\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino14/etc/avrdude.conf -v -patmega328p -carduino -PCOM3 -b57600 -D -Uflash:w:C:\ARDUINO\hex\icp_asm.ino.hex:i
Да и скомпилил, тот же баг, ясно жеж что в ардуине какой то косяк...
Залил в другую ардуино, тот же эффект...
Чудеса всё чудесатее и чудесатее при том, что твой код, что был в том же разделе выше работает на ура
ua6em, а что за плата? У меня для Уно скомпилировано, соответссно с нановским бутлоадером может залиться криво. Лучше программатором влить.
ua6em, а что за плата? У меня для Уно скомпилировано, соответссно с нановским бутлоадером может залиться криво. Лучше программатором влить.
платы nano от robodyn... понял, надо загрузчик от UNO вшить и попробовать
ua6em, нашел косячёчек, компилятор не понимал что я что-то вывожу из функции (но в 1.6.8 -понимал! ) Как грится век живи -век учись ! Проверил, работает в 1.8.5
ua6em, нашел косячёчек, компилятор не понимал что я что-то вывожу из функции (но в 1.6.8 -понимал! ) Как грится век живи -век учись ! Проверил, работает в 1.8.5
А я уж постеснялся озвучивать, что и на UNO не пошло ни через компиляцию ни через загрузку HEX )))
Надеюсь, что исследование было полезным.
заработало, компилил в 1.8.7
Функция правда получается блокирующая, но не суть, интересно, как она отрабатывает с библиотекой servo.h
И сам отвечу, конфликтует! Не измеряет однако. Оно и не удивительно, используют же один таймер
Коллега Arhat109-2 обнадёжил, что asm-вставки нормально работают в прерывании, поэтому решил воплотить старую задумку -измерить как быстро после появления стороннего сигнала на входе сработает прерывание INT0/1 Подал сигнал от внешнего генератора на INT0, в прерывании дёргаю ногу PB4(Пин12). Опция ISR_NAKED отключает вмешательство компилятора, поэтому нога поднимается уже спустя 2 такта после входа прерывание. Посколько я не использовал рабочие регистры, то сохранять в стек ничего не нужно. Команда sbi не изменяет флагов, поэтому регистр SREG тоже нет нужды бэкапить. В общем прерывание реально пустое. Nop-ов добавил что б немного подзатянуть импульс.
И вот что получилось - На осциллограф выведено жёлтый луч -внешний сигнал пришедший на вход INT0 . Бирюзовый - сигнал снятый с 12 ноги.
Как видно по статистике дельта фронтов прыгает. В среднем от появления сигнала на входе до выполнения прерывания - около 700nS без учёта двух тактов. В реальной программе с сохранением регистров в стек добавится ещё куча команд. Счёт уже на микросекунды пойдёт.
И второй эксперимент - просто циклический опрос ноги.
Тут прога ждёт единицу, выдаёт импульс, затем снова ждёт ноль. Скриншот выкладывать не буду, он такой-же только цифры другие - min290, max480, avg -387. Однако в 2 раза быстрее реагирует, хотя ожидал цифр ещё меньше. Но блокирующая. Но зато никаких потенциальных накладных расходов на сохранение в стек. Думайте сами, решайте сами, иметь или не иметь :)
По первому коду: вроде всё согласно даташита, ну или я не увидел косяков. Цена деления 200нсек.
1. Вход в naked перывание: 2 пуша адреса возврата (по 2 такта - SRAM!) + выборка 2 слов из таблицы векторов прерываний + исполнение команды jump (там лежит именно она). Итого 7 тактов.
2. Нога поднимается гарантировано ещё через 2 такта, уже имеем 9 тактов между фронтами;
3. Схема реагирования на входной сигнал по даташиту в среднем 2 такта (для INTx и 4 такта для PCINT, как понял) - вот ни 11 тактов между фронтами в среднем. Там жеж и писано что +- 1 такт, смотря когда придет сигнал .. полностью подтверждается вышим средним от 10 до 12 тактов.
4. Ширина импульса - это 5 тактов нопов + 2 такта на команду (задержан результат как первой так и второй ОДИНАКОВО) .. 7 тактов. Хорошо согласуется с вашей осцилограммой.
.. разве что похоже частота в 16Мгц несколько "гуляет" и слегка понижена. :)
P.S. Когда давал рекомендацию использовать таймер для определения длительностей импульсов (и не в одной теме сразу!), именно это и имел ввиду (пройдено уже давно) и даже где-то приятно что столько человек бросилось сразу же разбираться с режимом захвата таймера.
Он не даром сделан, ибо точнее. :)
P.P.S. уточнил по даташиту на Мега2560 - выделил жирным. Собственно, отсутствие на типовой меге2560 ножек ICP для режима "захват таймера" и подвигло отрисовать свою версию. Там ещё и PCINT далеко не все и третий UART отсутствует.
Замер длительности импульса ещё очень удобен для датчика цвета TCS3200, лучше которого мне пока повстречать не довелось. Видеть цвет при общей освещенности в 6-10лк .. это лучше чем "кошка на страже". :)
ua6em, нашел косячёчек, компилятор не понимал что я что-то вывожу из функции (но в 1.6.8 -понимал! ) Как грится век живи -век учись ! Проверил, работает в 1.8.5
Здравствуйте. Подскажите пожалуйста, как из вашей функции вывести не только длительность логической '1', а еще и длительность логического '0', который следует за '1'? Или даже лучше длительность импульса в сумме ('1' + '0'). Потом мне нужно будет просуммировать например 20 импульсов ('1' + '0') и уже исходя из этого посчитать частоту. Если со вторым я справлюсь, то с первым прошу у вас помощи.
RuleZZZZ, пример из ссылки #12 не подошёл?
RuleZZZZ, пример из ссылки #12 не подошёл?
Попробовал его - в симуляторе "Proteus 7" частота 5000 Гц, подаваемая на вход со скважностью 50%, скачет от 4600 Гц до 5000 Гц примерно, скважность скачет от 46% до 58% соответственно. Не могу понять в чем косяк.
А помоему с ICP проще, и точнее, нежели с Асм вставками. Тут уже люди говорили по этому поводу. И измеряемая длительность не ограничивается 16-ю разрядами.
Green, в принципе это и есть ICP, только без прерывания я в #34 писал в чём суть.
PS: после погружения в мир таймеров stm32 всё это стало каменным веком ;-)
Да согласен, для измерения коротких импульсов прерывания не подходят.
Как там любят говорить некоторые - кал мамонта?)
Тут ещё и не с таким приходится работать.(
Green, в принципе это и есть ICP, только без прерывания я в #34 писал в чём суть.
Кстати, обработкку ICP можно сделать и без прерываний. Тогда все плюсы сохранятся.
Green, так я и сделал без прерываний. Но ICP умеет ловить только один фронт, что не позволяет измерить длительность импульса. Поэтому пришлось на асме дописать ожидание флага, быстро перепрограммировать таймер на ловлю другого фронта. Или есть какие то ещё варианты?
)) Да, всё верно, других вариантов не видно. Я не успел вчера посмотреть, извиняюсь. Подумал что это какая то софтовая реализация а-ля ICP.)
Кстати, обработкку ICP можно сделать и без прерываний. Тогда все плюсы сохранятся.
Это с TIFR1&(1<<ICF1) ?
sadman41, ага. Можно всё это же на СИ написать, но вряд ли так компактно скомпилируется.
Тут, как обычно, дилема в скорости или оформлении. Думаю, на Си тоже будет компактно.
Здравствуйте. Подскажите пожалуйста, как из вашей функции вывести не только длительность логической '1', а еще и длительность логического '0', который следует за '1'? Или даже лучше длительность импульса в сумме ('1' + '0'). Потом мне нужно будет просуммировать например 20 импульсов ('1' + '0') и уже исходя из этого посчитать частоту. Если со вторым я справлюсь, то с первым прошу у вас помощи.
Сдаюсь. В общем, со вторым я тоже не справился. :(
dimax, подскажите пожалуйста, как с помощью вашей ассемблерной вставки http://arduino.ru/forum/programmirovanie/schityvanie-impulsov#comment-218898 просуммировать например 20 первых импульсов для последующего расчета частоты (для уменьшения погрешности расчета) исходя уже из длины этих 20 импульсов, а не одного отдельно взятого импульса?
RuleZZZZ, ну и запускайте 20 раз функцию. Но это не имеет смысла, на низких частотах будет 20 раз одно и тоже число. А для измерения высоких этот алгоритм не подходит.
Частоты нужны от 3 кГц до 30 кГц примерно. Запускать функцию - это значит при появлении единицы на 12 порту запускать? И так 20 раз? Разве не будет задержки от считывания состояния пина?
RuleZZZZ, от 3кГц есть смысл использовать другой алгоритм -измерение кол-ва периодов за секунду, точность будет существенно выше. Есть стандартная библа для этого.
Если б все было так просто...
В том и дело, что мне не надо за секунду. А нужны всего-лишь 10-20 первых импульсов от начала их подачи, причем, что на 3 кГц, что на 30 кГц. При 3 кГц 10 имп. будут длиной около 3.3 мс, а при 30 кГц соответственно 330 мкс, то есть периоды в каждом случае будут разные и очень короткие относительно 1 секунды.
Если б все было так просто...
В том и дело, что мне не надо за секунду. А нужны всего-лишь 10-20 первых импульсов от начала их подачи, причем, что на 3 кГц, что на 30 кГц. При 3 кГц 10 имп. будут длиной около 3.3 мс, а при 30 кГц соответственно 330 мкс, то есть периоды в каждом случае будут разные и очень короткие относительно 1 секунды.
А может библиотека от ЕвгенийП TimeMeasure.h поможет отцу русской демократии )))
Спасибо за ссылку. Попробовал эту библиотеку - к сожалению, она измеряет только ширину импульса, когда на ноге '1' (либо я не разобрался), а мне нужно измерять общую ширину '1' + '0', чтобы потом рассчитать частоту.
Почитав еще несколько тем по прерываниям и таймерам, нарыл код от dimax, немного его допилил под свои нужны и получилось не то, что хотелось. Может кто наведет на путь истинный и подскажет в чем проблема и почему переменная 'alltic' вместо положенных просуммированных 64000 тиков за 20 периодов при 5 кГц (3200 тиков за 1 период * 20 периодов) выводит какое-то непонятное число 168341, после чего неправильно считает частоту сигнала? Если убрать из захвата прерываний переменные 'n' и 'alltic', и просто выводить переменную 'tic' как это было в первоначальном варианте, то все отлично выводится - 3200 (количество тиков за 1 период) и правильно считается частота - 5 кГц. Понимаю, что ошибка где-то в логике, но не могу понять, где именно.
RuleZZZZ, а зачем вам суммировать 20 отсчётов? Там не должно быть разброса значений при хорошем сигнале. По поводу вопроса - перед выполнением арифметических операций с volatile-переменными размерностью >8бит нужно запретить прерывания.
Сигнал будет с компаратора LM339. Суммированием хочу добиться лучшей точности, а также сдвига этих 20 периодов ближе-дальше по времени. То есть например считать не с первого импульса, а с 10-го или 20-го.
По поводу арифметических операций - то есть 'n' можно сделать 8бит и использовать внутри прерывания, а подсчет 'alltic' вывести в loop() с запретом прерываний в лупе во время этого счета?
RuleZZZZ, ну да. Можно ещё грамотнее -через макрос атомик блок. Я сам правда им не пользуюсь :)
Попробовал так сделать, но теперь переменная 'alltic' просто накапливает тики каждого периода и выводится на дисплей, то есть идет по нарастающей - 3200, потом 6400 и т.д. Частота при этом постоянная - '0'. В коде подсветил строки, которые поменял||добавил.
В общем, если кому интересно, то вроде бы удалось победить свое ТЗ. Напоминаю, что нужно узнать время в тиках, которое необходимо посчитать за N-нное количество периодов сигнала, чтобы в дальнейшем уже высчитать частоту этого сигнала. Причем сигнал может быть с разной скважностью. Также необходимо, чтобы была возможность сдвига чтения сигналов по времени, то есть чтобы можно было сделать выборку N-нного количества импульсов не с первого, а например с 10-го, или 20-го импульса. Частоты нужны примерно от 3 кГц до 30 кГц, если больше 30 кГц, то лишним не будет.
Так вот, надыбал еще один код в интернете, на основании которого сделал то, что мне нужно, и кажысь оно работает, по крайней мере в симуляторе Протеуса. УРА, товарищи! Выкладываю, вдруг кому будет еще интересно. И возможно кто-то сможет подсказать, какие "подводные" камни могут приключиться в реальной работе? Ведь в "ISR (TIMER1_CAPT_vect)" слишком много операций производится.
Вы в курсе куда пойдёт программа, если случайно выполнится 82 строка?
А оно ему ннада?
Вы в курсе куда пойдёт программа, если случайно выполнится 82 строка?
Неа, не в курсе, подскажите пожалуйста. А что не так со строкой этой? То, что мне надо, программа выполняет - при подаче импульсов выводит на экран частоту, при прекращении подачи импульсов оставляет на экране последнее значение частоты. При повторной подаче импульсов очищает экран и снова выводит нужную частоту.
Вы в курсе куда пойдёт программа, если случайно выполнится 82 строка?
Неа, не в курсе, подскажите пожалуйста.
Перейдет к строке 79. ;)) Тут все нормально. В lcd не нужно println писать, просто это бессмысленно, а так - не перживай, если и есть пара недочетов, то не критичных. Только с "n" ты ничего не делаешь, случайно закомментил?
Спасибо за комментарий. 'n' в 21 строке - забыл убрать оттуда, так как пытался сразу там сделать подсчет количества периодов. Но потом сделал в лупе, так как понял, что триггер "щелкает" после захвата фронта импульса и идет дальнейшее выполнение программы. Думаю этого мне и не хватало, я никак не мог прикрутить "флаг", при котором программа в основном цикле будет видеть, что пришел фронт импульса.