Writing Maintainable Picaxe Code

mrburnette

Senior Member
I will agree that a non-RevEd pre-processor could take a prototype declaration such as
Integer function wxyz( integer x, integer y)
and convert this to PICAXE BASIC by utilizing existing named or unnamed RAM, but it seems to me that one should just buy naked uC and use compilers designed for such purposes... I do it all the time and have less than $100 in the tool chain.

But, you go guys. Whatever makes one happy... or, as I remember from the 1960's : Live, love, laugh & be happy

-Ray
 

Brewer

New Member
System for Parameter Passing and Local Variables in PICAXE BASIC

Tom2000 wrote:
Picaxe BASIC, as it stands, isn't suited for large programs. Not even one little bit. With processors having limited program memory, this usually doesn't create insurmountable problems; you won't be writing large programs. But with the new breed supporting programs of several thousand lines, readability and maintainability become important issues.

So, when I write a large micro program, I invariably choose the C language. C, like most modern high-level languages, is well suited for large programs for a number of reasons. In part, because it supports long, descriptive function ("subroutine") and variable names. Also, it supports parameter passing and local variables in its functions. (And, for microcontroller purposes, C is a good language because it generates fairly compact and efficient machine code, as does Picaxe BASIC.)

Local variables are particularly important. A local variable is used solely within its function, and isn't visible to the rest of the program. A local variable may even share the name as another local does in another function, but these locals are completely independent of one another. When programming a Picaxe, however, no local variables means that it's just too easy to inadvertently change the value of a variable used elsewhere in your program.
I agree. To partially address these issues I have developed a template, or system of coding, that allows parameter passing and local variables in PICAXE BASIC. This was developed on a 08M2 chip using MacAXEpad and should work on other chips.

I'm new to PICAXE, so I was looking for a place to post this information and came across this excellent discussion. I appears that several approaches have been suggested to address these issues but I didn't find a generic system that should work in most cases. Please let me know if I am reinventing the wheel or if you have questions or comments.

My system of coding implements parameter passing by value to and from subroutines on a PICAXE 08M2 micro controller and makes effective use of high RAM for temporary storage. This system allows the creation of variables that are private to a subroutine and even supports recursive subroutine calls within the 8 nested levels that the chip supports. In pseudocode you can do the following:
Code:
symbol bA = b1
symbol bB = b2
symbol bC = b3
bA = 3
bB = 4
gosub Add_1( bA,  bB,  bC)
print(bA, bB, bC) reports 3, 4, 7
end

Add_1:(input bA_1, input bB_1, output bC_1)
	symbol bA_1 = b2
	symbol bB_1 = b3
	symbol bC_1 = b1
	bC_1 = A_1 + B_1
Return bC_1
Note that the subroutine Add_1 walks all over the variables defined at the beginning of the program yet only bC is changed by the call to Add_1. This is accomplished by creating a parameter queue in high RAM to allow parameter passing by value and a variable queue in high RAM for temporary storage of general purpose (gp) variable values while the gp variables are reused in subroutines.

I call these "queues" for lack of a better term. Groups of bytes that are stored in a queue are accessed first-in-last-out, like a stack, but these data groups are accessed via pointers. These queues store data from the top down. Data in the two queues are located via two pointers defined as two gp byte variables. These pointers to data on the queues are managed by subroutines that allocate and deallocate space in the queues.

The template requires that each call to a subroutine, that passes parameters in and out, be wrapped in template code (5 lines of code). It also requires that template code be included at the beginning (at least 6 lines of code) and end (5 or 6 lines of code) of each subroutine that has input and output parameters and uses local variables. The attached demo program gives a complete description of the system.

There is, of course, a cost to using this system. It appears to be fairly fast but I haven't timed it. I don't have enough experience with interrupts to say how well it will work with those. The demo program uses 462 bytes of program memory and includes one subroutine to add two numbers and another subroutine to recursively calculate the factorial of the sum. The queue sizes in the demo allow the effective definition of up to 82 bytes of directly addressable variables at the same time leaving the remaining RAM available for other purposes. The queue sizes can be adjusted to use more of less RAM as needed.

