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 2 3 [4] 5 6 ... 34
46
Technical support / Re: Test Bit for greater than or Less than
« on: January 14, 2020, 02:37:08 PM »
Lorne,

Your ladder logic code is much better than my version.  It is much simpler and solves the problem very directly.  I am very impressed!

I will try and explain how my TBASIC program operates.  TBASIC supports three types of operators:
  • Arithmetic Operators: +, -, *,/ and MOD.  The first 4 show up on calculators. MOD is the remainder from integer division and is what was taught in elementary school (10 divided by 3 is 3 remainder 1).
  • Bitwise Boolean Operators: &, |, ^, and ~ ( bitwise AND, bitwise OR, bitwise Exclusive OR and bitwise NOT)
  • Relational Operators: =, <>, ... AND, OR

I will explain the use of Bitwise Boolean operators in my program line by line.
N = RELAY[1] & &h00ff   ' isolate bits 0..7

RELAY[1] is a 16-bit value that represents the the state of Relays 1..16.   If Relay #3 and Relay #16 is ON and the other 14 relays are OFF, then the value of RELAY[1] would have a decimal value of -32764 or &h8004.  But all arithmetic in TBASIC is done with 32-bit signed arithmetic the Value of RELAY[2] will be sign-extended to a 32-bit value.  I only care about the lower 8-bits of the value so I will use the bitwise Boolean & (and) operator to clear all but the least significant 8 bits of the value.

Look at the last two lines in the table to see the effect of the Bitwise Boolean & (AND) operator.

Comment                 Decimal    HexadecimalBinary
RELAY[1] as 16-bit-3276480041000 0000 0000 0100
RELAY[1] as 32-bit-32764FFFF80041111 1111 1111 1111 1000 0000 0000 0100
&h00ff value255000000FF0000 0000 0000 0000 0000 0000 1111 1111
RELAY[1] & &h00ff4000000040000 0000 0000 0000 0000 0000 0000 0100

This line of code is an assignment statement.  The variable, N, will be assigned the value that results from the Bitwise Boolean & (AND) operator.
N = RELAY[1] & &h00ff   ' isolate bits 0..7

The next line of code generates a map of all of the bits that are less than the single bit set in RELAY[1] and assigns this value to "L":

L = N - 1            ' low mask (bits less than single bit in N)

Comment                 Decimal    HexadecimalBinary
N400000040000 0000 0000 0000 0000 0000 0000 0100
N-13000000030000 0000 0000 0000 0000 0000 0000 0011

The line of code:
L = N - 1            ' low mask (bits less than single bit in N)
doesn't use any binary operators, just old school subtraction.  The reason that subtracting 1 from N gets a bit map is because of the fact that that only possible values of N are 1,2,4,8,16,32,64 and 128. Those values have only a single 1 set in their binary representations.  128 in binary is 10000000 subtracting 1 from 128 will get you 127 or 01111111 binary (lots of carries).

Table that shows the bit map off all of the bits less than N for all possible values of N:
Comment                 Decimal    HexadecimalBinary
N=1, L0000000000000 0000 0000 0000 0000 0000 0000 0000
N=2, L1000000010000 0000 0000 0000 0000 0000 0000 0001
N=4, L3000000030000 0000 0000 0000 0000 0000 0000 0011
N=8, L7000000070000 0000 0000 0000 0000 0000 0000 0111
N=16, L150000000F0000 0000 0000 0000 0000 0000 0000 1111
N=32, L310000001F0000 0000 0000 0000 0000 0000 0001 1111
N=64, L630000003F0000 0000 0000 0000 0000 0000 0011 1111
N=128, L1270000007F0000 0000 0000 0000 0000 0000 0111 1111

The next line of code is:
H = &hff ^ (L | N)      ' high mask (bits greater than single bit in N)
That line of code generates the mask for the bits that are greater than then single bit in N (RELaY[1]). This line of code uses 2 bitwise Boolean operators, "|" (XOR) and "^" (exclusive OR).

The Boolean OR operator looks at 2 bits and if either bit is a "1" the output will be a "1".  TBASIC's bitwise Boolean OR, "|", works with two 32-bit values and on a bit-by-bit basis to create a new 32-bit value.

Exclusive OR (XOR) is a Boolean operator that is used to compare two bits. If the bits have different values then the output is a "1". If the two bits have the same value (both 0s or 1s) then the outputs is a "0".

TBASIC supports a bitwise Boolean XOR, "^". Bitwise means that it works with two 32-bit input values and outputs a new 32-bit value based on a bit by bit comparison.

At this point in the program we have N and L.  L is a bit map of all of the bits less than the value of N. What we need to do is compute the bit map for all of the bits that are greater than N. 

