Page 1 of 1
Task needs another pair of eyes.
Posted: 10 January 2008, 16:23 PM
by Don_Kirby
This task has been running for about 5000 hours. In the past, I have noticed glitches here and there, but never had any record of it beside the occasional "what was that?".
In order to locate the problem, I set up a datalogger on the comport to monitor the timing information. After 860 Hours and 14 minutes, the time stopped moving for a number of hours, then started again. The rest of the application, including this task, was still running (the com routine is embedded in it). I'm at a loss as to what the problem is, especially after running flawlessly for so long. The effect is as if the WaitForInterval never triggers, although, if that were the case, the console output routine wouldn't run either.
The code is shown below. Coding conventions aside, are there any hidden problems here that I'm not seeing?
Code: Select all
Public Sub HourTask() 'Hour Meter Task - Call this task in Sub Main() to start counting
Dim I As Byte
Dim Loc as Byte
Dim Temp as Single
Dim SECONDS_VALUE as Byte
Temp = 0.0
Loc = 0
SECONDS_VALUE = 0
For I = 0 to 199
If Hours(I) > Temp Then
Temp = Hours(I)
Loc = I
End If
Next
Call SetInterval(1.0) 'set the interval
Do
Call WaitForInterval(0) 'Wait for 1 Second
SECONDS_VALUE = SECONDS_VALUE + 1
If SECONDS_VALUE >= 60 Then
Hours(Loc) = Temp + 1.0 'increment the value
Temp = Hours(Loc)
'Circular Buffer
Loc = Loc + 1
If Loc >= 200 Then
Loc = 0
End If
SECONDS_VALUE = 0
End If
'*********Monitor Output******************************
If Monitor > 0 Then 'logger output (once per second)
Console.WriteLine(ConvertTime(HoursTotal()) & "," & C & "," _
& CStr(CBool(Register.PortC>=&hF0)) & "," & CStr(Alarm_Request))
End If
Loop
End Sub
-Don
Re: Task needs another pair of eyes.
Posted: 10 January 2008, 17:43 PM
by dkinzer
Don_Kirby wrote:This task has been running for about 5000 hours. In the past, I have noticed glitches here and there,
This type of problem is difficult due to the elapsed time issue. If you aren't already doing so, you may want to log the data to a file that is sent by the ZX. That way, you'll have an historical record of what transpired. If necessary, you can build a half-Y serial cable that allows the transmitted data to be sent both to a PC and another destination.
Other ideas: you may want to run only the HoursTask to see if the problem occurs in the absence of any other activity. Also, you may want to temporarily re-write the HoursTask so that it uses no strings or string functions at all. You'll need to write some conversion routines that produce characters that can be sent directly to the Com1 output queue.
Code: Select all
Dim b as Byte
' compute a character to write
b = someValue
Call PutQueue(CByteArray(Register.TxQueue), b, 1)
Re: Task needs another pair of eyes.
Posted: 10 January 2008, 18:32 PM
by mikep
Don_Kirby wrote:In order to locate the problem, I set up a datalogger on the comport to monitor the timing information. After 860 Hours and 14 minutes, the time stopped moving for a number of hours, then started again.
This is a hard one. Is the problem repeatable - let's us know in another 35 days
You could try setting the interval to 1 instead of 1.0. The code would run 512 times quicker so you may see a problem after only an hour or two.
Posted: 10 January 2008, 18:51 PM
by Don_Kirby
I'm logging the output via a serial port logging application, so there is a record of the issue. I wouldn't have known about it otherwise, except for the occasional 'glitch'. With that in mind, a wise embedded programmer once said "There are no glitches, only unfound bugs."
At the expense of the EEPROM, I'm going to speed up the routine so I don't have to wait another 35 days... I'm curious if I can reproduce the issue.
As per Don's suggestion, I'll have to run 2 tests. One to see if the task is the problem, and one to see if intertask 'weirdness' is the issue.
-Don
Posted: 11 January 2008, 15:59 PM
by Don_Kirby
Well, so far I haven't been able to reproduce the bug.
Normally, the task calls WaitForInterval in order to loop just once per second. For this test, I've removed the call entirely, but still cannot get the loop to speed up by more than a factor of ten.
The problem area is a function that cycles through the EEPROM array of 200 singles to find the largest value. Rather than looping through all of them, can anyone suggest a faster method?
The current code is shown below.
Code: Select all
Public Function HoursTotal() As Single
'call this function to retrieve the current value in minutes.
Dim I As Byte
HoursTotal = 0.0
For I = 0 to 199
If Hours(I) > HoursTotal Then
HoursTotal = Hours(I)
End If
Next
End Function
I'm trying to wrap my brain around a method to find the highest value without the loop. When the values are written, they are in consecutive order. The modification shown below speeds up the loop a bit.
Code: Select all
Public Function HoursTotal() As Single
'call this function to retrieve the current value in minutes.
Dim I As Byte
HoursTotal = 0.0
For I = 0 to 199
If Hours(I) > HoursTotal Then
HoursTotal = Hours(I)
ElseIf Hours(I) < HoursTotal Then
Exit For
End If
Next
End Function
This loops faster, but the loop time varies with how many array locations have to be checked. Is there a 'loopless' method?
-Don
Task needs another pair of eyes.
Posted: 11 January 2008, 16:16 PM
by twesthoff
Don_Kirby wrote:can anyone suggest a faster method
The easy thing is don't use a Single variable. Since you are counting seconds, use a Long. It will be a lot faster, or an Integer if that is big enough...
Posted: 11 January 2008, 16:44 PM
by dkinzer
Don_Kirby wrote:Rather than looping through all of them, can anyone suggest a faster method?
Add another Persistent variable that holds the largest value seen so far.
You'll probably want to go through the list at startup and set the "largest so far" variable but after that, just update it (if the data warrants) whenever a new data item is added to the array. Depending on what else you're doing with the data you might be able to abandon the 200 item array altogether.
One thing to note is that the VM routines that write Persistent data read the location first before they write. If the location already contains the correct data, a write is not performed. Although this strategy costs a few CPU cycles, it minimizes "wear" on the EEPROM cells.
Posted: 11 January 2008, 16:54 PM
by Don_Kirby
The reason for the array is to spread the write cycles out over many cells, hopefully to extend the EEPROM life. If I constantly write to one cell to remember the highest value, that one cell will wear out long before the array does. On the other hand, if I write the high value to a RAM variable, I can keep the array (for saving the information across power cycles) and have quick access to the value while the application is running.
-Don
Posted: 11 January 2008, 17:16 PM
by Don_Kirby
Well, it seems that I already had the code in place to do it. The variable 'Temp' shown in the first post holds the most recent value. The only change I needed to make pertained to the possibility of the task not running when the HoursTotal function is called. To account for that, a TaskIsValid check is performed, and if false, the loop method is used.
I've also changed the Singles to Longs, although they are both 4 bytes, so I don't think it will make a difference speed-wise.
-Don
Edit: without the loop delay, and running without the WaitForInterval, the clock has accumulated 8 hours in about 10 minutes. That should speed up the testing dramatically.
Posted: 11 January 2008, 17:54 PM
by dkinzer
Don_Kirby wrote:they are both 4 bytes, so I don't think it will make a difference speed-wise.
Operations (other than loading/storing) on Single values take longer than operations on Long values. A Single comparison takes roughly 37uS while a Long comparison takes about 20uS.
Posted: 12 January 2008, 16:36 PM
by stevech
I have an application that logs certain data to peristent storage. The technique I use is probably widely used, and is, in gist:
All data items are in a well defined structure that is created and maintained in RAM. A from-to region in EEPROM is set aside for a data log which contains one or more such structures.
Each structure has an "ascencion number". It begins at 1 for an empty log.
At power-up/reset, the data log region is searched for the largest ascencion number. This number is remembered in RAM. Note: the region-scan is done only once, at reset.
To add another data log entry, I use the saved highest-ascencion number to compute the offset into the EEPROM. Now I increment the ascencion number then copy the in-RAM data structure along with the new ascencion number, to EEPROM.
This has worked well on a 24/7 application in ZX24.
Some details:
must modulo-region-size to compute EEPROM offset; this permits overwriting the oldest log data.