# Verification of C program for 8051 required (Phase angle control of AC motor using TRIAC)

#### Vedu2611

Joined Aug 3, 2017
26
CSS:
hi
Hi everyone,

So I am working on a project that aims to control the speed of a single phase induction motor, the ones that you find in your home fans.

Entire idea is to create an low cost IR remote with buttons for ON-OFF, but let's not bring that up now.

Stage one of the project is getting a microcontroller to get your tasks (of increasing/decreasing as well as turning on/off) programmed.

Okay so my progress so far:
1) Used Proteus to build the circuit (attached below) for Simulation
2) Wrote the C program (also attached below)
3) Compiled it using Keil Uvision 5

The problem is that while the compiler gave no errors, I am unable to see the results as required.

I'm pretty sure the fault is with the code.

Kindly have a look and revert if you find anything.

Best Regards,
Vedant.

Mod edit: add source code with code tags.
Edit1: As suggested by Mod, added source code with code tags

Code:
#include<reg51.h>

//project is to control speed of single phase AC motor operating at 230V, 50Hz

unsigned int speed;    // for the 4 speeds : 0(switched off),1,2,3 and 4(max, no delay)

code unsigned char timer_h[3]={0xe8,0xf0,0xf8};
code unsigned char timer_l[3]={0x8f,0x5f,0x2f};

//delay value calculations
//Supply frequency= 50Hz, time period=20ms
//        one step takes apprx. 1us to be executed. Therefore
//  speed=3 2ms (10%delay) = 2000us = 2000 steps = load F82F in timer
//    speed=2 4ms (20%delay) = 4000us = 4000 steps = load F05F in timer
//    speed=1 6ms (30%delay) = 6000us = 6000 steps = load E88F in timer

void delay_ms (unsigned int);

sbit  led1 = P3^7;
sbit  led2 = P3^6;
sbit s1 = P1^0;              //switch one for ON/OFF operation
sbit s2 = P1^1;              //switch two for increasing speed
sbit s3 = P1^2;              //switch three for decreasing speed
sbit output =P0^0;

bit flag=0;

void decrease(void)          // subroutine for decreasing speed
{
if(speed>0)
{speed--;
}
//else, message = Can't reduce speed further
}

void increase(void)           // subroutine for increasing speed
{
if (speed<2)
{speed++;
}
//else, message = Can't increase speed further
}

void int0(void) interrupt 0          // external interrupt 0 subroutine
{                                  // Called whenever zero crossing detected

if(flag==1)                         //indicates that Switch 1 has been pressed
{                             // to turn on

if(speed==0)                 //Motor turned on but speed still to be kept 0
{                         // Off the triac permanently
output=1;

}
else if(speed==4)             //Motor turned on and speed is to be made max
{                         // on the triac permanently
output=0;

}
else
{                          //indicates either SW2 or SW3 has been pressed
output=1;
TL0 = timer_l[speed-1];    //setting new speed to variables
TH0 = timer_h[speed -1];
TR0 = 1;                      //starting timer again
}
}
else
output=1;

}

void timer0 (void) interrupt 1           //called whenver timer operation finishes or TF=1
{
TR0=0;                               //stop timer
output=0;                           //fire triac
}

void int1(void) interrupt 2      // external interrupt 1 subroutine
{                              //happens when button is pressed

if(s1==0)                       // for first key
flag=~flag;                     // on/off operation

else if(s2==0)                  // for second key
{increase();                    // increase phase angle decrease power
}

else if(s3==0)                  // for third key
{decrease();                    // decrease phase angle increase power
}

delay_ms(1);                    // 1 ms delay
}

void delay_ms (unsigned int j)
{    int i;
for(i=j*250;i>0;i--);            //approximate delay in milliseconds
}                                //considering 1 iteration in FOR loop takes 4 steps i.e.
//four microseconds

void main(void)
{
TMOD=0x01;                                   // initialize timer0 as 16 bit timer
IT0=IT1=1;                                   // making edge trigger
EX0=EX1=ET0=1;                                  // enable interrupts of timer0 and external interrupt 0 & 1
EA=1;
TL0=TH0=TR0=TF0=0;

if(flag==0)
led2=1;
else
led2=0;                                         //Glow LED if Motor is turned ON
}

#### Attachments

• 28.6 KB Views: 30
• 3.4 KB Views: 20
Last edited:

#### Vedu2611

Joined Aug 3, 2017
26
Oh and I forgot to mention... Kindly ignore the serial communication part... I will incorporate that later on.

#### JohnInTX

Joined Jun 26, 2012
3,824
What exactly is is not doing correctly? It's hard to know where to start looking without knowing that.

I do notice that you seem to be turning the off the gate drive to the triac after the zero crossing is detected (pulse = 1 in interrupt 0 before loading the timers). That would maybe re-trigger the triac at zero-crossing instead of after the timer runs out. You may need to just pulse the triac (after the zero crossing delay) so that it will turn off by itself at the next zero crossing.

An overall assessment is that you are doing way too much in the interrupt routines and blocking delays are not a good idea in an interrupt service. When I do something like this I do the calculations, speed adjustment etc. in main and then post values to use at the next zero-crossing. For example, compute the phase angle time in main then post that value in a variable. At zero crossing, simply load the pre-computed timer delay for the phase angle into a timer then return. When that timer runs out, its interrupt routine pulses the triac to fire it then exits. To turn it completely off, just disable the timer interrupt enable. Your interrupt routines become quick and simple. You also only have to compute the values once for a given speed setting. Do all of the changes i.e. button-sensing, speed adjust etc. in main at your leisure.

