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.


Topics - garysdickinson

Pages: 1 [2] 3 4 5
16
The simulator's handling of CHR$(n) does not match the PLC's behavior.

The simulator accepts 16-bit values for n and can create strings that appear to be an array of 16-bit values.

As an example:

A$ = CHR$(&H4548)

Creates a string that is 1 character in length and this only character has a value of &H4548 when run with the simulator.

When run on a PLC this same code creates a string that is 1 character in length and this character has a value of &H48.

I vote that the PLC's handling is correct.

I suspect that the Java library that you are using supports Unicode in some general way (localization) and that the java code that is close to CHR$() will accept 16-bit Unicode values.

I think that you should mask off and only use the least significant 8 bits of the "n" argument to CHR$(n).  This would make the simulator behavior match the PLC.

I don't care that the simulator implements strings as arrays of 16-bit integers rather than 8-bit integers as the PLC does.  This difference is not important and only shows up because of the implementation of CHR$(n) is flawed.

Gary D*ckinson

17
Technical support / ILock Documentation Issue
« on: November 29, 2016, 04:43:47 PM »
I got bored and decided to do some recreational PLC coding and decided to write a Tic Tac Toe program in ladder logic.  

To make the problem more challenging I decided to attempt to write the code without using custom functions, just 100% ladder logic.  

This is where I got into some issues with the [ILock] and [ILoff] functions.  Turns out that they are not broken, but I was transfixed by the example in section 9.3.5. of the TL6ReferenceManual.  

 [ILock] and [ILoff] turned out to not be very useful for my purposes, but I'd like to know how to program the "Equivalent Circuit".  I have tried everything, but I can't build this circuit with i-TRiLOGI 6 or 7.

Best regards,

Gary D*ckinson

18
Technical support / parsing problem with Print #n statement
« on: September 28, 2016, 08:47:54 PM »
The following statement will compile

print #3 1;2;3
[/font][/color]

This statement will not compile the leading "-" is not correctly parsed as part of a valid integer or as an arithmetic expresion:

print #3 1;-2;3
[/font][/color]

The following statement will compile. The expression "0-2" is recognized and is evaluated as "-1".  The code runs correctly with the simulator.

print #3 1;0-2;3
[/font][/color]

Just add it to the list of things that are not quite right.

Best regards,

Gary D*ckinson

19
Technical support / Status(2) behavior with extended filesystem
« on: July 01, 2016, 08:02:18 PM »
I use the extended file system as a method to update PLC settings via text files. The text files are uploaded to the  PLC via FTP. The PLC code checks for the existence of a new configuration file, periodically.  If a new file is found, the PLC code opens the file for read, extracts the new configuration data and then deletes the file.

The small problem is that it is difficult to tell if a file exists.  The filesystem documentation suggests that reading the value returned from the status(2) function will indicate if the command was successful.  A value of "1" indicates success.

PRINT #8 "<READ Z000.TXT>"   ' Attempt to open file
s = status(2)
[/color][/font]

The call to status(2) will return a 1, to indicate success, under more conditions that you might think. I get a value of 1 for all of the following:
  • File does not exist
  • File exists and contains no data
  • File exists and contains data
It is not possible to determine if a file exists by issuing the "<READ xxxx>" command. In fact I have found no way to determine if a file exists.  It is possible to determine the size of a file after you have "successfully" opened it with the "<READ xxxx>" command using the status(19) function.

Status(19) returns 0 for non-existent files.  Status(19), also, returns 0 if the file exists but hold no data.  For my purposes, an empty file is about as useless to me as a non-existent file.


PRINT #8 "<READ Z000.TXT>"   ' Attempt to open file for read

if status(2) <> 1
   ' File may be already open
   '
   PRINT #8 "</>"         ' Close  file
   return               '   and bail out
endif

if status(19) = 0
   ' File contains no data or it does not exist. You can't actually tell...
   '
   PRINT #8 "</>"         ' Close file
   return               '   and bail out
