Home Perennial flowers Timer in avr studio examples. Programming AVR microcontrollers. OC0 output control

Timer in avr studio examples. Programming AVR microcontrollers. OC0 output control

IN Lately More and more beginners are faced with the problem of mastering Timers/Counters (hereinafter referred to as T/C) at the stage of studying microcontrollers. In this article I will try to dispel fears of these modules and clearly explain how and with what these same T/S are used.

We will take as a basis a book that is very popular among developers of MK devices, authored by A.V. Evstifeev. Using the links at the end of the article you can find the project in and project in. In this article we will analyze the operation of the 8-bit T/S T2, which is part of the Atmega8 T/S MK.

So what is a Timer/Counter? T/S is one of the modules of the AVR MK with which you can measure certain periods of time, organize PWM and many other tasks. Depending on the MK model, the number of T/S can be 4 or more. An example of this is the Atmega640x, 1280x/1281x, 2560x/2561x MKs, which contain 6 T/S on board: two 8-bit and four 16-bit. The Atmega8 MK contains three T/S: T0 and T2 with 8 bits, T1 with 16 bits.

Let's take a closer look at the T/C T2 microcontroller Atmega8.

This timer can operate in several modes: Normal, Phase correct PWM, CTC (reset on coincidence), Fast PWM. You can read more about each mode in the book.

This T/C consists of a control register, a counting register, a comparison register, and an asynchronous mode status register. The block diagram of T2 is shown in Fig. 1

Let's look at how this module works in theory. To make it clearer for you to begin with, we will not consider all the unnecessary gadgets of the timer and will consider its most common mode - NORMAL. Let us determine for ourselves that the MK is clocked from an internal RC oscillator with a frequency of 1 MHz and the timer is configured to operate in NORMAL mode.

Clock pulses arrive at the clk i\o input and enter the timer prescaler. The prescaler can be configured, according to your needs, to pass clock pulses directly or to divide incoming pulses, passing only a certain part of them. Incoming pulses can be divided into /8, /64, /256, /1024. Since our T\S can operate in asynchronous mode, when it is turned on in this mode, the number of prescalers increases significantly, but we will not consider them for now. From the prescaler, clock pulses enter the control unit and from it enter the counting register. The counting register, in turn, increments for each incoming pulse. The T2 counting register is 8-bit, so it can only count up to 255. When the counting register overflows, it is reset to 0 and starts counting again in the same cycle. Also, when the counter register overflows, the TOV2 flag (overflow interrupt flag) of the TIFR register is set.

Now, since we have touched on words such as REGISTER, it’s time to get acquainted with them. To begin with, we will only touch on those registers with which we will directly work, so as not to fill the brain with unnecessary information.

TCNT2 is a counting register, we have already talked about its operation.

TCCR2 - timer control register.

TIMSK - interrupt mask register (in Atmega8 this register is the only one for all timers).

TIFR - interrupt flag register (in Atmega8 this register is the only one for all timers).

And now about each in detail:

Control register TCCR2. You can see the contents of this register in Fig. 2.


Fig.2

Bits 0-2 are responsible for clocking the timer. Setting certain combinations of these bits configures the prescaler for a given timer. If all three bits are clear, the timer is turned off.

Bits 3.6 are responsible for the operating mode of the timer.

Bits 4.5 are needed to configure the behavior of the OSn output (in other words, they are used when setting up PWM)

And the last bit of this register is bit 7. With its help, we can forcefully change the state of the OSn pin.

Interrupt mask register - TIMSK. We see it in picture No. 3

From this register we are only interested in the last two bits, bits 6 and 7. With these bits we enable interrupts.

Bit 6, if one is written to it, enables interruption due to the event "T\C T2 overflow"

Bit 7, if you write to itNitsya, enables interruption on the event "Coincidence of the counting register with the comparison register"

Interrupt flag register TIFR. We see it in picture No. 4

Fig.4

In this register we are also interested in the last two bits: bits 6 and 7.

Bit 6 - flag, set by the event "T\C T2 overflow"
Bit 7 - flag, is installed by the event "Coincidence of the counting register with the comparison register"

These bits are reset automatically when the interrupt handler exits, but to be safe, you can reset them yourself by resetting these bits to "0".

The remaining bits of the TIMSK and TIFR registers are used by T\C T0 and T1. As you have already noticed, the bits of these registers even have the same names, with the exception of the number at the end of the name, which indicates which timer this bit applies to.