That's it. What do you think?
 

Attachments

BESQUEUT

Senior Member
Code:
'Calculate wCFact = bC!
'----Start subroutine call for Factorial_2--Template Code
Poke cParmQTop,  1			'Number of bytes to pass to the subroutine. 
gosub AllocateParm			'Allocate space in the parameter queue for the bytes to be passed to the subroutine.
poke bParmQPtr, bC			'Poke to the parameter queue pointer location the parameters to pass to the subroutine.
gosub Factorial_2 			'Call the subroutine.
peek bParmQPtr, word wCFact   'Get the returned value(s) from the parameter queue at the pointer location.
gosub DeallocateParm		  'Deallocate the returned parameter space from the queue.
'----Subroutine call finished---------------------------
To my mind, this is totally unreadable.
But I think that the idea can be widely improved using the #macro directive.

For bytes variables, you also can use the PUSH/POP commands => no need to re-create a stack...

@Technical : unfortunately PUSH/POP does not work for word variables...

The "local variable" problem have also been adressed with the PUSHRAM command.
 
Last edited:

techElder

Well-known member
I guess I'm not writing the right programs, but this seems like a solution to a problem that I haven't encountered.

Being "new to PICAXE", Brewer, what kind of program have you been writing that needs to use so many variables that you had to invent a way to handle them beyond how PICAXE already does handle them? (PUSH/POP, PUSHRAM/POPRAM).
 

oracacle

Senior Member
while I did only skim through the thread and there are some interesting points, one thing I did notice was there was no mention of keeping a change log.
As a result of having a change log you can go back and find point before the error start, along side this keeping a copy of the software before any changes are made makes it easy to either switch back with no guessing what it was before. you can keep track of the version of software in the title (ie flash controller V0.1). even create entire new workbooks for updated system if major changes are made, personally I call these proto 1, proto2 ...

you can even keep extra info in there on how things are connected and how memory tables are setup
heres a basic version that I made for the macro rail, the first time I had done it and I found it very useful

Code:
#rem
Camera Sutter
-------------
	 ------
a.0--->|1  6|
     --|2  5|<----Focus
     | |3  4|--
     | ------ |
     |	  |
     | ------ |
a.1--->|1  6| |
     +-|2  5|<----Shutter
     | |3  4|-+---Common For camera
     | ------
     |
  Ground

using 2 4N25 opto isolators. Common refers to the commen connection to both focus and shutter circuits.

I beleive that comman would be the ground side of the camera but a prefrence to not interfering with camera
votlage and circuits would be prudent due to the cost of repairing or replacing such equipment

The wired shutter release has been modified and fitting with 3.5mm stereo plug interface to allow easy connection
to other home made device

sleeve - Camera comon
Ring   - Shutter
Tip    - Focus

Sleeve  |Ring |Tip
-------- ---\ /--\
	 | |  | |--|
-------- ---/ \--/

User interface
--------------

Display and button to be set in "Cash point" type arrangment with
button at the side of display
20x4 OLED Display
	  |---|
row1 - 0|---|0
row2 - 0|---|0
row3 - 0|---|0
Port A, pins a.2 and a.3 will supply power to the switches on either side of the display
a.2 will supply left side while a.3 will supply right side
The power on button is on the top of the control box
Peizo sounder inside the control box
LED lower rigth front of the control box

Memory Setup
------------

system should be able to store some data, 6 seem like a good option
memory map for storing camera and rail data
Slot--->|  0  |  1  |  2  |  3  |  4  |  5  |
---------------------------------------------
strimage|20|21|30|31|40|41|50|51|60|61|70|71|
strapert|22|23|32|33|42|43|52|53|62|63|72|73|
 strshut|24|25|34|35|44|45|54|55|64|65|74|75|
strdepth|26|27|36|37|46|47|56|57|66|67|76|77|
 overlap| 28  | 38  | 48  | 58  | 68  | 78  |