This bit of code is evaluated first as it group in parenthesis:
(L | N)
The bitwise Boolean "|" create a new value that is a bit wise OR of L and N.  This value is the pattern of all of the bits that are less than N and equal to N. This is value is actually all the bits that we don't want.  The bitwise XOR comes to the rescue by allowing us to sort of subtract out the bits we don't want to generate the mask of bits greater than N.

H = &hff ^ (L | N)

&hff is a constant that represents all possible bits in an 8-bit value. The "^" operator will remove the result from the  (L | N) expression from the &hff constant. The result will be assigned to H as a map of all of the bits greater than N.

All of the possible values of H:
Comment                 Decimal    HexadecimalBinary
N=1, H254000000FE0000 0000 0000 0000 0000 0000 1111 1110
N=2, H252000000FC0000 0000 0000 0000 0000 0000 1111 1100
N=4, H248000000F80000 0000 0000 0000 0000 0000 1111 1000
N=8, H240000000F00000 0000 0000 0000 0000 0000 1111 0000
N=16, HE0000000E00000 0000 0000 0000 0000 0000 1110 0000
N=32, HC0000000C00000 0000 0000 0000 0000 0000 1100 0000
N=64, H128000000800000 0000 0000 0000 0000 0000 1000 0000
N=128, H0000000000000 0000 0000 0000 0000 0000 0000 0000

Now that the high and low bit maps have been calculated, it is pretty easy to look for bits in RELAY[2] that are less than and greater than the value in N.

if RELAY[2] & L
   SetIO LowFlg
else
   ClrIO LowFlg
endif

The bitwise Boolean operator "&" (AND) is used to determine if any bits in RELAY[2] are set at the bit positions determined by L. The result of the & operation will be either 0 or something non-zero. If any bits are set in RELAY[2] that correspond to bits in the L map the result will be non-zero and the SetIO LowFlag statement will execute.

I'm sorry that the length of the explanation.

Gary D

47
Technical support / Re: Test Bit for greater than or Less than
« on: January 13, 2020, 07:50:11 PM »
Lorne,

I thought about the ladder logic implementation.  I wrote 1/2 of the code that checks for bits in RELAY[2] that are less than the single bit in RELAY[1].

Why?  Just to see how hard it was to write.  I did manage to write more code on a single rung that the ladder logic can support so I broke the problem across two rungs.

I attached the code to this email.  The 1/2 that I wrote took 2 lines of ladder logic and a total of 50 ladder diagram words. Double that to get the complete program.

If your application requires this bit testing code to checked on every scan of the ladder logic, then I'd use the ladder logic rung rather than calling the CF solution.

Best regards,

Gary Dickinson

48
Technical support / Re: Test Bit for greater than or Less than
« on: January 10, 2020, 10:59:36 AM »
Thanks Lorne,

I like algorithms. Before I sent the reply I came up with some rather complicated ideas, also. I got balled up with numeric values and the idea of comparing values.

But the easier algorithm was to just figure out how to build bit maps base on the single bit in RELAY[1]. TBASIC supports a good set of Boolean operators which makes bit twiddling easy. 

The trick was that the less than map just needed a subtraction.  The bit map for greater than is easier, it's all the other bits that aren't in the less than map and the bit in RELAY[1] and this is just a bit of Boolean bit twiddling.

Gary d

49
Technical support / Re: Test Bit for greater than or Less than
« on: January 09, 2020, 10:39:35 PM »
    Lorne,

    This is not too difficult to do in TBASIC. It is a bit more complicated do do in ladder logic.

    This is the sort of code that I would suggest:

' SetFlgs - CF to test lower 8-bits or RELAY[1] against RELAY[2]
'
N = RELAY[1] & &h00ff   ' isolate bits 0..7
L = N - 1            ' low mask (bits less than single bit in N)
H = &hff ^ (L | N)      ' high mask (bits greater than single bit in N)

if RELAY[2] & L
   SetIO LowFlg
else
   ClrIO LowFlg
endif

if RELAY[2] & H
   SetIO HighFlg
else
   ClrIO HighFlg
endif


Comments on code:
  • L is map of the bits less that the single bit set in RELAY[1]
  • N is map of the bits greater than the single bit set in RELAY[1]
  • LoFlg is a RELAY that is set when there are bits in RELAY[2] that are less than the single bit in RELAY[1]
  • HighFlg is a RELAY that is set when there are bits in RELAY[2] that are set at positions greater than the single bit in RELAY[1]
