HCS08 long timer question

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

HCS08 long timer question

1,773 Views
gaminn
Contributor IV

Hello,

I need to implement long timer which should time say 234 375 ticks (using prescaler = 7). So I decided to divide whole period to 4 segments of length: 65 535, 65 535 ,65 535 and 37 770 (that is 234 375 total). These values will be stored in modulo register of my timer, each time the timer overflows the modulo will be updated by the next modulo value.

 

I assumed that modulo registers are updated only after timer overflows (so when I change modulo register, timer should count with old modulo value till next overflow, then the modulo is updated)

 

 

 

// counter (long) variable stores information about ticks left on the next overflow// moduloSet (long) variable stores all ticks, ie. 234 375interrupt VectorNumber_Vtpm2ovf TPM2OVFISR(void) {  (void) TPM2SC;  TPM2SC_TOF = 0;  if(counter == 0) {    // time to do some action (234 375 ticks should be gone)    counter = moduloSet;  }    // compute ticks left after next overflow  // timer now counts with modulo value stored in TPM2MOD, so subtract it from counter variable  if(counter > TPM2MOD) {    counter = counter - 1 - TPM2MOD;  } else {    counter = 0;  }    // compute next modulo, update it  // timer should time with old modulo value till next overflow  if(counter == 0) {     // whole timer will count down on the next overflow     if(moduloSet > 0x1FFFF) {      TPM2MOD = 0xFFFF;    } else if(moduloSet <= 0xFFFF) {      TPM2MOD = (unsigned int) (moduloSet - 1);    } else {      TPM2MOD = (unsigned int) (moduloSet / 2) - 1;    }      } else if(counter <= 0xFFFF) {      // only few ticks left      TPM2MOD = (unsigned int) (counter - 1);    } else {      // many ticks left      TPM2MOD = 0xFFFF;    }    }

 

 

But It doesn't work... I can see (when debugging) that modulo registers stores values 0xFFFF, 0xFFFF, 0xFFFF, 0x9386 (that's 234 375 in total) but in real, I can see my actions are done with longer period (of about 30%) than 234 375 should be.

 

Please, can you give me any advice how to implement long timer? The timer must be very accurate.

 

My MCU is SH4.

Labels (1)
0 Kudos
15 Replies

780 Views
bigmac
Specialist III

Hello,

 

I rather like Rocco's approach using output compare interrupts.  The only thing not mentioned is that the TPM counter would need to be cleared at the commencement of the timeout period.  However, if there were more than one timer channel, as Rocco alluded to, this reset process would affect any delays already in progress on other channels.

 

With your existing code, you are probably having problems with the coherency mechanism within the TPM module.  Using an output compare is preferable to changing the modulo value, especially since a TPMMOD value of 0xFFFF is equivalent to the free running case.  Your code will have cumulative software latency errors, which the revised approach does not.

 

You have also assumed that a TPMMOD value of 0xFFF will generate an overflow period of 65535 prescaled TPM clock cycles.  The correct period is actually 65536 cycles.

 

Regards,

Mac

 

0 Kudos

780 Views
rocco
Senior Contributor II

Hi Gaminn and Mac,

 

Glad you got it working Gaminn, and in record time.

 

 


bigmac wrote:

. . .  The only thing not mentioned is that the TPM counter would need to be cleared at the commencement of the timeout period.  However, if there were more than one timer channel, as Rocco alluded to, this reset process would affect any delays already in progress on other channels.


 

Let me clarify by adding some points:

 

The timer free-runs, with the modulus set to the default of $FFFF. So the extended-timer always reflects the time since the MCU started-up.

 

When I calculate the time of a new timeout, I do it relative to the current time. So I never need to reset the timer.

 

I use the same timer for all timeouts in the queue. That way I'm not limited to the number of tasks I can schedule with the timer. The queue is always ordered with soonest-first, so I set the output-compare to the value for the timer at the head of the list, which is the next timeout.

0 Kudos

780 Views
kef
Specialist I

Rocco, what about small modulo ticks % 65536 case? Setting up output compare to occur on old timer counter + too few ticks may fail if done too late and output compare will then occur on old timer counter + too few ticks + 65536.

I'm using it like you to generate various waveforms on S12. Prescaler could ease life of course, but we need the best possible time resolution. In case ticks%65536 is low, say amount of ticks is N*65536+ 50, I'm dividing one 65536+50 period into two, 32768 and 32768+50.

 

0 Kudos

780 Views
rocco
Senior Contributor II

Hi Kef,

 

