## Timebase on ATmega644 with Timer-1 16 Bit

15 posts / 0 new
Author
Message

dear all,

While adding a timebase into my program, I have setup the 16bit timer1 on ATmega644 in normal mode and use prescaler 1024. My CPU is running at 22Mhz.

It means that 22000000/1024/65535 = 0,33 overflows. Now to calculate the time in seconds I use the formula time(s)  = 1/period = 1/ 0,33 = 3,0 seconds. What I don't get is this time = 3 seconds is the what? in which way I can correlate the TNCT1 and this formula to print out in serial a running of time=?. Please see the code below.

```/** \brief initTimer1() Function sets-up 16-bit on-board Timer/Counter-1
*         for normal mode operation, and is therefore used for timekeeping
*         purposes. The clock prescaler is set to FOSC div/1024.
* \param  accept no arguments
* \param  accept no arguments
* \return nothing
*
*/
static inline void initTimer1(void){

TCCR1B |= (1 << CS12)|(1 << CS10); // timer clock prescaler F_OSC/1024
}```

work in progress...

Can you elaborate on what you mean by time-keeping?   Each increment of TCNT1 represents 1024 clock-cycles/count / 20,000,000 clock-cycles/sec = 0.0000512 seconds/count.  So, as you note, TCNT1 will count from 0 to 3.355392 seconds in 65535 counts.  When TCNT1 has a count of 321, then the time is 321*51.2 us = 16,435.2 us = 16.4352 ms.

from what you wrote I can use this formula to calculate,     "time (milliseconds) = (TCNT1 * 51) / 1000"  to recalculate timer overflows into milliseconds if I understood you right.

` 1024 clock-cycles/count / 20,000,000 clock-cycles/sec = 0.0000512 seconds/count << shouldn't this be "count/seconds"=?`

after applying the formula above I get this output in terminal, but I don't understand it, could you please comment on it. What actually I see there,  is that time in milliseconds and why my variable is overflowing and starting from zero again

Thanks

```00000
00000
00001
00001
00001
00002
00002
00002
00003
00003
00003
00004
00004
00004
00005
00005
00005
00006
00006
00006
00007
00007
00007
00008
00008
00008
00009
00009
00009
00010
00010
00010
00011
00011
00011
00012
00012
00012
00013
00013
00013
00014
00014
00014
00015
00015
00015
00016
00016
00016
00017
00017
00017
00018
00018
00018
00019
00019
00019
00020
00020
00020
00021
00021
00021
00022
00022
00022
00023
00023
00023
00024
00024
00024
00025
00025
00025
00026
00026
00026
00027
00027
00027
00028
00028
00028
00029
00029
00029
00030
00030
00030
00031
00031
00031
00032
00032
00032
00033
00033
00033
00034
00034
00034
00035
00035
00035
00036
00036
00036
00037
00037
00037
00038
00038
00038
00039
00039
00039
00040
00040
00040
00041
00041
00041
00042
00042
00042
00043
00043
00043
00044
00044
00044
00045
00045
00045
00046
00046
00046
00047
00047
00047
00048
00048
00048
00049
00049
00049
00050
00050
00050
00051
00051
00051
00052
00052
00052
00053
00053
00053
00054
00054
00054
00055
00055
00055
00056
00056
00056
00057
00057
00057
00058
00058
00058
00059
00059
00059
00060
00060
00060
00061
00061
00062
00062
00062
00062
00063
00063
00064
00064
00064
00065
00065
00000
00000
00000
00001
00001
00001
00002
00002
00002
00003
00003
00003
00004
00004
00004
00005
00005
00005
00006
00006
00006
00007
00007
00007
00008
00008
00008
00009
00009
00009
00010
00010
00010
00011
00011
00011
00012
00012
00012
00013
00013
00013
00014
00014
00014
00015
00015
00015
00016
00016
00016
00017
00017
00017
00018
00018
00018
00019
00019
00019
00020
00020
00020
00021
00021
00021
00022
00022
00022
00023
00023
00023
00024
00024
00024
00025
00025
00025
00026
00026
00026
00027
00027
00027
00028
00028
00028
00029
00029
00029
00030
00030
00030
00031
00031
00031
00032
00032
00032
00033
00033
00033
00034
00034
00034
00035
00035
00035
00036
00036
00036
00037
00037
00037
00038
00038
00038
00039
00039
00039
00040
00040
00040
00041
00041
00041
00042
00042
00042
00043
00043
00043
00044
00044
00044
00045
00045
00045
00046
00046
00046
00047
00047
00047
00048
00048
00049
00049
00049
00050
00050
00050
00050
00051
00051
00052
00052
00052
00053
00053
00053
00054
00054
00054
00055
00055
00055
00056
00056
00056
00057
00057
00057
00058
00058
00058
00059
00059
00059
00060
00060
00060
00061
00061
00061
00062
00062
00062
00063
00063
00063
00064
00064
00064
00065
00065
00000
00000
00000
00001
00001
00001
00002
00002
00002
00003
00003
00003
00004
00004
00004
00005
00005
00005
00006
00006
00006
00007
00007
00007
00008
00008
00008```

