INT4 on ATmega128...

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

Hi,

My learning curve continues ;-)

I'm using a 32768 Hz square wave input on INT4 for an ATmega128 running at 16MHz. The mark:space ration is about 1:4 and the leading/rising edge is not completely square but the waveform is clean and stable with no overshoot or noise (looked at on a good Tek 2465 with 1 Mohm input impedence) - when I measure the frequency on an HP 5385A ovened & calibrated TXCO, its actually 32768.105 Hz over a several hour period, so the pulse chain is very accurate (its sourced from an ovened RTC).

So, I'm counting a second every 32768 interrupts, but every 12 hours I'm about 30 seconds short, i.e. the timer is running somwhat slow.

INT4 is different to INT0:3 in that it has a polled mode by the IOclk - I've tried the polled mode (10) and the "every state change" mode (01 but obviously counting to 65536 in that case) but both run slow. Is INT4 a problem?

When I use the same counting logic, but driven off timer0_comp_vect with an external watch crystal, it counts very accurately.

Am I missing the obvious here?

Many thanks

Nick

Nicko

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

Does your app ever hang around in an ISR, or keep interrupts otherwise disabled, for longer than 30 microseconds?

Since you are using a Mega128 that has lots of toyz, I think most of us would either use a timer-as-counter on Tn pin, or off TOSC1 which I think you are referring to above with timer0_comp_vect. Then you have slow interrupts like every second and have another second to get around to servicing it before there is a lost tick.

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

theusch wrote:
Does your app ever hang around in an ISR, or keep interrupts otherwise disabled, for longer than 30 microseconds?

Since you are using a Mega128 that has lots of toyz, I think most of us would either use a timer-as-counter on Tn pin, or off TOSC1 which I think you are referring to above with timer0_comp_vect. Then you have slow interrupts like every second and have another second to get around to servicing it before there is a lost tick.


Thanks for the reply.

I'll probably not be using a 128 in the final item - its far too top-heavy as I only need one bit of PWM & A/D, I2C, SPI and about 4kb of program space. I'm using a 128 at the moment as its my only real AVR development system - an Olimex AVR-128-MT.

I tend not to hang around in ISRs, but this one is about 0x230 bytes long (according to avr-nm -S -sort-size *.o) - it does test 3 port bits and maybe increment a few counters, but that's all - no I/O or real time-consumers...

Is there any way to time an ISR? I have a USB JTAG (again, the Olimex one which acts the same as the Atmel version).

Thanks

Nick

Nicko

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

With a JTAG you can break point the entry to the ISR then I think the Cycle counter/Stopwatch in the debugger will be useable with JTAG to show you how many cycles/us are being used.

But for the functionality you described 0x230 bytes of ISR sounds WAY WAY too much - I'd be looking at the .lss or building the .s to see what's going on!

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

Put all the stuff from the isr body in a subroutine and call 10 times in a loop of 100,000 calls. The time in seconds is the time in usecs. You should be < 30usecs (1/32768hz), which is about 480 instructions.

Imagecraft compiler user

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

clawson wrote:
With a JTAG you can break point the entry to the ISR then I think the Cycle counter/Stopwatch in the debugger will be useable with JTAG to show you how many cycles/us are being used.

But for the functionality you described 0x230 bytes of ISR sounds WAY WAY too much - I'd be looking at the .lss or building the .s to see what's going on!

I'm generating long & short press detection for 3 buttons by using a macro to test the debounce & set flags for each button's no/short/long press - the code in the macro is pretty straight-forward, but seems to generate 0x68 bytes per invokation, which seems a lot for a few if/then/elses - I'll have a close look at that. I had an idea for a cheap win. Rather than:

ISR(...)
{
   static ubyte16_t tick = 0;
   if (++tick == 32768U)
   {
      count_second();
      tick = 0;
   }
   DO_BUTTON(B1);
   DO_BUTTON(B2);
   DO_BUTTON(B3);
}

