Read a voltage and scale it to 0-3.60 volts via D to A or PWM?

radiogareth

Senior Member
I'm making a small 12V DC powered aerial rotator. The mechanical part drives a potentiometer through 280 degrees (of its 300 degrees possible travel) as itself rotates 0-360 degrees. The 10 degrees 'spare' at each end of the potentiometer is to save it from end-stop damage in the event of minor overshoot.
I'd like to display the 'Degrees direction' via a voltmeter, i.e. representing 0-360 degrees from an output of 0 - 3.60 volts.
By using readadc10 I can get plenty of resolution of the actual position and in software I can provide a raised 0 and lowered 360, adding code to stop the motor if either are exceeded, ie turn motor drive off and light a RED led.
The DAC command seems to only offer 32 steps, not accurate enough really. Some of the forum posts show using PWM for improved accuracy but the PWM wizard only offers steps of 1%, ie that would represent 3.6 degrees? Is there a way round that? Or could I just use a simple pause loop like pause 500 high 1 pause 500 low 1 =50% pwm. With a bit of RC integration I'd end up with a stable voltage that would follow the potentiometer (20 seconds rotation 0-360)
I really wanted the visibility of the big red LED voltmeter as a display, rather than another LCD 20*2 display.
Thoughts and comments welcome...
Gareth
 

hippy

Technical Support
Staff member
With a PWMOUT period of 250, duty can go from 0 to 1000, which is an effective resolution of 0.1%.

It should then just be an issue of reducing the 100% PWMOUT voltage by a resistor divider to 3.6V or one could adjust the PICAXE power rail until 100% does equal 3.6V.

But, given you describe it as a "big red LED voltmeter", I'm guessing it's got a 7-segment display. If so then I would suggest looking for a cheap 7-segment display module which can very easily be controlled from a PICAXE without all the palaver of creating an analogue voltage.

One example pulled from the WWW -

https://hobbycomponents.com/displays/597-max7219-8-digit-seven-segment-display-module

An 8-digit display would allow a 'desired setting' and a 'where it's pointing' indication. There may be some with different colours per 4-digit pairs, and some purely 4-digit displays which could be used, and perhaps some with larger digits, or you could build your own.
 

radiogareth

Senior Member
Thanks Hippy, that first line opens the lock :) A quick google does of course lead me to Picaxe code that drives the suggested LED display. Certainly a nice solution with a whole host of options possible....that I wasn't considering.
Onward..... :cool:
 

erco

Senior Member
I like your thinking, radiogareth. A few years ago I posted about using a dollar voltmeter with PWM to save money and output pins:

 

AllyCat

Senior Member
Hi,

You might need to take a little care with the PICaxe maths in converting a 10 bit value down to 7 bits (0 - 360). Tip: you may find the ** operator helpful.

Erco's original thread inspired me to write a Code Snippet on the topic, and in the process discovered a relevant bug in the PWM generation of (just) the 20M2.

Cheers, Alan,
 

radiogareth

Senior Member
All very interesting reading.....I'm hoping a combination of code from above pointers and adjustable 'padding resistors' at the top end (ie towards 5 volts so the 3.6V/360 degree point can be preset) may provide a solution. Although 1 degree accuracy would be nice, the antenna(s) have a beamwidth of 50-5 degrees (depending on what band I'm on) so its not essential.....
Thanks for the pointers....
 
Last edited:

AllyCat

Senior Member
Hi,

Sorry, a slip of the finger (or brain), 360 is of course around 8.5 bits. The point being that a simple multiplication or division will generally involve the sum of the number of bits, which exceeds the basic 16 bits internal resolution of the PICaxe. ** uses effectively a two stage process with a 32 intermediate value.

Cheers, Alan
 

radiogareth

Senior Member
Before I sit down and attempt the maths, I've remembered that the A to D can have different reference voltages used, including one of your own. I'm hoping for a 'sweet spot' to appear whereby the input voltage range (which I can configure to be whatever I want within the supply rails) and the A to D reference voltage (also entirely variable) ends up with a nice simple piece of maths (not my strong point) to control the PWM. Accuracy of 1 degree would be nice but equally 360/256 (~1.4 degrees) would not make any real difference in the real world. Build a test circuit and play will be easier for me I expect.....
 

AllyCat

Senior Member
Hi,

The ADC Reference voltage is not really the issue, changing that will probably make matters more complex (e.g. because the pot output could go "over-range"). At the moment, both the Pot and ADC work with "ratiometric" values (proportional to the supply rail) so the supply voltage is largely unimportant (cancels out).

If you really want a "hardware" calibration method, then put a trimming Pot in series with the main Pot. But the conversion/calibration maths is not too difficult and I'm sure somebody here will help out if needed.

