Два одновременно действующих счетчика на Uno R3 - возможно ли?

E_Krendel
Offline
Зарегистрирован: 24.05.2017

Здравствуйте всем! Прошу сильно не пинать (по крайней мере, сначала).

Есть задумка сделать два независимых и одновременно действующих счетчика импульсов (и далее тахометра), от двух датчиков (допустим, Холла).

Один из счетчиков должен определять период вращения в диапазоне 12...600 мс, а второй в диапазоне 8...600 мс, т.е. границы периодов примерно равны, но соотношения периодов в один и тот же период времени могут быть произвольными.

Возможно ли в принципе организовать одновременный счет импульсов по двум каналам на Uno R3?

DmitryN
Offline
Зарегистрирован: 23.04.2018

Может быть я не увидел всей глубины вашего замысла, но непонятно в чем проблема, в каком месте вообще возникла трудность подсчета импульсов?

E_Krendel
Offline
Зарегистрирован: 24.05.2017

В том, как одновременно подсчитывать число миллисекунд между импульсами каждого датчика.

Пока цикл "ждет" очередной импульс, импульс второго датчика может "проскочить" незамеченным.

DmitryN
Offline
Зарегистрирован: 23.04.2018

E_Krendel пишет:

Пока цикл "ждет" очередной импульс, импульс второго датчика может "проскочить" незамеченным.

Думаю, циклу не нужно ждать импульса, но в цикле loop (без блокировки) постоянно смотеть, что пришел импульс, и первый и второй. А еще лучше повесить счетчики на прерывания, в Arduino UNO можно использовать как раз 2 внешних прерывания: на pin 2 и pin 3.

Я бы сделал так:

volatile unsigned long count1 = 0;
volatile unsigned long count2 = 0;

void setup()
{
  pinMode(2, INPUT); // Interrupt 0
  pinMode(3, INPUT); // Interrupt 1
  attachInterrupt(0, sensor1, RISING); // срабатывать на повышение
  attachInterrupt(1, sensor2, RISING);
 
  
}

void loop() {
  // что-то делать со счетчиками count1, count2

}

void sensor1() {
   count1++;
}

void sensor2() {
   count2++;
}

 

 

E_Krendel
Offline
Зарегистрирован: 24.05.2017

DmitryN пишет:

Я бы сделал так:

Спасибо, попробую. Но так будет считаться только количество срабатываний каждого датчика, но не интервал времени между срабатываниями "своих" датчиков?

DmitryN
Offline
Зарегистрирован: 23.04.2018

E_Krendel пишет:

Но так будет считаться только количество срабатываний каждого датчика, но не интервал времени между срабатываниями "своих" датчиков?

Ну, это вы уж сами смотрите, что именно вам надо считать. В том же обработчике можно запоминать время срабатывания, и при новом срабатывании вычислять интервал как разность. Только переменные изменяемые в обработчике не забывайте объявлять как volatile.

E_Krendel
Offline
Зарегистрирован: 24.05.2017

И еще вопрос: если оба сигнала пришли одновременно (или почти), не получится ли пропуска второго сигнала, пока будет обрабатываться прерывание по первому сигналу?

E_Krendel
Offline
Зарегистрирован: 24.05.2017

DmitryN пишет:
[можно запоминать время срабатывания, и при новом срабатывании вычислять интервал как разность

А какой дискрет по времени (или минимально возможная разность)?

DmitryN
Offline
Зарегистрирован: 23.04.2018

E_Krendel пишет:

И еще вопрос: если оба сигнала пришли одновременно (или почти), не получится ли пропуска второго сигнала, пока будет обрабатываться прерывание по первому сигналу?

Через прерывания нет —  ничего не будет пропущено.

DmitryN
Offline
Зарегистрирован: 23.04.2018

E_Krendel пишет:

А какой дискрет по времени (или минимально возможная разность)?

Вы же сами указывали для своей задачи: "12...600 мс, а второй в диапазоне 8...600 мс"

Если нужно поймать еще меньший интервал используйте micros() вместо millis()

 

E_Krendel
Offline
Зарегистрирован: 24.05.2017

DmitryN пишет:

Если нужно поймать еще меньший интервал используйте micros() вместо millis()

Понял, спасибо! millis() было бы слишком грубо.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

По мне так в loop опрашивать - плохая идея - можно прозевать. Если надо стопудово - протактируйте два таймера Вашими сигналами. А с третьего снимайте время. Точно ничего не прозеваете. И время будет с точностью до тика.

Третий таймер просто тикает и при переполнении инкрементирует счётчики перполнений (их два надо завести) А два основных таймера конфигурируем на посчет внешних мипульсов и каждый работает так.

1. Пришёл импульс (впервые) берём с третьего таймера значение счётчика и запоминаем, заодно обнуляем счётчик переполнений
2. Пришёл импульс (повторно) берём с третьего таймера значение счётчика. Из текущего и ранее запомненного значений счётчика (а также счётчика переполнений) считаем время между импульсами (за это и боролись!), запоминаем текущее значение счётчика третьего таймера и обнуляем счётчик переполнений.

П.2 повторяется до бесконечности.

Третий таймер инкрементирует счётчики переполнений, чтобы два первых могли правильно время посчитать.

E_Krendel
Offline
Зарегистрирован: 24.05.2017

ЕвгенийП пишет:

По мне так в loop опрашивать - плохая идея - можно прозевать. Если надо стопудово - протактируйте два таймера Вашими сигналами. А с третьего снимайте время.

Спасибо, попробую и так.

E_Krendel
Offline
Зарегистрирован: 24.05.2017

Попробовал запрограммировать модуль 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

Как быть?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Искать библиотеку.

Ну, или плюнуть и забить на это дело.

E_Krendel
Offline
Зарегистрирован: 24.05.2017

Так под видом Wire скачиваются две: IWire.h и TwoWire.h

Модуль DS3231 имеет пины SCL и SDA (ну и VCC, GND). Так что вроде TwoWire.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Так ото ж.

E_Krendel
Offline
Зарегистрирован: 24.05.2017

Взял пример от DS1307RTC, все заработало и текущее время прописалось :)