Yes, the idea falls apart if you try to queue a time that is too close to now. You could code to prevent the failure by defining a lower limit to the time-out. I guess you could also code to detect the failure, by checking the current time after queuing a time-out, to make sure it hasn't past, but it would be ugly.

 

I haven't had to worry about that case, however, as I am usually queuing tasks that are milliseconds to hours away. Anything tight I would run from interrupts, use a dedicated timer, or support in an FPGA (in that order of preference).

0 Kudos

780 Views
kef
Specialist I

Hi

 

I'm not queueing times too close to now. But similar "too close" problem must bite you also queueing now+50+65536, now +50+65536+65536 etc. Fine if you can skip such black holes in time scale. I can't and the only solution I found is to divide 50+65536 interval into 32768+50 and 32768.

0 Kudos

780 Views
rocco
Senior Contributor II

Hi Kef and Mac,

 

I read the posts since mine, and don't understand the problem. Or maybe I do?

 

It sounds like you are trying to count the number of output-compares? If you are, then missing one would be a problem. That is not what I do.

 

I have the timer extended to 24-bit (and sometimes 32-bits), and increment the extension on each timer-overflow.

 

When I get an output-compare interrupt, I compare the extended-timer with the extended value that I am waiting for (from the queue entry), and only dispatch the task if they match. So it doesn't matter whether or not you missed an interrupt. Only if that missed interrupt is the one you wanted, which is my "too close to now" problem.

 

In effect, I have a 24-bit output-compare.

 

Am I understanding this correctly?

0 Kudos

780 Views
kef
Specialist I

Rocco,

 

No problem at all, I just wondered how are you dealing with too small ticks%65536. Too small is for example less than interrupt latency.

 

As i understand it, you are not triggereing too small timeouts, but you are queueing them and sorting them. Right? Then

1) it is not clear what are you doing with long timeouts but with too smal lower 16bits of timeout? I may be wrong, but without extra work timer will be not accurate and could delay for additional up to 2^16 ticks. You are checking if timeout is done in two ISR's, output compare and timer overflow, right?

2) OK, you are not using "bad" timeouts with too small lower 16bits, but are you sure difference between two adjacent queued delays doesn't have too small lower 16bits? The same issue may happen.

 

If I was not clear, for long delays I'm using only output compare interrupt, timer is free running. To count overflows, output compare is triggered with old compare register setting, etc

 

0 Kudos

780 Views
rocco
Senior Contributor II

Hi Kef,

 

I wish I could simply post the code. It is assembly language, so it is pretty fast, even though it may sound complex.


kef wrote:

. . . I just wondered how are you dealing with too small ticks%65536. Too small is for example less than interrupt latency.


I have not needed to. I use this timer queue to primarily schedule tasks. If I need to do things on a 'tight' schedule, I would do that in a dedicated routine with a dedicated timer. I don't need to generate waveforms, which does sound demanding.


As I understand it, you are not triggering too small timeouts, but you are queuing them and sorting them. Right?

Yes, I'm not dealing with short timeouts at all. This technique is mainly for long timeouts. By "short", I mean around 200 microseconds, which is how often I schedule scanning & debouncing a keypad. I am using a queue, and new timer requests get inserted in time-order. For the queue, I use a unidirectional linked-list (so that I don't need to move things around).


Then

1) it is not clear what are you doing with long timeouts but with too small lower 16bits of timeout? I may be wrong, but without extra work timer will be not accurate and could delay for additional up to 2^16 ticks.


Long and short timers are handled the same way, with the caveat that a short timer must be long enough to not risk missing the output-compare interrupt. If you look at the timer and my extension to it as a 24-bit timer, than it works as follows:

 

Let's say the timer counts 1 tick per microsecond. And

I need to schedule two time-outs: 2 seconds from now and 100 microseconds from now.

 

I read the 24-bit timer (timer+extension).

I add 2 million to it. I find that the queue is empty, so I stick the low 16-bits into the output-compare register, and the high 8 bits in the queue entry. The 2-second timer is now running.

 

I read the 24-bit timer (timer+extension).

I add 100 to it. Note that the high 8 bits probably has not changed, So the first output-compare will be the correct one. I find that this queue entry goes ahead of the prior one, so I stick the low 16-bits into the output-compare register, and the high 8 bits in the queue entry. The 100 microsecond timer is now running.

 

100 microseconds later:

The output compare interrupt occurs. In the ISR, I compare the high 8 bits and find they match. I schedule the task (its semaphore was stored in the queue entry), take the low 16-bits from the next queue entry and load it in the output-compare register. We are now waiting for the 2-second timer.

 

