Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - garysdickinson

Pages: 1 ... 8 9 [10] 11 12 ... 34
136
Technical support / Re:INTRDEF
« on: November 06, 2017, 10:46:26 AM »
Lorne,

The use of interrupts can be tricky.  

If your goal is to respond quickly to some external event by changing an OUTPUT, the response time is dominated by the ladder logic scan rate.  The OUTPUT will not change until the end of the scan.  In thus sort of scenario, it often makes more sense to just handle the input with ladder logic.

If you have an external INPUT that wiggles at a rate greater than you can handle with ladder logic, and you need to count each wiggle, I’d suggest that you look at using the specialized high speed counter system.

In any case, I strongly urge you to build a test system to throughly exercise and test your PLC firmware.  For most of my clients, I use a second PLC to model their system.  I typically model analog signals, Modbus devices, pulsatile inputs and many other things.  

You can model high speed digital pulses using the stepper motor or PWM SYSTEM. This is what I use.

If your system's response to these interrupt inputs is critical and the failure to respond could result in Catastrophe, please test the snot out of your code.  Make certain that your code can handle input rates significantly higher then the actual system can generate.

Best regards,

Gary D*ckinson


137
Technical support / Re:INTRDEF
« on: November 03, 2017, 04:31:21 PM »
Lorne,

The answer to your question is no.

A specialized interrupt INPUT can only respond to either a rising edge or a falling edge event, not both.  If you issue 2 IntrDef statements for the same interrupt channel:

IntrDef 3,ISRPosEdge,1
IntrDef 3,ISRNegEdge,0

Only the last IntrDef statement would be active.  

If you need to respond to both the positive and negative edges of a signal, you will need to route this signal through 2 separate Interrupt input channels and setup one channel for positive edge and the other for negative edge responses:

IntrDef 3,ISRPosEdge,1
IntrDef 4,ISRNegEdge,0


Please note that the documentation for the IntrDef statement is slightly incorrect.  I have added some text in RED to correct the documentation:


INTRDEF ch, fn_num, edge
Purpose
Enable Interrupt Input channel ch.
ch = interrupt channel number (pls refer to PLC installation guide)
fn_num= Custom Function name or number to execute when interrupt pin changes according to the defined edge. This is the Interrupt Service Routine ISR.
edge = Positive number means rising edge-triggered. 0 or negative number means falling-edge triggered.

At my urging, I have gotten TRI to allow the use of the name of the custom function as well as the number of the CF for the 2nd argument.  I would suggest that you only use the name of the custom function as it is both more readable and it is safer.  

Why do I mention safer?  It is possible to change the number associated with a custom function by shifting functions up and down in the "I/O Table" definitions.  If you use the CF number and shift the CFs around in the table the WRONG CF will be executed as the ISR and your code will not function as you had intended. If you reference the CF by name, then the code should compile and execute as expected, even if you re-arrange the CF in the "I/O Table".

Best regards,

Gary D*ckinson

138
Technical support / Re:Device ID
« on: October 31, 2017, 10:15:25 PM »
Lorne,

I've been working with the TRI PLCs for over 14 years. I, too, struggled with their documentation.

I share your option of TRI's technical support.  They are fantastic! Very, very bright people at TRI.

90% of the PLC code that I have written is test and debug code. This is the only way to figure out how some of the stuff works.  The other 10% is actual product code for clients.

My specialty is algorithms.  Let me know if you get stumped.  I've probably already written code that may be helpful.

Gary D*ckinson

139
Technical support / Re:Device ID
« on: October 30, 2017, 03:28:27 PM »
Lorne,

You might want to look at the SetSystem statement.

Specifically  SetSystem 8, n

This is the documentation that I found:

Allow the PLC to change its own ID from within TBASIC temporarily to the lower 8-bit value provided in the "data" parameter for individual COMM port. The new ID is volatile. It does not overwrite the default PLC ID which can only be changed from a host program using the "IWxx" command.
SETSYSTEM 8, n     
COMM1:  n = &H0001 to &H00FF 
COMM2:  n = &H0101 to &H01FF    
COMM3:  n = &H0201 to &H02FF    
COMM4 (Ethernet): n = &H0301 to &H03FF
ID for the respective COMM port will be set to 01 to FF after running the above command. (only applicable to PLC with firmware r76 and above)


The documentation suggests that the changed to the device ID is "temporary".  You will probably have to run this command each time the PLC restarts.

You may, also, want to look at the Status(8) function. This returns some version of the PC ID.  I suspect that the value returned is the default value that was assigned to the PLC and not the temporary ID assigned with the SETSYSTEM command.

