parallel tasking

rob53

New Member
Hi guys, let me start by saying that I am very new to micro-controllers and programming. I have no formal training whatsoever. I have literally just started playing around with picaxe a few weeks ago. I got into it to see if I could build a circuit to control certain lighting effects on a model that I am building. The model is a 1/350 scale version of the USS Enterprise as seen in Star Trek The Motion picture. The lighting effects I'm working on are for the deflector dish and photon torpedos.

I'm using two momentary switches (on pins C.0 and C.1), one blue/yellow bi-colored LED (yellow on pwm C.2, blue on pwm C.3.) two red/white bi-colored LEDs (left red on pwm C.5, right red on pwm B.1)

By using pulse width modulation, the deflector dish will ramp up in yellow at the first button push, then at the second button push, the yellow LED fades off and the blue LED ramps up, then at the third button push the blue LED fades off.

The photon torpedo simulation alternates left and right, push the button once and the left red LED ramps up, then a quick 50ms burst from the white LED, then the red LED fades off, push the button again, the sequence repeats on the right red/white bi-LED.

As I have NO programming skills of my own, I have basically been operating from deductive reasoning using snippets of code I have found here and there, both in the picaxe manuals and various forums and tutorials. I am using the picaxe 20M2. The code I have managed to cobble together works just fine except for one thing, if I try to fire a photon torpedo while the deflector dish is in the process of ramping up or fading off, the LED will not respond to my switch until the PWM for the deflector dish has ceased, OR the deflector dish LED will not respond while a torpedo is being fired.

I was under the impression the M2 series could do parallel tasking. Is there something that can be done to easily rectify this, or will my code have to be totally revamped, or will I just have to live with it?

Don't laugh at my code guys (and gals). Like I said, I am a total rookie.

Code:
init:
                symbol buttonA=b0		'rename the b0 variable to buttonA (for the deflector dish control switch) 
	symbol buttonB=b1                  'rename the b1 variable to buttonB (for torpedo launcher control switch)

                let buttonA=0
                let buttonB=0		
          
main:      if pinC.0 = 1 then deflector             'if input pin C.0 goes high, go to the "deflector" subroutine.
                 if pinC.1 = 1 then torpedo         'if input pin C.1 goes high, go to the "torpedo" subroutine.	
                       goto main                         'otherwise loop back to main


deflector: 
                pause 50                                   ' pause 50 milliseconds to debounce switch
                let buttonA = buttonA + 1          'adds 1 to the variable, this counts the number of button presses

             If buttonA=1 then	
                 pwmout pwmdiv4, C.2, 99, 0    'start PWM on Port C Pin 2 @ 0%
                      for w1 = 0 to 400                  ' pwmduty needs a (W)ord var, not a (B)yte var
                        pwmduty C.2, w1               'adjust PWM Duty from 0% to 100%
                         pause 2                             ' increase the on time
                            next w1
        
           elseif buttonA=2 then
               for w1 = 400 to 0 step  -1            'same as above, but reduce the ON time
                  pwmduty C.2, w1
                     pause 2
                       next w1
                         pwmout C.2, off
              pwmout pwmdiv4, C.3, 99, 0       'start PWM on Port C Pin 3 @ 0%
                 for w1 = 0 to 400                       ' pwmduty needs a (W)ord var, not a (B)yte var
                    pwmduty C.3, w1                  'adjust PWM Duty from 0% to 100%
                      pause 2                               ' increase the on time
                        next w1
           elseif buttonA=3 then
                 for w1 = 400 to 0 step  -1            'same as above, but reduce the ON time
                      pwmduty C.3, w1
                         pause 2
                           next w1
                              pwmout C.3, off
                             buttonA=0                     ' reset counter to 0
                             endif                             'end of the if statement
                             goto main                     'loop back to main

