Thursday, November 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

31 comments:

  1. pls explain how to introduce pwm for 12 led chaser. i want to make comet tail effect. jaya19995@gmail.com

    ReplyDelete
    Replies
    1. Do you want to use PWM for controlling the brightness? What do you have in mind? One LED turns on at full brightness, one after the other, while the one trailing has reduced brightness?

      Delete
    2. Dear Tahmid can this circuit is used for controlling of 220 vdc motor
      if yes then what will be the change ??

      Delete
  2. im not getting pwm by tis code.... it jst gives the same wave for all value's

    ReplyDelete
    Replies
    1. Have you checked the output waveform using an oscilloscope? Could you upload an image of the oscilloscope displayed signal?

      Delete
  3. Please help. I'm trying to produce pulses with widths varying from 1ms - 2ms and I need to make the variations in fine increments (i.e. 1.1 ms, 1.2 ms, 1.3 ms, etc...). Also, I need the frequency to be around 50 or 60 Hz (the servo controller outputs 66.31 Hz). It isn't possible using the regular pwm function in MikroC. I have to recreate the pulses using PIC16F877a. Thanks in advance!

    ReplyDelete
  4. Hi Tahmid! Nice project it is working in Proteus.
    Can you please write code for 3 phase DC brushless motor. TNX

    ReplyDelete
  5. dear brother
    can i get a sample code with hitech c compiler to used with PIC16F877A ?
    to light up 8 leds ?
    set at port C
    thx

    ReplyDelete
    Replies
    1. Sorry, but I don't use Hitech C compiler.

      Delete
  6. dear Tahmid .. thank you for the articale. i am new to embedded systems. to be adept in this field , please advise me where to start?

    muhammad saeed anwer

    ReplyDelete
    Replies
    1. I would recommend getting a book and starting to read it and practicing along with it. Once you understand the concepts in the book, start using Google to look up more stuff and more projects. And keep on coding and designing circuits and projects.

      Good luck!

      Delete
  7. great article on Gear DC Motor i had issues but now they are resolved..thankyou :))

    ReplyDelete
  8. This comment has been removed by the author.

    ReplyDelete
  9. thanks mr. tahmid. it works in my proteus,,
    But it get error while I try load the hex file to my programmer (i'm using DIY K150). The error message is fuse error 0x2007, good 0x2F4A, and bad 0x3F7A.
    Thank you for any help, Mr.

    ReplyDelete
    Replies
    1. I don't know what those error messages mean.

      Make sure your programmer is working properly and that you have everything set up properly. Ensure that you selected the right microcontroller.

      Delete
  10. Dear Tahmid, Please explain how get the 0.3V to 5VDC regulate in high Current on for Thyristor. when 0.3 v on any 5volt lamp but thyristor not on. input transformer 15Amp. voltage drop by 2N3055

    ReplyDelete
    Replies
    1. I'm not clear regarding what you're asking. Do you want a 0.3V to 5V 15A voltage regulator?

      Delete
  11. how are you divide reading by 4?

    ReplyDelete
    Replies
    1. >>2 means bit shift to the right twice which is the same as dividing by 4.

      Delete
  12. Hi. I need to control a 230v,100 watts motor. Using PIC i have to design the gate drive circuit for the DC-DC converter connected to the motor. Thus controlling the input voltage of the motor. I have to sense the speed of the motor and give to the PIC and control the gate drive accordingly.

    ReplyDelete
  13. Great explanation, but how did u find the constanst values? I mean Kp,Ki, Kd?
    - electric dc motor

    ReplyDelete
  14. hi. i really need your help
    i have to do mini project. controlling speed of motor by using 5 switch which means there are 5 speed.
    how to do it? im computer engineering student. so i dont know much about electronic stuff
    can you help me? really appreciate it.
    this is my email. : noorsyamiera92@gmail.com

    ReplyDelete
  15. Thanks for your blog. I always try to keep up on latest innovations. If you are interested, I'd recommend checking this website for latest products: http://www.directindustry.com/industrial-manufacturer/dc-motor-60997.html

    ReplyDelete
  16. tahmid can you pls write an article on current sensing via pic microcontroller adc pins .......pls email me ......microprogrammer@ymail.com..thanks

    ReplyDelete
  17. Very awesome project. Thanks for sharing. If you like you can help other people through our community and Facebook page.

    ReplyDelete
  18. Dear Tahmid
    We had written to you in Forum Edaboard about "using of ADC,PID and PWM in 16F877A.(user name cuneyt tasci) We coudnt find any example about this theme or any comprehensive explanning .If you help us about it we would be very appriciated for your guide.Regards

    ReplyDelete
  19. I have tried this circuit .Proteus gives failure because of timestep too small. Pls give us your Protesu.DSN file with settings .Thanks in advance

    ReplyDelete
  20. Tahmid, I first found you on a thread at alllaboutcircuits.com . From your work and input, I believe you are well knowledged and respect your insight. I'm working on a project at AllAboutCircuits (http://forum.allaboutcircuits.com/threads/configuring-the-multi-channel-breathing-led-pwm.121674/) and would greatly appreciate any advice or input you could offer me. Thank you Tahmid

    ReplyDelete
  21. Hi Tahmid, I am using a PIC24FJ16GA002 in my project. I am new to this controller.. I have to generate a constant voltage signal with a regular interrupt on the same singnal. the output should be a ramp or a pwm signal. May I get a sample code for p1c24... for the same application.

    Thank you in advance.

    ReplyDelete
  22. Hello, I love reading through your blog, I wanted to leave a little comment to support you and wish you a good continuation. Wish you best of luck for all your best efforts.
    Planetary gear motor

    ReplyDelete