Concerns about RELAY[1]
  • This code works if there is exactly one bit set in RELAY[1] bit positions 0..7
  • This code fails miserably if there are no bits set in RELAY[1] bit positions 0..7
  • This code fails in interesting ways if there is more than one bit set in RELAY[1] bit positions 0..7
  • If there is any chance that your rules about RELAY[1] may not be held sacred, then you need to add some code to test and verify that RELAY[1] meets the rules before you get to the code that sets/clears flags.

Best Regards,

Gary Dickinson[/list]

50
Technical support / Demo Program that uses the Stack
« on: January 07, 2020, 04:57:08 PM »
I attached a simple program that uses the stack mechanism.

The code shows a method to generate a string representation of a 32-bit signed value.  My version allows you to specify a field width and will right justify the value in a column width that you specify.

The demo code is similar in behavior to the TRI Str$(n,d) function.  The TRI version pads the converted value with leading zeros which are not very useful for my purposes.

The demo code is just that.  If I really want to right justify a 32-bit integer value in a fixed-width column I use code like this:

a$ = Str$(n) : a$ = mid$("                  ",1,d-len(a$)) + a$


I apologize for the fact that this code is 100% unreadable, it's just the best you can do in TBASIC.
 
Best regards,

Gary Dickinson

51
Documentation for Stack.PC6

52
Technical support / Implementation of a Stack or LIFO in TBASIC
« on: January 06, 2020, 03:31:35 PM »
This post shows how to implement a stack or Last In First Out data structure in TBASIC.

This example builds the stack in DM32[]. 
The size and location of the stack is controlled by entries in the #Define table.
Separate custom functions to initialize, push data and pop data are written to be as small and fast as possible.

The Stack.PC6 file is attached to this post. This is test bed code that will run in the i-TRiLOGI  simulator. This code should be easily adaptable for many uses.

The next post will have post a bit of documentation on how the stack operates.

Best regards,

Gary Dickinson

53
Technical support / Re: TBASIC Input On/Off Question
« on: December 11, 2019, 03:19:34 PM »
Jeff,

Good to see that you got something that works for you.

If this code is something that has to be maintained, I’d suggest using the "names" of things rather than the numbers.  As an example TIMER #9 was assigned the name "GndrTmr" in the "I/O Table tab in the i-TRiLOGI app.

So TimerPV[9] and TimerPV[GndrTmr] are equivalent.  The second version makes more sense.  The other reason for using the Itepms name rather than the number is that it is possible to re-arrange the Timers in the I/O and then TimerPV[9] may no longer refer to the GndrTmr.

The same goes for GetTimerSV(15).  Which timer is #15?  Use the name and not the number.

You will find that this convention of numbers and names for references includes things like custom functions, COUNTERS, TIMERs and several other things.

The use of “then" is optional.  It was probably put into TBASIC for the same reason as was the optional "let" keyword.  I suspect that this made TBASIC look more like Dartmouth BASIC from 1964 or Apples integer BASIC from the 1970s...  There is no reason to include this useless keyword.

The use of the "refresh" statement is interesting.  TRI has only documented the refresh statement in terms of updating both the internal copy of the physical INPUTs and making the internal copy of the OUTPUTs become the physical OUTPUTS.  This is normally handled by the before the first line of ladder logic is executed (INPUTSs) and after the last line of ladder logic is executed for the OUTPUTs. 

There is no mention  in TRIs documentation about refresh affecting the State of RELAYs.  You may be on to something!

Gary D

54
Technical support / Re: TBASIC Input On/Off Question
« on: December 10, 2019, 08:28:06 AM »
Jeff,

I don't think that I can help you out of the hole you are digging. 

This is what I understand
  • Your original code was written in 100% ladder logic and apparently worked on a T28.
  • You swapped out the T28 for a newer, more capable PLC. 
  • The old code was ported to your newer PLC.  And operated correctly???
  • You tried to make a "small" change using ladder logic but you were not successful.
  • You then attempted to make the change using a custom function. And this didn't work.

Nothing in your custom function makes any sense.

My best advice at this point:
  • Delete your custom function.
  • Document exactly what the original PLC program does.  It is based on 2 "drum sequencers" using Seq1 and Seq2.  Figure out exactly what is controlled at each state.  Figure out exactly why and when the sequencers change state. Document all of the time delays.  Document all inputs/outputs.
  • Go back to 100% ladder logic and add the new feature.
          OR
    Now that you have fully documented the program and understand how it works, it should be easy to rewrite it.

Gary Dickinson

55
Technical support / Re: TBASIC Input On/Off Question
« on: December 07, 2019, 07:18:50 PM »
 Input[10]. Allows read/write access to  groups of 16 input bits.

