If, thens, and gosubs

jodicalhon

New Member
My little two-wheeled robot is now controlled by a TV remote controller, using infrain2. Depending on the button pushed, the program chooses a subroutine for turn left, turn right, go forward, or reverse. So far so good!

I thought it would be fun to have it perform a sequence from one button push, which leads me to my question.

Upon receipt of IR the program chooses a subroutine by if...then statements. Perhaps some code would help:

<code><pre><font size=2 face='Courier'>
loop:
infrain2
if infra = 0 then left
if infra = 1 then fwd
if infra = 2 then right
if infra = 4 then rverse
if infra = 8 then sequence
'else allstop

allstop:
high 0 'disables motors
goto loop

left:
low 2 'right motor forward
high 4 'left motor reverse
low 0 'enable motors
goto loop
</font></pre></code>

And so on for the other subroutines.

The sequence subroutine would go something like:
sequence:
gosub left
pause 1000
gosub fwd
pause 1500
etc, etc

But the 'gosubs' need a 'return' in the subroutine they point to. Whereas the if/then statements do not. D'you see what I mean?

My solution has been to have the if/then statement point to a subroutine which contains a gosub to the wanted subroutine. The wanted subroutine can then contain a 'return', and so can be used easily by my 'sequence' subroutine. Like so:

infrain2
if infra = 0 then zero
. . . . . . . .

zero:
gosub left
goto loop

left:
whatever my motors need to do!
return

Is there a better way to handle this problem?

Thanks for any help.

 

hippy

Technical Support
Staff member
There is an option. What you have ...

- Loop:
- INFRAIN2
- IF infra = 0 THEN zero
- :
- GOTO Loop
-
- Zero:
- GOSUB Left
- GOTO Loop
-
- Left:
- :
- RETURN

You can turn that into this ...

- Loop:
- GOSUB HandleCommand
- GOTO Loop
-
- HandleCommand:
- INFRAIN2
- IF infra = 0 THEN Left
- :
- RETURN ' or GOTO HandleCommand
-
- Left:
- :
- RETURN
 

SD2100

New Member
I don't think you can do that, the address after the IF/THEN is the new address and you can't do a RETURN, you have a return at the end of the &quot;left&quot; subroutine instead of a goto. You might have overlooked it !!!


Edited by - Phil75 on 21/04/2006 16:49:55
 

Bloody-orc

Senior Member
could someone tell me, what will happen i i do like this:

main:
if b0 &gt; 8 then loop
b0 = b0+1
goto main

loop:
b0 = 0
return
 

hippy

Technical Support
Staff member
Trust me, it does work :)

