Three-channel thermostat, thermostat, timer on ATmega8. Kitchen timer on ATmega8 Description of timer operating modes

01.11.2023 Water heaters

This device will be useful to anyone who has to cook constantly, and will reliably protect your kitchen from excessive amounts of smoke. This device, as the name suggests, is a kitchen timer. It is designed to count intervals when preparing various dishes. The timer has several keys with which you can easily set the time from 1 minute to 99 hours. The countdown starts automatically 3 seconds after setting the time. Thanks to the loud beeper, you will definitely hear when the dish is ready. The device is assembled based on the ATMega8 microcontroller.

Description of design

The heart of the device is the microcontroller U1 () with a quartz resonator X1 (16 MHz) and a string of two capacitors C1 (22 pF) and C2 (22 pF). Stabilizer U2 () with capacitors C3 (100 µF) and C4 (47 µF) stabilizes the 5V supply voltage necessary for the correct operation of the microcontroller and related components. A voltage of 7-12V is supplied to the Zas connector. If you have a power supply with a voltage of 5-6V, you can exclude the voltage stabilizer from the circuit. Buzzer B1 and display anodes W1 are controlled by transistors T1 - T5 (), with resistors R1 - R8 (3.3 kOhm), R17 (3.3 kOhm) and R18 (3.3 kOhm). Resistors R9 - R16 (330 ohms) limit the current through the display segments. The Prog connector and one R pin are used to connect the programmer. The timer keyboard is connected to the Sw connector.

Manufacturing

A drawing of the printed circuit board for the device is in the archive at the end of the article. Installation of parts begins with soldering two jumpers. Then all resistors and other elements are installed in order from smallest to largest. Quartz X1 must be “low” - it is installed under the display - otherwise it simply will not fit there. The B1 buzzer can be soldered onto the board as shown in the photos, but it was later discovered that the sound was too quiet after closing the case (despite the holes drilled into the case). It is better to glue the buzzer to one side of the case (as shown in the last photo) and connect it with wires to the board. The keyboard consists of 5 non-latching 12x12mm buttons directly on the front of the case, so that their pushers are slightly above the surface of the case. For this device, it is good to use a phone charger as a power supply due to its light weight and size.

Source: cxem.net


This diagram is also often viewed:

This timer is designed to set shutter speeds from 5 seconds to 100 minutes. At its output there is a fairly powerful electromagnetic relay that allows you to switch current up to 30A at a voltage of 12V and current up to 10A at a voltage of 220V. Thanks to the use of an electromagnetic relay, the timer can control not only heating or lighting devices, but also electronic devices that are critical to the form of the AC supply voltage. Transformer power supply, in combination with a relay, provides complete galvanic isolation of the timer electronic circuit from the network.

To communicate between the timer and the operator, there is a four-digit LED indicator; it contains four very old 7-segment AL304 matrices, connected into a matrix by connecting together segment pins of the same name. Of course, you can use more modern LED indicators, and even ready-made four-digit matrices for dynamic display.

The timer is controlled by buttons S1, S2, S3, S4. When you press the S1 button, the load is turned on and the timer starts. To set the time during which the load should operate, you need to press S4. The two most significant digits (minutes) will flash on the display. Now you can use buttons S2 and S3 to set the minutes. Then you need to press S4 again. Now the low-order digits will flash and you can use the S2 and S3 buttons to set the seconds. To save the settings, press S4 again. Now the indicator will show the set shutter speed. To start the timer you need to press S1. The load is turned on, and the indicator readings begin to decrease. As soon as the set time has expired, “OFF” appears on the indicator, and the load is turned off by the electromagnetic relay. To repeat, press the S1 button twice. The first time you press “OFF” it will change to show the set time, and the second time the timer starts. Relay control via pin 23 D1. Switching on is a logical unit. The key on VT5 and VT6 controls the electromagnetic relay K1. Such relays are used in car alarm circuits. They can switch both direct current (12V) and alternating current (220V), since they have good insulation.

The power source is made using a low-power transformer. Since the secondary winding of the transformer is tapped from the middle (12-0-12), the rectifier is made not using a bridge, but using a full-wave circuit using two diodes VD2 and VD3. If the transformer has a 12V winding without a tap, then a rectifier bridge is needed. The relay is powered directly from the output of the rectifier, and the rest of the circuit through the 5V voltage regulator A1.

