OCFreaks!

Sine Wave Generator using PWM with LPC2148 Microcontroller Tutorial

Introduction

I had received 3 requests for “how to generate sine PWM using lpc2148 microcontroller“. So , I planned to do it as soon as I could. In previous article I had posted a tutorial for PWM in LPC2148 @ Here. This tutorial is just an extension to that.

The basic idea here is to generate or synthesize a sine wave by passing a digitally generated PWM through a low pass filter. This is technically called “Direct Digital Synthesis” or DDS for short. This technique can be also used to generate other waveforms like sawtooth , square , etc.. Generally lookup tables are used to make it easier and faster. Also just by changing the lookup table we can change the wave form. I’ll explain lookup tables as we proceed.

The Basics

When we pass a PWM signal through a corresponding low pass filter then we get an analog voltage(or say Signal) at the output which is proportional to the dutycycle and given by:

Vout = VH x Dutycycle

Where VH is Voltage for Logic HIGH for PMW pulse. In our case its 3.3V.

Now, when we change the dutcycle i.e the T-ON time of PWM in sine fashion we get a sine waveform at the filter output. The basic idea here is to divide the waveform we want , Sine wave in our case , into ‘x’ number of divisions. For each division we have a single PWM cycle. The T-ON time or the duty cycle directly corresponds to the amplitude of the waveform in that division which is calculated using sin() function. Consider the diagram shown below :

Here I’ve divided the Sine wave into 10 divisions. So here we will require 10 different PWM pulses increasing & decreasing in sinusoidal manner. A PWM pulse with 0% Duty cycle will represent the min amplitude(0V) , the one with 100% duty cycle will represent max amplitude(3.3V). Since out PWM pulse has voltage swing between 0V to 3.3V , our sine wave too will swing between 0V to 3.3V.

It takes 360 degrees for a sine wave to complete one cycle. Hence for 10 divisions we will need to increase the angle in steps of 36 degrees. This is called the Angle Step Rate or Angle Resolution.

Now we can increase the number of divisions to get more accurate waveform. But as divisions increase we also need to increase the resolution .. but we cant increase the resolution after we reach the max resolution which depends on the Processor(& Peripheral) clock.

Resolution(PWM) : Its the minimum increment required to increase or decrease the pwm T-ON time i.e Duty Cycle. For e.g if we have a resolution of 1us then valid T-ON values would be 1,2,3,.. and if we have a resolution of 0.5us then valid T-ON values will be 1,1.5,2,2.5,.. (i.e now we get double the values)

Note : A high(i.e more Fine) Resolution in our case means that the actual value is low and vice-verse. For eg. 0.25us is a higher Resolution as compared to 1us. – This is my convention others might use it in reverse.

50 Hz Sinewave using Microcontroller based PWM

The above was just a simple example to explain the basic stuff. Now lets the actually example that I’ll be using. Here we will be generating a 50Hz Sine wave with 200 divisions.

To get the output waveform as smooth as possible we need to calculate the best possible divisions and resolution .. given we know the frequency of the output waveform. In our case will be generating a 50Hz sine wave using PWM signal generated by lpc2148 microcontroller.

Now for a 50Hz sine wave we get a period time 1/50 = 20 milliseconds which is the time required for the sine wave to complete 1 full cycle. Now our job is divide an interval of 20ms into ‘X’ divisions. Hence the number of division i.e X will decide the period of PWM signal and hence the best possible resolution can be chosen.

Note : The first thing that we must keep in mind is that we need the resolution to be a rational number like 0.25 and not 0.1666666… to keep things more accurate and calculations straight forward. Given that maximum speed of lpc2148 is 60mhz it would take 1us(micro-second) for 60 ticks of CPU clock(assuming CPU and Peripheral Clocks are at same speed) , 0.1 us for 6 ticks , 0.05us for 3 ticks and 0.0166666… for 1 tick. Hence the maximum resolution will be 0.016666..us i.e 16.6666..ns(nano-seconds). But since its an irrational number the best maximum resolution we can use is 0.05 us or 50ns. But in our case we wont be using such fine resolution since our number or divisions are less. If our number of division would have been more than we could have used 50ns as our resolution. For our case 0.5us resolution is enough.

