Using a GPIO pin for both input and output.

Our team would like to use an ultrasonic rangefinder that uses the signal pin for both input and output (ping control and echo are on the same data line).

How would you go about using WPILib to switch a GPIO pin between input and output? Specifically, I’d like to do something along the following lines:


 DigitalOutput ping(12);
 Counter echo(12);
 echo.SetSemiPeriodMode(true);

 /* ... */
 ping.Pulse(pingPulseWidth);
 double range = echo.GetPeriod();

This is very easy to do with most microcontrollers, but I can’t see an easy way to do this with WPILib. Any tips from the WPI guys?

I can’t find the post right now, so I can’t be sure I’m remembering this properly. I recall someone who should know saying that reconfiguring a pin between input and output “stalls” the FPGA briefly and can mess up other measurements.

What happens if you try connecting the sensor’s signal line to a pair of GPIO lines, one input and one output? I don’t know whether the FPGA ignores sidetone or if it relies on the sensor itself to not respond to its own signal.

Our team used two cRIO pins for a Radio Shack “parallax” ultrasonic sensor. The cRIO output pin is wired through a small signal diode (e.g. 1n4148) to the sensor SIG pin. The diode cathode is oriented towards the SIG pin. Add a 1K resistor between SIG and ground (because there is a weak pull down on the SIG pin and the pull up in the cRIO will otherwise cause the SIG pin to go high when the sensor floats the pin). Wire SIG to a cRIO input pin. The diode serves to “remove” the cRIO output from the circuit when the output goes low and allow the sensor to drive the pin high and low. It a quick and dirty fix rather than adding a true isolation circuit, and the forward voltage drop is not important in this application.

Then write your own ultrasonic code so that it doesn’t look for a high going pulse until the outgoing trigger is finished and returns to a low e.g. modify the existing source code like this:


void Ultrasonic_SIG::Ping() 
{ 
  // TODO: Either assert or disable, not both. 
  wpi_assert(!m_automaticEnabled); 
  SetAutomaticMode(false); // turn off automatic round robin if pinging single sensor 
  m_pingChannel->Pulse(kPingTime); // do the ping to start getting a single range 
  while(m_pingChannel->IsPulsing()) {}; // wait for the pulse to end 
  m_counter->Reset(); // reset the counter to zero (invalid data now) 
} 

Or add your own method to the existing ultrasonic.cpp file, or create a new class that inherits the ultrasonic class, or however you prefer to modify the code.

It’s a little complicated but we have our Parallax sensors working.

Thanks a lot for the replies, Alan and oddjob. I had considered the use of two GPIO lines and tristate buffers to ensure that at least one of the lines was always disconnected, but as long as the single diode approach works without causing appreciable noise, I’ll give it a shot.

Thanks for the code sample, oddjob! I’ll report back on its success when it’s implemented.

To the WPILib/FPGA guys:
I’d be nice to have the option bypass the FPGA completely and allow users to implement their own GPIO logic, for those willing to take the plunge.

Take a look at the I2C interface schematic for another implementation.

Can you explain what you mean by this? I am unsure as to what feature you want to add. The direction change timing is a function of the module, not of the FPGA image. The provided image doesn’t get in the way unless you ask it to.

We don’t have a spare cRIO so I’m not doing any coding now. Often I2C is a special hardware module in the microcontroller and that module takes care of the I2C data line direction. Also, I2C data and clock are open drain connections.

For our robot, it would be great to have a direction controllable pin. Call it whatever, maybe DigitalPin, with a method to control the pin drive mode.

Get() to read the pin.
Set() to set the pin. (the pin needs to be set to drive for the pin to change).
DriveMode() to drive or float the pin.

If there is no FPGA constraint to changing drive mode at run time, the DigitalInput and DigitalOutput classes could be obsoleted by DigitalPin.

Thanks for the clarification; I was under the impression the FPGA was handling the digital I/O as well. If the digital I/O is left untouched, what is the expected delay of changing the direction? Is it enough to warrant using a custom circuit with two pins?

One more quick question: is there any more concrete documentation on what exactly the FPGA does and does not do, and perhaps documentation on how to interface directly with the cRIO functionality? Looking at the WPILib implementation, there’s a lot of headers without corresponding source files in the DIO/ChipObject interfaces. If I’d like to work on my own interface/API, would it be possible to get more specific docs or would I have to use WPILib as a reference to how to use the hardware?