pulseIn() on the Dash


#1

I need to use the Dash to read a PWM signal from an opto-isolated ADC device. It looks like pulseIn is not yet supported. I have a feeling this has to do with some of the port and register info. Does anyone have any idea if there is an existing way to do this? The pulseIn() function is defined in wiring as follows. The trouble is I need to know the ports etc. which are board specific. How can I adapt the code below to use the pulseIn() function?

Tyler

/*
  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)
{
	// 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
	
	// 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;
	
	// 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++ == maxloops)
			return 0;
		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); 
}

#2

@Tyler_C

I have a solution for you. Seems to work ok. Connect a jumper wire between R03 and R04 on the Dash to test.

I tested with the attached sketch (41 usec) and with a 1kHz square wave (500 usec). I’ll roll this into the next release.

uint8_t digitalPinToBitMask(uint8_t pin) {
  if(IO_VALID(pin)) {
    return GPIO_PIN(pin); 
  }
  return 0;
}

uint8_t digitalPinToPort(uint8_t pin) {
  if(IO_VALID(pin)) {
    return PINS_PORT(pin); 
  }
  return 0;
}

volatile uint32_t* portInputRegister(uint8_t port) {
  GPIO_Type * pGPIO = (GPIO_Type *)(  PTA_BASE+(port*PERIPH_GPIO_SIZE));
  return &pGPIO->PDIR;
}

unsigned long pulseIn(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 width = 0; // keep initialization out of time critical area
  
  // 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;
  
  // 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++ == maxloops)
      return 0;
    width++;
  }
  
  // convert the reading to microseconds. The loop has been determined
  // to be 11 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 * 11 + 16); 
}

void setup() {
  analogWrite(R03, 128);
  Serial.begin(115200);
  pinMode(R04, INPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  __disable_irq();
  unsigned long pulse = pulseIn(R04, HIGH, 1000000);
  __enable_irq();
  Serial.print("Pulse: ");
  Serial.println(pulse);
  delay(3000);
}

#3

Thanks Erik! That’s exactly what I needed - works great for me. It also allows me to free up a UART :slight_smile:


#4

I’ve a similar issue, I’ve got to measure the distance of an object with an Hc-sr04 sensor, but I receive always zero with every pin of the dash. Any suggestions?
Why do you need to connect R03 and R04?


#5

That was a demo sketch. As you can see in setup() R03 is used to make the test output signal (a square wave) and R04 is set to INPUT, then used as the pin for the pulseIn() function. The pulseIn() function has been added to the Arduino package, so you can use just the setup() and loop() functions to perform the same demo.

The Sparkfun breakout for that sensor has a link to demo code that should work with the Dash. The only change you’ll need to make to the pulseIn() call is a third argument for the timeout, for which the default is 1000000 (one second).
So
duration = pulseIn(echoPin, HIGH);
becomes
duration = pulseIn(echoPin, HIGH, 1000000);

I’ll add the default timeout value for the next release.

If it’s still not working, check that a pulse is being generated by using an oscilloscope or logic analyzer. If you don’t have one, you could connect the pulse line to a Dash pin and attach an interrupt for it to at least see if there is a pulse.


#6

Thank you for your answer. I’m trying again, but with the last firmware update now I can’t use Arduino serial monitor, I receive always this message:

“Error while setting serial port parameters: 9,600 N 8 1”

I’ve tried different baud rate.
Down below ther is the code I’m using, I see that the LED is always on (with my code I think it should be turned off for at least 3 seconds after having tried to read the sensor).

What’s going wrong?

int echoPin = R04;
int trigPin = R03;

void setup() {
    delay(200);
    Serial.begin(9600);
    Dash.begin();
    pinMode(echoPin, INPUT);
    pinMode(trigPin, OUTPUT);
}

String misura() {
    long duration, cm;

    // make pulse
    digitalWrite(trigPin, LOW);
    delayMicroseconds(2);
    digitalWrite(trigPin, HIGH);
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW);

    Serial.println(".");
    duration = pulseIn(echoPin, HIGH,1000000);

    // convert the time into a distance
    cm = duration / 29.1 / 2;
    Serial.println(duration);
    Serial.println(cm);
    delay(200);
}

void loop() {
    Dash.onLED();
    misura();
    Dash.offLED();
    delay(3000);
}

#7

I don’t see anything obviously wrong with the code. I don’t have that sensor, so unfortunately I can’t test it out. A couple of notes though:

  • USB baud rate isn’t used, so it doesn’t matter if you put 9600 or you can even omit it entirely.
  • Dash.begin(); no longer needs to be called explicitly, it’s already done before setup()

You could try removing or moving Serial.println("."); as it might be causing you to miss the pulse.
And, as always, double-check the wiring.


#8

Hi Erik,
and thank you for your time.
I’ve tried the sensor with the same sketch on an Arduino Nano, and it worked fine. On the Dash it doesn’t work, so I’ve connect the pin R03 to a tester and alternate for 3 seconds LOW and HIGH values in the loop:

digitalWrite(R03,HIGH);
delay(3000);
digitalWrite(R03,LOW);
delay(3000);

The value read of the tester go from 3.26V to 0V.
On the Arduino Nano the same test lead to values 4.78 and 0V.

So I think the problem is that the R03 pin when put to HIGH doesn’t have enaugh volts. Are there pins with 5v that I can use as output?

Thankyou,
Giulio


#9

Ah, that makes sense. Sorry I didn’t think to ask about that. The Dash uses 3.3V IO. Many Arduino boards run off 5V, so they have 5V IO. The Dash can accept a 5V input, but it can’t drive a 5V output directly.

So what you can try is powering the Hc-sr04 using the 3.3V pin on the Dash and see if that will work well enough. Otherwise, you’ll need a transistor to get the 5V/0V range, and drive that using the Dash 3.3V output.


#10

The HC-SR04 doesn’t work at 3.3 V.
It needs 5V at its VCC pin and 5V on the trigger pin.
At this moment I’m using the USB 5V pin to power the VCC. Can I use this USB 5V pin also when I will use a battery?
And more:
How can I connect a transistor to make the pulse 5V high? (Also when I will use a battery)

Sorry for my poor skills in electronics, I need help: I have a bunch of transistors here but I don’t know which one to use, do you have any suggestion? :slight_smile:
Which type?

Thanks.


#11

The 5V comes from the USB input power, it is not generated by the board itself. The battery voltage will vary from 4.2V to 3.0V, depending on the charge state. To get 5V from battery power, you can add a boost regulator, such as this. Here’s a good video from Sparkfun about boost regulators. You could use the 3.3V or the battery pin as in the input.

The 3.3V trigger pin from the Dash will probably be enough to work as the trigger. Otherwise, you’ll need to add a transistor to drive the trigger at 5V to the HC-SR04. Or you can use a level shifter, which will convert the 3.3V to 5V and back. The level shifter may be your best bet.


#12

Hi Erik, I’ve discovered that the HCSR04 works also with 3.3 pulse, for example the Raspberry Pi uses pin with 3.3v. Everything over 2.7v is considered 1, so it should work.

So I’ve tried with a friend with an oscilloscope to understand if the HCSR04 is working and we saw the 3.26 pulse correctly sent and we read the 1 coming back from HCSR04 with digitalRead.

So we think there is a problem in your implementation of the pulseIn function, I’ve tried to make my own function using micros, but cant’ make it work.

unsigned long  pulseIn2(int pin, int value, unsigned long timeout) {

  unsigned long m0 = micros();
  unsigned long m1 = m0 + timeout;
  if(m1<m0) {/*handle overflow*/
    delayMicroseconds(timeout);
    m0 = micros();
    m1 = m0 + timeout;
  }
  int v = digitalRead(pin);
  while (micros()<m1 && v!=value) {
    v=digitalRead(pin);
  }
  unsigned long duration = micros() - m0;
  if (v == value) return duration;
  return timeout;
}

