Question about PulsePosition for same pin I/O

Status
Not open for further replies.

u_cap

Active member
Sanity check needed regarding a possible PulsePosition modification.

I am trying to capture data at 48MHz, similar to what was done here:

https://trmm.net/Lighthouse
https://github.com/osresearch/lighthouse/blob/master/firmware/InputCapture.cpp



But, instead of using the simpler TS3633, I am using its successor:

https://www.triadsemi.com/product/ts4231/

The problem is that the TS4231 uses two pins, E and D, as input as well as output.

There is a somewhat involved (and timing dependent) setup and configuration to be done:

https://github.com/TriadSemi/TS4231/blob/master/ts4231.cpp

My question is this: is it reasonable to attempt to do this setup and config using PulsePosition?

Is it at least theoretically possible to modify PulsePosition to do I/O in this manner, on the same pin?

Is it sensible to try to add something like

bool begin(uint8_t txPin);
bool end(uint8_t txPin);

to allow for switching back and forth between PulsePositionOutput and PulsePositionInput on the same pin?

Thanks!
 
Preamble:

I understand this will take a while for me to sort out.

At this point the guidance I could really use is somebody who understands the Teensy/FTM hardware constraints to tell me "this will never work because ..."
Or a confirmation that, yes, this can be done.


To elaborate:

I want to use PPM with an ASIC product called TS4231. This is a current-to-digital converter designed to be combined with a photodiode. Its predecessor, the TS3633, was an analog part. It had a single digital output channel, and you can use Teensy and PPM to capture that output. The retail hardware uses an 1.8MHz modulated IR signal, the TS3633 contains a TIA and an RF amplifier, and generates an envelope signal for as long as the diode registers the infrared beam.

If you want to learn about the entire system, see
https://www.youtube.com/watch?v=75ZytcYANTA

Supported setup is either one "base station", or two base stations taking turns. Each has 2 rotors at 60Hz and 180 degree offset, and an LED blinker. Each base will blink at 120Hz, and each rotor will add a sweep signal, so the total rate at which envelopes are produced by the TS3633 is 240Hz. Typical envelopes are 2-35 usec for sweeps, and 60-130usec blinks. The time between the blinks and sweeps varies, but is O(msec). That's the E channel.

The TS4231 successor part adds a D channel, which can provide a straight amplification of the actual IR signal, that is, each carrier pulse. Its datasheet indicates it supports 1-10MHz carrier frequency.

So signals on the D channel are a steady (or, in the future, modulated) pulse train from 1usec edge-to-edge / 0.5usec pulse width, down to 0.1usec/0.05usec.

The retail hardware uses a Lattice FPGA running at 48MHz to read up to 32+ sensors. The project I referenced above used a Teensy 3.2 to service the TS3633 using the "inputCapture" derivative code I referenced above:
https://github.com/osresearch/lighthouse/blob/master/firmware/InputCapture.cpp

TL;DR: This has been done with the Envelope-only ASIC, it works.


I am now trying to do the same with the TS4231. The problem is that the two channels, D+E, are used as input and output, switching back and forth. The TS4231 has a DIO which has to be configured first, which happens in stages: send commands (pin out), check for inputs (pin in), send commands (pin out). Once it is set up, I can read signals. If the part goes to sleep or I need to reconfigure it, I need to switch back to pin out.

The TS4231 reference code I linked in the initial posting does something like

digitalWrite(E_pin, LOW);
pinMode(E_pin, OUTPUT);
delayUs(BUS_DRV_DLY);
pinMode(E_pin, INPUT);
....

Eventually, when the TS4231 is in WATCH state, it ends with

pinMode(D_pin, INPUT);
pinMode(E_pin, INPUT);

From then on, I can capture data, which works fine using digitalRead.

But using PulsePositionInput or InputCapture at that point does not read any samples, and calling ppi.begin( EITHER_PIN ) appears to put the device into SLEEP state, which is usually accomplished by