From 'Loop:' we enter the 'HandleCommand:' routine which can jump about all over the place, including as a result of IF THEN, and any return will exit 'HandleCommand:'. If the IF is ever true, 'Left:' is reached ( and we're still in the 'HandleCommand:' routine, and then returns from that. The code then continues at 'GOTO Loop' and the whole cycle repeats.

The 'Left:' routine can also be called from anywhere else in the program, and from within 'HandleCommand:' itself, because it is effectively a subroutine in its own right.
 

hippy

Technical Support
Staff member
Re : Bloody-orc - Your program will crash, or go off and behave in some way unpredictable.

Maybe not entirely true, depending on how the stack is initialised by firmware, which PICAXE you are using and whether there are any GOSUB's in your progam elsewhere, but basically, don't expect it to do anything sensible.
 

SD2100

New Member
OK ya got me, should have kept my gob shut
before I mouthed off. just looked a bit strange heading towards a RETURN without a GOSUB. but I see it now. sorry
 

jodicalhon

New Member
Thanks Hippy.

Just to check I understand:
In your code, the final RETURN (from the LEFT subroutine) takes me back to just below the GOSUB HandleCommand line, thence to GOTO loop, and so on and so forth.

I'll give it a whirl!

This will help me with my latest brainwave, which is to make the sequence user programmable via the remote controller. (Sort of like Robosapien, just a lot crappier!) Methinks my program will be jumping all over the place, so this sort of info is invaluable.
 

hippy

Technical Support
Staff member
<i>Just to check I understand:
In your code, the final RETURN (from the LEFT subroutine) takes me back to just below the GOSUB HandleCommand line, thence to GOTO loop, and so on and so forth. </i>

Yes, that's right.
 

hippy

Technical Support
Staff member
Phil75 : <i>OK ya got me, should have kept my gob shut </i>

Not at all, and apologies if I did reply in any way which felt patronising. Nothing wrong in asking, &quot;How the heck does that work ?&quot;, or as you did, and I haven't always get it right !

We're all here to help each other, so never hold back if you feel something needs to be said, asked or explained.
 

SD2100

New Member
No worries Hippy my fault, if I'd spent a few minutes reading through your code or even testing it myself I would have found that it was ok.
 

jodicalhon

New Member
From me earlier: <i>This will help me with my latest brainwave, which is to make the sequence user programmable via the remote controller. </i>

I've got this sorted now, and your information really helped a great deal, Hippy. Every 'return' in my program relates to 'gosub commandhandle'and I've been able to jump all over the place.

Of interest may be my 'playback' subroutine, which has a split for/next loop in it.

It's important to note that none of my 'left', 'fwd', 'right', 'rverse' commmands have any timing info, as this depends on when the operator wants to change direction.

The record mode just notes the movements wanted in order of input. But the 'playback' needs some timing info, which I have set at 1000mS per step. I hope to make this programmable in version 2, if my brain doesn't hurt too much!

We get to 'playback' via the 'commandhandle' routine, which has been called by a 'gosub'.

In 'playback', a for/next loop is begun for the number of steps in the sequence. The wanted direction is read, we go to the appropriate routine which sets the motors how we want, and reach a 'return' which takes us out of 'commandhandle' IN THE MIDDLE OF THE FOR/NEXT LOOP! We reenter 'commandhandle' via a gosub, and go to the timing subroutine which sets the time the motors are on and calls 'next b0'. Which calls the 'for' part of the loop, which sets the motor direction and exits the 'gosub' a second time.

So we do a 'for' in one gosub/return, and do a 'next/for' in another gosub/return.

Do others know about this sort of trick?

I'll post the code if anyone's interested. (I'm quite chuffed I've been able to make it work!)

Cheers, and thanks again, Hippy.
 

jodicalhon

New Member
Here's the complete listing, Craig. I've used PWM for the beeps because this is an addition to an existing robot. And I like the sound the PWM gives! PWM outputs the sound then returns the pin to an input. Using the SOUND command will save a few bytes.

I thought I had all the bugs and gave it to my 7yo son, who promptly had it locked up and going in circles! I think all is well now.

<code><pre><font size=2 face='Courier'>
'Remote controlled buggy, with user-programmable sequence
'Jo Charles, April 2006 Programmed on an 08M.

'Retains last recorded sequence in eeprom after powerdown
'All returns relate to gosub commandhandle - thanks Hippy!
'Next version to have programmable timing.
'Needs way to interrupt playback - working on it!

'At the end of every record session, the step counter value is written
'into eeprom 0, and the actual moves into eeprom 1 and upwards.
'Using SOUND for the beep will save a few bytes.



high 0 'disable motors

pwm 1,10,20 'beep

b0 = 0 'a multi-use variable
b2 = 0 'sets flag for going to timing subroutine or not
read 0,b1 'playback step counter

pause 500 'just to let things settle

loop:
gosub commandhandle
goto loop

commandhandle:
if b2 = 1 then timing

infrain2
pwm 1,10,20 'beep

choose:
if infra = 0 then left
if infra = 1 then fwd
if infra = 2 then right
if infra = 4 then rverse

if infra = 6 then record
if infra = 8 then playback
'else allstop

allstop:
high 0 'disables motors
return

left:
low 2 'right motor fwd
high 4 'left motor reverse
low 0 'enable motors
return

fwd:
low 2 'right motor fwd
low 4 'left motor fwd
low 0 'enable motors
return

right:
high 2 'right motor reverse
low 4 'left motor fwd
low 0 'enable motors
return

rverse:
high 2 'right motor reverse
high 4 'left motor reverse
low 0 'enable motors
return

record:
high 0 'disables motors - thanks Callum!(7yo bug finder)
b0 = 1 'initial eeprom location
b1 = 0 'initialises counter for number of steps in sequence

getuserinput:
pause 1000 'helps prevent unwanted multiple inputs of same command
'or going straight into stoprecording - modify to suit.
infrain2
pwm 1,10,20 'beep
if infra = 6 or infra = 8 then stoprecording 'ends recording and returns
'to normal mode
write b0, infra
b0 = b0 + 1 'increments eeprom location
b1 = b1 + 1 'increments counter
goto getuserinput

stoprecording:
write 0,b1 'writes counter total into eeprom location 0
pause 1000 'helps prevent going straight back into record mode
return

playback:
b2 = 1 'sets timing subroutine flag

getdata:
for b0 = 1 to b1 'number of steps in sequence
read b0, infra 'put data into infra
goto choose 'execute command

timing:
pause 1000 'every step is for 1 second
next b0

b2 = 0 'removes timing flag
goto allstop

</font></pre></code>

I hope you have fun!
 

jodicalhon

New Member
I forgot to say, you could enhance this design by adding LED's to show what state the robot was in - free play, record, or playback.

Programmable timing is something I thought about today. There are two options that I see.

By leaving the program as is, and shortening the step time from 1000mS to whatever resolution you wanted, you would use multiple identical commands to control the time spent going in a particular direction. The finer the resolution, the more eeprom locations used for a particular duration of time.

The other option is to follow the recording of a direction command (saved to eeprom) with and associated timing command (saved to the next eeprom location). Hopefully, some sort of loop could be implemented that counted multiple button presses, with each button press being one timing resolution unit, summed the result, and saved that into the timing slot. Then onto the next direction command, etc.

One way uses more eeprom memory, the other uses more program memory. As I'm using an 08M, with shared eeprom and program memory, it's a matter of how many more bytes will be taken up with option 2, as against what I'm likely to use, in what is basically a toy, with option 1.

I think I am better off with option 1, but just to give myself a headache I'll see what I can do about implementing option 2.

Cheers :)
 

craigcurtin

Senior Member
thanks Joc - very happy with what you posted, I will study for another project and might in the meantime make this for my 4 year old son !!

Craig
 

jodicalhon

New Member
One more potential bug. It's great to have these little beta-testers around!

If you record too many moves (on a picaxe that shares data and program memory) you can crash into the program, corrupting it. So limit the maximum no of steps in the sequence. I have added this in the getuserinfo subroutine:

<code><pre><font size=2 face='Courier'>
write b0, infra
b0 = b0 + 1 'increments eeprom location
b1 = b1 + 1 'increments counter
if b0 &gt; 50 then stoprecording 'stops sequence crashing into program
'thanks Honour!(6yo bug finder)
goto getuserinput
</font></pre></code>

Cheers, Craig.

Jo
 
Top