The maths in the Code Snippet that I linked above may look rather overwhelming, but I had set a very stringent target: that the DVM should display exactly the same 8 bit value (0 - 255) as contained in the byte variable. This means that to display a value of (say) "123", then the DAC voltage must be between 1.230 and 1.239 volts or nominally 1.235 v (assuming the DVM doesn't itself do any "rounding" above 1.235 v) under all circumstances. So the calculation must be accurate to (much) better than 5 mV (in > 2.6 volts), or <0.2 %. But if just one digit error is acceptable, the maths can be much simpler.

Cheers, Alan.
 

radiogareth

Senior Member
Some progress today as the voltmeter module arrived from eBay. First problem is it doesn't display real voltage below 0.3 V, it jumps direct to 0.000. Maybe the really cheap modules will do so when they arrive.
Having given further thought to the necessary resolution its not actually worth pursuing beyond 256/360 degrees as even if I end up with a very narrow (microwave freq) beam it would still be 'swept' either side of the signal source so once a general direction has been determined, the rest is done by ear/eye anyway.
My initial code and hardware explorations have shown the following: My padding voltages (10 degree 'no-go' zone at each end of pot travel) give target voltages of into the ADC of 0.1666V and 4.666. (5 volt supply from 78L05).
My thinking is as follows: Subtract 3 from whatever value the A2D gets (0.1666/5.0) ~3. That gives zero when I'm still actually 10 potentiometer degrees off the bottom end. This works, but it does of course 'ROLL OVER' and gives 255,254,254. Not sure how I can stop that happening and MIN or MAX won't, I think, help.
For the top end, I need to find simple divide and multiple numbers that feed to PWM such that 4.666V gives a PWM equivalent of 3.6 volts. That can be pretty much any numbers that fit.
Onwards, but mostly sideways at the moment......
Gareth
 

lbenson

Senior Member
My thinking is as follows: Subtract 3 from whatever value the A2D gets (0.1666/5.0) ~3. That gives zero when I'm still actually 10 potentiometer degrees off the bottom end. This works, but it does of course 'ROLL OVER' and gives 255,254,254. Not sure how I can stop that happening and MIN or MAX won't, I think, help.
Not sure what kind of "help" you're looking for, but why wouldn't "whatever value" MIN 3 - 3 always give you an answer of 0 or greater, never 255, 254, or 253 regardless of what the value of "whatever value" is (assuming a 1-byte value with a maximum of 255)?
 

hippy

Technical Support
Staff member
If you can let us know what READADC10 value you get for zero degrees and what you get for 360-degress - or for whatever particular degree positions you have near the extremes - then I am sure you will start a race for members to come up with good and accurate means of converting the pot position and reading to 0 to 360 degrees, which can then be converted to SERTXD text, for LCD or 7-Segment display, or the analogue meter.
 

radiogareth

Senior Member
Well well, having wired things up to the ACTUAL hardware it seems that the first 10-15 degrees (exact to be confirmed) of pot shaft travel don't change the V out anyway! In fact to get a representation of the full 360 degree rotation I'm going to have to change my gearing (cue up the CAD and 3D printer). So the control software can simply stop the motor if A2D reads 0 or 255. All that remains is to output a representative PWM such that FSD = 3.6 volts (on a 5 volt supply rail) or just use a voltage divider to cull it as required. With luck my cheapo DVMs will display down to 0 volts and being 3 digits only (Not sure yet if they auto-scale as sold as 0-30V units so maybe not in which case they go into the 'one day' box for other duties). The option remains to drive a good old analog meter, or which I have a lot. Or, ignoring the digital output option altogether, put a calibrated dial on the target pot, ie the user sets the desired direction with a target pot and the Picaxe drives the motor as required until Vi_target = Vi_Feedback. At least the Picaxe can add PWM start/stop and transit speed adjust, plus end approaching warning noises/lights like a reversing sensor......and idiot lockout is easier in software too.....
 

radiogareth

Senior Member
Other things called today but this evening I've had a look at other potentiometers in the same range and other better quality units. It seems I have a 'got what I paid for' (ie cheap) unit. Better quality units do have a small'dead-zone at each end of their travel but far less degrees of dead (cheap unit had 16 at one end and 18 at the other). This is actually handy for my current gearing at 28:36) The one I've just tested gives pretty much a change for the full 300 degrees of pot rotation.
I don't want the precision offered by anything optical. This device needs to be short term outdoors reliable and cheap too.
My next step is to reprint the pot drive gear to suit the new pot shaft. Then I'll try using a 'set-target-degrees' pot and get the software to track the motor via a L298N module until the two agree (feedback value = target value). No digital or analog meter needed, just a big pointer on my set target pot and a scale. If I'm lucky with the hardware I'll get slightly over 360 degrees rotation but be able to stop before the mechanical end of the pot track.
Work in progress......
Thanks for comment, its good to talk :)
 