endif

' we got here because the file exists and contains data
'
' what follows is just test code to allow me to inspect the contents
' of small text files
'
for i = 1 to 26            ' write null strings to A$..Z$
   $$ = ""
next

' read at most 26 strings from the file and place then in A$..Z$
'
for i = 1 to 26
   $$ = INPUT$(8)
   if status(2) = 255
      exit      ' end of file reached,  break out of for loop
   endif
next

PRINT #8 "</>"      ' close file
[/color][/font]

Just some hints on the extended filesystem behavior.

Gary D*ickinson

20
Technical support / #Define Parsing Issue with "SeqN"
« on: February 28, 2016, 04:29:08 PM »
Gentlemen,

I have run into a small issue with parsing.  I attempted to enter the following text into the "Label Name" field of the "Define Variable Names" window, "Seq8ImgEEP16".

I got a Duplicate Label Error window with the following message:

    Label Name "Seq8ImgEEP16" is already defined as #8"

I actually know that #8 is the value for COUNTER #8.  I know that "Seq8" is a special name that can only be used with COUNTER #8. I, also, know that when you guys thought up the idea of using a COUNTER as a sequencer that dinosaurs roamed the earth.

I am a little concerned that your token parser when working with the string, "Seq8ImgEEP16" would "think" that "ImgEEP16" was a terminator for a TBASIC reserved word, "Seq8".  It is not white space, it is not a TBASIC operator, not a special character such as , : ( ) " and  it is not the end of the entry string.  It seemed like an odd place for the parser to decide that it had read enough characters.

It's not end of life, I just picked another text string, "ImgSeq8EEP16".  I am surprised that the token parser didn't find the "Seq8" buried in the middle of the string...

Best regards,

Gary D*ickinson

21
Technical support / User-Defined Runtime Error
« on: February 22, 2016, 04:22:16 PM »
I am attempting to debug a problem with an FMD88-10 PLC located in another continent.  My client describes the problem as the PLC being "paused".  The behavior that he is describing is consistent with a run-time error.  

I am working to get more accurate information about the issue from my client. But this will take both time and patience on my part.  I seem to not be blessed with much of either...

So in the mean time, I am trying to "instrument" the clients PLC code to log the problem.  I think that there are a few things that I can look for:
  • User-defined runtime Error: defining an interrupt handler for run-time errors using the INTRDEF 100, n statement.
  • Watch dog timer.  
So here are my questions about INTRDEF to catch a run-time error
  • With the INTRDEF approach, how does the custom function "know" what triggered the run-time error?  
  • Because CFs on the FMD88-10 do not accept arguments, you can't pass the error info to the CF.  Is the error stuff stored in some undocumented register that the CF might be able to access, say DM[4001]? Oh that would just generate another run-time error....
  • Yes, I know that the error info should be displayed on the 4x20 LCD. How does the CF "read" back what was written to the LCD by the PLC firmware?  


I believe that the only way to access this info is via a host link command.  I don't have enough spare serial ports on the FDM88-10 to allow me to wire the ports together. This way I could issue a host link command(s) to read the LCD display and get the strings spit out one serial port and sucked up by the other port.  

Could I achieve this by using issuing a host link command via Ethernet?

  • If I could "read" back the error info can I write it to the extended file system to generate an error log?  How much of the PLC code can I expect to run from the run-time error interrupt handler?
  • Is there anything that I can do from the real-time error interrupt handler other than to report the PLC?
On the dead-man timer mechanism.  Is there any way that I could log the events that led up to the timeout?  Or does the system just reset and start over?

Best regards,

Gary D*ickinson

22
I have run into the need to simulate multiple Modbus RTU devices while working on PLC projects.  

As an example one of my customers is using from 1 to 16 Modbus RTU pressure sensors and from 1 to 8 Modbus RTU totalizers. So, for him, I might need 24 Modbus devices in my office to be able to test his PLC code.