digitalWrite(E_pin, LOW);
pinMode(E_pin, OUTPUT);
delayUs(BUS_DRV_DLY);
pinMode(E_pin, INPUT);
delayUs(BUS_DRV_DLY);

I can transition from SLEEP back to WATCH, but I cannot get PPM or InputCapture to sample anything.

I am working my way towards a simple repro as well as the PPM source (and eventually the FTM docs). Maybe FTM ISR and switching back and forth with I/O on the serviced pin cannot be done at all, and I am wasting my time (or need to use 4 pins, and some additional hardware). Maybe this is just an issue of timing, but looking over the InputCapture::begin() sources I have to wonder whether writing to FTM registers will cause signals on the target pin that inevitably will reset the TS4231.

TL;DR: if anybody can think of a hardware reason why I cannot use FTM timers on a single pin to go back and forth between generating output and capturing input, I really would like to know.
 
Last edited:
First you need to understand every pin has a 8-way mux which connects it to 1 of 8 possible functions. This is described in chapter 10 of the reference manual, where you can find a huge table that shows all 8 mux possibilities for every pin. Use the schematic to translate Arduino pin numbers to Freescale's native names in that table.

You didn't mention specific pins, so I'm going to talk about pin 5. From the schematic, you can see that's "PTD7". You can find it on the very last line of the huge table, on page 209 of the MK20DX256 reference manual.

This pin has 5 of the 8 mux channels implemented. So when the pin's mux is configured to 1 (called "ALT1"), the pin is controlled by the GPIO module. Then it's set to ALT4, the pin is controlled by FTM0_CH7.

So, to answer your original question, yes you can switch between FTM input capture and FTM output compare. But both of those require the mux to be set to ALT4. Then to change how the FTM timer uses FTM0_CH7, you would write to the FTM0_C7SC register. The 4 bits MSB,MSA,ELSB,ELSA documented on pages 783-784 directly control whether the pin is PWM, input capture, output compare or other modes. Changing those 4 bits causes the pin to become an input or output, depending on which way the FTM uses it.

However, the FTM0 timer only controls pin 5 (PTD7) when its mux is set to ALT4. If you use pinMode(5, OUTPUT), the pinMode function will set the mux to ALT1, so the pin becomes controlled by the GPIO hardware. When GPIO has control of the pin, it becomes input vs output depending on the GPIOD_PDDR register.

Likewise, when the pin's mux is configured to the other settings, the pin is effectively disconnected from GPIO & FTM and fully controlled by whatever module you selected. If you use ALT2, then the pin is fully controlled by the CMT timer (eg, used by the IrRemote library). Using ALT0 (the default) put the pin's hardware into a low power disabled state, even if the FTM and GPIO are running.

As long as the mux is configured for anything other than ALT4, that pin is effectively disconnected from the FTM0 timer and you can change from input capture to output compare or any other feature of the FTM timer by writing to FTM0_C7SC and the other FTM registers. But if you start mixing pinMode and other non-FTM usage, you must be aware of the pin mux. Only 1 of the 8 possible functions fully controls the pin at a time. The other 7 have no effect, so if you switch the mux to ALT1 for GPIO, the FTM no longer has access to the pin.



While this doesn't pertain directly to your situation, for the sake of completeness (for anyone who later finds this by searching), the very similar TPM timers (which we sometimes mistakenly call FTM for the sake of software compatibility, especially on Teensy LC) have a few minor differences. Their "SC" registers that configure the modes like input capture vs output compare can not directly change modes like FTM can. The register bits and everything else looks virtually identical, but the TPM's SC register must first be written to zero, and then you must wait a brief delay until the write takes effect (if the TPM is running from a slow clock). The TPM ignores any changes to its SC channel config registers if you don't first clear them to zero, but it defaults to zero from hardware reset, so your first write to the SC register will always succeed (and the Teensyduino startup code initializes them all to the mode used for PWM by analogWrite). Simple if you know about this little gotcha, but incredibly frustrating if you try porting complex FTM code to the TPM timers and don't know you have to clear them to zero to get any config changes to take effect!
 
