Branch command and return statements

steliosm

Senior Member
Hello all.

I'm experimenting with a potential USB gadget toy that will be using a Picaxe 08m chip as it's base and the USB-2-TTL cable from China (of course).

The application logic is to receive data from the serial cable and act accordingly. The data is a number from 1 to 9. I'm using the branch command since it will minimize the code I use. Some labels/branches have a 'goto main' at their ends and a few have a 'return' statement. Both seem to work fine for some weird reason. I haven't found anything on the manual concerning branch command and return statements.

Anyone has seen this behavior?
 

hippy

Technical Support
Staff member
Post your code; it's hard to guess what may be happening. It's probably just good luck that things aren't 'going bad'.

If you have a RETURN without a GOSUB then you're inviting disaster. You can have a GOSUB without a RETURN but it's stongly advised against; it can work because the gosub stack is circular rather than a list / array. There's no such thing as 'stack overflow' on the PICAXE, just unexpected things that can happen on 'stack underflow'.
 

steliosm

Senior Member
Ok Hippy, you 'asked for it'! I haven't had time to clean-up the code yet, I'm creating the control-panel GUI using Liberty Basic at the moment.

Code:
'
' USB Xmas LED flasher
' Get command from the PC, flash the leds and play tunes
'
'
' Commands:
' 0 - All LEDs off
' 1 - Play Jinge Bells
' 2 - Play Silent Night
' 3 - Play Rudolph the Red Nosed Reindeer
' 4 - Play all tunes
' 5 - Flash the LEDs slow
' 6 - Flash the LEDs fast
' 7 - Animate LEDs slow
' 8 - Animate LEDs fast



' Define the output pins
Symbol LED = 4
Symbol PIEZO = 2
Symbol DISPLAY = 0
Symbol SLOW = 800
Symbol FAST = 400

' Define some extra symbols
Symbol CMD = b0
Symbol NEWCMD = b1

' Initialize
' Make a small pause to let everything settle down
pause 3000
let NEWCMD = 0
high DISPLAY

' Set the interrupt to receive commands from the keyboard
setint %00000000, %00001000

' Move to a standard action
goto animate_leds_slow

main:
  ' Check the command we received
  ' Reset the NEWCMD flag
  let NEWCMD = 0
  high DISPLAY
  if CMD > 9 then goto animate_leds_slow
  branch CMD, (turn_off, play_tune_1, play_tune_2, play_tune_3, play_all_tunes, flash_leds_slow, flash_leds_fast, animate_leds_slow, animate_leds_fast)
  goto turn_off


interrupt:
   ' Get a new command from the PC
   ' Display the command prompt after a brief delay
   pause 100
   serout 1, t2400, ("*?")
   ' SerIn to get the command
   serin 3, t2400, #CMD
   ' Respond with the command
   serout 1, t2400, ("!", #CMD) 
   ' Set the interrupt again
   'pause 100
   setint %00000000, %00001000
   ' Set the NEWCMD to1
   let NEWCMD = 1
   ' Return to main loop
   return

'
' Play tunes part
'

play_tune_1:
  ' Play the Jingle Bells tune
  play 1, 2
  return

play_tune_2:
  ' Play Silent Night tune
  play 2, 2
  return
  
play_tune_3:
  ' Play the Rudolph tune
  play 3, 2
  return

play_all_tunes:
  ' Play all tunes in sequence (CMD 4)
  ' Call the first tune
  gosub play_tune_1
  pause 1000
  ' Call the second tune
  gosub play_tune_2
  pause 1000
  ' Call the third tune
  gosub play_tune_3
  return


'
' Flash LEDs part
'

flash_leds_slow:
  ' Flash the LEDs while animating them slowly
  ' Pulse the LED pin
  gosub pulse_led
  ' Set the DISPLAY pin high and wait
  high DISPLAY
  pause SLOW
  low DISPLAY
  pause SLOW
  if NEWCMD = 1 then
  	' We had a new command - move control to main subrouting to execute the new command
  	goto main
  endif
  goto flash_leds_slow

flash_leds_fast:
  ' Flash the LEDs while animating them slowly
  ' Pulse the LED pin
  gosub pulse_led
  ' Set the DISPLAY pin high and wait
  high DISPLAY
  pause FAST
  low DISPLAY
  pause FAST
  if NEWCMD = 1 then
  	' We had a new command - move control to main subrouting to execute the new command
  	goto main
  endif
  goto flash_leds_fast

  
animate_leds_slow:
  ' Animate the LEDs slowly - this requires to send pulses through pin4
  ' Send a pulse
  gosub pulse_led
  ' Check f we got interrupted for a new command
  if NEWCMD = 1 then
  	' We had a new command - move control to main subrouting to execute the new command
  	goto main
  else
  	' do the loop again
  	pause SLOW
          goto animate_leds_slow
  endif


animate_leds_fast:
  ' Animate the LEDs fast - this requires to send pulses through pin4
  ' Send a pulse
  gosub pulse_led
  ' Check f we got interrupted for a new command
  if NEWCMD = 1 then
  	' We had a new command - move control to main subrouting to execute the new command
  	goto main
  else
  	' do the loop again
	  pause FAST
          goto animate_leds_fast
  endif
  

pulse_led:
  ' Send a pulse through the LED port
  high LED
  pause 20
  low LED
  return

turn_off:
  ' Turn the LEDs off until a new command is in
  low DISPLAY
  off_loop:
    ' loop
    pause 1000
      if NEWCMD = 1 then
  	' We had a new command - move control to main subrouting to execute the new command
  	goto main
    endif
    goto off_loop
 

hippy

Technical Support
Staff member
[shudder] :) [/shudder]

