Написание своей функции pulseIn()

BrightSide
Offline
Зарегистрирован: 18.06.2013

Считываю данные с ультразвукового дальномера HC-SR04 с помощью pulseIn(), но при большом значении расстояния сигнал получается довольно длинный (каждые 58 микросекунд означают 1 см, а датчиков я использую 5 шт и накапливаются значительные задержки), и я решил написать свою версию этой функции, чтобы она считывала длину импульса не более 58 миллисекунд. Получилось следующее:

long mPulseIn(int pin, int state) {
  unsigned long pulse=0;
  for(long i=0; i<=1000000; i+=5) if(digitalRead(pin)==state) {break;} else delayMicroseconds(5);
  for(long i=0; i<=58000; i+=5) if(digitalRead(pin)!=state) {pulse=i; break;} else delayMicroseconds(5);
  return pulse;
}

Но работает это некорректно. Чаще всего функция выводит значение примерно в два раза меньше, чем штатный pulseIn(), а при малых расстояниях вообще что-то не относящееся к делу.

В чем я ошибся, возможно ли вообще написание этой функции таким образом и можно ли реализовать то, что мне нужно, другим способом? Заранее спасибо.

maksim
Offline
Зарегистрирован: 12.02.2012

Ваша ошибка в том что вы не внимательно читаете описание функций прежде чем изобретать велосипед.  pulseIn()

pulseIn(pin, HIGH, 85000);

 

BrightSide
Offline
Зарегистрирован: 18.06.2013

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

sds
Offline
Зарегистрирован: 29.10.2012

Без таймера измерять задержки сложно. Я отказался от этой затеи.

Ну и сам puleIn Довольно хитро реализован. Рекомендую посмотреть его исходники

 

BrightSide пишет:

long mPulseIn(int pin, int state)

 

Я делал повтор работы с дальномером и pulseIn следующим образом. Структурно, код такой:

1. Ждем, пока на ECHO пине дальномера не появится LOW (значит ничем он сейчас не занят и можно юзать)

2. TRIG рин в LOW, 2 мкс задержки, TRIG рин в HIGH, 10 мкс задержки, TRIG рин в LOW (это повор необходимого для работы дальномера)

3. Ждем пока на ECHO пине не появится HIGH (450 микросек примерно)

4. Запускаем таймер для отсчета микросекунд

У себя настраивал так:

 

	TCCR1B = (1<<CS10) | (1<<CS11); // CLK/64 (ATmega328p)
	// 16.000.000 / 64 = 250.000 раз в секунду будет 
	// срабатывать (обновляться счетчик)
	// т.е. каждые 4 микросекунды (считаем, что такой точности достаточно)
	// Timer1 16-ти разрядный, поэтому до переполнения может досчитать до 65.535
	// т.е. при таких настройках до переполнения можно замерять длительности до
	// 262-х милисекунд (65.535 / 250.000)	
						   
	// Соответственно, в TCNT1 будет храниться каждая 4-я микросекунда и для получения
	// Прошедших микросекунд содержимое регистра надо умножить на 4

5. Ждем LOW на ECHO пине + отслеживаем таймаут

while (getBit(T_ECHO_PINREG, T_ECHO_PIN) && (mTimer_getMicros() <= timeOut)) {};

6. Остановливаем таймер и смотрим длину импульса. Или считаем, что измерение не прошло, если длина больше или равна таймауту

 

Вам, собственно, надо сделать неблокирующие циклы, которые есть в PulseIn

 

maksim
Offline
Зарегистрирован: 12.02.2012

BrightSide пишет:

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

И ожидает и "ограничивает" - если импульс превышает заданный таймаут возвращает тоже 0.

BrightSide
Offline
Зарегистрирован: 18.06.2013

sds, спасибо, если совсем не будет получаться, буду разбираться более подробно. maksim, тоже спасибо, не знал, об ограничении самого импульса нигде, вроде, не писали (или я невнимательный).

maksim
Offline
Зарегистрирован: 12.02.2012

Кстати, в самой функции вы можете изменить время ожидания импульса и время длительности импульса. 

в файле arduino-1.Х\hardware\arduino\cores\arduino\Arduino.h замените строки:

unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout);

на

unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout, unsigned long timepulse);

и
 

unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L);

на

unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L, unsigned long timepulse = 0);

и в файле arduino-1.Х\hardware\arduino\cores\arduino\wiring_pulse.c замените его содержимое на это:

/*
  wiring_pulse.c - pulseIn() function
  Part of Arduino - http://www.arduino.cc/

  Copyright (c) 2005-2006 David A. Mellis

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General
  Public License along with this library; if not, write to the
  Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  Boston, MA  02111-1307  USA

  $Id: wiring.c 248 2007-02-03 15:36:30Z mellis $
*/

#include "wiring_private.h"
#include "pins_arduino.h"

/* Measures the length (in microseconds) of a pulse on the pin; state is HIGH
 * or LOW, the type of pulse to measure.  Works on pulses from 2-3 microseconds
 * to 3 minutes in length, but must be called at least a few dozen microseconds
 * before the start of the pulse. */
unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout, unsigned long timepulse)
{
	// 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 width = 0; // keep initialization out of time critical area
	if(!timepulse) timepulse = timeout;
	// convert the timeout from microseconds to a number of times through
	// the initial loop; it takes 16 clock cycles per iteration.
	
	unsigned long numloops = 0;
	unsigned long maxloops = microsecondsToClockCycles(timeout) / 16;
	unsigned long maxloopspulse = microsecondsToClockCycles(timepulse) / 16;
	
	// wait for any previous pulse to end
	while ((*portInputRegister(port) & bit) == stateMask)
		if (numloops++ == maxloops)
			return 0;
	
	// wait for the pulse to start
	while ((*portInputRegister(port) & bit) != stateMask)
		if (numloops++ == maxloops)
			return 0;
	
	// wait for the pulse to stop
	while ((*portInputRegister(port) & bit) == stateMask) {
		if (numloops++ == maxloopspulse)
			return timepulse;
		width++;
	}

	// convert the reading to microseconds. The loop has been determined
	// to be 20 clock cycles long and have about 16 clocks between the edge
	// and the start of the loop. There will be some error introduced by
	// the interrupt handlers.
	return clockCyclesToMicroseconds(width * 21 + 16); 
}

Тогда при указании 4-го параметра, время ожидания и ограничения будут разными и функция при превышении ограничения будет возвращать ораничение, а не 0:

pulseIn(pin, HIGH, 1000000, 85000);

 

 

BrightSide
Offline
Зарегистрирован: 18.06.2013

Большое спасибо, так действительно удобнее