It remains to consider two simple tables, namely: a table that describes the control of the clock signal (Fig. 6), and a table that describes how to generally set up a timer (Fig. 5).

I wrote above about what is in these tables, but I am bringing them to you for clarity.

Now we're done with the theory, and it's time to start the practical part. I’ll make a reservation right away.

THE CLOCKS YOU OBTAIN FROM STUDYING THIS ARTICLE ARE NOT HIGHLY ACCURATE. THIS ARTICLE IS ORIENTED ON THE GENERAL PRINCIPLES OF WORKING WITH TIMERS.

Open Studio 6, create a project and select Atmega8.

At the very beginning, we indicate the clock frequency and connect the libraries we need for work

< avr/io.h >#include< avr/interrupt.h >

In the first line we indicate the frequency. This is necessary so that the compiler understands us better if we suddenly want to use the _delay_() functions.

The second line of code includes the library with general description registers of our MK. It also assigns readable names to all registers.

The third line includes a library for working with interrupt vectors.

TIMSK |= (1< < TOIE2); TCCR2 |= (1< < CS22)|(1< < CS20); SREG |= (1< < 7);

This completes the setup of our timer. Let's take a closer look at the last three lines of code.

In the first line we enabled interrupts for the event "Timer/counter T2 overflow"

And in the third line we globally enabled interrupts. It could also be written like this:

Asm("sei");

All that remains is to add the interrupt handler and the code for our real-time clock.

