pulseIn (вопрос новичка)

arduproger
Offline
Зарегистрирован: 23.06.2017

Всем привет.

Прошу помощи с сабжевой функцией.

 

Преамбула (возможно, можно обойтись из без pulseIn):

Есть SANWA пульт-передатчик (RDS-8000 2.4GHz FHSS) и приемник (RX-841fs) (для авиамоделей). Нужно на ардуино (UNO) принимать сигнал (приемник, соответственно, подключен к ардуино и по питанию и по сигналам) по 4 каналам (газ, тангаж, крен и рысканье, и, соответственно, от приемника идут проводки на 4 цифровых пина на ардуино). Для получения сигнала используется pulseIn (почему - не знаю, пока моих познаний в электронике/электротехнике явно не хватает; digitalRead - не работает).

 

Амбула.

Сначала для теста был подключен 1 канал (тангаж) к пину 3 и все было хорошо (шевелишь рукоять тангажа - значения меняются; также все было хорошо с любым другим отдельным каналом):

void setup()
 {
  Serial.begin(115200);
  pinMode(3, INPUT);
  Serial.println("Begin...");
 }

void loop()
 {
  unsigned long p3 = pulseIn(3, HIGH, 20000);
  if( p3 != 0)
  {
    Serial.print("p3: ");
    Serial.println(p3);
  }
 }

Проблема началась с подключением второго канала (крен, к пину 4):

void setup()
 {
  Serial.begin(115200);
  pinMode(3, INPUT);
  pinMode(4, INPUT);
  Serial.println("Begin...");
 }

void loop()
 {
  unsigned long p3 = pulseIn(3, HIGH, 20000);
  if( p3 != 0)
  {
    Serial.print("p3: ");
    Serial.println(p3);
  }

  unsigned long p4 = pulseIn(4, HIGH, 20000);
  if( p4 != 0)
  {
    Serial.print("p4: ");
    Serial.println(p4);
  }
 }

// цикл пока не стал делать сознательно

- нет реакции на один из каналов (пишутся нули), причем канал, который не "отсекается", выбирается случайно при включении (или в какой-то другой момент), т.е.:

0) залили прогу в ардуино;

1) выключили ардуино, включили ардуино, включили передатчик - тангаж показывает, крен - нет;

2) выключили ардуино, включили ардуино, включили передатчик - теперь крен показывает, тангаж - нет;

3) выключили ардуино, включили ардуино, включили передатчик - снова крен показывает, тангаж - нет;

4) выключили ардуино, включили ардуино, включили передатчик - опять тангаж показывает, крен - нет;

и так далее...

 

На всякий случай добавлю еще момент: решил отсекать все 4 канала (в цикле):


const int  pin[] = { 3, 4, 5, 6 };    // пины, к которым подключены каналы
const int  pins = sizeof(pin) / sizeof(pin[0]);
unsigned long val[pins];

void setup()
 {
  Serial.begin(115200);

  for( int i=0; i < pins; ++i )
    pinMode(pin[i], INPUT);

  Serial.println("Begin...");
 }

void loop()
 {
  for( int i=0; i < pins; ++i )
  {
    val[i] = pulseIn(pin[i], HIGH, 20000);
    Serial.print(" p"); Serial.print(pin[i]); Serial.print(":\t"); Serial.print(val[i]); Serial.print("\t|");
  }
 
  Serial.println("");
}

не отсекались два канала.

Но когда же я добавил пятый ("пустой") пин (7й) в обработку  - все заработало! (все каналы показывали значения рукоятей на пульте)

const int  pin[] = { 3, 4, 5, 6, 7 };
                             //  ^ - добавленный "пустой" пин, к нему ничего не подключено, но он участвует в обработке якобы поступающих на него данных наравне со всеми

==========

Что это за хитрые нюансы pulseIn?

Что я в принципе делаю не так, и как надо правильно?

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Ну, чтобы поставить диагноз, яено недостаточно информации (возможно, ее бы и хватимло при наличии дэйташитов на RDS-8000 2.4GHz FHSS и RX-841fs). 

По поводу преамбулы. 

У вас есть 4 непрерывно меняющиеся величины. Вы хотите получать их значения по 4-м пинам. Но пины - цифровые. У них бывают значения, грубно говоря, "включено" и "выключено". Других состояний нет. Чтобы передать что-то более "непрерывное", нужна развертка во времени: либо последовательный цифровой код, либо импульс изменяемой длительности. Собственно pulseIn именно это и измеряет. Поэтому digitalRead и не подходит.

Но у pulseIn есть особенность - это блокирующая функция. Т.е. как мы запросили pulseIn, то контроллер останавливается. Либо до получения нужного значения, либо до наступленя таймаута. Если в это время приходят какие-то сигналы по другим линиям, контроллер их просто не замечает. И избавиться от этих потерь, используя pulseIn, невозможно.

Как работает передатчик (RDS-8000 или что у Вас там), я не знаю. Но, судя по тому результату, что Вы описали, он, скорее всего, передает в цикле по очереди все 4 величины.

Теперь по вариантам Вашей программы:

1. Вы опрашиваете единственный пин - информация по всем стальным теряется, но от этого - доходит 100%.

2. Вы опрашиваете 2 или 4 датчика. Важно то, что это кратные числа. Соответственно, в зависимости от того, с каким периодом передает передатчик и величины установленного таймаута Вы какие-то сигналы получатете, а какие-то - не успеваете. При этом "фаза" задается моментом включения. Т.е. для одних датчиков потери составляют 100%, а для других - неизвестно, но вероятно, что тоже часть теряется.