premelec

Senior Member
I have worked with a variety of pots including ones that were sensing position. usually wire wound - some single turn with no stops and very small dead zone. That said perhaps a 3 turn pot could work well for you - you just work with 1/3 rotation total at 360 deg... I haven't looked at the current market - there used to be a lot surplus. I have a pile of 10T a few 15T. The servo types used to have ball bearings for long life... Hope you have found you need what... good luck. [BTW years ago we determined that AB molded carbon track pots got quieter with use rather than the usual noisier with the thin carbon tracks]
 

radiogareth

Senior Member
I'm pleased to report that its up and running and by a fortuitous coincidence of several things, works like a dream. I appreciate the software contains duplicity and could be tidied up but as-it its exactly what I wanted. Even my 'guess the PWM divisor/multiplication' follows within a degree or two! Mechanical gearing of 36-tooth on the rotor shaft driving 26-tooth on the pot. This leaves 15-20 degrees dead-zone on the pot, both electrical and before the mechanical end is reached and conveniently provides the full 0-255 range indication for the 360 of rotor shaft rotation. The rest is just re-measuring and controlling the L298N for enable and direction. Both sections of the chip are in parallel as the motor is a bit hungry on 12V. When its mechanically and electrically finished I'll add a youtube link of it working.

#No_data 'Simple rotator control software
#PICAXE 14M2 'Processor
Main:
readadc b.1,b0 'Feedback
readadc b.3,b3 'Target
'debug
let b2=b0*8/11 'PWM divisor for 360=3.6Volts
pwmout b.2, 63, b2' Signal for DVM
if b0=b3 then goto main 'if no change go and check
If b0 > b3 then gosub Turn_Right
If b0 < b3 then gosub Turn_Left
goto Main

Turn_Right:
High c.0 'Enable for L298N
High b.4 ' One way
Low b.5 'One way
do until b3=b0 'Until F & T match
readadc b.1,b0 'Read again....
readadc b.3,b3 'Read again....
let b2=b0*8/11
pwmout b.2, 63, b2' Signal for DVM TBC
loop 'Repeat until conditions met
low b.4, c.0 'Stop and disable
Return 'Go back from whence

Turn_Left:
High c.0
High b.5
Low b.4
do until b0=b3
readadc b.1,b0
readadc b.3,b3
let b2=b0*8/11
pwmout b.2, 63, b2' Signal for DVM TBC
Loop
low b.5,c.0
Return

goto main ' Just in case....
 

radiogareth

Senior Member
Of course, a night of thinking about it produces yet more minor tweaks that could improve the operation. On 12 volts it tends to overshoot the target and hunts back. This would be made worse with the mechanical inertia of a real beam being turned round too. So I added PWM to the enable pin of the drive chip L298N, itself fed by another POT to give it the speed value which helps that. If I was clever the software could decrease the PWM drive to the drive signal as the feedback value approaches target value.
Also depending on how close to the target value it gets, there is understandably a little 'jitter' (just like RC servos do) so I need to add a 'resting place' whereby it all stays off unless a significant change (say +/-2 or 3 ) occurs. And a bi-colour LED to show running/stopped, because I have spare pins :cool:
 

radiogareth

Senior Member
I'm trying to add some 'slow down when near the target' code as follows...

readadc b.1,b0 'Read again....
readadc b.3,b3 'Read again
if b0-b3 <=5 then gosub slower 'Loads the PWM driver with a slower speed
pwmout pwmdiv16, c.0, 249,W4 'Word variable used as number over 255

but the syntax fails underscoring b3? I've read the guidance but can't see why its not a legitimate 'sum'? Any ideas?
 

eclectic

Moderator
Try something like

Code:
readadc b.1,b0 'Read again....
readadc b.3,b3 'Read again

b10 = b0 - b3

if b10 <=5 then gosub slower 'Loads the PWM driver with a slower speed
 

radiogareth

Senior Member
Thanks, that's sorted it, I now have a nice smooth 'slowing down buzz' as it approaches the set target.
My remaining problem is finding a way to stop 'jitter' when one or the other pots is just at the transition between values. I need to create (ideally a software) 'dead-zone' of a couple of bits either side that are ignored until there is a difference GREATER than a couple of bits.
So say target = 90. Feedback is 89/89.6 so there is an occasional twitch. Ignore twitch until say 88 or 92 is read by target then it kicks off again.
What I really want is a 'wait until a postive or negative difference is greater than' command. Is that even possible?
 

lbenson

Senior Member
What I really want is a 'wait until a postive or negative difference is greater than' command. Is that even possible?
This is a problem I face with temperature readings--how to avoid excessive dithering when a temperature is right on the boarder.

Something like this may work for you (with byte variables).

diff = oldreading - newreading
if diff > 1 and diff <255 then ' . . . positive or negative difference greater than 1
 