torpedo: 
              pause 50                                   ' pause 50 milliseconds to debounce switch
              let buttonB = buttonB + 1          'adds increments of 1 to the variable, this counts the number of button presses  

     If buttonB=1 then	
       pwmout pwmdiv4, C.5, 99, 0 
          for w1 = 0 to 200                   ' pwmduty needs a (W)ord var, not a (B)yte var
            pwmduty C.5, w1                'adjust PWM Duty from 0% to 100%
              pause 2                             ' increase the on time
               next w1 
       high B.2                                     ' turn on white LED on output B.2
       pause 50                                 ' pause 50 milliseconds 
       low B.2                                          ' turn off white LED on output B.2
           for w1 = 200 to 0 step  -1      'same as above, but reduce the ON time
               pwmduty C.5, w1
                 pause 2
                   next w1
                     pwmduty C.5, 0
    elseif buttonB=2 then
         pwmout pwmdiv4, B.1, 99, 0 
          for w1 = 0 to 200                   ' pwmduty needs a (W)ord var, not a (B)yte var
            pwmduty B.1, w1                'adjust PWM Duty from 0% to 100%
              pause 2                             ' increase the on time
               next w1 
       high B.3                                     ' fire photon torpedo
       pause 50                                 ' 50 milliseconds burst of white light
       low B.3                                          ' turn off white LED on output 4
           for w1 = 200 to 0 step  -1      'same as above, but reduce the ON time
               pwmduty B.1, w1
                 pause 2
                   next w1
                     pwmduty B.1, 0
                 buttonB=0      	       'reset the counter			
	           endif                     'end the if statement
	           goto main 	             ' loop back to main
 

hippy

Ex-Staff (retired)
Welcome to the PICAXE forum.

You need to explicitly enable parallel tasking by using "start0:", "start1:" labels in your program.

It looks like your deflector LED's are on C.2 and C,3, your torpedo LED's on C.5 and B.1. This example code should see the deflector LED's alternating quickly while those for your torpedo alternate slowly -

Code:
Start0: ; Deflector
Low  C.2
High C.3
Do
  Toggle C.2
  Toggle C.3
  Pause  300
Loop

Start1: ; Torpedo
Low  C.5
High B.1
Do
  Toggle C.5
  Toggle B.1
  Pause  1000
Loop
You can extend that to handle the buttons and PWM for the deflector in the Star0: code and the buttons and PWM for the torpedo in the Star1: code.
 

techElder

Well-known member
Or ...

Separate your program into 2 parts: the button part and the LED part.

Use your Start0: task to handle your buttons and apply values to variables. These variables then become "global" in nature for the Start1: task.

Use your Start1: task to do the fancy flashing and fading using the variable whose values were assigned in the Start0: task.

Easy?
 

rob53

New Member
Or ...

Separate your program into 2 parts: the button part and the LED part.

Use your Start0: task to handle your buttons and apply values to variables. These variables then become "global" in nature for the Start1: task.

Use your Start1: task to do the fancy flashing and fading using the variable whose values were assigned in the Start0: task.

Easy?
I understand the concept, but I have no idea how to write it that way. I wrote this code by modifying code others have written, and modifying the example code from the picaxe web-site for how to use a pushbutton switch. It's going to be a little while before I am good enough at this to get creative. If it's not too much trouble, could you please give me an example? I would really appreciate it.

Welcome to the PICAXE forum.

You need to explicitly enable parallel tasking by using "start0:", "start1:" labels in your program.

It looks like your deflector LED's are on C.2 and C,3, your torpedo LED's on C.5 and B.1. This example code should see the deflector LED's alternating quickly while those for your torpedo alternate slowly -

Code:
Start0: ; Deflector
Low  C.2
High C.3
Do
  Toggle C.2
  Toggle C.3
  Pause  300
Loop

Start1: ; Torpedo
Low  C.5
High B.1
Do
  Toggle C.5
  Toggle B.1
  Pause  1000
Loop
You can extend that to handle the buttons and PWM for the deflector in the Star0: code and the buttons and PWM for the torpedo in the Star1: code.
Thanks for the welcome. :)

So you're saying all I have to do is change the names of the subroutines from "deflector:" and "torpedo:" to "start0:" "start1:" ? I'll give it a try :)

Thank you, hippy :)
 

rob53

New Member
I tried changing deflector to start0:, but the code was flagged when I tried opening the simulator. It informed me the "start0" must be at the start of the program", but when I change "init" to start0, main to start1, deflector to start2 and torpedo to start3, the LEDs just come on when I load the program, they don't even respond to the switches.
 

hippy

Ex-Staff (retired)
It is usually a little more involved than simply changing labels to 'start0:' and 'start1:' etc. Each separate task has to handle the button pushes for its own task or ( as Texasclodhopper suggests ) the variables which control each task.

Did you try running the code in post #2 ? That will at least show you how multi-tasking works, even if it is just controlling the LED's.
 

rob53

New Member
It is usually a little more involved than simply changing labels to 'start0:' and 'start1:' etc. Each separate task has to handle the button pushes for its own task or ( as Texasclodhopper suggests ) the variables which control each task.

Did you try running the code in post #2 ? That will at least show you how multi-tasking works, even if it is just controlling the LED's.
Yeah, I kinda figured just changing those labels wouldn't do what i needed, and yes, I did run the code you posted. I totally understand the concept of parallel tasking, I'm just trying to figure out how to go about writing the code, like which commands to use. Since I have no education in programming, it helps me to see an example, but the example you gave me doesn't really apply to my problem. Don't get me wrong, I am very grateful that you're trying to help me.

