Shop OBEX P1 Docs P2 Docs Learn Events
BS2 Decimal Math Help! — Parallax Forums

BS2 Decimal Math Help!

RGuyserRGuyser Posts: 90
edited 2006-06-05 02:39 in BASIC Stamp
Hello. I want to use my BS2 for positioning task. I must divide a decimal number representing a distance by a decimal number representing a machine resolution. The code will eventually produce one TTL pulse for each unit of travel(resolution)... I would prefer 3 or 4 decimal places. Also, being able to handle negative and positive numbers would be great!

Please help! I have looked at the FPU unit, but would very much prefer to avoid that if there is a code only solution. I look forward to any advice.

Robert

SO...(Roughly, i am no PBASIC Wiz, and dont have the editor on hand).
main:

DEBUGIN "Enter distance to travel (xx.xxxx), distance, CR
DEBUGIN "Enter Resolution(Duistance/Pulse in xx.xxxx), resolution, CR

distance / resolution = steps

[noparse][[/noparse]Insert code to round steps to nearest whole number] 'Something with the <<, >> command, i figure. no idea! Result is WholeSteps

steps - WholeSteps = tolerance 'this displays how much deviance from the absolute location we achieve based on resolution
DEBUG tolerance, CR 'Display tolerance

Goto Motion

motion:
[noparse][[/noparse]INSERT CODE FOR MOTION CONTROL] 'I will worry about this later. I am more confident here

goto main
end sub

Goto Main
end
«1