On subsequent output-compare interrupts, I compare the timer extension with the high 8-bits in the queue entry, and they don't match, so I just exit.

 

2 seconds (minus 100 microseconds) later:

After many output-compare interrupts where I did nothing because the high eight bits did not match the timer-extension, the extension finally matches, and I schedule its task. The timer queue is now empty again.


You are checking if timeout is done in two ISR's, output compare and timer overflow, right?

No, I just check for timeouts in the output-compare ISR. All the overflow ISR does is increment the timer extension counter.

 


2) OK, you are not using "bad" timeouts with too small lower 16bits, but are you sure difference between two adjacent queued delays doesn't have too small lower 16bits? The same issue may happen.

Yes, you are right! I had not seen that! That is a possibility, but it hasn't bitten me yet (that I know of).

 

Looking at the code, the output-compare ISR takes just under 3 microseconds on a 20mHz bus clock. Add that to my maximum latency of 5 microseconds, and it means 8 microseconds is the closest two timers can safely get (I almost always use 2 microseconds per tick, left over from 68701). So I can't support less than a 4-count difference.

 

I guess I should add code to my queuing routine to check for that. It wouldn't be too hard since I'm already looking at the time in order to find the correct place to insert into the list. 


If I was not clear, for long delays I'm using only output compare interrupt, timer is free running. To count overflows, output compare is triggered with old compare register setting, etc

Yes. I believe I understand. In summary, we are both using the output compare interrupt. But you are counting down the interrupts to your final destination, and I am comparing with an extension to the timer for my final destination.

 

 I suspect your way is more efficient. But mine took a lot longer to write.

 

 

0 Kudos

780 Views
bigmac
Specialist III

Hello Rocco,

 

I did misunderstand your first post in this thread.  As you had made no mention of overflow interrupt use, I had assumed that you were not using them.  However, what I thought was your solution was far more interesting to me - to be able to use the output compare interrupts only for a long time delay, and not need to be concerned about overflow interrupts (which I now assume to be Kef's method).

 

I would characterise the delay method you were actually using with the expression (N*65536 + M), i.e. a partial count cycle follows N full cycles.  Whereas the single interrupt method would be characterised by (M + N*65536), i.e. the partial cycle occurs at the start of the delay.

 

Both methods require that the M value exceeds a minimum value, for all N values,  The corrections previously described should work for the (N > 0) cases of Kef's method.  To apply low M corrections to your method I think may be more difficult.

 

I think that I would probably not use either method for the purpose of general event scheduling, which will usually tolerate much larger time increments - I can't imagine that I would ever need a timing resolution smaller than 1ms for this purpose.

 

Regards,

Mac

 

0 Kudos

780 Views
kef
Specialist I

Rocco,

 
So we are doing almost the same things. The only difference is absolute vs relative.  You are comparing if requested time matches long timer counter. I'm downcounting.
 
I know how I will eliminate dividing 65536+small interval into two halves. Let's consider code to generate output compare (OC) pulse relative to input capture (IC).  Delay is variable. Jitter should be as small as possible. Simple. But once I had to allow delays dangerously too close to interrupt latency limit. Jitter on small delays was OK, but not missing edge! I did it this way:

 

IC_ISR()
{
   OC = IC + delay; // calculate compare register setting

 

   setup OC for desired pin level on compare();

 

   if( (signed short)(OC - timer_counter) < 0 )
   {
      // delay was too small this time, we are late setting OC on time
      force pin to required level();
   }
}

 

Above is OK for delay <=32767. It's not hard to extend it for all 16bit values. How I didn't come to idea that the same approach could be used fro long timeouts! Thanks.

 

 

Mac, sorry for my bad English skills, I was not clear, but I'm not using timer overflow interrupt. Overflows (N's of 655336 timer ticks) are counted inside OC ISR.

 

Best Regards

 

0 Kudos

780 Views
bigmac
Specialist III

Hello,

 

Maybe a potential solution to this issue, with some added code complexity, might be as follows.

 

Currently the delay may be represented by the expression M + N*65536.  Firstly, a suitable minimum value for M would need to be established, consistent with the overhead cycles required, say 50.  If (M <= 50) adjust the 16-bit modulo value to, say (M - 60), to be written to the TPM channel register. The register value would now be somewhat less than 65536, allowing plenty of time from the assumed zero start point until the first output compare operation.  The starting value for the N counter should also be reduced by one (assuming this counter will be decremented to zero).

 

Operation would then continue, as previously described, until what would normally be the final OC interrupt when the channel register value would be increased by the same amount as previously subtracted.  This would then give a register value within the range 0-50, to provide the additional timing correction by means of a very short cycle, i.e. (M - 60 + 65536) + (N - 1)*65536 + 60.

 

