Rotary Encoder - GPIO interrupts failing to fire

Recently switched from an Odroid-C2 to the Jetson Nano Dev Kit. On the c2, I have a python function using wiringpi that properly interfaces with a PCL11 rotary encoder. Using the jetson nano and Jetson.GPIO, ticks are recorded sporadically (if at all).

Info

  • Which version of Jetson.GPIO you are using: Current Release
  • Which Jetson board you are using: Jetson Nano Developer Kit

To Reproduce

Steps to reproduce the behavior:
PCL11 Rotary Encoder: https://www.bourns.com/docs/Product-Datasheets/PEC11L.pdf

  1. Connect encoder channel A to BCM Pin 6 (Board 31, gpio200)
  2. Connect encoder channel B to BCM Pin 5 (Board 29, gpio149)
  3. Connect encoder push-switch to BCM 12 (Board 32, gpio168) [optional]

Here’s how I performed my test on both the Odroid-C2 and the Jetson Nano Dev Kit
clone https://github.com/guyc/py-gaugette.git and modify gaugette/rotary_encoder.py to use Jetson.GPIO

  • Remove gaugette.gpio and replace with Jetson.GPIO
  • Modify setup calls to match syntax, add setmode() to set to BCM or BOARD, and convert all trigger() calls to ``` self.gpio.add_event_detect(self.[pin_a/pin_b], self.gpio.BOTH, callback=isr) ```
  • Create simple driver code `test.py`
import time
import Jetson.GPIO as GPIO
import gaugette.rotary_encoder
A_PIN = 6
B_PIN = 5
gpio = GPIO
GPIO.setmode(GPIO.BCM)
encoder = gaugette.rotary_encoder.RotaryEncoder(gpio, A_PIN, B_PIN)
encoder.start()
while True:
  delta = encoder.get_cycles()
  if delta!=0:
    print(str(delta))
  else:
    time.sleep(0.01)

Run test.py and rotate encoder clockwise and counterclockwise

Expected behavior

Depending on the direction you rotate, the result should be a simple output:

1
1
1
1
-1
-1
-1
-1
-1

Actual Output
Little to no output. There may be a single output of ‘-1’ or ‘1’ for every 100-200 turns.

I’ve confirmed that this works on my old hardware (Odroid-C2, running Ubuntu 18.04). For every 1 tick, you get one output from the code. Just wondering why this may be different on the Jetson Nano.

I am wondering if this issue comes from jetson GPIO or even the nano ifself.

Are you able to use the gpio sysfs to check this issue?

Wayne,

Yes, I was able to look at the GPIO via sysfs. Inspecting it showed the GPIO was set as an input, and had the IRQ indicator.

In a rudimentary test, I wired two GPIOs together with a 800 Ohm resister in series. Performing a looped toggle of the output, and with the trigger set on GPIO.BOTH, the interrupt handling seemed to work appropriately.

While I haven’t looked into it yet, could it be some difference with lower level logic between the WiringPi library and the Jetson.GPIO library? I imagine they functioned in similar ways…I’ll see what I can find.

Just wanted to include some images from an oscilloscope showing the differences in signals.

https://imgur.com/a/YveumHe

This album has both images

Jetson Nano rotary encoder signal

Can’t put images directly, so i’ll link individually as well

https://i.imgur.com/tBGPTSm.png

Odroid-C2 rotary encoder signal

https://i.imgur.com/MRvJNj7.png

Seems like there might be too much capacitance, or perhaps the pull-ups are a bit higher on the nano. Thoughts?

I cleaned up my test circuit, and this slow rising action has disappeared. Signal is similar to that of the odroid, with just a little noise when active and dropped to 0V. Guess it’s back to square one!

EDIT I noticed this post from another Moderator: https://devtalk.nvidia.com/default/topic/1052582/jetson-nano/jetson-gpio-interrupt-processing-on-nano/post/5354004

Is this still the case? If so, the logic of the rotary encoder program I have will be hard to replicate. The idea is that, on a given delay (0.001s), the system reads the input. When the rotary encoder is twisted, it follows a sequence in its signaling that allows a system to determine which way its being turned.

If this quote is true, then the periodic update invalidates the interrupt for both falling edges. It would seem that the only workaround is to avoid add_event_detect() altogether and make it purely loop logic.

Yes, the quote is still true.

Thanks Wayne,

I’m continuing to test at a rudimentary level, and what I’m seeing is that the rising edges are not being detected.

I have a simple program written that sets interrupts on both edges for two pins corresponding with signal A and signal B of the rotary encoder. On interrupt, the channel input is read and printed out; from within the interrupt function so as to not affect the event status.

On the odroid, I get a response like this:

b tick 0
a tick 0
b tick 1
b tick 1
a tick 1

On the nano, I only see a tick 0

Unsure as to why I’m not seeing the rising edges at the moment.

Edit Starting to run out of ideas. Seems that it never reads logic high when using GPIO.input() on the signal pins. scope shows voltage at 1.7V when the encoder is not active, and it dropped to near-zero when used.

Continuing testing, and I can confirm the nano detects rising edges when shifting from 0V to 3.3V by just wiring a loopback from an output to an input with a resistor in series.

In the case of the rotary encoder, the logic level shifts from 1.7V to 0V, and does not go higher. Might this be why the rising edge isn’t detected?

Edit I’ll be attempting to increase the voltage of the rotary encoders signal paths to 3.0V to see if that has any effect. Can’t do it today though so i’ll update this when I have results

Okay, did some additional testing and I’m still at a loss.

