Multitasking and symbol limitations

Hello, I am new to the Picaxe but not new to basic programming languages. I have about 20 years of structured text programming in my professional past, but I have a project that I think the picaxe can do. I first learned about it many years ago in school where I used to teach the parallax bs2 to kids in a robotics program at my local community college. Regardless I have found a few things differnt that I expected and these questions I think are rather basic, but maybe the answers might help others that are searching as I did.

1) In an IF statement is it expected to not reference a "Symbol" that is also an IO pin? It would seem I can't name the I/O and use it for example. Am I just dumb and the "symbol" command is only related to variables?

The below is what I think I could do which the compiler says no to.
Code:
let dirsC = %11001011
let dirsB = %11110000
Symbol Myinput = pinB.0
Symbol OutputThing = C.1
Symbol flipflop = w0

do  
    if Myinput = 1 then
        OutputThing = flipflop
    Else
        Low OutputThing
    endif

This I know works, and I understand it, but am I just using the syntax wrong? Does it have something to do with defining the i/o wrong? Or the direction wrong? Or am I correct there is no way to label these pins as an alias?
Code:
do
    if pinb.0 = 1 then
        pinc.1 = flipflop
    Else
        Low c.1
    endif

2) I understand multitasking is a contentious discussion on the forum here. I considered its use to run one start making a 1.5 second toggling oscillator that is writing to bit0. Then I would use this in other starts. I would employ the pause command to do the wait as my understanding is it will branch during pause and scan my inputs. Everything is slow here so I don't need any sort of interrupt capability.

The 18M2 internal counter seems to only output 1hz and the overflow size isn't adjustable like the X2. So, I assume this is the only way to do it internally without an RTC. I don't want to add one and do not require that level of precision it offers. I bet the internal resonator is good enough for what I'm trying to do.

So, the real question. If the multitask is emulated parallelism by branching during commands like pause. How much of each line actually executes? I can't find in the manual a real description of how much time it would spend in start1 vs start0 ? Is pause the only command that it will branch during, as I assume serial commands will not as they cannot stop. So, it would run one line in Start0 then one in start1 ?

Thank You in advance for your time. I look forward to learning this microcontroller better but it's a huge refresh for me to go back to something so simple after all this time. I think some of my "cleverness" has been lost working with much more capable systems. But, I understand the pride in writing less to do more and make more efficient code. I think this back to basics might be good for me.

Phil
 
Last edited:
Hi,
... it's a huge refresh for me to go back to something so simple after all this time. I think some of my "cleverness" has been lost working with much more capable systems. But, I understand the pride in writing less to do more and make more efficient code. I think this back to basics might be good for me.
Welcome to the forum and +1 from me.

Most of PICaxe's behaviour is quite predictable when you understand what is happening "under the hood" (as hippy used to say), much of which is a "Legacy" from when the maximum program size was 256 bytes . Also of course, Rev Ed's existence (and this forum) being dependent on selling pre-programmed chips with a proprietary Operating System.

Firstly, the "compiler" converts Port.pin references to simply a "number" (0 to 5 for a small chip, up to around 32 for some X2s) so the surrounding syntax is quite critical (for example whether the number represents an input or an output). Thus the normal pin output control is a HIGH , LOW or TOGGLE <pin> , and C.x = <a number> is not. However, you can write pinC.x = <variable> where the pin will be set to the value of the LSbit of bit, byte or word variable (or a constant). Note there are also other versions such as PinsC = and OutPinsC = , etc.. It's also important to discriminate between variables and constants at the Symbol level, because IF variable <qualifier> constant/variable THEN ... is a valid syntax but IF constant <qualifier> variable THEN ... is not. Note that the IF .... structure has a significant number of different syntax versions (... GOTO or ENDIF, etc..)

