SOLVED: Timer2, RTC & INT1 - Atmega644

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

hi all.

I'm having some strange problems when counting Timer2' overflows. Timer2 is configured to use an external 32768Hz clock (RTC), and it overflows every 1 sec (presc=128) when things go right.

Since my app needs to keep time and some other things even when powered off, I planned to add a battery to keep RAM data. I also use INT1 to let me know if there is main power or not.

INT1 is configured for both rising and falling edges, so that any "change" in powering would trigger the interrupt.

the service routine for INT1 is just:

{ lostPower = 1; }

the one for timer2 overflow is

{
   time++;
   
   if ( lostPower )
   {
      lostPower = 0;
      soft_reset_avr();
   }
}

where soft_reset_avr() is just a function pointer which points to 0x0000, and would just make the avr reinit all vars ( I'm using avr-gcc ). Of couse the variable time is defined as .noinit, as all the other stuff whose data I need to remain.

main is just..

uint8_t mcusr_mirror;
int main()
{
      mcusr_mirror = MCUCR;
      MCUCR = 0;
      //only if it's being powered for the first time
      if ( testbit( mcusr_mirror, PORF) )
      {
            init_timer2_and_ints();
            clrbit(mcusr_mirror, PORF);//clear
      }

      sei();

      for (;;)
         ;

      return 1;
}

So far everything should work fine, but the problem is that when I switch from battery power to main power, just after the clock has "ticked", the timer2 ovf interrupt gets called a lot of time before the "1 second" interval. I could see it because I set up a led to turn on and off when a clock tick was made. (the problem was found earlier because I saw my avr's time was ahead of real time! )

I even tried only using main power and stimulating int1 with a cable and VCC (I already used pulldowns at int1), and the same happens.. so even with no voltage change at the avr's VCC, it keeps happening.

It's really strange. I also tried clearing the flags for INT1 and TIMER2_OVF before calling sei() in main, but nothing changed.

Trying to track the problem, I made INT1's handler empty, and checked for INT1's pin value, just instead of checking the variable "lostPower" in the code above. It still happens. I can't find an explanation for this. The problem has to be something I'm not aware of at gcc's startup routine.

Oh, yes.. It works alright if I disable INT1! It's so strange, since that wouldn't change anything! INT1's handler wasn't doing anything at all anyway!

I am able to fix the problem by not using INT1 that way, but I want to know what's going on. If it's a mega644 bug, or what.

I know i'm resetting the avr from an interrupt routine, but since the soft reset should clear the stack, and since ints are disbled in the ISR, everything should work fine, right?

Thanks a lot, and sorry for the so long post, but there was no other way to explain it.

Carlos

Last Edited: Sun. Feb 18, 2007 - 03:57 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Finally solved!

Even thou I was clearing TIMER2 OVF flag by issuing the right instruction, the datasheet says that I have to wait 1 Timer cycle + 3 system clock cycles for that to make effect.

I didn't, so when the avr got "software resetted", the flag was still active and enabling interrupts would make it raise and count 1 tick, which was already counted.

I thought I read the whole TIMER1 specs for async operation, but clearly didn't see this notes!

Oh, btw, another nice "way" to "software reset" the avr is by using longjmp routines. Really useful and cleaner!

Thanks to all who read the post,

Carlos

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

"way" to "software reset" the avr is by using longjmp routines. Really useful and cleaner!

What is your "software reset" supposed to accomplish? If an I/O bit got corrupted, a "software reset" doesn't accomplish much.

I don't know if longjmp is cleaner or dirtier than anything else, but when I want to force my production AVR app to restart, because of an anomaly detection or otherwise, I'll stick to the tight loop+watchdog reset method.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have a watchdog timer already running, just in case something gets messed up.

When powered, the AVR has to log data into a SD card, and connect to several other devices on a RS485 network.
When main power down is detected, it doesn't matter what the AVR is doing, it has to put itself into power save mode to keep counting real time and reduce power consumption (I have an external 3.3V battery as backup).
If I have to keep counting ticks from TIMER2, then I can't let the registers reset. So longjmp is a good choice.

Just to comment things a lot more, here follows some more explanations:

Due to possible noises, I decided to get into sleep mode only when a timer (rtc) tick accomplishes.
That way I am resetting the avr from an interrupt vector.

If I use the WDT I would have to keep another variable to find out if the WDT was a real mess up or just "go to sleep" command. With longjmps it gets really simple.

Anyway, before going to sleep, I disable every module, so I have to reinit again. So in the end it looks like "wdt" but not quite.

If you find any other way to do that, I'd be glad to hear from it, maybe it makes design simpler.-

Thanks

Carlos

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

longjmp is a good choice.

I'll disagree. longjmp() is rarely a good choice for anything, and in particular not for a common "mode change" situation as you just described.

Rarely does anything need to be done "right now". And "right now" means some time needs to be taken to get to your longjmp(). Just let everything unwind, come back to your normal flow that considers mode changes, and change to the power-saving mode.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It it an elegant solution to let everything unroll first, but on the other side I thought that it is safer to force this "software" reset. There is no way it won't happen, since ints are disabled.
If it fails, however, the watchdog will do its job.

Would "unrolling" be better for an specific reason? I mean, other than code organization.

Thanks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It certainly will vary from case-to-case. Are stack considerations always properly handled, even with multiple invocations in-and-out of the longjmp() situation? Does >>every<< reference to every variable not make any assumptions about the value of the variable being initialized or not, or have an invalid value for that state?

For a simple "let's run off the battery for a while" state-change I see no reason to take extraordinary measures. Mr. Dijkstra claimed that "go to" was considered harmful; I suspect a stronger word than "harmful" would be reserved for longjmp().

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I took care of variable initialization (both .init and .noinit).
I think it's a good method to restart the avr.
Of course a function pointer to location 0x0 would do too, initializing variables as if the program started from scratch.

If one knows what longjmp() does, I don't think it's that harmful. About goto, they're dangerous sometimes, but on certain cases the save up a lot of code, specially when checking for errors in a routine, and something needs to be done before returning.

Thanks for the message, I appreciate it, I know I can mess it all up with this functions. Now I know I have to pay even more attention in case I use them.

Thanks,

Carlos