Friday, 15 February 2013

Varying bulb luminosity using ATMEGA16


We know that on the market there are plenty of devices that replace normal wall switches and let you adjust the luminosity of a bulb. But if you want to make an "intelligent" home or a more complicated device to adjust the power of a light bulb what possibilities do you have?

1. If you are using DC , than it's simple: just use a switching element ( a BJT or a MOSFET ). You just apply PWM (Pulse Width Modulation)  and vary the duty cycle from  0% to 99% and you can adjust the luminosity of a lightbulb or LED or you can set the speed of a motor.

2. If  you are using AC .... well you can do the  following:
       2.a. Use a rectifier to obtain DC from AC, filter it and apply what was written at 1.
       2.b.1 Use "something" to turn on and off AC.

If I would use a BJT to implement my application, I would have to consider that in the reverse active region the behavior is different from the forward active region.

Because the application will be used at 50Hz(low frequency) to "drive" a light bulb (small load), and the maximum necessary current that will pass through it will be under 4 - 5 Amps, I have chosen to use a TRIAC. The same application can be easily modified(add some diodes) and can be used to drive high power loads(motors, heating elements, etc) using Thyristors.

In this article, to keep it simple and effective,  I will present how to fire a TRIAC  using an ATMEGA16.


The following schematics will be used:


We can see that the schematics consists of 6 blocks.
1. Separation block(transformer)
2. Rectifier block
3. Voltage source block
4. 0 crossing detector block
5. Triac block
6. Computational block

1. The galvanic separation is used for safety. The transformer takes the 230V AC from the mains and gives on the output a voltage between 8 to 24V(which is safe tot touch).
This block can be replaced with a group of resistors, capacitors, diodes. IT IS DANGEROUS TO HAVE THE MAINS GO DIRECTLY TO THE MICROCONTROLLER.

2. Use the voltage from the secondary winding of the transformer to power up the uC, and also to detect de 0 crossings of the mains. To power up the uC we need DC, so we take the AC from the transformer and after rectifying(which include a bridge rectifier and a capacitor for filtering) block we obtain DC voltage

3. The uC uses 5V. We take the DC obtained at 2, use it as an input for the 7805 integrated circuit. At the output of the 7805 we will obtain 5V stabilized. We will use this voltage to power up the uC.

4. Zero crossing detector - it's just an opto-coupler. Internally, a diode lights up and turns on or off a photo-transistor which pulls the output to GND. When the transistor is turned off , the output is pulled to VCC by the pull-up resistor.

5. When the output of the uC is HIGH, transistor Q0 is in saturation and the diode inside the opto-triac is lighten and the BT136 TRIAC is fired (it conducts current from A1 to A2).

6. The computational block consists of an ATMEGA16 uC used to run a small program. We use the external interrupt pin INT1 (PD3) and an output(PD7).


#include <avr/io.h>
#define F_CPU 16000000
#include <util/delay.h>

ISR ( INT1_vect )
    _delay_ms( 1 );
    TCCR1B |= ( 1 << CS11 );

    PORTD |= ( 1 <<  PD7 ) ;
    TCCR1B &= ~ ( 1 << CS11 ) ;
    TCNT1 = 0 ;
    TCCR0 |= ( 1 << CS01 );

    PORTD &= ~ ( 1 << PD7 ) ;
    TCCR0 &= ~ ( 1 << CS01 ) ;
    TCNT0 = 0 ;
int main()
    DDRD = ( 1 << 7 ) ;
    PORTD |= ( 1 << PD7 ) | ( 1 << PD3 ) ;
    MCUCR  |= ( 1 << ISC10 ) ;
    GICR |= ( 1 << INT1 ) ;
    TCCR1B = 0x00;
    TCCR1A = 0x00;
    TCCR1B |= ( 1 << WGM12 ) ;
    TIMSK |= ( 1 << OCIE1A ) ;
    OCR1A = 0X09FF;
    TCCR0 = 0x00;
    TCCR0 |= ( 1<<WGM01 ) ;
    TIMSK |= ( 1 << OCIE0 ) ;
    OCR0 = 0XFF;
    return 0;


ISR- means interrupt service request. It is a function that it's called when an interrupt occurs.
TIMER - internal structure of the microcontroller that counts. In our case the timer works in CTC "Clear Timer on Compare" mode. When TCNT(timer register that is incremented with every tick) is equal to OCR an interrupt is generated.

1 << X means 1 shifted to left with X bits
example: 1<<0 means 00000001
and 1<<7 means 10000000

In the main function we make the initialisation for the code to work(initialise the timers, set the interrupts and then wait for something to happen).

When the sinus signal reaches almost 0V, in the detection block a rising or falling edge will be generated. This edge, aplied to PD3(INT1) pin generate an interrupt. When the interrupt appears, the function  ISR ( INT1_vect ) is called. It waits for 1ms(for the voltage to go down to 0 ) and then enables Timer 1 to start counting.the time for the delay until the trigger signal for the opto-triac will be given.
When Timer 1 generates an interrupt(it has finished counting), PD3 pin is set to TRUE(5V) and the trigger signal is sent to the opto-triac which fires the triac that powers on the load (light bulb). Timer 1 is turned off and timer 0 starts counting.
When Timer 0 generates an interrupt(it has finished counting), it means the trigger signal must end , PD3 is set to FALSE(0V) and timer 0 stops counting.

The program will be enhanced in the future to receive the data from USART or ADC and to be set dynamically using an algorithm. Right now, everything is static. This is just a Proof Of Concept.

PCB - it's easy to design, I had no design, just used one board with ATMEGA16 and components soldered in air to it.


Trigger signal after 0 crossing with delay:

Trigger signal and signal on the load:

Bulb at low luminosity and signal with trigger for that:

If you want to drive a motor instead of a light bulb, no problem, but you have to use a snubber circuit to protect the TRIAC from damaging because of the inductive load or from auto-triggering.
Also, it's good practice to make the length of the signal until the sinus reaches 0, but it is a little tricky(it is not included in the code). You have to adjust timer 1 for that.
The same stuff can be done with ARDUINO, which uses Atmel microcontrollers and the code can be written in the same stile or using ARDUINO IDE ( I prefer using the presented style, and defenetly not Arduino IDE).

THIS IS MY INTELLECTUAL PROPERTY, if you want to borrow photos or information please ask first.

Article about thyristor:
Article about the TRIAC:
ATMEGA16 Datasheet: 
Hope you like it and it's useful.
If you have something to say (feedback - negative or positive , don't hesitate to post it).


  1. Congratulations, I waiting episode 2

  2. complicated article that seemed to me difficult to understand...
    MRO Supply com.

  3. When Timer 1 generates an interrupt(it has finished counting), PD3 pin is set to TRUE(5V) ..
    >>>>>>>>>>it should be PD7 not PD3
    When Timer 0 generates an interrupt PD3 is set to FALSE(0V) >>>PD7 NOT PD3