At the programming level, the M2 PICaxes do not have a "Timer", they only have a pre-defined 16-bit variable named Time (or S_W7) which is updated by a system interrupt, nominally once every second. There is an internal hardware pre-scaler from the nominal 4 MHz clock (it's actually the PIC's Timer 1) down to a 20 ms period (50 Hz) to generate Servo Pulses (whether they are active or not). Then an interrupt updates a / 50 pre-scaler for "Time", but very sadly this pre-scaler value is NOT accessible to the User/Programmer. Note that some "blocking" commands (such as SERTXD("Hello World") or SERIN ... , etc.) disable the (system) interrupts, so the "timer" effectively stops for their entire duration.

Personally, I've never used the TASK feature and I don't believe anybody uses it "seriously", because a custom polling loop is far more practical. It's more intended as a Teaching/Demonstration aid (rather like the "Assembler" feature). Basically, it automatically switches to the next "active" task after EVERY "instruction"; which is very probably no more than a single program line, and might be much less. I don't know how PAUSEs are handled, but normally they are just a "Wait and Count to Ten" type of instruction. They can be "interrupted", but their timing does NOT continue after the RETURN from the interrupt (however long they are).

Finally a quick advert for the Code Snippets section of the forum. It used to be the repository for some "serious"/Library code, to avoid being buried in almost 1000 pages of "Active" discussion, but is now hardly needed.

I think that covers most of your questions, but feel free to ask more. ;)

Cheers, Alan
 
It's also useful to look at the syntax colouring for subtle hints between constants and variables.

C.1 (blue) is a constant, the pin number for use in commands such as high/low etc. Hence it cannot be allocated a different value.

pinB.0 (purple) is a variable, that contains the value 0 or 1 depending on the current state of the input pin

outpinB.0 is a variable, that contains the value 0 or 1 depending on the state of the output latch.

So to control an output pin, assuming it's already set as an output, you can use

high C.1
outpinC.1 = 1
outpinsC = xxxx

if you use pinC.1 = 1 the compiler will normally be able to 'auto-correct' this to outpinC.1 during compilation, but it is good practice to use the correct notation to start with, as outpinC.1 and pinC.1 are subtly differently in the silicon, one is the output driver latch state, the other is the current read state of the input.

Also note

high pinC.0

would result in switching either pin 0 or pin 1 high, depending on the state of input variable pinC.0
 
Thank you both. The post office has my programming cable hostage so I have been simulating. Wrote the program I intended to write and it works after much tweaking to catch unexpected combinations of user inputs. I will say the simulator is great compared to things I use that costs $1000s. So far that's my favorite part.

Now come the next phase. It's 177bytes. Can I do better? I'm not sure yet: but I feel like I have overused the "if" statements and could better combine the logic.

I suppose a truth table is in order.


Thanks,

Philip
 
Can I do better? I suppose a truth table is in order.
Hi,

No, Posting your code is the next stage, we're not telepathic ! Within [code ] [/code] tags (no spaces within the [ ] ), not an attached file, because most of us (or at least me) don't want to open files and clutter up our PCs with other people's "test" Programs. ;)

There are numerous ways to code most (and particularly PICaxe) Programs, depending whether you are primarily coding for Readability, Maintainability , Speed or "It Works".

Cheers, Alan.
 
Hi,

No, Posting your code is the next stage, we're not telepathic ! Within [code ] [/code] tags (no spaces within the [ ] ), not an attached file, because most of us (or at least me) don't want to open files and clutter up our PCs with other people's "test" Programs. ;)

There are numerous ways to code most (and particularly PICaxe) Programs, depending whether you are primarily coding for Readability, Maintainability , Speed or "It Works".

Cheers, Alan.
Alan,

The "can I do better" was rhetorical. I don't want you or the forum to do it. I want me to do it. I got it to work with about 70 lines, but that contains 20ish IF statements. I think I could maybe do it in less if I were more clever. My next step is to try some other methods of branching, or subroutines, instead of using goto to skip around. Maybe that can compartmentalize the code into smaller blocks for reuse.

Thank,

Phil
 
Hi,

The number of Program Lines is not a very useful measure. The number of bytes (from a Syntax Check) can be more useful and give some indication of the execution speed, because the interpreter's instruction (token) decoding time can be quite significant. But there are exceptions, the RETURN is very slow for its codesize, and the Subroutine codesize can vary between M2 types (because some have an additional "invisible" table).