If I'm understanding correctly, I should setup the switches and variables to count the button presses in the start0: label, but should I also put the commands for each button press in there as well (e.g. if buttonA=1 then *** , elseif buttonA=2, elseif buttonA=3 etc.), then have the program flow to a sub-procedure (start1, start2 etc.) for each task that's being carried out by it's respective variable?

Obviously I don't want all the commands to run simultaneously, as in your example code. I just want it to carry out a command while a previous command is still in progress.

For the time being at least....I'm stuck right here......

Code:
start0:
            symbol switchA=b1     'rename byte variable b1 to switchA
            symbol switchB=b2     'rename byte variable b2 to switchB
            
            let switchA=0             'switchA is used to count the button presses, let it begin at 0
            let switchB=0             'same as above

if C.0=1 then                         'if input C.0 goes high.....
         pause 50                      ' debounce
              inc switchA              '...increase switchA variable by increments of 1
if C.1=1 then
         pause 50                          'debounce                               
              inc switchB               ' same as above, but for switchB variable
endif                                       ' end of the if statement
If you guys could just nudge me along, I can stew over it until I figure it out.
 

hippy

Ex-Staff (retired)
Multi-tasking can be complicated. I think this should do what you want but haven't tested it with real hardware ...

Code:
[color=Navy]#Picaxe [/color][color=Black]20M2[/color]

[color=Blue]Symbol [/color][color=Purple]switchA [/color][color=DarkCyan]= [/color][color=Purple]pinC.0[/color]
[color=Blue]Symbol [/color][color=Purple]switchB [/color][color=DarkCyan]= [/color][color=Purple]pinC.1[/color]

[color=Blue]Symbol [/color][color=Purple]buttonA [/color][color=DarkCyan]= [/color][color=Purple]b0[/color]
[color=Blue]Symbol [/color][color=Purple]ButtonB [/color][color=DarkCyan]= [/color][color=Purple]b1[/color]
[color=Blue]Symbol [/color][color=Purple]pwmA    [/color][color=DarkCyan]= [/color][color=Purple]w1[/color]
[color=Blue]Symbol [/color][color=Purple]pwmB    [/color][color=DarkCyan]= [/color][color=Purple]w2[/color]

[color=Green]; ====== Button Handler ===================[/color]

[color=Blue]Start0:
  Do
    If [/color][color=Purple]switchA [/color][color=DarkCyan]= [/color][color=Navy]1 [/color][color=Blue]Then [/color][color=Black]: [/color][color=Blue]Resume [/color][color=Navy]1 [/color][color=Black]: [/color][color=Blue]End If
    If [/color][color=Purple]switchB [/color][color=DarkCyan]= [/color][color=Navy]1 [/color][color=Blue]Then [/color][color=Black]: [/color][color=Blue]Resume [/color][color=Navy]2 [/color][color=Black]: [/color][color=Blue]End If
    Pause [/color][color=Navy]50
  [/color][color=Blue]Loop[/color]

[color=Green]; ====== Deflector ========================[/color]

