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 ... 6 7 [8] 9 10 ... 34
106
Technical support / Re:Stepper Motor Demo Code
« on: May 02, 2018, 03:40:10 PM »
This post adds the documentation.  I had to do some ugly compression to get this under the forum 200K file size limit.

Gary D*ckinson

107
Technical support / Re:Stepper Motor Demo Code
« on: May 02, 2018, 03:13:21 PM »
The stepper motor demo code does not need a user interface.

But, I added one to make this into a stand-alone test system.

I used a Weintek 7" HMI for the user interface.  The attached code will work on any of the Weintek HMIs. You can re-edit it to work on the smaller 4.3" displays.

108
Technical support / Stepper Motor Demo Code
« on: May 02, 2018, 03:10:38 PM »
I have attached the PLC program StepperTest V2_01.PC6 to this post.

This is a test program that I wrote about 3 years ago to test a stepper motor-controlled valve.  This is a simplified version of that test code.  

If you are thinking about use the PLC for stepper control, this is an excellent starting point as all of the code can be used and extended for your purposes.

Best regards,

Gary D*ckinson

109
Technical support / Re:Stepper motor homing sequence not behaving
« on: April 25, 2018, 03:27:54 PM »
Neal,

I am glad that you are making progress on your project.  Your code didn't get attached.  The Yabbse forum acts real funny with attachments.  They tend to vanish.

Stepper motors are deceptively simple.  The simple view is that for every step you issue the stepper will rotate a predicable amount.  Unfortunately the simple view is an illusion.  There are real-world issues with starting torque, holding torque, acceleration profiles, winding currents and micro-stepping modes.

1. The big issue with stepper motors is torque.  If a stepper is a rest and you issue a single step pulse, the stepper will move if the torque that it can generate is greater than what it takes to move the stepper's rotor and the attached load.  Torque is determined by the design of the stepper motor, the current that your stepper driver supplies and the step mode that your driver is using.

2. Once you can issue a single step pulse, the next problem is figuring out how long you have to wait to issue the 2nd step pulse.  You trying to figure out the maximum step rate without losing any steps.  If the stepper stalls and hums or if the system continues to move (inertia) after the last step pulse, you are stepping too fast.  You need to figure out this maximum step rate and cut it in 1/2.  This is as fast as you can operate without using acceleration / deceleration ramps.

3. Next you will run into mechanical resonance.  Stepper based systems can howl and scream at certain step rates and these are determined by a mess of physics.  To keep the noise down you can change the step mode of your controller (micro-stepping) rates like 1/4 or 1/8 might help quiet the system down. However, this reduces the starting torque and you will need to re-evaluate the maximum step rate without acceleration (step 2).  The other strategy is to run your system at higher step rates and you will need to figure out the acceleration/deceleration profiles to ensure that you do not lose position.

If you are using the stepper motor to move the door against gravity, you get into another stepper motor bit of physics, holding torque.  Most stepper motors use permanent magnets and this results in a motor resisting rotation when the coils are not powered.  Most stepper controllers, reduce the current through the coils when the stepper is not actively moving to reduce power usage. With no or low current it will be much easier for your load to move due to gravity.

If your system requires the stepper coils to remain energized to keep the door from closing due to gravity, you may need to use a much larger stepper and deal with the larger power supplies and the cooling issues to keep things from burning up.

I'd vote to counter balance the door.  This will increase the inertial mass of your system, but will require much less power to operate.  You might, also, consider a positive latching mechanism to hold the door open if living humans are going to use this gizmo.  I guy named Otis solved a bunch of these problems back in 1861.

Best regards,

Gary D*ckinson

110
Technical support / Re:Stepper motor homing sequence not behaving
« on: April 23, 2018, 04:11:48 PM »
Your program appears to be a single ladder logic rung that will invoke the custom function, "Homing", on the rising edge of the RELAY, Start.  Nothing wrong with this...

This is your entire Start CF:

STEPSPEED 1, 100, 200

While Input[1] = 0
STEPMOVE 1, 400, 99
endwhile

STEPSTOP 1
STEPHOME 1

