[TUT] [C] Creating an RTC using an internal counter/timer

Go To Last Post
65 posts / 0 new

Pages

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

pedro_nf wrote:
The C compiler doesn't initialize variables

But Ticks256 is declared into the .bss so of course the C compiler initialises it?!?

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

clawson wrote:
pedro_nf wrote:
The C compiler doesn't initialize variables

But Ticks256 is declared into the .bss so of course the C compiler initialises it?!?

Well, it is true that for static variables the C standard says that they are initialized to 0.
But you know, if you get the habit of initializing all your variables this will pay in time, no more random bugs because a missing initializetion. I always do it, even if I waste a few bytes, I think it is a good practice ;-)

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

But there's no point in doing it for globals - you are just wasting code - the C compiler already generated about 20 bytes to do it all in a single block for you - that's the whole point of it gathering them all together in the single .bss (block started by symbol) block. You just need to do it for automatics.

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

I'm a computer programmer and normaly I worry about the possibility of bugs, but never for the amount of code generated ;-)
In this case you are right, if the compiler does it we should not waste code doing it again.

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

You do realise you are posting on a board about programming microcontrollers don't you? In the bloatware world of unlimited resources that is Windows/Linux you can waste mega or giga bytes of resource at will. When programming 2K or 4K micrcontrollers 20 bytes can be the difference between using a $1.10 and a $1.32 part. 500,000 lots of $0.22 is a fairly significant reason to save 20 bytes!

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

clawson wrote:
When programming 2K or 4K micrcontrollers 20 bytes can be the difference between using a $1.10 and a $1.32 part. 500,000 lots of $0.22 is a fairly significant reason to save 20 bytes!

I never thought about it that way...! Ok, you win :)

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

Hi,
I'm getting thoroughly confused. I've got an ATmega168, 12.9024MHx crystal and all I want (ha-ha) is to have an ISR triggered every millisecond.

I just can't seem to get the right combination of registers and counters and stuff. I tried ajcrm125's example and get nothing (I was going to worry about prescalers once I got this going):

void TimerInit(void)
{
  TCNT0 = 0x00;   // clear Timer/Counter
  TCCR0B |= 0x05;
  // enable interrupts on timer 0 overflow
  TIMSK0 |= _BV(TOIE0);
}
ISR(TIM0_OVF_vect)
{
  // do my stuff every millisecond
}

Enabling of interrupts is done elsewhere.

Confused,
Alf

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

Alf,

Suggest you do a search here for "avrcalc" - it's a very useful utility. I just plugged in the numbers you gave (12.9024, 1ms) and it shows the following. I tried each pre-scale setting but 'none' gets you closest to 1ms but you aren't going to get it exactly. As shown it'll be 1.000031002945ms in fact.

Cliff

Attachment(s): 

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

Hi,
Actually got help from a friend: he sent me this:

void TimerInit(void)
{
  TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode
  TIMSK1 |= (1 << OCIE1A); // Enable CTC interrupt
  // Set CTC compare value to 1 KHz (note there may be truncation)
  OCR1A   = CLOCK_FREQUENCY / 1000ul; 
  TCCR1B |= ( /* (1 << CS12) | (1 << CS11) | */ (1 << CS10) ); // Start timer at Fcpu/1
}
ISR(TIMER1_COMPA_vect)
{
  timer_msec_free_running_counter++;   // increment our millisecond counter.
}

Thanks, will remember avrcalc for future projects,
Alf

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

And guess what CLOCK_FREQUENCY / 1000ul is! 12902400 / 1000 is 12902 which in hex is 0x3266 which is exactly what avrcalc is showing above - so now you know how it's working behind the scenes! (the /1000 is obviously because of your requirement for 1/1000th of a second (1ms) ticks)

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

referring to avr134 APhttp://www.atmel.com/Images/Avr134.zip, can somebody tell me why I've to disable TC0 interrupts (clear TOIE0 and OCIE0) before setting asynchronous clock source for the timer (set AS0) ?

PS: why I should avoid -O0 optimisation at all cost?

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

Quote:

PS: why I should avoid -O0 optimisation at all cost?

Because its only a test mode for the compiler. Apart from the fact that you lose out on about 90% of the clever stuff the compiler can do and your code could be several times larger and several times slower than it could be many things simply won't work if built -O0.

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

Could someone please explain how TCCR0B becomes set to 0x05 here - the 1024 pre-scale? Its not immediately clear. Thanks 

 

ConfigureDevice:

cli ;disable interrupts clr temp

out TCNT0, temp ;clear the timer ldi temp, 0x01

out TCCR0B, temp ;set the timer to a prescaler of 1024 (also starts timer)

ldi temp, 1<<TOIE0

out TIMSK0, temp ;enable the timer0 overflow interrupt sei ;enable interrupts

ret

Last Edited: Tue. Jun 30, 2015 - 08:34 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you sir. You have just proven a point I often make about the use of macros in code. It's possible to obfuscate the operation of code by using macros. If you look closely you will actually see:

#define START_TIMER   TCCR0B |= 0x05

...

void ConfigureDevice(void)
{
 cli(); // disable interrupts just in case
 
 // configure PORTB... and other settings

 TCNT0 = 0x00;   // clear Timer/Counter
 START_TIMER;
 
 // enable interrupts on timer 0 overflow
 TIMSK0  |= _BV(TOIE0);

 sei(); // enable interrupts
}

Now, when the author already makes direct register assignments to TCNT0 and TIMSK0 in that routine it's not entirely clear why he has "hidden" the write to TCCR0B inside a macro called START_TIMER. But that's what he has done. Presumably someone thought this makes the code "easier to read" - it clearly doesn't.

 

When I build the code ("avr-gcc -mmcu=attiny13 -Os -g avr.c -o avr.elf") I get:

0000008a <ConfigureDevice>:
///////////////////////////////////////////////////////////////////////////////
// Configure device configures the micro's setting per our requirements
///////////////////////////////////////////////////////////////////////////////
void ConfigureDevice(void)
{
 cli(); // disable interrupts just in case
  8a:	f8 94       	cli
 
 // configure PORTB... and other settings

 TCNT0 = 0x00;   // clear Timer/Counter
  8c:	12 be       	out	0x32, r1	; 50
 START_TIMER;
  8e:	83 b7       	in	r24, 0x33	; 51
  90:	85 60       	ori	r24, 0x05	; 5
  92:	83 bf       	out	0x33, r24	; 51
 
 // enable interrupts on timer 0 overflow
 TIMSK0  |= _BV(TOIE0);
  94:	89 b7       	in	r24, 0x39	; 57
  96:	82 60       	ori	r24, 0x02	; 2
  98:	89 bf       	out	0x39, r24	; 57

 sei(); // enable interrupts
  9a:	78 94       	sei
}
  9c:	08 95       	ret

That actually raises another point. He is doing a read-modify-write (using OR) to the TCCR0B register. It could just have been a direct assignment as all other bits in that register will be 0.

Last Edited: Tue. Jun 30, 2015 - 08:43 AM

Pages