Öldman©
2004-07-21 11:26:39 UTC
Sorry I haven't got back to you for a while regarding C program.
I met with my client and discussed the issue.
They are considering using more up to date hardware, as I think
I told you earlier, this was originally put together in 94-95.
***************************************************************
Note to the casual reader of this post - have a look at the
C code in this post, it's what makes our 'toys' tick.
***************************************************************
I am confident after reviewing the C code enclosed that I am
able to change the register assignments for the updated BIOS.
I would like you to have a hard look at this to see if you
are able to improve the RPM data output of the progy.
A bit like Windoze 95, all things were not exactly what my
client wanted at the time but trade offs were made between
speed (sample rate), size and cost.
I have been researching different PLC's that are on the
market and the one that looks interesting runs Windoze CE
services or C++ or ladder logic. More on that another time.
Are you familiar with the term "moving window averaging"?
Our RPM counters need to run two different sample rates,
the first at 100 RPM and up - the second for 100 RPM and
down. The sample rate needs to be higher for the lower
RPM values to sense 'stall' on the motor under test.
The data is used to plot torque curves and stall happens
within 2 seconds from approx. 1200 RPM when load reaches
critical. We found a LOT of 'jitter' in our values due to
low sample rate and clock speed of the PLC presently used.
The 'work around' was to accumulate counts then average.
This issue was never fully resolved - I think partly due
to the programers level of expertize and partly due to
the clock speed of the PLC.
I proposed that the program be set up to accumulate counts
for X clock cycles as a 'snap shot' then average as many of
these 'snap shots' as practical due to jiffy timer to
obtain readings quickly enough. This worked well for the
higher RPM values but not for the low end (not enough plot
points to draw the curve properly).
Possible that you know a better way?
Is there a way to 'split' clock cycles (jiffies) with
software? Can we 'roll our own' timer somehow?
This issue will determine whether we keep using the
present unit or 'upgrade' the hardware!
The library:
1 vol - ANSI C Constructs and Libraries
1 vol - MCCM77 Documentation set.
1 vol - Telepace C Tools
1 vol - Telesafe Micro 16 SCADA Controller
1 vol - Smartwire System Manual
I have permission to copy required info from these manuals but I
think maybe scanning the important pages and either email or
post the scans (although you may not need them). Email has
limits for binary attachments (you will end up with a 'plugged'
in box and all other mail 'bounces'.
Had this problem when I tried to send my client all of the
AutoCad drawings for a project once.
Spec:
Processor - M37702 16 bit 14.7456 MHz clock with integrated watchdog timer.
Memory - 128 kBytes (expanded to 1 MBytes) CMOS RAM
- 64 kBytes to 2 MBytes EPROM
- 1 kBytes EEPROM (holds port configuration data)
Baud - 300 to 38400 (9600 used)
Word - 7 or 8 bits
Counter Hz- 5 kHz max. (prox. sensor used for input-1800 RPM max with 15 pulses
per revolution (used for higher resolution of motor speed which seldom hits 500
RPM)) Counters are 32 bits wide. *Configured to clear on being read by program?
Assembly language code may be included directly within C programs.
Library routines are called from operating system ROM.
The controller provides 32 software timers individually programmable for
tick rates from 10 per second (1 jiffy) to once every 25.5 seconds. All
timers operate in the background from a hardware interrupt generated from the
main system clock.
Watchdog timer trigger pulses may be generated by the user program or
by the system clock. If watchdog times out CPU is reset. Used for error
detection in system. *Check code to see if it is incorporated!
The C Tools library is an enhancement of the ANSI C in that it has
functions dedicated to this controller for IO register assignment.
eg. read_counter
Timer functions:
interval - set the tick interval in tenths of seconds
settimer - set a timer. Timers count down from set value to zero.
time - read the time period remaining in a timer.
pulse, pulse_train and timeout not used for application.
*Jiffy Clock:
setjiffy - set the jiffy clock.
jiffy - read the jiffy clock.
*resets when power applied to the controller and rolls over after 24 hours to
zero. It is real time clock.
The original programmer only used one timer. Would it not be better
if multiple timers were used?
stall = readcounter
if counter < # settimer_1 ###
if counter > # settimer_2 ###? (pardon my syntax, C programmer I'm not.
8888888888888888888888888888888888 start 8888888888888888888888888888888888888
/*****************************************************************************/
/* C language source code file for Control Microsystems MICRO16 PLC */
/* */
/* Written by Programmer */
/* Written for Customer */
/* */
/* This program below monitors a drill motor test stand, and must be compiled*/
/* along with the file FILENAME.H with the Microtech Research MCCM77 compiler*/
/*****************************************************************************/
/*****************************************************************************/
/* Conditional compilation directives */
/*****************************************************************************/
/* define DEBUG */
/*****************************************************************************/
/* global variables */
/*****************************************************************************/
struct clock now; /* real time clock structure */
struct sensors readings; /* structure containing all the sensor values */
struct scale_vals scale; /* scale values for unit conversions */
/*****************************************************************************/
/* Include files */
/*****************************************************************************/
#include "filename.h" /* #define statements for this file */
#include <stdio.h> /* Microtec compiler standard I/O header */
#include <database.h> /* TelePACE I/O database functions */
#include <iohw.h> /* TelePACE I/O hardware functions */
#include <primitiv.h> /* Real Time Operating System functions */
#include <protocol.h> /* More I/O database related functions */
#include <rtc.h> /* Real time clock functions */
#include <serial.h> /* TelePACE RS232 I/O header */
/*****************************************************************************/
/* Function Prototypes */
/*****************************************************************************/
void main (void);
void read_inputs(unsigned motorsize,unsigned timeout);
void write_dbase(int dataptr,int lasttime);
void init_serial(void);
void print_data(void);
int stalled(void);
void line_printer(void);
void write_panel_meters(void);
unsigned which_motor(void);
void flash_led(void);
void init_scale(void);
/*****************************************************************************/
/* begin function MAIN */
/*****************************************************************************/
void main (void) {
unsigned int motorsize; /* determines which motor (if any)is running */
unsigned dataptr; /* offset from start of A/O to BEGINDATA */
/* is 42000-40000= 2000 */
unsigned lasttime; /* for keeping track of loop timing */
unsigned timeout; /* equals 60/60 seconds or 1 second timing */
unsigned numloops; /* number of iterations of main loop */
/*****************************************************************************/
/* Initialization */
/*****************************************************************************/
init_scale(); /* Initialize scaling factors */
init_serial(); /* Com 1 = PRN, Com 2 = MODBUS */
fprintf(com1, "Customer Oil Drill Motor Test Stand Data\r\n");
fprintf(com1, "----------------------------------------\r\n");
/*****************************************************************************/
/* Beginning of main run time loop, 1 second or 0.1 during stall */
/*****************************************************************************/
while (TRUE) { /* run-time loop never stops */
while (!(motorsize=which_motor())) { /* Wait here for motor to start */
read_inputs(motorsize,timeout); /* read and scale input data */
write_panel_meters();
flash_led(); /* Run LED flashs if both motors stopped */
}
request_resource(IO_SYSTEM);
setdbase(MODBUS,INPROGRESS,1); /* Flag for LabVIEW indicates test on */
release_resource(IO_SYSTEM);
setjiffy(0UL); /* start 1/60 resolution clock for timing, */
lasttime=0; /* as one of the motors is now moving. */
dataptr=BEGINDATA-BEGINAOUT; /* offset from start of A/O to BEGINDATA */
/* is 42000-40000= 2000 */
timeout=60;
numloops=0;
do {
read_inputs(motorsize,timeout); /* read and scale input data */
write_dbase(dataptr,lasttime); /* write to I/O modbus database */
dataptr+=5;
if ((BEGINAOUT+dataptr)>ENDDATA) dataptr=BEGINDATA-BEGINAOUT;
/* wrap dataptr if overflow */
if (stalled()){ /* if stalled, loop time is 1/10 seconds */
timeout=12;
if (!(numloops%5)) {
line_printer(); /* only print and write meters on 5th passes */
write_panel_meters();
}
}
else {
timeout=60; /* if not stalled, loop time is 1 second */
line_printer(); /* so print every second (if required */
write_panel_meters();
}
lasttime=lasttime+timeout; /* time to wait to is advanced */
numloops++;
while (((jiffy()-lasttime)/timeout)>0)
; /* wait till timeout for cycle, 0.2 or 1.0 seconds */
}
while(readings.rpm > STOPPED); /* must be at least moving to read data */
request_resource(IO_SYSTEM);
setdbase(MODBUS,INPROGRESS,0); /* Flag for LabVIEW indicates test off */
release_resource(IO_SYSTEM);
} /* Drop out at end of test (motor stopped) , main loop never stops */
} /* end main (never exits) */
/*****************************************************************************/
/* init_scale <Customer.C> */
/* */
/* SYNTAX: init_scale (void); */
/* */
/* DESCRIPTION: Loads default values to I/O database only if examining */
/* the optionswitch 1 is true. This prevents LabVIEW from updating constant
*/
/* values and then having them reset to defaults. */
/* */
/* RETURN VALUE: None. */
/* */
/* */
/*****************************************************************************/
void init_scale(void) {
if (optionSwitch(OPEN)) { /* if switch is open copy default values */
scale.flow=FLOWSCALE; /* FLOW scale from counts - numeric val */
scale.rpmlarge=RPMLARGESCALE/1000.0;
scale.rpmsmall=RPMSMALLSCALE/1000.0; /* " "
*/
scale.pressure=PRESSURESCALE/1000.0;
scale.torquelarge=TORQUELARGESCALE/1000.0;
scale.torquesmall=TORQUESMALLSCALE/1000.0;
scale.stallrpm=STALLRPM;
}
else { /* if switch 1 is off, copy values from I/O database */
request_resource(IO_SYSTEM);
scale.flow=dbase(MODBUS,FLOWSCALEADDRESS);
scale.rpmlarge=dbase(MODBUS,RPMLARGESCALEADDRESS)/1000.0;
scale.rpmsmall=dbase(MODBUS,RPMSMALLSCALEADDRESS)/1000.0;
scale.pressure=dbase(MODBUS,PRESSURESCALEADDRESS)/1000.0;
scale.torquelarge=dbase(MODBUS,TORQUELARGESCALEADDRESS)/1000.0;
scale.torquesmall=dbase(MODBUS,TORQUESMALLSCALEADDRESS)/1000.0;
scale.stallrpm=dbase(MODBUS,STALLRPMADDRESS);
release_resource(IO_SYSTEM);
}
}
/*****************************************************************************/
/* read_inputs <Customer.C> */
/* */
/* SYNTAX: read_inputs(unsigned in motorsize); */
/* */
/* DESCRIPTION: This function reads and scales inputs to standard units */
/* */
/* */
/* RETURN VALUE: None. */
/* */
/* */
/*****************************************************************************/
void read_inputs(unsigned motorsize,unsigned etime){
request_resource(IO_SYSTEM);
readings.flow=readCounter(INFLOW,READCOUNTER) * scale.flow * 60 / etime;
readCounter(INFLOW,CLEARCOUNTER);
readings.pressure=ain(INPRESSURE) * scale.pressure;
if (motorsize==LARGEON){ /* read torque/rpm depending on motor */
readings.torque=ain(INTORQUELARGE) * scale.torquelarge;
readings.rpm=readCounter(INRPMLARGE,READCOUNTER) * scale.rpmlarge * 60 /
etime;
readCounter(INRPMLARGE,CLEARCOUNTER);
}
else {
readings.torque=ain(INTORQUESMALL) * scale.torquesmall;
readings.rpm=readCounter(INRPMSMALL,READCOUNTER) * scale.rpmsmall * 60 /
etime;
readCounter(INRPMSMALL,CLEARCOUNTER);
}
release_resource(IO_SYSTEM);
} /* end read_inputs */
/*****************************************************************************/
/* which_motor <Customer.C> */
/* */
/* SYNTAX: unsigned which_motor(void); */
/* */
/* DESCRIPTION: This function is used to detect which motor is running by */
/* reading the RPM counters. This function executes in one second. This */
/* time is required to allow counters to accumulate */
/* */
/* RETURN VALUE: OFF = No motors running */
/* LARGEON = Large motor running */
/* SMALLON = Small motor running */
/* */
/*****************************************************************************/
unsigned which_motor(void) {
interval(0,1); /* set timer 0 for 10 hz tick rate */
request_resource(IO_SYSTEM);
readCounter (INFLOW,CLEARCOUNTER); /* Clear counters to zero */
readCounter (INRPMLARGE,CLEARCOUNTER);
readCounter (INRPMSMALL,CLEARCOUNTER); /* Clear counters to zero */
release_resource(IO_SYSTEM);
settimer(0,10); /* set timer for 1 second */
while (timer(0))
; /* wait for 1 second timer to expire */
if (readCounter (INRPMLARGE,READCOUNTER) > 5) { /* large-16RPM,small-40RPM*/
/* request_resource(IO_SYSTEM);
dout(DOUT_START,LARGELAMP); -- 5409 digital output (not specified)--
release_resource(IO_SYSTEM); leave in in case later requested */
return (LARGEON) ;
}
if (readCounter (INRPMSMALL,READCOUNTER) > 5) {
/* request_resource(IO_SYSTEM);
dout(DOUT_START,SMALLLAMP);
release_resource(IO_SYSTEM); */
return (SMALLON) ;
}
else {
/* request_resource(IO_SYSTEM);
dout(DOUT_START,NOLAMP);
release_resource(IO_SYSTEM); */
return (OFF);
}
} /* end which_motor */
/*****************************************************************************/
/* write_dbase <Customer.C> */
/* */
/* SYNTAX: void write_dbase(dataptr); */
/* */
/* DESCRIPTION: write out data to the I/o MODBUS accessible database. */
/* Data is inserted at address offset dataptr, and the offset */
/* address is written to address LASTVALUE in IO database so */
/* that LabVIEW program can query PLC on where data is. */
/* */
/* RETURN VALUE: None. */
/* */
/*****************************************************************************/
void write_dbase(int dataptr,int lasttime) {
request_resource(IO_SYSTEM);
setdbase(MODBUS,BEGINAOUT+dataptr,readings.rpm); /* write sensor vals */
setdbase(MODBUS,BEGINAOUT+dataptr+1,readings.torque);
setdbase(MODBUS,BEGINAOUT+dataptr+2,readings.flow);
setdbase(MODBUS,BEGINAOUT+dataptr+3,readings.pressure);
setdbase(MODBUS,BEGINAOUT+dataptr+4,lasttime/6); /* write time in .1 sec */
setdbase(MODBUS,LASTVALUE,dataptr); /* update lastvalue register */
release_resource(IO_SYSTEM);
}
/*****************************************************************************/
/* flash_led <Customer.C> */
/* */
/* SYNTAX: void flash_led(void); */
/* */
/* DESCRIPTION: flashes an LED to indicate the PLC is waiting for a motor */
/* to start up */
/* */
/* RETURN VALUE: None. */
/* */
/*****************************************************************************/
void flash_led(void) {
request_resource(IO_SYSTEM);
now=getclock();
release_resource(IO_SYSTEM);
if (now.second%2) runLed(LED_ON);
else runLed(LED_OFF);
}
/*****************************************************************************/
/* stalled <Customer.C> */
/* */
/* SYNTAX: int stalled(void); */
/* */
/* DESCRIPTION: This function checks to see if motor is in stall condition */
/* */
/* */
/* RETURN VALUE: TRUE if stalled, else FALSE */
/* */
/* */
/*****************************************************************************/
int stalled (void){
if (readings.rpm < scale.stallrpm)
return (TRUE);
return(FALSE);
}
/*****************************************************************************/
/* write_panel_meters <Customer.C> */
/* */
/* SYNTAX: write_panel_meters(void); */
/* */
/* DESCRIPTION: Write out to the four analog output meters via the model 5301*/
/* micro16 modules. The module is a 0-20mA output, requireing a 500 ohm */
/* load resistor to deliver 0-10 volt output. 10 volts reads 19999 on meter */
/* */
/* RETURN VALUE: none */
/* */
/* */
/*****************************************************************************/
void write_panel_meters(void) {
aout(OUTTORQUE,readings.torque);
aout(OUTPRESSURE,readings.pressure * 10.0);
aout(OUTFLOW,readings.flow *37.0 );
aout(OUTRPM,readings.rpm * scale.meter * 17.0);
}
/*****************************************************************************/
/* line_printer <Customer.C> */
/* */
/* SYNTAX: void line_printer(void); */
/* */
/* DESCRIPTION: Data is written to line printer on interrupt switch keypress */
/* or if the print switch has been depressed for three seconds (count 120 on */
/* the jiffy() clock plus 1 loop. Switch logic is performed in this function,*/
/* print_data is a separate function that does the printing, if necessary */
/* */
/* RETURN VALUE: none. */
/* */
/* */
/*****************************************************************************/
void line_printer(void){
static unsigned lockstate; /* is printing locked on via interrupt */
static unsigned long last_switchoff; /* last time in no_switch, no PRINTLOCK */
unsigned switchon; /* results of reading interrupt switch */
switchon=interruptInput(); /* read switch input */
if (!switchon) { /* if switch is OFF */
last_switchoff=jiffy(); /* mark the last time its off */
if (lockstate) print_data(); /* print if we are in lock state */
return;
}
else { /* if switch is ON */
print_data(); /* print the data */
if (!lockstate) { /* if we are not locked and */
if ((jiffy()-last_switchoff)>180) { /* if not recently off, lock on */
lockstate=TRUE;
return;
}
}
else { /* switch is ON, lockstate ON */
if ((jiffy()-last_switchoff)<180) /* if not recently off, lock off */
lockstate=FALSE;
}
}
} /* end line_printer */
/*****************************************************************************/
/* print_data <Customer.C> */
/* */
/* SYNTAX: void print_data(void); */
/* */
/* DESCRIPTION: This function writes an ascii string out to a line printer */
/* containing all the data scaled from sensors. Data format is: */
/* */
/* YEAR:MONTH:DAY:HOUR:MINUTE:SECOND :RPM:TORQUE:FLOW:PRESSURE <CR><LF> */
/* */
/* Note that real time clock must be reset if necessary by labVIEW (or */
/* remain in standard/daylight saving time) Clock handles leap-years. */
/* */
/* RETURN VALUE: none. */
/* */
/* */
/*****************************************************************************/
void print_data(void) {
now=getclock(); /* update structure "now" with real time clock values */
fprintf(com1, "%2d/%2d - ",now.month,now.day); /* print date */
fprintf(com1, "%2d:%02d:%02d ",now.hour,now.minute,now.second);/* and time
*/
fprintf(com1, "RPM:%4d TORQUE:%4d FLOW:%4d PRESSURE:%4d\r\n",readings.rpm,
readings.torque,readings.flow,readings.pressure);
}
/*****************************************************************************/
/* init_serial <Customer.C> */
/* */
/* SYNTAX: void init_serial(void); */
/* */
/* DESCRIPTION: Sets up conditions for writing straight ASCII to com1 and */
/* MODBUS to com2 */
/* */
/* RETURN VALUE: none. */
/* */
/* */
/*****************************************************************************/
void init_serial(void) {
struct prot_settings settings;
struct pconfig lineconfig;
lineconfig.baud=BAUD9600;
lineconfig.duplex=FULL;
lineconfig.parity=NONE;
lineconfig.data_bits=DATA8;
lineconfig.stop_bits=STOP1;
lineconfig.flow_rx=DISABLE;
lineconfig.flow_tx=DISABLE;
lineconfig.type=RS232;
lineconfig.timeout=600;
set_port(com1,&lineconfig); /* set up printer port line config */
set_port(com2,&lineconfig); /* set up labVIEW port */
/* Disable the protocol on serial port 1 -line printer output */
settings.type = NO_PROTOCOL;
set_protocol(com1, &settings);
/* Enable MODBUS protocol on serial port 2 -LabVIEW /download interface */
settings.type = MODBUS_RTU;
settings.station = 1; /* station address - BusVIEW calls slave address */
settings.priority = 3;
settings.SFMessaging = FALSE;
set_protocol(com2, &settings);
}
8888888888888888888888888888888888888 eof 888888888888888888888888888888888888
/*****************************************************************************/
/* Customer oil PLC program header file */
/* Contains constants and prototypes for functions in Customer.c */
/*****************************************************************************/
/*****************************************************************************/
/* Input signals from ANALOG INPUTS and COUNTERS */
/* */
/* Small motor RPM, counter 0, has I/O database address 10193 */
/* Large motor RPM, counter 1, has I/O database address 10194 */
/* Flow, counter 2, has I/O database address 10195 */
/* Small motor Torque, analog input 0, has I/O database address 30001 */
/* Large motor Torque, analog input 1, has I/O database address 30002 */
/* Pressure, analog input 2, has I/O database address 30003 */
/* */
/* Note that MODBUS accessible I/O database addresses above are raw values */
/* LabVIEW program should read the scaled output values in the I/O database */
/* */
/*****************************************************************************/
#define INRPMSMALL 0 /* COUNTER # 0 ON CPU BOARD */
#define INRPMLARGE 1 /* COUNTER # 1 ON CPU BOARD */
#define INFLOW 2 /* COUNTER # 2 ON CPU BOARD */
#define INTORQUESMALL 0 /* ANALOG INPUT 0 ON A/D BOARD */
#define INTORQUELARGE 1 /* ANALOG INPUT 1 ON A/D BOARD */
#define INPRESSURE 2 /* ANALOG INPUT 2 ON A/D BOARD */
/*****************************************************************************/
/* Output signals for PANEL METERS */
/* */
/* Scaled output torque, analog out 0, has I/O database address 40001 */
/* Scaled output pressure, analog out 1, has I/O database address 40002 */
/* Scaled output flow, analog out 2, has I/O database address 40003 */
/* Scaled output rpm, analog out 3, has I/O database address 40004 */
/* */
/* Note that MODBUS accessible I/O database addresses above are scaled for */
/* writing to 0-10 volt panel meters only. */
/* LabVIEW program should read the scaled output values in the I/O database */
/* */
/*****************************************************************************/
#define OUTTORQUE AOUT_START /* OUTPUT 0 OF ANALOG OUT BASE ADDRESS 0
*/
#define OUTPRESSURE AOUT_START+1 /* OUTPUT 1 OF ANALOG OUT BASE ADDRESS 0 */
#define OUTFLOW AOUT_START+2 /* OUTPUT 0 OF ANALOG OUT BASE ADDRESS 2 */
#define OUTRPM AOUT_START+3 /* OUTPUT 1 OF ANALOG OUT BASE ADDRESS 2
*/
/*****************************************************************************/
/* Digital outs are not being used; leave in in case added later */
/*****************************************************************************/
#define LARGELAMP 2 /* BIT 1 OF DIGITAL OUT BOARD BASE ADDRESS 0 */
#define SMALLLAMP 1 /* BIT 0 OF DIGITAL OUT BOARD " " " */
#define NOLAMP 0
/*****************************************************************************/
/* Output data for MODBUS (LabVIEW program addressable) */
/* These are scaled 16 bit unsigned values */
/* */
/*****************************************************************************/
#define INPROGRESS 41998 /* Address of "test running flag for LabVIEW */
#define LASTVALUE 41999 /* This location contains a pointer offset from*/
/* BEGINDATA to where the last data value has */
/* been written */
#define BEGINDATA 42000 /* BEGINNING OF LabVIEW data transfer area */
/* by moving a pointer, we can keep history. */
/* but this is not implemented yet. */
#define BEGINAOUT 40000 /* Analog output area starts at 40000, but the */
/* Modbus addresses this as relative address 0 */
/* NOTE: not documented in Micro16 manual. */
#define ENDDATA 49995 /* end of data transfer area */
#define BEGINSCALE 41712 /* BEGINNING OF scaling constant data xfer area*/
#define ENDSCALE BEGINSCALE+7
/*****************************************************************************/
/* Scaling constants - This may end up changing during install ... */
/* */
/* RPM: true rpm = counter output / PULSESPERREV (15) */
/* No calibration should ever be needed on RPM measurement */
/* Large motor prox. sensor is geared down by 750/450 = 1.67 RPM/count */
/* Small motor prox. sensor is geared down by 1800/450= 4 RPM/count */
/* Elapsed time will be .2 or 1.0 depending on if in stall */
/* These scaling factors have been fully tested */
/* Note: RPM wont go over 19999 so there is no risk of overflow */
/* */
/* FLOW: 12Hz. pulses = 446GPM = 1688LPM */
/* litres per second per pulse = 1688/12=141 litres/sec/pulse */
/* Calibration constant will be needed for liner size */
/* This liner_size constant can be updated by LabVIEW program through dbase */
/* Elapsed time will be .2 or 1.0 depending on if in stall */
/* These scaling factors have been fully tested */
/* Note: flow wont go over 19999 lps so there is no risk of overflow */
/* */
/* */
/* TORQUE: 0-20 mA signal -> 0 to 32768 binary */
/* Large motor full scale =37280 lbft * 1.356 = 50551 NM = 32768 binary */
/* Small motor full scale =4167 lbft * 1.356 = 5650 NM = 32768 " */
/* full a/d 12 bit binary output is 32768 */
/* Large motor scaling factor is 50551/32768=1.543 NM/bit */
/* Small " " " " 5650 /32768=0.1724 */
/* */
/* NOTE: THIS IS TESTED, BUT OVERFLOW WILL RESULT ON LARGE MOTOR TORQUE */
/* IF TORQUES OVER 19999 NM - THE SCALING MAY HAVE TO BE CHANGED BY 10 X IF */
/* LARGER TORQUES ARE GENERATED, AND A DIGIT DROPPED ON THE DISPLAY. */
/* */
/* PRESSURE: 4-20 mA signal -> 0 to 32768 binary */
/* full scale = 3000 PSI = 3000 * 6.8948 = 20684KPa */
/* full a/d 12 bit binary output is 32768 */
/* scaling factor = 20684/32768=.6312 KPa/bit */
/* */
/* NOTE: THIS IS TESTED, BUT OVERFLOW WILL RESULT IF PRESSURE EXCEEDS */
/* 19999 KPa - THE SCALING MAY HAVE TO BE CHANGED BY 10 x IF LARGER */
/* PRESSURES ARE GENERATED, AND A DIGIT DROPPED ON THE DISPLAY */
/* */
/* METER SCALING: */
/* Analog out is 0-20mA range. Use 500 ohm terminator to get 0-10 volts for */
/* the meter. (there is a 4-20mA switch - ensure it is set for 0-20mA) */
/* Scaling takes place in init_scale in customer.c */
/* */
/*****************************************************************************/
#define FLOWSCALE 141
#define RPMLARGESCALE 1667
#define RPMSMALLSCALE 4000
#define PRESSURESCALE 631
#define TORQUELARGESCALE 1543
#define TORQUESMALLSCALE 172
#define STALLRPM 100 /* stall speed */
/*****************************************************************************/
/* I/O database addresses of scaling constants */
/*****************************************************************************/
#define FLOWSCALEADDRESS BEGINSCALE + 0 /* I/O addresses of above */
#define RPMLARGESCALEADDRESS BEGINSCALE + 1
#define RPMSMALLSCALEADDRESS BEGINSCALE + 2
#define PRESSURESCALEADDRESS BEGINSCALE + 3
#define TORQUELARGESCALEADDRESS BEGINSCALE + 4
#define TORQUESMALLSCALEADDRESS BEGINSCALE + 5
#define STALLRPMADDRESS BEGINSCALE + 6
#define SCALETOMETERADDRESS BEGINSCALE + 7
#define WORKCONVERT 0.737 /* N-M to lb-ft CONVERSION FACTOR */
#define PRESSURECONVERT 0.145 /* KPA to PSI CONVERSION */
#define FLOWCONVERT 0.264 /* LPM to GPM CONVERSION */
/*****************************************************************************/
/* Other constants */
/*****************************************************************************/
#define TRUE 1
#define FALSE 0
#define LARGEON 2 /* motor selector */
#define SMALLON 1
#define OFF 0
#define CLEARCOUNTER 1 /* CLEAR COUNTER TO ZERO */
#define READCOUNTER 0 /* GET COUNTER READING */
#define STOPPED 0
#define PRINTLOCK 1 /* FLAG THAT PRINTER IS TO RUN CONTINUOUS */
#define PRINTUNLOCK 0 /* FLAG THAT PRINTER IS NOT TO RUN CONTINUOUS */
/*****************************************************************************/
/* templates */
/*****************************************************************************/
struct sensors {
unsigned rpm; /* RPM of the running motor */
unsigned torque; /* TORQUE of the running motor */
unsigned flow; /* FLOW (common to both motors) */
unsigned pressure; /* PRESSURE (common to both motors) */
};
struct scale_vals {
float flow; /* FLOW scaling from counts to numeric value */
float rpmlarge; /* we need variables, since the PLC has to read */
float rpmsmall; /* values from either default or LabVIEW program. */
float pressure; /* These scale values corrispond to the default */
float torquelarge; /* values listed above under scaling constants */
float torquesmall;
unsigned stallrpm;
float meter;
};
8888888888888888888888888888 eof 888888888888888888888888888888888888888888
Error noted in Torque section of customer.h - should be 4-20 ma not 0-20.
rpm determination:
15 counts per revolution @ 1800 rpm/60 seconds per minute = 450 Hz full scale
(32768 binary)
Full scale = 10 vdc @ analog output module therefor 1 vdc = 45 Hz (3276.8
binary)
In addition we use a file called 'scaling.txt' which is loaded to the PLC
via Labview application to update scaling constants for each set up. These
over ride the defaults which are there so program doesn't crash initially.
Further - I think the stall RPM=100 is to high for the small motor.
I think we need:
#define small_stall=<50
#define large_stall=<100
???
So there it is. Give me a cost estimate to modify please.
_ _
Sometimes, something or someone - or maybe another person!
_________________________________
I met with my client and discussed the issue.
They are considering using more up to date hardware, as I think
I told you earlier, this was originally put together in 94-95.
***************************************************************
Note to the casual reader of this post - have a look at the
C code in this post, it's what makes our 'toys' tick.
***************************************************************
I am confident after reviewing the C code enclosed that I am
able to change the register assignments for the updated BIOS.
I would like you to have a hard look at this to see if you
are able to improve the RPM data output of the progy.
A bit like Windoze 95, all things were not exactly what my
client wanted at the time but trade offs were made between
speed (sample rate), size and cost.
I have been researching different PLC's that are on the
market and the one that looks interesting runs Windoze CE
services or C++ or ladder logic. More on that another time.
Are you familiar with the term "moving window averaging"?
Our RPM counters need to run two different sample rates,
the first at 100 RPM and up - the second for 100 RPM and
down. The sample rate needs to be higher for the lower
RPM values to sense 'stall' on the motor under test.
The data is used to plot torque curves and stall happens
within 2 seconds from approx. 1200 RPM when load reaches
critical. We found a LOT of 'jitter' in our values due to
low sample rate and clock speed of the PLC presently used.
The 'work around' was to accumulate counts then average.
This issue was never fully resolved - I think partly due
to the programers level of expertize and partly due to
the clock speed of the PLC.
I proposed that the program be set up to accumulate counts
for X clock cycles as a 'snap shot' then average as many of
these 'snap shots' as practical due to jiffy timer to
obtain readings quickly enough. This worked well for the
higher RPM values but not for the low end (not enough plot
points to draw the curve properly).
Possible that you know a better way?
Is there a way to 'split' clock cycles (jiffies) with
software? Can we 'roll our own' timer somehow?
This issue will determine whether we keep using the
present unit or 'upgrade' the hardware!
The library:
1 vol - ANSI C Constructs and Libraries
1 vol - MCCM77 Documentation set.
1 vol - Telepace C Tools
1 vol - Telesafe Micro 16 SCADA Controller
1 vol - Smartwire System Manual
I have permission to copy required info from these manuals but I
think maybe scanning the important pages and either email or
post the scans (although you may not need them). Email has
limits for binary attachments (you will end up with a 'plugged'
in box and all other mail 'bounces'.
Had this problem when I tried to send my client all of the
AutoCad drawings for a project once.
Spec:
Processor - M37702 16 bit 14.7456 MHz clock with integrated watchdog timer.
Memory - 128 kBytes (expanded to 1 MBytes) CMOS RAM
- 64 kBytes to 2 MBytes EPROM
- 1 kBytes EEPROM (holds port configuration data)
Baud - 300 to 38400 (9600 used)
Word - 7 or 8 bits
Counter Hz- 5 kHz max. (prox. sensor used for input-1800 RPM max with 15 pulses
per revolution (used for higher resolution of motor speed which seldom hits 500
RPM)) Counters are 32 bits wide. *Configured to clear on being read by program?
Assembly language code may be included directly within C programs.
Library routines are called from operating system ROM.
The controller provides 32 software timers individually programmable for
tick rates from 10 per second (1 jiffy) to once every 25.5 seconds. All
timers operate in the background from a hardware interrupt generated from the
main system clock.
Watchdog timer trigger pulses may be generated by the user program or
by the system clock. If watchdog times out CPU is reset. Used for error
detection in system. *Check code to see if it is incorporated!
The C Tools library is an enhancement of the ANSI C in that it has
functions dedicated to this controller for IO register assignment.
eg. read_counter
Timer functions:
interval - set the tick interval in tenths of seconds
settimer - set a timer. Timers count down from set value to zero.
time - read the time period remaining in a timer.
pulse, pulse_train and timeout not used for application.
*Jiffy Clock:
setjiffy - set the jiffy clock.
jiffy - read the jiffy clock.
*resets when power applied to the controller and rolls over after 24 hours to
zero. It is real time clock.
The original programmer only used one timer. Would it not be better
if multiple timers were used?
stall = readcounter
if counter < # settimer_1 ###
if counter > # settimer_2 ###? (pardon my syntax, C programmer I'm not.
8888888888888888888888888888888888 start 8888888888888888888888888888888888888
/*****************************************************************************/
/* C language source code file for Control Microsystems MICRO16 PLC */
/* */
/* Written by Programmer */
/* Written for Customer */
/* */
/* This program below monitors a drill motor test stand, and must be compiled*/
/* along with the file FILENAME.H with the Microtech Research MCCM77 compiler*/
/*****************************************************************************/
/*****************************************************************************/
/* Conditional compilation directives */
/*****************************************************************************/
/* define DEBUG */
/*****************************************************************************/
/* global variables */
/*****************************************************************************/
struct clock now; /* real time clock structure */
struct sensors readings; /* structure containing all the sensor values */
struct scale_vals scale; /* scale values for unit conversions */
/*****************************************************************************/
/* Include files */
/*****************************************************************************/
#include "filename.h" /* #define statements for this file */
#include <stdio.h> /* Microtec compiler standard I/O header */
#include <database.h> /* TelePACE I/O database functions */
#include <iohw.h> /* TelePACE I/O hardware functions */
#include <primitiv.h> /* Real Time Operating System functions */
#include <protocol.h> /* More I/O database related functions */
#include <rtc.h> /* Real time clock functions */
#include <serial.h> /* TelePACE RS232 I/O header */
/*****************************************************************************/
/* Function Prototypes */
/*****************************************************************************/
void main (void);
void read_inputs(unsigned motorsize,unsigned timeout);
void write_dbase(int dataptr,int lasttime);
void init_serial(void);
void print_data(void);
int stalled(void);
void line_printer(void);
void write_panel_meters(void);
unsigned which_motor(void);
void flash_led(void);
void init_scale(void);
/*****************************************************************************/
/* begin function MAIN */
/*****************************************************************************/
void main (void) {
unsigned int motorsize; /* determines which motor (if any)is running */
unsigned dataptr; /* offset from start of A/O to BEGINDATA */
/* is 42000-40000= 2000 */
unsigned lasttime; /* for keeping track of loop timing */
unsigned timeout; /* equals 60/60 seconds or 1 second timing */
unsigned numloops; /* number of iterations of main loop */
/*****************************************************************************/
/* Initialization */
/*****************************************************************************/
init_scale(); /* Initialize scaling factors */
init_serial(); /* Com 1 = PRN, Com 2 = MODBUS */
fprintf(com1, "Customer Oil Drill Motor Test Stand Data\r\n");
fprintf(com1, "----------------------------------------\r\n");
/*****************************************************************************/
/* Beginning of main run time loop, 1 second or 0.1 during stall */
/*****************************************************************************/
while (TRUE) { /* run-time loop never stops */
while (!(motorsize=which_motor())) { /* Wait here for motor to start */
read_inputs(motorsize,timeout); /* read and scale input data */
write_panel_meters();
flash_led(); /* Run LED flashs if both motors stopped */
}
request_resource(IO_SYSTEM);
setdbase(MODBUS,INPROGRESS,1); /* Flag for LabVIEW indicates test on */
release_resource(IO_SYSTEM);
setjiffy(0UL); /* start 1/60 resolution clock for timing, */
lasttime=0; /* as one of the motors is now moving. */
dataptr=BEGINDATA-BEGINAOUT; /* offset from start of A/O to BEGINDATA */
/* is 42000-40000= 2000 */
timeout=60;
numloops=0;
do {
read_inputs(motorsize,timeout); /* read and scale input data */
write_dbase(dataptr,lasttime); /* write to I/O modbus database */
dataptr+=5;
if ((BEGINAOUT+dataptr)>ENDDATA) dataptr=BEGINDATA-BEGINAOUT;
/* wrap dataptr if overflow */
if (stalled()){ /* if stalled, loop time is 1/10 seconds */
timeout=12;
if (!(numloops%5)) {
line_printer(); /* only print and write meters on 5th passes */
write_panel_meters();
}
}
else {
timeout=60; /* if not stalled, loop time is 1 second */
line_printer(); /* so print every second (if required */
write_panel_meters();
}
lasttime=lasttime+timeout; /* time to wait to is advanced */
numloops++;
while (((jiffy()-lasttime)/timeout)>0)
; /* wait till timeout for cycle, 0.2 or 1.0 seconds */
}
while(readings.rpm > STOPPED); /* must be at least moving to read data */
request_resource(IO_SYSTEM);
setdbase(MODBUS,INPROGRESS,0); /* Flag for LabVIEW indicates test off */
release_resource(IO_SYSTEM);
} /* Drop out at end of test (motor stopped) , main loop never stops */
} /* end main (never exits) */
/*****************************************************************************/
/* init_scale <Customer.C> */
/* */
/* SYNTAX: init_scale (void); */
/* */
/* DESCRIPTION: Loads default values to I/O database only if examining */
/* the optionswitch 1 is true. This prevents LabVIEW from updating constant
*/
/* values and then having them reset to defaults. */
/* */
/* RETURN VALUE: None. */
/* */
/* */
/*****************************************************************************/
void init_scale(void) {
if (optionSwitch(OPEN)) { /* if switch is open copy default values */
scale.flow=FLOWSCALE; /* FLOW scale from counts - numeric val */
scale.rpmlarge=RPMLARGESCALE/1000.0;
scale.rpmsmall=RPMSMALLSCALE/1000.0; /* " "
*/
scale.pressure=PRESSURESCALE/1000.0;
scale.torquelarge=TORQUELARGESCALE/1000.0;
scale.torquesmall=TORQUESMALLSCALE/1000.0;
scale.stallrpm=STALLRPM;
}
else { /* if switch 1 is off, copy values from I/O database */
request_resource(IO_SYSTEM);
scale.flow=dbase(MODBUS,FLOWSCALEADDRESS);
scale.rpmlarge=dbase(MODBUS,RPMLARGESCALEADDRESS)/1000.0;
scale.rpmsmall=dbase(MODBUS,RPMSMALLSCALEADDRESS)/1000.0;
scale.pressure=dbase(MODBUS,PRESSURESCALEADDRESS)/1000.0;
scale.torquelarge=dbase(MODBUS,TORQUELARGESCALEADDRESS)/1000.0;
scale.torquesmall=dbase(MODBUS,TORQUESMALLSCALEADDRESS)/1000.0;
scale.stallrpm=dbase(MODBUS,STALLRPMADDRESS);
release_resource(IO_SYSTEM);
}
}
/*****************************************************************************/
/* read_inputs <Customer.C> */
/* */
/* SYNTAX: read_inputs(unsigned in motorsize); */
/* */
/* DESCRIPTION: This function reads and scales inputs to standard units */
/* */
/* */
/* RETURN VALUE: None. */
/* */
/* */
/*****************************************************************************/
void read_inputs(unsigned motorsize,unsigned etime){
request_resource(IO_SYSTEM);
readings.flow=readCounter(INFLOW,READCOUNTER) * scale.flow * 60 / etime;
readCounter(INFLOW,CLEARCOUNTER);
readings.pressure=ain(INPRESSURE) * scale.pressure;
if (motorsize==LARGEON){ /* read torque/rpm depending on motor */
readings.torque=ain(INTORQUELARGE) * scale.torquelarge;
readings.rpm=readCounter(INRPMLARGE,READCOUNTER) * scale.rpmlarge * 60 /
etime;
readCounter(INRPMLARGE,CLEARCOUNTER);
}
else {
readings.torque=ain(INTORQUESMALL) * scale.torquesmall;
readings.rpm=readCounter(INRPMSMALL,READCOUNTER) * scale.rpmsmall * 60 /
etime;
readCounter(INRPMSMALL,CLEARCOUNTER);
}
release_resource(IO_SYSTEM);
} /* end read_inputs */
/*****************************************************************************/
/* which_motor <Customer.C> */
/* */
/* SYNTAX: unsigned which_motor(void); */
/* */
/* DESCRIPTION: This function is used to detect which motor is running by */
/* reading the RPM counters. This function executes in one second. This */
/* time is required to allow counters to accumulate */
/* */
/* RETURN VALUE: OFF = No motors running */
/* LARGEON = Large motor running */
/* SMALLON = Small motor running */
/* */
/*****************************************************************************/
unsigned which_motor(void) {
interval(0,1); /* set timer 0 for 10 hz tick rate */
request_resource(IO_SYSTEM);
readCounter (INFLOW,CLEARCOUNTER); /* Clear counters to zero */
readCounter (INRPMLARGE,CLEARCOUNTER);
readCounter (INRPMSMALL,CLEARCOUNTER); /* Clear counters to zero */
release_resource(IO_SYSTEM);
settimer(0,10); /* set timer for 1 second */
while (timer(0))
; /* wait for 1 second timer to expire */
if (readCounter (INRPMLARGE,READCOUNTER) > 5) { /* large-16RPM,small-40RPM*/
/* request_resource(IO_SYSTEM);
dout(DOUT_START,LARGELAMP); -- 5409 digital output (not specified)--
release_resource(IO_SYSTEM); leave in in case later requested */
return (LARGEON) ;
}
if (readCounter (INRPMSMALL,READCOUNTER) > 5) {
/* request_resource(IO_SYSTEM);
dout(DOUT_START,SMALLLAMP);
release_resource(IO_SYSTEM); */
return (SMALLON) ;
}
else {
/* request_resource(IO_SYSTEM);
dout(DOUT_START,NOLAMP);
release_resource(IO_SYSTEM); */
return (OFF);
}
} /* end which_motor */
/*****************************************************************************/
/* write_dbase <Customer.C> */
/* */
/* SYNTAX: void write_dbase(dataptr); */
/* */
/* DESCRIPTION: write out data to the I/o MODBUS accessible database. */
/* Data is inserted at address offset dataptr, and the offset */
/* address is written to address LASTVALUE in IO database so */
/* that LabVIEW program can query PLC on where data is. */
/* */
/* RETURN VALUE: None. */
/* */
/*****************************************************************************/
void write_dbase(int dataptr,int lasttime) {
request_resource(IO_SYSTEM);
setdbase(MODBUS,BEGINAOUT+dataptr,readings.rpm); /* write sensor vals */
setdbase(MODBUS,BEGINAOUT+dataptr+1,readings.torque);
setdbase(MODBUS,BEGINAOUT+dataptr+2,readings.flow);
setdbase(MODBUS,BEGINAOUT+dataptr+3,readings.pressure);
setdbase(MODBUS,BEGINAOUT+dataptr+4,lasttime/6); /* write time in .1 sec */
setdbase(MODBUS,LASTVALUE,dataptr); /* update lastvalue register */
release_resource(IO_SYSTEM);
}
/*****************************************************************************/
/* flash_led <Customer.C> */
/* */
/* SYNTAX: void flash_led(void); */
/* */
/* DESCRIPTION: flashes an LED to indicate the PLC is waiting for a motor */
/* to start up */
/* */
/* RETURN VALUE: None. */
/* */
/*****************************************************************************/
void flash_led(void) {
request_resource(IO_SYSTEM);
now=getclock();
release_resource(IO_SYSTEM);
if (now.second%2) runLed(LED_ON);
else runLed(LED_OFF);
}
/*****************************************************************************/
/* stalled <Customer.C> */
/* */
/* SYNTAX: int stalled(void); */
/* */
/* DESCRIPTION: This function checks to see if motor is in stall condition */
/* */
/* */
/* RETURN VALUE: TRUE if stalled, else FALSE */
/* */
/* */
/*****************************************************************************/
int stalled (void){
if (readings.rpm < scale.stallrpm)
return (TRUE);
return(FALSE);
}
/*****************************************************************************/
/* write_panel_meters <Customer.C> */
/* */
/* SYNTAX: write_panel_meters(void); */
/* */
/* DESCRIPTION: Write out to the four analog output meters via the model 5301*/
/* micro16 modules. The module is a 0-20mA output, requireing a 500 ohm */
/* load resistor to deliver 0-10 volt output. 10 volts reads 19999 on meter */
/* */
/* RETURN VALUE: none */
/* */
/* */
/*****************************************************************************/
void write_panel_meters(void) {
aout(OUTTORQUE,readings.torque);
aout(OUTPRESSURE,readings.pressure * 10.0);
aout(OUTFLOW,readings.flow *37.0 );
aout(OUTRPM,readings.rpm * scale.meter * 17.0);
}
/*****************************************************************************/
/* line_printer <Customer.C> */
/* */
/* SYNTAX: void line_printer(void); */
/* */
/* DESCRIPTION: Data is written to line printer on interrupt switch keypress */
/* or if the print switch has been depressed for three seconds (count 120 on */
/* the jiffy() clock plus 1 loop. Switch logic is performed in this function,*/
/* print_data is a separate function that does the printing, if necessary */
/* */
/* RETURN VALUE: none. */
/* */
/* */
/*****************************************************************************/
void line_printer(void){
static unsigned lockstate; /* is printing locked on via interrupt */
static unsigned long last_switchoff; /* last time in no_switch, no PRINTLOCK */
unsigned switchon; /* results of reading interrupt switch */
switchon=interruptInput(); /* read switch input */
if (!switchon) { /* if switch is OFF */
last_switchoff=jiffy(); /* mark the last time its off */
if (lockstate) print_data(); /* print if we are in lock state */
return;
}
else { /* if switch is ON */
print_data(); /* print the data */
if (!lockstate) { /* if we are not locked and */
if ((jiffy()-last_switchoff)>180) { /* if not recently off, lock on */
lockstate=TRUE;
return;
}
}
else { /* switch is ON, lockstate ON */
if ((jiffy()-last_switchoff)<180) /* if not recently off, lock off */
lockstate=FALSE;
}
}
} /* end line_printer */
/*****************************************************************************/
/* print_data <Customer.C> */
/* */
/* SYNTAX: void print_data(void); */
/* */
/* DESCRIPTION: This function writes an ascii string out to a line printer */
/* containing all the data scaled from sensors. Data format is: */
/* */
/* YEAR:MONTH:DAY:HOUR:MINUTE:SECOND :RPM:TORQUE:FLOW:PRESSURE <CR><LF> */
/* */
/* Note that real time clock must be reset if necessary by labVIEW (or */
/* remain in standard/daylight saving time) Clock handles leap-years. */
/* */
/* RETURN VALUE: none. */
/* */
/* */
/*****************************************************************************/
void print_data(void) {
now=getclock(); /* update structure "now" with real time clock values */
fprintf(com1, "%2d/%2d - ",now.month,now.day); /* print date */
fprintf(com1, "%2d:%02d:%02d ",now.hour,now.minute,now.second);/* and time
*/
fprintf(com1, "RPM:%4d TORQUE:%4d FLOW:%4d PRESSURE:%4d\r\n",readings.rpm,
readings.torque,readings.flow,readings.pressure);
}
/*****************************************************************************/
/* init_serial <Customer.C> */
/* */
/* SYNTAX: void init_serial(void); */
/* */
/* DESCRIPTION: Sets up conditions for writing straight ASCII to com1 and */
/* MODBUS to com2 */
/* */
/* RETURN VALUE: none. */
/* */
/* */
/*****************************************************************************/
void init_serial(void) {
struct prot_settings settings;
struct pconfig lineconfig;
lineconfig.baud=BAUD9600;
lineconfig.duplex=FULL;
lineconfig.parity=NONE;
lineconfig.data_bits=DATA8;
lineconfig.stop_bits=STOP1;
lineconfig.flow_rx=DISABLE;
lineconfig.flow_tx=DISABLE;
lineconfig.type=RS232;
lineconfig.timeout=600;
set_port(com1,&lineconfig); /* set up printer port line config */
set_port(com2,&lineconfig); /* set up labVIEW port */
/* Disable the protocol on serial port 1 -line printer output */
settings.type = NO_PROTOCOL;
set_protocol(com1, &settings);
/* Enable MODBUS protocol on serial port 2 -LabVIEW /download interface */
settings.type = MODBUS_RTU;
settings.station = 1; /* station address - BusVIEW calls slave address */
settings.priority = 3;
settings.SFMessaging = FALSE;
set_protocol(com2, &settings);
}
8888888888888888888888888888888888888 eof 888888888888888888888888888888888888
/*****************************************************************************/
/* Customer oil PLC program header file */
/* Contains constants and prototypes for functions in Customer.c */
/*****************************************************************************/
/*****************************************************************************/
/* Input signals from ANALOG INPUTS and COUNTERS */
/* */
/* Small motor RPM, counter 0, has I/O database address 10193 */
/* Large motor RPM, counter 1, has I/O database address 10194 */
/* Flow, counter 2, has I/O database address 10195 */
/* Small motor Torque, analog input 0, has I/O database address 30001 */
/* Large motor Torque, analog input 1, has I/O database address 30002 */
/* Pressure, analog input 2, has I/O database address 30003 */
/* */
/* Note that MODBUS accessible I/O database addresses above are raw values */
/* LabVIEW program should read the scaled output values in the I/O database */
/* */
/*****************************************************************************/
#define INRPMSMALL 0 /* COUNTER # 0 ON CPU BOARD */
#define INRPMLARGE 1 /* COUNTER # 1 ON CPU BOARD */
#define INFLOW 2 /* COUNTER # 2 ON CPU BOARD */
#define INTORQUESMALL 0 /* ANALOG INPUT 0 ON A/D BOARD */
#define INTORQUELARGE 1 /* ANALOG INPUT 1 ON A/D BOARD */
#define INPRESSURE 2 /* ANALOG INPUT 2 ON A/D BOARD */
/*****************************************************************************/
/* Output signals for PANEL METERS */
/* */
/* Scaled output torque, analog out 0, has I/O database address 40001 */
/* Scaled output pressure, analog out 1, has I/O database address 40002 */
/* Scaled output flow, analog out 2, has I/O database address 40003 */
/* Scaled output rpm, analog out 3, has I/O database address 40004 */
/* */
/* Note that MODBUS accessible I/O database addresses above are scaled for */
/* writing to 0-10 volt panel meters only. */
/* LabVIEW program should read the scaled output values in the I/O database */
/* */
/*****************************************************************************/
#define OUTTORQUE AOUT_START /* OUTPUT 0 OF ANALOG OUT BASE ADDRESS 0
*/
#define OUTPRESSURE AOUT_START+1 /* OUTPUT 1 OF ANALOG OUT BASE ADDRESS 0 */
#define OUTFLOW AOUT_START+2 /* OUTPUT 0 OF ANALOG OUT BASE ADDRESS 2 */
#define OUTRPM AOUT_START+3 /* OUTPUT 1 OF ANALOG OUT BASE ADDRESS 2
*/
/*****************************************************************************/
/* Digital outs are not being used; leave in in case added later */
/*****************************************************************************/
#define LARGELAMP 2 /* BIT 1 OF DIGITAL OUT BOARD BASE ADDRESS 0 */
#define SMALLLAMP 1 /* BIT 0 OF DIGITAL OUT BOARD " " " */
#define NOLAMP 0
/*****************************************************************************/
/* Output data for MODBUS (LabVIEW program addressable) */
/* These are scaled 16 bit unsigned values */
/* */
/*****************************************************************************/
#define INPROGRESS 41998 /* Address of "test running flag for LabVIEW */
#define LASTVALUE 41999 /* This location contains a pointer offset from*/
/* BEGINDATA to where the last data value has */
/* been written */
#define BEGINDATA 42000 /* BEGINNING OF LabVIEW data transfer area */
/* by moving a pointer, we can keep history. */
/* but this is not implemented yet. */
#define BEGINAOUT 40000 /* Analog output area starts at 40000, but the */
/* Modbus addresses this as relative address 0 */
/* NOTE: not documented in Micro16 manual. */
#define ENDDATA 49995 /* end of data transfer area */
#define BEGINSCALE 41712 /* BEGINNING OF scaling constant data xfer area*/
#define ENDSCALE BEGINSCALE+7
/*****************************************************************************/
/* Scaling constants - This may end up changing during install ... */
/* */
/* RPM: true rpm = counter output / PULSESPERREV (15) */
/* No calibration should ever be needed on RPM measurement */
/* Large motor prox. sensor is geared down by 750/450 = 1.67 RPM/count */
/* Small motor prox. sensor is geared down by 1800/450= 4 RPM/count */
/* Elapsed time will be .2 or 1.0 depending on if in stall */
/* These scaling factors have been fully tested */
/* Note: RPM wont go over 19999 so there is no risk of overflow */
/* */
/* FLOW: 12Hz. pulses = 446GPM = 1688LPM */
/* litres per second per pulse = 1688/12=141 litres/sec/pulse */
/* Calibration constant will be needed for liner size */
/* This liner_size constant can be updated by LabVIEW program through dbase */
/* Elapsed time will be .2 or 1.0 depending on if in stall */
/* These scaling factors have been fully tested */
/* Note: flow wont go over 19999 lps so there is no risk of overflow */
/* */
/* */
/* TORQUE: 0-20 mA signal -> 0 to 32768 binary */
/* Large motor full scale =37280 lbft * 1.356 = 50551 NM = 32768 binary */
/* Small motor full scale =4167 lbft * 1.356 = 5650 NM = 32768 " */
/* full a/d 12 bit binary output is 32768 */
/* Large motor scaling factor is 50551/32768=1.543 NM/bit */
/* Small " " " " 5650 /32768=0.1724 */
/* */
/* NOTE: THIS IS TESTED, BUT OVERFLOW WILL RESULT ON LARGE MOTOR TORQUE */
/* IF TORQUES OVER 19999 NM - THE SCALING MAY HAVE TO BE CHANGED BY 10 X IF */
/* LARGER TORQUES ARE GENERATED, AND A DIGIT DROPPED ON THE DISPLAY. */
/* */
/* PRESSURE: 4-20 mA signal -> 0 to 32768 binary */
/* full scale = 3000 PSI = 3000 * 6.8948 = 20684KPa */
/* full a/d 12 bit binary output is 32768 */
/* scaling factor = 20684/32768=.6312 KPa/bit */
/* */
/* NOTE: THIS IS TESTED, BUT OVERFLOW WILL RESULT IF PRESSURE EXCEEDS */
/* 19999 KPa - THE SCALING MAY HAVE TO BE CHANGED BY 10 x IF LARGER */
/* PRESSURES ARE GENERATED, AND A DIGIT DROPPED ON THE DISPLAY */
/* */
/* METER SCALING: */
/* Analog out is 0-20mA range. Use 500 ohm terminator to get 0-10 volts for */
/* the meter. (there is a 4-20mA switch - ensure it is set for 0-20mA) */
/* Scaling takes place in init_scale in customer.c */
/* */
/*****************************************************************************/
#define FLOWSCALE 141
#define RPMLARGESCALE 1667
#define RPMSMALLSCALE 4000
#define PRESSURESCALE 631
#define TORQUELARGESCALE 1543
#define TORQUESMALLSCALE 172
#define STALLRPM 100 /* stall speed */
/*****************************************************************************/
/* I/O database addresses of scaling constants */
/*****************************************************************************/
#define FLOWSCALEADDRESS BEGINSCALE + 0 /* I/O addresses of above */
#define RPMLARGESCALEADDRESS BEGINSCALE + 1
#define RPMSMALLSCALEADDRESS BEGINSCALE + 2
#define PRESSURESCALEADDRESS BEGINSCALE + 3
#define TORQUELARGESCALEADDRESS BEGINSCALE + 4
#define TORQUESMALLSCALEADDRESS BEGINSCALE + 5
#define STALLRPMADDRESS BEGINSCALE + 6
#define SCALETOMETERADDRESS BEGINSCALE + 7
#define WORKCONVERT 0.737 /* N-M to lb-ft CONVERSION FACTOR */
#define PRESSURECONVERT 0.145 /* KPA to PSI CONVERSION */
#define FLOWCONVERT 0.264 /* LPM to GPM CONVERSION */
/*****************************************************************************/
/* Other constants */
/*****************************************************************************/
#define TRUE 1
#define FALSE 0
#define LARGEON 2 /* motor selector */
#define SMALLON 1
#define OFF 0
#define CLEARCOUNTER 1 /* CLEAR COUNTER TO ZERO */
#define READCOUNTER 0 /* GET COUNTER READING */
#define STOPPED 0
#define PRINTLOCK 1 /* FLAG THAT PRINTER IS TO RUN CONTINUOUS */
#define PRINTUNLOCK 0 /* FLAG THAT PRINTER IS NOT TO RUN CONTINUOUS */
/*****************************************************************************/
/* templates */
/*****************************************************************************/
struct sensors {
unsigned rpm; /* RPM of the running motor */
unsigned torque; /* TORQUE of the running motor */
unsigned flow; /* FLOW (common to both motors) */
unsigned pressure; /* PRESSURE (common to both motors) */
};
struct scale_vals {
float flow; /* FLOW scaling from counts to numeric value */
float rpmlarge; /* we need variables, since the PLC has to read */
float rpmsmall; /* values from either default or LabVIEW program. */
float pressure; /* These scale values corrispond to the default */
float torquelarge; /* values listed above under scaling constants */
float torquesmall;
unsigned stallrpm;
float meter;
};
8888888888888888888888888888 eof 888888888888888888888888888888888888888888
Error noted in Torque section of customer.h - should be 4-20 ma not 0-20.
rpm determination:
15 counts per revolution @ 1800 rpm/60 seconds per minute = 450 Hz full scale
(32768 binary)
Full scale = 10 vdc @ analog output module therefor 1 vdc = 45 Hz (3276.8
binary)
In addition we use a file called 'scaling.txt' which is loaded to the PLC
via Labview application to update scaling constants for each set up. These
over ride the defaults which are there so program doesn't crash initially.
Further - I think the stall RPM=100 is to high for the small motor.
I think we need:
#define small_stall=<50
#define large_stall=<100
???
So there it is. Give me a cost estimate to modify please.
_ _
Sometimes, something or someone - or maybe another person!
_________________________________