Good Picaxe programming practices

geoff07

Senior Member
I thought it might be helpful to beginners at least to have a blog that covered good programming practices. Some might simply be helpful habits that save a bit of time. Others might be approaches that potentially make the code easier to maintain. Inevitably one person's practice could be another person's bad habit. It all depends on what the code is for and whether it has any chance of a long life.

Many users of Picaxes will not have had any software training. Electronics has been after all mainly a hardware discipline until now. However, the two disciplines of hardware and software engineering are merging rapidly, and each can learn from the other.

So I invite forum members to add short tips that they feel might be helpful to those without a background in software, with the objectives of saving time in design, reducing debugging and maintenance effort, prolonging the life of code, etc.

I thought I would start with a simple statement about the use of the GOTO, a bit of a hobby-horse of mine, but based on experience. I have programmed in most language types over the years and currently have around 3000 lines of Picaxe basic running 24 x 7 in my house operating things that I don't want to go wrong over many years.

GOTOs are unnecessary and lead to code that is hard to maintain
-----------------------------------------------------------------------
Why? Well it isn't the goto itself that is the problem, it is the label to which the goto points. It is impossible in any program of length to be perfectly clear about the paths through the code unless they are explicitly coded. If you look at a piece of code that has labels in it, from where does control come from when the program runs? Generally, it is very hard to tell. You only have to look at some of the beginners spaghetti that is on the forum to know this.

There is lots of academic background about program proving that explains why, but in practice, all you need to do is use the explicit control structures that are available instead. If you don't have labels then you won't use them. Furthermore, using proper structures will help you think through the program flow much more rigorously. The result will be code that is easier to understand as you write it, and much easier to modify a year or two later.

So, where you see

main:
(code)
goto main

simply use

Do
(code)
loop

You can nest these loops quite deeply.

In fact so far as I can tell there is only one case in Picaxe Basic where a goto is necessary, and that is when a timeout is used in serial communications. If you have to have gotos, such as in this case, the safe way to do it is to make sure gotos always point downwards and cannot create loops. Loops should have proper control structures (e.g. loop while or loop until etc.). The EXIT command is a very useful way of exiting loops without using a goto.

Variable labelling
-------------------
While I'm on a roll I will add a second tip. Some may think this unnecessarily bureaucratic, but it really does make a difference!

I always name variables and constants with a prefix that explains what the variable type is. This way, it is much harder to mistakenly assign e.g. a Word to a Bit and not understand the subsequent program behaviour. I use W_, B_, C_, F_ as prefixes for Words, Bytes, Compiler Constants and Flags (bits). If there are different types of constants, e.g. in my finite-state-machine code, I use St_ and Ev_ for states and events. You can invent your own terminology. The effect is to make it clear when you read the code just what each name represents and is capable of. You might be surprised just how many problems this prevents.

That is my two-pennyworth for the moment. I don't expect everyone to agree, but that isn't the point. Perhaps you have your own tips that you will contribute.
 

Jamster

Senior Member
Use Indents!!
It makes life so much easier when you have multiple nesting and long winded programs!

Take your pick:
Code:
SYMBOL hello=b1
SYMBOL goodbye=b2

main:
do
if hello=10 then
sertxd("hello")
else
select goodbye 
case 12
sertxd("woop")
case 13
sertxd("woodle")
endselect
endif
hello=goodbye+2
goodbye=hello+3
loop
Code:
SYMBOL hello=b1
SYMBOL goodbye=b2

main:
      do
            if hello=10 then
                 sertxd("hello")
            else
                 select goodbye 
                 case 12
                         sertxd("woop")
                 case 13
                         sertxd("woodle")
                 endselect
            endif
            hello=goodbye+2
            goodbye=hello+3
      loop
 

mrburnette

Senior Member
I build blank project templates for each uC that I use. I start every project with the template and File-Save-As to create the new project. Later when everything is complete and working, I have been known to clean-up but most of the time I just leave unused Symbols because the compiler will not utilize and will not penalize storage for unreferenced Symbols.

Example 08M2
Code:
' PICAXE-08M2
'  
' ADC is at junction, PIN #3 on PICAXE 08M2 = C.4
'
' Compiler directives 
#picaxe 08m2
#Terminal 4800

'08M2 valid values - k31, k250, k500, m1, m2, m4, m8,m16,m32
' The default power-up operating frequency is 4MHz
#freq m4
'
'SYMBOL table - aliases for byte values
SYMBOL ChangeMe00 = B0		' Danger B1-B0 overmapped by W0 & by bit0 - bit7
SYMBOL ChangeMe01 = B1 
SYMBOL ChangeMe02 = B2		' Danger B3-B2 overmapped by W1 & by bit8 - bit15
SYMBOL ChangeMe03 = B3
SYMBOL ChangeMe04 = B4		' Danger B5-B4 overmapped by W2 & by bit16 - bit31
SYMBOL ChangeMe05 = B5
SYMBOL ChangeMe06 = B6		' Danger B7-B6 overmapped by W3
SYMBOL ChangeMe07 = B7
SYMBOL ChangeMe08 = B8		' Danger B9-B8 overmapped by W4
SYMBOL ChangeMe09 = B9
SYMBOL ChangeMe10 = B10	' Danger B11-10 overmapped by W5
SYMBOL ChangeMe11 = B11
SYMBOL ChangeMe12 = B12	' Danger B13-B12 overmapped by W6
SYMBOL ChangeMe13 = B13
SYMBOL ChangeMe14 = B14	' Danger B15-B14 overmapped by W7
SYMBOL ChangeMe15 = B15
SYMBOL ChangeMe16 = B16	' Danger B17-B16 overmapped by W8
SYMBOL ChangeMe17 = B17
SYMBOL ChangeMe18 = B18	' Danger B19-B18 overmapped by W9
SYMBOL ChangeMe19 = B19

'SYMBOL table - aliases for word values
Symbol ChangeMe99  = W10
SYMBOL ChangeMe98 = W11
SYMBOL ChangeMe97 = W12
SYMBOL ChangeMe96 = W13

'SYMBOL table - aliases for system values
  Symbol CLOCK = m4
  Symbol BAUD = N4800_4
  Symbol Serial = C.0

Initialize:
  ' Set frequency to CLOCK value since different than the 8MHz default
  setfreq CLOCK

  ' Send startup message to serial port
  Gosub Display


Main:

  STOP


SubRoutine1:
  Return


SubRoutine2:
  Return


Display:
  
  Return


END

'''''''''''''''''''''''''''''''''''''''''''''''''''''''
  ' EEPROM  0, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 16, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 24, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 32, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 40, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 48, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 56, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 64, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 72, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 80, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 88, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 96, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 104, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 112, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 120, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 128, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 136, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 144, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 152, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 160, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 168, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 176, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 184, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 192, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 200, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 208, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 216, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 224, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 232, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 240, (0, 0, 0, 0, 0, 0, 0, 0)
  ' EEPROM 248, (0, 0, 0, 0, 0, 0, 0, 0)
 
Top