How to Generate PWM in 8051 Microcontroller

Hello everyone, hope you all are fine and having fun with your lives. In today's post, I am going to share How to generate PWM in 8051 Microcontroller. PWM is an abbreviation of Pulse Width Modulation and is used in many engineering projects. It is used in those engineering projects where you want an analog output. For example, you want to control the speed of your DC motor then you need a PWM pulse. Using PWM signal you can move your motor at any speed from 0 to its max speed. Similarly suppose you wanna dim your LED light, again you are gonna use PWM pulse. So, in short, it has numerous uses. If you are working on Arduino then you should read How to use Arduino PWM Pins.

PWM, as the name suggests, is simply a pulse width modulation. We take a pulse and then we modulate its width and make it small or big. Another term important while studying PWM is named duty cycle. The duty cycle shows the duration for which the PWM pulse remains HIGH. Now if the pulse remains high for 50% and LOW for 50% then we say that PWM pulse has a duty cycle of 50%. Similarly, if the pulse is HIGH for 70% and Low for 30% then it has a duty cycle of 70%.

Most of the microcontrollers have special pins assigned for PWM as in Arduino UNO it has 6 PWM pins on it. Similarly, PIC Microcontrollers also have PWM pins but unfortunately, the 8051 Microcontroller doesn't have this luxury means there are no special PWM pins available in 8051 Microcontroller. But PWM is necessary so we are going to manually generate the PWM pulse using Timer0 interrupt. So, before reading this tutorial you must first read How to use Timer Interrupt in 8051 Microcontroller so that you understand the functioning of Timer Interrupt. Anyways, let's get started with the generation of PWM in the 8051 Microcontroller.

Where To Buy?
No.ComponentsDistributorLink To Buy
18051 MicrocontrollerAmazonBuy Now

How to Generate PWM in 8051 Microcontroller ???

  • You can download both the simulation and the programming code for PWM in 8051 Microcontroller by clicking the below button:
Download PWM Code & Simulation
  • First of all, design a simple circuit as shown in the below figure:
  • Now what we are gonna do is we are gonna generate a PWM pulse using timer0 interrupt and then we are gonna send it to P2.0.
  • I have attached an oscilloscope on which we can easily monitor this PWM pulse and can check whether it's correct or not.

Code in Keil uvision 3

  • Now, copy the below code and paste it into your Keil uvision software. I have used Keil uvision 3 for this code compiling.
#include<reg51.h>

// PWM_Pin
sbit PWM_Pin = P2^0;		   // Pin P2.0 is named as PWM_Pin

// Function declarations
void cct_init(void);
void InitTimer0(void);
void InitPWM(void);

// Global variables
unsigned char PWM = 0;	  // It can have a value from 0 (0% duty cycle) to 255 (100% duty cycle)
unsigned int temp = 0;    // Used inside Timer0 ISR

// PWM frequency selector
/* PWM_Freq_Num can have values in between 1 to 257	only
 * When PWM_Freq_Num is equal to 1, then it means highest PWM frequency
 * which is approximately 1000000/(1*255) = 3.9kHz
 * When PWM_Freq_Num is equal to 257, then it means lowest PWM frequency
 * which is approximately 1000000/(257*255) = 15Hz
 *
 * So, in general you can calculate PWM frequency by using the formula
 *     PWM Frequency = 1000000/(PWM_Freq_Num*255)
 */
#define PWM_Freq_Num   1	 // Highest possible PWM Frequency


// Main Function
int main(void)
{
   cct_init();   	       // Make all ports zero
   InitPWM();              // Start PWM
 
   PWM = 127;              // Make 50% duty cycle of PWM

   while(1)                // Rest is done in Timer0 interrupt
   {}
}

// Init CCT function
void cct_init(void)
{
	P0 = 0x00;   
	P1 = 0x00;   
	P2 = 0x00;   
	P3 = 0x00;  
}

// Timer0 initialize
void InitTimer0(void)
{
	TMOD &= 0xF0;    // Clear 4bit field for timer0
	TMOD |= 0x01;    // Set timer0 in mode 1 = 16bit mode
	
	TH0 = 0x00;      // First time value
	TL0 = 0x00;      // Set arbitrarily zero
	
	ET0 = 1;         // Enable Timer0 interrupts
	EA  = 1;         // Global interrupt enable
	
	TR0 = 1;         // Start Timer 0
}

