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
Technical support / Inconsistent parsing of ":" as delimiter 7.12
« on: June 15, 2017, 11:42:25 AM »
I noticed that the parsing of ":" as a delimiter is very inconsistent.

This is the documented example of the use of ":" as a delimiter:
Code: [Select]
IF A >B:C=D*5:ELSE:C=D/5:ENDIF
The sample leads one to believe that the delimiter is exactly ":" with no additional whitespace requirements.  However, in some cases the parser requires " :" or " : " to be recognized as a delimiter.

This is my test sample that came up with my observed results.  The first line of code uses exactly ":" as a delimiter.  Each successive line I added white space until the parser was happy.  The last line will compile and please note that  ":" is not always recognized as a delimiter:
Code: [Select]
a#=0.0:call StrFmt:PRINT #8 A$;"\09ProductGpmGoal"      ' Error:Unknown Keyword: "all"
a#=0.0 :call StrFmt:PRINT #8 A$;"\09ProductGpmGoal"      ' Error:Unknown Keyword: "all"
a#=0.0: call StrFmt:PRINT #8 A$;"\09ProductGpmGoal"      ' Error:Undefined Custom Function Label:: "StrFmt:PRINT"
a#=0.0: call StrFmt :PRINT #8 A$;"\09ProductGpmGoal"   ' Error:Unknown Keyword: "RINT"
a#=0.0: call StrFmt :PRINT #8 A$;"\09ProductGpmGoal"   ' Error:Unknown Keyword: "RINT"
a#=0.0: call StrFmt: PRINT #8 A$;"\09ProductGpmGoal"   ' Error:Undefined Custom Function Label:: "StrFmt:"
a#=0.0: call StrFmt : PRINT #8 A$;"\09ProductGpmGoal"   ' Compiles
I know that this is not the biggest problem on the face of the earth, but it is just plain messy and very inconsistent.

Best regards,

Gary D*ckinson

17
I am having problem debugging a trivial program in 7.12.  I am breaking the simulator with the following code:
Code: [Select]
' FmtFloat - test CF that passes a copy of the the floating value, A#, and the argument, 1, to
'   the StripPlus function with the home that it will return a string in A$

%[1] = StripPlus(A#, 1)

When I try to test this code in the simulator I get the following run-time error message after StripPlus() executes and returns:


"Run-Time Error in Fn #1
 Error:illegal memory access!
 Program has been Halted"


This is the CF, StripPlus:
Code: [Select]
' StripPlus on entry %[1] is the float to convert, %[2] is the number of decimal digits
'
A$ = str$(%[1], -%[2])

' Now discarad the leading "+" that gets attached to positive values

if strcmp(mid$(A$,1,1), "+") = 0
   A$ = mid$(A$,2,len(A$)-1)
endif

Strip Plus actually does what I want, but I can't figure out what I have done to annoy the simulator.

I can write the code without the use of the "new" parameterized function calls, but I keep thinking there must be a use for this feature.

Do you have any ideas on how I broke the simulator?

Best regards,

Gary D*ckinson

18
Technical support / SetStop and Fx1616-BA Issue
« on: April 18, 2017, 02:04:08 PM »
The Fx1616-BA has 4 MOSFETs that drive OUTPUTS 5,6,7 and 8.  These can be used to drive 2 stepper motors using external driver boards.  This is fact and well documented.

In order to use Stepper Channel #2 to drive OUPUTS 7 and 8, i-TRiLOGI the ch argument must be 32 to access the stepper channel for the following TBASIC statements:
  • StepMove 32, count, r
  • StepMoveAbs 32, count, r
The following TBASIC functions and statements will accept either "32" and "2" as the "ch" argument and execute on real hardware as expected:
  • StepCountAbs(32)
  • StepCountAbs(2)
  • StepCount(32)
  • StepCount(2)
  • StepSpeed 32, pps, acc
  • StepSpeed 2, pps, acc
The "odd man out" is the StepStop statement.  The i-TRiLOGI complier will accept both "StepStop 2" and "StepStop 32". When running on Fx1616BA hardware,  "StepStop 32" does not abort the current motion command . "StepStop 2" does abort the current motion command.

The online help does suggests that legal values of "ch" are 1..8.  This is not true of any PLC that you have manufactured, yet.  There is no mention of the additional bits that are added into the channel number that determine if the channel is used as a direct stepper controller with 1 and 2 prefixes.  The 32 value is mentioned in the Fx1616BA User's manual for use with the StepMove statements.

Just add it to the list of stuff that is neither obvious nor consistent.

Best regards,

Gary D*ckinson

19
Technical support / StepMove and StepMoveAbs Documentation
« on: April 13, 2017, 05:03:04 PM »
There is a key bit of information that is not spelled out in the TRI documentation of the "StepMove ch, count, r" and the "StepMoveAbs ch, count, r" statements.

The issue has to do with the handling of the "r" argument to these statements. The "r" argument specifies which RELAY is to be cleared by the execution of the statement and then set, later, when the motion of the stepper motor has completed.  If the value of the "count" argument will result in the stepper not moving, then the RELAY WILL NOT be SET.

The problem with the StepMove and StepMoveAbs statements is that certain values for the "count" argument will result in the RELAY "r" not being set and this will result in PLC programs hanging up waiting for this RELAY "r" to be set.

The value that breaks StepMove is when "count" = 0.  The documentation states, "count is a 32-bit integer number which allows you to program the stepper motor to move from -231 to +/-231 .(i.e. about ± 2 x 109) steps".  Notice that the documentation includes the value of "0" in the range of legal values.  Trust me, 0 is not handled the same as the other 2 x 109 possibilities.

The value that breaks StepMoveAbs is when "count" equals the current absolute position.  The documentation for this statement does not mention this fact.

This is the sort of work-around that I use to ensure that all legal values for "count" work the same:


   ' The StepMove statement will not set the BrnCmdCmp RELAY if the
   ' the statement will result in no physical motion of the stepper motor
   '
   ' The fix is to execute the StepMove statement and then
   ' set the BrnCmdCmp RELAY
   '
   StepMove BrineStpprChnl,BrnArg1,BrnCmdCmp
   if (BrnArg1 = 0)
      SetIO BrnCmdCmp            ' set the command completion RELAY
   endif



   ' The StepMoveAbs statement will not set the BrnCmdCmp RELAY if the
   ' statement will result in no stepper motion
   '
   ' The fix is to test if the current absolute position is the same
   ' and the position specified in the argument to StepMoveAbs and after
   ' the StepMoveAbs statement is executed, set the BrnCmdCmp RELAY to indicate
   ' that the statement completed
   '
   if StepCountAbs(BrineStpprChnl) <> BrnArg1
      ' Move stepper to new position
      '
      StepMoveAbs BrineStpprChnl,BrnArg1,BrnCmdCmp
   else
      ' Stepper will not move, so execute the StepMoveAbs statement
      ' to update the stepper motion counters and then set the
      ' command completion RELAY to indicate that the statement has
      ' completed
      '
      StepMoveAbs BrineStpprChnl,BrnArg1,BrnCmdCmp
      SetIO BrnCmdCmp
   endif

[/color]


I know that the StepMove family of statements have been around for a long time.  I'm just new to using them.

Best regards,

Gary D*ckinson

20
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

21
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

22
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

23
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

24
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

25
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

26
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

27
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

28
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

29
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



30
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






Pages: 1 [2] 3 4 5