Embedded Systems and Power Electronics

Total Pageviews

About Me

My photo
I am currently a PhD student at UC Berkeley, following a 6-year journey working at Apple after my undergrad years at Cornell University. I grew up in Dhaka, Bangladesh where my interest in electronics was cultivated, resulting in the creation of this blog.

BTemplates.com

Powered by Blogger.

Nov 5, 2012

Research on transformerless AC-AC (sine wave) conversion- Part 1: AC-AC Buck



In Bangladesh, mains AC line voltage may vary quite a lot, sometimes down to about 180V (or maybe lower!) and sometimes up above 220V. Thus, using a voltage regulator/stabilizer is necessary so that the output voltage is fixed, if not exactly at 220V, somewhere around that. 210-225V is an acceptable range. The common method of AC-AC voltage regulation/stabilization is to use relays to switch the AC input voltage into different tappings of a transformer, so that the voltage is stepped up or down by an amount, as required, depending on the input voltage.

There are 2 things in this circuit that could be improved:
1) Output voltage variation - Output voltage may sometimes vary by 10V or maybe even up to 20V. While, in most cases, it may be acceptable, a tighter regulation would certainly be better.
2) Cost - This type of stabilizer becomes quite expensive due to the large 50Hz transformer required. Also labor costs are high due to winding transformer with multiple tappings.

So, for the last six months, I have been researching on AC-AC "transformerless" converters, attempting to convert the line voltage to provide an output voltage either higher or lower than the input voltage, as required. If input voltage is lower than required (the reference voltage is approximately 220VAC), then the voltage is stepped up. If input voltage is higher than required (the reference voltage is approximately 220VAC), the voltage is stepped down. However, I've been trying to do this without the use of any 50Hz transformer, which is quite bulky, large and expensive.

My first idea was to first convert the line voltage to DC, step it up and then use SPWM (sinusoidal pulse width modulation) on a MOSFET with feedback to provide a sine wave output with the required voltage (approximately 220VAC).

However, I thought, there must be a simpler way. There must be a way to control the line AC without having to convert to DC. There must be a way to achieve regulation without the use of 5 MOSFETs.

Then, I thought of the buck-boost converter. If it could be done on DC, it can also be done on AC.

But that was the problem. How would it be done on AC? I would need a high voltage high frequency device - a high frequency high voltage switch that I can control with a microcontroller. In the DC-DC buck-boost converters, MOSFETs and diodes are used with DC. But, they can not be used in the same way with AC since direction of current is alternating and I can not directly control the flow of AC with a single MOSFET.

So, I decided to break it up. Instead of attempting to construct the DC-DC buck-boost converter at once, I thought of first constructing the buck converter to test it out to see if my idea of direct line voltage control was correct.

I analyzed the DC-DC buck converter so that I could make a similar circuit for AC: an AC-AC buck converter.

Here is the DC-DC buck converter (the switch can be a BJT, MOSFET or IGBT):

Here, you can see the current flow when the switch is on:

Here, you can see the current flow when the switch is off:


