How can I detect when a period of 10ms to 100ms has elapsed?

Flenser

Senior Member
I'm working on a project to convert ladder logic to run as PICAXE basic code and from the little google research I've done it appears that historically PLCs have run ladder logic code using a fixed cycle time with typical cycle times being in the 10ms to 100ms range.

The program structure I'm planning to use to is this:
Code:
; Set the PLC cycle time to use here in whatever units are convenient
PLC_CYCLE_TIME = <some-value>

; Initialize the time the last PLC cycle ended as now
LAST_PLC_CYCLE_TIME = <the-current-timer-value>

main:
	<read the input pins>
	<execute the ladder logic program to update the values for the output pins>
	<write the output pins>

	; Execute this loop to delay until the PLC cycle time has elapsed
	do
		ELAPSED_TIME = <the-current-timer-value> -  LAST_PLC_CYCLE_TIME 
	until ELAPSED_TIME > PLC_CYCLE_TIME

	; Update the time the last PLC cycle ended  
	LAST_PLC_CYCLE_TIME = LAST_PLC_CYCLE_TIME + PLC_CYCLE_TIME
	goto main
The only way I can think of of detecting when this sort of period has passed is to use peeksfr to modify the settings for timer1 but my preference would be to not alter the timer1 settings from what the PICAXE firmware sets.

Can anyone recommend another technique I could use to detect elapsed times in the 10ms to 100ms range?
 

hippy

Technical Support
Staff member
Which PICAXE type are you wanting to use, M2 or X2 ?

With the X2 you can use the SETTIMER to interrupt and set a flag, run the ladder when it is set.

Code:
#Picaxe 28X2

Symbol kLoopMs    = 20        ; 20 ms
Symbol kTickUs    = 32        ; 32 us at 8 MHz

Symbol kLoopUs    = kLoopMs * 1000
Symbol kTickNum   = kLoopUs / kTickUs
Symbol kPreload   = 65536   - kTickNum

Symbol runCount   = w1

SetTimer kPreload
Gosub Interrupt_Enable
Do
  Gosub LadderProgram
  Do : Loop While runCount = 0
  runCount = runCount - 1
Loop

Interrupt:
  runCount = runCount + 1

Interrupt_Enable:
  toFlag = 0
  timer  = $FFFF
  SetIntFlags $80,$80
  Return
 

AllyCat

Senior Member
Hi,

It depends if you're using an M2 or X2 PICaxe.

In M2s, Timer1 is arranged to overflow once every 20 ms (at 4 MHz clock, 1 MHz cycle time) by a system interrupt preloading it with 45536. So you could simply synchronise your code to 20ms by polling the Timer1 SFR (high byte) and waiting until it rolls over. Other clock frequencies may give the option of some other periods, but you need to examine the on-line details about the "time" variable (not "timer") to see how the timing periods change (or more particularly when they don't).

I believe the X2s give more options with Timer Interrupts, but haven't used them myself.

Cheers, Alan.
 

Buzby

Senior Member
Hi,

The 'fixed cycle time' feature you mention is not standard, and even on the PLCs that offer it, it's very rarely used.

The usual requirement if for the cyclic code to execute as fast as possible, and if repeatable fixed interval stuff is needed ( e.g. for PID ) then usually an accurate clock signal is used trigger the routine as required.

I'd concentrate more on features that people use lots, like different types of timers, edge detectors, 'forcing' abilities, etc.

If you want more info on PLC programming I'll be happy to help.

Cheers,

Buzby
 

Flenser

Senior Member
Thanks to you all for your suggestions.

My intention is for the code to run on both M2 and X2 chips with programs generated for the X2 chips taking advantage of the X2s extra features where possible.
Up until now I have only written programs for M2 chips so my thinking has been focused about how to implement a fixed cycle time on M2s. For the X2 chips using timer3 would seem to be the obvious choice.
As Busby points out that the fixed cycle time is rarely used I think I'll leave this as an unimplemented feature at this stage.

Busby,
My code is going to be an incremental thing. My plan is to try and get a "v0.1" of the code working with just input pins and output pins (which are mapped to contacts and coils in the ladder program). If I get that working I'll look at adding the next feature, an "internal" relay (where the coil and contacts are virtual and not mapped to physical pins) or a simple timer.
Do you have a feel for what an absolute minimum set of features might be for a simple PLC? The shorter the list and the simpler the features the more likely the program is to get finished to that point.
 

hippy

Technical Support
Staff member
For code which works on both the M2 and X2 using the same mechanism I would probably go for peeking the free running timer1 then determining when the time period has elapsed. You would only be using data provided, not modifying anything within the PICAXE. That will be trickier on the M2 but can be done.
 

Buzby

Senior Member
... Do you have a feel for what an absolute minimum set of features might be for a simple PLC?
If the 'engine' of your PLC is structured right, it will be easy to add new instructions, so I wouldn't worry about what instructions you need yet, just concentrate the engine.

Taking a really simple ladder rung like this :
Code:
     X1      X2     Y1
|---] [-----] [----( )--|
This can be represented as an instruction list like this

A X1
A X2
= Y1

Each line now contains an instruction and an address. ( If these are both byte values, then your PLC could have 256 instruction types and access to 256 addresses.)
Your 'engine' just needs to step through the list, calling a specific subroutine depending on the instruction type, which then updates the relevant address.

( I'm very familiar with how 'Old Skool' Siemens S5 PLCs internally do their stuff, so most of my descriptions will be based on how S5 works, but the principle is basically the same on modern PLCs.)

There is an internal memory bit called RLO. This is the 'Result of Logical Operation', and is updated after each instruction is processed.

So process our simple rung the 'engine' would :

Set RLO to 0
Call 'A' subroutine, which uses the value of X1 to update RLO
Call 'A' subroutine again, which uses the value of X2 to update RLO
Call '=' subroutine, which uses the value of RLO to update Y1

If the engine is coded like this it's really easy to add and test new instruction types.

First start with the 'A' and '=' subroutines, using just internal memory location for the addresses.
Then add the 'AN' ( AND-NOT) instruction, and the basic 'O' ( OR from left rail ) instruction.
Then expand the address decoder to include real I/O, and add the basic on delay timer.

This would be enough to code 99% of the industrial requirements in the World today !.

After that you can add as many bells and whistles as you want.

( Note: You will be able to make your instruction set hugely more interesting if your memory 'bit' locations are actually individual bytes, one for each bit, with the MSB representing the specific bit addressed. If you structure all your addressed data like this then all kinds of clever stuff can be added very easily. )

Cheers,

Buzby
 
Top