Now , since we will be using 200 divisions , the period of each division will be 20ms/200 = 0.1ms = 100us. This means that the period of the pwm signal will be 100us. Hence if we use a resolution of 1us then we will only have a set 100 values(actually 101 values if you consider 0 also) ranging from 1 to 100 to describe a full sine wave cycle. If we use a resolution of 0.5ms then we will have a set of 200 values. These values are the match values i.e the number of ticks of PWM counter. So if the resolution is 0.5us and period is 100 us then we will require 200 ticks to complete 100us. Similarly if resolution is 0.25us we will require 400 ticks for 100us.

Max Number of PWM counter Ticks = PWM Period / Resolution

This gives us the Maximum value for the match register i.e 100% Dutycycle.

NOTE: The higher this count is the more accurate the sine wave will be – given we have correspondingly high number of divisions!.

Generating the Sine lookup table

In our case since our period time is 100us we will require max 200 ticks @ 0.5us resolution. So in a lot of 200 divisions or PWM cycles the match value i.e the no.of ticks must vary from 0 to 200 in a sine fashion. This can done by finding the angle for the corresponding division. So now we must first find the angle step rate or the angle resolution , which can be given by:

Angle step rate = 360 / No.of divisions

In our case we get Angle step rate = 1.8 degrees. Hence the 1st division represents 0 degrees , 2nd division 1.8 degs , 3rd division 3.6 degs and so on.

Now , by multiplying the sine of angle with 50% T-ON value we can get the T-ON values for a complete cycle. But since sine gives negative values for angle between 180 and 360 we need to scale it since PWM Match values cannot be negative. Again we use 50% T-ON and add it so all values are positive. This is given by :


SineLookupTable[Y] = rint( 100 + 100 * sin( Angle_Step_Rate*Y ) ); 

//where Y=0,1,...X-1 & X=no.of Divisions

Note that the function “rint(..)” is used to round the computed Match Value to the nearest integer.

Using the above equation the min amplitude will be 0V(match val=0) and max 3.3V(match val=200). In order to limit the amplitude between say 0.05V(match val=1) and 3.25V(match val=199) we can modify the equation by reducing the sine scale factor from 100 to 99. So , the equation in this case will be :


SineLookupTable[Y] = rint( 100 + 99 * sin( Angle_Step_Rate*Y ) ); 

//where Y=0,1,...X-1 & X=no.of Divisions

Basic Digital(PWM) to Analog Filter Design

We need a Low Pass filter(LPF) which we will be using as Digital to Analog Converter i.e DAC for converting PWM into Sine. This Low Pass Filter will block the high frequency PWM signal and will let the ‘encoded’ low frequency sine wave to pass through. To design this filter , first we must decide the Cut-Off or Corner frequency. Here we can follow a rule of thumb that the cut-off frequency must be 3 to 4 times the frequency we want at the output. Since we want 50hz sine at the output we will design a simple low pass RC filter with cut-off frequency of around 200hz.

The Cut-off Frequency for a RC Filter is given by:

Here the Time Constant is Tc = R * C

So , from the above equation we get the equation for Time Constant as :

Now we can Find the Time Constant Tc from Fcut-off and then select appropriate values for R and C.

So , In our case with Fcut-off=200 , we get Tc=796us. Hence in our case we can select a 1.8K Resistor and a 0.47uF Capacitor which gives a Time constant of 846us.

If you don’t have R & C with those values and just want to check the Sine wave output – A Quick & Dirty method to make an RC filter for the PWM in our case can be as follows – use a resistor between 1.8K and 5.6K and a Capacitor between 0.33uF and 1uF. For example you may use a 4.7K Resistor with a 0.47uF or 1uF Capacitor.

Note : If you need more better Sine Wave you can use a Low-Pass RC or Chebyeshev Filter of Higher order.

The Sine Wave Output in Oscilloscope

Here is the output for the code(given in Source Code section below). It generates a Sine Wave having a frequency of 50Hz after filter of-course.