strfocal| 29  | 39  | 49  | 59  | 69  | 79  |

Initial address slot 0
next slot address is addres + 10 * slot number

scancol subprocedure generates a number from 0 to 5 which can be the multipier

Change Log:
Proto 0 - 25/12/13
Never used on hardware beyound testing circuit, Used 3 program slots for basic operation
slot 0 - intialise
slot 1 - Camera setting and DoF calculions
slot 2 - Main Menu

Proto 1 - 12/01/14
slot 0 - initalise
slot 1 - main menu, includes input of all information required for stack to be performed
Slot 2 - Sub menu, menus for settle time, stack splitting saving and loading camera and rail data and re-calibration
First used on hadrware 24/03/14, perliminary testing while waiting for reast of chassis comonent to arrive
25/03/14 - added check for rail connection
01/04/14 - ran full sequnce succesfully, few minor changes made duing test
03/04/14 - slot 2 added
03/04/14 - stack splitting adding to avoid filling camera memory card added
03/04/14 - re-calibration feature added
03/04/14 - variable settle time added
04/04/14 - save and load feature added

Proto 2 - 06/04/14
Large changes made to general operation of the system to improve perfomnce and functionality
slot 0 - initialisem main menu and sub menu
slot 1 - Depth shooting, menu for inputing all data
slot 2 - conitinueos movement from front of rail to end
slot 3 - save and load menu
06/04/14 - added main menu and sub menu to intialise slot
06/04/14 - small alteration to depth shooting and general sub procedures
06/04/14 - added continuos movement function
06/04/14 - moved save and load sequnce to seperate slot
06/04/14 - added mirror lock up function
06/04/14 - added restart in sub menu
06/04/14 - moved settle time to fire subprocedure

13/04/14 - Moved focus release for sequnce abort to cancil
13/04/14 - Removed program flag from return to menu to allow saving
13/04/14 - Added reset of curent carriage position when returning to main menu.
13/04/14 - Altered progress display in conituous shoot
13/04/14 - Added Postion display to conituous shoot
13/04/14 - Added power off during calibration

15/04/14 - Increased maximum Mounting ring to lense distance to 1000mm
15/05/14 - Added abilty for system to detect and correct rail over run upon starting

21/04/14 - Altered flow of calcultions pertaining to DoF, magnification, image depth, apeture.
		This included consolidating to one comand for each instead of doing each part
		(perenthesis) indivually
21/04/14 - Changed defult overlap percentage t0 30% from 50%
21/04/14 - Update Save and Load selection, memory is split into folders (for user ease) each
		folder contains 4 memory slots. There are 255 slots of memory, freeing up
		some word variables may allow this to increase but there is little need beyound
		the number of storage places currently available. Information is stored in blocks
		of ten addresses starting at address 20. File save_load v1.1, this is revision 2 of the save_load programme

31/07/14 - Added ability to change CoC and flange distance to initialise programme
	   - Altered loading of FPU data and eeprom to allow for CoC and FtS changes
	   - Altered calibration menu to allow update of camera info (CoC, FtS) to be changed without full calibration

13/08/14 - Move programme V1.1 allowing custom distance between shots of 1mm, 0.1mm, 10um, 1um.
#endrem
later projects have more detailed change logs
 

inglewoodpete

Senior Member
while I did only skim through the thread and there are some interesting points, one thing I did notice was there was no mention of keeping a change log.
As a result of having a change log you can go back and find point before the error start, along side this keeping a copy of the software before any changes are made makes it easy to either switch back with no guessing what it was before. you can keep track of the version of software in the title (ie flash controller V0.1). even create entire new workbooks for updated system if major changes are made, personally I call these proto 1, proto2 ...
Don't forget the automatic backup feature of PE6. It's not a change log but it does create a code backup file every 10 minutes. BTW, I also use a change log with the filename containing the version when I'm developing or revising a project's code.
 

Brewer