I think you've been lucky or have had command sequences which haven't revealed the problems.

The 'goto main' are okay because they effectively 'flush the stack, and start again' - The gosub stack doesn't actually flush, but as it's circular you can go on using it forever.

The RETURN without GOSUB are either causing a complete reset by way of a program crash ( eg, a slow 'goto main' ) or are jumping to gosub stack return addresses previously pushed. That's the 'luck' part.

The easy fix is for -

main:
:
Branch ...
goto turn_off

To become -

main:
:
Gosub handle_command
goto turn_off

handle_command:
Branch ...
Return
 

steliosm

Senior Member
I think I can just get rid of the gosub/return commands completely.
I have switched the 'Play Tune' commands to:

Code:
play_tune_1:
  ' Play the Jingle Bells tune
  play 1, 2
  goto animate_leds_slow
So, when a tune is done playing, the program will goto to the 'default' animation scheme. I just had this idea and I haven't had a change to try it yet. Will post an update when I do try the code.

Thank you for your help.
 

steliosm

Senior Member
This code seems to be more stable in terms of interrupts/serial in and proper use of the branch command.
I'll start documenting the project and post it in the appropriate section on the forum. I'm posting the last version of the code just as an update.


Code:
'
' USB Xmas LED flasher
' Get command from the Keyboard, flash the leds and play tunes
' ver 0.2
'
' Commands:
' 0 - All LEDs off
' 1 - Play Jinge Bells
' 2 - Play Silent Night
' 3 - Play Rudolph the Red Nosed Reindeer
' 4 - Play all tunes
' 5 - Flash the LEDs slow
' 6 - Flash the LEDs fast
' 7 - Animate LEDs slow
' 8 - Animate LEDs fast



' Define the output pins
Symbol LED = 4
Symbol PIEZO = 2
Symbol DISPLAY = 0
Symbol SLOW = 800
Symbol FAST = 400

' Define some extra symbols
Symbol CMD = b0
Symbol NEWCMD = b1

' Initialize
' Make a small pause to let everything settle down
pause 3000
let NEWCMD = 0
high DISPLAY

' Set the interrupt to receive commands from the keyboard
setint %00000000, %00001000

' Move to a standard action
goto animate_leds_slow

main:
  ' Check the command we received
  ' Reset the NEWCMD flag
  let NEWCMD = 0
  high DISPLAY
  if CMD > 9 then goto animate_leds_slow
  branch CMD, (turn_off, play_tune_1, play_tune_2, play_tune_3, play_all_tunes, flash_leds_slow, flash_leds_fast, animate_leds_slow, animate_leds_fast)
  goto turn_off


interrupt:
   ' Get a new command from the PC
   ' Display the command prompt
   serout 1, t2400, ("*?")
   ' SerIn to get the command
   serin 3, t2400, #CMD

   ' Pause before setting the interrupt again
   pause 100
   ' Set the interrupt again
   setint %00000000, %00001000
   ' Set the NEWCMD to1
   let NEWCMD = 1
   ' Return to main loop
   return

'
' Play tunes part
'

play_tune_1:
  ' Play the Jingle Bells tune
  play 1, 2
  goto animate_leds_slow

play_tune_2:
  ' Play Silent Night tune
  play 2, 2
  goto animate_leds_slow
  
play_tune_3:
  ' Play the Rudolph tune
  play 3, 2
  goto animate_leds_slow

play_all_tunes:
  ' Play all tunes in sequence
  ' Call the first tune
  play 1, 2
  pause 1000
  ' Call the second tune
  play 2, 2
  pause 1000
  ' Call the third tune
  play 3, 2
  goto animate_leds_slow


'
' Flash LEDs part
'

flash_leds_slow:
  ' Flash the LEDs while animating them slowly
  ' Pulse the LED pin
  gosub pulse_led
  ' Set the DISPLAY pin high and wait
  high DISPLAY
  pause SLOW
  low DISPLAY
  pause SLOW
  if NEWCMD = 1 then
  	' We had a new command - move control to main subrouting to execute the new command
  	goto main
  endif
  goto flash_leds_slow

flash_leds_fast:
  ' Flash the LEDs while animating them slowly
  ' Pulse the LED pin
  gosub pulse_led
  ' Set the DISPLAY pin high and wait
  high DISPLAY
  pause FAST
  low DISPLAY
  pause FAST
  if NEWCMD = 1 then
  	' We had a new command - move control to main subrouting to execute the new command
  	goto main
  endif
  goto flash_leds_fast

  
animate_leds_slow:
  ' Animate the LEDs slowly - this requires to send pulses through pin4
  ' Send a pulse
  gosub pulse_led
  ' Check f we got interrupted for a new command
  if NEWCMD = 1 then
  	' We had a new command - move control to main subrouting to execute the new command
  	goto main
  else
  	' do the loop again
  	pause SLOW
          goto animate_leds_slow
  endif


animate_leds_fast:
  ' Animate the LEDs fast - this requires to send pulses through pin4
  ' Send a pulse
  gosub pulse_led
  ' Check f we got interrupted for a new command
  if NEWCMD = 1 then
  	' We had a new command - move control to main subrouting to execute the new command
  	goto main
  else
  	' do the loop again
	  pause FAST
          goto animate_leds_fast
  endif
  

pulse_led:
  ' Send a pulse through the LED port
  high LED
  pause 20
  low LED
  return

turn_off:
  ' Turn the LEDs off until a new command is in
  low DISPLAY
  off_loop:
    ' loop
    pause 1000
      if NEWCMD = 1 then
  	' We had a new command - move control to main subrouting to execute the new command
  	goto main
    endif
    goto off_loop
 
Top