Topic : Question concerning the Lite Timer on a ST7LIT19B

Forum : ST7/STM8

Original Post
Post Information Post
April 27, 2009 - 2:08pm
Guest

Hello,

I have got the following problem. I am using a ST7FLIT19B microcontroller. I would like to use the lite timer of the controller for a system clock. To do this I defined a time structure similar to the timeval structure:

typedef struct stTimeSpec
{
  UINT32    tv_sec;      // seconds since last reboot
  UINT16    tv_msec;   // milliseconds since last reboot
} stTimeSpec_t;

I then defined a global variable volatile stTimeSpec_t systemTimeGlob; which I increment in the timer interrupt routine of the lite timer once every millisecond.

To guarantee the consistency of the time structure for any given access, I would like to restrict access by any function other than the timer interrupt through a time_get function. This time_get function copies the value of the global structure to an address supplied and protects this memcopy by disabling the timer interrupt before starting the copy and enabling it again after the copy is done.

void get_time(stTimeSpec_t *currentSystemTimePtr)
{
  TB1IE = 0; // Disable Timebase interrupt
  memcpy(currentSystemTimePtr, &systemTimeGlob, sizeof(systemTimeGlob));
  TB1IE = 1; // Enable Timebase interrupt
}

In order to do this, I would like to selectively disable the timer interrupt for the Lite Timer by clearing the TB1IE bit in the LTCSR1 register of the controller and then enable it again by setting the bit after the memcopy is done. When I try this approach however, I am losing a millisecond from time to time.

My first approach was to clear and set the bit by accessing the byte-address of the register and just using a bitmask for the TB1IE bit. This made me lose lots of timer interrupts, often several in a row. After reading the documentation a second time, I guess this was due to the fact, that accessing the LTCSR1 register this way cleared the TB1F flag and therefore a timer counter overflow occurring during the memcopy would get lost.

The second approach now is to directly address the bit in the register, using the directive at 0x5B hbit TB1IE ; and then just setting TB1IE to 0 or 1 (as seen in the code example for get_time above). What I would expect to happen with this is, that the TB1F flag would be preserved when enabling the interrupt after the memcopy and then immediately getting a timer interrupt if a timer counter overflow occurred during the memcopy, therefore not losing any timer interrupts at all. Unfortunately for some reason I am still losing a millisecond now and then.

The only way I was able to lock the interrupts for the memcopy and not lose any timer interrupts at all was to use the _rim_(); and _sim_(); instructions, therefore locking all interrupts for the time of the copy operation (which is not a good idea and which I would like to avoid).

Can you explain to me, why this is happening and if there is any way to prevent this while still being able to deactivate the timer interrupt during the memcopy? Do you have any suggestions how I could accomplish what I have in mind, or is it just not possible with the controller I am using?

Thanks for your help,
Stephan

Replies
Post Information Post
+1
0
-1
April 27, 2009 - 5:45pm
Raisonance Support Team

Hi Stephan,

From your example I think you have an invalid bit address for TB1IE. It should be:

at 0x5C hbit TB1IE; // NOT 0x5B!

I tried to compile your code and it should work fine now:

#include 

typedef struct stTimeSpec
{
  unsigned long tv_sec;     // seconds since last reboot
  unsigned short tv_msec;   // milliseconds since last reboot
} stTimeSpec_t;

volatile stTimeSpec_t systemTimeGlob = {0}; // Null-initialized

at 0x0B hreg LTCSR1;    /* Control and stats register 1*/
#define MASK_TB1IE 0x10;

void get_time(stTimeSpec_t *currentSystemTimePtr)
{
  LTCSR1 &= ~MASK_TB1IE;     // Disable Timebase interrupt
  memcpy(currentSystemTimePtr, &systemTimeGlob, sizeof(systemTimeGlob));
  LTCSR1 |=  MASK_TB1IE;     // Enable Timebase interrupt
}

at 0x5C hbit TB1IE;

void get_time2(stTimeSpec_t *currentSystemTimePtr)
{
  TB1IE = 0; // Disable Timebase interrupt
  memcpy(currentSystemTimePtr, &systemTimeGlob, sizeof(systemTimeGlob));
  TB1IE = 1; // Disable Timebase interrupt
}

void main(void)
{
    stTimeSpec_t timenow;
    while(1)
        {
        get_time(&timenow);
        get_time2(&timenow);
        }
}

The initial code you posted was not devalidating the interrupts, hence some registers accesses could be done in non-atomic (destructive) ways.

Let us know if you still have some issues.
Regards

+1
0
-1
April 28, 2009 - 5:24pm
Guest

Hi Bruno,

thanks for the fast answer.

I followed the example given in the hbit keyword description of the RCST7 compiler manual for determining the TB1IE bit address.

Quote:

at 0x188 hbit OC1E; // Timer Output Compare 1, @31h bit7
at 0x189 hbit OC2E; // Timer Output Compare 2, @31h bit6

From this I deducted that the TB1IE bit (bit 4 of the LTCSR1 register) should have the address (0x0B * 8) + 3 which equals 0x5B. After your answer I checked the compiler manual again and found another description for the at keyword.

Quote:

For the “bit” or “hbit” variables, the absolute address must be specified as follows: *8 + .
Bits are numbered starting with 0 for the least significant bit, up to 7 for the most significant one.

Following this description the address would be (0x0B * 8) + 4 which equals 0x5C (as you pointed out to me). So I was mislead by the example for the hbit keyword in the compiler manual which seems a bit confusing to me in regard to the numbering of the bits.

Unfortunately even with the correction you posted I am still losing timer interrupts (and hence milliseconds) when disabling timer interrupts. The reason for this seems to be, that I am using the time struct to implement a wait_ms function. In this function I am spending a lot of time with the locked timer interrupt. Now I seem to lose an interrupt every time the timer counter overflows while the interrupt is locked. I was expecting that the TB1F flag would be set when an overflow occurs, regardless if the timer interrupt is enabled or disabled. I was further expecting, that after enabling the timer interrupt again by doing a direct bit access I would immediately get the timer interrupt, since the TB1F bit should indicate that a counter overflow has occured. But it seems to me that this is not happening.

I could supply you with a stripped down sample project which shows how I am using the global timestruct and might better explain my problem if you could supply me with an email address where I can send it to.

Regards,
Stephan

+1
0
-1
April 29, 2009 - 9:34am
Raisonance Support Team

Hi Stephan,

You are right about hbit, this is a compiler documentation bug! The "hbit" examples are invalid. We will correct this in the documentation for the next release. Thanks for the bird's eye!

Concerning your millisecond loss, I am surprised by this behavior, and I do not have much idea how to proceed. Can you simulate the overflow interrupt (using Ride7) and retrieve the cycle time required by the whole increment? The operations should not be more than few tens of cycles, which obviously fit within a millisecond.

Do you loose _all_ the overflowed milliseconds, or only some of them?

Also can you try to slow down the speed of the interrupt and check if the problem still happens?

Also did you activate the SPEED optimization mode (at least for the interrupt handler and get_time)? This could make the execution faster and keep you safe of the problem.

Regards,