periph / conn

Go·Hardware·Lean - Interfaces
https://periph.io
Apache License 2.0
67 stars 12 forks source link

gpio Pin Read doesn't work as expected #11

Closed gsexton closed 3 years ago

gsexton commented 3 years ago

Sorry for the length, but I'm trying to sort this out in my mind...

Description

Hardware: Raspberry Pi 4 OS: Raspbian w/ All Updates Go Version: 1.15.8 linux/arm

I'm running into a problem using the GPIO library. I'm using an Adafruit rotary switch and I can get the CCW turns working, but not the CW turns.

On the same device with the same switch, I can run a Java implementation using Pi4J, and both clockwise and counter-clockwise work as expected.

I'll explain how it works inline, but the best description is in the section labeled Quadrature Phase Shift Encoding here:

https://www.best-microcontroller-projects.com/rotary-encoder.html

I'm provisioning two pins:

statePin := gpioreg.ByName("GPIO20"),
dataPin  := gpioreg.ByName("GPIO21"),

statePin.In(gpio.PullUp, gpio.BothEdges)
// I tried BothEdges on dataPin with no effect.
dataPin.In(gpio.PullUp, gpio.NoEdge)

Counter-Clockwise Turn

On a Counter-Clockwise Turn, things work as expected. statePin unblocks with an edgeEvent and state=low. Reading the dataPin returns High. statePin then unblocks again, and dataPin returns Low. Here's some output on a switch turn.

2021/08/10 16:31:50 rotaryHandler() unblocked. State: Low  Last State: High Data: Low  Last Data: High time (Nanos):...29 879 957
2021/08/10 16:31:50 rotaryHandler() unblocked. State: High Last State: Low  Data: High Last Data: Low  time (Nanos):...96 961 395

Clockwise Turn

A clockwise turn starts with the state line transitioning to low, and the Data lines SHOULD be LOW. The second event is the state line transitioning to high, and the data line should be HIGH. However, what I'm seeing is the data pin is high on both edge events.

2021/08/10 16:30:36 rotaryHandler() unblocked. State: Low  Last State: High Data: High Last Data: High time (Nanos):...164 553 515
2021/08/10 16:30:36 rotaryHandler() unblocked. State: High Last State: Low  Data: High Last Data: High time (Nanos):...248 856 352

Additional Information

The only thing that I can see is that the time between the transitions is "slightly" longer for a CW turn. Here's a time sample for four turns in each direction. I think the time difference could be explained by the geometry of the switch contacts.

CW Turn

84.5ms 94.1ms 90.5ms 100ms

CCW Turn

66.8ms 62ms 73ms 69ms

I suppose one theory is that for CW turns, the latency to read the data pin exceeds the time that the data pin remains low. For CCW turns, slightly different geometry makes the system work. Unfortunately, I don't have a digital signal storage oscilloscope (yet) that I could use to measure the widths.

I measured the time to execute both .Read() commands and it's around 4-5 ms. Perhaps the issue is a mixture of the geometry and the time for .WaitForEdge() to unblock.

Do you have any ideas on how I could address this? If you'd like, I can post the source that I'm working on. For my use, doing only CCW turns is a work-around, but I would like to figure out how to make it work in both directions.

Thanks.

George

gsexton commented 3 years ago

I think I figured this out. I re-configured the code to only trigger on the rising edge. That seems to be much more reliable.

gsexton commented 3 years ago

If anyone ever stumbles upon this, I did some more investigation. I found my Java implementation was triggering a CCW turn in both directions as well. After a lot of research, I found that the pinout of the rotary encoder was not ACB where A is CLOCK, C is Common, and B is DATA, but ABC. Once I changed the connections to the pi, I was able to get it working correctly.

maruel commented 3 years ago

Glad you found a solution!