Thursday, 14 November 2024

PWM in Arduino

 

Aim:

Pulse Width Modulation or PWM is a common technique used to vary the width of the pulses in a pulse-train. PWM has many applications such as controlling servos and speed controllers, limiting the effective power of motors and LEDs.

Description:


Pulse width modulation is basically, a square wave with a varying high and low time. A basic PWM signal is shown in the following figure.
PWM
There are various terms associated with PWM −
  • On-Time − Duration of time signal is high.
  • Off-Time − Duration of time signal is low.
  • Period − It is represented as the sum of on-time and off-time of PWM signal.
  • Duty Cycle − It is represented as the percentage of time signal that remains on during the period of the PWM signal.

Period

As shown in 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 following equation −

=+

Duty Cycle

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

=+=

analogWrite() Function

The analogWrite() function writes an analog value (PWM wave) to a pin. It can be used to light a LED at varying brightness or drive a motor at various speeds. After a call of the analogWrite() function, the pin will generate a steady square wave of the specified duty cycle until the next call to analogWrite() or a call to digitalRead() or digitalWrite() on the same pin. The frequency of the PWM signal on most pins is approximately 490 Hz. On the Uno and similar boards, pins 5 and 6 have a frequency of approximately 980 Hz. Pins 3 and 11 on the Leonardo also run at 980 Hz.
On most Arduino boards (those with the ATmega168 or ATmega328), this function works on pins 3, 5, 6, 9, 10, and 11. On the Arduino Mega, it works on pins 2 - 13 and 44 - 46. Older Arduino boards with an ATmega8 only support analogWrite() on pins 9, 10, and 11.
UNO PWM Pins
The Arduino Due supports analogWrite() on pins 2 through 13, and pins DAC0 and DAC1. Unlike the PWM pins, DAC0 and DAC1 are Digital to Analog converters, and act as true analog outputs.
You do not need to call pinMode() to set the pin as an output before calling analogWrite().

analogWrite() Function Syntax

analogWrite ( pin , value ) ;
value − the duty cycle: between 0 (always off) and 255 (always on).

Block Diagram

pwm

Schematic


Code

// ***********************************************
// Project: Interfacing PWM to 8051
// Author: Hack Projects India
// Module description: Operate array of LED's
// ***********************************************
#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
}
// 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
}

Downloads:

The code was compiled in Keil uvision4 and simulation was made in Proteus v7.7.
To download code and proteus simulation click here.

Further Reading suggestions:

You may also like,

No comments:

Post a Comment