(PWM Tutorial first commit)
 
(PWM Tutorial: 8051 implementation added)
Line 39: Line 39:
  
 
If T<sub>on</sub> is 0, V<sub>out</sub> is also 0. if T<sub>on</sub> is T<sub>total</sub> then V<sub>out</sub> is V<sub>in</sub> or say maximum.
 
If T<sub>on</sub> is 0, V<sub>out</sub> is also 0. if T<sub>on</sub> is T<sub>total</sub> then V<sub>out</sub> is V<sub>in</sub> or say maximum.
 +
 +
 +
== Implementing PWM on 8051 ==
 +
The basic idea behind PWM implementation on 8051 is using timers and switching port pin high/low at defined intervals. As we have discussed in the introduction of PWM that by changing the Ton time, we can vary the width of square wave keeping same time period of the square wave.
 +
 +
We will be using 8051 Timer0 in Mode 0. Values for high and low level will be loaded in such a way that total delay remains same. If for high level we load a value X in TH0 then for low level TH0 will be loaded with 255-X so that total remains as 255.
 +
 +
=== Assembly Code Example ===
 +
==== Timer setup for PWM ====
 +
<source lang="asm">
 +
PWMPIN EQU P1.0 ; PWM output pin
 +
PWM_FLAG EQU 0 ; Flag to indicate high/low signal
 +
 +
PWM_SETUP:
 +
MOV TMOD,#00H ; Timer0 in Mode 0
 +
MOV R7, #160 ; Set pulse width control
 +
; The value loaded in R7 is value X as
 +
; discussed above.
 +
SETB EA ; Enable Interrupts
 +
SETB ET0 ; Enable Timer 0 Interrupt
 +
SETB TR0 ; Start Timer
 +
RET
 +
</source>
 +
==== Interrupt Service Routine ====
 +
As we are using Timer0 for generating PWM, timer interrupt service routine uses PWM_FLAG bit to selects the high and low section of PWM signal. When timer overflows, PWM_FLAG is checked. If flag is set timer is loaded with timer value for low time and if flag is cleared timer is loaded with high time value.
 +
 +
<source lang="asm">
 +
TIMER_0_INTERRUPT:
 +
JB PWM_FLAG, HIGH_DONE ; If PWM_FLAG flag is set then we just finished
 +
; the high section of the
 +
LOW_DONE: ; cycle so Jump to HIGH_DONE
 +
SETB PWM_FLAG ; Make PWM_FLAG=1 to indicate start of high section
 +
SETB PWMPIN ; Make PWM output pin High
 +
MOV TH0, R7 ; Load high byte of timer with R7
 +
; (pulse width control value)
 +
CLR TF0 ; Clear the Timer 0 interrupt flag
 +
RETI ; Return from Interrupt to where
 +
; the program came from
 +
HIGH_DONE:
 +
CLR PWM_FLAG ; Make PWM_FLAG=0 to indicate start of low section
 +
CLR PWMPIN ; Make PWM output pin low
 +
MOV A, #0FFH ; Move FFH (255) to A
 +
CLR C ; Clear C (the carry bit) so it does
 +
; not affect the subtraction
 +
SUBB A, R7 ; Subtract R7 from A. A = 255 - R7.
 +
MOV TH0, A ; so the value loaded into TH0 + R7 = 255
 +
CLR TF0 ; Clear the Timer 0 interrupt flag
 +
RETI ; Return from Interrupt to where
 +
; the program came from
 +
</source>
 +
 +
As everything is handled in ISR. so to stop PWM you can simply disable the timer.
 +
<source lang="asm">
 +
PWM_STOP:
 +
CLR TR0 ; Stop timer to stop PWM
 +
RET
 +
</source>
 +
 +
The width of PWM can be changed by changing the value of R7 register. In above example I am using 160, you can choose any value from 0 to 255. R7 = 0 will give you o/p 0V approx and R7 = 255 will give you 5V approx.
 +
 +
You can also make use of Timer1 if you want. And the output pin can be changed to whatever pin you want.
 +
 +
=== C Code Example ===
 +
As explained in assembly example, the same code can be implemented in C.
 +
<source lang="c">
 +
/* Global variables and definition */
 +
#define PWMPIN P1_0
 +
 +
unsigned char pwm_width;
 +
bit pwm_flag = 0;
 +
 +
void pwm_setup()
 +
{
 +
TMOD = 0;
 +
pwm_width = 160;
 +
EA = 1;
 +
ET0 = 1;
 +
TR0 = 1;
 +
}
 +
 +
/* Timer 0 Interrupt service routine */
 +
void timer0() interrupt 1
 +
{
 +
if (!pwm_flag) { /* Start of High level */
 +
pwm_flag = 1; /* Set flag */
 +
PWMPIN = 1; /* Set PWM o/p pin */
 +
TH0 = pwm_width; /* Load timer */
 +
TF0 = 0; /* Clear interrupt flag */
 +
} else { /* Start of Low level */
 +
pwm_flag = 0; /* Clear flag */
 +
PWMPIN = 0; /* Clear PWM o/p pin */
 +
TH0 = 255 - pwm_width; /* Load timer */
 +
TF0 = 0; /* Clear Interrupt flag */
 +
}
 +
}
 +
 +
void pwm_stop()
 +
{
 +
TR0 = 0; /* Disable timer to disable PWM */
 +
}
 +
</source>

Revision as of 12:04, 9 April 2015

Pulse width Modulation or PWM is one of the powerful techniques used in control systems today. It is used in wide range of application which includes: speed control, power control, measurement and communication. This tutorial will take you through basics of Pulse width modulation and its implementation on microcontrollers.