Lorne, I noticed that you are using the "Fx" series PLCs.  With my customers that use the Fx PLCs or the FMD PLCs with the RTC/256M memory card, I use the FTP server mechanism to manage PLC configuration data.  I generate an ASCII text file and upload this file to the PLC's FTP server. The PLC, periodically, checks for the configuration file and if it is found does the following with it:
1. Reads it to determine that it is valid
2. Parses out the configuration data and saves them the EEPROM.
3. Erases the uploaded file.  This indicates that the PLC processed the configuration data.
4. Writes out to a 2nd FTP file the new PLC configuration.  This allows me to check and verify the configuration of the PLC program.

I also, allow, my customer's to change configuration parameters via the HMI.  When the customer commits to the changes, the PLC will write out a new 2nd FTP file that documents the new configuration.

This 2nd FTP file can be copied from the FTP server, renamed and then used to configure other PLCs or just archived for documentation purposes.


Maybe this will help,

Gary D*ckinson

140
Technical support / Re:HST
« on: October 05, 2017, 09:15:06 PM »
Art,

Sorry, I misread your question.

About all that makes sense for High Speed Timers is to put the "HSTIMER 2" statement in a custom function that is called on the first scan of the ladder logic.  

I see no reason to save the PV (present value) for a TIMER and attempt to restore it after the PLC reboots.

The Set Value of the TIMER is defined by in the I/O table and will be initialized when the PLC restarts.  

If, however, your program changes the SV for TIMERs and COUNTERs during the execution of the code and you need to restore the changed SV, then you need to save to EEPROM and restore the SV of these items.  

I do change the SV for TIMERs and COUNTERs to values other than what is defined in the "I/O Table" while the PLC is running.  I have clients that need to customize the behavior of a PLC based system for specific clients.  Rather than writing a different PLC program for each of these customers, I have the PLC read the system configuration from an ASCII text file stored in the PLC filesystem.  This configuration file can be changed either via the HMI attached to the PLC or by uploading via FTP a new configuration file.

I do use High Speed Timers in my PLC programming.  But I am very careful to ensure that the worst case scan time through my ladder logic program is less than the time-base of the TIMERs to ensure that the TIMER is reasonably accurate.  

Sorry for not reading your question correctly.

Gary D*ckinson

141
Technical support / Re:HST
« on: October 05, 2017, 11:16:04 AM »
Art,

You probably need to add the following sort of statements to your startup up CF to enable the high speed counters #1 and #2:
Code: [Select]
' InitHSC - Initialize High Speed Counters that are used by the turbine flow sensors
'
' The first argument is the high speed counter number 1..3
'
' The second argument is the custom function to be called when the counter reaches the
' value specified by the third argument. This custom function is responsible for
' keeping the running total of gallons and for resetting the high speed counter back to 0
' to start counting the next gallon.
'
' The third argument is the number of pulses per gallon
' &H7ffffff is a really large positive integer that effectively disables the interrupt from ever
' being called. The interrupt mechanism is buggy in F91.2 (Fx series PLCs).
' I am periodically polling the high speed counters and directly managing their count values
' without using the interrupt CFs mechanism as a work-around for the F91.2 issues.
'
HSCDEF  1, TotalizeProdFlw,  &h7fffffff
HSCDEF  2, TotalizeBrineFlw, &h7fffffff

' Reload counter values from EEPROM
'
HSCPV[1] = Load_EEP32(HSC1EEPAddr)
HSCPV[2] = Load_EEP32(HSC2EEPAddr)

You will need to add a CF to save the count values of your high speed counters to non-volatile memory.  You have to decide when to save the count values. You can set up a CF that saves the values periodically or based on the power fail interrupt. The CF to save the values would look something like this:
Code: [Select]
Save_EEP32 HSCPV[1], HSC1EEPAddr
Save_EEP32 HSCPV[2], HSC1EEPAddr

Gary D*ckinson

142
Technical support / Re:When t use delimiter
« on: September 27, 2017, 10:10:42 PM »
Lorne,

I thought a little more about the use of delimiters in TBASIC, and how to write code that is 100% unreadable but actually compiles and works.

The 1st half of the following code example is how I usually write PLC code.  The 2nd half is the same code but I have intentionally pushed the boundaries of common sense with how I have use delimiters to make the code unreadable.

