Два одновременно действующих счетчика на Uno R3 - возможно ли?
- Войдите на сайт для отправки комментариев
Втр, 24/04/2018 - 16:37
Здравствуйте всем! Прошу сильно не пинать (по крайней мере, сначала).
Есть задумка сделать два независимых и одновременно действующих счетчика импульсов (и далее тахометра), от двух датчиков (допустим, Холла).
Один из счетчиков должен определять период вращения в диапазоне 12...600 мс, а второй в диапазоне 8...600 мс, т.е. границы периодов примерно равны, но соотношения периодов в один и тот же период времени могут быть произвольными.
Возможно ли в принципе организовать одновременный счет импульсов по двум каналам на Uno R3?
Может быть я не увидел всей глубины вашего замысла, но непонятно в чем проблема, в каком месте вообще возникла трудность подсчета импульсов?
В том, как одновременно подсчитывать число миллисекунд между импульсами каждого датчика.
Пока цикл "ждет" очередной импульс, импульс второго датчика может "проскочить" незамеченным.
Пока цикл "ждет" очередной импульс, импульс второго датчика может "проскочить" незамеченным.
Думаю, циклу не нужно ждать импульса, но в цикле loop (без блокировки) постоянно смотеть, что пришел импульс, и первый и второй. А еще лучше повесить счетчики на прерывания, в Arduino UNO можно использовать как раз 2 внешних прерывания: на pin 2 и pin 3.
Я бы сделал так:
Я бы сделал так:
Спасибо, попробую. Но так будет считаться только количество срабатываний каждого датчика, но не интервал времени между срабатываниями "своих" датчиков?
Но так будет считаться только количество срабатываний каждого датчика, но не интервал времени между срабатываниями "своих" датчиков?
Ну, это вы уж сами смотрите, что именно вам надо считать. В том же обработчике можно запоминать время срабатывания, и при новом срабатывании вычислять интервал как разность. Только переменные изменяемые в обработчике не забывайте объявлять как volatile.
И еще вопрос: если оба сигнала пришли одновременно (или почти), не получится ли пропуска второго сигнала, пока будет обрабатываться прерывание по первому сигналу?
А какой дискрет по времени (или минимально возможная разность)?
И еще вопрос: если оба сигнала пришли одновременно (или почти), не получится ли пропуска второго сигнала, пока будет обрабатываться прерывание по первому сигналу?
Через прерывания нет — ничего не будет пропущено.
А какой дискрет по времени (или минимально возможная разность)?
Вы же сами указывали для своей задачи: "12...600 мс, а второй в диапазоне 8...600 мс"
Если нужно поймать еще меньший интервал используйте micros() вместо millis()
Если нужно поймать еще меньший интервал используйте micros() вместо millis()
Понял, спасибо! millis() было бы слишком грубо.
По мне так в loop опрашивать - плохая идея - можно прозевать. Если надо стопудово - протактируйте два таймера Вашими сигналами. А с третьего снимайте время. Точно ничего не прозеваете. И время будет с точностью до тика.
Третий таймер просто тикает и при переполнении инкрементирует счётчики перполнений (их два надо завести) А два основных таймера конфигурируем на посчет внешних мипульсов и каждый работает так.
1. Пришёл импульс (впервые) берём с третьего таймера значение счётчика и запоминаем, заодно обнуляем счётчик переполнений
2. Пришёл импульс (повторно) берём с третьего таймера значение счётчика. Из текущего и ранее запомненного значений счётчика (а также счётчика переполнений) считаем время между импульсами (за это и боролись!), запоминаем текущее значение счётчика третьего таймера и обнуляем счётчик переполнений.
П.2 повторяется до бесконечности.
Третий таймер инкрементирует счётчики переполнений, чтобы два первых могли правильно время посчитать.
По мне так в loop опрашивать - плохая идея - можно прозевать. Если надо стопудово - протактируйте два таймера Вашими сигналами. А с третьего снимайте время.
Спасибо, попробую и так.
Попробовал запрограммировать модуль RTC DS3231SN с памятью AT24C32.
Скачал библиотеки Wire, DS3231.
Открыл пример из DS3231 (начало):
#include <Wire.h>
#include "ds3231.h"
#define BUFF_MAX 128
uint8_t time[8];
char recv[BUFF_MAX];
unsigned int recv_size = 0;
unsigned long prev, interval = 5000;
void parse_cmd(char *cmd, int cmdsize);
void setup()
{
Serial.begin(9600);
Wire.begin();
DS3231_init(DS3231_INTCN);
memset(recv, 0, BUFF_MAX);
Serial.println("GET time");
}
И сразу же ошибка: #include <Wire.h> подсвечено красным (типа нет такой). Действительно, нет. А есть IWire.h и TwoWire.h
Как быть?
Искать библиотеку.
Ну, или плюнуть и забить на это дело.
Так под видом Wire скачиваются две: IWire.h и TwoWire.h
Модуль DS3231 имеет пины SCL и SDA (ну и VCC, GND). Так что вроде TwoWire.
Так ото ж.
Взял пример от DS1307RTC, все заработало и текущее время прописалось :)
Еще такой вопрос возник: как быстро Ардуино перехватывает прерывания?
Допустим, на цифровом пине появился уровень. Как быстро сработает прерывание? В течение последующих 4 микросекунд, или в тот же (или следующий) такт процессора?
А как вы будете определять момент этого "сработало".
Допустим, на цифровом пине появился уровень. Как быстро сработает прерывание? В течение последующих 4 микросекунд, или в тот же (или следующий) такт процессора?
4 такта.
Вы привыкайте даташит читать.
А как вы будете определять момент этого "сработало".
Например, нужно по сигналу цифрового входа считать значение с аналогового входа.
Важно, чтобы считывание AI произошло максимально быстро после DI (а не когда-нибудь после, т.к. сигнал быстроменяющийся). Вот и вопрос: как быстро можно считать AI после появления сигнала на DI?
Допустим, на цифровом пине появился уровень. Как быстро сработает прерывание? В течение последующих 4 микросекунд, или в тот же (или следующий) такт процессора?
4 такта.
Вы привыкайте даташит читать.
Спасибо!
Ну, там еще куча подготовительных операций перед входом в обработчик. И всё это, поди, завязано на настройки оптимизации компилятора. Плюс в обработчике начнется какая-нить инициализация пользовательских переменных, перекуры, чай... Так что "как быстро" - это вопрос даже несколько философский. Вот ЕвгенийП написал вам 4 такта. + такты на сохранение регистров + такты на неизвестный нам код, который вы напишете. Про какой момент вы пишете "сработало", определиться не можете. А на такой вопрос какой можно дать ответ?
В моем понимании, сработало - это момент физического появления ненулевого сигнала на пине DI.
Понятно, что на обнаружение этого факта затратится какое-то время (количество тактов), плюс на собственно считывание AI.
А мне вот кажется, что "сработало" - это когда достигнут результат, т.е. данные попали в память.
причём, чтение аналогового входа - операция сама по себе не очень быстрая.
А мне вот кажется, что "сработало" - это когда достигнут результат, т.е. данные попали в память.
В данной конктретной задаче важно именно, чтобы AI был считан максимально быстро после физического появления ненулевого сигнала на DI.
Т.е., DI является командой "немедленно считать AI". При этом допустимо, что в память результат считывания попадет гораздо позже.
В моем понимании, сработало - это момент физического появления ненулевого сигнала на пине DI.
Странные у Вас понимания.
В общем между фронтом сигнала и началом исполнения первой команды обработчкиа прерывания - 4 такта. А дельшо "крутитесь как хотите". Только я не понял нафига это Вам?
Не проще запустить ADC во free-running режиме. Запустил и забыл. А в нужной переменной будет появляться свежий результат измерений каждые 104µS (именно столько надо, чтобы измерить аналоговый сигнал с 10-битным разрешением при тактовой частоте 16МГц).
Быстрее у Вас всё равно не получится ни с каким прерыванием. Если нужно быстрее - надо жертвовать точностью измерений - брать не 10 бит, а меньше. Можно ли это делать? ХЗ! Вашу задачу никто, кроме Вас не знает.
E_Krendel, тут есть нюансы. Если прерывание вызывать ардуиновской функцией attachInterrupts, то сгенерится некоторое кол-во лишнего кода. Если прерывание вызывать прямым программированием регистров, то скорость вызова прерывания может вырости раза в 2. Но есть ещё нюанс - при входе в прерывание идёт сохранение регистров стека. Если их сохранять не нужно, например в прерывание требуется только послать что-то в порт) можно вызвать прерывание с аргументом ISR_NAKED, что ускорит ещё раза в 2. Со всеми "разгонами" мне не удавалась выполнить команду в преываниии быстрее, чем спустя 1µS от момент появления сигнала. Через attachInterrupts это будет порядка 4µS, так что эту цифру вы взяли явно не с потолка, могу подтвердить что именно столько и удёт.
104µS это слишком (в разы) много. Получается, Ардуино UNO для этой конкретной задачи не годится.
E_Krendel, тут есть нюансы. ... порядка 4µS, так что эту цифру вы взяли явно не с потолка, могу подтвердить что именно столько и удёт.
Спасибо!
104µS это слишком (в разы) много. Получается, Ардуино UNO для этой конкретной задачи не годится.
написали же, можно пожертвовать точностью преобразования, тогда скорость будет выше
И так всего 1023 уровня, да еще если сигнал в половину шкалы, да если разрядность уменьшить хотя бы вчетверо, вообще ничего не останется :)
104µS это слишком (в разы) много. Получается, Ардуино UNO для этой конкретной задачи не годится.
Если нужна точность 10 бит, то быстрее чем за 104 микросекунды - никак. А если можно точностью пожертвовать, то можно и быстрее.
Кстати, про эти 104µS написано в даташите. Сегодня я уже рекомендовал Вам привыкать его читать. Похоже, рекоммендация прошла мимо.
Сегодня я уже рекомендовал Вам привыкать его читать. Похоже, рекоммендация прошла мимо.
Я пока на работе, дома почитаю.
Плохо, када на работе читать запрещают. Можно читать украдкой, под столом. Можно даже Марсельезу при этом петь.
Плохо, када на работе читать запрещают.
забавно, что лошить в форумах можно, а читать документацию - нет. :)
104µS это слишком (в разы) много. Получается, Ардуино UNO для этой конкретной задачи не годится.
Есть ещё такое решение: УВХ. Устройство выборки - хранения. По внешнему сигналу запоминает измеряемое напряжение. А его потом можно преобразовывать в цифру хоть до второго пришествия. Также можно применить внешний АЦП. Пусть он постоянно осуществляет преобразование. При необходимости забирать данные с него. В некоторых АЦП есть даже регистр - защелка. По одному и тому же сигналу вызывать прерывание и защелкивание выходных данных с АЦП. Тогда время между прерванием и измерением можно сократить до минимума.
Есть ещё такое решение: УВХ. Устройство выборки - хранения. По внешнему сигналу запоминает измеряемое напряжение. А его потом можно преобразовывать в цифру хоть до второго пришествия. Также можно применить внешний АЦП. Пусть он постоянно осуществляет преобразование. При необходимости забирать данные с него. В некоторых АЦП есть даже регистр - защелка. По одному и тому же сигналу вызывать прерывание и защелкивание выходных данных с АЦП. Тогда время между прерванием и измерением можно сократить до минимума.
Спасибо, насчет внешнего АЦП думал уже. УВХ поизучать надо, тоже интересно.