The IF exists in two distinctive formats, the "Structured" IF ..... ENDIF (with derivatives using ELSE and EXIT, etc.) and the IF .. THEN {GOTO} , which personally I normally use because I "know" its execution time. For complex "decisions", the CASE ... SELECT can be very neat, but also very slow (that I've not been able to predict when). For rapid branching, the ON ... GOTO syntax (aka BRANCH.....) can be the most efficient in the right conditions. You may also be surprised how many of the instructions the complier converts to IF ... THENs (and GOTOs) "Under the Hood", but ultimately all Internal Conditional expressions will end with a GOTO anyway. ;)

Cheers, Alan.
 
pmacduffie,

I've been using picaxe chips for a fair while but I've never used multitasking and your questions made me curious so I did some research for my own knowledge and thought I'd post what I found out that is related to some of the points in your original post:

I understand multitasking is a contentious discussion on the forum here

This appears to me to simply be a case of "horses for courses".
i.e. for some programs multitasking might be a "better" solution and for others a "worse" one.

Hippy (now retired) was a RevEd employee with a lot of experience and access to internal details.

In this post
Hippy gives the following opinion about using multitasking:
I am not convinced doing it by way of multi-tasking is inappropriate and would even argue that allowing tasks to define their own timing with PAUSE is a lot simpler to follow than having to keep track of elapsed time with a variable.​
and then goes on to provide some example code with this comment:​
To me there's an elegance, simplicity, and more easily obtained understanding, with (this) code.​

and in this post
Hippy gives two other examples where he considers multitasking a "better" solution.
In the simplest of terms the PICAXE is executing commands at the 16MHz rateOne thing for which multi-tasking is useful for is "I'm Alive!" indication such as a flashing LED. With multi-tasking it's a simple case of adding an extra task such as -​
​
Start3:​
Do​
Toggle LED​
Pause 500​
Loop​
​
It's also useful if you have two buttons which being pushed do two different things. While it's often easy enough to put both in a single loop of a single task but it can make it easier to deal with separate tasks, one per button, each doing their own thing.​
​
If the multitask is emulated parallelism by branching during commands like pause. How much of each line actually executes?

In this post
Hippy explains:
The way multi-tasking works is it executes one command from one task, then executes one from another. That means how quickly any task is running, or how frequently a particular command is reached, depends on what is being done in other tasks.​
​
and in this post
Hippy explains:
In the simplest of terms the PICAXE is executing commands at the 16MHz rate​

I considered its use to run one start making a 1.5 second toggling oscillator that is writing to bit0.

As AlleyCat has already pointed out, you won't be able to do this on an M2 chip because the only timer we have access to is the 1sec TIMER variable. You will need to use an X2 chip to get a 1.5s oscillator.

I interpret all the info I've found to mean that a toggling task like you describe would always have timing jitter, and the scale of that jitter would depend on:
⦁ How many other tasks you have running that get one command run in a round-robin fashion and which task the sequence is executing when 1.5s elapses.
i.e. if the 1.5s period ends just before your toggle task starts then it will be as accurate a possible but if 1.5s ends just before you toggle task ends and there are 4 other tasks then your toggle won't occur until after 1 instruction in each of the other 4 tasks have been run.​
⦁ How long the commands in the other tasks take to execute. If the other tasks have a mix of long and short commands then the frequency you toggling task executes would also vary due to the other tasks sometimes running a long command and at other times a short command.

With multitasking most commands run at 16MHz and 1.5 seconds is quite a long period when compared to the duration of one command from each of the other tasks at 16Mhz so the jitter of in the toggling period may not be an issue.
e.g. if there are 4 other tasks, they all run non-blocking commands, and the longest non-blocking command in each task takes 500uS at 16MHz then the timing jitter would be 2mS.​

Whether or not you decide to use multitasking for the rest of your code, a timer interrupt might do what you want for your 1.5s toggling oscillator.
  • An interrupt is basically a preemptive task that runs immediately after the current command is completed.
  • A timer interrupt behaves no differently in a multitasking program than in a non-multitasking program, so you could try it out with either.
  • The timer interrupt is only available on the 20X2, 28X1, 28X2, 40X1, & 40X2 chips.
 
Hi,

My "negativity" about the multi-tasking feature is that the User/Programmer appears to lose all knowledge and maybe control over the speed of execution of the Program.. It is known that only a clock frequency of 16 MHz is permitted, so if two tasks are set up, will the code run twice as fast as "normal" (default), and what if one of those tasks is a PAUSE or is "suspended"? Also, I've never seen a specification of what is an "Instruction", because it's possible for the Programmer (or the Basic Complier, that often creates "Internal Macros") to write very long "lines" of code. I've already given an example of "Hello World", but much the same could apply to PEEK/POKE , READ/WRITE, etc., and a simple PWMDUTY compiles to about 30 bytes, or almost 50 "tokens". So what is the (potential) worst case Latency, even excluding "Blocking" commands (e.g. SERIN or PAUSEUS) ?

My (uncertain) recollection was that the Multi-tasking is only applicable to M2 (not X2) chips, so I tried a very quick test in the Simulator with the following program:
Code:
#picaxe 20x2
setfreq m4
do
start1:
  pause 100
start2:
  pause 200
loop
Thus, I was surprised to be told by the Syntax Check that the program compiles to 10 bytes of code. However, changing the PIXaxe type to 20M2 produces the response:
"Syntax error on line 2 at/before position 5 : Error: setfreq not allowed in multi task program"
Putting a PULSOUT after each PAUSE, and a DO : LOOP around each Start: section seems to indicate that the X2s do NOT multi-task.

Thus my contention is still that "nobody" uses the Multi-tasking feature (for serious applications) because AFAIK nobody has ever asked these questions, or at least never found any definitive answers. ;)