New Member
Code:
'Calculate wCFact = bC!
'----Start subroutine call for Factorial_2--Template Code
Poke cParmQTop,  1			'Number of bytes to pass to the subroutine. 
gosub AllocateParm			'Allocate space in the parameter queue for the bytes to be passed to the subroutine.
poke bParmQPtr, bC			'Poke to the parameter queue pointer location the parameters to pass to the subroutine.
gosub Factorial_2 			'Call the subroutine.
peek bParmQPtr, word wCFact   'Get the returned value(s) from the parameter queue at the pointer location.
gosub DeallocateParm		  'Deallocate the returned parameter space from the queue.
'----Subroutine call finished---------------------------
To my mind, this is totally unreadable.
But I think that the idea can be widely improved using the #macro directive.

For bytes variables, you also can use the PUSH/POP commands => no need to re-create a stack...

@Technical : unfortunately PUSH/POP does not work for word variables...

The "local variable" problem have also been adressed with the PUSHRAM command.
Since I can't change PICAXE BASIC syntax, this is the best I could do with regards to readability. The point is that my template is a pattern of code used when a subroutine is called and a pattern used within the subroutine. This template code is only applied if you are passing parameters to/from a subroutine that uses local variables.

With regards to the PUSH/POP/PUSHRAM commands, I could have used them if the 08M2 chip I am using supported them. I suspect, however, that using PUSH/POP wouldn't have done much to improve readability. In most modern languages, the complexity of having local variables and doing parameter passing is hidden from the programmer, but we're using PICAXE BASIC.

Texasclodhopper wrote:
Being "new to PICAXE", Brewer, what kind of program have you been writing that needs to use so many variables that you had to invent a way to handle them beyond how PICAXE already does handle them? (PUSH/POP, PUSHRAM/POPRAM).
My first idea for a PICAXE project was to build an electric water pump controller. For this project I needed to measure the power being used and turn the pump on or off based on the power reading. I quickly found that PICAXE BASIC doesn't do floating point (fp) calculations. Doing accurate electric power calculations without fp math is hard, so I went off on a tangent and developed a system for doing fp math on a 08M2 chip. This system worked, but used half of the general purpose variable space to just do fp addition, subtraction, multiplication, division, square root, and factorial. I didn't help that each fp variable uses 3 bytes, but the real killer was the lack of parameter passing and local variables. I haven't yet converted my fp math subroutines to use my parameter passing system. It might take to much program memory and be too slow for most applications.
 

Jeremy Harris

Senior Member
Well, I manage to measure, record and log 16 bit bi-directional power data without needing to use floating point math, and have to say it isn't exactly hard. Mind you, I have a background in coding without FP math, so have a repertoire of workarounds. FWIW, I've also been using 10 bit temperature measurement and logging, again using positive and negative numbers, that would include decimal places if in FP, and it's pretty easy to just use integer math to do the job.
 

BESQUEUT

Senior Member
Since I can't change PICAXE BASIC syntax, this is the best I could do with regards to readability....
I suspect, however, that using PUSH/POP wouldn't have done much to improve readability. In most modern languages, the complexity of having local variables and doing parameter passing is hidden from the programmer, but we're using PICAXE BASIC.
As I said, with Picaxe you need to use the #macro directive. Did you try it ?

I am also using VB.NET to write very large programs. Of course, there are many more tools.
But for readability, there is no such difference.
Picaxe as now sufficient tools to write readable programs.
You can certainly also write very unreadable, and unmaintainable code, but that is true for any language.
 

Brewer

New Member
Well, I manage to measure, record and log 16 bit bi-directional power data without needing to use floating point math, and have to say it isn't exactly hard. Mind you, I have a background in coding without FP math, so have a repertoire of workarounds. FWIW, I've also been using 10 bit temperature measurement and logging, again using positive and negative numbers, that would include decimal places if in FP, and it's pretty easy to just use integer math to do the job.
On second thought, you're right, it shouldn't be that hard to do the the power calculations with positive integer math. To be honest, I think it just didn't appeal to me, so I went off on a tangent. It's a hobby, darn it, practicality has nothing to do with it.:D
 