When flashing the firmware, you need to set it to work with an internal 8 MHz oscillator.

The circuit is assembled on a purchased prototype printed circuit board, on one side there is a microcircuit and other parts, and buttons and indicators on the other side. Power transformer outside the board.

KT315 transistors can be replaced with KT3102 or any analogues. The KT815 transistor can be replaced with KT817, KT604. Diode KD521 - almost any analogue. Diodes in the KD209 rectifier are any rectifier diodes for direct current not lower than 150 mA. The 7805 integrated stabilizer can be replaced with any 5-volt one, for example, KR142EN5A. Or make a stabilizer using a parametric circuit using two transistors and a 5V zener diode. Regarding indicators, it was said above. These can be any seven-segment indicators with a common anode (cathode).

Archive for the article "Timer on Atmega8 and LED indicators"
Description: Firmware files
File size: 5.58 KB Number of downloads: 4 319

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 a huge number of times! 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 terms, this is an intermediate link between the timer and the controller clock frequency. The prescaler makes our task easier by allowing us to divide the clock frequency by a certain number before feeding 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 the maximum value of the TCNT 1 register is reached. We will talk more about interrupts in the next lesson.

Register TCCR 1B is responsible for the configuration of timer 1. 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

The countdown timer will help you accurately measure the time interval in the range from 1 second to 24 hours.

Today you won’t surprise anyone with the design of the timer, because... There are as many similar devices on sale and on the Internet as you like. And all timers seem to be similar to each other. And when you begin to consider the functions of the circuit in more detail, you find some inconvenience in it for yourself.

For these reasons, I made a timer program that meets the following parameters:
– compact design and simple circuitry;
– operational push-button control;
– when controlling buttons, duplication of actions on the LCD;
– setting the time accurate to the second;
– counting range from 1 second to 24 hours;
– start, pause function;
– function of resetting the countdown and set time values;
– when the value 00.00.00 is reached, the actuator is turned on;

All assigned tasks were implemented in this project.

Lesson 10

Timers-counters. Interrupts

Today we will find out what it is timers-counters in microcontrollers and why they are needed, as well as what is interrupts and why they are also needed.

Timers-counters- these are devices or modules in a microcontroller that, as the name suggests, constantly count something. They count either up to a certain value, or up to a value equal to their bit depth. They constantly count at the same speed, with the speed of the microcontroller clock frequency, adjusted for frequency dividers, which we will configure in certain registers.

And these timer-counters constantly count if we initialize them.

Timers in MK Atmega8 three.

Two of them are eight-bit timers, that is, those that can count up to a maximum of only 255. This value will not be enough for us. Even if we use the maximum frequency divider, not only will we not be able to count a second, we will not even be able to count half a second. And our task is precisely this: to count up to 1 second in order to control the increase in the count of the LED indicator. You can, of course, also use increasing the variable to a certain value, but I would like a completely hardware calculation.

But there is one more timer - this is a full-fledged one 16-bit timer. He not only 16-bit, but it still has certain charms that other timers do not have. We will get acquainted with these options later.

It is this 16-bit timer that we will study and use today. Also, having become familiar with this timer, it will not cost you anything to independently study the operation of the other two, since they are much simpler. Nevertheless, in the future we will also consider 8-bit timers, since one timer will not be enough for us to achieve more complex tasks.

Now briefly about interruptions.

Interrupts (Interruptions) are mechanisms that interrupt the code depending on certain conditions or a certain environment that will dictate certain devices, modules and buses located in the microcontroller.

There are 19 types of interrupts in our Atmega8 controller. Here they are all in the table in the technical documentation for the controller

What type of conditions might there be? In our case, for example, the timer counted up to a certain value, or, for example, a byte and other conditions arrived on some bus.

At the moment we will process the interrupt, which is located in the table located above at position 7 - TIMER1 COMPA, called at address 0x006.

Now let's look at our 16-bit timer or TIMER1.

Here is its block diagram

We see a register there TCNTn, in which the number is constantly changing, that is, it is constantly increasing. In practice, this is a counter. That is, this register stores the number to which the timer has counted.