work in progress...

If you want time in milliseconds then set the timer up in CTC mode to interrupt every millisecond. In the timer isr, increment a variable. Read the variable to get time in milliseconds.

i was wondering that method will not consume to many CPU cycles? coz I am planning to use some other interrupts as well, would it be possible to run multiple interrupt without sacrificing system performance.

work in progress...

Having a system tick is a common method in embedded systems to obtain a timebase.

There's a price to pay for anything. It's up to you to determine of the price is right for the value you receive. There's an old saying - 'qualify and quantify'. to qualify the question would be 'how many cycles does implementing a system tick consume?', to quantify, we need to answer the question. My guess is around 60 clocks. To put that in context, that's 60 clocks times 1000 = 60,000. As a percentage of total cpu clocks, 60000/16000000= 0.375%. Is this a fair price to pay? Write some real code to implement the system tick and measure the cycles the isr consumes to get a real value.

As an aside, Arduino implements a numbering system for their i/o pins. This is quite expensive in terms of cycles. For the most part, if you're just turning relays or leds on or off, the penalty is insignificant. You tradeoff convenience vs execution time. The convenience is that code for one Arduino platform works similarly on another platform.

Here's something I wrote a few years ago.....

https://www.avrfreaks.net/forum/...

I get this output in terminal, but I don't understand it,

NO ONE here understands it either! What is the point if showing a long string of output numbers without the code you are using to actually generate those numbers?

the generated output in #post3 above is obtained with the following code laid in "main.c" and  "In_Lining_Functions.h" files.

I would like to add one thing, I have the ADC signal printed out in serial and to create a plot i need an X Axis to make a graph of analog voltage change over time (µs or ms or sec). For that I must print out time in parallel to ADC signal in terminal. Hope this clarifies more what I am trying to do.

Here is the main.c

```//----------------- Preamble ----------------- //
#include <avr/io.h>
#include <util/delay.h>
#include <avr/power.h>
#include <avr/interrupt.h>
#include <Hardware_Connections_Bay.h>
#include <In_Lining_Functions.h>
#include <USART.h>
uint16_t ADC_data; // low and high bytes combined into one 16-bit word
uint16_t tetta = 0; // timebase
//----------------- Initializations ----------------- //
int main (void){

//initInterrupt2(); // start interrupt hardware
initUSART0(); // start USART-0
printString("\nUSART-0 Hardware >>> initialized");
printString("\r\n\n");
initTimer0(); // start Timer-0
printString("Timer-0 Hardware >>> initialized");
printString("\r\n\n");
initTimer1(); // start Timer-1
printString("Timer-1 Hardware >>> initialized");
printString("\r\n\n");
_delay_ms(1700);
printString("\r\n\n\n\n");
printString("//---------------------=( Starting Physalis )=--------------------//");
printString("\r\n\n");
//printString("|------------------------|\r\n");
//printString("|       /         |      |\r\n");
//printString("|    <>/<>_ __ _<>|<>    |\r\n");
//printString("|     /    |      |  |   |\r\n");
//printString("|  (o)     |(o)   | (o)  |\r\n");
//printString("| (o)     (o)     | (o)  |\r\n");
//printString("|        (o)      |      |\r\n");
//printString("|               <>|<>    |\r\n");
//printString("|               (o)(o)   |\r\n");
//printString("|               (o)(o)   |\r\n");
//printString("|                (o)     |\r\n");
//printString("|                        |\r\n");
//printString("|------------------------|\r\n\n");
printString("//------------=( xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx )=---------//");
printString("\r\n\n");
printString("//--------------------=( Firmware version 1.0a )=-----------------//");
printString("\r\n\n");
printString("//-------------=( Compiled on: " __DATE__ " " __TIME__ " )=------------//");
printString("\r\n\n");
_delay_ms(2000);

SAMPLE_ADC_CONTROL_DDR |= (1 << STATUS_LED); // LED setup for loop execution timing checks

SAMPLE_ADC_LOW_DDR = 0x00; // ADC low byte data direction register setup in input mode BANK(A)

SAMPLE_ADC_HIGH_DDR = 0x00; // ADC high byte data direction register setup in input mode BANK(C)
OCR0A = 100; // Timer-0 output frequency  setup for ADC(CLKIN) pin ~ 1.0 MHz
//sei();
//----------------- Event loop ----------------- //
while(1){

// SAMPLE_ADC_CONTROL_PORT ^= (1 << STATUS_LED); // helps to read the loop execution (with function get_ADC_data() time which is 3.2 MHz
// getDataADC(); // collects low and high bytes from ADC and combines them
tetta = (TCNT1 * 51) / 1000;
printWord(tetta);
// printString("\r\n");
}
return(0);
}```