E_Krendel
Offline
Зарегистрирован: 24.05.2017

Еще такой вопрос возник: как быстро Ардуино перехватывает прерывания?

Допустим, на цифровом пине появился уровень. Как быстро сработает прерывание? В течение последующих 4 микросекунд, или в тот же (или следующий) такт процессора?

sadman41
Offline
Зарегистрирован: 19.10.2016

А как вы будете определять момент этого "сработало".

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

E_Krendel пишет:

Допустим, на цифровом пине появился уровень. Как быстро сработает прерывание? В течение последующих 4 микросекунд, или в тот же (или следующий) такт процессора?

4 такта.

Вы привыкайте даташит читать.

E_Krendel
Offline
Зарегистрирован: 24.05.2017

sadman41 пишет:

А как вы будете определять момент этого "сработало".

Например, нужно по сигналу цифрового входа считать значение с аналогового входа.

Важно, чтобы считывание AI произошло максимально быстро после DI (а не когда-нибудь после, т.к. сигнал быстроменяющийся). Вот и вопрос: как быстро можно считать AI после появления сигнала на DI?

E_Krendel
Offline
Зарегистрирован: 24.05.2017

ЕвгенийП пишет:

E_Krendel пишет:

Допустим, на цифровом пине появился уровень. Как быстро сработает прерывание? В течение последующих 4 микросекунд, или в тот же (или следующий) такт процессора?

4 такта.

Вы привыкайте даташит читать.

Спасибо!

sadman41
Offline
Зарегистрирован: 19.10.2016

Ну, там еще куча подготовительных операций перед входом в обработчик. И всё это, поди, завязано на настройки оптимизации компилятора. Плюс в обработчике начнется какая-нить инициализация пользовательских переменных, перекуры, чай... Так что "как быстро" - это вопрос даже несколько философский. Вот ЕвгенийП написал вам 4 такта. + такты на сохранение регистров + такты на неизвестный нам код, который вы напишете. Про какой момент вы пишете "сработало", определиться не можете. А на такой вопрос какой можно дать ответ?

E_Krendel
Offline
Зарегистрирован: 24.05.2017

sadman41 пишет:
Про какой момент вы пишете "сработало", определиться не можете.

В моем понимании, сработало - это момент физического появления ненулевого сигнала на пине DI.

Понятно, что на обнаружение этого факта затратится какое-то время (количество тактов), плюс на собственно считывание AI.

sadman41
Offline
Зарегистрирован: 19.10.2016

А мне вот кажется, что "сработало" - это когда достигнут результат, т.е. данные попали в память.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

причём, чтение аналогового входа - операция сама по себе не очень быстрая.  

E_Krendel
Offline
Зарегистрирован: 24.05.2017

sadman41 пишет:

А мне вот кажется, что "сработало" - это когда достигнут результат, т.е. данные попали в память.

В данной конктретной задаче важно именно, чтобы AI был считан максимально быстро после физического появления ненулевого сигнала на DI.

Т.е., DI является командой "немедленно считать AI". При этом допустимо, что в память результат считывания попадет гораздо позже.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

E_Krendel пишет:

В моем понимании, сработало - это момент физического появления ненулевого сигнала на пине DI.

Странные у Вас понимания.

В общем между фронтом сигнала и началом исполнения первой команды обработчкиа прерывания - 4 такта. А дельшо "крутитесь как хотите". Только я не понял нафига это Вам?

Не проще запустить ADC во free-running режиме. Запустил и забыл. А в нужной переменной будет появляться свежий результат измерений каждые 104µS (именно столько надо, чтобы измерить аналоговый сигнал с 10-битным разрешением при тактовой частоте 16МГц).

Быстрее у Вас всё равно не получится ни с каким прерыванием. Если нужно быстрее - надо жертвовать точностью измерений - брать не 10 бит, а меньше. Можно ли это делать? ХЗ! Вашу задачу никто, кроме Вас не знает.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