Boolean: TBASIC does not have a Boolean numeric type.  All arithmetic work is done with 32-bit sighed values. PLC data can be stored as 32-bit signed, 16-bit signed or 8-bit sign values.  The Fx / Smart Tile adds 32-bit floating values.  No Boolean.

The INPUTs can be accessed by TBASIC as a group of 16 INPUTs using the INPUT[n]. You are accessing an array of inputs in groups of 16.  The same access is available for RELAYS[] and many other PLC internals.

Accessing groups of INPUTs as an integer:

A = Input[1].     ' copy the state of Inputs 1..16 to the variable a.

The argument in the square brackets determines which group of inputs that you are accessing:
Input[1] accesses physical INPUTS 1..16 as an integer.
Input[2] accessed physical INPUTS 17..32 as an integer
   .
   .  There is a pattern to this
   .
Input[10] accesses physical INPUTS 145...160 as an integer.  I BET YOU DON'T CARE ABOUT THESE INPUTs!

So if INPUTS 145..160:
    a = Input[10]    ' a will have the value of &h00000000
If INPUT #145 was ON, only:
    a = Input[10]    ' a will have the value of  &h00000001
If INPUT #146 was ON, only: 
    a = Input[10]    ' a will have the value of  &h00000002
If INPUT #160 was ON, only:
    a = Input[10]    ' a may have the value of  &h00008000 or &hffff8000
                            '    Complicated answer follows

TBASIC does all arithmetic as 32-bit signed numbers.  Input[10] only represents 16 bits, but TBASIC promotes 16 bit things to 32 bit values.  The integer variables a..z are 32 bit signed variables so  the assignment, a = Input[10], will result in a 16 bit signed value being promoted to a 32-bit signed value that is assigned to the variable a.

The rules of signed integer arithmetic says that when going from 16 to 32 bits, the most significant bit in the 16 bit value will be propagated in to the most significant 16 bits of the 32-bit value.  This how &h8000 can end up as &hffff8000.  The most significant bit determines if the number is negative.

The reason that I worry you, is that TRI does not consistently document the behavior of how it deals with 8 and 16 bit stuff.  They have Improved their documentation a lot.  When in doubt write test code and run it in both simulation and on real hardware to be sure.

Accessing INPUTs as a single bit:

If you want to "see" if a single INPUT is ON, I would suggest using the "TestIO(n)" function where "n" is the name you assigned to the INPUT.  Testio() returns a 0 if the bit is off.  TestIO() returns a non-zero value if the bit is ON. Let's save that you named physical INPUT #10 "Stop",  the you can write TBASIC code that looks like this:

if TestIO(Stop)
    ' Stop is ON
    '
else
    ' Stop is Off
    '
endif

There are statements to set and clear 1 bit things.  SetI0 and ClrIO.  Look them up.

I have no idea what you are attempting to do with the SetTimerSV and GetTimerSV stuff.  Try something simpler.  Connect a relay to a timer in ladder logic then set the relay in your CF and then when the CF terminate you can watch the Timer count down.


Gary Dickinson

56
Technical support / Re: PLC Ignoring Timer
« on: December 05, 2019, 10:48:58 PM »
I can't stop thinking about this. I know that you can't rewrite this thing and just need to get something working.

I suggest that you get rid of both rungs that control the grinder TIMERs. The original and the new one that you added for the longer duration TIMER.

Create a new RELAY with a name that makes sense (i picked GrinderDone) to replace the use of the Grndr_Tmr contact earlier in the program.

Create two new replacement rungs as shown in the attachment.

Maybe this will get things working without a total rewrite.

Gary Dickinson

57
Technical support / Re: PLC Ignoring Timer
« on: December 05, 2019, 05:16:37 PM »
I took a look at line 44.  It looks a lot like 43.  But it uses a different timer, Grindr_Hi.  However, Grindr_Hi is not used any where else in the program.  So it has no possible affect on the program.

I think I figured out the usage of many of the timers and why the complicated logic around them.

The explanation: 

1.  the program is using the sequencer mechanism as a drum sequencer using the [AVseq] terminal on line 32.

2. The complicated logic  with the time is to use the TIMER going active to create a pulse for the [AVseq]. It does this by advancing the sequencer when the TIMER goes active. This changes the state of Seq1 and this disables the input to the timer.  The end result is a 1 scan time wide pulse when the TIMER runs out. This pulse somehow is used to advance the sequencer to the next state.

I use another approach for sequential logic that simply assigns the next state to the sequencer.  Oddly enough this direct approach is used on line 3 of the program.