techElder

Well-known member
You understand that this topic is not for everyone, its great that you spent the time working this out. Nothing is ever wasted.

You will have something to work with for awhile, and you will be ready when the hardware gets updated to bigger and faster stuff.

Good job!
 

Brewer

New Member
You understand that this topic is not for everyone, its great that you spent the time working this out. Nothing is ever wasted.

You will have something to work with for awhile, and you will be ready when the hardware gets updated to bigger and faster stuff.

Good job!
Thanks. I was beginning to think it was pile on newbie time. I learned some things in the process, so fair enough.:)
 

techElder

Well-known member
Oh, no. Don't get that way. We don't do that here. We don't let much go by without interrogation, but its all for the good.

You'll look at parts of that code in a few months and wonder why you didn't do this or that. Something new will come to mind.

Improve it ... that's the idea. No one writes a best selling novel the first time.
 

mrburnette

Senior Member
Wow....
It has been 3.5 years and imagine my surprise to find an active topic that I had commented on page #9.

Lots of the same voices, hi guys!

Ray
Some of my projects: https://www.hackster.io/rayburne

FYI:
I find myself today looking over the PICAXE BASIC code I wrote in 2011 for the OLED serial display... I am updating my wife's OLED clock and I will be using an ESP8266 ... taking the 14M2 ( I think) back to factory. Amazingly, I had absolutely no problem following my code. BASIC is a fine language if your uC resources match your project needs. I mainly do C++ these days, but there is absolutely nothing wrong with PICAXE... great little device. I think there is more bang for the buck available, but honestly it is meaningless for hobby number of units.

Most importantly, if PICAXE is handling your hobby needs and keeping you mentally stimulated - stay where you are. Some of us must roam and are always looking for the next "perfect" thing but never satisfied.
 

mleeee

New Member
I'm an old computer chap, helping out at a school.
This is great stuff.
In the first year at this school, the kids barely have time to program anything, and their excitement when they get something to work is reward enough.
So, no time for the above.
Using SYMBOL to define variable names is about the best readability trick we have time for.

The notes above bring great, big program, practice to the relatively small Picaxe world.

What would the top 3 points be for beginners?
I might be able to introduce those to a class in the time we have.

Matt Lee
 

mleeee

New Member
...ah yes ... I'm trying to save some bytes in a chokka-block full 40X2 ... and I have lots of text going to an OLED display.
Reading your notes above, I shall move the text to the EEPROM.
I don't really want the code to say something like 'Print Message 5', as that makes the code unreadable (I have to go look up 'Message 5' to find out what I was about to say)

Best way to do it, anyone?

I decided to repeat the message in the comments!

Matt Lee
 

BESQUEUT

Senior Member
My first one would be : "apprenez à marcher avant d'essayer de courir..."
Witch I can translate : "Learn walking before trying to run..."
Or "Start with very smalls and obvious projects, well understand them, and only then goto bigger "
Of course, my second one would be "Do not use the GOTO command"
And my third : if your code does not work as you want, start by making it more readable...
 
Last edited:

hippy

Technical Support
Staff member
Reading your notes above, I shall move the text to the EEPROM.
I don't really want the code to say something like 'Print Message 5', as that makes the code unreadable (I have to go look up 'Message 5' to find out what I was about to say)
If your EEPROM messages are indexed by number, you can use named comments to make that easier ...

Code:
Symbol TEMPERATURE_IS = 5

b0 = TEMPERATURE_IS
Gosub ShowMessage
 

BESQUEUT

Senior Member
If your EEPROM messages are indexed by number, you can use named comments to make that easier ...

Code:
Symbol TEMPERATURE_IS = 5

b0 = TEMPERATURE_IS
Gosub ShowMessage
And with a MACRO :
Show_Message (TEMPERATURE_IS)
To my mind, no need for any comment with a so "readable" code...
 
Top