It doesn’t work, do you see any mistake?


#13

Hi @Erik I don’t know why the above code doesn’t work. I’ve also modified it without using micros, just counting the cycles. But it doesn’t work.

I’ve tried the code you put for @Tyler_C (8 april 2017), I’ve just changed the functions’ name to avoid duplicate names with the Dash library… and it works!

So I think you’ve got a problem in the implementation of the pulseIn function in your 0.10.2 dash library.
I’m using a PC with Windows 10 32 bit, I don’t know if the 32 bit uploader could be the problem.


#14

Thanks for letting me know. I’ll check and see what the difference is between the two implementations. Glad you got it working!


#15

Hi Erik,

I also experienced the PulseIn() not working in Version 10.4 and 10.5.
I tried using both Rxx and Dxx pin names hoping it’s just the pin-name conversion that’s not working but both fails.

However, the code you implemented above works fine as how @Giulio_Pons did it (renaming all functions)


#16

Hi Erik,

Using your pulseIn code, I’m getting Pulse Width = 0. Thus it mean it is getting frequency higher than system clock setting (currently set at 120MHz)?

PULSE WIDTH: 0
PULSE WIDTH: 999
PULSE WIDTH: 1997
PULSE WIDTH: 0
PULSE WIDTH: 1997
PULSE WIDTH: 162
PULSE WIDTH: 999
PULSE WIDTH: 1998
PULSE WIDTH: 29
PULSE WIDTH: 0
PULSE WIDTH: 998
PULSE WIDTH: 998
PULSE WIDTH: 1997
PULSE WIDTH: 162


#17

A pulse width of 0 means it timed out, so no pulse was detected. Default timeout is 1,000,000 microseconds.


#18

Just tried the HC-SR04P. it works on 3.3v to 5.xV
plugged on 3.3 board output, GND D01 and D02. keep receiving value : 0. Work great on arduino mega on 3v3 pin. So does the pulseln() works? I’m not familiar enough to try the working code above, I would like to use it the way it should.

edit: I use “dash 1.2”