chapter 10 of the reference manual, table that shows all 8 mux possibilities for every pin. Use the schematic to translate Arduino pin numbers to Freescale's native names in that table. ALT1 GPIO module. ALT4, tFTM0_CHx.

So, to answer your original question, yes you can switch between FTM input capture and FTM output compare. But both of those require the mux to be set to ALT4. Then to change how the FTM timer uses FTM0_CH7, you would write to the FTM0_C7SC register. The 4 bits MSB,MSA,ELSB,ELSA documented on pages 783-784 directly control whether the pin is PWM, input capture, output compare or other modes. Changing those 4 bits causes the pin to become an input or output, depending on which way the FTM uses it.

Thanks a bunch for the references, Paul. I think I understand the InputCapture setup, but I am not sure this covers the problem I am observing.

First, I have to generate a bunch of once-off pulse signals when using the D,E pins as output. Maybe I do not understand OutputCompare, but I read it to be a fixed frequency train of pulses repeated as the timer cycles. Doing the initial setup - input, output, input, and all just for a handfull of pulses - is a lot easier in GPIO.

I do that, and it works. So my assumption is that it would make sense for me to start in GPIO, get the TS4231 configuration done in mux ALT1, switch to ALT4 for InputCapture.
I would use GPIO again only to force the mux back to ALT1 if I ever have to reconfigure the TS4231, before going back to ALT4 capture.

The last GPIO calls to get into WATCH are

digitalWrite(E_pin, HIGH);
pinMode(E_pin, OUTPUT);
digitalWrite(D_pin, HIGH);
pinMode(D_pin, OUTPUT);
digitalWrite(E_pin, LOW);
digitalWrite(D_pin, LOW);
pinMode(D_pin, INPUT);
digitalWrite(E_pin, HIGH);
pinMode(E_pin, INPUT);

At this point, mux is still in ALT1 for both pins, I am ready to read input.

What I need to do next is switch from ALT1 to ALT4. I have done this so far by invoking the InputCapture or PPM code.

Turns out that this actually works for the D channel. If I set ALT4 on the pin connected to D, and select any InputCapture mode, I read the expected results.

But when I try to do the exact same transition for the pin connected to the E channel, the TS4231 transitions to SLEEP.

If I wanted to send an explicit GPIO command to make the TS4231 go from WATCH to SLEEP I would have to send (w/ GPIO):

digitalWrite(E_pin, LOW);
pinMode(E_pin, OUTPUT);
delayUs(BUS_DRV_DLY);
pinMode(E_pin, INPUT);

Note that this command uses only the E channel. There are no other valid state transitions from WATCH. No TS4231 command in WATCH uses the D channel.

I suspect that there is something happening on either pin whenever I change the mux from ALT1 GPIO input to ALT4 and configure the mode for InputCapture.
If that happens on D, the TS4231 apparently just ignores it.

But if it happens on E, the TS4231 registers it as a signal, and goes to SLEEP..

Is there anything happening during that mux: ALT1->ALT4 transition that could register as a signal on the wire?

It happens whether I configure the E channel pin as InputCapture RISE,FALL or BOTH, so it has nothing to do with the mode.
 
Is there anything happening during that mux: ALT1->ALT4 transition that could register as a signal on the wire?

Somebody kindly suggested I should consider the built-in pull-up resistor to ensure that my E pin, configured as input, does not get drawn to LOW - assuming that's what happens when setting ALT4.

Looking at 11.14.1 Pin Control Register on page 227pp I am guessing:

#define PORT_PCR_MUX(n) ((uint32_t)(((n) & 7) << 8)) // Pin Mux Control
#define PORT_PCR_PE ((uint32_t)0x00000002) // Pull Enable
#define PORT_PCR_PS ((uint32_t)0x00000001) // Pull Select