And to the registers OCRnA And OCRnB(the letters n are the timer number, in our case it will be 1) - these are the registers in which we enter the number with which the number in the TCNTn register will be compared.

For example, we entered some number into the OCRnA register and as soon as this number coincides with the value in the counting register, an interrupt will occur and we will be able to process it. Timers with interrupts are very similar to a regular delay in code, only when we are in a delay, then we cannot execute any code at that time (well, again figuratively “we”, in fact the ALU). And when the timer counts, the entire code of our program is quietly executed at this time. So we win enormously, not allowing the huge resources of the controller to be idle for a second or even half a second. At this time we can handle button clicks, which we can also handle in a timer and more.

There is also a TCCR register. This register is a control register. There, certain bits are configured that are responsible for the timer configuration.

The timer also has several modes, which we will also get acquainted with a little later.

It consists of two halves, since our controller is 8-bit and cannot have 16-bit registers. Therefore, the high part of the register is stored in one half of the register (and physically in one register), and the low part is stored in the other half. You can also call it a register pair consisting of two separate registers TCCR1A and TCCR1B. The number 1 means that the register belongs to timer 1.

This TCCR register is responsible for setting the divider so that the timer does not count so quickly, and it is also responsible (or rather, its certain bits) for setting a certain mode.

The WGM bits are responsible for setting the mode

We see a lot of different modes here.

Normal- this is normal mode, the timer counts until the end.

PWM- This PWM only different varieties, that is, the timer can play a role pulse width modulator. We will get acquainted with this technology in later lessons.

CTC- this is a reset by coincidence, just what we need. This is where the TCNT and OCR registers are compared. There are two such modes, we need the first, the second works with a different register.

We will not study all types of modes in this lesson. When we need these modes, then we’ll figure it out.

Well, let's not torment ourselves with documentation and finally try to enter something into some registers.

The code, as always, was created from a previous project. For Proteus, the code was also copied and renamed from the last lesson, and the path to the new firmware was indicated in the controller properties. We will name the projects Test07.

Let's try, as always, to compile the code and run it in Proteus. If everything works fine, then we start adding new code.

Let's add one more function, since we learned how to add functions in the last lesson. The function code will be placed after the segchar function and before the main function. Afterwards, due to the fact that we will call the segchar function inside our new function.

Moreover, we will create not one function, but two. We will place all the initialization code for our timer in one function, and the other function will be the timer interrupt handler, and such functions are specific and do not need to be called. When the need arises, they will call themselves depending on certain conditions that were specified above.

Therefore, we will call the first function timer_ini

//———————————————

voidtimer_ini( void)

{

}

//———————————————

Also, let’s separate our functions, as well as some complete blocks with the declaration of global variables, with function prototypes, from each other by these lines, which, due to the presence of two slashes in front, the compiler will not process and will take them as comments. Due to these delineations, we will see where one function ends and another begins.

This function, as we see, does not have any arguments - neither input nor return. Let's immediately call this function in the main() function

unsignedcharbutcount=0,butstate=0;

timer_ini();

Now we will begin to slowly fill this function with code.

Let's start with a timer control register, for example TCCR1B. Using our favorite “OR” operation, we will enter one into a certain bit of the register

voidtimer_ini( void)

TCCR1B|= (1<< WGM12);

From the commentary we see that we are working with mode bits, and we will set only the WGM12 bit from them, leaving the rest as zeros. Based on this, we configured this mode:

The timer also has the following register: TIMSK. This register is responsible for interrupt masks - Interrupt Mask. This register is available for all timers, not only for the first one, it is common. In this register we will set the bit OCIE1A, which will enable the type of interrupt we need TIMER1 COMPA

TCCR1B|= (1<< WGM12); // set the CTC mode (reset by coincidence)

TIMSK|= (1<< OCIE1A);

Now let's play with the comparison registers themselves OCR1A(H and L). To do this you will have to do a little math. Register OCR1AH stores the leading part of the number for comparison, and the register OCR1AL- the youngest.

But before we count, let’s write code with any values ​​of this register and then correct it, since we will then initialize the divisor and it will also participate in calculating the required counting time. Without a divider, the timer will count too quickly.

TIMSK|= (1<< OCIE1A); //set the interrupt enable bit of the 1st counter to coincide with OCR1A(H and L)