Comments

  • Paul BakerPaul Baker Posts: 6,351
    edited 2006-06-01 21:08
    It can be done, just adjust the values to incorporate the fractions. Say you want 3 decimal places, then the value of 1 would be represented by 1000, 4.506 is 4506 etc. Keep in mind that however many decimal places you include, you are taking away from the integer portion. A BS2 uses 16 bit (word) variables, which has a maximum value of 65535, so having 3 decimal places leaves a maximum integer value of 65. To get more you would have to use 32 bit math, heres a guide on doing that: http://www.emesystems.com/BS2math6.htm

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    1+1=10
  • RGuyserRGuyser Posts: 90
    edited 2006-06-01 21:10
    Some clarification...

    So. The purpose of this is to move parts accurately for drilling holes on a drill press.. The actuators are screws driven by steppers.. if the screw takes 20 rotations to move 1 inch, and the steper takes 400 pulses to move one rotation that is:

    1 / (20*400) = .000125 in/step resolution

    Lets say I want to move 1.635 inches:

    1.635 / .000125 = 13080 steps.

    this was easy as 1.635 is divisible by .000125.

    The hardware then outputs 13080 pulses on an IO line, stepping a stepper controller.

    Please Help or Advise!
  • Paul BakerPaul Baker Posts: 6,351
    edited 2006-06-01 21:11
    What is the maximum travel?

    Given your setup, a maximum travel of 8.192 inches can be fit into a 16 bit number. I would keep the units in steps internally, and perform a translation from the input into steps.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    1+1=10

    Post Edited (Paul Baker) : 6/1/2006 9:18:33 PM GMT
  • RGuyserRGuyser Posts: 90
    edited 2006-06-01 21:16
    Quoting Paul:

    "It can be done, just adjust the values to incorporate the fractions. Say you want 3 decimal places, then the value of 1 would be represented by 1000, 4.506 is 4506 etc. Keep in mind that however many decimal places you include, you are taking away from the integer portion. A BS2 uses 16 bit (word) variables, which has a maximum value of 65535, so having 3 decimal places leaves a maximum integer value of 65. To get more you would have to use 32 bit math, heres a guide on doing that: http://www.emesystems.com/BS2math6.htm&quot;



    Ah, I see. So, if I wanted to use .000125 as a constant divisible, I would have to incorporate these 32 bit math 'routines' (or whatever they are). Complex, but I suppose worth it.

    Another question arrises, how do i let the user input a decimal number? (ie 6.742) I think I remember seeing code somewhere that would do this? Ideally I will eventually have an LCD and a keypad matrix for this task... Ideally... In the mean time, I am just trying to figure out the core 'motion-engine'


    Thanks for the quick response!

    Anyone know of any example code i could look at that would answer some of these questions?
  • RGuyserRGuyser Posts: 90
    edited 2006-06-01 21:17
    max travel was expected to be about 8" - scalability would be great though.. upto about 46", the travel of my largest linear actuator components..


    Thanks!
  • RGuyserRGuyser Posts: 90
    edited 2006-06-01 21:18
    another idea i had for inputting the distance to travel was 4 buttons, 2 for up\down, and 2 to select which decimal place to adjust.. again, this is easier for me to do than figure out this 32-bit math stuff...

    i am going to study this link you have provided though - it seems like a hot ticket!
  • Mike GreenMike Green Posts: 23,101
    edited 2006-06-01 21:20
    Can you give more information about the range and resolution of the numbers involved. You will likely represent the distance and resolution as integers. For example, a distance of 29.465 is easily represented as the integer 29465 which fits nicely in 16 bits and works with the standard integer arithmetic operations. If your distances are never more than 63.999 than you can always represent them in 16 bits and things are easier. If your resolution is always less than the distance, all you have to do is divide the two integers to get the step size. If you want to round it, it depends on the range of numbers allowed. You can always check for rounding separately like: (step=distance/resolution: if (distance - (step*resolution)) >= resolution/2 then step = step + 1. This avoids overflow problems.
  • Paul BakerPaul Baker Posts: 6,351
    edited 2006-06-01 21:22
    Ok it you want 46" youll need 32 bits, but if you can get by with 8" for now you can use the word value. It can be a bit akward, but you should use an offset of 8000, or the number of steps per inch. So internally to the system, 1 inch would be 8000, 2 inches would be 16000, etc. So all you'll need is a means for translating the value inputted into steps for the system to understand.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    1+1=10
  • Mike GreenMike Green Posts: 23,101
    edited 2006-06-01 21:29
    You could use double precision with the number of inches in one word and the decimal fraction of an inch in the second word. In adding and subtracting, you just have to check for "inch carry" or "inch borrow". This might make displaying and entering the information easier.
  • RGuyserRGuyser Posts: 90
    edited 2006-06-01 21:31
    Mike:

    After a quick check I have found my resolution will be either .000125 or .0000625 (20tpi / 800 or 1600 steps/rev)

    My main application is for a small X\Ytable with about 6-8" travel in each direction. However, I would like to keep it flexible if possible, if all it takes to allow greater travels is a routine that breaks a large number into 2 16-bit words, that is fine..

    I looked over that 32-bit math article. It is obviousely going to take a bit of time to understand. I probably have to wait till I am at home.. Not in my cubicle!

    Thanks again. When I get home, I can post some links to pictures of the linear slide, for reference...
  • Mike GreenMike Green Posts: 23,101
    edited 2006-06-01 21:32
    You don't really have to have a division routine if you only need to count out steps. Just subtract the step size. When the inches part is zero and the fraction part is less than 1/2 the step size, stop, otherwise output a step pulse.
  • RGuyserRGuyser Posts: 90
    edited 2006-06-01 21:42
    Quoting Mike:

    "You could use double precision with the number of inches in one word and the decimal fraction of an inch in the second word. In adding and subtracting, you just have to check for "inch carry" or "inch borrow". This might make displaying and entering the information easier."

    I am afraid I dont completely understand. This is probably because I am something of a 'newbie' with programming microcontrollers. The whole process of shifting info in and out of xx-bit words is not something I am conversational in. As i understand your suggestion:

    1. Ask for distance to travel as a whole integer. 6.325" would be (6325)
    2. Assume resolution of .000125 - This is stored in a "double precision word"? This double precision word is 2 16-bit locations?
    3. Divide input/resolution


    It turns out with this resolution of .000125, and a movement in xx.xxx places, there is no rounding.. The same would be true for .0000625.
  • RGuyserRGuyser Posts: 90
    edited 2006-06-01 21:44
    Mike - could you be so kind as to provide some 'pseudo-code' for this approach? I think i understand... However, I dont see how I can escape the fact that my resolution is .000000 (6)places or higher!
  • RGuyserRGuyser Posts: 90
    edited 2006-06-01 21:45
    Oh. i understand, I think. It still requires the double precision code... Just the subtract code, not divide...
  • Mike GreenMike Green Posts: 23,101
    edited 2006-06-01 21:48
    With your numbers, you have a total of 32000 steps per inch or 16000 steps per inch. That makes it really easy storing the number of inches in one byte and the number of steps within the inch in a separate word. You can easily display the value at 1600 steps/rev as:
    - LOOKUP steps&%11111,[noparse][[/noparse]0,625,1250,1875,2500,3125,3750,4375,...],xxx
    - DEBUG DEC inches,".",DEC3 steps>>5,DEC3 xxx
    where the LOOKUP gives you the last 4 digits corresponding to the number of steps within 0.001 inch. I only included the first 8 step sizes. You should get the idea. It's similar with 16000 steps per inch.
  • Mike GreenMike Green Posts: 23,101
    edited 2006-06-01 22:03
    Your idea of using 4 switches would work fine. One pair of switches moves from 10s of inches to inches to 0.1, 0.01, 0.001. The next position has 8 or 16 steps depending on the steps per rev selected with some other switch. At 800 steps per rev or 16000 steps per inch, your display code looks like:
    - LOOKUP steps&%111,[noparse][[/noparse]0,125,250,375,500,625,750,875],fract
    - DEBUG DEC2 inches,".",DEC3 steps>>3,DEC3 fract
    At 1600 steps per rev or 32000 steps per inch, it looks like:
    - LOOKUP steps&%1111,[noparse][[/noparse]0,0625,1250,1875,2500,3125,3750,4375,5000,5625,6250,6875,7500,8125,8750,9375],fract
    - DEBUG DEC2 inches,".",DEC3 steps>>4,DEC4 fract
    When you're subtracting the resolution, keep in mind that you have to borrow an inch when there are fewer than 16000 steps or 32000 steps depending on the step size.
  • Mike GreenMike Green Posts: 23,101
    edited 2006-06-01 22:06
    The 20 turns per inch is confusing me. I may be off by a power of two somewhere. I hope you get the basic idea.
  • RGuyserRGuyser Posts: 90
    edited 2006-06-01 22:07
    Mike,

    I think I understand the general idea.. But I need to be at the editor to figure out what this lookup code does. It is not totally obvious to me.

    I cannot tell how the device takes a xx.xxx number and subtracts .000125 from it over and over until 0 using the code you have provided.. I think I need to do about a dozen short learning example programs... Unfortunately I do not compeltely understand the lookup command

    LOOKUP steps&%11111,[noparse][[/noparse]0,625,1250,1875,2500,3125,3750,4375,...],xxx

    this is a mystery to me without loading the documentation.. Something I look forward to doing after my arduous 1.25 hour commute home!
  • RGuyserRGuyser Posts: 90
    edited 2006-06-01 22:11
    the 20tpi refers to the mechanical actuator - in this case a screw with 20 turns per inch... The stepper usually has 200 steps/revolution, but with microstepping techniques, this can be multiplied many times. In my case 2 or 4 times..

    I really wish I understood your response better. I feel dumb and useless; like I am wasting time. I was hoping by asking before I did to much experimentation, I would be able to think this over on the commute and form a plan to quickly bang this out... You and Paul's input has allready helped alot, actually.. I will first work on a simple program that asks the user for 2 decimal numbers and stores them, and retrieves them... Then move onto mathematical operations on these numbers, then try this crazy lookup stuff you recomend..

    My 'style' of programming is to look at as much applicable source code as i can find on any given problem and then hack it all together... this is why i am such a crummy programmer!

    thanks for the help!
  • Mike GreenMike Green Posts: 23,101
    edited 2006-06-01 22:40
    Basically, I'm trying to suggest a way to store your actuator position in such a way that display is relatively easy, data entry is relatively easy, and the actual coding for the function to be performed is easy. Once you told me that there were only two resolutions and they were related (power of two), I thought of storing the position information in two pieces: 1) inches taking a byte, 2) steps within an inch taking a word. Since the step sizes are powers of two in relation to 1/1000 of an inch (0/8, 1/8, 2/8, ... , 7/8) I decided to store this fraction of a 1/1000 inch in the lower 3 bits of the word. The upper 13 bits of the word stores the number of 1/1000 of an inch to the next inch. At the higher resolution, we use 4 bits to store the fractions of 1/1000 inch (0/16, 1/16, ... , 15/16) and that leaves 12 bits to store the number of 1/1000 inch to the next inch. What I gave you was the display code for this. The LOOKUP statement just takes the fraction of 1/1000 of an inch from the lower 3 or 4 bits and converts it to 3 or 4 decimal digits for display purposes. You can display the number of 1/1000 of an inch directly and the number of inches directly as I indicated.

    For entering the data, at the 10's position, just add or subtract 10 to the byte. At the 1's position, just add or subtract 1 to the inches byte. At the 0.1 inch position, add or subtract 100*8 or 100*16 to the "steps" word. At the 0.01 position, add or subtract 10*8 or 10*16. At the 0.001 position, add or subtract 8 or 16. To the right of that position, add or subtract 1 to change the fraction of 1/1000 inch. Obviously, you have to check for a valid number. You can easily tease out the various parts of the "steps" word with simple division and modulus (//) operators and check for limits (0 to 9) before incrementing or decrementing the digit.
  • Mike GreenMike Green Posts: 23,101
    edited 2006-06-01 23:13
    For entering data say at 0.1 inch position (base is set to 8 or 16 depending on the resolution)
    -TenthsPosition:
    - call getKeyPress ' Get a value into key
    - if key = left then OnesPosition ' Previous position
    - if key = right then HundredsPosition ' Next position
    - value = (steps / base) / 100 // 10 ' Get actual digit
    - if key = up then TenthsUp ' Check for up or down
    - if value = 0 then TenthsPosition ' Lower limit
    - steps = steps - (base * 100) ' Decrement
    - goto TenthsPosition
    -TenthsUp:
    - if value = 9 then TenthsPosition ' Upper limit
    - steps = steps + (base * 100) ' Increment
    - goto TenthsPosition
  • Mike GreenMike Green Posts: 23,101
    edited 2006-06-01 23:19
    For entering data at fractional 1/1000 position
    -FractPosition:
    - call getKeyPress
    - if key = left then ThousandthsPosition
    - if key = right then FractPosition ' No next position
    - value = steps // base
    - if key = up then FractUp
    - if value = 0 then FractPosition
    - steps = steps - 1
    - goto FractPosition
    -FractUp:
    - if value = base-1 then FractPosition ' Upper limit
    - steps = steps + 1
    - goto FractPosition
    Note: I'm assuming that getKeyPress displays the value in inches,steps when it's first entered so the display is up to date.
  • Mike GreenMike Green Posts: 23,101
    edited 2006-06-01 23:34
    Actually stepping the actuator is easy.
    -StepLoop:
    - if inches = 0 and steps = 0 then allDone
    - if steps > 0 then noBorrow
    - steps = base * 1000
    - inches = inches - 1
    -noBorrow:
    - steps = steps - 1
    - call stepTheMotor
    - goto StepLoop
  • RGuyserRGuyser Posts: 90
    edited 2006-06-02 01:12
    Mike. Thank you! I appreciate the responses. Because I am not super versed in programming, I figure I must either work diligently for a bit and wait untill I really cannot go further, or ask some specific questions which makes me feel lazy and irritating! However, I am pushing ahead with the questions. You got way ahead of me, so let me ask some specific questions! Thank you, oh so very much.

    I am trying to get this to work, ive got:

    distInch VAR Byte
    distDeci VAR Word

    Main:

    DEBUG CR,"Distance To Travel(x inches): ",CR
    DEBUGIN DEC2 distInch
    DEBUG "Distance .(xxx decimal): ",CR
    DEBUGIN DEC4 distDeci
    PAUSE 250
    DEBUG CLS, "Distance to travel: ",DEC2 distInch,".", DEC3 distDeci
    PAUSE 500

    GOTO main
    END

    Which seems imperfect. Is there a better way to fill the initial variables?

    After this initial gathering of data, using your method, I am supposed to: "Since the step sizes are powers of two in relation to 1/1000 of an inch (0/8, 1/8, 2/8, ... , 7/8) I decided to store this fraction of a 1/1000 inch in the lower 3 bits of the word. The upper 13 bits of the word stores the number of 1/1000 of an inch to the next inch."

    I dont really follow. I am tempted to follow this method because it sounds like it will use much less code and therefor make integrating an LCD and keypad matrix alot easier in the long run than the 32-bit math stuff. However, I dont understand the exact mechanics of what you are recomending..

    I am going to study your replys more and attempt to understand.. any further assitance is greatly appreciated.
    if you are a curious fellow, you can see some of the sorts of stuff i am into www.robertguyser.com.... this explains why i am so programmatically challenged, but compelled to keep trying..
  • RGuyserRGuyser Posts: 90
    edited 2006-06-02 02:40
    Wow. I think I just realized a few things... But the main thing is: If it takes 8000 steps to move an inch, and 8 steps(at .000125 each, or 400 pulses/rev) to move .001, then all i need is (total number of thousandths) * 8 to move any xxx location.

    To do inches, it is total inches * 8000

    So...

    Get inches
    Get thous
    steps= (inches * 8000) + (thous * 8)

    Hurrah... I think... Am I missing anything?
  • Mike GreenMike Green Posts: 23,101
    edited 2006-06-02 02:45
    OK What you have will get you started with a number of inches in distInch and a number of 1/1000 inch in distDeci. What I've proposed for you is a way to enter the decimal part in units of 0.000125 inch or 0.0000625 inch and to display this easily and use this to issue stepper pulses. The Stamp, like most computers, works with integers. In this case, the Stamp "knows" about 8 bit integers and 16 bit integers. Anything else has to be constructed. I've divided the numbers you want to work with into 3 parts. You already understand about keeping the number of inches and the fractional inches separately. Does it make more sense if I stress that the fractional part is in units of 0.000125 or 0.0000625 inches? If we're using units of 0.000125 inches, 8 such units makes one unit of 0.001 inch. If I divide such a fraction by 8, I get units of 0.001 inch. What I'm calling "steps" is in units of 0.000125 inches. I divide "steps" by 8 to get a value between 000 and 999 which I can display with "DEBUG DEC3 steps/8". I take the remainder of the division by 8 which is in units of 0.000125 inch and use it to look up numbers that will display the remaining decimal digits of the actual inch value. I don't have to do any floating point or complicated arithmetic to make things look right on the display. The same ideas apply when I use units of 0.0000625 inch. Does this help?
  • Mike GreenMike Green Posts: 23,101
    edited 2006-06-02 02:54
    We're kinda crossing postings. Because of the 16 bit limitation on arithmetic, you have to limit the size of numbers. It's easy to separate the whole inches from the fractional inches. The double precision notion is that you do subtraction by 0.000125 in two parts. First you check "steps" to see if you have to borrow an inch from "inches". If not, then you subtract 1 (unit of 0.000125) from "steps". You borrow an inch by subtracting 1 from "inches" and adding an inch (in units of 0.000125) to "steps". Since "steps" was already zero, you can just set "steps" to 8000, then go subtract the 1.
  • RGuyserRGuyser Posts: 90
    edited 2006-06-02 03:00
    working Code!!! Thanks for the help guys! It was so simple in the end.

    Now I will attempt to figure out how to use a keypad matrix and LCD... I suppose that will actually be harder than this was. Any hints on how to break the word-length barrier of the basic stamp so I can get travels above 7 inches?



    ' {$STAMP BS2}
    ' {$PBASIC 2.5}

    inches VAR Byte
    thous VAR Word
    steps VAR Word
    total VAR Word
    x VAR Word
    xxx VAR Word

    main:
    DEBUG CLS,"Distance to Travel 7 inches max! (x.xxx): "
    DEBUGIN DEC1 inches ' Read in inches to travel
    DEBUG "." ' after inches entered, display a decimal point
    DEBUGIN DEC3 thous 'read in thous

    DEBUG CR,"Total: ", DEC1 inches,".",DEC thous,CR 'display distance to go
    DEBUG "Whole Inches: ", DEC1 inches, CR
    DEBUG "Thousandths: " ,DEC3 thous, CR

    x= inches * 8000
    DEBUG "x steps:", DEC5 x,CR
    xxx= thous * 8
    DEBUG "xxx steps:", DEC5 xxx, CR
    total = x+xxx

    DEBUG "Total Steps to Move: ",DEC total, CR
    PAUSE 5000

    GOTO main
    END
  • Mike GreenMike Green Posts: 23,101
    edited 2006-06-02 03:07
    Keeping the inches and thousandths of an inch separate as I suggested will allow you to go to 255 inches. Don't try to make a total number of steps to move. Just move 8000 steps for each inch, then move 8 steps for each thousandth of an inch.
  • Mike GreenMike Green Posts: 23,101
    edited 2006-06-02 03:11
    Try:
    i var word
    j var byte
    ...
    for j=1 to inches
    for i=1 to 8000
    call stepOneStep
    next
    next
    for i=1 to thous
    for j=1 to 8
    call stepOneStep
    next
    next
Sign In or Register to comment.