and this is the helper file In_Lining_Functions.h

```/**********************************************************************************
**  Header File where [AVR's] hardware initialization functions are populated    **
**  ---------------------------------------------------------------------------- **
** -> Timer-0             -> External ADC Device No-1                            **
** -> USART-0             -> External ADC Device No-2                            **
** -> System ISR                                                                 **
** -> CPU clock/Speed                                                            **
**********************************************************************************/
uint16_t tetta;
//---------------------------- Hardware Initializations ------------------------ //

static inline void initTimer0(void){
/** \brief  initTimer0() Function sets-up the on-board Timer/Counter-0 for waveform
*          generation. It is running in CTC mode with output on PB3 port/OC0A.
*          The OC0A/PB3 toggles each time through generates 1.0 MHz clock signal
*  \param  takes no parameters
*  \param  takes no parameters
*  \return nothing
*/
TCCR0A |= (1 << WGM01) | (1 << COM0A0); // toggle OCR0A on compare match
TCCR0B |= (1 << CS00); // Timer-0/8 Bit in CTC mode and clock prescaler set div/1024
}

static inline void initTimer1(void){
/** \brief initTimer1() Function sets-up 16-bit on-board Timer/Counter-1
*         for normal mode operation, and is therefore used for timekeeping
*         purposes. The clock prescaler is set to FOSC div/1024.
* \param  accept no arguments
* \param  accept no arguments
* \return nothing
*
*/
TCCR1B |= (1 << CS12)|(1 << CS10); // timer clock prescaler F_OSC/1024
}

/** \brief getDataADC() Function call uses [AVR's] 2 x PORTs E/H banks 2 x 8-Bits
*          The 16-Bit value is then saved in variable.
* \param   takes no argument
* \param   takes no argument
* \return  nothing, saves the value in ADC_data
*/
{ } // waiting time for DRDY to transition from HIGH >> LOW
printString("\r\n");
}
```

work in progress...

The code that you inline would probably be infrequently called, so why inline? It seems like a case of premature optimisation.

the most heavy lifting is done by the getDataADC() function, it is called in event loop at speeds of ~ 1.6 - 2.0 Mhz. But for the functions initTimer0() and initTimer1() I guess its is not needed.

```static unsigned long start_millis = 0;
unsigned long current_millis;

time_delta = current_millis = millis();
```

this could work if I could make function like millis().

work in progress...

What did I suggest in #4?

Refer to the Arduino core code on how they implement millis()

Thank you so much for the spraying light into darkness. Okay, i did as you advised. Configured Timer-1 Overflow interrupt, then removed the setup with no-prescaling. Then wrote a fucntion in ISR() to count the ticks. All this is done in helper file, "In_Lining_Functions.h".

Then I go to "main.c" and add code snippet to check for overflows and invocation of  "secondTicktock()" , which then counts seconds. Hopefully this is correct.

```/**********************************************************************************
**  Header File where [AVR's] hardware initialization functions are populated    **
**  ---------------------------------------------------------------------------- **
** -> Timer-0             -> External ADC Device No-1                            **
** -> USART-0             -> External ADC Device No-2                            **
** -> System ISR                                                                 **
** -> CPU clock/Speed                                                            **
**********************************************************************************/
#include <USART.h>
#include <Hardware_Connections_Bay.h>
#include <avr/io.h>
#include <avr/interrupt.h>

volatile uint16_t sys_tick;
volatile uint8_t seconds_;
//---------------------------- Hardware Initializations ------------------------ //

static inline void initTimer0(void){
/** \brief  initTimer0() Function sets-up the on-board Timer/Counter-0 for waveform
*          generation. It is running in CTC mode with output on PB3 port/OC0A.
*          The OC0A/PB3 toggles each time through generates 1.0 MHz clock signal
*  \param  takes no parameters
*  \param  takes no parameters
*  \return nothing
*/
TCCR0A |= (1 << WGM01) | (1 << COM0A0); // toggle OCR0A on compare match
TCCR0B |= (1 << CS00); // Timer-0/8 Bit in CTC mode and clock prescaler set div/1024
}

static inline void initTimer1(void){
/** \brief initTimer1() Function sets-up 16-bit on-board Timer/Counter-1
*         for normal mode operation, and is therefore used for timekeeping
*         purposes. The clock prescaler is set to no-prescaling, full speed.
* \param  accept no arguments
* \param  accept no arguments
* \return nothing
*
*/
TCCR1B |= (1 << CS10); // timer clock prescaler at full speed no/prescaling
TIMSK1 |= (1 << TOIE1); // timer overflow interrupt enabled
}

/** \brief getDataADC() Function call uses [AVR's] 2 x PORTs E/H banks 2 x 8-Bits
*          The 16-Bit value is then saved in variable.
* \param   takes no argument
* \param   takes no argument
* \return  nothing, saves the value in ADC_data
*/
{ } // waiting time for DRDY to transition from HIGH >> LOW
printString("\r\n");
}

void secondTicktack(void){

seconds_++ ;
if(seconds_ > 59){
seconds_ = 0;
}
}

//---------------------------- Interrupt Service Routines ---------------------- //

ISR(TIMER1_OVF_vect){

sys_tick++;
}
```

