Tripling A Picaxe Code

Gramps

Senior Member
This code (below) runs one motor perfectly.
We want to use this code to service three motor controllers.
The new pin numbers would be as follows.

MotorCon (original code)
Symbol desired_pot = 13 'B.5
Symbol feedback_pot = 11'B.4
Symbol MOTOR = B.0' Energize PWM
Symbol Direction = B.7

MotorCon2
Symbol desired_pot 2= 9 'B.3
Symbol feedback_pot2 = 8 'B.2
Symbol MOTOR2 = C.2' Energize PWM
Symbol Direction2 = B.6

MotorCon3
Symbol desired_pot3 = 10 'B.1
Symbol feedback_pot3 = 19' C.7
Symbol MOTOR3 = C.1 ' Energize PWM
Symbol Direction3 = C.6

We added a "2" and a "3" to each entire set of symbols, and that passes syntax.
Now, must we change all the variables?
Question.
In the first symbol:
symbol xb0 = b0 ' reserved for bit variables
I get a syntax error when changed to:
symbol xb8 = b8 ' reserved for bit variables
Why? Does that symbol need to remain as a stand-alone for all three motor controllers?

Code:
'Shadow-Bot feedback control code with limits.
#picaxe 28x2
#no_data
#no_table

symbol xb0 = b0 ' reserved for bit variables
symbol bReverseForward=bit1 ' 0=reverse,=forward
symbol bMotorState=bit0     ' 0=off,1=on
symbol REVERSE_=0
symbol FORWARD_=1
Symbol desired_pot_value = b1
Symbol feedback_pot_value = b3
Symbol diff=b4
Symbol  desired_pot = 13 'B.5
Symbol  feedback_pot = 11'B.4
Symbol MOTOR = B.0' Energize PWM
Symbol Direction = B.7
Symbol MAXPWMVALUE = 399
Symbol bSlowState=bit2
Symbol old_feedback_value= b5
Symbol old_desired_value= b6
Symbol ENDSTOP_A  = 35 ; desired_pot MINimum value
Symbol ENDSTOP_C = 210 ; desired_pot MAXimum value
;Lower shoulder min=35 max=210
;Upper shoulder min=?? max=???

