@refaQtor said:
... there are plenty of enhancements to displaying live the status of the system. and assist in debugging. all well beyond those languages.
That's no longer a language discussion at all. That's tools.
@refaQtor said:
and, from the current miserable experience I'm having of trying to decipher and modify Ladder Logic code for these fancy mass-flow controllers that require stepping through strings of characters and doing math digit by digit, which I find on industrial control forums, not uncommon.... I see that there is an official language that covers the full spectrum of digital/analog discrete/continuous. so, that is why I look to making that more useful and easier to see visually live what is going on in the system.
There is an extension to Ladder in modern PLCs that I only know as Function Block. Not the IEC's FBD.
Function blocks allow incorporating of other languages as subordinate to the Ladder. In the Ladder, such blocks look not dissimilar to a timer but can be placed in the middle of a rung - Some PLCs have timers in mid-rung too. The number of inputs and outputs are definable. For the code author, they are conceptually a little tricky because the blocks are not processed sequentially within the Ladder execution order but rather in their own time. Presumably still synchronised with the I/O scan though.
EDIT: Replaced "Ladder sequence" with "Ladder execution order".
PS: It might not be so new an idea either. I suspect Siemens PLCs (from the S5's at least) might have always provided this extension.
@evanh said:
There is an extension to Ladder in modern PLCs that I only know as Function Block. Not the IEC's FBD.
I see the OpenPLC does have those, and you can add your own in other languages. It doesn't enforce Ladder at the top level, though. But, their examples show well when they do. Each rung has a functional block in the middle with a different language.
Well, that'll teach me to step into fixing/finishing another person's project started, apparently, on an inadequate PLC with only one limited language IDE option.
Yep, knowing what the options are helps. You'll strike this same problem in every profession. Only select polytechs will teach the speciality and you only get to find out what that is after beginning an apprenticeship in the field.
@evanh said:
and you only get to find out what that is after beginning an apprenticeship in the field.
well, as I might've mentioned before, I began Ladder Logic in the '80s, it was actual Relay Logic on 45KW LORAN transmitters - water-colled vacuum tubes from the '50s. I liked it. you could hear/feel/see the execution of the ladder down the relay panel, and standby with the the wooden safety cane to rap on sticky relays, if your internal timer felt it was taking too long on startup. ah, the days when you could commit the entire schematic & identifier of every cap and tube and resistor and triac to memory . Debugging often started with turning off the lights and turning on your nose. the smell of each type of overhot component was different, and may have been glowing as well.
then computer engineeing in college spoiled me with math and string libraries. just learning how they've been combined thanks to good people sharing.
@Rayman said:
Alternatively, could use the c file that ldmicro can make. But, this way allows for eventual on chip editing of the ladder…
Catalina can compile and run it. I demonstrated this with OpenPLC, which is the part of LDmicro that generates C code. You may need to write some runtime library functions.
@Rayman said:
Need to see how I/o pins are handled…
Only read before the ladder and written after?
Or, can they change within the loop?
I believe that the I/O mustn't change within the loop. I think you want to read them all into registers that shadow the state of them all at the beginning, then work only from those registers, while the actual I/O may be shifting.
on the other side of things... be writing results to these interim register, then only copy them out to the output pins at the end of the logic processing stage.
@evanh said:
You're a lot older than I expected. :embarrassed:
I sound a lot younger than I am. :embarrassed:
While I have come to respect the-way-things-are-done, from electrronics to databases to protocols, and why. I also haven't had my dreams of how I want a machine to work completely beaten out of me. After a long percolation, the designs are coming together in my head and getting out on designs and hardware. difficult to convey except in terms of similar things. In fact- I called this project "platypus" for a long time, because it was so hard to describe. it is more af a cephalopod, now.
The unique features of the Propeller have gone a long way to soliidifying designs. so, I pester you all around here. I apologise that I communicate better with machines than humans.
@Rayman said:
Thanks @refaQtor
Same for registers? (i.e., "internal relays")
I think the internal registers are accessed and changed as necessary in the strict sequence of the rungs.
some are binary, and when doing math, they will be stored in bytes/words/longs. there is some definition of standard datatypes. I can't look up now. but, the process can be put in place with just longs, to start.
I've only casually looked at ladder logic. Am I wrong to think that the synchronized loop in the Propeller (using waitcnt() and waitct()) lend themselves to converting ladder to Spin? Structurally, something like...
t := cnt
repeat
inputs := ina
run_rung1
run_rung2
run_rung3
waitcnt(t += LOOP_TICKS)
update_timers
update_outputs
Think it could be done in Spin, especially with the new structures.
Thought about doing it that for a moment...
What is happening in this code is that the program is turned into a null terminated list of structures...
Then, that list is used for drawing by going through the list one element at a time.
What does help a lot is typecasting the structures. Assume can do that in new Spin2, but don't really know...
Thinking that executing the program will also be just going through the list...
a simple as possible is good.
there is to be a timer update section after the rung processing, where the state of all the timers/counters are updated by adding the time that transpired since last time 'round.
next processing loop will read and act based on the updated timer values. similar thing happens with the counters, I believe.
...where the state of all the timers/counters are updated by adding the time that transpired since last time 'round.
In my simple world view, this becomes easy by creating a variable for each timer, syncing it to the system clock, and then adding/subtracting the loop ticks value -- unless elements of the runs are able to gate them, which does make sense.
@JonnyMac said:
I've only casually looked at ladder logic. Am I wrong to think that the synchronized loop in the Propeller (using waitcnt() and waitct()) lend themselves to converting ladder to Spin? Structurally, something like...
t := cnt
repeat
inputs := ina
run_rung1
run_rung2
run_rung3
waitcnt(t += LOOP_TICKS)
update_timers
update_outputs
Or am I being far too simplistic?
Looks good to me. Although the rungs, being dynamic length, would be processed by a loop and end up existing as a DAT section or even loaded from a separate file in the end.
...where the state of all the timers/counters are updated by adding the time that transpired since last time 'round.
In my simple world view, this becomes easy by creating a variable for each timer, syncing it to the system clock, and then adding/subtracting the loop ticks value -- unless elements of the runs are able to gate them, which does make sense.
You've got free rein on how complex a timer you want. PLCs all vary on this front. You can have several variants if you like. Multiple control inputs is an option. Same for counters. There is often even a small number of special timers/counters that can operate faster than the rest. Tying them to dedicated hardware is also up for grabs.
Got the motor stop/start example going now in "simulation mode" with fake input.
Liking the idea of simulation mode along with single stepping, like ldmicro does...
Guess need to add mouse support next, so can control simulation.
and also this speed claim The data operation instruction efficiency of Mitsubishi PLC is too low. For example, 1000 single-word registers are squared and the result is 32 bits, which is equivalent to 1000 multiplication and addition operations. The program is implemented with loop instructions. The actual time taken by Delta ES2 series PLC (CPU is 72MHz STM32F103) is 27.9ms, while the actual time taken by this program on 32MHz STC32G12K128 is 10ms.
Here is an SPI call example, as an image :
That suggests live pin access is certainly supported, here doing the SPI chip select and send within the blocks.
Below is a larger i2c library, I copied and AI translated.
Syntax is similar but not quite identical :
Ladder in-line uses =, +, etc , whilst code block uses MOV and AND etc
Sometimes it uses assembler level lines like MOV Temp1,H00A2
and also supported is more HLL lines DM0[RtcAddr]=Temp1+DM0[ValAddr]
It does not look like formal IEC61131, but somewhere between HLL and Assembler.
It seems the compiler/parser can accept any mix of the Ladder conditionals, and the text code ?
The examples I've seen so far have at most 1-2 lines of text language within any ladder rung, maybe convention is to call a function when code gets larger to keep the 'Ladder look' ?
RdSec: FUN I, Void As D0
; Function: Read the seconds of the real-time clock.
| Function: Read the seconds information in the real-time clock module as the return value of the function.
| Void: meaningless, should be 0.
| Return value: The seconds in the real-time clock module (binary number).
| For example: DM300=RdSec(0)
IICSTART
MOV %D0,H00A2
IICW %D0
MOV %D0,H0002
IICW %D0
IICSTART
MOV %D0,H00A3
IICW %D0
IICRNAK %D0
IICSTOP
RST %D0.7
BIN %D0
RETURN %D0
EndFun
;================================================== ================================================== ================================================== ================================================== = ================================================== ================================================== ================================================== ================================================== ==
RdRTC: FUN I,RTC_DM As D0
;Function: Read the real-time clock function.
| Function: Read the year, week, month, day, hour, and minute information in the real-time clock module and store them in the specified data block (integer array).
| RTC_DM: The first address of the array that stores the real-time clock information (all expressed in BCD code). The meanings of each element in the array are as follows:
| [0]: Stores the year and week. The thousands and hundreds of the BCD number are the year, and the tens and ones are the week, such as: H0401 represents Monday, 2004.
| [1]: Stores the month and day. The thousands and hundreds of the BCD number are the month, and the tens and ones are the day, such as: H1205 represents December 5.
| [2]: Stores the hours and minutes. The thousands and hundreds of the BCD number are the hours, and the tens and ones are the minutes, such as: H2031 represents 20:31.
LOCAL Temp1,Temp2 AS D1
IICSTART
MOV Temp1,H00A2
IICW Temp1
MOV Temp1,H0003
IICW Temp1
IICSTART
MOV Temp1,H00A3
IICW Temp1
IICRACK Temp1 ;min
SWAP Temp1
IICRACK Temp1 ; hours
AND Temp1,0X7F3F
SWAP Temp1
DM2[RTC_DM]=Temp1
IICRACK Temp1 ; Day
IICRACK Temp2 ; Week
SWAP Temp1
IICRACK Temp1 ;Month
AND Temp1,0X3F1F
SWAP Temp1
DM1[RTC_DM]=Temp1
SWAP Temp2
IICRNAK Temp2 ;year
IICSTOP
AND Temp2,0X07FF
SWAP Temp2
DM0[RTC_DM]=Temp2
EndFun
;================================================== ================================================== ================================================== ================================================== = ================================================== ================================================== ================================================== ================================================== ==
WrRTC: FUN I, year As D0, day As D1, time As D2
; Write real-time clock function
| Function: Write the specified time information (week, year, month, day, hour, minute) into the real-time clock.
| year: year and week, BCD number. The thousands and hundreds of the BCD number are the year, and the tens and ones are the week, such as: H0401 means Monday, 2004.
| day: month and day, BCD number. The thousands and hundreds of the BCD number are the month, and the tens and ones are the day, such as: H1205 means December 5.
| time: hours and minutes, BCD number. The thousands and hundreds of the BCD number are the hours, and the tens and ones are the minutes, such as: H2031 means 20:31.
LOCAL Temp1 AS D3
IICSTART
MOV Temp1,H00A2
IICW Temp1
MOV Temp1,H0000
IICW Temp1
IICW Temp1
IICW Temp1
IICW Temp1 ; seconds
IICW time; minutes
SWAP time
IICW time;
IICW day; day
IICW year; week
SWAP day
IICW day; month
SWAP year
IICW year;
IICSTOP
EndFun
;================================================== ================================================== ================================= ================================================== ================================================== =================================
RtcToVal: FUN I, RtcAddr As D0, ValAddr As D1
; Convert the real-time clock format to a numerical format
| Function: Convert the compressed BCD format of the real-time clock to a single integer numerical format.
| Input parameters:
| RtcAddr: The first address of the data block (in DM) storing the compressed BCD format of the real-time clock.
| It occupies 3 words,
[0] is the year and week (high byte is year, low byte is week),
[1] is the month and day (high byte is month, low byte is day),
[2] is the hour and minute (high byte is hour, low byte is minute).
| ValAddr: The first address of the data block (in DM) storing the integer numerical format of the converted real-time clock.
| It occupies 6 words, [0]: week, [1]: year, [2]: month, [3]: day, [4]: hour, [5]: minute.
| For example: RtcToVal(#DM300,#DM400)
LOCAL Temp As D2
Temp=DM0[RtcAddr]&H0FF00
SWAP Temp
BIN Temp
DM1[ValAddr]=Temp+2000
DM0[ValAddr]=DM0[RtcAddr]&H00FF
Temp=DM1[RtcAddr]&H0FF00
SWAP Temp
BIN Temp
DM2[ValAddr]=Temp
Temp=DM1[RtcAddr]&H00FF
BIN Temp
DM3[ValAddr]=Temp
Temp=DM2[RtcAddr]&H0FF00
SWAP Temp
BIN Temp
DM4[ValAddr]=Temp
Temp=DM2[RtcAddr]&H00FF
BIN Temp
DM5[ValAddr]=Temp
EndFun
;================================================== ================================================== ================================= ================================================== ================================================== =================================
ValToRtc: FUN I, ValAddr As D0, RtcAddr As D1
; Convert the real-time clock value format to the compressed BCD format
| Function: Convert the single integer value format of the real-time clock to the compressed BCD format.
| Input parameters:
| ValAddr: The first address of the data block (in DM) storing the integer value format of the real-time clock.
Occupies 6 words, [0]: week, [1]: year, [2]: month, [3]: day, [4]: hour, [5]: minute.
| RtcAddr: The first address of the data block (in DM) storing the compressed BCD format of the converted real-time clock.
Occupies 3 words, [0] is the year and week (high byte is year, low byte is week), [1] is the month and day (high byte is month, low byte is day), [2] is the hour and minute (high byte is hour, low byte is minute).
| For example: ValToRtc(#DM400,#DM300)
LOCAL Temp1,Temp2 As D2
Temp1=DM1[ValAddr]-2000
BCD Temp1
SWAP Temp1
DM0[RtcAddr]=Temp1+DM0[ValAddr]
Temp1=DM2[ValAddr]
BCD Temp1
SWAP Temp1
Temp2=DM3[ValAddr]
BCD Temp2
DM1[RtcAddr]=Temp1+Temp2
Temp1=DM4[ValAddr]
BCD Temp1
SWAP Temp1
Temp2=DM5[ValAddr]
BCD Temp2
DM2[RtcAddr]=Temp1+Temp2
EndFun
This also needs live pin access to work.
and here is an example of a file include, and some direct SFR access, looking very assembler like, but giving MCU register level access here.
It's not so much the ladder is doing the live I/O, it's those Sxxx blocks are specially made to do it. Each one is a purpose built function that is designed to be used in execution sequence to produce I2C packets. I'd hazard a guess they're working with a hardware I2C module in the MCU.
I'd also say a little unusual to have such fine grain support at the ladder level but it does give an example of how one PLC maker has chosen to add extensions.
PS: It also demonstrates ordered execution. It's an example of where it's handy to have the function completed within the Ladder execution. Normally, this is reserved for maths on internal memory/logic only.
Got simulation mode working with mouse input on the Motor Start/Stop example.
If you want to try it, connect VGA adapter on basepin 16 and USB host on basepin 24.
Believe it is behaving similar to the ldmicro simulation mode....
There is no timing yet, there's enough serial diagnostic output right now to slow it down enough...
Added Emergency Stop contact and seems to work in simulation.
Note that the cursor is a magenta block and you single click on a contact to toggle it's fake i/o pin value...
ldmicro changes color scheme in simulation mode so that active lines are red.
Maybe will get to that eventually, but that seems like icing at this point...
Just noticed something in ldmicro that don't fully understand...
Contacts have an option for "Set High Level before simulation" that is only enabled for I/O pins that are INPUTS.
This puts a caret character in front of the symbol.
Why can't this apply to "Internal Relay" or OUTPUTS too? Not really getting that.
Also, doesn't seem like super important to implement right away...
Comments
That's no longer a language discussion at all. That's tools.
There is an extension to Ladder in modern PLCs that I only know as Function Block. Not the IEC's FBD.
Function blocks allow incorporating of other languages as subordinate to the Ladder. In the Ladder, such blocks look not dissimilar to a timer but can be placed in the middle of a rung - Some PLCs have timers in mid-rung too. The number of inputs and outputs are definable. For the code author, they are conceptually a little tricky because the blocks are not processed sequentially within the Ladder execution order but rather in their own time. Presumably still synchronised with the I/O scan though.
EDIT: Replaced "Ladder sequence" with "Ladder execution order".
PS: It might not be so new an idea either. I suspect Siemens PLCs (from the S5's at least) might have always provided this extension.
I see the OpenPLC does have those, and you can add your own in other languages. It doesn't enforce Ladder at the top level, though. But, their examples show well when they do. Each rung has a functional block in the middle with a different language.
Well, that'll teach me to step into fixing/finishing another person's project started, apparently, on an inadequate PLC with only one limited language IDE option.
Yep, knowing what the options are helps. You'll strike this same problem in every profession. Only select polytechs will teach the speciality and you only get to find out what that is after beginning an apprenticeship in the field.
well, as I might've mentioned before, I began Ladder Logic in the '80s, it was actual Relay Logic on 45KW LORAN transmitters - water-colled vacuum tubes from the '50s. I liked it. you could hear/feel/see the execution of the ladder down the relay panel, and standby with the the wooden safety cane to rap on sticky relays, if your internal timer felt it was taking too long on startup. ah, the days when you could commit the entire schematic & identifier of every cap and tube and resistor and triac to memory . Debugging often started with turning off the lights and turning on your nose. the smell of each type of overhot component was different, and may have been glowing as well.
then computer engineeing in college spoiled me with math and string libraries. just learning how they've been combined thanks to good people sharing.
You're a lot older than I expected. :embarrassed:
Catalina can compile and run it. I demonstrated this with OpenPLC, which is the part of LDmicro that generates C code. You may need to write some runtime library functions.
Need to see how I/o pins are handled…
Only read before the ladder and written after?
Or, can they change within the loop?
I believe that the I/O mustn't change within the loop. I think you want to read them all into registers that shadow the state of them all at the beginning, then work only from those registers, while the actual I/O may be shifting.
on the other side of things... be writing results to these interim register, then only copy them out to the output pins at the end of the logic processing stage.
I sound a lot younger than I am. :embarrassed:
While I have come to respect the-way-things-are-done, from electrronics to databases to protocols, and why. I also haven't had my dreams of how I want a machine to work completely beaten out of me. After a long percolation, the designs are coming together in my head and getting out on designs and hardware. difficult to convey except in terms of similar things. In fact- I called this project "platypus" for a long time, because it was so hard to describe. it is more af a cephalopod, now.
The unique features of the Propeller have gone a long way to soliidifying designs. so, I pester you all around here. I apologise that I communicate better with machines than humans.
Thanks @refaQtor
Same for registers? (i.e., "internal relays")
These seem to be bits, but might be easier to handle as bytes or longs...
Can't imagine there would ever enough of them to be a memory problem...
I think the internal registers are accessed and changed as necessary in the strict sequence of the rungs.
some are binary, and when doing math, they will be stored in bytes/words/longs. there is some definition of standard datatypes. I can't look up now. but, the process can be put in place with just longs, to start.
I've only casually looked at ladder logic. Am I wrong to think that the synchronized loop in the Propeller (using waitcnt() and waitct()) lend themselves to converting ladder to Spin? Structurally, something like...
Or am I being far too simplistic?
Think it could be done in Spin, especially with the new structures.
Thought about doing it that for a moment...
What is happening in this code is that the program is turned into a null terminated list of structures...
Then, that list is used for drawing by going through the list one element at a time.
What does help a lot is typecasting the structures. Assume can do that in new Spin2, but don't really know...
Thinking that executing the program will also be just going through the list...
a simple as possible is good.
there is to be a timer update section after the rung processing, where the state of all the timers/counters are updated by adding the time that transpired since last time 'round.
next processing loop will read and act based on the updated timer values. similar thing happens with the counters, I believe.
In my simple world view, this becomes easy by creating a variable for each timer, syncing it to the system clock, and then adding/subtracting the loop ticks value -- unless elements of the runs are able to gate them, which does make sense.
Going to test with a simple motor Start/Stop button controller...
But, ldmicro won't let me use an I/O pin as both output and input....
Removes pin from the list of pins when one is selected...
Did not expect that...
So, to do a Start/Stop operation with pins as input and another pin as output, guess on needs to use an internal relay?
Want XStart and XStop to be controlled by I/O pins and also have an LED on YMotorOn that lights when "motor" is running...
Well, guess this is how it is done in the Wikipedia Start/Stop example too, so guess that's how it is...
https://en.wikipedia.org/wiki/Ladder_logic
Pull up situation is another question...
The ldmicro .ld file now has a section like this:
Think can hack ldmicro to just have PA and PB.
But, should all the I/O really have pullups turned on?
Ok, found this window where you can set pullup bits.
But, they are only 8-bits...
Might have to pretend there are eight 8-bit I/O ports...
Looks good to me. Although the rungs, being dynamic length, would be processed by a loop and end up existing as a DAT section or even loaded from a separate file in the end.
You've got free rein on how complex a timer you want. PLCs all vary on this front. You can have several variants if you like. Multiple control inputs is an option. Same for counters. There is often even a small number of special timers/counters that can operate faster than the rest. Tying them to dedicated hardware is also up for grabs.
Outputs used as inputs is quite normal. Don't know why you had any issue there.
Ok, I get it now... ldmicro will let you use an "output" as both an output and an input...
Got the motor stop/start example going now in "simulation mode" with fake input.
Liking the idea of simulation mode along with single stepping, like ldmicro does...
Guess need to add mouse support next, so can control simulation.
I found a Chinese PLC on MCU effort here
https://www.stcaimcu.com/forum.php?mod=viewthread&tid=7796
and some speed claims here
https://www.stcaimcu.com/forum.php?mod=viewthread&tid=7945
and also this speed claim
The data operation instruction efficiency of Mitsubishi PLC is too low. For example, 1000 single-word registers are squared and the result is 32 bits, which is equivalent to 1000 multiplication and addition operations. The program is implemented with loop instructions. The actual time taken by Delta ES2 series PLC (CPU is 72MHz STM32F103) is 27.9ms, while the actual time taken by this program on 32MHz STC32G12K128 is 10ms.
Here is an SPI call example, as an image :
That suggests live pin access is certainly supported, here doing the SPI chip select and send within the blocks.
Below is a larger i2c library, I copied and AI translated.
Syntax is similar but not quite identical :
Ladder in-line uses =, +, etc , whilst code block uses MOV and AND etc
Sometimes it uses assembler level lines like
MOV Temp1,H00A2
and also supported is more HLL lines
DM0[RtcAddr]=Temp1+DM0[ValAddr]
It does not look like formal IEC61131, but somewhere between HLL and Assembler.
It seems the compiler/parser can accept any mix of the Ladder conditionals, and the text code ?
The examples I've seen so far have at most 1-2 lines of text language within any ladder rung, maybe convention is to call a function when code gets larger to keep the 'Ladder look' ?
This also needs live pin access to work.
and here is an example of a file include, and some direct SFR access, looking very assembler like, but giving MCU register level access here.
It's not so much the ladder is doing the live I/O, it's those Sxxx blocks are specially made to do it. Each one is a purpose built function that is designed to be used in execution sequence to produce I2C packets. I'd hazard a guess they're working with a hardware I2C module in the MCU.
I'd also say a little unusual to have such fine grain support at the ladder level but it does give an example of how one PLC maker has chosen to add extensions.
PS: It also demonstrates ordered execution. It's an example of where it's handy to have the function completed within the Ladder execution. Normally, this is reserved for maths on internal memory/logic only.
Got simulation mode working with mouse input on the Motor Start/Stop example.
If you want to try it, connect VGA adapter on basepin 16 and USB host on basepin 24.
Believe it is behaving similar to the ldmicro simulation mode....
There is no timing yet, there's enough serial diagnostic output right now to slow it down enough...
Added Emergency Stop contact and seems to work in simulation.
Note that the cursor is a magenta block and you single click on a contact to toggle it's fake i/o pin value...
ldmicro changes color scheme in simulation mode so that active lines are red.
Maybe will get to that eventually, but that seems like icing at this point...
Just noticed something in ldmicro that don't fully understand...
Contacts have an option for "Set High Level before simulation" that is only enabled for I/O pins that are INPUTS.
This puts a caret character in front of the symbol.
Why can't this apply to "Internal Relay" or OUTPUTS too? Not really getting that.
Also, doesn't seem like super important to implement right away...