```//----------------- Preamble ----------------- //
#include <avr/io.h>
#include <util/delay.h>
#include <avr/power.h>
#include <avr/interrupt.h>
#include <Hardware_Connections_Bay.h>
#include <In_Lining_Functions.h>
#include <USART.h>

#define OVERFLOWS_PER_SECOND  336  // 22 MHz/65535 ~ = 335.6984 overflows

uint16_t ADC_data; // low and high bytes combined into one 16-bit word
volatile uint16_t sys_tick; // timebase
volatile uint8_t seconds_;
//----------------- Initializations ----------------- //
int main (void){

initUSART0(); // start USART-0
printString("\nUSART-0 Hardware >>> initialized");
printString("\r\n\n");
initTimer0(); // start Timer-0
printString("Timer-0 Hardware >>> initialized");
printString("\r\n\n");
initTimer1(); // start Timer-1
printString("Timer-1 Hardware >>> initialized");
printString("\r\n\n");
_delay_ms(1700);
printString("\r\n\n\n\n");
printString("//---------------------=( Starting Physalis )=--------------------//");
printString("\r\n\n");
//printString("|------------------------|\r\n");
//printString("|       /         |      |\r\n");
//printString("|    <>/<>_ __ _<>|<>    |\r\n");
//printString("|     /    |      |  |   |\r\n");
//printString("|  (o)     |(o)   | (o)  |\r\n");
//printString("| (o)     (o)     | (o)  |\r\n");
//printString("|        (o)      |      |\r\n");
//printString("|               <>|<>    |\r\n");
//printString("|               (o)(o)   |\r\n");
//printString("|               (o)(o)   |\r\n");
//printString("|                (o)     |\r\n");
//printString("|                        |\r\n");
//printString("|------------------------|\r\n\n");
printString("//------------=( Advanced Micro-Scale Thermal Analysis )=---------//");
printString("\r\n\n");
printString("//--------------------=( Firmware version 1.0a )=-----------------//");
printString("\r\n\n");
printString("//-------------=( Compiled on: " __DATE__ " " __TIME__ " )=------------//");
printString("\r\n\n");
_delay_ms(2000);

SAMPLE_ADC_CONTROL_DDR |= (1 << STATUS_LED); // LED setup for loop execution timing checks

SAMPLE_ADC_LOW_DDR = 0x00; // ADC low byte data direction register setup in input mode BANK(A)

SAMPLE_ADC_HIGH_DDR = 0x00; // ADC high byte data direction register setup in input mode BANK(C)
OCR0A = 100; // Timer-0 output frequency  setup for ADC(CLKIN) pin ~ 1.0 MHz
sei();
//----------------- Event loop ----------------- //
while(1){

if(sys_tick == OVERFLOWS_PER_SECOND){
sys_tick = 0;
secondTicktack();
}
printByte(seconds_);
SAMPLE_ADC_CONTROL_PORT ^= (1 << STATUS_LED); // helps to read the loop execution (with function get_ADC_data() time which is 3.2 MHz
// getDataADC(); // collects low and high bytes from ADC and combines them
printString("\r\n");
}
return(0);
}
```

now, on my PC terminal I have the following output which seems to make sense, it starts from zero and ends at 59......

```005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
005
```

but what happens now, my event loop execution time dropped to 3.0 KHz ..... why is this a bottleneck or I am not clever enough, I guess both

work in progress...

How long do you think functions like printbyte() take then? In computing terms things like that take aeons.

Having said that I guess it's true we haven't actually seen printByte(), printWord(), printString(). Maybe they are asynchronous with a tx interrupt and a ring buffer? In which case I take it all back. But I strongly suspect they are synchronous so are blocking on UDRE which, at low baud rates will take 10's of ,000's of CPU cycles to change state.

Try changing the UART functions to "do nothing" (just "return;" right at the top) and see the impact of not printing on your 3kHz loop.

Having said all that, why does it actually matter. It seems like navel fluff gazing to me. Things like execution loop timing are only important when they are "too long". Is 3,000 times around the loop per second really too long?

(and if it is this is why God invented UART TX interrupts!)