If you are not clear about how buck converters work, you should read up on them. Many tutorials can be found online and there are many books on SMPS available. These are 3 of the books I learnt from (and they're great books):
Power Supply Cookbook - Marty Brown
Switching Power Supplies Demystified - Raymond Mack Jr
Switching Power Supply Design - Abraham Pressman

I started thinking about porting the same DC-DC buck concept for AC-AC buck converter. The switch could no longer be a single BJT, MOSFET or IGBT as they are unidirectional switches (can control flow of current in only one direction) and so can not be used for AC. The diode also needed to be replaced with a switch that would be turned on when the main switch was off, and vice versa. Why? Because in the DC-DC buck converter, direction of flow of current when the switch is off will be the same at all times. However, in an AC-AC buck converter, direction of flow of current will alternate every half cycle. So, one half cycle, the diode would be forward biased, but the next half cycle, it would be reverse biased.

So, here is the AC-AC buck converter circuit:

Notice how the diode is now replaced with a switch - SW2. Also, notice how no electrolytic capacitor is used at the output. The reason is obvious - the output is AC, so we can not use a capacitor that has polarity.

If you have understood how a buck converter works, the next 4 diagrams showing the flow of current should be self-explanatory in demonstrating an AC-AC buck converter.

Current flow when SW1 is on in the positive half cycle:

Current flow when SW1 is off in the positive half cycle:

Current flow when SW1 is on in the negative half cycle:

Current flow when SW1 is off in the negative half cycle:

That seemed reasonable - it should work, I thought. But, what would I use for SW1 and SW2. I can't use triacs since they're too slow and there's no way they can be used with frequencies of tens of kiloHertz. Plus, they would just latch on when fired. So, I had to somehow use MOSFET(s) or IGBT(s). But how?

Well, with some clever circuitry, it's possible to control AC loads with MOSFETs/IGBTs. And I showed how here: http://tahmidmc.blogspot.com/2012/11/controlling-ac-load-with-mosfet.html

For convenience, I'll summarize it here.



Perhaps you don't see how it works now. But consider the two diagrams below, which show the flow of current during the two AC half cycles. I'm sure you'll get it better then.



As you can see, due to the bridge rectifier, the MOSFET always "sees" a DC voltage as the drain is always positive with respect to the source. Thus, with this combination of the bridge rectifier and MOSFET, by controlling a DC switch - the MOSFET, you can control the AC current flow.

So, I could replace SW1 with a bridge rectifier and MOSFET, and SW2 with another bridge rectifier and MOSFET.

Initially when testing, I hadn't included SW2 and found that it caused the inductor to saturate. After a few seconds, volt-time averaging wasn't happening. This is because the inductor's flux is not "reset" and it just stores the energy which is not dissipated when SW1 is off.

Then, I included SW2 and everything worked well. There was no more problem. You must remember that since frequency of operation is 20kHz, a normal bridge rectifier can not be used. For the bridge rectifier, ultrafast diodes must be used.

Now comes the part about control - how to control the voltage.

In the AC-AC buck circuit, SW2 acts like the diode in a DC-DC buck converter. So, our buck conversion and chopping is done by SW1.

In a DC-DC buck converter, the output voltage is given by:

Vout = Vin * DC [where DC = Duty Cycle]

So, if duty cycle is 50% or 0.5, the output voltage is half the input voltage.
Is that also correct for AC-AC buck converter? It should be, since it uses the same principle. And, yes, it is.

Here's the principle of operation:
A microcontroller (I used PIC16F684) detects zero-crossing. Upon zero-crossing, PWM is started. The selected frequency is 20kHz. It's the maximum frequency I can get with a PIC16F684. Let's take an example with 50% duty cycle. So time period is 50us. Every cycle (20kHz, not the 50Hz one), SW1 is kept on for 50% of the time. So, for 25us, SW1 is kept on. Then, SW1 is turned off. A small deadtime is provided and SW2 is kept on for the remainder of the cycle to act as the catch diode. At the end of the cycle another dead time is provided before SW1 is turned on. The deadtime (or deadband delay) is provided so that SW1 and SW2 aren't on at the same time.

So, what happens is, the 50Hz AC wave is broken down into 400 parts and pulse width modulation is carried out over each part. Each part represents a time of 50us. So, when half of each of the 400 parts is kept on and the other half is kept off, due to the LC filter which will do the volt-time average, you will get a sine wave output - the same waveshape as that of the input - but with half the amplitude. So, the input AC voltage has become halved in amplitude, without a distortion of the waveshape. The duty cycle can be varied to obtain other voltages.

These 2 diagrams illustrate the above example:


Signal from microcontroller (in red)


Chopped Input Voltage (in red)
Filtered Output Voltage (in blue)


These 3 diagrams should clear your doubts:



In the AC-AC buck converter, feedback is implemented. The microcontroller senses the output voltage and increses or decreases the duty cycle to adjust the output voltage as required, to deal with line/load variations.

Here is the prototype at work (generating an output voltage of 115VAC from 230VAC input):



The output waveform without proper filtering:

The output waveform with proper filtering (the "jerkiness" of the waveform is due to the slow response of the camera sensor):

So, I've shown and explained AC-AC buck conversion. In the next part (part 2), I will show and explain the boost part. With the buck and boost parts combined, a transformless AC-AC converter can be created, which when controlled precisely by the microcontoller, can scale the line voltage up or down without having to regenerate the sine wave, thus outputting a wave that has a different amplitude but same waveform as that of the input wave.

My research on this topic is still going on and once I am done, I will post part 2 of my research (boost conversion) on transformerless AC-AC (sine wave) conversion.

Nov 4, 2012

Feedback in sine wave inverter (PIC16F series based)


I have previously shown how to calculate the values for the sine table: http://tahmidmc.blogspot.com/2011/01/generation-and-implementation-of-sine.html

I have also shown how to implement SPWM in PIC16: http://tahmidmc.blogspot.com/2012/10/generation-of-sine-wave-using-spwm-in_10.html

Now I will show how to implement feedback for SPWM.

Due to various limitations in PIC16, such as ADC speed, instruction time and the ALU, it is extremely difficult, if not impossible, to calculate in real time the values required for feedback in sinusoidal pulse width modulation (SPWM). Thus, to implement feedback, a different approach must be used. That approach would be to retrieve the values from a sine table that contains the duty cycle values for a specific duty cycle. Here is one sine table I used, for example:

const unsigned char sin_table[416]={
0, 16, 32, 47, 62, 77, 91, 103, 115, 126, 136, 144, 151, 156, 160, 162, 163, 162, 160, 156, 151, 144, 136, 126, 115, 103, 91, 77, 62, 47, 32, 16, //65%

0, 17, 33, 49, 65, 80, 94, 107, 120, 131, 141, 149, 156, 162, 166, 168, 169, 168, 166, 162, 156, 149, 141, 131, 120, 107, 94, 80, 65, 49, 33, 17, //67.5%

0, 17, 34, 51, 67, 82, 97, 111, 124, 135, 146, 154, 162, 167, 172, 174, 175, 174, 172, 167, 162, 154, 146, 135, 124, 111, 97, 82, 67, 51, 34, 17, //70%

0, 18, 35, 53, 69, 85, 101, 115, 128, 140, 150, 160, 167, 173, 178, 180, 181, 180, 178, 173, 167, 160, 150, 140, 128, 115, 101, 85, 69, 53, 35, 18, //72.5%

0, 18, 37, 55, 72, 89, 104, 119, 133, 145, 156, 166, 174, 180, 184, 187, 188, 187, 184, 180, 174, 166, 156, 145, 133, 119, 104, 89, 72, 55, 37, 18, //75%

0, 19, 38, 56, 74, 91, 108, 123, 137, 150, 161, 171, 179, 186, 190, 193, 194, 193, 190, 186, 179, 171, 161, 150, 137, 123, 108, 91, 74, 56, 38, 19, //77.5%

0, 20, 39, 58, 77, 94, 111, 127, 141, 155, 166, 176, 185, 191, 196, 199, 200, 199, 196, 191, 185, 176, 166, 155, 141, 127, 111, 94, 77, 58, 39, 20, //80%

0, 20, 40, 60, 79, 97, 114, 131, 146, 159, 171, 182, 190, 197, 202, 205, 206, 205, 202, 197, 190, 182, 171, 159, 146, 131, 114, 97, 79, 60, 40, 20, //82.5%

0, 21, 42, 62, 82, 100, 118, 135, 151, 165, 177, 188, 197, 204, 209, 212, 213, 212, 209, 204, 197, 188, 177, 165, 151, 135, 118, 100, 82, 62, 42, 21, //85

0, 21, 43, 64, 84, 103, 122, 139, 155, 169, 182, 193, 202, 210, 215, 218, 219, 218, 215, 210, 202, 193, 182, 169, 155, 139, 122, 103, 84, 64, 43, 21, //87.5%

0, 22, 44, 65, 86, 106, 125, 143, 159, 174, 187, 198, 208, 215, 221, 224, 225, 224, 221, 215, 208, 198, 187, 174, 159, 143, 125, 106, 86, 65, 44, 22, //90%

0, 23, 45, 67, 88, 109, 128, 147, 163, 179, 192, 204, 213, 221, 227, 230, 231, 230, 227, 221, 213, 204, 192, 179, 163, 147, 128, 109, 88, 67, 45, 23, //92.5%

0, 23, 46, 69, 91, 112, 132, 151, 168, 184, 198, 210, 220, 228, 233, 237, 238, 237, 233, 228, 220, 210, 198, 184, 168, 151, 132, 112, 91, 69, 46, 23 //95%

//0, 25, 49, 73, 96, 118, 139, 159, 177, 193, 208, 220, 231, 239, 245, 249, 250, 249, 245, 239, 231, 220, 208, 193, 177, 159, 139, 118, 96, 73, 49, 25, //100%


Each set of values corresponding to one duty cycle has 32 values.

A table pointer is used to retrieve the values for a given duty cycle. So, when the value of the table pointer is 0, the program reads the first 32 values (65% duty cycle), then the next 32 values when value of table pointer is 1 and so on.

The microcontroller first starts with the lowest duty cycle and then analyses the output voltage.

If the output voltage must be increased, the value of the table pointer is incremented and so, the next set of values is retrieved, increasing duty cycle and thus output voltage. If output voltage must be decreased, the value of the table pointer is decremented so that the previous set of values is retrieved, lowering duty cycle and thus output voltage.

Here is how the table pointer is updated:

                    FBV = ADC_Get_Sample(FBCh);
                    if (FBV < 512){
                       FB_Step++;
                       if (FB_Step > 12) FB_Step = 12;
                    }
                    else{
                         if (FB_Step > 0){
                            FB_Step--;
                         }
                    }
                    adder = FB_Step << 5;
                    TMR1L = 0;
                    TMR1H = 0;
                    T1IF_bit = 0;

The reference value of the ADC is 5V, so 512 represents a voltage of 2.5V, which is the feedback reference voltage in this example. When voltage on ADC pin is >2.5V, table pointer value is decremented and when it is <2.5V, table pointer value is incremented.

The required set of values is retrieved and applied by something like this:
        TBL_POINTER_NEW = TBL_POINTER_OLD + SET_FREQ;
        if (TBL_POINTER_NEW < TBL_POINTER_OLD){
           P1M1_bit = ~P1M1_bit;
        }
        TBL_POINTER_SHIFT = TBL_POINTER_NEW >> 11;
        DUTY_CYCLE = TBL_POINTER_SHIFT + adder;
        CCPR1L = sin_table[DUTY_CYCLE];
        TBL_POINTER_OLD = TBL_POINTER_NEW;
        TMR2IF_bit = 0;

Now that I've shown how to generate a sine table manually and with "Smart Sine", implement SPWM in PIC16 and now, how to implement feedback, you can easily make a sine wave inverter using the information I've provided. All you need to do is make sure you've understood what I've said and do some research on your own to make the project complete.

To make this clearer and so that you understand better, I am presenting some common questions and their answers.

Question: I am designing a pure sine wave inverter using an output H bridge at 310vdc. Is there any need for feed back control if I keep the 310 Vdc steady?
Answer: If you can keep the 310VDC steady, then there is no need for feedback of SPWM. Your output will be relatively stable, since the DC bus is regulated.

Question: Why is the feedback not necessary, since there will be a voltage drop when there is increase in load? Remember the code is not incrementing or decrementing at this time. Now take an example of your normal modified sine inverter using SG3524 or any PWM IC. The output voltage drops when
there is increase in load but the battery voltage doesn't change. Therefore there will be a need to control the code also. Right?
Answer: When there is increase in load, you will notice that the battery voltage always decreases due to the internal resistance. Measure the battery voltage of the inverter under load and you will see that it decreases as load increases. A battery that measures about 12-13V open circuit may drop to 11V or lower under load. Plus, there is a significant drop by the transformer under load.

Also, think about this: in the H-bridge converter, when load increases (and so current increases) but the +ve bus voltage is constant, where will the voltage be dropped? There is no transformer to deal with, as
the primary side PWM will take care of drops by the transformer (and battery voltage drop) and keep the output fixed. The only place for voltage drop is the MOSFETs and there will be a drop in voltage in the MOSFETs, but not so large that the output voltage will be beyond acceptable bounds.

Question: You have shown 32 values for each duty cycle in the program. Can I use 192 for my 3.5kHz design?
Answer: You can use any number of duty cycle values in an array. 32 is usually sufficient, but with 192, greater precision can be had, especially since the frequency is 3.5kHz and the time period is much longer as compared to 16kHz. So, the SPWM is more accurate. However, just keep in mind that all of the values are stored in program memory. So, it will consume more program memory. You can use the software Smart Sine to generate the sine table: http://tahmidmc.blogspot.com/2012/10/smart-sine-software-to-generate-sine.html
For manual calculation, go through this tutorial: http://tahmidmc.blogspot.com/2011/01/generation-and-implementation-of-sine.html

Question: What about this unsigned char I can see (416) in the code. What is it used for?
Answer: 32 values correspond to the sine wave at one duty cycle. I have set 13 duty cycle settings. 13 * 32 = 416
There are total 416 values in the array, 13 sets, and each set (containing 32 values) corresponds to one duty cycle setting.

Each set of values corresponding to one duty cycle has 32 values.

A table pointer is used to retrieve the values for a given duty cycle. So, when the value of the table pointer is 0, the program reads the first 32 values (65% duty cycle), then the next 32 values when value of table pointer is 1 and so on.

Practical Example:
The microcontroller first starts with the lowest duty cycle and then analyses the output voltage.

If the output voltage must be increased, the value of the table pointer is incremented and so, the next set of values is retrieved, increasing duty cycle and thus output voltage. If output voltage must be decreased, the value of the table pointer is decremented so that the previous set of values is retrieved, lowering duty cycle and thus output voltage. The table pointer keeps increasing by 1 to increase the duty cycle and so, the output voltage of the inverter.
Practical example: At the beginning of program execution, the table pointer is zero and the microcontroller starts with 65% duty cycle. Let's assume we have a 8V (primary) to 256V (secondary) transformer and a full bridge converter is being used to drive the transformer. The bridge is being driven by signals from the microcontroller - these SPWM signals.

The RMS voltage to the transformer primary will be (0.65 * 12/1.4142135)V = 5.5V.
Turns ratio of the transformer (primary:secondary) is 1:32.
So, secondary voltage (assuming 100% transformer efficiency) will be 32*5.5V = 176V.
As we require an output of 220V, this is lower than required. So, output voltage must be increased. So, table pointer is incremented to 1.
Then, duty cycle is 67.5% and output voltage is 183V.
Table pointer is incremented to 2.
Duty cycle is 70% and output voltage is 190V.
Table pointer is incremented to 3.
Duty cycle is 72.5% and output voltage is 197V.
Table pointer is incremented to 4.
Duty cycle is 75% and output voltage is 204V.
Table pointer is incremented to 5.
Duty cycle is 77.5% and output voltage is 210V.
Table pointer is incremented to 6.
Duty cycle is 80% and output voltage is 217V.
Table pointer is incremented to 7.
Duty cycle is 82.5% and output voltage is 224V.
Table pointer is decremented to 6.
Duty cycle is 80% and output voltage is 217V.
Table pointer is incremented to 7.
Duty cycle is 82.5% and output voltage is 224V.
Table pointer is decremented to 6.
Duty cycle is 80% and output voltage is 217V.
.
.
.
.
And so on.

This is how regulation is achieved.

This is all at 12V. But, you can see how the table pointer will be changed to maintain a constant output.

Nov 2, 2012

Controlling an AC load with a MOSFET


In most cases where you want to control an AC load, a triac or SCRs will be used. However, it is not easy to drive a triac or SCR. The drive requirement for the triac or SCR makes it sometimes difficult to control it as we want. One thing is that we can not turn the triac or SCR on or off as we desire, because once we turn it on, it latches and stays on until the next zero crossing or until current stops flowing through it. Also, driving a triac with reference to MT1 (or A1) is not as straightforward as we would want.

However, a MOSFET can be controlled as we want. Set the gate high (with a sufficient voltage) and current can flow from drain to source. Set the gate low and current can no longer flow. Convenient!

However, a MOSFET can only be used to control DC loads since it is a unidirectional switch - current flow can be controlled when it is flowing from drain to source, but can not be controlled from source to drain. So, certainly it can not be used to control AC loads. Right?

Well, directly, it can not be used to control AC loads the same way you'd control DC loads. But, with some clever circuitry, it can be used to control AC loads. And here's how:



 Perhaps you don't see how it works now. But consider the two diagrams below, which show the flow of current during the two AC half cycles. I'm sure you'll get it better then.
 


As you can see, due to the bridge rectifier, the MOSFET always "sees" a DC voltage as the drain is always positive with respect to the source. Thus, with this combination of the bridge rectifier and MOSFET, by controlling a DC switch - the MOSFET, you can control the AC load.

The MOSFET must be turned on fully by driving it high by at least 8V above source level - 8V with respect to source.

So, you can turn the MOSFET on and off at any time and accordingly turn the load on and off as required. This makes driving the AC load so easy!

Nov 1, 2012

DC motor control with PIC16F877A - Practical example of PIC PWM


We all know what a motor is and what it does. The simplest way to run a motor is to just connect it to a power source. For a DC motor, that would mean, just connecting the motor to the DC voltage that the motor was rated for (or less). But, can you control the speed?

Yes. The simplest method is to control the speed of the motor by controlling the voltage the motor runs off. Imagine we have a 12V motor. If you run it off 12V, you get maximum speed (you can get more at higher voltage, but let's not go over rated specifications!). So, simple logic dictates that as we decrease the voltage to the motor, the speed must decrease. You can use a simple LM317 regulator to adjust the voltage to the motor and thus the speed.

But, this method of speed control has one huge disadvantage - inefficiency when using a linear regulator (and you may find the motor not running at all at lower voltages). Let's talk about the inefficiency. If we use a linear regulator to give 6V output from 12V input, where does the other 6V go? Well, that is dissipated as heat and this is the inefficiency I'm talking about. Let's say that the motor will draw 1A at 6V. So, your power supply output rated at 12V is regulated to 6V (at 1A current). And 6V is dropped and dissipated as heat. So, useful power = 6W. Wasted power = 6W. And you have an efficiency of only 50%. See?

The other method is PWM and this is far more efficient. Instead of converting the remaining power to heat, it "chops" the signal (that has a constant frequency) and lets the remaining to go through.


The fraction of the period for which the signal is on is known as the duty cycle. The average DC value of the signal can be varied by varying the duty cycle. The duty cycle can be anywhere between 0 (signal is always off) to 1 (signal is constantly on). If the signal has +12 V while it is on and 0 V during off condition, then by changing the duty cycle of the signal, any voltage between 0-12 V can be simulated.

With a duty cycle of 0, you get 0 output. With a duty cycle of 25% (0.25), you get 25% power. With duty cycle of 60% (0.6), you get 60% power. With duty cycle of 100% (1), you get full power. So, this provides an efficient way of controlling the power to a load. So, if power to a motor is controlled like this, the motor's speed can be controlled.

So, to control the speed of the DC motor, I have used PWM. The PIC16F877A has a CCP module, that allows us to use PWM at a hardware level.

Timer 2 is used as the time base. PR2 sets the PWM frequency according to the formula:
The ratio of CCPR1L to (PR2 + 1) sets the duty cycle. If PR2 = 249, CCPR1L = 125 gives 50% duty cycle. If PR2 = 249, CCPR1L = 180 gives 72% duty cycle. And so on.

CCP1CON is the CCP control register (image taken from PIC16F877A datasheet):

As you can see from the register details above, to set CCP1 to PWM mode, bits 3 and 2 of CCP1CON must be set to 1. The values of bits 1 and 0 do not matter. In my code, I have set CCP1CON = 12, but you can use one of
CCP1CON = 12
CCP1CON = 13
CCP1CON = 14
CCP1CON = 15

Any one of the above four values of CCP1CON will set CCP1 for PWM mode.

In the code, I did not use the mikroC libraries for PWM since I wanted to demonstrate the use of the registers associated with PWM.

Here is the code:

-------------------------------------------------------------------------------------------------------

//Programmer: Syed Tahmid Mahbub
//Compiler: mikroC PRO for PIC v4.60

unsigned int ADR;

void main() {
     PORTC = 0;
     TRISC = 0; //all PORTC pins output
     PORTA = 0;
     TRISA = 0x01; //RA0 input for pot
     CMCON = 7; //Disable analog comparators
     T2CON = 0; //Prescaler 1:1, TMR2 off
     ADCON1 = 14; //AN0 only analog
     ADC_Init(); //mikroC initializes ADC
     CCP1CON = 12; //PWM mode
     PR2 = 249; //16kHz PWM frequency
     while (1){
           ADR = ADC_Get_Sample(0) >> 2; //Get ADC reading for channel 0
           //and divide reading by 4
           if (ADR < 10){ //Setting lower bound
              TMR2ON_bit = 0; //Stop Timer and so stop PWM
              TMR2 = 0; //Clear Timer
              RC2_bit = 0; //Clear PWM output
           }
           else{
                if (ADR > 240){ //Setting upper bound
                   TMR2ON_bit = 0; //Stop Timer and so stop PWM
                   TMR2 = 0; //Clear Timer
                   RC2_bit = 1; //Keep PWM output high
                }
                else{ //Within upper and lower bounds
                      CCPR1L = ADR; //Set PWM duty cycle
                      TMR2ON_bit = 1; //If not on, turn on TMR2. If on, keep on
                }
           }
     }
}

-------------------------------------------------------------------------------------------------------

I've added comments to the code to make it as self-explanatory as I could.

There's one trick I used here and I will clarify that here.
I have used a 16MHz oscillator. With PR2=249, frequency is 16kHz.
CCPR1L must lie between 0 and (PR2 + 1), so in this case, between 0 and 250.
The ADC result can vary between 0 and 1023, 1023 corresponding to maximum speed. So, I've used divide by 4 to scale down the ADC result to between 0 and 255. However, a value of 255 can not be assigned to CCPR1L as it is beyond the maximum allowable value. So, I've set an upper bound of 240, above which the motor will run at maximum speed. I've also set a lower bound below which, PWM is stopped and motor is stopped.

Here's the circuit (you can see the generated PWM signal in the oscilloscope):

Q1 and Q2 form a totem-pole driver to drive the logic-level MOSFET IRL3803. A power MOSFET can not be fully turned on at a voltage of 5V, and requires voltages above 8V to be fully on. But a logic-level MOSFET can be fully turned on at 5V. To avoid drive complexity, I've used a logic level MOSFET. D1 is the freewheel diode.

Reference documents:
PIC16F877A datasheet: ww1.microchip.com/downloads/en/devicedoc/39582b.pdf
IRL3803 datasheet: fenykapu.free-energy.hu/pajert59/irl3803.pdf
Modalities of Using the ADC module of PIC16F877A: http://tahmidmc.blogspot.com/2012/03/modalities-of-using-adc-module-of.html