OCR1AH= 0b10000000;

OCR1AL= 0b00000000;

TCCR1B|= ( ); //set the divisor.

We are not installing any divisor yet, since we have not calculated it yet. Let's do this.

Currently in our register OCR1A the number 0b1000000000000000 is found, which corresponds to the decimal number 32768.

Our microcontroller operates, as we agreed, at a frequency of 8,000,000 Hz.

Divide 8,000,000 by 32,768 to get approximately 244.14. This is the frequency in hertz that our timer will operate at if we do not use a divider. That is, our numbers will change 244 times per second, so we won’t even see them. Therefore, you will need to use a timer frequency divider. Let’s choose a divider by 256. It’s just right for us, and then we’ll correct it exactly up to 1 Hz using the comparison number.

Here are the divisors for 1 timer:

I have highlighted the divisor we need in the table. We see that we only need to set the bit CS12.

Since our frequency divider is 256, we divide 8000000 by this divider, we get 31250, and this is the number we must enter in TCNT. Our timer will count up to this number to count to 1 second. The number 31250 is 0b0111101000010010 in binary representation. Let's put this number in the register pair, and also apply the divisor

OCR1AH= 0b 01111010 ; //write a number to the register for comparison

OCR1AL= 0b 00010010 ;

TCCR1B|= (1<< CS12 ); //set the divisor.

That's it with this function.

Now the next function is a coincidence timer interrupt handler. It's written like this

ISR( TIMER1_COMPA_vect)

{

}

And the body of this function will be executed itself upon the occurrence of a coincidence of numbers.

We will need a variable. Let's declare it globally, at the beginning of the file

#include

//———————————————

unsignedchari;

//———————————————

Accordingly, we will remove the same variable from the code in the main() function

intmain( void)

unsignedchari;

Let's also comment out all the code in the infinite loop. Its role will now be played by a timer, and I think it will cope with this just as well, and even better, without interfering with “anyone”.

while(1)

{

// for(i=0;i<10;i++)

// {

// while (butstate==0)

// {

// if (!(PINB&0b00000001))

// {

// if(butcount< 5)

// {

//butcount++;

// }

//else

// {

// i=0;

//butstate=1;

// }

// }

//else

// {

// if(butcount > 0)

// {

//butcount—;

// }

//else

// {

//butstate=1;

// }

// }

// }

// segchar(i);

// _delay_ms(500);

//butstate=0;

// }

Now, actually, the body of the handler function. Here we will call the segchar function. Then we will increase by 1 variable i. And so that it does not go beyond a single digit number, we will reset it to zero under this condition

ISR( TIMER1_COMPA_vect)

if( i>9) i=0;

segchar( i);

i++;

Now let's slightly correct the code at the beginning of the main() function. Port D, which is responsible for the state of the segments, we will set it to ones so that when we turn it on, the indicator does not light up, since it has a common anode. Then we'll put the number 0 into the global variable i here, just for order. In general, as a rule, at startup, uninitialized variables are always zero. But we will still initialize it. And, most importantly, for the timer interrupt to work, it is not enough to include it in the initialization of the timer. Also, in general, for all interrupts to work, global interrupts must be enabled. There is a special function for this sei() — Set Interrupt.

Now the code will be like this

DDRB= 0x00;

PORTD= 0b 11111111 ;

PORTB= 0b00000001;

i=0;

sei();

while(1)

We also have to include the interrupt library file at the beginning of the file

#include

#include

#include

Also, we won’t need variables for the button yet, since we won’t be working with the button today. Let's comment them out

intmain( void)

//unsigned char butcount=0, butstate=0;

timer_ini();

Let's assemble our code and check its performance first in Proteus. If everything works fine, then we’ll also check it in the live circuit

Everything works for us. Great!

This is how the stopwatch turned out. But since we don’t even have a quartz resonator, this stopwatch cannot be called accurate.

Nevertheless, today we learned a lot. We learned about interrupts, we also learned how to process them, we learned how to work with timers, configure several new microcontroller registers, before that we only worked with port registers. Also, due to all this, we have significantly relieved the arithmetic-logical device of our microcontroller.

Watch VIDEO TUTORIAL

Post Views: 17,258