' Custom function to print 0..99 on serial device #2
'
' This is the text that will be sent to the serial device:
'
'   00 01 02 03 04 05 06 07 08 09
'   10 11 12 13 14 15 16 17 18 19
'   20 21 22 23 24 25 26 27 28 29
'   30 31 32 33 34 35 36 37 38 39
'   40 41 42 43 44 45 46 47 48 49
'   50 51 52 53 54 55 56 57 58 59
'   60 61 62 63 64 65 66 67 68 69
'   70 71 72 73 74 75 76 77 78 79
'   80 81 82 83 84 85 86 87 88 89
'   90 91 92 93 94 95 96 97 98 99
'
print #2 "First Version"      ' Debug Message
for i = 0 to 9               ' outer loop for the 1s digits
   for j = 0 to 9            '   inner loop for the 10s digits
      print   #2 i;j;" ";      '   print each number with 2 digits and a space
   next
   print #2               '   newline at the end of each group of 10 numbers
next

' this code will produce the same results
'
print   #2
"Second" ;
print   #2
" Version"
for  i = 0
to  9  for
j = 0 to 9
print   #2
i;j ;" " ;
next print
#2    next
[/font][/color]

When run under simulations this is what is output to the simulated serial port:

First Version
00 01 02 03 04 05 06 07 08 09
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
30 31 32 33 34 35 36 37 38 39
40 41 42 43 44 45 46 47 48 49
50 51 52 53 54 55 56 57 58 59
60 61 62 63 64 65 66 67 68 69
70 71 72 73 74 75 76 77 78 79
80 81 82 83 84 85 86 87 88 89
90 91 92 93 94 95 96 97 98 99
Second Version
00 01 02 03 04 05 06 07 08 09
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
30 31 32 33 34 35 36 37 38 39
40 41 42 43 44 45 46 47 48 49
50 51 52 53 54 55 56 57 58 59
60 61 62 63 64 65 66 67 68 69
70 71 72 73 74 75 76 77 78 79
80 81 82 83 84 85 86 87 88 89
90 91 92 93 94 95 96 97 98 99
[/font][/color]

TBASIC unlike very early dialects of BASIC can have statements and expressions split over multiple program lines.  You can have multiple statements on a single line as long as you have some delimiter between them.

Arithmetic, bitwise and relational expressions are handled a little differently by the TBASIC expression parser and in most cases do not require white space delimiters.  You can write code like this and it works:
A=45/C*42+2MOD4  ' no spaces needed!!!

Wasn't this fun?

Gary D*ckinson

143
Technical support / Re:How to protect my PLC code from others?
« on: September 26, 2017, 01:38:47 PM »
My best suggestions on how to product one's PLC programming from being stolen or used by others is to write the program using the following guide lines:
  • Use no comments either in ladder logic or in custom functions
  • Never use the # Define mechanism in custom functions to provide meaningful names for variables
  • If you need to access DM[] as 32-bit variables, then never use the DM32[n] syntax.  Always access it as two 16-bit values.
  • Write all custom functions as a block of code with no indentations or delimiters.  Attempt to make the code margins even or perhaps in the shape of a tree.
  • If you use a subroutine, don't give it a name just invoke it as "call 3".
  • Don't use for/next  or while/endwhile statements, just use as many GOTO  statements as possible.  You don't want anyone to be able to guess that you have a bit of code that might run in a bit of a loop.
  • Don't use if..elif..elif..elif..else..endif only use nested if..else..if..else..if..else..endif..endif..endif (nested if statements).  And if possible nest the if statements as deeply as possible.
  • Name all INPUT, OUTPUT, RELAY, COUNTER and TIMER items with names like INPUT1, INPUT2 and OUPUT3.  If you give any of these things useful names, some causal user might guess what you are doing.


I am certain that their are more creative ideas on how to obfuscate your work, but these will get you a long way.

Best regards,

Gary D*ckinson
[/list]

144
Technical support / Re:When t use delimiter
« on: September 26, 2017, 01:26:57 PM »
Lorne,

TBASIC is a TRI's programming language has syntax that is close to many other BASIC languages.

Your question about the use of delimiters is a very valid one to ask.  First I must remind you that ":" is only one of many delimiters used by TBASIC.  The other delimiters are ASCII spaces, ASCII tabs and ASCII newline characters.

The purpose of a delimiter is to allow the TBASIC compiler to be able to break the program you have written down into bits of stuff that can be understood.

Your first program statement:
IF A>B  THEN
    THEN C=D*5
ELSE
    C=D/5
ENDIF