Your problem is with the use of the arithmetic statement, "Input[1] = 0", to determine when to stop looping.  Input[1] is a 16-bit integer where each bit represents one of 16 physical inputs 1..16.  If any one of these inputs is non-zero, then "Input[1]" will be non-zero and this will end the loop.

I suggest that you use the TestIO() function to return the value of a single physical INPUT:

STEPSPEED 1, 100, 200

While TestIO(Limit_Switch) = 0
    STEPMOVE 1, 400, 99
endwhile

STEPSTOP 1
STEPHOME 1

There are several other problems with your code.  I will list the 3 most grievous:
  • Your are polling for a physical INPUT in a CF.  This will lock up the PLC until the INPUT goes active.  I believe that it should not be possible to poll for a changing INPUT from within a CF. But I have been wrong, before.
  • Your are issuing the next STEPMOVE statement before the previous STEPMOVE comand completes the motion command.  This can't possibly work.  The 3rd parameter in the  "STEPMOVE 1, 400, 99" statement "99" is the RELAY that is set when the motion is completed.  I use this 3rd argument to control the sequence of events. I will issue the STEPMOVE in one CF and the SETSTOP stuff would be in a 2nd CF that is invoked when the RELAY goes active. This removes all looping in CFs.
  • If you need to "home" the system, then you don't know where you are.  The 3rd argument in the "STEPSPEED 1, 100, 200", statement, has to do with acceleration.  If you don't know where you are, why would you want to accelerate? You should step no faster than the system can stop without losing steps.  I have been able to destroy low cost, hobbyist, stepper driver boards when issuing a SetStop command when the stepper running at full tilt.  The inertial load of the system caused the stepper to continue moving and the stepper motor acted as a alternator and produced very large voltage spikes that destroyed the protection diodes in the driver chip.

Best regards,

Gary D*ckinson

111
Congo,

Let me explain what I was doing with this snippet of code:

' Reload HSCPV[1] with residual count value
'   the while loop is used to verify that HSCPV[1] was successfully reloaded
'
while (HSCPV[1] >= 100)
   HSCPV[1] = HSCPV[1] - 100      ' Reload counter with residual count value
endwhile

The HSC was programmed to call the CF in response to HSCPV[1] being equal to 100.  I wanted the value of HSCPV[1] to increment from 0 to 100 and when it hit 100 the CF that was called was to reload HSCPV[1] back to a value of 0.  The problem is that the CF is NOT called on the instant that the HSCPV[1] becomes zero, but is called at some time later and synchronized to the ladder logic.  

In my application the value of HSCPV[1] may have been incremented beyond the target value of 100 by the time the CF is called.  If the HSCPV[1] has been incremented to 105, this code would reload HSCPV[1] with the value of 5 so that 95 counts later HSCPV[1] will reach 100.  


   HSCPV[1] = HSCPV[1] - 100      ' Reload counter with residual count value


If your applications increments the HSC at a rate of 50 Hz, then the value of the HSC counter will change every 20 mS.  The HSCPV[1] will only be at 100 for 20 mS.  In order to poll the HSCPV using CF, you will have to poll at 2x rate that the HSCPV can increment. The minimum poll rate would be 100 Hz or once every 10 mS (Nyquist rate...).

If your application absolutely must perform your math equation only when the HSCPV[1] is exactly 100, 200, 300 ... this may be impossible to achieve with the PLC.  Remember that the HSCPV[1] is only at a given value for 20 mS!  And your PLC code must complete each scan in only 10 mS to ensure that you do not miss the critical HSCPV[1] value.

If your application would be OK with HSCPV[1] values of 100, 202, 300, 401, 505 ..., then I see no problem with using the PLCs to get this job completed.

I suggest that you do not use the HSCDEF interrupt mechanism, but simply call a custom function on every scan of the ladder logic. This code just compares the current HSCPV[1] against a 32-bit limit value stored in DM32[].  Each time you do the math just add 100 to the limit...

This is the simplest code that I know of that might solve your problem:

' HSCPV[1] Polled on every scan
'
' LimitValue is a 32 bit variable in DM32[]  Use a #Define to set this up
'
if HSCPV[1] < LimitValue
   return
endif

' Setup next LimitValue
'
LimitValue = LimitValue + 100

' Your code specific to your task follows:
'