// PWM initialize
void InitPWM(void)
{
	PWM = 0;         // Initialize with 0% duty cycle
	InitTimer0();    // Initialize timer0 to start generating interrupts
					 // PWM generation code is written inside the Timer0 ISR
}

// Timer0 ISR
void Timer0_ISR (void) interrupt 1   
{
	TR0 = 0;    // Stop Timer 0

	if(PWM_Pin)	// if PWM_Pin is high
	{
		PWM_Pin = 0;
		temp = (255-PWM)*PWM_Freq_Num;
		TH0  = 0xFF - (temp>>8)&0xFF;
		TL0  = 0xFF - temp&0xFF;	
	}
	else	     // if PWM_Pin is low
	{
		PWM_Pin = 1;
		temp = PWM*PWM_Freq_Num;
		TH0  = 0xFF - (temp>>8)&0xFF;
		TL0  = 0xFF - temp&0xFF;
	}

	TF0 = 0;     // Clear the interrupt flag
	TR0 = 1;     // Start Timer 0
}

  • I have added the comments in the above codes so it won't be much difficult to understand. If you have a problem then ask in the comments and I will resolve them.
  • Now in this code, I have used a PWM variable and I have given 127 to it as a starting value.
  • PWM pulse varies from 0 to 255 as it's an 8-bit value so 127 is the mid-value which means the duty cycle will be 50%.
  • You can change its value as you want it to be.

Proteus Simulation Result

  • So, now when you upload the hex file and run your simulation then you will get below results:
  • Now you can check in the above figure that the duration of HIGH and LOW is the same means the pulse is HIGH for 50% and LOW for the remaining 50% cycle.
  • Now let's change the PWM duty cycle to 85 which is 1/3 and it will generate a PWM pulse of 33% duty cycle. Here's the result:
  • Now you can easily compare the above two figures and can get the difference. In the above figure now the duty cycle has decreased as the HIGH timing of the pulse is now reduced to 1/3 and pulse is LOW for 2/3 of the total time.
That's all, for today. That's how we can generate PWM in 8051 Microcontroller. Will meet you guys in the next tutorial. Till then take care !!! :)

Interrupt Based Digital Clock with 8051 Microcontroller

Hello friends, hope you all are fine and having fun with your lives. In today's post, I am going to share Interrupt based Digital clock with 8051 Microcontroller. In the previous post, I have explained in detail How to use Timer Interrupt in 8051 Microcontroller. We have seen in that post that we can use two timers in 8051 Microcontroller which are Timer0 and Timer1. Using these timers we can easily generate interrupts. So, before going into details of this post, you must read that timer post as I am gonna use these timer interrupts in today's post.

After reading this post, you will also get the skilled hand on timer interrupt and can understand them more easily. In today's post, I am gonna design a digital clock which will increment after every one second and we will calculate this one second increment using timer interrupt. This clock will be displayed on LCD so if you are not familiar with LCD then must read Interfacing of LCD with 8051 Microcontroller. You can also implement this digital clock with any other microcontroller like Arduino or PIC Microcontroller but today we are gonna implement it on 8051 Microcontroller. The complete simulation along with code is given at the end of this post but my suggestion is to design it on your own so that you get most of it. Use our code and simulation as a guide. So, let's get started with Interrupt based Digital clock with 8051 Microcontroller. :)

Interrupt Based Digital Clock with 8051 Microcontroller

  • First of all, design a circuit as shown in below figure:

  • Now use the below code and get your hex file. I have designed this code in Keil uvision 3 compiler for 8051 Microcontroller.
#include<reg51.h>

//Function declarations
void cct_init(void);
void delay(int);
void lcdinit(void);
void WriteCommandToLCD(int);
void WriteDataToLCD(char);
void ClearLCDScreen(void);
void InitTimer0(void);
void UpdateTimeCounters(void);
void DisplayTimeToLCD(unsigned int,unsigned int,unsigned int);
void WebsiteLogo();
void writecmd(int);
void writedata(char);

//*******************
//Pin description
/*
P2.4 to P2.7 is data bus
P1.0 is RS
P1.1 is E
*/
//********************

// Defines Pins
sbit RS = P1^0;
sbit E  = P1^1;

