Timebase on ATmega644 with Timer-1 16 Bit

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

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...

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

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.

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

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...

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

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.

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

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...

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

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/...

 

 

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

 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?

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

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>
uint8_t ADC_lowbyte, ADC_highbyte; // global variables which hold 16 bit DB(0-15) ADC data-bus
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_CONTROL_DDR &= ~ (1 << DATA_READY_ADC); // DRDY data-ready/PB2 setup for input mode
    SAMPLE_ADC_CONTROL_DDR |= (1 << CLOCK_IN_ADC); // PB3/OC0A setup in output mode for ADC clocking via Timer-0

    SAMPLE_ADC_LOW_DDR = 0x00; // ADC low byte data direction register setup in input mode BANK(A)
    SAMPLE_ADC_LOW_PORT = 0x00; // ADC Low byte port pull-up enabled, BANK(A)

    SAMPLE_ADC_HIGH_DDR = 0x00; // ADC high byte data direction register setup in input mode BANK(C)
    SAMPLE_ADC_HIGH_PORT = 0x00; // ADC high byte port pull-up enabled, 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
     // printWord(ADC_data);
        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                                                            **
**********************************************************************************/
uint8_t ADC_lowbyte, ADC_highbyte;
uint16_t ADC_data;
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
 *          on ADC [CLKIN] pin.
 *  \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
}

static inline void getDataADC(void){
/** \brief getDataADC() Function call uses [AVR's] 2 x PORTs E/H banks 2 x 8-Bits
 *          to read the 16-Bit data-bus from the AD-7723 external Analog-Digital-Converter.
 *          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
 */
    while((SAMPLE_ADC_CONTROL_PIN & (1 << DATA_READY_ADC)))
        { } // waiting time for DRDY to transition from HIGH >> LOW
    ADC_lowbyte = SAMPLE_ADC_LOW_PIN; // saves status of pins in BANK(A)in ADC_lowbyte variable
    ADC_highbyte = SAMPLE_ADC_HIGH_PIN; // saves status of pins in BANK(C)in ADC_highbyte variable
    ADC_data = ADC_lowbyte | (ADC_highbyte << 8); // Bitwise OR-ing to combine low-byte and high-byte
    printWord(ADC_data);
    printString("\r\n");
  //SAMPLE_ADC_CONTROL_PORT ^= (1 << STATUS_LED);
}

 

work in progress...

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

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

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

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.

 

Meanwhile I thought about this idea:

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...

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

What did I suggest in #4?

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

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

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>

uint8_t ADC_lowbyte, ADC_highbyte;
uint16_t ADC_data;
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
 *          on ADC [CLKIN] pin.
 *  \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
}

static inline void getDataADC(void){
/** \brief getDataADC() Function call uses [AVR's] 2 x PORTs E/H banks 2 x 8-Bits
 *          to read the 16-Bit data-bus from the AD-7723 external Analog-Digital-Converter.
 *          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
 */
    while((SAMPLE_ADC_CONTROL_PIN & (1 << DATA_READY_ADC)))
        { } // waiting time for DRDY to transition from HIGH >> LOW
    ADC_lowbyte = SAMPLE_ADC_LOW_PIN; // saves status of pins in BANK(A)in ADC_lowbyte variable
    ADC_highbyte = SAMPLE_ADC_HIGH_PIN; // saves status of pins in BANK(C)in ADC_highbyte variable
    ADC_data = ADC_lowbyte | (ADC_highbyte << 8); // Bitwise OR-ing to combine low-byte and high-byte
    printWord(ADC_data);
    printString("\r\n");
  //SAMPLE_ADC_CONTROL_PORT ^= (1 << STATUS_LED);
}

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

uint8_t ADC_lowbyte, ADC_highbyte; // global variables which hold 16 bit DB(0-15) ADC data-bus
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_CONTROL_DDR &= ~ (1 << DATA_READY_ADC); // DRDY data-ready/PB2 setup for input mode
    SAMPLE_ADC_CONTROL_DDR |= (1 << CLOCK_IN_ADC); // PB3/OC0A setup in output mode for ADC clocking via Timer-0

    SAMPLE_ADC_LOW_DDR = 0x00; // ADC low byte data direction register setup in input mode BANK(A)
    SAMPLE_ADC_LOW_PORT = 0x00; // ADC Low byte port pull-up enabled, BANK(A)

    SAMPLE_ADC_HIGH_DDR = 0x00; // ADC high byte data direction register setup in input mode BANK(C)
    SAMPLE_ADC_HIGH_PORT = 0x00; // ADC high byte port pull-up enabled, 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
     // printWord(ADC_data);
        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 sad is this a bottleneck or I am not clever enough, I guess both

work in progress...

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

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!)

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

BTW if something in your whole design does have a very tight timing constraint then rather than putting the UART stuff (that is only there to entertain the shaved ape who is 10,000's of times slower than a computer!) onto interrupts the natural candidate for the interrupts would be the thing that has to be handled very rapidly. Just let the slow loop in main() trundle along as slow as it likes - if it's only there to entertain the human it will still be going 1,000's of times faster than she can cope with!

 

Last Edited: Mon. Apr 29, 2019 - 01:06 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

good argument, shaved ape is totally out of importance. So I will dump all of the ADC nightmare from the getDataADC into ISR(), and will use the ADC DRDY pin to call the function for reading the word. Will try to compare it and see how much it improves the timing. I am still scared even at looking on the ring buffer and Tx interrupts implementation frown. The problem is that I already have down-clocked the ADC to some hundreds of Khz~200Khz. Getting into region where i'll be on my own is not good.

work in progress...