This code simply allows HSCPV[1] to increment continuously.  There is no need to reload HSCPV[1] when the limit or target value is reached as this code does not use the HSCDEF to set up an interrupt function.  

The HSC counters are implemented in hardware and are 32-bit counters.  TBASIC works fine with 32-bit values and will treat the HSCPV as 32-bit signed variable (positive and negative values). When the 32-bit counter gets to the biggest positive number (134217727 decimal or &h7FFFFFF) the next count will be the most negative number ( -2147483648 or &H8000000).  The code that I presented should work fine.  However, I have been proven wrong on many occasions.

Best regards,

Gary D*ckinson



112
Congo,

There are a couple of approaches to solve your problem:

Use the HSCDEF statement that initializes the HSCDEF to call a custom function when the HSC (high-speed counter) reaches a specified count.  The CF that is associated with the high-speed counter is called after the HSC reaches the count.  You will need to reload the HSC counter with the next count value.  The interrupt CF will look something like this:

' HSCPC[1] Interrupt function
'

' Reload HSCPV[1] with residual count value
'   the while loop is used to verify that HSCPV[1] was successfully reloaded
'
while (HSCPV[1] >= 100)
   HSCPV[1] = HSCPV[1] - 100      ' Reload counter with residual count value
endwhile

' Your code specific to your task follows:
'


The second approach is to not use the HSCDEF statement and check the HSCPV[1] on each scan of the ladder logic rather than every 0.01 seconds.  The code for this looks a bit different:

' HSCPC[1] Polled on every scan
'
if HSCPV[1] < 100
   return     ' nothing to do...
endif

' Reload HSCPV[1] with residual count value
'   the while loop is used to verify that HSCPV[1] was successfully reloaded
'
while (HSCPV[1] >= 100)
   HSCPV[1] = HSCPV[1] - 100      ' Reload counter with residual count value
endwhile

' Your code specific to your task follows:
'


The reasons that I use the while ... endwhile loop as part of the reload the HSCPV[1] is that there is a potential conflict between your CF write to the HSC and the PLC's hardware/firmware. It is possible for the write to the HSC from the CF to be lost.  The loop ensures that the HSC is correctly updated by the CF by reading the HSC back after the write operation.

The reason that I don't just reload the HSCPV[1] with 0 is that there are delays in both approaches between when the HSCPV gets to your magic number and when the CF can execute.  Do not be surprized if the HSCPV[1] is at 101, 102, ... 199 by the time that the CF executes.  You just need to deal with this possibility.

Gary D*ckinson



113
Technical support / Re:PID negative saturation
« on: March 30, 2018, 09:40:52 PM »
Congo,

Without knowing a lot more about what you are trying to accomplish, it is hard to give you very good advice on the usage of the PID algorithm.  

One of my systems use a variable speed pump to maintain a constant flow rate through a water filtration system. The pump is controlled by a VFD (variable frequency drive) that I can set the drive frequency from 00.00 Hz to 60.00 Hz.  This directly determines the pump RPM.

The other bit of trivia is that the VFD controls how quickly the pump can change speed (accelerate / decelerate).  In this case the VFD is set to take 10 seconds to go from 0 to 60 Hz.

The PLC code that uses the PIDCompute algorithm is called every 1/2 second, so the VFD can change a maximum of 3.0 Hz in 1/2 second.  I set the PID limit to 3.0 Hz as this is as big a change as the pump can make in 1/2 second.

The PIDDef statement is called once on startup:

PID_limit1 = 3.0      ' +/- 3 Hz limit
PID_P1 = 0.1800
PID_I1 = 0.0000       ' No Integral term
PID_D1 = 0.0000       ' No Differential term

PIDDef 1,PID_Limit1,PID_P1,PID_I1,PID_D1      ' Constant flow mode

This is the code that is called every 1/2 second to adjust the pump speed to maintain constant flow:

' Adjust pump speed to achieve target flow
'
D# = ProductGpmGoal - ProductFlowRate
if ABS(D#/ProductGpmGoal) <= 0.02
   ' error is within 2% of goal
   ' consider this close enough
   '  no adjustment required to pump speed
   return
else
   ' Adjust pump speed to get to goal
   '
   E# = PIDCompute(1, D#)            ' correction to pump speed in E#
   VFDTargetHz = VFDTargetHz + E#    ' This is the new speed for the main pump in Hz

   ' Enforce pump speed limits
   '
   if (VFDTargetHz > 60.0)
      VFDTargetHz = 60.0        ' Maximum pump speed before it throws a winding
   elif (VFDTargetHz < 0.0)
      VFDTargetHz = 0.0         ' minimum pump speed. Cannot go any slower
   endif

   SetIO ReqSetVFDSpeed    ' This RELAY invokes another custom function to set the VFD speed
endif

Please note the following:
  • The variable, ProductGpmGoal, is the target flow rate
  • The variable, ProductFlowRate, is the current flow rate
  • If the ProductGpmGoal and ProuctFlowRate is within 2% of each other, no adjustments are made.  This is close enough for my client's application.
  • The variable, E#, is generated by the PID algorithm and I add this computed error term to the current VFD speed to determine the next VFD speed.  This eliminates the need for the "I" or integral term for the PID algorithm.
  • I bound the next VFD speed variable, VFDTargetHz, to stay between 0 and 60
  • The RELAY, ReqSetVFDSpeed, invokes a Custom Function to send then value VFDTargetHz to the VFD to adjust the pump speed   
This may give you a hint of what is involved in actually using the PID algorithm.

Gary D*ckinson

114
Technical support / Re:DAC stability
« on: March 26, 2018, 09:26:36 PM »
I suspect that the problem that you are seeing is the result of noise being coupled into the DAC signal.  

The amount of noise, 20 mv is pretty small, so you have a very good shot at taming the issue.

In my experience, the #1 problem with analog signals and PLCs has to do with grounding.  If you are using the PLC digital outputs to switch high current loads, you must be very careful to ensure that you use the PLC's Analog Gnd output as the ground signal for the external device.

You should use a single point ground for your PLC/system wiring.
 
If you are not using the PLC's Analog Gnd, then rewire your cabling to use this as the gnd reference for the external device.

If the valve is dithering or hunting, this will result in big currents in the wiring that supplies the power for the valve.  If you 0..10V signals are bundled in with the valve power wiring this is the most likely source of the noise. If you have done this, then I'd suggest that you:
1. Twist the power/ground wires (running to the analog valve) together to minimize their ability to radiate EMF.
2. Shield the 0..10V signals using shielded twisted pair wiring. Connect the shield at one end.  I'd suggest the PLC Analog gnd.
3. Keep the 0..10V signals away from all other wiring.

If you have a good quality DVM that can do true RMS AC measurements and can be set to show only the AC component then I'd start with a couple of simple tests:
  • Disconnect the cable to the analog controlled valve.
  • Connect the DVM between the PLC Analog ground and the DAC output that you are using.
  • Program the DAC output for 0 volts.  Measure the DC voltage and the AC voltage.
  • Program the DAC output for 5 volts.  Measure the DC voltage and the AC voltage.
  • Program the DAC output for 10 volts.  Measure the DC voltage and the AC voltage.
If you are seeing the 20 mv AC noise with no cable connected to the PLC, then the noise problem is with the PLC.  You will need  to add passive analog filtering to reduce the noise.  

If the noise is not present with the cable disconnected, then the problem is either with the cabling, routing of the cabling or your external device.
  • Connect the cable to the analog controlled valve.
  • Verify that the voltage measured between the PLC Analog Gnd and the signal ground input at the analog valve controller is 0.000 VDC.
  • Connect the DVM at the input to the analog valve controller.
  • Program the DAC output for 0 volts.  Measure the DC voltage and the AC voltage.
  • Program the DAC output for 5 volts.  Measure the DC voltage and the AC voltage.
  • Program the DAC output for 10 volts.  Measure the DC voltage and the AC voltage.
If you are getting non-zero DC measurement on the analog gnd wire then you  need to fix this first.

Now that you have fixed the DC ground issues, repeat all of the tests and see if this fixes the voltage at the automated valve.  It it does, then you are golden.

You may have to change the routing of the analog signals to ensure that they are not running close to any wiring carrying high current noise, AC wiring, AC motor wiring, arc welders, VFD drive wiring, big solenoids, ignition systems...

You may have to shield the signal wiring to minimize any noise pickup.

Once you have done all of this stuff and still have a noise issue when connected to the automated valve, then you will need to work on passive noise suppression right at the analog input to the valve.

Best regards,

Gary D*ckinson


115
Technical support / Re:Simulation of Clock
« on: March 06, 2018, 11:21:15 AM »
Lorne,

You are right that Status(18) returns hours, minutes and seconds as a single integer. There are two reasons that Status(18) is very important:
  • It simplifies your code when testing to see if the current time is equal to some target time.  Otherwise you have to make tests that involve TIME[1], TIME[2], and TIME[3].
  • It solves a problem with CF reads of the TIME[] registers in the PLC firmware. The registers update asynchronously to the execution of custom functions.  As an example, if the PLC's RTC is at 23:59:59 then it is possible to observe the time registers going through all sorts of incorrect values before getting to 00:00:00.


It is possible to observe the following transitions: 23:59:59 -> 23:59:00 -> 23:00:00 -> 00:00:00
[/list]

The following test code can be used to avoid the illogical  TIME/DATE register transitions:

' AvoidRipple
'
while 1
   ' read all TIME[] and DATE[] registers, one time
   '
   DM[301] = TIME[1]   ' Hours
   DM[302] = TIME[2]   ' Minutes
   DM[303] = TIME[3]   ' Seconds
   DM[304] = DATE[1]   ' Year
   DM[305] = DATE[2]   ' Month
   DM[306] = DATE[3]   ' Day
 
   ' re-read the TIME[] register and verify that they did not change
   ' from the previous set.  The whole point of this exercise is to
   ' detect and discard erroneous readings that can occur because the PLC
   ' firmware is constantly updating these register.  It is possible to
   ' observe the following transitions:
   '
   '  23:59:59 -> 23:59:00 -> 23:00:00 -> 00:00:00
   '
   if      DM[301] <> TIME[1]
      continue
   elif    DM[302] <> TIME[2]
      continue
   elif    DM[303] <> TIME[3]
      continue
   elif    DM[304] <> DATE[1]
      continue
   elif    DM[305] <> DATE[2]
      continue
   elif    DM[306] <> DATE[3]
      continue
   else
      exit
   endif

endwhile


If you directly access the TIME registers you need to:
  • Only use Status(18)
  • Never read/write them if there is any possibility that the RTC will update the registers.  Say only at time xx:xx:30.
  • Read all registers as a set and then re-read all of the registers until you get the same values twice in a row.
  • Shut off the RTC mechanism during CF access to the registers.

Please note that the same issues happen with the DATE[] registers.

Please be aware that if you set the RTC registers it is possible for the registers to be changed while you are updating.  You should either shut down the RTC during the writing or following the write operations re-read the registers to verify that you have set them without interference from the PLC firmware.

Gary D

116
Technical support / Re:Simulation of Clock
« on: March 06, 2018, 08:43:09 AM »
Lorne,

You are doing something very wrong.  The simulator does a reasonable job of simulating the hardware RTC clock mechanism.  I can set the time.  

I have attached a demo program that runs in the simulator.  Run it and click on "View" and you will set the current time on the 4x20 LCD display.  Click on the RELAY Set and the current values of the variables A,B and C will be load the Hours, Minutes and second registers.

This is the code to display the RTC time:

T = Status(18)
S = T MOD 100
T = T/100
M = T MOD 100
H = T/100

SetLCD 1,1,str$(H,2)+":"+str$(M,2)+":"+str$(s,2)

This is the code to change the RTC registers:

' SetTime - set RTC from the variables A:B:C
'
TIME[1] = A   ' set the hours
TIME[2] = B ' set the minutes
TIME[3] = C ' set the minutes


After you run the code, the question that you need to ask is why did I use Status(18) to read the time registers.  If you ask, I will tell you why.

Best regards,

Gary Dickinson

117
Technical support / Re:SetDimmer
« on: February 19, 2018, 01:15:12 PM »
Neal,

Sounds like your are on a good track.  Don't let the size fool you. Most SSRs are sold without heatsinks.  If this is the case with the OMRON units, you may have to bolt the SSR to a big heat sink to ensure that it does not fry.

If your application has a big thermal mass or your application can tolerate a few degees of temperature change, you might not need much more  than on/off control of your heater.  Read "cheap SSR".

The PWM mechanism supported by PLCs is not useful for SSR as the PWM frequency is two high.  It is possible to use a pair TIMERS to build a low speed variable duty cycle system for SSRs.

Best regards,

Gary D*ckinson




118
Technical support / Re:SetDimmer
« on: February 16, 2018, 08:03:47 PM »
The FMD series PLC do not have the hardware to support the dimmer mechanism.  You will need to look at the Fx1616, Fx2424 or the Tile series PLCs if you want to use the SetDimmer hardware/software.

You can download the user manual for the Fx models from TRI’s website.  Look at chapter 18 for some info on how to wire up stuff and use the SetDimmer commands.

If you are willing to invest in a more expensive SSR you can get the dimmer functionality with the FMD PLC by using a “Proportional Control SSR”.  Crydom makes these special SSR.  You can use one of the FMD analog outputs (0..5V) to get 0 to 100% AC output when using a proportional control SSR to control your heater.
 
Best regards,

Gary D*ckinson

119
Technical support / Re:Servo Motor and SC
« on: January 10, 2018, 04:06:27 PM »
Lorne,

If you are talking about the servos used for radio control models, the answer is yes.  The position of these servos is determined by the width of a pulse that is sent to the servo. Most servos are centered when the pulse width is 1.5 milliseconds.  The typical range of pulse widths for these servos is from 1.0 to 2.0 milliseconds.  Your 180 degree servo may require a bit wider range of pulse widths to turn the full amount.

The Fx series PLCs all support some number of PWM (pulse width modulation) outputs.  It is possible to program these outputs to produce the pulses for RC-type servos.  The PWM mechanism can handle the required range of pulse widths and repetition rates expected by these servos.

You will have to deal with the fact that the PLC's PWM outputs go from 0 to 24V and the servo's inputs are from 0 to about 5 volts.  There are notes in the user manuals in chapter 11 that shows typical circuits to interface the PLCs to RC-type servos to solve the signal level issues.

As an alternative to using the PLC's PWM outputs, you can purchase low cost RC-servo controllers that can be connected to the PLC's RS232. The Pololu "Serial 8-Servo Controller" is a widely available controller.  This controller can handle 8 servos at the same time.  The PLC sends a 5 or 6 byte command via its serial port to position the servos.  

A nice thing about using an external controller is that you can set a parameter that determines how quickly the servo moves.  A typical servo moves about 60 degrees in about 0.2 seconds.  If you would like the have the servo move more slowly, say take 2 seconds to move 60 degrees, it is easy with the external controller.  If you need to do this with the PLC, you will have to change the PWM outputs several times with delays between each change.  This is possible to do with the PLC, but it is a little complex (fun?).

Best regards,

Gary D*ckinson

120
Technical support / Re:week number
« on: January 10, 2018, 09:08:10 AM »
I think that you should stick with the simpler algorithm that treats any year that is divisble by 4 without a remainder as being a leap year.

That algorithm will pick the following years as being leap years. The red years are in error:

2000 2004 2008 2012 2016 2020 2024 2028
2032 2036 2040 2044 2048 2052 2056 2060
2064 2068 2072 2076 2080 2084 2088 2092
2096 2100

Your algorithm makes the same mistake at the year 2100:

2000 2004 2008 2012 2016 2020 2024 2028
2032 2036 2040 2044 2048 2052 2056 2060
2064 2068 2072 2076 2080 2084 2088 2092
2096 2100

This is the test code that I ran in simulation to check your algorithm:

c = 8   ' column count
for i = 2000 to 2100
   If (i MOD 400)&(i MOD 100)&(i MOD 4)|(i MOD 400)&(i MOD 4)
      continue      ' not a leap year
   endif

   ' this is a leap year, so print it out
   '
   c = c - 1
   if (c <> 0)
      print #1 str$(i);" ";
   else
      print #1 str$(i)
      c = 8
   endif
next
[/color]

As this code is run in simulation, the print #1 statements are "output" to a window that I can inspect and copy into documentation.

Gary D*ckinson

Pages: 1 ... 6 7 [8] 9 10 ... 34