Consider adding more comments i.e. pulse=0 should indicate that it triggers the triac etc.

Good post, though! Nice to have all the code and schematic too.

Good luck!

Last edited:

Joined Jul 18, 2013
19,194
When I do something like this I do the calculations, speed adjustment etc. in main and then post values to use at the next zero-crossing. For example, compute the phase angle time in main then post that value in a variable. At zero crossing, simply load the pre-computed timer delay for the phase angle into a timer then return. When that timer runs out, its interrupt routine pulses the triac to fire it then exits. To turn it completely off, just disable the timer interrupt enable.
That is about the same way I have done it also in the past, just that it was in assembly.
Max.

Joined Jul 18, 2013
19,194
So I am working on a project that aims to control the speed of a single phase induction motor, the ones that you find in your home fans.
Stage one of the project is getting a microcontroller to get your tasks (of increasing/decreasing as well as turning on/off) programmed.
Once I get a bit of spare time I am looking at a similar project but using burst firing for control to reduce the sometimes annoying 120 hz buzz when using phase angle control on shaded pole motors.
Max.

#### JohnInTX

Joined Jun 26, 2012
3,824
Once I get a bit of spare time I am looking at a similar project but using burst firing for control to reduce the sometimes annoying 120 hz buzz when using phase angle control on shaded pole motors.
Max.
What is burst firing?

Joined Jul 18, 2013
19,194
What is burst firing?
Per Microchip AN958.
Also typical control of an element or heating device.
Max.

#### JohnInTX

Joined Jun 26, 2012
3,824
Thanks, Max. AKA time-proportional control.

Joined Jul 18, 2013
19,194
Yes, zero switching only, no phase angle control.
Max.

#### Vedu2611

Joined Aug 3, 2017
26
Hi @JohnInTX,

I've added the source code tags - Kindly have a look at it now; I've made sure to make it as easy to follow as possible.

What exactly is is not doing correctly? It's hard to know where to start looking without knowing that.
Well, I used this code with microcontroller AT89C51 in Proteus and simulated the result; but I didn't get the desired output. The triac never got fired and the output of the micro(connected to the negative of triac) was always at +Vcc.

The auxiliary circuit works pretty much fine, satisfactorily generating zero crossing pulse as well as constant 5V DC.

I do notice that you seem to be turning the off the gate drive to the triac after the zero crossing is detected (pulse = 1 in interrupt 0 before loading the timers). That would maybe re-trigger the triac at zero-crossing instead of after the timer runs out. You may need to just pulse the triac (after the zero crossing delay) so that it will turn off by itself at the next zero crossing.
Alright, I did change those and made the code much simpler. Have a look now!

An overall assessment is that you are doing way too much in the interrupt routines and blocking delays are not a good idea in an interrupt service. When I do something like this I do the calculations, speed adjustment etc. in main and then post values to use at the next zero-crossing. For example, compute the phase angle time in main then post that value in a variable. At zero crossing, simply load the pre-computed timer delay for the phase angle into a timer then return. When that timer runs out, its interrupt routine pulses the triac to fire it then exits. To turn it completely off, just disable the timer interrupt enable. Your interrupt routines become quick and simple. You also only have to compute the values once for a given speed setting. Do all of the changes i.e. button-sensing, speed adjust etc. in main at your leisure.
Okay but is that the reason why the circuit isn't working? Because I think that now that I have modified it; it seems good enough...

Good post, though! Nice to have all the code and schematic too.

Good luck!
Thanks for the help, much, MUCH appreciated!

Best Regards,
Vedant

#### JohnInTX

Joined Jun 26, 2012
3,824
I am traveling so this will be a bit short. Don't think anything negative..

There is no while(1) loop in main so the code will restart over and over. All embedded systems need a whole loop in main to keep running.
Your interrupt 0 routine should pulse the triac gate to trigger it. As I indicated earlier, if you wait until zero-crossing to remove the gate drive it may be too late and the triac may be re-triggered right there.
What is the delay in the interrupt 2 routine for? Whatever it is, get rid of it. Interrupts by their nature need to be fast with minimal processing.
I would not do the switch sampling in an interrupt. It's a common misconception that you need an interrupt to look for switch changes - you don't. A far better way to do it is to sample and debounce the switches in main, compute the resulting timer setting land post it in a global variable - all outside of the interrupts. With that while loop in main, you will loop around anyway so why not check the switches there and compute the speed at your leisure?

For full speed, set a flag that tells the zero-crossing interrupt to trigger immediately rather Han use the timer. For OFF, turn off the interrupts entirely.

I would do that and have only two interrupt routines:
1- at zero crossing, check full-speed timer and load the phase-delay timer or trigger immediately.
2- enabled only at less than full speed-when the timer runs out and interrupts, pulse the gate drive on then off to trigger the triac - done.

Post any edited code in a new post so we have something to compare it to.

Boarding.. good luck!

Last edited:

#### jayanthd

Joined Jul 4, 2015
902
Where is the Zero Cross detector ? I have done AC Phase Angle Control using Triacs. I can help with the code if you use PIC12F1840 (8 pin PIC).

Last edited: