Shop Learn
Help reading potentiometer, please! — Parallax Forums

Help reading potentiometer, please!

AwesomeCronkAwesomeCronk Posts: 1,055
edited 2020-01-12 22:38 in Propeller 1
I have been out of the spin game for quite a while. I am now trying to get my model train remote built, but have run into an issue. I want to use potentiometers to serve as menu selectors and a throttle knob. To get them working, I have breadboarded a circuit to charge the cap, wait for it to discharge through the potentiometer, then print out on the LCD how long that took. Attached is my code and a pdf of my circuit.

When I program the prop in RAM, I manage to get the LCD's backlight to come on, as it should. Elsewise, the display does nothing. I suspect it is getting stuck in the look or is printing and clearing too fast for me to see anything.

Prop Mini, Spin1


  • Outputpin P17 are connected direct to ground when Rv in endposition. BIG SMOKE!
  • @feng,
    Could you elaborate on this?
  • Cronk, read Propeller Education Kit Labs Pg131.
  • The example object from that chapter called "TestRcDecay" uses pin 17, the same as your schematic.
      _clkmode = xtal1 + pll16x                  ' System clock → 80 MHz
      _xinfreq = 5_000_000
      pst : "Parallax Serial Terminal"           ' Use with Parallax Serial Terminal to
                                                 ' display values 
    PUB Init
      'Start Parallax Serial Terminal; waits 1 s for you to click Enable button
      ' Configure counter module.
      ctra[30..26] := %01000                     ' Set mode to "POS detector"
      ctra[5..0] := 17                           ' Set APIN to 17 (P17)
      frqa := 1                                  ' Increment phsa by 1 for each clock tick
      main                                       ' Call the Main method
    PUB Main | time
    '' Repeatedly takes and displays P17 RC decay measurements.
         ' Charge RC circuit.
         dira[17] := outa[17] := 1               ' Set pin to output-high
         waitcnt(clkfreq/100_000 + cnt)          ' Wait for circuit to charge
         ' Start RC decay measurement.  It's automatic after this...
         phsa~                                   ' Clear the phsa register
         dira[17]~                               ' Pin to input stops charging circuit
         ' Optional - do other things during the measurement.
         pst.Str(String(pst#NL, pst#NL, "Working on other tasks", pst#NL))
         repeat 22
           waitcnt(clkfreq/60 + cnt)        
         ' Measurement has been ready for a while.  Adjust ticks between phsa~ & dira[17]~.
         time := (phsa - 624) #> 0                
         ' Display Result                                  
         pst.Str(String(pst#NL, "time = "))
         waitcnt(clkfreq/2 + cnt)
  • This is code that I've used in a couple commerical products. Note the values are smaller than yours, because I want to minimize the time to read the pot.
    pub read_pot(p)
    '' Reads RC circuit on pin p
    '' -- takes about 2ms
    '' -- assumes 10K + 0.01uF
      ctra := (%01000 << 26) | p                                    ' ctra in pos detect mode
      frqa := 1
      io.high(p)                                                    ' charge the cap
      phsa := 0                                                     ' clear counter
      dira[p] := 0                                                  ' start discharge
      repeat                                                        ' wait for discharge to finish
      while (ina[p])
      ctra := 0                                                     ' disable ctra
      ' scale 0..100
      return (0 #> ((phsa >> 4) - 37) <# 400) >> 2                  ' return scaled value
  • kwinnkwinn Posts: 8,695
    Could you elaborate on this?

    He is saying that pin 17 is connected directly to ground at one end of the potentiometers travel. That may blow the pin when you set it as an output to charge the capacitor. Put a ~1K resistor between ground and the potentiometer terminal.
  • Ahhh! Thank you! I will do that ASAP!
  • Not to mention what happening when you select P17 as output and make it high directly connected to a 220uF cap with no charge. BIG SMOKE!
  • Search for "sigma-delta ADC" in the forum. Lots of examples for reading analog voltages including schematics and programs.
  • You could always use a MCP3208 ADC. I use these with a INA111 to make a very accurate process meter. And gives you 8 channels
  • RaymanRayman Posts: 12,318
    You can also just use two resistors and two capacitors to make a sigma-delta ADC using two prop pins...

    Like here:
  • I just want to say thank you to everyone here for jumping on and trying to solve the problems at hand. This is the best forum community I have ever seen!
  • I use to try and do that with the propeller and gave up. One of the problems is that the processor creates noise pulses that cause the detection of charge rate to jump around.

    I have since used the MCP3202.

  • RaymanRayman Posts: 12,318
    edited 2020-01-14 17:20
    You do want to average several readings together to get a clean signal...
    An external ADC chip is probably always better.
    But, for just reading a pot, this can work very well.
    Only real drawback is that you have to calibrate the maximum and minimum positions then turn the reading into something like a 0..255 answer.
  • I agree with Ray. In the past I have coded pan/tilt controllers for cameras, and even though we used an ADC I uses a running average approach to smooth the analog input values. In that particular case I took a reading every 5ms and then averaged the last eight readings. By using a buffer size that corresponds to a power of 2, you can use shifting instead of division. Something like this.
    pub read_analog | n
      analogbuf[aidx++] := get_analog_value
      aidx &= %111
      repeat n from 0 to 7
        result += analog[n]
      result >>= 3
    Remember, Spin functions do not have persistent variables so aidx must be global to the object.
  • Another viable approach, given that you aren't really relying on analog performance of the potentiometer for the function, is to use incremental rotary encoders.
    There's an OBEX object that reads two from a single cog.

    Advantages are:
    Prop pins are input only, so very hard to make BIG SMOKE;
    No need to calibrate or average readings, as a step is a step;
    Upon reboot you can ensure the throttle comes up at zero reading, rather than having trains run away on you if people have fiddled with controls with the power off;
    With more involved code you can implement velocity control, where not only the distance turned, but the rate at which you turn the control is relevant;
    Menu selection can be rock solid, rather than risking dithering or needing to allocate hysteresis zones to avoid dithering; and
    Pots can develop annoying, hard to diagnose, patchy response over time, while rotary encoders tend to work until they fail.

    Disadvantages are:
    You generally feel the steps as you turn rather than turning smoothly; and
    You don't have the built-in positional feedback indication that you can have with the knob on a pot.

    Simpler again is a pair of push buttons per control, but rotary encoders can include the debouncing provisions that you would need to add for switches, and I can understand the lack of aesthetic appeal with the push button approach.
  • kwinnkwinn Posts: 8,695
    You might want to consider using optointerrupters to make your own encoders. Simple, reliable, and inexpensive (under $1.00 ea), and only requires 2 per encoder.
  • I will have one or two knobs, one for locomotive selection, maybe one for function selection. Then there will be buttons to pick the loco from the list and execute the function from the list. Then I dreamt up a throttle lever like the hyperdrive control on the millennium falcon.
    I will likely have a system initialization like a drone remote to ensure that throttle is zeroed before sending the signals to the trains.
  • AJLAJL Posts: 457
    edited 2020-01-15 02:10
    I will have one or two knobs, one for locomotive selection, maybe one for function selection. Then there will be buttons to pick the loco from the list and execute the function from the list. Then I dreamt up a throttle lever like the hyperdrive control on the millennium falcon.
    I will likely have a system initialization like a drone remote to ensure that throttle is zeroed before sending the signals to the trains.

    All of this, even the throttle lever, sounds like a good job for rotary encoders, rather than pots.
    You can also get absolute encoders, but they tend to be significantly more expensive and the interface can require more pins.

    Something like this can give you the select button built in too.
  • Given my druthers, I’d be inclined do this project with optical-type encoders, ala a mouse-type arrangement as suggested earlier. But pots should work just fine too. We arent talking about fine resolution. Its a train motor, and I’d be surprised if even a 1/256th step size would yield a discernable change in speed. I bet even a 1/32 fraction would even be overkill.

    People tend to believe a small DC motor is a linear device. Under load, it really isnt, especially at the low end. You may need to scale the speed commands by maybe 3x at the bottom end, and maybe 1x as you get near “full throttle” in order to yield an apparently linear relationship between throttle position and speed.

    Sounds like a fun project! Do post your results!
  • I think I will just use the NMRA’s speed resolution, mostly to simplify coding on my end. I will post the results when it is done!
  • AwesomeCronkAwesomeCronk Posts: 1,055
    edited 2020-01-17 17:53
    Got school off for snow, which gave me some time to work on projects!

    I am thinking I want an RC decay circuit, like the one on page 139 of the book 'Programming and Customizing the Propeller Multicore Microcontroller'. I have attached a schematic of the circuit they use. Only difference is they have theirs rigged up to pin 18. My biggest holdout with this is that I am wanting to use a parallel LCD in the final product and keep my serial one for quick debugging, etc. That means I need to keep other pins to a minimum.
  • JonnyMacJonnyMac Posts: 7,811
    edited 2020-01-17 19:23
    Are you sure you want to use those values? 50K and 220uF give you a time constant of 11 seconds!

    -- insert 100 ohm resistor between pin and cap (limits pin current to safe level)
    -- change cap to 0.01uF
    -- change pot to 10K

    Now the cap will fully charge in in about 5 microseconds, and discharge to the pin threshold (with the pot set to 10K) in under 100 microseconds.

    Use good quality parts and then do a bit of empirical testing. You'll notice that at the end of the code (see read_pot() posted above) I do a bit of scaling so that the method returns 0 to 100.

    Edit: A few minutes later...

    I just opened the book you referenced and the circuit they suggest uses 10K and 0.01uF. They don't use the 100 ohm series resistor like I do. That's okay, you just have to make sure that you don't set the IO pin used HIGH and leave that way for very long, otherwise you could damage the output driver if the pot wiper is connected to ground. A 100 ohm resistor is cheap insurance.
  • Oops, forgot to update the values! I’ll take your advice!
  • Chiming in from the cheap seats:

    Section 7 of the Prop 1 datasheet shows 40mA max per pin so 100 ohm limits the current to 33mA on that pin. Also, the datasheet lists a max current on the power pins of 300mA. The pin currents may need to be planned such that the total of pin currents don't exceed the max allowable for the chip.

    @AC, You can use a parallel LCD display with three prop pins and a shift register chip. There are a few examples of this in the OBEX.

    Whether you use the PropTool or not, it is a good thing to install. The help button will pull up multiple prop documents including the Datasheet, Prop Manual, Prop education kit manual and many other goodies. I still prefer to use BST for spin/PASM.

Sign In or Register to comment.