// Define Clock variables
unsigned int usecCounter = 0;
unsigned int msCounter   = 0;
unsigned int secCounter  = 0;
unsigned int minCounter  = 0;
unsigned int hrCounter   = 0;



// ***********************************************************
// Main program
//
void main(void)
{
   cct_init();             // Make all ports zero
   lcdinit();              // Initilize LCD
   InitTimer0();           // Start Timer0
  // WebsiteLogo();			
	while(1)
	{
		if( msCounter == 0 )                                       // msCounter becomes zero after exact one sec
		{
			DisplayTimeToLCD(hrCounter, minCounter, secCounter);   // Displays time in HH:MM:SS format
		}

		UpdateTimeCounters();                                      // Update sec, min, hours counters
  	}
}
void writecmd(int z)
{
   RS = 0;             // This is command
   P2 = z;             //Data transfer
   E  = 1;             // => E = 1
   delay(150);
   E  = 0;             // => E = 0
   delay(150);
}

void writedata(char t)
{
   RS = 1;             // This is data
   P2 = t;             //Data transfer
   E  = 1;             // => E = 1
   delay(150);
   E  = 0;             // => E = 0
   delay(150);
}

void cct_init(void)
{
	P0 = 0x00;   //not used 
	P1 = 0x00;   //not used 
	P2 = 0x00;   //used as data port
	P3 = 0x00;   //used for generating E and RS
}


void InitTimer0(void)
{
	TMOD &= 0xF0;    // Clear 4bit field for timer0
	TMOD |= 0x02;    // Set timer0 in mode 2
	
	TH0 = 0x05;      // 250 usec reloading time
	TL0 = 0x05;      // First time value
	
	ET0 = 1;         // Enable Timer0 interrupts
	EA  = 1;         // Global interrupt enable
	
	TR0 = 1;         // Start Timer 0
}


void Timer0_ISR (void) interrupt 1     // It is called after every 250usec
{
	usecCounter = usecCounter + 250;   // Count 250 usec
	
	if(usecCounter==1000)              // 1000 usec means 1msec 
	{
		msCounter++;
		usecCounter = 0;
	}

	TF0 = 0;     // Clear the interrupt flag
}

void WebsiteLogo()
{
   writecmd(0x95);
   writedata('w');                                 //write
   writedata('w');                                 //write
   writedata('w');                                 //write
   writedata('.');                                 //write
   writedata('T');                                 //write
   writedata('h');                                 //write
   writedata('e');                                 //write
   writedata('E');                                 //write
   writedata('n');                                 //write
   writedata('g');                                 //write
   writedata('i');                                 //write
   writedata('n');                                 //write
   writedata('e');                                 //write
   writedata('e');                                 //write
   writedata('r');                                 //write
   writedata('i');                                 //write
   writedata('n');                                 //write
   writedata('g');                                 //write
 
   writecmd(0xd8);
 
   writedata('P');                                 //write
   writedata('r');                                 //write
   writedata('o');                                 //write
   writedata('j');                                 //write
   writedata('e');                                 //write
   writedata('c');                                 //write
   writedata('t');                                 //write
   writedata('s');                                 //write
   writedata('.');                                 //write
   writedata('c');                                 //write
   writedata('o');                                 //write
   writedata('m');                                 //write
   writecmd(0x80);
}

void UpdateTimeCounters(void)
{
	if (msCounter==1000)
	{
		secCounter++;
		msCounter=0;
	}

	if(secCounter==60)
	{
		minCounter++;
		secCounter=0;
	}

	if(minCounter==60)
	{
		hrCounter++;
		minCounter=0;
	}

	if(hrCounter==24)
	{
		hrCounter = 0;
	}
}


void DisplayTimeToLCD( unsigned int h, unsigned int m, unsigned int s )   // Displays time in HH:MM:SS format
{
	ClearLCDScreen();      // Move cursor to zero location and clear screen

	// Display Hour
	WriteDataToLCD( (h/10)+0x30 );
	WriteDataToLCD( (h%10)+0x30 );

	//Display ':'
	WriteDataToLCD(':');

	//Display Minutes
	WriteDataToLCD( (m/10)+0x30 );
	WriteDataToLCD( (m%10)+0x30 );

	//Display ':'
	WriteDataToLCD(':');

	//Display Seconds
	WriteDataToLCD( (s/10)+0x30 );
	WriteDataToLCD( (s%10)+0x30 );
}


