An example of how a PICAXE could be used to make a PLC

Buzby

Senior Member
Hi All,

Prompted by the recent question about making a PICAXE PLC here is an 'engine' that someone could use as the basis of such a project, if anyone thinks making a PLC out of a PICAXE is a Good Thing.

The difficult part of a PLC system is not the engine ( this one here took less than a day to write ), it's the software needed to provide the user programming interface. Without a graphic front end for programming and monitoring any PLC is just a black box running some code.

The code below is a first attempt. If I was to take it further I would re-map all the buffers and coils into the scratchpad, along with the PLC instructions. This would then allow a second PICAXE to read the buffers and modify the programme 'on the fly', by using I2C. This second PICAXE would then be the basis of the programming interface.

The code demonstrates fundamental concepts that a real PLC uses, such as byte-wide buffers for each bit, reading inputs at the beginning of the scan, writing outputs at the end, off-loading 1-shot generation, etc., and it's functionality can be extended considerably from here.

If anyone wants to take this on as a project then I'm happy to help, but I'm not developing it any further myself.

Cheers,

Buzby


Code:
#picaxe 28X2
#no_data
#no_table

#rem

Memory usage

RLO is RAM bit8

Inputs are pins B.0 to B.7, mapped to RAM Bytes b4 to b11, Addr 4 to 11

Outputs are pins C.0 to C.5,mapped to RAM Bytes b12 to b17, Addr 12 to 17

Coils are bytes 18 to 25, Addr 18 to 25


#endrem

symbol Inst = b2		' Code of instruction, -[ ]-  -[/]-  etc
symbol Addr = b3		' Address of operand, X3 F2 etc, as RAM byte number

symbol RLO  = bit8	' RLO bit
symbol PEND = bit9	' Programme End instruction 255 found
symbol Scan1 = bit10	' This bit is set on first scan of PLC, often used to initialise stuff.

'Input buffer
symbol X1 = b4	' Structure for pinB.0 
symbol X2 = b5	' Structure for pinB.1
symbol X3 = b6	' Structure for pinB.2
symbol X4 = b7	' Structure for pinB.3
symbol X5 = b8	' Structure for pinB.4
symbol X6 = b9	' Structure for pinB.5
symbol X7 = b10	' Structure for pinB.6
symbol X8 = b11	' Structure for pinB.7

'Output buffer
symbol Y1 = b12	' Structure for pinC.0
symbol Y2 = b13	' Structure for pinC.1
symbol Y3 = b14	' Structure for pinC.2
symbol Y4 = b15	' Structure for pinC.3
symbol Y5 = b16	' Structure for pinC.4
symbol Y6 = b17	' Structure for pinC.5

'Internal Coil buffer
symbol F1 = b18	' Structure for internal bit 1
symbol F2 = b19	' Structure for internal bit 2
symbol F3 = b20	' Structure for internal bit 3
symbol F4 = b21	' Structure for internal bit 4
symbol F5 = b22	' Structure for internal bit 5
symbol F6 = b23	' Structure for internal bit 6
symbol F7 = b24	' Structure for internal bit 7
symbol F8 = b25	' Structure for internal bit 8



' Code starts here
' ================

dirsC = %00111111	' Enable output pins

Scan1	= 1

do  ' This loop executes one complete scan of the PLC programme

	gosub GetInputs ' Get physical input pins into buffers, ( addr 4 to 11 )
	
	ptr =  0 ' Point to start of instruction list in scratchpad
	RLO =	 1 ' Reset RLO for start of first rung
	PEND = 0 ' Reset programme end flag
	
	do ' This loop processes rungs until the programme end instruction is found
		
		' This loop should really pull the instructions from scratchpad.
		' Here it's just three hardcoded example rungs.
				
		Inst = 1 : Addr = 4	' -[ ]- A X1
		gosub ProcessInstr
		Inst = 4 : Addr = 12	' -[ ]- O Y1
		gosub ProcessInstr		
		Inst = 2 : Addr = 5	' -[/]- AN X2
		gosub ProcessInstr
		Inst = 3 : Addr = 12	' -( )- Y1
		gosub ProcessInstr	
		Inst = 254 : Addr = 0	' Rung end
		gosub ProcessInstr	
		
		Inst = 1 : Addr = 4	' -[ ]- A X1
		gosub ProcessInstr
		Inst = 3 : Addr = 22	' -( )- F5	
		gosub ProcessInstr
		Inst = 254 : Addr = 0	' Rung end
		gosub ProcessInstr				

		Inst = 1 : Addr = 22	' -[ ]- A F5
		gosub ProcessInstr
		Inst = 3 : Addr = 13	' -( )- Y2	
		gosub ProcessInstr
		Inst = 254 : Addr = 0	' Rung end
		gosub ProcessInstr			
		
		Inst = 255 : Addr = 0	' PLC Programme end
		gosub ProcessInstr
		
	loop until PEND = 1 ' Finish loop when Programme End 255 found
	
	' gosub UpdateTimers ' ( Future feature )

	gosub PutOutputs ' Put output buffers to physical pins
	