Is not written correctly and cannot be compiled.  You are still holding on to the use of the "THEN" that I have corresponded with you about earlier. There is no reason to use "THEN" in TBASIC .  The use of "THEN" is 100% optional, however the use of THEN twice in row is 100% wrong.

First get rid of both "THEN" keywords. Your program snippet will compile:

IF A>B
    C=D*5
ELSE
    C=D/5
ENDIF

I have marked the delimiters in red for your programming snippet.  __ is where you used an ASCII space(s) as a delimiter and <CR> is where you you used an ASCII newline as a delimiter.

IF__A>B<CR>
__C=D*5<CR>
ELSE<CR>
__C=D/5<CR>
ENDIF<CR>

You will notice that you have several delimiters in a row.  You can make this program "smaller" by eliminating the extra delimiters. This is about as small as you can make your programming snippet:

IF_A>B_C=D*5_ELSE_C=D/5_ENDIF<CR>

OR (without the delimiter highlighting):

IF A>B C=D*5 ELSE C=D/5 ENDIF

Your shortened snippet will not compile as you used "IS" rather than "IF":

IS A>B: C=D*5:ELSE:C=D/5:ENDIF

OK, what about the ":" delimiter?  In many BASIC dialects the ":" was used to allow one to place multiple statements on a single line of code.  In TBASIC you can get this done with the use of the ASCII space.  I am not certain when or if the ":" delimiter is ever required by TBASIC. However, If I wanted to put the following code on a single line, I might use the ":" to remind me of the statement boundaries:

IF A>B
    C=D*5
    SetIO BIT0
ELSE
    C=D/5
    ClrIO BIT0
ENDIF

Could be written as:

IF A>B  C=D*5 : SetIO BIT0 ELSE C=D/5 : ClrIO BIT0 ENDIF

I added the " : " between the statements to make it a bit easier to read.  Please note that  I am using " : " as the delimiter rather than ":" as  some versions of the i-TriLOGI do not consistently recognize ":" as a delimiter.  TRI is aware of this and may be fixed this problem in future releases.

Your last question about:

IF SUMCV >= 27 SETIO SUCT27 ELSE CLRIO SUCT27

You have supplied all of the delimiters required by TBASIC. This code will compile and it will execute as you expect.

The key things to think about is this, "If you write code with litter or no structure, will you be able to understand or maintain this code in the future?"

Best regards,

Gary D*ckinson

145
Technical support / Re:TIMER Contact Behavior
« on: September 19, 2017, 12:14:28 PM »
Thanks so much for adding the corrected info to the discussion.

I had forgotten about the ”-1” value for the TIMER PV. Thanks for reminding me.

Gary D*ckinson

146
Technical support / Re:TIMER Contact Behavior
« on: September 13, 2017, 10:12:20 PM »
OK, thought about it and I think that I can explain how TIMERs work.

1.  TIMERs are implemented in PLC firmware.
2.  Each TIMER has a data structure managed by the PLC firmware. This data
   structure for each TIMER includes these things and probably a few other items:
      TIMER SV
      TIMER PV
      TIMER control input state
      TIMER contact state

OK, with that in mind, this is how I think a TIMER works:
1. Following reset, the input state for all TIMERs is set to zero to indicate that the TIMERs are inactive.
   The contact associated with each TIMER is set to 0 to indicate that the TIMER has not timed out.
2. On the each scan of the ladder logic the evaluates the control input state for each structure during the
   ladder logic scan.  This state is maintained in the data structure associated with the TIMER.
3. If the ladder logic scan determines that the TIMER control input is 0 or false, the contact associated with the
   TIMER is set to 0 or de-asserted.
4. If the ladder logic scan detects that the TIMER control input has transitioned to l, then the TIMER is active.
   The TIMER SV is reloaded and the TIMER's contact is set to 0 or FALSE. These changes happen when the
   rung of ladder logic that controls the TIMER is executed.  These changes are visible to all other rungs of ladder
   logic that follows the rung that controls the TIMER.
5. At the start of each ladder logic scan, each TIMER that is active at the end of the previous scan may have its PV
   decremented based on either a 0.1 second or 0.01 clock. If the TIMER's PV is now 0, then the contact associated
   with this TIMER will be set asserted (set TRUE).  Please note that contact transitions for 0..1 at the start of the
   ladder logic scan and NOT at the rung of code that controls the TIMER.

The most important points to take away from this long winded guess on TIMER behavior is that a TIMER's contact is set TRUE (1) at the beginning of the scan and the contact is set FALSE (0) the ladder logic rung that controls the TIMER is evaluated.