After modifying the board a bit. I can confirm the following:

The voltage at the node for each signal path before being attached to the Jetson Nano is 2.75V

After connecting the GPIOs it actually rises to 3.0/3.1V

If I perform a cat on /sys/class/gpiox/value it reads 1

If I turn the encoder and read value, it reads 0

It will remain at 0, despite the encoder properly functioning and returning to logic high as seen on the pictures above from my oscilloscope.

As this works correctly with both a Raspberry Pi 3 B+, and an Odroid C2, I’m forced to believe this is an issue with the Jetson Nano itself. Thoughts please?

EDIT
Here’s the circuit that’s I’m currently using:


I thought I’d look to see how the encoder works when connected to the nano vs disconnected. Pink is disconnected, green is connected. The capture was the rotary encoder unturned and then 1 turn clockwise. It was a similar (shifted) response from the B signal to the A signal path.

Noticeably, the starting voltage is higher when connected to the nano before it plunges to 1.2V after the rotary encoder is turned. Unsure as to why.

Despite the lack of response, I’ve worked more on figuring this out. Fortunately the community at /r/JetsonNano took a swing at this.

Since most of the IO on the module are 1.8V, the Jetson Nano devkit has TXB0108 bidirectional level shifters on the carrier board to bring the voltage to 3.3V. You can take a look at the schematics for the carrier board here: https://developer.nvidia.com/embedded/dlc/Jetson-Nano-Carrier-Board-Reference-Design-Files
From the level shifter’s datasheet: (section 8.3.2)
For proper operation,the device driving the dataI/Os of the TXB0108 must have drive strength of at least±2 mA.
The resistor divider only provides a current of ~220uA. It’s not enough to make the level shifter change state from 0.

Seems to make sense, so I originally replaced the 10k and 12.2k resistors wih 1k and 1.2k respectively. This gave a noticeable response to the GPIOS. However, connecting to the nano showed a noticeable voltage drop from what was supposed to be 2.5 to 1.7V. Still misbehaving.

So, I started trying to figure out what kind of current draw I was looking at from the nano because the voltage drop was fairly large. Quick calculations show a much larger current draw of about 2.88mA than spec’d.

Worked a new circuit to accomodate the higher draw. Here it is disconnected from the nano:

Hooked it up to the oscope and it matched. Connected to the nano and got some fun behavior:

  • Signal path A: 300mV, still shows encoder response when turned
  • Signal path B: 0V, no response

I’ll continue posting my updates in this thread. It’s a shame that the nano has so many problems with a simple circuit that works out of the box with a Raspberry Pi and an Odroid C2. Significantly cheaper products, but no headaches.

Hi,

Sorry for late reply. Is your issue same as the one below?

We already started our investigation internally for this issue but not sure if your topic is same as this one.

Did you try another 3.3V power supply to do this? I.e. V1=3.3V, keep S1 & S2 and R1= R2 = 1 kohm, remove R3, R4, C1, C2. TXB0108 level shift IO ports needs correct input voltage (3.3) and sufficient input current (>2mA).

Trumany,

I have indeed tried this.

Removing C1, C2, R3, R4, and switching to a 3.3V source has the system mostly working.

The encoder is detecting motion in the correct directions, but does ocassionally miss a turn when multiple are done in sequence. Curious as to why it does this…

Also, I was able to get this working by purposefully not using the interrupt system available through Jetson.GPIO. I’d like to use this as polling is just a wasteful process. Replacing polling with interrupts in my test code lends to missed turns about 20-30% of the time.

What’s the switching frequency? Do you have the waveform of it? I’d like to see the rising edge of it with 3.3v. If it is not so good, maybe a strengthened pu is necessary. If the edge and freq have no problem, then it could be sampling code problem.

You got it, I’ll connect my scope tomorrow and get back with this information. I’ll also provide both the sampling and polling code for reference.

Well…it’s a rotary encoder all right

Besides the obvious noise, it looks like there’s some ringing going on as well. Guess it’s back to adding caps?

I’ve continued the testing with varying values for the resistors and capacitors when hooked up to the standard filtering circuit.

Here’s an album, values are in the descriptions: https://imgur.com/a/WZRnnCs

What I’ve found is that a low resistance of 100 ohm - 1k ohm works the best when connected to the nano.

Capacitance is weird. Adding the capacitor causes some strange waveforms with the low resistance:

I’m wondering if this has to do with the level shifter.

Overall, I was unable to remove the noise to a sufficient level to allow my polling code or interrupt-driven code to register all turns. Loss rate on single turns high. Continuous turn in one direction starts poorly but is then registered pretty consistently. Changing direction almost always caused a missed read.

Highly suspect it is caused by load capacitance. As you can see in the data sheet of TXB0108 http://www.ti.com/lit/ds/symlink/txb0108.pdf , there is pf level request on CL.

Also the pull-up should be able to make the drive strength > 2mA, which means the pu should be less than ~1.65 kohm if V=3.3V.

Trumany,

The testing definitely showed the need for smaller resistors. I’ve seen now it’s only a 70pF capacitive load limit on the shifter. I can try something around that amount and keep the resistance around 1.5k at the most.

Regardless, it’s looking that the nano dev kit won’t be able to support the rotary encoder like the other SBCs I’ve tried, not without some filtering on the software-side at least. Unless there’s some other thoughts as to why it’s misbehaving?

To hardware, it is only high/low pulse input, if can be read correctly there should be no sw support problem. The noise signals might be caused by this kind of mechanical rotary switch if you can see the noise even without nano devkit. If so, the noise could cause the reading mistake.