So I should modify my FTM setup to include two bits:

*portConfigRegister(pin) = PORT_PCR_MUX(4) | PORT_PCR_PE | PORT_PCR_PS;

I tried that - setting it on the E pin, not the D pin - and it does not make a difference. Is this just not the right hole to dig, or am I doing it wrong?
 
I tried that - setting it on the E pin, not the D pin - and it does not make a difference. Is this just not the right hole to dig, or am I doing it wrong?

Very hard to say, when I don't know anything about this TS4231 chip and I don't even fully understand what you're actually trying to do.
 
Is this just not the right hole to dig, or am I doing it wrong?

I also tried INPUT_PULLUP at the end of the final GPIO read verifying the state transition, same issue.

If I read this right, the internal pullups ensure that disconnected pins are guaranteed HIGH, not random, so I suspect that they do not affect what registers externally.

I also made sure that just setting the portConfigRegister(E_pin) is sufficient to produce the problem - skipping CSC_CHANGE and NVIC calls has no impact.

Edit: definitely not the right idea.
 
Last edited:
Very hard to say, when I don't know anything about this TS4231 chip and I don't even fully understand what you're actually trying to do.

I can post source of the exact sequence of Teensy API calls for GPIO and InputCapture, but w/o the hardware it's not much of a repro.

I can phrase my question like this: what I observe is that:

*portConfigRegister(pin) = PORT_PCR_MUX(4)

has the same effect as

digitalWrite(E_pin, LOW);
pinMode(E_pin, OUTPUT);

In other words, switching E_pin from GPIO INPUT to ALT4 - not even setting cscEdge or IRQ, just the MUX - has the exact same effect on the TS4231 as switching E_pin to LOW OUTPUT.

Does anybody have any idea why that would happen, and whether and how it could be prevented?

The datasheet
https://www.triadsemi.com/download/17260/
and the GitHub Arduino reference library
https://github.com/TriadSemi/TS4231/blob/master/ts4231.cpp

are the entirety of the publicly available programming documentation, plus PCB Layout guidelines, https://www.triadsemi.com/download/18126/


The datasheet on page 4 states:
7.1 Sleep Mode
After the device has been configured to detect the optical carrier, the SteamVR Tracking HDK transitions the
device back to Watch state by driving E pin low, then D pin low, then E pin back high. Once in Watch state the
SteamVR Tracking HDK promptly releases the E/D bus so that the TS4231 can drive the E and D outputs when
IR light is detected. While configured to pulse out the carrier on the D pin, the SteamVR Tracking HDK can put
the device to sleep by driving E pin low.
To transition back to Watch mode, the SteamVR Tracking HDK sets
the D pin low and then the E pin high providing for quick transitions between Watch and Sleep to support
dynamic power control.


I am trying to substitute a Teensy for the proprietary (licensing agreement required) HDK software.
My problem is that setting MUX to ALT4 for the E_pin will trigger that transition to SLEEP, and I do not understand why.
 
Last edited:
I think I found the solution - it occured to me that the FTM calls are currently made in this order:

Code:
*portConfigRegister(pin) = PORT_PCR_MUX(4);
CSC_CHANGE(ftm, cscEdge);

If ALT4 does not default to InputCapture, then this could make the pin an output instead in the time between those two calls.

So I changed the order

Code:
CSC_CHANGE(ftm, cscEdge); // set InputCapture first
*portConfigRegister(pin) = PORT_PCR_MUX(4);

and now the otherwise identical code works as expected.

TL;DR: unless there is something else wrong about the change above, I appear to be set. Not sure whether this is a good change for the general case.
 
Last edited:
TL;DR: unless there is something else wrong about the change above, I appear to be set. Not sure whether this is a good change for the general case.

Looks fine to me.

When it comes to these sorts of timer-based input & output projects, there really isn't any "general case". They're all quite specialized.
 
Status
Not open for further replies.
Back
Top