void delay(int a)
{
   int i;
   for(i=0;i<a;i++);   //null statement
}

void WriteDataToLCD(char t)
{
   RS = 1;             // This is data

   P2 &= 0x0F;		   // Make P2.4 to P2.7 zero
   P2 |= (t&0xF0);     // Write Upper nibble of data

   E  = 1;             // => E = 1
   delay(150);
   E  = 0;             // => E = 0
   delay(150);

   P2 &= 0x0F;		   // Make P2.4 to P2.7 zero
   P2 |= ((t<<4)&0xF0);// Write Lower nibble of data

   E  = 1;             // => E = 1
   delay(150);
   E  = 0;             // => E = 0
   delay(150);
}


void WriteCommandToLCD(int z)
{
   RS = 0;             // This is command

   P2 &= 0x0F;		   // Make P2.4 to P2.7 zero
   P2 |= (z&0xF0);     // Write Upper nibble of data

   E  = 1;             // => E = 1
   delay(150);
   E  = 0;             // => E = 0
   delay(150);

   P2 &= 0x0F;		   // Make P2.4 to P2.7 zero
   P2 |= ((z<<4)&0xF0);// Write Lower nibble of data

   E  = 1;             // => E = 1
   delay(150);
   E  = 0;             // => E = 0
   delay(150);
}

void lcdinit(void)
{
  ///////////// Reset process from datasheet /////////
     delay(15000);

	 P2 &= 0x0F;		   // Make P2.4 to P2.7 zero
	 P2 |= (0x30&0xF0);    // Write 0x3
	
	 E  = 1;               // => E = 1
	 delay(150);
	 E  = 0;               // => E = 0
	 delay(150);

     delay(4500);

	 P2 &= 0x0F;		   // Make P2.4 to P2.7 zero
	 P2 |= (0x30&0xF0);    // Write 0x3
	
	 E  = 1;               // => E = 1
	 delay(150);
	 E  = 0;               // => E = 0
	 delay(150);

     delay(300);

	 P2 &= 0x0F;		   // Make P2.4 to P2.7 zero
	 P2 |= (0x30&0xF0);    // Write 0x3
	
	 E  = 1;               // => E = 1
	 delay(150);
	 E  = 0;               // => E = 0
	 delay(150);

     delay(650);

	 P2 &= 0x0F;		   // Make P2.4 to P2.7 zero
	 P2 |= (0x20&0xF0);    // Write 0x2
	
	 E  = 1;               // => E = 1
	 delay(150);
	 E  = 0;               // => E = 0
	 delay(150);

	 delay(650);

  /////////////////////////////////////////////////////
   WriteCommandToLCD(0x28);    //function set
   WriteCommandToLCD(0x0c);    //display on,cursor off,blink off
   WriteCommandToLCD(0x01);    //clear display
   WriteCommandToLCD(0x06);    //entry mode, set increment
}

void ClearLCDScreen(void)
{
	WriteCommandToLCD(0x01);   // Clear screen command
	delay(1000);
}
  • Now run your simulation and if everything goes fine then you will get results as shown in below figure:
  • The above figure is taken after 10 seconds of start of simulation in Proteus ISIS.
  • As the simulation keeps on running the clock will also keep on ticking.
  • The code is self explanatory but let me explain the interrupt function.
  • I have used Timer0 interrupt in this digital Clock.
  • The timer interrupt function is incrementing the userCounter variable by 250 which is in micro seconds. So we need 1000us as it will become 1 second. That's why I have placed the check that when userCounter == 1000 then increment the second.
  • I have added comments in the code so read it in detail and still if you stuck somewhere then ask in comments and I will resolve them.
  • You can download the complete code along with Proteus Simulation by clicking the below button:

Download Proteus Simulation and Code for Digital Clock

That's all for today. Hope you have enjoyed today's project. Will meet you guys soon in the next post. Till then take care !!! :)
Syed Zain Nasir

I am Syed Zain Nasir, the founder of <a href=https://www.TheEngineeringProjects.com/>The Engineering Projects</a> (TEP). I am a programmer since 2009 before that I just search things, make small projects and now I am sharing my knowledge through this platform.I also work as a freelancer and did many projects related to programming and electrical circuitry. <a href=https://plus.google.com/+SyedZainNasir/>My Google Profile+</a>

Share
Published by
Syed Zain Nasir