I have come up with a simple solution to the problem.  I am using a spare, cheap, single Nano-10 to emulate the behavior of all 24 devices.  Oh and while I am at it, I can program the Nano-10 to create all sorts of test patterns for the sensors to verify my PLC code.  

I simply wrote my own Modbus RTU code and asked the PLC to not respond to Modbus on the RS485 port.

I have attached a very simple PC6 file that shows how I emulate a single Modbus device. In this example I am emulating a Modbus connected motor speed controller. The reason I had to emulate this device is that my client didn't have a spare motor speed controller to loan me and even if he did, I didn't have the a way to power up the device.  My office is not wired for 460 VAC, 60 Hz, 3-phase power!

I'll post a bit of the ladder logic to pique your interest.

If you have any questions about this sort of thing, just ask.

You are more than welcome to use and hack up this code. Have at it!

Gary D*ickinson

23
Technical support / PulsePeriod/PulseFrequency Fx PLC
« on: January 22, 2016, 10:59:04 PM »
I have coded myself into a corner and need some expert advice.

I am using the Fx1616-BA PLC firmware revision 91.1

My client's application uses 3 flow sensors with pulse outputs.  They are connected to PLC inputs 1,3 and 5.  I am using the the High Speed counters 1,2, and 3 to capture the total pulse count for each flow sensor.  Additionally I am using the pulse measurement system to measure either the PulsePeriod on the same 3 inputs.

The incoming pulse frequencies of interest are between 10 and 300 Hz.

For test I am driving the PLC inputs from the PLC's PWM outputs.  This allows me to simulate the behavior of the flow sensors without having to have a 15 HP pump and 460 volt 3 phase electrical service at my home.

I am having a problem with PulseFrequency(1) and PulsePeriod(1).  With a constant input signal at 50 Hz, if I call Pulse Frequency(1) every 100 ms the returned value will not always be 50 Hz, but periodically a value close to 30 Hz is returned.  The 30 Hz measurement shows up about every 20 or 30 measurements and the erroneous measurment lasts for a single call to PulseFrequency(1).