which on the second executes the whole code path, I've simply changed it to:

ISR(...)
{
   static ubyte16_t tick = 0;
   if (++tick == 32768U)
   {
      count_second();
      tick = 0;
   }
   else
   {
      DO_BUTTON(B1);
      DO_BUTTON(B2);
      DO_BUTTON(B3);
   }
}

which just skips the debounce & press detection code on each second, thus hopefully not missing any more ticks. We'll see how this does overnight...

Thanks

Nick

Nicko

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

Well I'm now intrigued to know what's in the DO_BUTTON macro leading to 104 bytes (52 code words) on each invocation?

By the way, for debounce I guess you know about:

http://www.ganssle.com/debouncin...

He can do 8 in just a handful of instructions in a timer ISR, a whole port's worth is done in parallel.

Also watch out for danni's (aka Peter's) solution and I think the master himself will have something to offer too...

PS: Silly question but you are building wiht optimisation switched on (that is avoiding -O0) aren't you?

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

clawson wrote:
Well I'm now intrigued to know what's in the DO_BUTTON macro leading to 104 bytes (52 code words) on each invocation?

By the way, for debounce I guess you know about:

http://www.ganssle.com/debouncin...

He can do 8 in just a handful of instructions in a timer ISR, a whole port's worth is done in parallel.

Also watch out for danni's (aka Peter's) solution and I think the master himself will have something to offer too...

PS: Silly question but you are building wiht optimisation switched on (that is avoiding -O0) aren't you?

Firstly, I'm using -Os, but am puzzeled by the large code size - I'm using ubyte16_t variables everywhere, and I suspect that might not be helping. I should also point out that very little of the code path (out of the whole 0x228 bytes) is executed on each tick, so the "do it a million times" idea is actually a pretty neat one - I'll do that.

I am familiar with the vertical counting idea - I've used it before on PICs which I think is where it first appeared (on PICLIST-L). The concept is very neat, but it doesn't easily differentiate between a short & a long press. The original idea comes from http://www.dattalo.com/technical/software/pic/debounce.html - I've used a C version of that many times (mostly in PICC). The general case discussion is at http://www.dattalo.com/technical/software/pic/vertcnt.html

FWIW, my DO_BUTTON macro is:

//
// INT4 ISR
//
// This ISR is driven from the 32kHz output from the RTC.
//
// All button processing is done here - as each button is essentially
// the same, the code is generated by a macro.
//
#define DO_BUTTON( button ) \
	if (tick##button) \
	{ \
		if (get_input(_##button)) \
		{ \
			if (tick##button < BUTTON_SHORT) \
			{ \
				; \
			} \
			else if (tick##button < BUTTON_LONG) \
			{ \
				btn_status |= _BV(button##_S); \
			} \
			else \
			{ \
				btn_status |= _BV(button##_L); \
			} \
			tick##button = 0; \
		} \
		else \
		{ \
			if (tick##button < UINT16_MAX) \
			{ \
				++tick##button; \
			} \
		} \
	} \
	else if (!get_input(_##button)) \
	{ \
		++tick##button; \
		btn_status &= ~(_BV(button##_S) | _BV(button##_L)); \
	} \

ISR(INT4_vect)
{
	static uint16_t uTick = 0;
	static uint16_t tickB1 = 0;
	static uint16_t tickB2 = 0;
	static uint16_t tickB3 = 0;

	if ( ++uTick == 32768U )
	{
		//
		// Every second is 32768 ticks
		//
		count_one_second( &clockTime );
		uTick = 0;
	}
	else
	{
		//
		// Do the button processing.
		//
		// For each button, we ignore bounces (anything shorter
		// than BUTTON_SHORT) , and detect both short and long presses
		// which are mutally exclusive, i.e. each button can be
		// not pressed, have a short press or a long press.
		// Its up to the user to clear the relative bits in the
		// btn_status byte when they've finished processing.
		//
		DO_BUTTON( B1 );
		DO_BUTTON( B2 );
		DO_BUTTON( B3 );
	}
}
#undef DO_BUTTON

I'm going to move to a ubyte8_t counter system with an 8mS base tick for the debounce code - I suspect that'll also dramatically reduce the code size.

Cheers

Nick

Nicko

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

bobgardner wrote:
Put all the stuff from the isr body in a subroutine and call 10 times in a loop of 100,000 calls. The time in seconds is the time in usecs. You should be < 30usecs (1/32768hz), which is about 480 instructions.
I did exactly what you said:
// (read RTC and display time on line 1)
for ( int i = 0; i < 10; ++i )
{
	for ( long j = 0; j < 100000L; ++j )
	{
		__vector_5(); // AKA ISR(INT4_vect)
	}
}
// (read RTC and display time on line 2)

...and it takes 9 seconds, i.e. 9uS for each invokation - I decided to call the ISR directly as I wanted all the prologue etc., i.e. a real test.

It still runs slow, and I'm runing out of ideas ;-(

Thanks

Nick

Nicko

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

OK put just a single invocation of the DO_BUTTON macro - nothing else in an ISR then look at the .lls or the .s and find out where it is "heavy"

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

clawson wrote:
OK put just a single invocation of the DO_BUTTON macro - nothing else in an ISR then look at the .lls or the .s and find out where it is "heavy"
I actually made a slight miscalculation - the original 32kHz interrupt was still running, when I stopped that, the ISR routine was actually consuming only 7uS on average, i.e. well within parameters.

I think I'm chasing shadows here - I really don't think the ISR routine itself is the problem as on any given invokation, only a small part of the code path is executed. 90% of the button code will never be touched unless a button is down. Each button is only checked every 7.8mS, and even then, no two buttons are checked on the same tick.

if ( (uTick % 64) == 0 )
	{
		switch ( (uTick >> 6) & 3 )
		{
			case 0:
				// No button here yet
				break;
			case 1:
				DO_BUTTON( B1 );
				break;
			case 2:
				DO_BUTTON( B2 );
				break;
			case 3:
				DO_BUTTON( B3 );
				break;
		}
	}

What other ways can I count to 32768 and then fire an ISR? Can I use T1 and some other method? Time (pun) to read up on the other counter modes, perhaps, though pointers much appreciated...

Thanks for the help so far, I'm learning...

Nick

Nicko

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

Quote:

I think I'm chasing shadows here

Quote:

What other ways can I count to 32768 and then fire an ISR? Can I use ...

That is kind of what we've been hammering at you--there are a LOT better ways to keep time than to count all the edges of a 32k crystal. In your very first post you mentioned what I think is the best/preferred way--asynchronous operation of one of the AVR's timers. Those are often set up to excite a crystal nicely.

If you have a 32kHz square wave then you could indeed keep time with timer-as-counter and Tn pin. (Gee, someone should have mentioned that before.)

If you DO insist on counting the edges you are going to need to keep your system design clean--ISR latency can never exceed 30us. Note the NEVER if you don't want to miss any. And IIRC you mentioned U(S)ART stream(s). So just discard the idea of doing any "busywork" in the ISRs. Period. None. Set a flag. Do the barest minimum. Get out.

30kHz is well within an AVR's interrupt rate--IF you take care in all aspects.

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

P.S. Why would your 32k signal necessarily be "better"--more accurate--than your AVR's own crystal, properly mounted and capped?

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

theusch wrote:
P.S. Why would your 32k signal necessarily be "better"--more accurate--than your AVR's own crystal, properly mounted and capped?
...because it comes from a TXCO - a DS3231 (http://www.maxim-ic.com/quick_view2.cfm/qv_pk/4627), which is a lovely lovely chip... The AVR's own XTAL does not have an inbuild temperature sensor together with a bunch of laser-trimmed caps that are automatically moved in an out to keep the RTC to within 2ppm...

Nick

Nicko