AllyCat

Senior Member
Hi,

You have to be a little careful with negative values "wrapping around" and being treated as large positive values. But you can also make that a simplification, for example:
Code:
diff = oldreading - newreading + 2      ; Can use bytes or words
if diff > 4 then  .....                  ; The difference is more than +/- 2
Cheers, Alan.
 

hippy

Technical Support
Staff member
What I really want is a 'wait until a postive or negative difference is greater than' command. Is that even possible?
Yes. The easiest way is to track if a change was going up or going down, and then, for going up, allow any change which increases it further, only allow larger decreases to change direction, similar when going down. There are some limitations with that, such as not always being able to reach 0 or 255 or 1023 without more complicated code.

A better solution is to keep an average and then move the value you are using towards that by some amount on each loop, the amount being proportional to how far from the average one is. I believe that's the basis for PID type control.

Simplistic implementation -
Code:
ReadAdc C.0, average
Do
  ReadAdc C.0, new
  average = average + new / 2
  Select Case current
    Case < average : current = current + 1
    Case > average : current = current - 1
  End Select
  Gosub UseCurrent
Loop
 

radiogareth

Senior Member
Thanks for the prompts and neat options: I've added the code Alan suggested and that works nicely, leaving the motor comfortably off unless given a meaningful tweak on the target pot. SMall changes use the PWM drive (so gently), larger commanded excursions go full speed. I'd post the code FWIW but can't remember how to 'enclose' it, neither did my search find how :-(
Gareth (very grateful :)
 

radiogareth

Senior Member
Thanks :)
Here's what I'm running at the moment. Just boxing it up ready for a real world trial.
Gareth
Code:
#No_data 'Simple rotator control software by Gareth, G4XAT. 12VDC Worm drive motor
#PICAXE 14M2 'Processor : driven via L298N doubled up.
Main:
    readadc b.1,b0     'Feedback potentiometer driven via gear off mainshaft
    readadc b.3,b3    'Target Pot on xontroller box with scale
    high c.4    'ready/running LED (flashing = ready, on = busy/running
    pause 100    'Flash delay
    b5=b0-b3+2
    'debug used to check variable behaviour
    low c.4
    pause 100
    If b5 > 4 then goto Point
    goto main
   
Point:
    readadc b.1,b0     'Feedback
    readadc b.3,b3    'Target
    'debug   
    let b2=b0*8/11    'PWM divisor for 360=3.6Volts
    pwmout b.2, 63, b2' Signal for DVM
    if b0=b3 then goto main    'if no change go and check
    If b0 > b3 then gosub Turn_Right
    If b0 < b3 then gosub Turn_Left
    goto Point
   
Turn_Right:
    High c.4        'On LED
    High c.0        'Enable for L298N (other half of chip hardwired in //el
    pwmout pwmdiv16, c.0, 249, 1000'sets up 250Hz PWM @100% (speed control)
    High b.4        ' One way
    Low b.5        'One way
    do until b3=b0    'Until F & T match
    readadc b.1,b0    'Read again....
    readadc b.3,b3    'Read again
    b10=b0-b3
    if b10>=5 then gosub faster
    if b10 <=4 then gosub slower 'Loads the PWM driver with a slower speed
    pwmout pwmdiv16, c.0, 249,W4    'Word variable used as number over 255
    let b2=b0*8/11
    pwmout b.2, 63, b2' Signal for DVM with 100nF across input
    loop            'Repeat until conditions met
    low b.4, c.0    'Stop motor and disable driver chip
    pwmout c.0,off    'Stops pwm
    low c.4        'LED off (goes back to fast flashing after 1 second
    wait 1        'So motor isn't instantly reversed (bad for PM Motors)
    Return        'Go back from whence
   
Turn_Left:   
    High c.4
    High c.0
    pwmout pwmdiv16, c.0, 249, 1000'Helps limit overshoot/hunting
    High b.5
    Low b.4   
    do until b0=b3
    readadc b.1,b0
     readadc b.3,b3
     b10=b3-b0
     if b10>=6 then gosub faster
    if b10 <=5 then gosub slower'Loads the PWM driver with a slower speed
    pwmout pwmdiv16, c.0, 249,W4    'Word variable used as number is over 255
     let b2=b0*8/11 'Maths for scaling 0-5 to 0-3.6 - close enough :-)
     pwmout b.2, 63, b2' Signal for DVM
    Loop
    low b.5,c.0    'stops and turns thigs off
    pwmout c.0,off ' stops the pwm 4 sure
    Low c.4
    wait 1'slight delay to stop user wrecking the PM motor
    Return
       
slower:
    W4=600    '1000 = full power
    return
       
Faster:
    W4=1000
    return
           
    goto main ' Just in case.....
 
Top