3. Вы опрашиваете количество датчиков некратное количеству отправляемых сигналов (5 штук при 4 сигналах). В результате у Вас "фаза" постоянно сдвигается и очередность межу опрашиваемыми и неопрашиваемыми датчивками - тоже. Т.е. в этом случсе Вы заведомо теряете значительную часть сигналов от каждого из датчиков, но с какой-то периодичностью (например, 1 сигнал из 5) Вы получаете от каждого из них.

Вот как-то так.

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

По совершенствованию программы: с pulseIn у Вас неизбежно будут потери информации. Притом, вероятноо всего, они будут составлять существенно более 50%. Если требуется отлавливать все или почти все изменения, нужно коренным образом менять логику программы - переделывать ее на прерывания.

arduproger
Offline
Зарегистрирован: 23.06.2017

andriano пишет:

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

 

Да, при увеличении таймаута до 40000 все стало ок. Спасибо за подсказку!

 

Цитата:

По совершенствованию программы: с pulseIn у Вас неизбежно будут потери информации. Притом, вероятноо всего, они будут составлять существенно более 50%. Если требуется отлавливать все или почти все изменения, нужно коренным образом менять логику программы - переделывать ее на прерывания.

Подскажите, пожалуйста, направление: как переделать программу на прерывания в моем данном конкретном случае (есть небольшой опыт работы с прерываниями на ассемблере под x86).

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

1. Насчет "ОК", думаю, Вы поторопились - наверняка ведь не пытались оценить процент потерь, как я посоветовал.

2. Переделать Вашу программу невозможнео (ну либо - нерационально) - нужно писать с нуля.

arduproger
Offline
Зарегистрирован: 23.06.2017

В любом случае буду благодарен за направление (ссылку): как в данном случае можно использовать прерывания (и/или не использовать pulseIn).

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

arduproger, была аналогичная тема.

arduproger
Offline
Зарегистрирован: 23.06.2017

dimax, спасибо.

  Но хочется сабж "добить" (т.е. понять до конца работу этой функции). Вышеупомянутый приемник я стал тестить осциллографом и выяснилось, что приемник выдает по каждому каналу сигналы (значения рычажков управления) раз в 24-25мс (миллисекунд). Тем не менее pulseIn не хочет нормально выдавать значения с заданным параметром timeout менее 34мс (34000 мкс) - и это если в цикле опрашивать только один канал. Если количество опрашиваемых в цикле каналов увеличивается, то приходится увеличивать и timeout - для 4х каналов не менее 40000 (т.е., например, при 39000 уже будут периодически, т.е. не всегда, но будут, выпадать нули). А всего там каналов 8 (восемь) (но последние четыре меня не интересуют).

  Где зарыта собака?
  Почему pulseIn не выдает сигналы в период (таймаут, окно), скажем, 27мс, когда осциллограф (новый и проверенный) выдает период 24-25мс?
  Почему приходится увеличивать таймаут при опросе нескольких каналов (ведь если мы не ловим сразу следующий сигнал, скажем, четвертого канала, то периода в, скажем, 30мс все равно должно хватить за глаза для ловли последующего сигнала того же четвертого канала)?
 

  Доп.инфа (вдруг окажется важно): тот же осциллограф показал, что по времени сигналы в каналах идут друг за другом, т.е. сначала идет сигнал по первому каналу, затем почти сразу (примерно через 5-10 микросекунд) после него по второму каналу, и так далее все 8 каналов. 'Синхропауза' меняется в зависимости от общей длительности всех сигналов каналов так, чтобы общая длина 'посылки' была где-то 24мс (как если бы это был PPM/PWM). Т.е. создается впечатление, что посылкой сигналов в каналы занимается какой-то один 'объект' последовательно.

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

arduproger, что вы хотите услышать сейчас? Без полной картины сигнала, снятой логическим анализатором хотя бы за 1 секунду, тут можно только пальцем в небо тыкать. При любом раскладе pulseIn вам не продходит, о чём вам ещё в первом ответе andriano сообщил. 

arduproger
Offline
Зарегистрирован: 23.06.2017

За секунду снять не вопрос (зачем только лог.анализатор - современный осциллограф вполне справится) - только что это даст?

Судя по всему про PPM и pulseIn спрашивать уже бессмысленно?

nik182
Offline
Зарегистрирован: 04.05.2015

Посмотрите на код pulseIn. Сначала ждет окончания предыдущего. Потом ждёт начало текущего. Потом измеряет текущий. И если таймаут непревышен - дает ответ. Вприципе можно переписать под себя. Можно просто правильно расставить последовательность обработки импульсов - через один и таймаут определять как 25000L. Нули игнорировать, потому что период одного канала 25мс, и если его не поймал, то 8 какналов уже дают 8*25 - не менее 200мс. У меня на тиньке25 на восьмиканальной аппаратуре два канала отрабатываюся без проблем. Проверку на ноль поставить только пришлось. 

unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout)
{
	// cache the port and bit of the pin in order to speed up the
	// pulse width measuring loop and achieve finer resolution.  calling
	// digitalRead() instead yields much coarser resolution.
	uint8_t bit = digitalPinToBitMask(pin);
	uint8_t port = digitalPinToPort(pin);
	uint8_t stateMask = (state ? bit : 0);

	unsigned long startMicros = micros();

	// wait for any previous pulse to end
	while ((*portInputRegister(port) & bit) == stateMask) {
		if (micros() - startMicros > timeout)
			return 0;
	}

	// wait for the pulse to start
	while ((*portInputRegister(port) & bit) != stateMask) {
		if (micros() - startMicros > timeout)
			return 0;
	}

	unsigned long start = micros();
	// wait for the pulse to stop
	while ((*portInputRegister(port) & bit) == stateMask) {
		if (micros() - startMicros > timeout)
			return 0;
	}
	return micros() - start;
}