The fact that the TIMER contact is set and cleared at different points in the execution of the ladder logic explains why the TIMER contact can be "seen" as TRUE before the ladder rung that controls the TIMER and is never seen after this rung in my one line PLC code to implement an oscillator.

This is a bit of tricky TIMER stuff but it is not broken, but it is now documented.


Best regards,

Gary D*ckinson




147
Technical support / TIMER Contact Behavior
« on: September 13, 2017, 04:41:50 PM »
I have run into a ladder logic behavior that I can't quite figure out.
I have attached a screen shot of the entire program.

The middle rung of logic is a simple oscillator that uses a TIMER and it's inverted contact in a bit of a loop.  I am an old hardware guy and I'd describe this as either a relaxation oscillator or a ring oscillator.

My expectations are that once the TIMER runs out that it's contact would go active for 1 entire scan of the ladder logic and then go inactive for the SV period of the TIMER.

This contact behavior is consistent with both the simulator and my Nano-10 test hardware.


What I observe:
  • The TIMER counts down and reloads with a period determined by it's SV.  It oscillates as expected!
  • The TIMER's contact goes active and inactive and can be "seen" on ladder rungs before the TIMER.  As expected!
  • The contact appears to only be inactive after the rung with the TIMER.
The sample code has 2 simple pulse catches that sets RELAYs if the TIMER contact is ever active.  These RELAYs can only be cleared by restarting the PLC or via the online monitoring program.

Why can't I come up with a good story as to why the RELAY contact is not visible below the line of code with the TIMER, but is visible on the next scan to ladder rungs that come before the TIMER.

I am just getting too senile to work with this stuff???

Best regards,

Gary D*ckinson

148
Technical support / Re:Self Starting Timer
« on: September 12, 2017, 10:45:41 AM »
This is a simpler version that gets a big fat visible output pulse and is based on 2 TIMERs.  The SV for the TIMERs determine how long the OUTPUT is on and off.

I use this setup when I need a very slow (low frequency) PWM output that the PLC PWM hardware cannot support.  I just change the SV on the fly to adjust the frequency and duty cycle.

Gary D*ckinson

149
Technical support / Re:Self Starting Timer
« on: September 11, 2017, 12:01:16 PM »
You can shorten your Self Starting Timer down to a single line of code that does not require a custom function to restart.  The trick is to use the inverted version of the TIMER contact to control the timer.

Please be aware that any ladder logic that uses the TIMER contact must be positioned before the rung of logic that controls the TIMER.

I'd suggest that you place the TIMER on the last rung in your program.

Please be aware that the width of the pulse created by the use of the TIMER is only a single scan of the ladder logic.  I like big fat 50% duty cycle outputs so that I can easily observe them with the flashing LEDs on the PLC OUTPUTs.  This is why I added the flip flop logic in the lines before the TIMER.

Best regards,

Gary D*ckinson

150
Technical support / Re:Modbus addresses
« on: September 11, 2017, 11:19:51 AM »
The first problem is that the correct Modbus register address to access DM[782] should be 1782 not 782.  The basic rule to generate a Modbus register address for DM[n] addressing is to add 1000 to "n".

If the Nano-10 responds to Modbus address 782, it is not documented as to what address 782 might contain.  I cannot explain what you are seeing if you attempt to read multiple registers from the Nano-10 starting at modbus address 782.

The internal Modbus addresses are documented (hinted at) in the Nano-10 User Manual version 7.2 in figure 14.6.1.1

I have a couple of suggestions:

  • Write a PC6 program that contains a single custom function that runs on the first scan of the ladder logic. This CF should write an incrementing 16-bit pattern to each of the DM[] locations.   This is what I'd suggest:
Code: [Select]
' Fill Nano-10 DM[n] locations with a value equal to the index, "n", for each of the
'   1000 DM[] registers supported by the Nano-10
'
for i = 1 to 1000
   DM[i] = i
next
Now you know exactly what to expect when accessing any DM[] location.
  • Change your Modbus TCP/IP program to use the correct registers address for DM[n].  Now see if you are actually getting the correct data from the Nano-10
  • Use a Modbus test program that allows you to see exactly how the Nano-10 responds.  I have used Chipkin Automation Systems CAS Modbus Scanner for both Modbus TCP/IP and Modbus RTU with the PLCs as clients and many other Modbus devices.  This is a free program at:

http://www.chipkin.com/products/software/modbus-software/cas-modbus-scanner/
[/list]

Best of luck,

Gary D*ckinson

Pages: 1 ... 8 9 [10] 11 12 ... 34