Basic Principal of PWM

Pulse width modulation is basically a square wave with a varying high and low time. A basic PWM signal is shown in the figure below.

center

Pulse width modulation wave

There are various terms associated with PWM:

  1. On-Time: Duration of time signal is high
  2. Off-Time: Duration of time signal is low
  3. Period: It is represented as the sum of on-time and off-time of PWM signal
  4. Duty cycle: It is represented as percentage of time signal remains on during the period of the PWM signal

Period

As shown in the the figure, Ton denotes the on-time and Toff denotes the off time of signal. Period is the sum of both on and off times and is calculated as shown in the equation below:

PWM Period

Duty Cycle

Duty cycle is calculated as on-time to the period of time. Using the period calculated above, duty cycle is calculated as:

PWM Duty cycle equation

PWM: Voltage Regulation

PWM signal when used at a different duty cycles gives a varying voltage at the output. This method is used in various areas of application like:

  • Switching regulators
  • LED dimmers
  • Audio
  • Analog signal generation
  • and many more...

Voltage regulation is done by averaging the PWM signal. Output voltage is represented by the following equation:

PWM Voltage regulation PWM Voltage regulation

As you can see from the equation the output voltage can be directly varied by varying the Ton value.

If Ton is 0, Vout is also 0. if Ton is Ttotal then Vout is Vin or say maximum.


Implementing PWM on 8051

The basic idea behind PWM implementation on 8051 is using timers and switching port pin high/low at defined intervals. As we have discussed in the introduction of PWM that by changing the Ton time, we can vary the width of square wave keeping same time period of the square wave.

We will be using 8051 Timer0 in Mode 0. Values for high and low level will be loaded in such a way that total delay remains same. If for high level we load a value X in TH0 then for low level TH0 will be loaded with 255-X so that total remains as 255.

Assembly Code Example

Timer setup for PWM

	PWMPIN EQU P1.0	; PWM output pin
	PWM_FLAG EQU 0	; Flag to indicate high/low signal

PWM_SETUP:
	MOV TMOD,#00H ; Timer0 in Mode 0
	MOV R7, #160	; Set pulse width control
	; The value loaded in R7 is value X as
	; discussed above.
	SETB EA ; Enable Interrupts
	SETB ET0 ; Enable Timer 0 Interrupt
	SETB TR0 ; Start Timer
	RET

Interrupt Service Routine

As we are using Timer0 for generating PWM, timer interrupt service routine uses PWM_FLAG bit to selects the high and low section of PWM signal. When timer overflows, PWM_FLAG is checked. If flag is set timer is loaded with timer value for low time and if flag is cleared timer is loaded with high time value.

TIMER_0_INTERRUPT:
	JB PWM_FLAG, HIGH_DONE	; If PWM_FLAG flag is set then we just finished
						; the high section of the
LOW_DONE:				; cycle so Jump to HIGH_DONE
	SETB PWM_FLAG		; Make PWM_FLAG=1 to indicate start of high section
	SETB PWMPIN			; Make PWM output pin High
	MOV TH0, R7			; Load high byte of timer with R7
						; (pulse width control value)
	CLR TF0				; Clear the Timer 0 interrupt flag
	RETI				; Return from Interrupt to where
						; the program came from
HIGH_DONE:
	CLR PWM_FLAG		; Make PWM_FLAG=0 to indicate start of low section
	CLR PWMPIN			; Make PWM output pin low
	MOV A, #0FFH		; Move FFH (255) to A
	CLR C				; Clear C (the carry bit) so it does
						; not affect the subtraction
	SUBB A, R7			; Subtract R7 from A. A = 255 - R7.
	MOV TH0, A			; so the value loaded into TH0 + R7 = 255
	CLR TF0				; Clear the Timer 0 interrupt flag
	RETI				; Return from Interrupt to where
						; the program came from

As everything is handled in ISR. so to stop PWM you can simply disable the timer.

PWM_STOP:
	CLR TR0				; Stop timer to stop PWM
	RET

The width of PWM can be changed by changing the value of R7 register. In above example I am using 160, you can choose any value from 0 to 255. R7 = 0 will give you o/p 0V approx and R7 = 255 will give you 5V approx.

You can also make use of Timer1 if you want. And the output pin can be changed to whatever pin you want.

C Code Example

As explained in assembly example, the same code can be implemented in C.

/* Global variables and definition */
#define PWMPIN P1_0
 
unsigned char pwm_width;
bit pwm_flag = 0;
 
void pwm_setup()
{
	TMOD = 0;
	pwm_width = 160;
	EA = 1;
	ET0 = 1;
	TR0 = 1;
}

/* Timer 0 Interrupt service routine */
void timer0() interrupt 1
{
	if (!pwm_flag) {	/* Start of High level */
		pwm_flag = 1;	/* Set flag */
		PWMPIN = 1;		/* Set PWM o/p pin */
		TH0 = pwm_width;	/* Load timer */
		TF0 = 0;		/* Clear interrupt flag */
	} else {			/* Start of Low level */
		pwm_flag = 0;	/* Clear flag */
		PWMPIN = 0;		/* Clear PWM o/p pin */
		TH0 = 255 - pwm_width;	/* Load timer */
		TF0 = 0;		/* Clear Interrupt flag */
	}
}

void pwm_stop()
{
	TR0 = 0;			/* Disable timer to disable PWM */
}
Powered by MediaWiki
ArrayContent is available under Creative Commons Attribution Non-Commercial Share Alike unless otherwise noted.