Main:
  Readadc Desired_pot, desired_pot_value
    Desired_pot_value = Desired_pot_value MIN ENDSTOP_A MAX ENDSTOP_C
    'sertxd (#desired_pot_value," ",#b1,13,10)
    Readadc feedback_pot, feedback_pot_value
   ' sertxd (#feedback_pot_value," ",#b3,13,10)
  'sertxd("Desired/Feedback: ",#desired_pot_value," ",#feedback_pot_value,13,10)
  If old_feedback_value<>feedback_pot_value or old_desired_value<> desired_pot_value then
  old_feedback_value=feedback_pot_value
  old_desired_value=desired_pot_value
  sertxd("Desired/Feedback: ",#desired_pot_value," ",#feedback_pot_value,13,10)
endif
if desired_pot_value > feedback_pot_value then
   diff=desired_pot_value - feedback_pot_value
   else
   diff=feedback_pot_value - desired_pot_value
endif
if diff < 12 then ' from 11 below a match to 11 above
  if bMotorState=1 then
    if diff > 4 then ' this value may need to be set experimentally
      if bSlowState = 0 then
        bSlowState = 1 ' note that we are in the "slow speed" state
        pwmout MOTOR, 199, 300 ' slow down to half speed (change to 100 if necessary)
      endif
    else
      pwmout MOTOR, OFF ' STOP
      bMotorState=0
      bSlowState = 0
    endif
  endif
else ' motor must be set for proper direction and then activated
  if  desired_pot_value > feedback_pot_value then
    if bReverseForward = REVERSE_ then
      high Direction ' go forward
      bReverseForward = FORWARD_
    endif
  elseif  desired_pot_value < feedback_pot_value then
    if bReverseForward = FORWARD_ then
      low Direction ' go in reverse
      bReverseForward = REVERSE_
  endif
  endif
  if bMotorState=0 then
    bMotorState=1
    pwmout MOTOR, 199, MAXPWMVALUE' rotate Reverse
  endif
endif

goto main
 

lbenson

Senior Member
symbol xb8 = b8 ' reserved for bit variables

Does not produce a syntax error for me, but it will not do what you intend, because there are no bit variables contained in b8--only b0-b3 have bit variables, numbered bit0-bit31.

But you've only used bit0 and bit1 in b0--you have 6 more bit variables that you can use to provide, for instance, bReverseForward2, bReverseForward3, bMotorState2, bMotorState3.

You only need to replicate for each motor the unique variables which are required by the original motor.

It might be possible to combine some operations so that one block of code could handle all 3 motors, but you will probably find it easier to understand and easier to debug if you use three blocks of code. You might have a structure like this:
Code:
main:
  do
    gosub controlMotor1
    gosub controlMotor2
    gosub controlMotor3
  loop

controlMotor1:
  Readadc Desired_pot, desired_pot_value
    Desired_pot_value = Desired_pot_value MIN ENDSTOP_A MAX ENDSTOP_C
    'sertxd (#desired_pot_value," ",#b1,13,10)
    Readadc feedback_pot, feedback_pot_value
   ' sertxd (#feedback_pot_value," ",#b3,13,10)
  'sertxd("Desired/Feedback: ",#desired_pot_value," ",#feedback_pot_value,13,10)
  If old_feedback_value<>feedback_pot_value or old_desired_value<> desired_pot_value then
  old_feedback_value=feedback_pot_value
  old_desired_value=desired_pot_value
  sertxd("Desired/Feedback: ",#desired_pot_value," ",#feedback_pot_value,13,10)
endif
if desired_pot_value > feedback_pot_value then
   diff=desired_pot_value - feedback_pot_value
   else
   diff=feedback_pot_value - desired_pot_value
endif
if diff < 12 then ' from 11 below a match to 11 above
  if bMotorState=1 then
    if diff > 4 then ' this value may need to be set experimentally
      if bSlowState = 0 then
        bSlowState = 1 ' note that we are in the "slow speed" state
        pwmout MOTOR, 199, 300 ' slow down to half speed (change to 100 if necessary)
      endif
    else
      pwmout MOTOR, OFF ' STOP
      bMotorState=0
      bSlowState = 0
    endif
  endif
else ' motor must be set for proper direction and then activated
  if  desired_pot_value > feedback_pot_value then
    if bReverseForward = REVERSE_ then
      high Direction ' go forward
      bReverseForward = FORWARD_
    endif
  elseif  desired_pot_value < feedback_pot_value then
    if bReverseForward = FORWARD_ then
      low Direction ' go in reverse
      bReverseForward = REVERSE_
  endif
  endif
  if bMotorState=0 then
    bMotorState=1
    pwmout MOTOR, 199, MAXPWMVALUE' rotate Reverse
  endif
endif
  return

controlMotor2:
  return

controlMotor3:
  return
Then you can comment out two of the GOSUB statements and get each motor working before uncommenting and running all three.
 
Last edited:

lbenson

Senior Member
Is this an example of a unique variable?
Symbol old_feedback_value= b5
Yes. You need a similar variable for each of the 3 motors.

What are bit variables?
Variables which consist of a single bit, and so can have only the values of 0 and 1 (sometimes called "flag" variables). The first 4 bytes of variables, b0-b3 (w0 and w1) have addressable bits, bit0-bit31. You have used 2 of them with these definitions:
Code:
symbol bReverseForward=bit1 ' 0=reverse,=forward
symbol bMotorState=bit0     ' 0=off,1=on
The statement, "symbol xb0 = b0 ' reserved for bit variables" is meant to provide documentation of the intended (possible) use of the bit variables bit0-bit7 which are the constituent bits of the variable, b0. Neither b0 nor xb0 should be used in the program when you are using the bit variables bit0-bit7. If you need more than those 8 bit variables, the constituent bits of b1, b2, and b3 are also available (but then you must not use b1, b2, b3 or w0 or w1).

See Manual 2, "Variables - General".
 

Gramps

Senior Member
Yes. You need a similar variable for each of the 3 motors.
So we could use:
Symbol old_feedback_value= b5 'original motor1
Symbol old_feedback_value2= b13 'motor 2
Symbol old_feedback_value3= b15 'motor 3

The statement, "symbol xb0 = b0 ' reserved for bit variables" is meant to provide documentation of the intended (possible) use of the bit variables bit0-bit7 which are the constituent bits of the variable, b0.
OK, so how would these be changed to service motor 2's code?

Like this?
symbol bReverseForward2=bit2 ' 0=reverse,=forward
symbol bMotorState2=bit3 ' 0=off,1=on
 

lbenson

Senior Member
Right. For the sake of those of us with programmers OCD, you might change "=forward" to "1=forward" to more precisely document the meanings of the 0 and 1 values for that variable.
 

Gramps

Senior Member
Found the answer to my question on Google, of course.LOL
programmers OCD
The OCD perfectionist programmer
When you do finally receive the finished product you will have no option but submit to the stunning glory and radiant beauty of perfectly formatted, no, perfectly beautiful code, that is so efficient that anything you would want to do to it would do nothing but defame a masterpiece..
 

Gramps

Senior Member
Please check my work.
Note I'm still not sure about the single bit reserve.
Code:
controlMotor2:
'symbol xb0 = b0 ' reserved for bit variables
'( symbol xb3 = b3????)

symbol bReverseForward2=bit2 ' 0=reverse,1=forward
symbol bMotorState2=bit3 ' 0=off,1=on
symbol REVERSE2_=0
symbol FORWARD2_=1
Symbol desired_pot_value2 = b7
Symbol feedback_pot_value2 = b8
Symbol diff2=b8
Symbol  desired_pot2 = 9 'B.3
Symbol  feedback_pot2 = 8'B.2
Symbol MOTOR2 = C.2' Energize PWM
Symbol Direction2 = B.6
Symbol MAXPWMVALUE2 = 399

Symbol bSlowState2 = bit4'Is this correct?

Symbol old_feedback_value2 = b11
Symbol old_desired_value2 = b13
Symbol ENDSTOP_A2  = 35 ; desired_pot MINimum value
Symbol ENDSTOP_C2 = 210 ; desired_pot MAXimum value
 

Gramps

Senior Member
Question. Will the last two numbers "13,10" have to change to display correctly?
Code:
    sertxd("Desired/Feedback2: ",#desired_pot_value2," ",#feedback_pot_value2,13,10)
 

inglewoodpete

Senior Member
Question. Will the last two numbers "13,10" have to change to display correctly?
Code:
    sertxd("Desired/Feedback2: ",#desired_pot_value2," ",#feedback_pot_value2,13,10)
13, 10 are the ASCII codes first used to operate teleprinters. 13 is Carriage Return (now interpreted as "change output point/cursor to the beginning of the current line") and 10 is Line Feed (now "move cursor/output point to the next line").

When coding for PICAXEs, you can use CR and LF which are interpreted as 13, 10.

So, to me, it makes more sense to use CR and LF:
Code:
sertxd("Desired/Feedback2: ", #desired_pot_value2, " ", #feedback_pot_value2, CR, LF)
 

lbenson

Senior Member
You do not need
Code:
symbol REVERSE2_=0
symbol FORWARD2_=1
These define "constants" (names for unchanging values), but the values 0 and 1 are already defined as REVERSE and FORWARD, so just use REVERSE and FORWARD in your code for motor 2 and motor 3.
Different names should not be given for the same variable in most circumstances (including this one):
Code:
Symbol feedback_pot_value2 = b8
Symbol diff2=b8
The second one should be "=b9" (unless that is already used or unless w4 which contains b9 is already used).
 

Gramps

Senior Member
(Banging head on workbench)
I lost a code snippet.
We have this:
Symbol ENDSTOP_A = 35 ; desired_pot MINimum value
Symbol ENDSTOP_C = 210 ; desired_pot MAXimum value
But can't find the code!
Is this it?
Readadc Desired_pot, Desired_pot_value ; (Assuming that b1 had been symbolised as "Desired_pot_value")
Desired_pot_value = Desired_pot_value MIN ENDSTOP_A MAX ENDSTOP_C

Inserted code in program, but endstops are not working with the hardware.
 

lbenson

Senior Member
Inserted code in program, but endstops are not working with the hardware.
What variety of "not working"?

We'd need to see all of the code, and SERTXD output of Desired_pot_value and feedback_pot_value showing the problem.
 

Gramps

Senior Member
What variety of "not working"?
The feedback pot is tracking the desired pot correctly, however the code that commands the ENDSTOP's is missing.
I think this is the snippet I'm missing:
Readadc Desired_pot, Desired_pot_value ; (Assuming that b1 had been symbolised as "Desired_pot_value")
Desired_pot_value = Desired_pot_value MIN ENDSTOP_A MAX ENDSTOP_C

Code:
'Shadow-Bot feedback control code with limits
#picaxe 28x2
#no_data
#no_table
symbol DEADZONE = 4   ; Switch motor off (Make as small as practical)
symbol SLOWZONE = 12  ; Slow motor down (NB: May depend on motor speed ! )
symbol MotorSpeed  = w4  ; (To replace the "300" in the present PWMOUT code)
symbol LOOPGAIN = 33      ;  i.e.  MAXPWMVALUE / SLOWZONE (= 399 / 12)
symbol PWMPERIOD = 199   ; Equivalent Max Duty Cycle is 799
symbol xb0 = b0 ' reserved for bit variables
symbol bReverseForward=bit1 ' 0=reverse,=forward
symbol bMotorState=bit0     ' 0=off,1=on
symbol REVERSE_=0
symbol FORWARD_=1
Symbol desired_pot_value = b1
Symbol feedback_pot_value = b3
Symbol diff=b4
Symbol  desired_pot = 13 'B.5
Symbol  feedback_pot = 11'B.4
Symbol MOTOR = B.0' Energize PWM
Symbol Direction = B.7
Symbol MAXPWMVALUE = 399
Symbol bSlowState=bit2
Symbol old_feedback_value= b5
Symbol old_desired_value= b6
Symbol ENDSTOP_A  = 35 ; desired_pot MINimum value
Symbol ENDSTOP_C = 210 ; desired_pot MAXimum value
;Lower shoulder min=35 max=210
;Upper shoulder min=?? max=???

Main:
  Readadc Desired_pot, desired_pot_value
    Desired_pot_value = Desired_pot_value MIN ENDSTOP_A MAX ENDSTOP_C
    sertxd (#desired_pot_value," ",#b1,13,10)
    pause 100
    Readadc feedback_pot, feedback_pot_value
    sertxd (#feedback_pot_value," ",#b3,13,10)
    pause 100
  'sertxd("Desired/Feedback: ",#desired_pot_value," ",#feedback_pot_value,13,10)
  If old_feedback_value<>feedback_pot_value or old_desired_value<> desired_pot_value then
  old_feedback_value=feedback_pot_value
  old_desired_value=desired_pot_value
  sertxd("Desired/Feedback: ",#desired_pot_value," ",#feedback_pot_value,13,10) 
endif
if desired_pot_value > feedback_pot_value then
   diff=desired_pot_value - feedback_pot_value
   else
   diff=feedback_pot_value - desired_pot_value
endif

  if diff > DEADZONE then
    MotorSpeed = diff * LOOPGAIN max MAXPWMVALUE  ; Limits speed outside of SLOWZONE
    if  desired_pot_value > feedback_pot_value then
      high Direction       ; Set forward direction
    else
      low Direction        ; Set reverse direction
    endif
    pwmout MOTOR , PWMPERIOD , MotorSpeed     ; Slows motor within SLOWZONE
  else
    pwmout MOTOR , OFF         ; STOP
    bMotorState=0      ; Not sure what these do (now)
    bSlowState = 0      ;
  endif
 
goto main
24242
 

lbenson

Senior Member
I think this is the snippet I'm missing:
Readadc Desired_pot, Desired_pot_value ; (Assuming that b1 had been symbolised as "Desired_pot_value")
Desired_pot_value = Desired_pot_value MIN ENDSTOP_A MAX ENDSTOP_C
I'm not sure why you say those are missing since they immediately follow "main:" in the code you posted.

The screenshot of the SERTXD output that you posted doesn't, to me, show anything at all which bears upon what you are saying.

The block beginning with this statement: If old_feedback_value<>feedback_pot_value or old_desired_value<> desired_pot_value then
was supposed to replace the two SERTXD statements above it, so that you only had new SERTXD output when a value had changed, instead of output with every loop through the code.

You still have dithering of desired_pot and feedback pot readings, so you won't eliminate all repetition, but the following will help (and please try to keep your block indentations consistant--that indicates that you understand how the flow of the program is organized):
Code:
Main:
  Readadc Desired_pot, desired_pot_value
    Desired_pot_value = Desired_pot_value MIN ENDSTOP_A MAX ENDSTOP_C
'    sertxd (#desired_pot_value," ",#b1,13,10)
    pause 100
    Readadc feedback_pot, feedback_pot_value
'    sertxd (#feedback_pot_value," ",#b3,13,10)
    pause 100
  'sertxd("Desired/Feedback: ",#desired_pot_value," ",#feedback_pot_value,13,10)
  If old_feedback_value<>feedback_pot_value or old_desired_value<> desired_pot_value then
    old_feedback_value=feedback_pot_value
    old_desired_value=desired_pot_value
    sertxd("Desired/Feedback: ",#desired_pot_value," ",#feedback_pot_value,13,10) 
  endif
  if desired_pot_value > feedback_pot_value then
    diff=desired_pot_value - feedback_pot_value
  else
    diff=feedback_pot_value - desired_pot_value
  endif

  if diff > DEADZONE then
etcetera
 

Gramps

Senior Member
but the following will help
Thankyou. Getting endstop now (set at 140 high and 80 low)
The screen shot shows 4or5 difference.
With the desired pot cranked past 140, the motor driver has stopped moving the arm at 136.
However, the motor driver is not turning completely off. (LED direction light is still lit.)

24246
 

AllyCat

Senior Member
Hi,
symbol DEADZONE = 4 ; Switch motor off (Make as small as practical)
A value of 1 (or even 0) should be satisfactory and maybe necessary for good performance. Zero is valid because the test is for > (greater than) DEADZONE so the motor can still stop.

But IMHO the "diagnostics" (and symbols*) have now taken over the program to such an extent that "you can't see the wood for the trees". At least with this/these big (slow-responding) motor/s it might be better to revert to DEBUG (particularly with those PAUSEs creeping back in) ! I believe the functional (main: loop) part of the program is now simply:
Code:
Main:
  Readadc desired_pot, desired_pot_value
  desired_pot_value = desired_pot_value MIN ENDSTOP_A MAX ENDSTOP_C
  Readadc feedback_pot, feedback_pot_value
;  sertxd("desired/Feedback: ",#desired_pot_value," ",#feedback_pot_value,CR,LF)'  ; Optional
  if desired_pot_value > feedback_pot_value then
    diff = desired_pot_value - feedback_pot_value
    high Direction       ; Set forward direction
  else
    diff = feedback_pot_value - desired_pot_value
    low Direction        ; Set reverse direction
  endif
  if diff > DEADZONE then
    MotorSpeed = diff * LOOPGAIN max MAXPWMVALUE     ; Limits speed outside of SLOWZONE
    pwmout MOTOR , PWMPERIOD , MotorSpeed                 ; Slows motor within SLOWZONE
  else
    pwmout MOTOR , OFF               ; STOP
  endif
  DEBUG           ; OR gosub debug_it
goto main
Of course with DEBUG it's not possible to paste the "progress" of the program into a thread for us to see what's happening, so I'd suggest replacing the DEBUG command with a GOSUB DEBUG_IT , where the subroutine might be:
Code:
symbol report = b20   ; Any spare register
debug_it:
  if report = 0 then
    sertxd(CR,LF,"Desired/Feedback",CR,LF)
;    sertxd(#desired_pot_value," / ",#feedback_pot_value,CR,LF)  ; Optional here
    report = 99
  endif
  if report < 10 then
    sertxd(#desired_pot_value," / ",#feedback_pot_value,CR,LF)
    report = report + 99
  endif
  report = report - 10
  sertxd(".")     ; Optional "heartbeat"
return
* Personally, I prefer shorter symbol names (I aim for no more than about 6 characters when possible) but some names are now unnecessary (IMHO) so let's see if the above code is satisfactory first, before a "tidy up".

Cheers, Alan.
 

Gramps

Senior Member
If by "LED direction light" you mean B.7, what would turn it off other than a reverse in direction?
When B.0 stops sending a signal to the motor driver, the LED on the driver goes out.
After the arm has reached the endstop, it remains lit. If I pull the jumper to B.0 it goes out.
Normally it goes out when ever the two pots are equal (within 2 or 3numbers)
edit: But at endstop the number between the desired pot and the feedback pot are 5 apart.
 
Last edited:

Gramps

Senior Member
I believe the functional (main: loop) part of the program is now simply:
Alan, just ran your code and the motor is a little more lively. Works well, but the motor driver is staying lit as explained in post #24.
Added your debug code. Now the motor can't seem to find a place to settle down. Bouncing all over the place with quick direction changes.
Terminal screen shows no numbers.
 

AllyCat

Senior Member
Hi,

I'm not sure when you swapped to my "analogue" feedback version (it wasn't in #1 above), but I recall there was one more refinement in post #103 of the earlier thread. It ensures that the motor keeps turning until the DEADSPACE is reached. So, add the Symbol line as below and add the min CREEP to the Motorspeed line :
Code:
Symbol CREEP = 100         ; PWM value for minimum speed (adjust as required)
......
MotorSpeed = diff * LOOPGAIN max MAXPWMVALUE min CREEP     ; Limit range of motor speed
As for the alternative Debugging subroutine, possibly there's a conflict of variables, or some part of the code hasn't been deleted (that should have been). Or maybe it needs a PAUSE to stabilise the loop (as they keep reappearing for some reason). You may need to post the whole of the latest version for us to check.

Cheers, Alan.
 

Gramps

Senior Member
when you swapped to my "analogue" feedback version
We have at least 6 versions of this code. Each one with little twigs and improvements. Hope you all can help me sort them out.
Seems like the one that ran the best is not saved.
Sigh..........
 

Gramps

Senior Member
Just made an interesting discovery.
When we power the arm with 12 volts, it appears the motor is coasting far enough so that when the arm reaches the endstop, the motor driver turns off completely!
 

Gramps

Senior Member
add the min CREEP to the Motorspeed line:
Nice improvement! Now slows down before stopping.
Working great on 12 volts. Forward and reverse very smooth even with a quick change of direction.
6 volts speed is creeping slow and at endstop the motor stops but a tiny little current is still keeping the motor driver activated.
 

AllyCat

Senior Member
Hi,
.... the motor stops but a tiny little current is still keeping the motor driver activated.
Then try adjusting the "constants" at the top of the Program (that's one reason why they're there). Increase the value of CREEP to perhaps 200 until the motor does what is says (creeps). Also the value of MAXPWM is currently 399 giving a PWM duty cycle of ~50%, or the equivalent of 6 volts with a 12 volt supply ! We "assumed" that is what you had found was best; we have no way of predicting how the motor(s) will behave in your particular hardware.

That's one reason we include "comments" in the code, but unfortunately can only mark them with a ' or ; character (it's not possible to use fancy fonts inside the code window), for example from the original thread :

Symbol CREEP = 100 ; PWM value for minimum speed
symbol DEADZONE = 4 ; Switch motor off (Make as small as practical)
symbol SLOWZONE = 12 ; Slow motor down (NB: May depend on motor speed ! )
symbol PWMPERIOD = 199 ; Equivalent Full Duty Cycle is 800
symbol MAXPWMVALUE = 399 ; What is says ! (ie currently 399 / 800 = 50% duty cycle)
symbol LOOPGAIN = 33 ; i.e. MAXPWMVALUE / SLOWZONE (= 399 / 12)
...... and of course the "ENDSTOPs".

Note that you can put simple "formulae" in the symbol declarations (only one operator in each line) so some of the constants can be "automatically" calculated, for example:

symbol LOOPGAIN = MAXPWMVALUE / SLOWZONE ; Currently 399 / 12 = 33 but adjust to control "overshoot" (eg. via SLOWZONE value)
symbol FULLPWMDUTYCYCLE = PWMPERIOD * 4 ; Strictly it's 4 greater, (ie currently 800)

Some/most of those values might need to be defined separately for each motor, depending how "similar" (or not) are the motors and their mechanical linkages.

Cheers, Alan.
 

Gramps

Senior Member
try adjusting the "constants
Okay. I didn't realize you had Incorporated these really cool features. That explains why the code works so well on 12 volts but not on 6. I'll be away from the hardware until Wednesday and will report back then.
 

Gramps

Senior Member
Well, three days away from the hardware turned into two weeks! We begin experimenting with the CONSTANT values this afternoon. Should have some data this evening.
 

Gramps

Senior Member
Hopefully this is enough data to see problem.
A little current is still keeping the motor driver activated. It's not turning completly off when an endstop or a midpoint position is reached.

Symbol CREEP = 100 ; PWM value for minimum speed
symbol DEADZONE = 4 ; Switch motor off (Make as small as practical)
symbol SLOWZONE = 12 ; Slow motor down (NB: May depend on motor speed ! )
symbol PWMPERIOD = 199 ; Equivalent Full Duty Cycle is 800
symbol MAXPWMVALUE = 399 ; What is says ! (ie currently 399 / 800 = 50% duty cycle)
symbol LOOPGAIN = 33 ; i.e. MAXPWMVALUE / SLOWZONE (= 399 / 12)
...... and of course the "ENDSTOPs".
(1)Test above original settings:
Results:
6volts: overall sluggish movement, SLOWZONE slowing correctly, DEADZONE motor stopping but controller is still energized.
12 volts overall peppy movement, SLOWZONE slowing correctly, DEADZONE motor stopping but controller is still energized.

Increase the value of CREEP to perhaps 200
Increase the value of MAXPWMVALUE = 499
Decrease DEADZONE = 2

Symbol CREEP = 200 ; PWM value for minimum speed
symbol DEADZONE = 2 ; Switch motor off (Make as small as practical)
symbol SLOWZONE = 12 ; Slow motor down (NB: May depend on motor speed ! )
symbol PWMPERIOD = 199 ; Equivalent Full Duty Cycle is 800
symbol MAXPWMVALUE = 499 ; What is says ! (ie currently 399 / 800 = 50% duty cycle)
symbol LOOPGAIN = 33 ; i.e. MAXPWMVALUE / SLOWZONE (= 399 / 12)
...... and of course the "ENDSTOPs"
.
(2)Test above settings:
Results:
6volts: DEADZONE motor stopping but controller is still energized.
12 volts DEADZONE motor stopping but controller is still energized.

Symbol CREEP = 200 ; PWM value for minimum speed
symbol DEADZONE = 4 ; Switch motor off (Make as small as practical)
symbol SLOWZONE = 12 ; Slow motor down (NB: May depend on motor speed ! )
symbol PWMPERIOD = 299 ; Equivalent Full Duty Cycle is 800
symbol MAXPWMVALUE = 399 ; What is says ! (ie currently 399 / 800 = 50% duty cycle)
symbol LOOPGAIN = 33 ; i.e. MAXPWMVALUE / SLOWZONE (= 399 / 12)
...... and of course the "ENDSTOPs".

Increased PWMPERIOD = 299
(2)Test above settings:
Results:
6volts: DEADZONE motor stopping, controller is also off. However we lost all creep.
12 volts DEADZONE motor stopping but controller is also off. We lost all creep.
 

AllyCat

Senior Member
Hi,

To ensure that the motor gets to its "final" position (the DEADZONE) to switch off the power, you will have to set the drive voltage level (PWM) so that the motor is able to Start moving, if it happens to have stopped. It might help to drop the PWM frequncy to say 100 Hz, to "Jog" the motor into movement. If the motor then overshoots too far past the required position, or is even unstable, then you may need to change the LOOPGAIN or SLOWZONE values (note they may be related), and of course the Maximum PWM value. It's impossible to predict what values might be optimum for your particular motor.

However, the "Static Friction" and/or Inertia of your motor might be so high that other methods are required. For example you might make the CREEP drive time-dependent, i.e. it starts at quite a low voltage/current but if the motor hasn't moved within a short period, then the voltage would be increased (perhaps just in short pulses) until the motor has moved to the desired (DEADZONE) position. Of course many other strategies have been devised to solve such problems, but "(Feedback) Control Loop Theory" can get very complicated.

Cheers, Alan.
 

Gramps

Senior Member
Thanks Alan for your suggestions.
but "(Feedback) Control Loop Theory" can get very complicated
True! I've been thinking about constructing a table using the parameters in various combinations to try to systematically find the right numbers.
 

Gramps

Senior Member
We worked through Alan's cleaned up main loop adding the Symbols and his cool "gosub debug_it"!!!

Code:
'Shadow-Bot feedback control code with limits updated 11-13-20
#picaxe 28x2
#no_data
#no_table
symbol DEADZONE = 2   ; Switch motor off (Make as small as practical)
symbol SLOWZONE = 12  ; Slow motor down (NB: May depend on motor speed ! )
symbol MotorSpeed  = w4  ; (To replace the "300" in the present PWMOUT code)
symbol LOOPGAIN = 33      ;  i.e.  MAXPWMVALUE / SLOWZONE (= 399 / 12)
symbol PWMPERIOD = 199   ; Equivalent Max Duty Cycle is 799
symbol xb0 = b0 ' reserved for bit variables
symbol bReverseForward=bit1 ' 0=reverse,=forward
'symbol bMotorState=bit0     ' 0=off,1=on
symbol REVERSE_=0
symbol FORWARD_=1
Symbol desired_pot_value = b1
Symbol feedback_pot_value = b3
Symbol diff=b4
Symbol  desired_pot = 13 'B.5
Symbol  feedback_pot = 11'B.4
Symbol MOTOR = B.0' Energize PWM
Symbol Direction = B.7
Symbol MAXPWMVALUE = 399
'Symbol bSlowState=bit2
Symbol old_feedback_value= b5
Symbol old_desired_value= b6
Symbol ENDSTOP_A  = 60 ; desired_pot MINimum value
Symbol ENDSTOP_C = 210 ; desired_pot MAXimum value
Symbol CREEP = 160         ; PWM value for minimum speed (adjust as required)

Main:
  Readadc desired_pot, desired_pot_value
  desired_pot_value = desired_pot_value MIN ENDSTOP_A MAX ENDSTOP_C
  Readadc feedback_pot, feedback_pot_value
;  sertxd("desired/Feedback: ",#desired_pot_value," ",#feedback_pot_value,CR,LF)'  ; Optional
  if desired_pot_value > feedback_pot_value then
    diff = desired_pot_value - feedback_pot_value
    high Direction       ; Set forward direction
  else
    diff = feedback_pot_value - desired_pot_value
    low Direction        ; Set reverse direction
  endif
  if diff > DEADZONE then
    MotorSpeed = diff * LOOPGAIN max MAXPWMVALUE     ; Limits speed outside of SLOWZONE
    pwmout MOTOR , PWMPERIOD , MotorSpeed                 ; Slows motor within SLOWZONE
  else
    pwmout MOTOR , OFF               ; STOP
  endif
   gosub debug_it
goto main
'Of course with DEBUG it's not possible to paste the "progress" of the program into a thread for us to see what's happening, so I'd suggest replacing the DEBUG command with a GOSUB DEBUG_IT , where the subroutine might be:
Code:
symbol report = b20   ; Any spare register
debug_it:
  if report = 0 then
    sertxd(CR,LF,"Desired/Feedback",CR,LF)
;    sertxd(#desired_pot_value," / ",#feedback_pot_value,CR,LF)  ; Optional here
    report = 99
  endif
  if report < 10 then
    sertxd(#desired_pot_value," / ",#feedback_pot_value,CR,LF)
    report = report + 99
  endif
  report = report - 10
  sertxd(".")     ; Optional "heartbeat"
return
'* Personally, I prefer shorter symbol names (I aim for no more than about 6 characters when possible) but some names are now unnecessary (IMHO) so let's see if the above code is satisfactory first, before a "tidy up".
 
Last edited:

Gramps

Senior Member
To get the motor controller to completely turn off after the motor stops we have to set the DEADZONE to 6.
24285
 
Top