Can anyone see potential flaws to this reasoning.  I have not actually tried this approach.

 

Regards,

Mac

0 Kudos

780 Views
kef
Specialist I

Mac,

 

I'm doing almost what you say and it's been working well for years. The difference is +-60 vs +-32768. For case N=2 nad M=50 timer compare intervals according to your description would look like this:

 

|--------- 65536-60 -------------|----------------65536----------------|- 50+60 -|

 

3 timer compares.

 

 

I'm doing it this way:

 

|----32768+50-----|--32768 ----------|------------65536------------------------|

 

That's also 3 timer compares.

 

I don't remember all the reasons for 32768. One of them was smaller instant interrupt frequency (sometimes :smileyhappy:. Another is that +-0x100, +-0x200.. or +-0x8000 requires 8bit arithmetic with upper byte, not full 16bit addition.

Output compare edge is set up or switched either in triggering routine (when interval is <65536), or in next to last interrupt (or in last in case final interrupt is not required) in case interval is long.

Variable is only first output compare interval. There's flag to generate or not generate next 0x8000 interval and compare events downcounter.

0 Kudos

780 Views
bigmac
Specialist III

Hello Kef,

 

I agree that the value subtracted, and then subsequently added, is fairly arbitrary, particularly If the overhead code can be reduced with a different choice.  Perhaps a value of 256 might be another reasonable choice, requiring only that the upper byte be complemented for the subtraction process.  I guess the position where the correction is added is also quite arbitrary, once the first intrrupt has occurred.

 

For the example given, the minimum allowable delay for this method would be 50.  So a project may require two different delay routines - this one for long and intermediate delays, and an additional one to cater for short delays.

 

Regards,

Mac

 

0 Kudos

780 Views
rocco
Senior Contributor II

Hi Gaminn,

 

I extend the timer to either 24-bits or 32-bits. Here is how I do it:

 

First, You extend the timer to 24-bits with one byte of ram. Increment that extended-timer byte in each timer-overflow.

 

Then:

1) When you need to to set a timer, calculate where the whole 24-bit timer would be at time-out, and load the low 16-bits into the output-compare register. Save the high 8bits.

2) On each output-compare interrupt, compare those saved 8-bits with the high byte of your (extended) timer. If they don't match, just clear the interrupt flag and exit.

3) When the two high-bytes match, your time-out has occurred.

 

I have used this approach to run multiple timers concurrently, using a timer queue, with time-outs as long as a day.

0 Kudos

780 Views
gaminn
Contributor IV

Hello rocco and bigmac!

 

Thank you for your answers. Your suggestion with the output compare is really good, I will use it.

 

I needed to make my timer working yesterday, so I programmed this:

 

 

 

interrupt VectorNumber_Vtpm2ovf TPM2OVFISR(void) {  unsigned char i;  unsigned int tpmModulo;  (void) TPM2SC;  TPM2SC_TOF = 0;  // compute left ticks
  if(counter > ((unsigned int) (TPM2MOD + TIMER_OFF_CYCLES))) {    counter = counter - 1 - TPM2MOD - TIMER_OFF_CYCLES;  } else {    counter = 0;  }  // timer expired  if(counter == 0) {    LED = 1;    for(i = 0 ; i < 8 ; i++) {}    LED = 0;        counter = moduloSet;  }    // compute new modulo value  if(counter > 0x1FFFF) {    tpmModulo = 0xFFFF;  } else if(counter <= 0xFFFF) {    tpmModulo = (unsigned int) (counter - 1);  } else {    tpmModulo = (unsigned int) (counter / 2) - 1;  }    // set new modulo  // we need to stop timer to make new modulo to be set immediatelly  // the stop of the timer lasts for 21 (TIMER_OFF_CYCLES) cycles, so simply subtract it  TPM2SC_CLKSx = 0b00;  TPM2MOD = tpmModulo - TIMER_OFF_CYCLES;   // LDHX ,SP = 5, AIX = 2, STHX opr8a = 4   TPM2SC_CLKSx = 0b01;                      // LDA opr8a = 3, AND #opr8i = 2, ORA #opr8i = 2, STA opr8a = 3      => total 21 cycles = TIMER_OFF_CYCLES  return;}

 

It works, we measured it on Agilent counter and the frequency 2 000 Hz was measured like 1999.987 Hz, that is 10 ppm accuracy, we use 40 MHz external oscillator with 50 ppm. But it is more complicated than output compare version.

 

Martin

 

 

0 Kudos