ISR (TIMER2_OVF_vect) ( takt++; if (takt>=4)(sek++; takt=0x00;) if (sek>=60) (min++; sek=0x00;) if (min>=60) (hour++; min=0x00 ;) if (hour>=24) (hour=0x00);

There is nothing complicated or new for you in the code that is in the interrupt handler. Let's pay attention only to the takt variable and the magic number "4". Where did this figure come from? Let's take a closer look at this point.

We know that our MK operates from an internal oscillator with a frequency of 1 MHz, the timer is clocked with a prescaler of \1024, our timer can count up to 255. Knowing these parameters, we can calculate how many overflows it will make in 1 second

1 000 000 \ 1024 \ 256 = 3,814697.....

Well, since we are learning to work with timers and did not set a goal to get a super-accurate clock, we round up our result and get “4”. Those. in 1 second the timer will overflow ~4 times.

Why did we divide by 256 if the timer only counts up to 255? Because "0" is also a number. I think everything is clear here.

Don't forget that all variables must be declared as global.

Here is the entire listing of the program that we got.

#define F_CPU 1000000UL #include< avr/io.h >#include< avr/interrupt.h >unsigned char takt = 0; unsigned char sek = 0; unsigned char min=0; unsigned char hour=0; ISR (TIMER2_OVF_vect) ( takt++; if (takt>=4)(sek++; takt=0x00;) if (sek>=60) (min++; sek=0x00;) if (min>=60) (hour++; min=0x00 ;) if (hour>=24) (hour=0x00); int main(void) (TIMSK |= (1< < TOIE2); TCCR2 |= (1< < CS22)|(1< < CS20); SREG |= (1< < 7); while(1) { } }

But what about the output of information to the user? And then whoever likes it. You can use seven-segment indicators, graphic or character-generating displays, etc.

In the archive you will find a project with display information from nokia5110, a project in Proteus 7 and that’s it necessary files and libraries for work.

Please note that the LCD_5110 library for working with the display was written by a forum participant and provided with his permission.

In this tutorial we will talk about timers.

This topic is directly related to the topic of microcontroller clocking. Therefore, I recommend that you read the previous one before reading this lesson.

So why do we need a timer?

When building projects on microcontrollers, there is often a need to measure precise time intervals. For example, the desire to blink an LED at a certain frequency, or poll the state of a button at the required time intervals.

Timers help solve the tasks. But AVR microcontroller timers do not know what a second, minute, or hour is. However, they know very well what tact is! They work precisely due to the presence of controller clocking. That is, the timer counts the number of controller cycles, thereby measuring time intervals. Let's say the controller operates at a clock frequency of 8 MHz, that is, when the timer counts to 8,000,000, one second will pass, counting to 16,000,000, 2 seconds will pass, and so on.

However, here comes the first obstacle. Our registers are 8-bit, that is, we can count up to a maximum of 255, and taking a 16-bit timer, we can count up to a maximum of 65535. That is, in one second we must reset the timer great amount once! Of course, you can do this if you have nothing else to do. But simply measuring time using a powerful microcontroller is not at all interesting; I want to do something more. This is where the predivider comes to our aid. IN general view This intermediate between the timer and the controller clock frequency. The prescaler makes our task easier by allowing us to divide the clock frequency by certain number, before submitting it to the timer. That is, by setting the prescaler to 8, in 1 second our timer will count to 1,000,000, instead of 8,000,000 (Of course, with a controller clock frequency of 8 MHz). It’s already more interesting, isn’t it? And we can divide not only by 8, but also by 64 and even by 1024.

Now it's time to assemble the circuit, set up our timer, prescaler, and do at least something useful!

And today we will make “running lights” from LEDs. That is, we will light 3 LEDs one by one, with a period of 0.75 seconds (That is, the operating time of one LED is 0.25 seconds). Let's put together the following diagram:

Calculate the values ​​of resistors R 1-R 3 yourself.

Next, let's look at the registers responsible for the operation of timers. In total, AtMega 8 has 3 timers. Two 8-bit (Timer 0, Timer 2) and one 16-bit (Timer 1). We will consider the example of 16-bit timer 1.

A pair of registers, 8-bit registers TCNT 1H and TCNT 1L, together form the 16-bit register TCNT 1. This register is open for both writing and reading. When timer 1 is running, the value of this register changes by one with each count. That is, register TCNT 1 records the number of clock cycles that the timer has counted. We can also write here any number in the range from 0 to 2 to the 16th power. In this case, the clock cycles will be counted not from 0, but from the number we recorded.

The TIMSK register is responsible for interrupts generated when the microcontroller timers operate. An interrupt is a handler for a special signal received when something changes.. Any microcontroller interrupt can be enabled or disabled. When an enabled interrupt occurs, the course of the main program is interrupted and this signal is processed. When a disabled interrupt occurs, the program flow is not interrupted and the interrupt is ignored. The TOIE 1 bit (Timer 1 Overflow Interrupt Enable) is responsible for enabling the overflow interrupt of the counting register TCNT 1 of timer 1. When writing 1 to this bit, the interrupt is enabled, and when writing 0, it is disabled. This interrupt is generated by timer 1 when it reaches maximum value register TCNT 1. We'll talk more about interrupts in the next lesson.

Register TCCR 1B is responsible for the configuration of timer 1. B in this case With bits CS 10-CS 12 we set the value of the prescaler according to the following table.

The remaining bits are not of interest to us for now.

There is also a register TCCR 1A, which allows you to configure other timer operating modes, for example PWM, but about them in a separate article.

And now the code in C:

#define F_CPU 16000000UL #include #include uint8_t num=0; ISR(TIMER1_OVF_vect) ( PORTD=(1<2) ( num=0; ) TCNT1=61630;//Initial timer value ) int main(void) ( DDRD|=(1<

#define F_CPU 16000000UL

#include

#include

uint8_t num = ;

ISR(TIMER1_OVF_vect)

PORTD = (1<< num ) ;

num++ ;

if (num > 2)

num = ;

TCNT1 = 61630 ; //Initial timer value

int main(void)

DDRD |= (1<< PD0 ) | (1 << PD1 ) | (1 << PD2 ) ;

TCCR1B |= (1<< CS12 ) | (1 << CS10 ) ; //Prescaler = 1024

TIMSK |= (1<< TOIE1 ) ; //Enable timer 1 overflow interrupt

TCNT1 = 61630 ; //Initial timer value

sei(); //Enable interrupts

while(1)

//The main loop of the program, it is empty, since all the work is in the interrupt

ASM code:

Assembly (x86)

Include "m8def.inc" rjmp start .org OVF1addr rjmp TIM1_OVF start: ldi R16,LOW(RamEnd) out SPL,R16 ldi R16,HIGH(RamEnd) out SPH,R16 ldi R16,1 ldi R17,0b00000111 out DDRD,R17 ldi R17,0b00000101 out TCCR1B,R17 ldi R17,0b11110000 out TCNT1H,R17 ldi R17,0b10111110 out TCNT1l,R17 ldi R17,0b00000100 out TIMSK,R17 sei main_loop: nop rjmp main_loop TIM1_OVF : out PORTD,R16 lsl R16 cpi R16,8 brlo label_1 ldi R16,1 label_1: ldi R17,0b10111110 out TCNT1L,R17 ldi R17,0b11110000 out TCNT1H,R17 reti

Include "m8def.inc"

Rjmp start

Org OVF 1addr

Rjmp TIM 1_OVF

start :

Ldi R 16, LOW (RamEnd)

Out SPL, R 16

Ldi R 16, HIGH (RamEnd)

Out SPH, R 16

Ldi R 16, 1

Ldi R 17, 0b00000111

Out DDRD, R 17

Ldi R 17, 0b00000101

Out TCCR 1B, R 17

Ldi R 17, 0b11110000

Out TCNT 1H, R 17

Ldi R 17, 0b10111110

One of the advantages of the ATmega8 microcontroller is its wide range of different interrupts.

Interrupt is an event upon the occurrence of which the execution of the main program is suspended and a function is called that handles an interrupt of a certain type.

Interrupts are divided into internal and external. Sources of internal interrupts include built-in microcontroller modules (timers, USART transceiver, etc.). External interrupts occur when external signals arrive at the microcontroller pins (for example, signals at the RESET and INT pins). The nature of the signals leading to the occurrence of an interrupt is set in the control register MCUCR, in particular in the bits - ISC00 (bit 0) and ISC01 (bit 1) for input INT 0; ISC10 (bit2) and ISC11 (bit3) for INT1 input.

In the ATmega8 microcontroller, each interrupt has its own interrupt vector(address at the beginning of the program memory area in which the command for jumping to the specified interrupt routine is stored). In mega8, all interrupts have the same priority. If several interrupts occur simultaneously, the interrupt with the lower vector number will be processed first.

Interrupt vectors in Atmega8

Address Interrupt source Description
0x0000 RESET Reset signal
0x0001 INT0 External interrupt request at INT0 input
0x0002 INT1 External interrupt request at INT1 input
0x0003 T/C1 Timer capture T/C1
0x0004 T/C1 Match T/C1 Timer Compare Register A
0x0005 T/C1 Match with compare register B of timer T/C1
0x0006 T/C1 T/C1 counter overflow
0x0007 T/C0 T/C0 counter overflow
0x0008 SPI SPI data transfer completed
0x0009 UART The UART transceiver has completed receiving data.
0x000A UART UART data register is empty
0x000B UART Data transmission by UART transceiver is completed
0x000C ANA_COMP Interrupt from analog comparator

Interrupt management

4 registers are responsible for managing interrupts in ATmega8:

GIMSK(aka GICR) - prohibit/enable interruptions based on signals at inputs INT0, INT1

GIFR- management of all external interrupts

TIMSK, TIFR- management of interruptions from timers/counters

Register GIMSK(GICR)

INTFx=1: an interrupt occurred at the INTx input. When entering the interrupt handling routine, INTFx is automatically reset to the log state. 0

Register TIMSK

7 6 5 4 3 2 1 0
TOIE1
OCIE1A
OCIE1B
-
TICIE
-
TOIE0
-

TOIE1=1: T/C1 overflow interrupt enabled

OCIE1A=1: interrupt when comparison register A matches the contents of counter T/C1 enabled

OCIE1B=1: interrupt when comparison register B matches the contents of counter T/C1 enabled

TICIE=1: Enable interrupt when capture condition is met

TOIE0=1: T/C0 overflow interrupt enabled

Register TIFR

7 6 5 4 3 2 1 0
TOV1
OCF1A
OCF1B
-
ICF1
-
TOV0
-

TOV1=1: T/C1 overflow occurred

OCF1A=1: comparison register A coincided with the contents of counter T/C1 allowed

OCF1B=1: comparison register B matches the contents of counter T/C1 allowed

ICF=1: capture conditions met

TOV0=1: T/C0 overflow occurred

When entering the interrupt handling subroutine, the TIFR register flag corresponding to the interrupt is automatically reset to the log state. 0

Interrupts only work when general interrupts are enabled in the SREG status register (bit 7 = 1). When an interrupt occurs, this bit is automatically reset to 0, disabling subsequent interrupts.

In this example, the INT0 pin is enabled in pull-up input mode. When the pin is shorted to ground using a button, logic 0 is set on it (the edge of the signal drops from the supply voltage to 0) and the interrupt handler is triggered, turning on the light bulb connected to the zero pin of port B

void lampON()
{
PORTB.0=1;
DDRB.0=1;
}

interrupt void ext_int0_isr(void)
{
lampON();
}

DDRD.2=0;
PORTD.2=1;

SREG|= (1 while(1) (

The example above also shows how interrupt vectors are set in Code Vision AVR (interrupt void ext_int0_isr(void)). Interrupt vectors are set similarly for other cases:

EXT_INT0 2
EXT_INT1 3
TIM2_COMP 4
TIM2_OVF 5
TIM1_CAPT 6
TIM1_COMPA 7
TIM1_COMPB 8
TIM1_OVF 9
TIM0_OVF 10
SPI_STC 11
USART_RXC 12
USART_DRE 13
USART_TXC 14
ADC_INT 15
EE_RDY 16
ANA_COMP 17
TWI 18
SPM_READY 19

In essence, a microcontroller timer is a digital counter, only “sophisticated”. A clock signal is supplied to the counter input, based on the drops of which the counter increases its value. When events occur - a counter overflow or its value matches a given value - an interrupt request is generated.

Let's look at how to use the T0 timer in Normal mode. In this mode, the timer counts from some initial value of the counting register to the maximum possible (up to 255 or 0xFF). When timer T0 counts to the maximum, then in the next clock cycle the counting register TCNT0 overflows - it is reset and the TOV0 flag is set. If the program allows interrupts globally (flag I of the SREG register) and the T0 timer overflow interrupt (flag TOIE0 of the TIMSK register), then the microcontroller will call the corresponding handler. If the value of the counting register coincides with the comparison register OCR0, then the OCF0 flag is set and if the match event interrupt is enabled, its handler will start.

Timer T0 in Normal mode

Let's consider a practical problem - we need to poll a button every 20 ms. Microcontroller frequency 8 MHz, ATmega16 microcontroller.

The first thing to do is decide on the choice of the timer prescaler coefficient and calculate the initial value for the TCNT0 counter register.

Timer T0 can be clocked from the internal clock signal of the microcontroller or from an external one, which is supplied to the T0 pin. When operating from an internal clock signal, the user can select the frequency division ratios of this signal. The T0 timer has five possible prescaler coefficient options - 1, 8, 64, 256, 1024.

To solve this problem, I reason as follows. If one tick of timer T0 had a period of 1 ms, then it would suit me. 20 clock cycles give 20 ms. What timer prescaler coefficient will allow you to get a clock period close to 1 ms? You can count.

Microcontroller clock frequency Fcpu = 8000000 Hz
Microcontroller clock period Tcpu = 1/Fcpu
The clock period of timer T0 is equal to Tt0 = (1/Fcpu)/k = k/Fcpu

At k = 1024, the clock period of timer T0 will be equal to Tt0 = 1024/8000000 = 0.128 ms

This is the maximum timer clock period that we can obtain under our conditions (Fcpu = 8 MHz). With lower odds, the period will be even shorter.

Well, okay, let’s say one timer clock is 0.128 ms, is the counting register wide enough to count this time interval and how many clock cycles will it take? We divide the required time interval (20 ms) by the duration of one timer tick and get the answer.

n = t/Tto = 20 ms/ 0.128 ms = 156.25

Rounding to the nearest whole, we get 156 clock cycles. This is less than 255 (the maximum value of the counting register), which means that the TCNT0 counting register is sufficient.

The initial value for the counting register TCNT0 is calculated as the difference between the maximum number of clock cycles of timer T0 and the required one, that is, 256 - 156 = 100. (256 is the maximum number of time intervals that any 8-bit timer can count.)

I think it’s now clear how to calculate the initial value of TCNT0 for the Normal mode:

We calculate the period of one timer cycle Tt0 = k/Fcpu,
- calculate the required number of clock cycles for a given interval n = t/Tto,
- calculate the initial value for the counting register TCNT0 = 256 - n.

You can automate this procedure using macros. For example, like this:

#define F_CPU 8000000UL
#define TIME_MS(time, k) (256L - ((time)*(F_CPU))/(1000L*(k)))

But with such a macro you need to be careful; errors may occur at certain values ​​of time and k.

Now let's move on to the code. To use timer T0 (and any other too), you need to configure it (initialize) and describe the interrupt handler (if used).

Initializing a timer consists of the following steps:

Stop the timer
- setting Normal mode in TCCR0 without start,
- setting the initial value TCNT0,
- resetting flags in the TIFR register,
- enable interrupt on overflow in TIMSK,
- setting the prescaler in TCCR0, that is, starting the timer

Variations are possible in this sequence.

For our task, the initialization code will look like this:


/*value for counting register*/
#define T_POLL 100

TCCR0 = 0;
TCCR0 = (0<TCNT0 = T_POLL;
TIFR = (1<TIMSK |= (1<TCCR0 |= (1<

The second initialization line is essentially useless; it was added for clarity. To clearly see which timer mode is being set.

Resetting the interrupt flags in the TIFR register is done by writing a 1 to the corresponding bit. This operation must be performed by register overwriting, and not using bitwise OR. And that's why.

Let's say two interrupt flags are set in the TIFR register - TOV1 and TOV0. TOV0 we need to reset. When setting the required digit using ORSomething like the following happens.


//TIFR has the value 0b00000101
//flags are set TOV1 and TOV0
//code is executed TIFR |= (1<
//TIFR is copied to R16
IN R16, 0x38

//in R16 the TOV0 bit is set
//although it is already installed
ORI R16, 0x02

//R16 equal to 0b00000101 is written to the TIFR register
OUT 0x38, R16

As a result, both flags were reset, but we wanted to reset one.

Let's continue.

The syntax for describing interrupt handlers is slightly different for different compilers. For IAR'a, the T0 timer interrupt handler for the overflow event will look like this:



{
TCNT0 = T_POLL;

/*there should be a button poll here*/

TIMER0_OVF_vect is the address of the overflow interrupt vector. It is taken from the header files on the microcontroller. In this case, I took it from the iom16.h file.

The first line of the handler (TCNT0 = T_POLL;) overwrites the counting register and sets its initial value. If this is not done, the timer will continue counting from 0. Rewriting the counting register must be done at the beginning of the interrupt handler.

All the code for our task will look something like this. (The code is shown for IAR. For other compilers, you need to change the header files and the interrupt handler.)

#include
#include
#include

#define T_POLL 100

int main(void)
{
/*initialize timer*/

TCCR0 = 0;
TCCR0 = (0<TCNT0 = T_POLL;
TIFR |= (1<TIMSK |= (1<TCCR0 |= (1<

/*initialize the rest of the peripherals*/
DDRB |= (1<

Enable_interrupt();
while(1);

/*interrupt handler T0
by overflow event*/
#pragma vector = TIMER0_OVF_vect
__interrupt void TimerT0Ovf(void)
{
/*overwriting the counting register*/
TCNT0 = T_POLL;

/*poll button*/

/*inversion of PB0 for debugging*/
PORTB ^= (1<

OC0 output control

In Normal mode, timer T0 can change the state of the OC0 pin when the counting register and the comparison register match. And even without interruptions. Control options are determined by bits COM01 and COM00 of the TCCR0 register.

Here is an example of a program that generates a square wave at pin OC0.

#include
#include

int main(void)
{
/*initialize timer T0*/

TCCR0 = 0;
TCCR0 = (0<TCNT0 = 0;
OCR0 = 0;
TIMSK = 0;
TCCR0 |= (1<

/*initialize OC0*/
DDRB |= (1<

While(1);
return 0;
}

The OS0 pin will change its state to the opposite when the counting register is zero.

A few points about using the timer

The timer interrupt handler (and any other peripheral) should be made as short as possible.

If the calculated value for the counting register (or comparison register) is rounded, the time interval will be counted by the timer with an error.

And one last thing. It may happen that the processing of a timer interrupt is delayed (for example, due to the fault of another handler) and the TCNT0 register will already count several clock cycles. If you simply overwrite the value of TCNT0, then the next interrupt will be called later than necessary. It turns out that the previous (delayed) and new interrupts will not withstand the required interval.

This situation can be smoothed out if you overwrite the counting register like this:

TCNT0 = TCNT0 + startValue;

Adding the current value of the counting register with the initialized one will take into account these extra clock cycles.There is really one BUT! If startValue is large, the addition operation may cause the counter register to overflow.

For example, startValue = 250, and the timer managed to count to 10. Then the addition operation will lead to the following result:

10 + 250 = 260

We take 8 digits from 260 and get 4. 4 is written to TCNT0.


Timers and counters for AVR microcontrollers (real time clock). AVR Lesson 7

When I was still starting to study microcontrollers, I wanted to do so. Honestly, I wanted to try to turn on the TV only from 7 to 8 o'clock, and the rest of the time it should have been turned off. I made the device, but never used it...

All AVR microcontrollers have several built-in timers. They can also be divided into general-purpose timers and a watchdog timer, which is designed to reboot the MK when it freezes.

General purpose timers can:

  • Clock from external clock quartz at 32768 hertz
  • Count different time intervals
  • Count external pulses in counter mode
  • Generate a PWM signal at certain MK pins
  • Generate interrupts for some event, for example, when there is an overflow

Counter timers can be clocked from an internal clock generator and from a counting input. Let's look at the functionality of timer-counter 1 in the atmega8 microcontroller. Launch CodeVision AVR, create a new project and agree to the offer to launch Code WizardAVR

Let's use timer2 as an example to implement a real-time clock with output to an LCD display; for this we set the timer as shown in the screenshot

here we set the external clock source for the timer, as an external source we will use a clock quartz at 32768 hertz, then we will set the prescaler to 128, that is, the timer will operate at a frequency of 32768/128=256, and the counting register is 8-bit (maximum number 255), it turns out that it will overflow once per second, then we check the box next to Overflow interrupt and click on file->Generate, save and exit.

Code Wizard generated the following code:

#include // Timer2 overflow interrupt service routine interrupt void timer2_ovf_isr(void) ( ) void main(void) ( // Input/Output Ports initialization // Port B initialization PORTB=0x00; DDRB=0x00; // Port C initialization PORTC=0x00; DDRC=0x00; // Port D initialization PORTD=0x00; // Timer/Counter 0 initialization // Clock source: System Clock // Timer 0 Stopped TCCR0=0x00; /Counter 1 initialization // Clock source: System Clock // Clock value: 125,000 kHz // Mode: Fast PWM top=00FFh // OC1A output: Discon. // OC1B output: Discon. // Noise Canceler: Off // Input. Capture on Falling Edge // Timer1 Overflow Interrupt: Off // Input Capture Interrupt: Off // Compare A Match Interrupt: Off // Compare B Match Interrupt: Off TCCR1B=0x0A; ICR1H=0x00; OCR1AH=0x00; OCR1BH=0x00; // Timer/Counter 2 initialization // Clock value: PCK2/128 // Mode: Normal top=FFh // OC2 output: Disconnected ASSR=0x08; TCCR2=0x05; TCNT2=0x00; OCR2=0x00; // External Interrupt(s) initialization // INT0: Off // INT1: Off MCUCR=0x00; // Timer(s)/Counter(s) Interrupt(s) initialization TIMSK=0x40; // Analog Comparator initialization // Analog Comparator: Off // Analog Comparator Input Capture by Timer/Counter 1: Off ACSR=0x80; SFIOR=0x00; // Global enable interrupts #asm("sei") while (1) ( ); )

#include #include // Alphanumeric LCD Module functions #asm .equ __lcd_port=0x12 ;PORTD #endasm #include unsigned char second=0; //variable for storing seconds unsigned char minute=0; //variable for storing minutes unsigned char hour=0; //variable for storing hours char lcd_buffer; //variable buffer for display output // Timer2 overflow interrupt service routine interrupt void timer2_ovf_isr(void) ( if (++second==59) //increase the number of seconds by 1 and check the equality 59 (second = 0; if (+ +minute==59) (minute = 0; if (++hour==59) ( hour = 0; ) ) lcd_clear(); // clear the display before output lcd_gotoxy(0,0); point x=0 y=0 sprintf(lcd_buffer,"%i:%i:%i",hour,minute,second); // create a line for output lcd_puts(lcd_buffer); // display the line ) void main( void) ( // Timer/Counter 2 initialization // Clock source: TOSC1 pin // Clock value: PCK2/128 // Mode: Normal top=FFh // OC2 output: Disconnected ASSR=0x08; TCCR2=0x05; TCNT2=0x00 ; OCR2=0x00; // Timer(s)/Counter(s) initialization TIMSK=0x40; // LCD module initialization lcd_init(16); ) ( )

The program is ready, now let's create a diagram in Proteus

New on the site

>

Most popular