The logic in the overall program is exceedingly convoluted.  The Seq:1 states are used to control other RELAYs C2 ... C7 and C14 for no apparent reason.  Perhaps this was done to ensure that no one could ever understand the program.

I assume you have been tasked with maintaining the program. I feel so sad for you.

Gary d

 

58
Technical support / Re: PLC Ignoring Timer
« on: December 05, 2019, 08:50:12 AM »
OK I can see sort of what you are trying to do.

I have a question and a suggestion.

Question. Line #43 that controls the TIMER, Gndr_Tmr, uses the contact associated with the TIMER as part of the logic. It looks like you are trying to create some sort of Seal-in circuit. The issue is that the TIMER contact only does not go active until it times out. This would then keep Gndr_Tmr active until either the contact with Seq1:1 goes active of SW_High goes inactive.  Is this what you want?

Can you simplify your program down to 2 or 3 lines and the problem still exists?  This would be a lot easier for me to figure out.

Suggestion:  The use of "_" in the names of the ladder logic elements used to work quite fine. Somewhere in the history of TRI they decided to allow attaching a comment to variable names in the TBASIC programming language that was added to PLCs after the T28.  This idea was stupid and it breaks all sorts of things.  Now in TBASIC the following are all references to the same thing, "A" "A_InputState", "A_A".  Essentially anything including the "_" is ignored.  If you use TBASIC and try to access INPUTs, OUTPUTs, RELAYs, TIMERs and such in TBASIC the "_????" will be ignored.

If you never use TBASIC you can keep the "_" names. If you use TBASIC, then DO not use "_" in any name anywhere. The "_" breaks the parts of the i-TRiLOGI syntax highlighter, also. 

TRI did come up with something that was a lot more powerful and I like a lot, the #define mechanism.  This mechanism does give you useful names for things. However, they didn't get rid of the stupid "_" idea.

Gary Dickinson

59
Technical support / Re: PLC Ignoring Timer
« on: December 04, 2019, 03:33:15 PM »
There is something wrong with the PC6 file that you attached to the post.  When I download and try to open your file with
i-TRiLOGi 6.52 I get the following error message:
"Open File Error
Error Opening FIle C:\Users\ ...\1500.1.3.5-1.pc6
File Not Found or is is not valid TRiLOBI file"

I opened your file and the first line of the file is as follows:
TRiLOGI Ver 3.0<SUB><LF>

<SUB> has a value of 0x1a  and is named a substitute character<-- I don't know why this character is present
<LF> is an ASCII line feed character this makes sense

The first line of .PC6 files produced by the current version of i-TRiLOGI looks like this

øõTRiLOGI Ver 5.0<LF>

The first two characters are Unicode. I am a 7-bit ASCII guy..

Perhaps your version of i-TRiLOGI has some limitations on what sort of files it can produce.  Tech support at TRI will recognize the problem, easily.

If you can print out your ladder logic to a PDF and either post a link or send it to the email address in my profile, I'd be happy to figure out why stuff is not happening for you.

Best regard,

Gary Dickinson

60
Technical support / Re: Changing STEPSPEED
« on: October 14, 2019, 12:45:10 PM »
Hello Thierry,

In regards to issuing a StepSpeed command during an active step move command, what I have observed is:
  • The step rate jumps to the PPS specified by the new StepSpeed command
  • The "acc" argument is ignored. There is no ramp. The step output jumps to the PPS value in the new StepSpeed command.

My testing of the StepMove statement is that it is ignored while the stepper is executing a StepMove command.  This approach will not get the behavior that you want.

The PLC stepper motor control hardware/firmware is very simplistic.  It assumes that you just need to move a stepper from one position to another.  The acceleration/de-acceleration and maximum step rate is specified before the StepMove statement executes.

The only thing that you can do while a StepMove is executing is to abort it with a StepStop statement.  This command just shuts down the stepper control system and the steps are abruptly stopped.

I do use the StepStop statement in production PLC code. I use this to stop the stepper system when a switch is closed indicating that the system is in the "home" position. However, the move statement that I use to find the "home" position has the PPS set to a very low rate and no acceleration/de-acceleration is used.

If you are really in need of a variable speed stepper based system you have only a few choices:
  • Stick with the PLC. If you speed changes are great enough for your system to lose steps then you will need to issue several intermediate StepSpeed statements before you get to your next target speed.
  • Look into some sort of motion control system that is external to the PLC. For some motor control needs I use 3 phase motors and VFDs.  The PLC communicates to the VFD via RS-485 (Modbus RTU).  VFDs manage the acceleration profiles to smooth out abrupt speed change requests that the PLC may make.

Best regards,

Gary Dickinson

Pages: 1 2 3 [4] 5 6 ... 34