Now what makes this more interesting, is that the PulseFrequency(3) and PulseFrequency(5) measurements are always correct.  I can connect all of the pulse inputs to the same frequency source (remember I am using the PLC's PWM outputs) and only the PulseFrequency(1) values are randomly wrong.

The HSC mechanism calls an interrupt routine when the HSC reaches a programmed limit.  I have instrumented the interrupt routine and have found that with a constant frequency input, that the time between calls to the interrupt routine  is not consistent. This tracks the behaviors of PusleFrequency(1) and PulsePeriod(1).  HSC[2] and HSC[3] do not show these behaviors.

I have tried to duplicate the problem with a minimal PLC program, but have not been able to get a simple program to exhibit the failure.  

Let me tell you what else is going on with this PLC that may have some bearing on the issue and why I can't get a simple program to fail:
1. I am using one of the RS-485 ports to communicate with an external Modbus RTU device.  The Modbus traffic is fairly light.  I am sending out 3 Modbus queries every second, maximum.  Only a single registers is accessed for each query.

2. The RS-232 port is running the most traffic. It is connected to Weintek HMI panel.  The HMI is using the RS-232 link to access the PLC internals.

3. I have turned on the "High Speed Timer" mechanism for 1 TIMER.  I needed a timeout of less than 100 ms.

4. There are no DELAY statements or polling loops in the custom functions. Everything is written to execute as quickly as possible and to execute as seldom as possible.

Do you have any ideas as to what I did in some other life to get into this mess?

I am concerned that there might some sort of issue in your firmware with regards to the handling of the pulse measurement / HSC[1].  I worry about interrupts getting shut off for too long and edge events being missed. I am not having problems with HSC[2] and HSC[3] and that is both "good news" and oddly interesting.

Any suggestions on what to look for or to test for would be greatly appreciated.  If I could get this problem down to a simple PLC program, It would be in the mail.

As always, I am happy to work with you to figure this out,

Gary D*icknson

24
Technical support / GetHigh16 / CRC16 functions do not return 32-bit values
« on: November 04, 2015, 12:10:46 PM »
You should correct the documentation for GetHigh16():

This function returns the upper 16-bit of a 32-bit integer variable v. This can be used to break the value of a 32-bit integer data or variable into two 16-bit values so that they can be saved to the EEPROM or to the 16-bit DM[n].
[/color]

The following code:
A  = GetHigh16(&H89ABCDEF)

Assigns &HFFFF89AB to A rather than &H000089AB.  I believe that GetHigh16() returns an Int16 and the assignment to a 32 bit integer, A, results in sign extension.  This behavior is consistent with TBASIC.

I think that fact that the return type for GetHigh16() should be documented as a 16-bit signed integer.  I think that it would be good to remind the uses that this will result in sign extension when the return value of the function is assigned to a 32-bit variable.

By the same token the documentation for CRC16 does not mention that the return type from this function is a 16-bit signed integer and the output of this function will be signed extended when assigned to a 32-bit variable.

The documentation for CRC16() fails to mention that it only uses the least significant 8 bits of the "range of integers":

This function returns the computed CRC16 for a range of integers starting from variable "var" with the range indicated in the parameter "count". CRC16 is a 16-bit version of "Cyclic Redundancy Check" - a popular mathematical formula for checking error in a data stream.


One of the examples in the documentation assigns the value returned the the CRC16() function to a 32 bit value:

X = CRC16(RELAY[2],4)


The example is misleading as only the lower 16 bits of "X" hold the CRC value and the most significant 16 bits will be either &H0000 or &HFFFF as a result of sign extension.  This return type of CRC16() should be documented. And it would be nice to remind the user that assignment of the return value from this function to a 32-bit variable will result in sign extension.

I am not trying to be a "whiner" but I have stumbled onto these old issues, recently, and they caused me some real grief. Just add them to the list of things that could be better documented.

Gary D*ickinson

25
Technical support / ADC Filtering Algorithm
« on: September 29, 2015, 02:00:16 PM »
As many have noted the FMD/Fx ADC readings dance around a bit from sample to sample.  I have not been alone in observing this behavior.

The PLC's have some options for enabling a moving average filter within the ADC firmware.  And this helps with the noise.

I have been using a simple digital filter that acts as a low pass filter to reject the random noise and at the same time increase the resolution of the ADC channel from 12 to 13 bits (0..8191).

This is the simplest version of the code:

DM32[1] = ((DM32[1] * 31)  + ADC(1) + ADC(1)) / 32
[/font][/color]

How do I use it?  I invoke this code 10 times every second usually timed to the Clk:0.1Sec signal.  My actual application only needs to look at the computed ADC value once every second or two.  I'm not working with stuff that changes all that rapidly.

How does it work? This is a simple single pole IIR (Infinite Impulse Response) low pass filter.  Each time the code is called the output, calculated output value, DM32[1] is updated. The new output value is 31/32 of the previous value with the "new" ADC value contributing only 1/32 of the "new" output value.  This is a digital implementation of an analog filter that might consist of a series resistor and a capacitor to ground.  Google "IIR low pass filter" for the real science.

Why do I add the ADC(1) value into the equation twice? I wanted to double the resolution of the ADC from 12 bits to 13 bits as a byproduct of over ADC over sampling. I found that if I use two separate ADC samples that this works much better.  There is actually a requirement for a bit of noise from the ADC conversion to make the oversampling work. Google ADC oversampling for the technical details.

Ok what is the down side of this trick?  Response time.  If the input makes a big change in value, the output, DM32[1] will take a few seconds to respond to the change.  The two constants 31 and 32 in combination with the sampling rate (0.1 Hz) determine the time constant for this filter.  If the filter is too sluggish for your requirements, increase the sampling rate or change the filter constants to 15/16 or 7/8.

I have been working, with systems that the analog values cannot change very rapidly.  One of my clients monitors the levels in 100,000 liter fuel tanks using a pressure gauge.  It takes over 2 hours to fill the tank and about a week to empty into transit buses.  So from one second to the next the level in the tank cannot change so the filter delay is insignificant in this application.

So I am happy to trade off some response time to get stable ADC readings and double the resolution of the ADC from 12 to 13 bits.


Best regards,

Gary D*ickinson



26
Technical support / i-TRiLOGI Simulator V6.49 build 5, INCOMM
« on: July 08, 2015, 11:43:14 AM »
Simulator problem with INCOMM(2)

The handling of INCOMM() in the simulator is wrong.  INCOMM() is supposed to return a value of -1 if there are no characters in the receive buffer.  However when running the simulator INCOMM() returns a value of 0.

This behavior converts the following code to an endless loop rather than a flush of the receiver buffer:

while (INCOMM(2) <> -1)
    ' flush receive buffer for this serial port
endwhile
[/font][/color]
I supposed I could add a loop counter to break the loop after 256 iterations to work around the simulator...

Sorry,

Gary D






27
Technical support / FileZilla 3.11.0.2 and Fx/FMD PLCs
« on: July 06, 2015, 01:55:06 PM »
I may be losing my mind, but I can't get FileZilla 3.11.0.2 to work with the Fx and FMD PLCs.

I've had older FileZilla versions work, but now when FilleZilla sends the "LIST" command to the PLC's FTP server, there is no response from the PLC and the command times out.

I can "talk" to the PLC using Windows command-line version of FTP. This works fine to send/receive files and list out the files on the PLC. So I know that the problem is not with the PLCs nor my marginal understanding of FTP.

Do you have a recommendation on a Windows based FTP client that still works with the PLC's.

The reason I bring this up, is that you have used FileZilla as sort of a standard FTP client for use with both the PLC web-server and the PLC's filesystem and FTP fileserver documentation.  And right now I can't get it to work...

Gary D.

28
Gentlemen,

Some of my favorite things that came with the addition of floating point support are the addition of local variables, custom functions that accept arguments and custom functions that return a value.

I think that it is very limiting that the only data type that is supported for the local variables, arguments and return values are floating point.  However, there are lots of cases where a 32-bit integer is much more useful.  

I understand that the the Bits2Float(n) and Float2Bits(n) functions can be used as sort of a "C-like" cast, that can allow a float variable to behave integer, but this mechanism is very awkward.

As an example, I'd like to use the local variable, %[1] as an integer:

%[1] = Bits2Float(3)
%[1] = Bits2Float(Float2Bits(%[1])/2)
[/color][/font]

Now I know that with the #DefineLocal I can make this code a little more readable, but it is still tricky:

#DefineLocal ILocal = Float2Bits(%[1])
#DefineLocal ILocalF = %[1]

ILocalF = Bits2Float(3)
ILocalF = Bits2Float(Ilocal/2)
[/color][/font]

This is what I like you guys to think about on future versions of the Fx firmware and the ITrilogi compiler, some sort of limited type definitions.  Some thing on this order:


' Declare a local variable that is to function as a signed 32-bit integer
'
#DefineLocal ILocal = (INT32)%[1]

ILocal = 3              ' Assignment of an integer variable to a local var
ILocal = Ilocal / 2   ' Integer math and an assignment to a local var
[/color][/font]

The cast "(INT32)" could clue the compiler that the type of the data stored at %[1] is an integer and should be treated as such.

Floats are nice, but there are a lot of cases where Floats can NOT do the job.  Try to represent the integer &h7ffffffffffffff with a float!  

If would be even nicer if there was a way to indicate to the compiler the behavior of a custom function so that arguments could be passed to a custom function as an integer and NOT promoted to a float. Think modern programming languages. Some sort of function declaration:


' The following function returns a 32-bit signed intger
'   and takes 2 arguments, the first one is an integer and the second is a float
'
INT32 FunctionX(INT32 A, FLOAT B)  ' function declaration

for i = 1 to 10
    DM32  = FunctionX(i, 3.14159)             ' using the function
next

[/color][/font]

The same function declartion would be needed to be repeated in actual custom function, "FunctionX".

I realize that this is probably  too big a change to a language like TBASIC to support, but I can always ask.

Gary D.



29
Technical support / Sorry, broke the simulator with PIDCompute
« on: May 08, 2015, 07:52:33 PM »
Sorry, but I broke the simulator in i_TRILOGI 7.03 Build 08.

I was working with the PidCompute function and got real different results between hardware and simulation.

This is the entire program:


PIDDef 1, 1.0, 0.333, 0.0, 0.0      ' chn, limit, P, I, D ...

A# = PIDCompute(1, 1.0)         ' A# will be 0.333 with simulator. 0.0 with hardware
[/font][/color]

Target hardware Fx-CPU F90.7

I think that the problem is with the PLC firmware and not the simulator. The issue may be setting the limits argument to PIDDef to a value of 1.  I was expecting the values returned from PIDCompute in the range of -1.000 to +1.000 but only got values of 0.0!

If I program everything using scaled arguments limit = 1000, P = 333.0 the the output from PID compute is 333. I suspect that something in the "new" PIDCompute function was not completely recoded for floating point calculations.

Is there a never version of firmware for the Fx processors?


Gary D.

30
Technical support / PLC Data EEPROM Manager 6.49 build 04
« on: April 20, 2015, 08:57:29 PM »
I am afraid that I've broken the tools, again.

This time it is with the "PLC Data EEPROM Manager" and 32-bit access to EEPROM.

Test hardware FMD1616-10 with FRAM-RTC installed, firmware r82A.

The program does not handle 32-bit integers very well.  There are mistakes in the code that are doing math as if the data is a 16-bit signed value and then sign extends it, incorrectly, to a 32-bit signed.

&H00008000 becomes &HFFFF8000 !

This is the PLC code:

' Quick test of EEP32 behaviors
'
' write a walking 1 pattern in memory
'
C = 1
for i = 1 to 32
   DM32 = C : C = C + C
next

a = 1      ' just a target for a break point

' copy first 32 DM32[] to eep32
for i = 1 to 32
   save_eep32 DM32, i
next

a = 2      ' just a target for a break point

' read eep32 back
'
for i = 1 to 32
   DM32[i+40] = load_eep32(i)
next

a = 3      ' just a target for a break point
[/font][/color]

The test code generates a walking 1 pattern in DM32[] writes this to EEPROM32, then reads it back without problem.

The EEPROM/FRAM manager can't do the same.

Look at location #16 in the jpeg for the problem with &H00008000 the EEPROM manger things that this location is &HFFFF8000!

The ".csv" file that can be produced contains the same error.

If the ".csv" file is edited and corrected then the value &H00008000 is loaded into EEPROM correctly without the 16/32 bit sign extension mistake.

The EEPROM manager has other problems with "reading" 32 bit values.  An example is if the value &H001BF5A (114522 decimal) is stored in EEPROM, the EEPROM manager will display it as &H0000BFA5. The EEPROM manager has lost the most significant 16 bits of the integer.  This is another 16/32 bit boundary issue.

I have a question.  If I have a file in .CSV format, how does the EEPROM/FRAM manager "know" if I want it to load the integer data as 16 vs 32 bit?  There doesn't seem to be anything in the .CSV file that specifies the data size/addressing scheme.

Gary D

Pages: 1 [2] 3 4 5