Need expert help on concept - incrementing counter to set pin states

I am looking for the most efficient way to achieve the following scenario, and am a bit stumped, I have set up on my 08M2 1 of the pins as inputs, and 4 as outputs, when the input pin is high a count is incremented and the outputs need to respond in the following manner:

(Note this is not code just my way of describing it for my own simplicity, but hopefully you get the idea of what it needs to do, my code is on another computer so I can't post it yet but basically it involves a lot of if <x and >y, and is not fully working anyway, and I want to try and keep it very efficient and small/fast)

if counter = 0 pins = %00010111 pause 40 %00000000
if counter = 6 pins = %00000001 pause 40 %00000000
if counter = 8 pins = %00000100 pause 40 %00000000
if counter = 12 pins = %00000011 pause 40 %00000000
if counter = 16 pins = %00000100 pause 40 %00000000
if counter = 18 pins = %00000001 pause 40 %00000000
if counter = 24 return counter to 0

For all the counter values in between those above pins = %00000000

I'd prefer not to have to do it using lots of "<" and ">" if possible. Thanks.
 

westaust55

Moderator
one example: ==> Down from 78 to 40 bytes (see below working version of your code for my version):
I have not added the commands such as DIRC = . . . . .
Code:
; 78 bytes version
SYMBOL Counter = b0
SYMBOL outputdata = b1

if counter = 0 THEN : pinsc = %00010111 pause 40 pinsc = %00000000 ENDIF
if counter = 6 THEN : pinsc = %00000001 pause 40 pinsc = %00000000 ENDIF
if counter = 8 THEN : pinsc = %00000100 pause 40 pinsc = %00000000 ENDIF
if counter = 12 THEN : pinsc = %00000011 pause 40 pinsc = %00000000 ENDIF
if counter = 16 THEN : pinsc = %00000100 pause 40 pinsc = %00000000 ENDIF
if counter = 18 THEN : pinsc = %00000001 pause 40 pinsc = %00000000 ENDIF
if counter = 24 THEN : counter = 0 ENDIF
LOOKUP command version:
Code:
; 40 bytes program space
SYMBOL Counter = b0
SYMBOL outputdata = b1

  IF counter = 24 THEN
  	counter = 0
 ELSE
  	Lookup Counter, (%00010111,%00000000,%00000000,%00000000,%00000000,%00000000,%00000001,%00000000,%00000100,%00000000,%00000000,%00000000,%00000011,%00000000,%00000000,%00000000,%00000100,%00000000,%00000001,%00000000,%00000000,%00000000,%00000000,%00000000), outputdata
  	pinsc = outputdata 
  	pause 40
  	pinsc = %00000000
 ENDIF
 
Last edited:

hippy

Technical Support
Staff member
Code:
#Picaxe 08M2

Eeprom  0, ( %00010111 )
Eeprom  6, ( %00000001 )
Eeprom  8, ( %00000100 )
Eeprom 12, ( %00000011 )
Eeprom 16, ( %00000100 )
Eeprom 18, ( %00000001 )

Symbol counter = b0
Symbol outbits = b1

Do
  Do : Loop Until pinC.3 = 0
  Do : Loop Until pinC.3 = 1
  counter = counter + 1 // 24
  Read counter, outbits
  pins = outbits
  If outbits <> 0 Then
    Pause 40
    pins = %00000000
  End If
Loop
 

AllyCat

Senior Member
Hi,

....this is not code just my way of describing it for my own simplicity...
.. I want to try and keep it very efficient and small/fast...
You may need to be much more specific about your actual code requirements if size and speed are critical (and which is more critical).

Because you have written your original code as a "list" of IFs (with no ELSEs), the "IF counter = 24..." will execute approximately as if there were a "PAUSE 5" before it (compared with the delay to the "IF counter = 0...."). But the overall execution time will be the same because ALL the IFs are always executed.

The LOOKUP is actually converted by the PE into a list of 24 IFs and ALL are executed, but somewhat faster than separately in the Basic program. I believe a LOOKUP with 24 data entries executes in approximately the time of a PAUSE 6. So Westy's version would execute faster (than yours) if you had more IFs, but slower if you had less. This was discussed from #13 in this thread.

It might be that a SELECT ... CASE structure is the correct one to use for your application. But bear in mind that "under the hood", the PE also converts that to a string of IFs, perhaps with or without ELSEs.

Hippy's code appears to be smaller (~33 bytes) but I believe will execute slower and/or with more variability in the execution time.

EDIT: For simply stepping through a list of actions (on the press of a button) then hippy's method of Reading the "next" value from EPROM or a Table seems to be by far the best method.

Cheers, Alan.
 
Last edited:

geoff07

Senior Member
I suggest using symbols instead of all those binary constants, e.g.
Code:
symbol no_thing  = %00000000 'or simply =0
symbol thing_one = %00010111
symbol thing_two = %00000001
symbol not_pressed = 0    etc.
and then I would use a case statement, which uses a few more bytes but is easier to follow:
Code:
do
  pinsc = no_thing  
  Do : Loop Until pinC.3 = not_pressed
  Do : Loop Until pinC.3 = is_pressed
  counter = counter + 1 // 24
  select counter
     case 0: pinsc = thing_one
     case 6: pinsc = thing_two
   ...
     else not_seen = true
  endselect
  if not_seen = true then
     not_seen = false
     pause 40
  endif
loop
By the way, the term for code that isn't code is 'pseudocode' and it is a very useful way of showing what you mean without having to fill in all the details.
 
Last edited:
As always gentlemen thank you for posting actual examples to look at rather than sending me off to some technical reference in the manual that I probably would not understand, I'll try these out and let you know how I get on, precision timing is actually a little more important than absolute minimal code, I suppose to my mind I was equating smaller code to precision/speed of execution. By the way I am running the 08m2 at 32mhz and using the disconnect* function to prevent the chip from looking for downloads, are there any other ways to optimize that I might be unaware of? Basically the 08m2 is looking at the pin and executing the output functions based on the input pin state, no other stuff is needed for this particular application, although later once all this sinks in I intend to branch it off to do some other things.

*I am able to still get the chip to receive downloads using the disconnect by sending the code without power applied then as soon as I apply power the download starts, so no worries there.
 
Here is my code at present by the way:

Code:
main:
if Clock = 1 and State = 1 and RunStat = 0 then 
    GOTO start_count
    
ENDIF
if State = 0 then reset_RunStat
if Clock = 0 and State = 0 then resetcount 
goto main


start_count:
if counter = 0 then go_high1234
if counter > 0 and counter < 12 then go_low 
if counter = 6 then go_high4
if counter > 6 and counter < 8 then go_low
if counter = 8 then go_high3
if counter > 8 and counter < 12 then go_low
if counter = 12 then go_high24
if counter > 12 and counter < 16 then go_low
if counter = 16 then go_high3
if counter > 16 and counter < 18 then go_low
if counter = 18 then go_high4
if counter > 18 and counter < 24 then go_low
if counter = 24 then resetcount

go_high1234:
pins = %00010111
pause 40 
inc counter
pins = %00000000
RunStat = 1
goto main

go_high24:
pins = %00010010
pause 40 
inc counter
pins = %00000000
RunStat = 1
goto main

go_high3:
pins = %00000100
pause 40 
inc counter
pins = %00000000
RunStat = 1
goto main

go_high4:
pins = %00010000
pause 40 
inc counter
pins = %00000000
RunStat = 1
goto main

go_low:
pins = %00000000
inc counter
RunStat = 1
goto main

resetcount:
counter = 0

reset_RunStat:
RunStat = 0
goto main
Pay no attention to my naming of the go_high subroutines with relation to pin numbers, basically a counter has 24 positions, all pins are high at postion 0 then one of the pins only goes high at the start (once per 24) the next pin goes high at the start(0) and halfway(12), the next pin goes high 3 times per 24 once at start(0), once at 8 once at 16, the fourth pin goes high every 6, so 0,6,12,18 the counter resets to 0 on 24.
 
Last edited:

geoff07

Senior Member
If speed is of the essence then you might like to look at interrupts.

What happens if an input arrives during a 'pause 40' ?
 
I am not sure, and it probably explains why it is not working correctly. Could you give an example of how to implement interrupts into this please? By the way the pause of 40 is to make the pin(s) stay high for the duration of 5 milliseconds (running at 32mhz) so if there is a better way to achieve this then please let me know.
 
By the way when I first started this project, I only had 2 output pins which went high 4 times per 24 count (every 6) and twice (every 12) and it worked just fine, it is only when I added the other 2 pins that I started to notice the problems, not sure if that is relevant but thought I'd mention it.
 

hippy

Technical Support
Staff member
basically a counter has 24 positions, all pins are high at postion 0 then one of the pins only goes high at the start (once per 24) the next pin goes high at the start(0) and halfway(12), the next pin goes high 3 times per 24 once at start(0), once at 8 once at 16, the fourth pin goes high every 6, so 0,6,12,18 the counter resets to 0 on 24.
This would seem to do the job of setting output pins in accordance with your counter value ...

Code:
pinC.0 = counter       Max 1 ^ 1 ; When 0
pinC.1 = counter // 12 Max 1 ^ 1 ; When 0 and 12
pinC.2 = counter //  8 Max 1 ^ 1 ; When 0, 8 and 16
pinC.4 = counter //  6 Max 1 ^ 1 ; When 0, 6, 12 and 18
 
^ Ahh, thank you I was hoping that there would be a way of doing it like that. But now how do I tie that to the pin scenarios where 2 or more pins are high, like for example when the counter is at 12 pinC1 and pinC4 would need to be high at the same time - I can see that the code posted calls those pins separately at those counts, but is it going to be the same as using pins %xxxxxxxx? Where the pins are simultaneous, do you understand what I mean?
 

hippy

Technical Support
Staff member
To set pins simultaneously you could do ...

Code:
bit0 = counter       Max 1 ^ 1 ; When 0
bit1 = counter // 12 Max 1 ^ 1 ; When 0 and 12
bit2 = counter //  8 Max 1 ^ 1 ; When 0, 8 and 16
bit4 = counter //  6 Max 1 ^ 1 ; When 0, 6, 12 and 18
pinsC = b0
Or slightly more optimised and faster ...

Code:
bit0 = counter       Max 1 ; When 0
bit1 = counter // 12 Max 1 ; When 0 and 12
bit2 = counter //  8 Max 1 ; When 0, 8 and 16
bit4 = counter //  6 Max 1 ; When 0, 6, 12 and 18
pinsC = b0 ^ %10111
 
Would you mind explaining the bit0 - bit4 idea, is it that in this scenario bit0 is pinC0, bit1 is pinC1, bit2 is pinC2, bit3 is pinC4, because of
Code:
pinsC = b0 ^ %10111
Is that right? And then how would I integrate it into the code I posted earlier?
 

geoff07

Senior Member
Bits 0-7 are b0, so you assemble b0 bit by bit. Then you XOR with the mask, to get the outcome you want. Using the overlay of the bits on the low bytes and words is a useful technique and very efficient.
 

westaust55

Moderator
Try running it step by step in the Programming Editor simulator watching the variables and IO pin changes.
That should help you understand what is happening.
 
I can't get it working, I have tried a few different things but it is not counting, here is the code as it stands
Code:
main:
if Clock = 1 and State = 1 and RunStat = 0 then 
    GOTO start_count
    
ENDIF
if State = 0 then reset_RunStat
if Clock = 0 and State = 0 then resetcount 
goto main


start_count:
inc counter 
bit0 = counter       Max 1 ; When 0
bit1 = counter // 12 Max 1 ; When 0 and 12
bit2 = counter //  8 Max 1 ; When 0, 8 and 16
bit4 = counter //  6 Max 1 ; When 0, 6, 12 and 18
pinsC = b0 ^ %10111

resetcount:
counter = 0

reset_RunStat:
RunStat = 0
goto main
The closest I got was without the "inc counter" in the start_count routine, without it I got the first high transition on pins 3 and 5 to make the output pins all go high, but then they stayed in that state regardless of what pins 3 and 5 were doing. Also I need the output pins to only stay high for around 5ms on each time they are called high but cannot for the life of me figure out where to implement that, help please!
 
Last edited:

AllyCat

Senior Member
Hi,

Your code "falls through" from startcount into resetcount and then into reset_RunStat.

You need to put GOTOs before the labels resetcount and reset_RunStat, or (better) make all three into subroutines, each terminated by a RETURN (and use GOSUB or CALL in the main program loop).

Cheers, Alan.
 
Last edited:

hippy

Technical Support
Staff member
You might need to reconsider your logic as well -

if State = 0 then reset_RunStat
if Clock = 0 and State = 0 then resetcount

With 'State' reading zero it will never reach your second IF statement.

Also, your 'RunStat' variable never seems to be set to anything other than zero.
 
@Alan - I added a goto main at the end of start_count and it now seems to be doing something, but still not working right, what happened due to all my chopping about I forgot to add that back in and of course it tripped me up, so good eye!

@hippy - State is the hi/lo state of pin 5, so when it is low basically nothing needs to happen, but when it goes high then it starts the counter for pin 3. On my earlier version it was working as I wanted but if you think that my logic is wrong can you tell me how it should be? Runstat is the running status of the counter, so in effect when pin 5 is high and on pin 3 hi/lo the counter is started, so state of zero means that it is running, then when the pin 5 goes low it should change state like a flip flop idea, so pin 5 toggles runstat between 0 and 1 - does that make sense?

Basically the purpose of the whole thing is to read a logic pulse stream coming in and count to 24 and divide it, and these divisions are sent to the 4 output pins only when pin 5 is high, when pin 5 goes low the counter is reset and the picaxe just waits for the next high and rinse, repeat. The odd thing is on my earlier code with just 2 output pins it was working perfectly, and the picaxe was able to handle the speed of the incoming pulses and lock to it no matter what rate, then when I added the other 2 outputs it started not working as expected, so I thought that some way of optimising the code was needed, but now I am a bit out of my league!

Any ideas how to best approach? My feeling is that the old way I was doing it seem to be executed faster, but I am probably not correct and I have done something wrong.
 
Last edited:

hippy

Technical Support
Staff member
If your old code was working and only stopped working when you were adding more outputs then perhaps post that and you may be able to get help modifying it without breaking its operation.

I have to admit that I cannot completely discern what the functional operation of the device should be from the description. Perhaps some state diagram would help or perhaps your earlier code will answer that.
 
Sure, although it does not really do much in the simulator, but if you are able to download it to a 08m2 connect 4 leds to the output pins, a button to 5v and pin 5 and a stream of logic hi/lo into pin 3 with a rate of around 1 pulse every 10ms or so, it will probably make more sense. Anyway here is the working code before I started trying to expand it. I added some comments to it.

Code:
main:
if Clock = 1 and State = 1 and RunStat = 0 then 
    GOTO start_count
ENDIF
if State = 0 then reset_RunStat
if Clock = 0 and State = 0 then resetcount 
goto main


start_count: 'want to add more divisions, increase counter to 24, and 1,2,3 and 4 pulses per 24
if counter = 0 then go_high12 'all pins to output on zero count
if counter > 0 and counter < 6 then go_low  
if counter = 6 then go_high2
if counter > 6 and counter < 12 then go_low
if counter = 12 then resetcount 'count to 24 and reset

go_high12:
pins = %00000110
pause 40 
inc counter
pins = %00000000
RunStat = 1
goto main

go_high2:
pins = %00000100
pause 40 
inc counter
pins = %00000000
RunStat = 1
goto main


go_low:
pins = %00000000
inc counter
RunStat = 1
goto main

resetcount:
counter = 0

reset_RunStat:
RunStat = 0
goto main
 
Last edited:
Anyone got any ideas why the version posted above works, but when I add more output pins as below the problems start?
Code:
start_count: 
if counter = 0 then go_high1234
if counter > 0 and counter < 6 then go_low 
if counter = 6 then go_high4
if counter > 6 and counter < 7 then go_low
if counter = 8 then go_high3
if counter > 8 and counter < 12 then go_low
if counter = 12 then go_high24
if counter > 12 and counter < 16 then go_low
if counter = 16 then go_high3
if counter > 16 and counter < 18 then go_low
if counter = 18 then go_high4
if counter > 18 and counter < 24 then go_low
if counter = 24 then resetcount
 
Last edited:

goom

Senior Member
The line "if counter > 6 and counter < 7 then go_low" will never be true. Probably should read "if counter > 6 and counter < 8 then go_low", or simply "if counter = 7 then go_low".
 

AllyCat

Senior Member
Hi,

All those tests for go_low are unnecessary, the following is exactly the same:
Code:
start_count: 
	if counter = 0 then go_high1234
	if counter = 6 then go_high4
	if counter = 8 then go_high3
	if counter = 12 then go_high24
	if counter = 16 then go_high3
	if counter = 18 then go_high4
	if counter = 24 then resetcount
	{goto go_low}    ; Optional
go_low:
Is that what you're trying to do? You may need to check that the code at all those extra label destinations is correct.

Cheers, Alan.
 
The line "if counter > 6 and counter < 7 then go_low" will never be true. Probably should read "if counter > 6 and counter < 8 then go_low", or simply "if counter = 7 then go_low".
Ah, good catch, maybe that is contributing to the incorrect function.

Alan, thanks I'll try your suggestion, certainly looks a lot cleaner that way.
 
Quick follow up, with Alan's changes it appears to be working much better now, I am not able to test it fully but so far so good.

Big thanks for the help!;)
 
Top