loop ' Repeat PLC scan


' ============================================================================
' Subroutines
' ============================================================================

GetInputs:
' This routine is called at the beginning of a scan. It updates the input buffers from the physical pins and generates rising/falling 1-shots.

'Input byte structure
'--------------------
'bit7 = State of physical input, read by -[ ]- and -[/]- instructions
'bit6 = Last scan state
'bit5 = Rising 1-shot, read by -[P]- instruction
'bit4 = Falling 1-shot, read by -[N]- instruction
'bit3 = 
'bit2 =
'bit1 =
'bit0 =

	' Get first input
	b0 = X1 ' Get current input structure
	bit7 = pinB.0 ' Copy physical input to structure
	Gosub ProcessInput
	X1 = b0 ' Update structure

	' Get second input
	b0 = X2 ' Get current input structure
	bit7 = pinB.1 ' Copy physical input to structure
	Gosub ProcessInput
	X2 = b0 ' Update structure
	
	' Get third input
	b0 = X3 ' Get current input structure
	bit7 = pinB.2 ' Copy physical input to structure
	Gosub ProcessInput
	X3 = b0 ' Update structure
		
	' etc ...
	

return
ProcessInput:
	' Code called as each input is processed at beginning of scan
	bit5 = 0 : bit4 = 0 ' Clear any rising/falling 1-shots 	
	if bit7 <> bit6 then ' input has changed
		if bit7 = 1 then ' it was rising 
			bit5 = 1
			else ' it was falling
			bit4 = 1 
		endif	
	endif
	bit6 = bit7 ' update last scan state
return


PutOutputs:
'Output byte structure
'---------------------
'bit7 = State of physical output, read by -[ ]- and -[/]- instructions
'bit6 = Force active  ( future feature )
'bit5 = Forced state  ( future feature )
'bit4 = 
'bit3 = 
'bit2 =
'bit1 =
'bit0 =


	b0 = Y1 ' Get structure for first output
	if bit6 = 1 then ' Check for force active, if it is then 
		pinC.0 = bit5 ' Copy forced value of structure to physical output
	else 
		pinC.0 = bit7 ' else copy RLO of structure to physical output
	endif
	Y1 = b0

	b0 = Y2 ' Get structure for second output 
	if bit6 = 1 then ' Check for force active, if it is then 
		pinC.1 = bit5 ' Copy forced value of structure to physical output
	else 
		pinC.1 = bit7 ' else copy RLO of structure to physical output
	endif
	Y2 = b0
	
	b0 = Y3 ' Get structure for third output 
	if bit6 = 1 then ' Check for force active, if it is then 
		pinC.2 = bit5 ' Copy forced value of structure to physical output
	else 
		pinC.2 = bit7 ' else copy RLO of structure to physical output
	endif
	Y3 = b0

	' etc ....
	
return

UpdateTimers:

	' Future feature
	
return


WriteInternal:
' Write to internal coil -( )- , addr 18 to 25

'Internal coil byte structure
'-----------------------------
'bit7 = State of coil, read by -[ ]- and -[/]- instructions
'bit6 = Last set state
'bit5 = Rising 1-shot, read by -[P]- instruction
'bit4 = Falling 1-shot, read by -[N]- instruction
'bit3 = 
'bit2 =
'bit1 =
'bit0 =

	peek Addr, b0 ' Get coil structure
	bit7 = RLO ' Update using RLO
	bit5 = 0 : bit4 = 0 ' Clear any rising/falling 1-shots 	
	if bit7 <> bit6 then ' coil has changed
		if bit7 = 1 then ' it has risen 
			bit5 = 1
			else ' it has fallen
			bit4 = 1 
		endif	
	endif
	bit6 = bit7 ' update last scan state
	poke Addr, b0 ' update coil structure
return

ProcessInstr:
' This is the routine to execute each instruction
	
	if Inst = 0 then ' NOP
		' do nothing
	endif

	if Inst = 1 then ' -[ ]- 'A' Scan for coil or pin state
		peek Addr, b0
		RLO = RLO AND bit7
	endif

	if Inst = 2 then ' -[/]- 'AN' Scan for inverse of coil or pin state
		peek Addr, b0
		RLO = RLO ANDNOT bit7
	endif

	if Inst = 3 then ' -( )- '=' Output to coil or pin
		if Addr >= 18 then ' it's an internal coil
			gosub WriteInternal
		else ' it must be a physical output pin
			peek Addr, b0 ' get pin structure
			bit7 = RLO ' Update RLO for output
			poke Addr, b0 ' Update structure
		endif
	
	endif

	if Inst = 4 then ' -[ ]- 'O' OR to left rail
		peek Addr, b0
		RLO = RLO OR bit7
	endif


	if Inst = 254 then ' Rung End
		RLO = 1 ' Reset RLO ready for next rung
	endif	

	if Inst = 255 then ' Programme End
		PEND = 1 ' Set Programme End flag
	endif

return
 
Top