Cheers, Alan.
 
Thanks all for the input. When I asked the original multitask question I thought or maybe just fabricated in my mind that during a pause of 10ms the processor could do something else. Not run one line "pause 10ms" where it just sits and ruminates for 10ms and then run a command in start1 until it completes.I really wanted to get some work out of those 10ms vs just idling.. Maybe I concocted this from the fact the internal clock is much shorter in an m2 thab what you can see thus allowing it.

Oh well.
 
When I asked the original multitask question I thought or maybe just fabricated in my mind that during a pause of 10ms the processor could do something else. Not run one line "pause 10ms" where it just sits and ruminates for 10ms and then run a command in start1 until it completes.I really wanted to get some work out of those 10ms vs just idling.

The Parallel Task Processing section of the PICAXE manual 1 explains that a PAUSE in multi-tasking behaves exactly as you had hoped:
During task ‘dead’ processing time (e.g. during delays such as within a pause command) the core automatically realizes that there is no current processing to be carried out within that task and moves immediately onto the next task. Therefore the response of another task is not affected by a pause delay.​

The manual entry for the PAUSE command has this note that the PAUSE command is reduced in multi tasking programs.
During M2 part multi task programs the accuracy of pause is reduced due to the parallel processing. The minimum resolution is around 20ms in multi task programs. For greater accuracy use single task mode.​

also, when reading this info I just realized that a timer interrupt can't be used with multi-programing because the interrupt is an X2 only feature while multi-programing is an M2 only feature.

Edited; Replaced the description of how a PAUSE behaves in multi-tasking in a post by Hippy with a reference to the PICAXE documentation.
 
Last edited:
Okay so I did some testing with the osciliscope with the following results:

One main loop with a GoTo subroutine that doesn't use "gosub" or "return". Just goto and goto back. Not sure if that complies differently than a real gosub/return.

The "flashing" counter is set for a pause of 400ms. The actual pulse width at the i/o is 900ms.

When in the "subroutine" mode the code overhead is much less. The actual pulse is 600ms. Checks out, less instructions to process.

So, since the pause time is combined load it makes sense to me that a pause of 400ms doest output a timing signature of that to the output pin. I could fudge the value but the real problem is they are inconsistent times depending on which blocks of code are active. I could fudge that too but it's not as easy to understand.

Next I modified the code to run with multitasking.

