On Mon, 26 Jul 2004 01:50:11 -0500,Frank McCoy's cat ran across
the 'puter keyboard and out came...
Post by Frank McCoyPost by Ãldman©Re posted two different ways.
First as one large zip file.
Second as split parts (by news reader and not
with any splitter progy).
It is possible that if posted as one large file
the post does not propagate to your news server.
That said, I let Gravity split the zip into smaller
parts as that is how you said you prefer binaries.
So now we wait and see :o]
Got the split version.
Didn't *install* it yet; but didn't seem to need to.
Got the stdio.h file and put it in as absolutes instead of changing
defs on my compiler (which would have been the "proper" way; but I
forget how; and am too lazy to look it up for a quick deal).
The thing now compiles fine on my Borlund compiler, except for an
error which is more the way I have my compiler set to report errors
than a real error. I can either live with it, or set the compiler to
ignore that error (which is the way I thought I had things setup).
But ... it does compile.
So ... I went looking through the 'C' code, and quickly saw where your
problem lies: wherever you use readCounter()
The problem (of course) is reading two asynchronous counters, niether
one of which is particularly high-speed.
The problem *I* have now, is I can't find the source-code for the
readCounter() routine; and more specifically the definitions for it.
I get the feeling it's done in assembly-code; and I couldn't find that
among the stuff you sent. I'm not saying it isn't there; just that I
can't find it. Uh ... What's the filename for the module that has
that routine?
I'm just guessing here; but I gather the readCounter routine returns
the ticks or counts of an RPM counter on the motor (motors); one tick
or count per revolution. Then, this count is timed using jiffies
(however long they are) from the system clock; which isn't all that
fast a clock.
One big problem I see (and that I've found with similar systems) is
that the counter is reset at the beginning, then read for a certain
period ... I ass-u-me one second to get the RPM/60. This would give
quite crude measurements.
A much better method would be to NEVER stop the counter, but look for
changes with a timeout. If timeout, then (of course) the motor is
stopped. If not, get the jiffie timer-value and save it.
Next, again look for changes with a timeout.
If timeout, again, motor-stopped.
If not, get jiffie timer and compare with saved value.
If enough time has elapsed to get an accurate measurement, then divide
counts/jiffies to get RPM.
Save new jiffie timer-value for next call to routine.
For twice the accuracy, average last two by adding both counts and
using new jiffie-timer value minus twice-previous value.
Never stop either jiffie-counter or RPM counter.
Take into account rollover-to-zero; and add full count if new value is
less than old value.
As I see it, this is best done by modifying the readCounter() routine
to be assembly-language, which should be non-interruptible in the
parts that read the counts and verify their stability (read three
times, must have same counts) and then reads the jiffy-clock.
The values returned, either as pointers, passed paramters, or actual
return-values would be current RPM-count, jiffy-time, and a timeout
flag.
static void readCounter(long *jiff, long *RPM)
{
static long this_count, current_count = 0;
static long this_RPM, current_RPM = 0;
static BOOLEAN flag = TRUE;
if (flag)
{
disable_interrupts();
this_count = jiffies();
this_RPM = get_RPM_count();
enable_interrupts();
flag = FALSE;
}
do
{
disable_interrupts();
current_count = jiffies();
current_RPM = get_RPM_count();
enable_interrupts();
if (current_count < this_count)
current_count += ONE_DAY;
if (current_RPM < this_RPM)
current_RPM += FULL_COUNT;
}
while ((current_RPM != this_RPM) &&
((current_count - this_count) < ONE_SECOND));
*jiff = current_count;
*RPM = current_RPM - this_RPM;
this_count = current_count;
this_RPM = current_RPM;
}
int current_speed(void)
{
static BOOLEAN flag = FALSE;
long RPM, current_RPM, current_jiff, temp_jiff;
static long jiff;
if (flag)
{
readCounter(&jiff, &RPM);
if (jiff > ONE_DAY)
jiff -= ONE_DAY;
flag = FALSE;
}
RPM = 0;
do
{
readCounter(¤t_jiff, ¤t_RPM);
if (current_jiff > ONE_DAY)
current_jiff -= ONE_DAY;
temp_jiff = current_jiff;
if (current_jiff < jiff)
current_jiff += ONE_DAY;
RPM += current_RPM;
}
while (current_RPM && ((current_jiff - jiff) < ONE_SECOND));
current_jiff -= jiff;
jiff = temp_jiff;
if (!current_RPM)
return 0;
else
{
return
(int)((current_RPM * SIXTY_SECONDS) / (current_jiff * 6));
}
}
main()
{
int speed;
do
{
if (speed = current_speed())
print_speed(speed);
else
print_stalled();
}
while (TRUE);
}
Only readCounter would be an assembly-language routine.
It's not debugged, and doesn't have the averaging routine; and could
be simplified to remove the flags, but I think the basis is there.
For a two-term average, I'd just use two or three extra variables.
For 8 or more, I'd use a FIFO stack. Two such stacks, actually; and
for speed I'd do it even binary: 8, 16, 32, etc., instead of decimal.
Even if it's desired to be ten deep, it would be faster to use a
16-deep FIFO and not use the extra counts, because you could avoid the
divisions needed to do MOD arithmetic.
Note: The thing runs *continously*; even continuing to count when
doing printouts; which (to my notion) is the way it should run. Note
also, that if the printout takes *more* than one second to run, the
accuracy actually *increases* not decreases, because the extra time is
used to gather more counts to average!
A. Each shaft produces one tic per revolution.
B. A truly WILD guess as to how big the counter on the RPM counter is.
C. The jiffy-clock counts 60 beats/second, and resets once per day.
(They don't need checks for rollover between digits.)
If they do need such checks; then such checks can be added.
E. The print_speed() routine expects an integer count
in RPM times ten. IOW, 19.6 RPM would be passed as 196.
I also didn't include *any* code to differentiate between the two
motors ... Which I ass-u-me from what I read run at two different
speeds; and that's how you tell which is which.
This is just an indication of some of what I would probably do, if you
wanted me to.
Running averages could be done for more accuracy, and yet possibly
combined with automatic shortening of the time-averaged, if the speed
changes drastically, for faster response time.
In any case, even without the averaging, I think I might expect at
least an order-of-magnitude increase in accuracy with the sort of
change I'm suggesting.
counts per rev = 15 (fixed by hardware)
max rpm scale = 1800 (/60=30 rps*15=450Hz)
motor running is determined by which counter input is
active (read_counter0=small, read_counter1=large, counter2
is flow)
counters are 32 bits wide each.
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.
Assembly language code may be included directly within C programs.
Library routines are called from operating system ROM.
#asm and #endasm statements are used to enclose in line
assembly language code, which is then assembled without
passing through the compiler, the compiler converts the
C program to assembly language.
controller provides 32 software timers individually
programmable for tick rates from ten per second to
once every 25.5 seconds. all timers operate in the
background from a hardware interupt generated by the
main system clock.
IO_SYSTEM resource must be obtained before using any of
the following funtions:
readCounterInput = read the status of the counter input
points
readCounter = read a counter with or without automatic
clearing of counter register
getclock = read the real time clock
jiffy counter 1/10 second interval (increments 60 times
per second) and rolls over to zero when it reaches
a value of 5183999 (the number of seconds in 24 hours)
setjiffy = set the jiffy clock
jiffy = read the jiffy clock
setClockAlarm = sets the realtime clock alarm
getClockAlarm = reads the real time clock alarm settings
resetClockAlarm = resets the real time clock alarm
alarmIn = returns absolute time of alarm given elapsed time
settimer = sets a software timer and starts it counting (down)
timer = reads contents of software timer
resourse numbers are declared in primitiv.h
status registers are single bit registers that a C
program can read and write - there are 4096 status
registers 10001 to 14096
dbase funtion reads values
setdbase funtion writes values
check and be certain that the existing program makes
no calls to these registers - they can be used for flags
10193 = address digital input 0
10194 = address digital input 1
10195 = address digital input 2
30329..30330 = address counter input 0
30331..30332 = address counter input 1
30333..30334 = address counter input 2
40065..40096 = software timers
40097..40128 = software timer intervals
40191 = real time clock hour
40192 = real time clock minute
40193 = rela time clock second
40198 = alarm hour
40199 = alarm minute
40200 = alarm second
40201 = alarm control (0=no alarm, 1=absolute time)
Make sure the C code includes
runLadderLogic(FALSE)
(reduces CPU load reading ladder logic interpreter)
Supporting tasks are typically created before the main
loop of the program - syntax -
void main (void)
{
/*perform initialization actions*/
/*start support tasks*/
/*main loop*/
while (TRUE)
{
/*perform application functions*/
}
}
tasks can be created and ended dynamically during the
execution of a program as well
The background I/O task is required for the timer
functions to operate. Its priority and execution can
be changed by modifying the following statement:
create_task(background_io, 4, APPLICATION, 2);
Do not retain control of a resourse for more than 0.1
seconds or background operations will not execute
properly.
syntax-
request_resourse (it will wait if resourse is not available)
poll_resourse (continue execution if resourse is not avail)
release_resourse - free resourse for use by other tasks
The RTOS supports up to 16 tasks with 4 priority levels
syntax-
create_task = create and make it ready to execute
end_task = terminate and free resourses
tasks are indipendantly executing routines
compiler directives and syntax -
DOS command line
mccm77 -v -nQ -Ml -c filename.c
-v = issue warnings for features in source file
-nQ = do not suppress diagnostic messages
-Ml = compile for large memory model (lower case L)
-c = compiler output is an object file
-Jdir = specify directory containing the include files
-o = enable standard optimizations - produces smaller and faster code
-Ot = optimize in favour of execution time rather than code size
-nOc = pop the stack after each funtion call (increases code size
and execution time. should only be used if there is a large
number of consecutive function calls in the program
the linker converts object files and object file libraries
into and executable program
lots of sh..t for the linker, I am going to leave it
off for now.
required modules:
appstart.obj
appinit.obj
romfunc.obj
cm77islf.lib
cm77islc.lib
C function library:
csum.h defines checksum related funtions
database.h defines I/O database related funtions
dialup.h defines modem realted funtions
iohw.h defines funtions that access the I/O hardware
pid.h defines PID controller related funtions
primitv.h defines real time operating system
product.h defines macros for product line information
(TELESAFE_MICO_16)
protocol.h defines communication protocol related funtions
(READ_INPUT_REGISTER, READ_HOLDING_REGISTER)
rtc.h defines funtions for the real time clock
serial.h defines serial communication related functions
system.h defines functions for system initialization and
for retrieving system information
installClockHandler function installs a real time clock
alarm handler funtion. the real time clock alarm funtions
calls this function each time a real time clock alarm occurs
enough for now
--
It is said that wisdom comes with age
--so why am I not very, very wise?
Öldman©