[color=Blue]Start1:
  Do
    Suspend [/color][color=Navy]1
    [/color][color=Purple]buttonA [/color][color=DarkCyan]= [/color][color=Purple]buttonA [/color][color=DarkCyan]+ [/color][color=Navy]1
    [/color][color=Blue]Select Case [/color][color=Purple]buttonA
      [/color][color=Blue]Case [/color][color=Navy]1
        [/color][color=Blue]PwmOut PWMDIV4[/color][color=Black], [/color][color=Blue]C.2[/color][color=Black], [/color][color=Navy]99[/color][color=Black], [/color][color=Navy]0
        [/color][color=Blue]For [/color][color=Purple]pwmA [/color][color=DarkCyan]= [/color][color=Navy]0 [/color][color=Blue]To [/color][color=Navy]400
          [/color][color=Blue]PwmDuty C.2[/color][color=Black], [/color][color=Purple]pwmA
          [/color][color=Blue]Pause [/color][color=Navy]2
        [/color][color=Blue]Next
      Case [/color][color=Navy]2
        [/color][color=Blue]For [/color][color=Purple]pwmA [/color][color=DarkCyan]= [/color][color=Navy]400 [/color][color=Blue]To [/color][color=Navy]0 [/color][color=Blue]Step [/color][color=DarkCyan]-[/color][color=Navy]1
          [/color][color=Blue]PwmDuty C.2[/color][color=Black], [/color][color=Purple]pwmA
          [/color][color=Blue]Pause [/color][color=Navy]2
        [/color][color=Blue]Next
        PwmOut C.2[/color][color=Black], [/color][color=Blue]off
        PwmOut PWMDIV4[/color][color=Black], [/color][color=Blue]C.3[/color][color=Black], [/color][color=Navy]99[/color][color=Black], [/color][color=Navy]0
        [/color][color=Blue]For [/color][color=Purple]pwmA [/color][color=DarkCyan]= [/color][color=Navy]0 [/color][color=Blue]To [/color][color=Navy]400
          [/color][color=Blue]PwmDuty C.3[/color][color=Black], [/color][color=Purple]pwmA
          [/color][color=Blue]Pause [/color][color=Navy]2
        [/color][color=Blue]Next
      Case [/color][color=Navy]3
        [/color][color=Blue]For [/color][color=Purple]pwmA [/color][color=DarkCyan]= [/color][color=Navy]400 [/color][color=Blue]To [/color][color=Navy]0 [/color][color=Blue]Step [/color][color=DarkCyan]-[/color][color=Navy]1
          [/color][color=Blue]PwmDuty C.3[/color][color=Black], [/color][color=Purple]pwmA
          [/color][color=Blue]Pause [/color][color=Navy]2
        [/color][color=Blue]Next
        PwmOut C.3[/color][color=Black], [/color][color=Blue]off
        [/color][color=Purple]buttonA [/color][color=DarkCyan]= [/color][color=Navy]0
    [/color][color=Blue]End Select
  Loop[/color]

[color=Green]; ====== Torpedo ==========================[/color]

[color=Blue]Start2:
  Do
    Suspend [/color][color=Navy]2
    [/color][color=Purple]buttonB [/color][color=DarkCyan]= [/color][color=Purple]buttonB [/color][color=DarkCyan]+ [/color][color=Navy]1
    [/color][color=Blue]Select Case [/color][color=Purple]buttonB
      [/color][color=Blue]Case [/color][color=Navy]1
        [/color][color=Blue]PwmOut PWMDIV4[/color][color=Black], [/color][color=Blue]C.5[/color][color=Black], [/color][color=Navy]99[/color][color=Black], [/color][color=Navy]0
        [/color][color=Blue]For [/color][color=Purple]pwmB [/color][color=DarkCyan]= [/color][color=Navy]0 [/color][color=Blue]To [/color][color=Navy]200
          [/color][color=Blue]PwmDuty C.5[/color][color=Black], [/color][color=Purple]pwmB
          [/color][color=Blue]Pause [/color][color=Navy]2
        [/color][color=Blue]Next
        High B.2
        Pause [/color][color=Navy]50
        [/color][color=Blue]Low B.2
        For [/color][color=Purple]pwmB [/color][color=DarkCyan]= [/color][color=Navy]200 [/color][color=Blue]To [/color][color=Navy]0 [/color][color=Blue]Step [/color][color=DarkCyan]-[/color][color=Navy]1
          [/color][color=Blue]PwmDuty C.5[/color][color=Black], [/color][color=Purple]pwmB
          [/color][color=Blue]Pause [/color][color=Navy]2
        [/color][color=Blue]Next
        PwmDuty C.5[/color][color=Black], [/color][color=Navy]0
      [/color][color=Blue]Case [/color][color=Navy]2
        [/color][color=Blue]PwmOut PWMDIV4[/color][color=Black], [/color][color=Blue]B.1[/color][color=Black], [/color][color=Navy]99[/color][color=Black], [/color][color=Navy]0
        [/color][color=Blue]For [/color][color=Purple]pwmB [/color][color=DarkCyan]= [/color][color=Navy]0 [/color][color=Blue]To [/color][color=Navy]200
          [/color][color=Blue]PwmDuty B.1[/color][color=Black], [/color][color=Purple]pwmB
          [/color][color=Blue]Pause [/color][color=Navy]2
        [/color][color=Blue]Next [/color][color=Purple]pwmB
        [/color][color=Blue]High B.3
        Pause [/color][color=Navy]50
        [/color][color=Blue]Low B.3
        For [/color][color=Purple]pwmB [/color][color=DarkCyan]= [/color][color=Navy]200 [/color][color=Blue]To [/color][color=Navy]0 [/color][color=Blue]Step [/color][color=DarkCyan]-[/color][color=Navy]1
          [/color][color=Blue]PwmDuty B.1[/color][color=Black], [/color][color=Purple]pwmB
          [/color][color=Blue]Pause [/color][color=Navy]2
        [/color][color=Blue]Next
        PwmDuty B.1[/color][color=Black], [/color][color=Navy]0
        [/color][color=Purple]buttonB [/color][color=DarkCyan]= [/color][color=Navy]0
    [/color][color=Blue]End Select
  Loop[/color]
 