Here is a closeup of the Sine wave :

As it can be seen the Sine wave it not completely smooth since I’ve used a simple RC filter. A better Sine wave get be obtained by using a good higher order filter.

Sinewave PWM Source code

In the Code given Below First I am generating a Sine Lookup Table with 200 entries and then Setting up PWM ISR which keeps on updating the Match Value for Match Register 1 after the end of each PWM cycle. The function “initClocks()” initializes the CPU and Peripheral Clocks at 60Mhz – its code is given in attached files. [Note – The startup file i.e startup.s by itself initializes CLK and PLCK @ 60Mhz .. even then I am doing it again for those who might want to edit my code and change the clocks as required]


/*
(C) Umang Gajera | Power_user_EX - www.ocfreaks.com 2011-13.
More Embedded tutorials @ www.ocfreaks.com/cat/embedded

Source for LPC2148 Sine PWM Tutorial
License : GPL.
*/
#include <lpc214x.h>
#include <math.h>
#define PLOCK 0x00000400
#define PWMPRESCALE 30 //30 PCLK cycles to increment TC by 1 i.e 0.5 Micro-second 
#define ANGLE_STEP_RATE 1.8 //i.e the Angle Resolution
#define PI 3.14159

void initPWM(void);

void initClocks(void);
void setupPLL0(void);
void feedSeq(void);
void connectPLL0(void);
void computeSineLookupTable(void);
void PWM_ISR(void) __irq;

char sineLookupTable[200];
int count=0;

int main(void)
{
	initClocks(); //Initialize CPU and Peripheral Clocks @ 60Mhz
	
	computeSineLookupTable(); //Compute Sine Lookup Table with 200 Divisions
	
	VICIntEnable |= (1<<8) ; 
	VICVectCntl0 = (1<<5) | 8 ;
	VICVectAddr0 = (unsigned) PWM_ISR;
	
	initPWM(); //Initialize PWM

	//Sinusoidal PWM Generation is Active Now!
   
	while(1){}
	
	//return 0; //normally this wont execute ever
}

void computeSineLookupTable(void)
{
	float angle;
	int i;
	for(i=0; i<200; i++)
	{
		angle = ( ((float)i) * ANGLE_STEP_RATE );
		sineLookupTable[i] = rint(100 + 99* sin( angle * (PI/180) )); //converting angle to radians
	}
}

void PWM_ISR(void) __irq
{
	long regVal = PWMIR;									
	
	PWMMR1 = sineLookupTable[count]; //current amplitude
	PWMLER = (1<<1); // set Latch Bit to High 
	count++;
	if(count>199) count = 0;
	
	PWMIR = regVal; // Clear Flags
	VICVectAddr = 0xFF; //End of ISR
}

void initPWM(void)
{
	/*Assuming that PLL0 has been setup with CCLK = 60Mhz and PCLK also = 60Mhz.*/

	PINSEL0 = (1<<1); // Select PWM1 output for Pin0.0
	PWMPCR = 0x0; //Select Single Edge PWM - by default its single Edged so this line can be removed
	PWMPR = PWMPRESCALE-1; // 0.5 micro-second resolution
	PWMMR0 = 200; // 200 ticks i.e 200x0.5=100ms period duration
	PWMMR1 = 100; // Initial pulse duration i.e width
	PWMMCR = (1<<1) | (1<<0); // Reset PWMTC & Interrupt on PWMMR0 match
	PWMLER = (1<<1) | (1<<0); // update MR0 and MR1
	PWMPCR = (1<<9); // enable PWM output
	PWMTCR = (1<<1) ; //Reset PWM TC & PR

	//Now , the final moment - enable everything
	PWMTCR = (1<<0) | (1<<3); // enable counters and PWM Mode

	//PWM Generation goes active now!!
	//Now you can get the PWM output at Pin P0.0!
}

Download Project Source for Sine Wave Generator Using LPC2148 @ LPC214x Sine PWM [Successfully tested on Keil UV4.70a , UV4.00d & UV3.31 ]

LPC2148 Development Boards that we Recommend:

Exit mobile version