E_Krendel, тут есть нюансы.  Если прерывание вызывать ардуиновской функцией attachInterrupts, то сгенерится  некоторое кол-во лишнего кода. Если прерывание вызывать прямым программированием регистров, то скорость вызова прерывания может вырости раза в 2. Но есть ещё нюанс - при входе в прерывание идёт сохранение регистров стека. Если их сохранять не нужно,  например в прерывание требуется только послать что-то в порт) можно вызвать прерывание с аргументом ISR_NAKED,  что ускорит ещё раза в 2.  Со всеми "разгонами"  мне не удавалась выполнить команду в преываниии быстрее, чем спустя 1µS от момент появления сигнала. Через attachInterrupts это будет порядка 4µS, так что эту цифру вы взяли явно не с потолка, могу подтвердить что именно столько и удёт.

E_Krendel
Offline
Зарегистрирован: 24.05.2017

ЕвгенийП пишет:
Не проще запустить ADC во free-running режиме. Запустил и забыл. А в нужной переменной будет появляться свежий результат измерений каждые 104µS (именно столько надо, чтобы измерить аналоговый сигнал с 10-битным разрешением при тактовой частоте 16МГц).

104µS это слишком (в разы) много. Получается, Ардуино UNO для этой конкретной задачи не годится.

E_Krendel
Offline
Зарегистрирован: 24.05.2017

dimax пишет:

E_Krendel, тут есть нюансы.  ... порядка 4µS, так что эту цифру вы взяли явно не с потолка, могу подтвердить что именно столько и удёт.

Спасибо!

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

E_Krendel пишет:

104µS это слишком (в разы) много. Получается, Ардуино UNO для этой конкретной задачи не годится.

написали же, можно пожертвовать точностью преобразования, тогда скорость будет выше

E_Krendel
Offline
Зарегистрирован: 24.05.2017

DetSimen пишет:
написали же, можно пожертвовать точностью преобразования, тогда скорость будет выше

И так всего 1023 уровня, да еще если сигнал в половину шкалы, да если разрядность уменьшить хотя бы вчетверо, вообще ничего не останется :)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

E_Krendel пишет:

104µS это слишком (в разы) много. Получается, Ардуино UNO для этой конкретной задачи не годится.

Если нужна точность 10 бит, то быстрее чем за 104 микросекунды - никак. А если можно точностью пожертвовать, то можно и быстрее.

Кстати, про эти 104µS написано в даташите. Сегодня я уже рекомендовал Вам привыкать его читать. Похоже, рекоммендация прошла мимо.

E_Krendel
Offline
Зарегистрирован: 24.05.2017

ЕвгенийП пишет:

Сегодня я уже рекомендовал Вам привыкать его читать. Похоже, рекоммендация прошла мимо.

Я пока на работе, дома почитаю.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Плохо, када на работе читать запрещают.  Можно читать украдкой, под столом.  Можно даже Марсельезу при этом петь. 

b707
Offline
Зарегистрирован: 26.05.2017

DetSimen пишет:

Плохо, када на работе читать запрещают. 

забавно, что лошить в форумах можно, а читать документацию - нет. :)

SLKH
Offline
Зарегистрирован: 17.08.2015

E_Krendel пишет:

ЕвгенийП пишет:
Не проще запустить ADC во free-running режиме. Запустил и забыл. А в нужной переменной будет появляться свежий результат измерений каждые 104µS (именно столько надо, чтобы измерить аналоговый сигнал с 10-битным разрешением при тактовой частоте 16МГц).

104µS это слишком (в разы) много. Получается, Ардуино UNO для этой конкретной задачи не годится.

т.к конкретной задачи нет - ничего не годится. или же годится всё.

VasiliyV
Offline
Зарегистрирован: 09.07.2018

Есть ещё такое решение: УВХ. Устройство выборки - хранения. По внешнему сигналу запоминает измеряемое напряжение. А его потом можно преобразовывать в цифру хоть до второго пришествия. Также можно применить внешний АЦП. Пусть он постоянно осуществляет преобразование. При необходимости забирать данные с него. В некоторых АЦП есть даже регистр - защелка. По одному и тому же сигналу вызывать прерывание и защелкивание выходных данных с АЦП. Тогда время между прерванием и измерением можно сократить до минимума.

E_Krendel
Offline
Зарегистрирован: 24.05.2017

VasiliyV пишет:

Есть ещё такое решение: УВХ. Устройство выборки - хранения. По внешнему сигналу запоминает измеряемое напряжение. А его потом можно преобразовывать в цифру хоть до второго пришествия. Также можно применить внешний АЦП. Пусть он постоянно осуществляет преобразование. При необходимости забирать данные с него. В некоторых АЦП есть даже регистр - защелка. По одному и тому же сигналу вызывать прерывание и защелкивание выходных данных с АЦП. Тогда время между прерванием и измерением можно сократить до минимума.

Спасибо, насчет внешнего АЦП думал уже. УВХ поизучать надо, тоже интересно.