rob53

New Member
Gosh, hippy, I don't know what to say, except thank you, thank you, thank you!!!

The code works great. It's doing exactly what I wanted, the PWM works better than it did before, responding a little faster to button presses. It must be the way the code is structured, because I noticed you didn't change any of the pwm variables.

I really didn't mean for you to write it for me, but after having read your solution, I probably never would have gotten it, at least not any time soon. Anyway, I'll be studying your code more closely to understand how it works, as I'll need to use it again, with minor variations, in other projects.

Thank you very much, hippy. I really appreciate your help.
 

hippy

Ex-Staff (retired)
I really didn't mean for you to write it for me, but after having read your solution, I probably never would have gotten it, at least not any time soon.
It was one of those cases where it is easier to do it ( and answer any questions arising ) than try and explain what it should become. In this case you were 98% there but struggling because you could not see the solution. Your programming skills are a lot better than you may think they are so I hoped it would help to overcome a frustrating hurdle rather than take the fun away.

The main trick is in use of SUSPEND to stop a task executing within the two tasks themselves, and the RESUME commands which kicks each into action when a button is pressed. Resuming a task that is not suspended does nothing and that can help simplify button handling by not needing any button de-bouncing.

An important change from your original code is the FOR-NEXT variables in the loops in each task have to use different variables or one task will affect what the other is doing. Hence pwmA and pwmB.

SELECT-CASE is my personally preferred method over IF-ELSEIF. There was nothing wrong with the way you had done it with IF-ELSEIF.

If you do have any questions on the code then please do ask.
 

Circuit

Senior Member
Hippy, just an interjection in this thread to say how much your advice is appreciated by those such as myself who are merely looking in on such threads. You take such care to help and your approach is always so constructive, informative and valuable. You are always patient, never critical nor scathing unlike some others occasionally, and your programming advice is excellent. Thank you for being there!
 

rob53

New Member
hippy said:
It was one of those cases where it is easier to do it ( and answer any questions arising ) than try and explain what it should become
I can see that now, after taking some time to closely examine what you did.


hippy said:
I hoped it would help to overcome a frustrating hurdle rather than take the fun away.
LOL, you definitely did not spoil any fun. What's fun (for me anyway) is writing the code once I have the solution in my head. I would have struggled with this one for quite a while.


The main trick is in use of SUSPEND to stop a task executing within the two tasks themselves, and the RESUME commands which kicks each into action when a button is pressed. Resuming a task that is not suspended does nothing and that can help simplify button handling by not needing any button de-bouncing.
Ingenious in how simple it looks. The command is sitting there, ready to go, just waiting for the "resume" to be given by the switch. It would have never occurred to me to use "resume" in the program BEFORE the suspend command. It seems counter-intuitive. That's what I call getting creative.

I do have a question about the "do-loops". Here's a quote from something I read in the PDF concerning the do-loop function

This structure creates a loop that allows code to be repeated whilst, or until, a
certain condition is met. The condition may be in the ‘do’ line (condition is
tested before code is executed) or in the ‘loop’ line (condition is tested after the
code is executed).
But in your code, it seems to be neither. It's as if you're just saying....."just do this, and loop"....not while or until, just do it. For example, you did this....

Code:
Start0:   
   Do
    If switchA = 1 Then : Resume 1 : End If
    If switchB = 1 Then : Resume 2 : End If 
    Pause 50                                                   
  Loop
Does the do loop really need to be there, or is it just habit to use that code structure? Could you have done it this way instead.....

Code:
start0:
     If switchA = 1 Then : Resume 1 : End If
     If switchB = 1 Then : Resume 2 : End If 
     Pause 50   
     goto start0
 
Last edited:

techElder

Well-known member
Rob53, on your "loop" question; six one way; half a dozen the other.

Then when you look at the do/loop structure once you see the do, you KNOW there are boundaries to the code contained. Everything is going to be between the do and the loop.

With the goto structure, you have to look for the end of a section of code to know that that section will start over at the top.

Then there's the until and the while with the do/loop instead of using if/then/else.

Clarity is the word of the day.
 

Paix

Senior Member
I think that the underlying tutorial in multi tasking was delivered subtly and is very informative for the general populous as well as addressing the specific issue of the OP.
 
Top