The counter making the pulses was lengthened to 1000ms. Closer to the actual value I want.

Now the "sub routine" and normal loop output almost the exact same timing. But the timing is 800ms. Where I'm puzzled is how I got shorter than the pause delay itself?

So my last step was to consider hippy's statement that the accuracy was ~20ms when toggling between tasks. My counter routine is evaluating the word variable into a bit by pausing for 10ms and adding 10 to the counter. I figure it might be missing counts some how as there isn't sufficient time to get back to start0. Changed the counter logic to 20ms pauses and I'm bang on 1000ms.

So in this case, multitasking totally.worked for me and overcame the fact I can't control the overflow of "time" like the x2 can. One could certainly argue the interrupt based time instruction with a predictable overflow is better. But the 20m2 can do this multitasking with good enough accuracy for what im trying to do here.

I actually picked the m2 for its multitasking option over the x2 not really knowing what the limitations were. Had I known, the x2 and it's adjustable "timer" would have been the choice. Perhaps I will change the code and buy an x2 ro try.

Thanks again for the input and everyones time.


Edit - clarified time vs timer
 
Last edited:
pmacduffie,

Thanks for the update. The fact that with the counter logic set to 20ms pauses got you bang on 1000ms is useful information about multi task programs.

hippy's statement that the accuracy was ~20ms when toggling between tasks

One clarification, the PAUSE command is described as having "minimum resolution is around 20ms" in multi task programs, not accuracy.
This means that in a multi task program you can only get a PAUSE that is a multiple of 20mS, you can never get one that is a multiple of 1mS.
i.e. if you specify PAUSE 807 or 815 in a multi task program then the delay will always be about 800 or 820. The manual does not describe if the delay is rounded up or rounded down and I didn't find any posts about this.
 
The manual does not describe if the delay is rounded up or rounded down and I didn't find any posts about this.
Hi,

Exactly my point from previous posts. ;) The PICaxe is much more complex than at first sight, and in some respects is very "clever" with some features that are unexpected and/or unpredictable. Hence I usually try to include references to the On-Line Manual (or sometimes the Simulator/Syntax Checker), this Forum, or to actual Measurements or Tests. But I don't think that the Multi-Tasking feature was ever envisaged to be used with an Oscilloscope or Logic Analyser in your other hand. :) However, competent versions of both are (now) available for less than 10 Dollars/Pounds (maybe plus shipping) in the form of the "PCB 'Scope" (Picaxe Store) and the "USB 8-channel, 24 MHz Logic Analyser" (Ali-Express, etc.). Of course both need the assistance of a PC, but there are examples from both of them in some of my older threads.

The reference to "20 ms" is interesting because that is the/an "Internal Interrupt" time of the PICaxe Interpreter/Operating System, primarily employed in M2s to generate Servo Pulses and to Pre-scale the "Time" variable (NOT a "Timer"). Sadly it has two serious limitations: The (software) Pre-scaler/counter is not accessible to the (Basic) Program(mer), so detecting the event is not easy (it can be done with some PEEKSFRs of Timer 1) and the 20 ms period does NOT reduce (much) at the Higher Clock frequencies. This is because the Servo pulses are also (intentionally) 20 ms at 16 MHz (see Effect of increased clock speed ), and "Time" can get very stretched at Low clock frequencies. :( However, if you do need a (coarse) "Timer Interrupt", then you can connect any Servo-capable Output pin (almost all of them) to an Interrupt Input pin (of which there are rather fewer). It seems a complex situation whether the same pin can be used for both (at the same time).

In microcontroller terms, 20 ms seems an eternity to me. Most users seem horrified that the simpler Basic PICaxe instructions execute about 400 times slower than the "Base PIC" raw clock cycle, but that's still only around 50 us with a 32 MHz clock and the timing Resolution can be around 1 microsecond (PULSEIN/OUT, PAUSEUS, etc.). Note that a (User) Interrupt automatically terminates any PAUSE (on Return) but you will need to test if this applies only to the "Task in Hand", or how that might be defined.

Cheers, Alan.
 
Back
Top