You are on page 1of 11

Application Note:

Control with PWM Signals using the PIC18F4520


Sean Hatch
3/21/08

Executive Summary:

Pulse Width Modulated (PWM) signals are used in a wide variety of electronic control
applications. Usually, the system under control will feedback a reading to a PWM
controller, and the PWM controller will adjust its duty cycle so that the subsequent
readings become nearer to the expected output of the system. The PIC18F4520,
specifically its Enhanced Capture/Compare/PWM (ECCP) module, can be used to
implement a PWM control system. Many other features of the PIC can be used in
combination with the ECCP module to create a versatile controller. For example, the
PIC’s interrupt capabilities can be used to change the set-point of the system on the fly.
The Analog to Digital Conversion (ADC) module is usually responsible for reading the
output of the system, or other inputs. Also, as the PIC is a programmed device, any sort
of control scheme may be programmed. Although this application note is written for the
PIC18F4520, most everything here applies to any PIC with an ADC and the ECCP
module.

Keywords:

PIC18F4520, PIC applications, control, bang bang control, PWM control

Controlling PWM Signals with the PICF84 1


1. Introduction:

PWM signals are used in a variety of control applications, including:

• DC to DC converters.
• Motor controllers.
• Power delivery control.

In each of the applications, the system under control has a certain output which it is
supposed to achieve. The basic operation of a PWM control system is outlined in
Figure 1. A PWM controller must read the current output, compare it to the specified
set-point, and adjust a PWM signal’s duty cycle to minimize the error between the
current output and the set-point.

Figure 1: Block diagram of a PWM control system using the PICF18 as a controller

There are many PWM control integrated circuit packages on the market today. However,
they are usually limited to one input, and use a specific control scheme. The PIC offers
the ability to control based on multiple inputs. Additionally, any type of control scheme
can be programmed. Other modules of the PIC can be used to improve the versatility of
the controller. For example, the PIC’s interrupt capabilities are useful in changing the
set-point of the system on the fly, turning off the control system, or changing between
inputs to the control system.

Controlling PWM Signals with the PICF84 2


2. Hardware Details:

In order for the PIC to correctly function as a controller, the PIC must be correctly
connected to the system under control, and it must be correctly configured. For these
general connection and operation configurations, refer to the PIC’s datasheet. The rest
of the hardware details presented here will be specific to the PIC in a PWM control
setting.

• The PIC must be correctly connected to VDD and VSS, and this voltage
difference must be consistent. The control system will likely need to deal with
input from the ADC module. The potential difference between VDD and VSS
must be constant in order for analog to digital conversions to be accurate and
usable.

• The output signal from the system under control must be correctly voltage
divided so that all possible input voltages to the ADC fall between VDD and VSS,
otherwise the ADC will not convert the values correctly. Also, the voltage divider
should be configured so that the largest possible voltage from the system under
control will be divided down to nearly VDD. This will allow the highest conversion
resolution.

• The PIC cannot source or sink current quickly enough to charge or discharge the
gate capacitance of a MOSFET. Therefore, a driver must be used between the
PIC and the switching element in order to switch at relatively high frequencies.

• In many cases, difficulties can arise if for some reason, the system under control
remains active, while the PIC is powered down. A combination of software and
hardware can be implemented to prevent this from occurring.

• The system under control can often introduce transients into the system resulting
from system shutdown, a change in set point, or a change in load. It is
necessary to isolate or protect the PIC from these transients, as the PIC can
easily be damaged from them.

3. Software Details:

Everything having to do with time in regards to the PIC is expressed in terms of clock
cycles. The ECCP module is no exception. Before the registers relevant to our PWM
signal are configured, the Timer2 module must be configured. Further information
pertaining to Timer2 configuration can be found in the PIC’s datasheet. We are only
concerned with bits 0 – 2.

First, bit 2 must be set in order to enable the Timer2 module. Bits 1 – 0 set the prescale
factor. The prescale factor can be used to scale the clock frequency by a factor of 4 or
16. For example, if the PIC’s clock frequency is 40 MHz, and the prescale factor is set
to 4, the ECCP PWM module will operate at a base frequency of 10 MHz. The prescale
factor does not decrease the clock speed in other modules of the PIC.

Controlling PWM Signals with the PICF84 3


After the prescale value has been selected, 3 remaining registers characterize the PWM
signal: the PR2 register, the CCP1CON register, and the CCPR1L.

Note that these registers are used to create a PWM signal on the CCP1 channel. Some
PIC packages also have a CCP2 channel. In this case, the 3 relevant registers are the
PR2 register, the CCP2CON register, and the CCPR2L register. This application note,
however, will use the CCP1 channel throughout.

The following two equations (supplied by Microchip) can be used to establish the 3
registers given the PWM signal’s duty cycle (as a fraction of 1) and period (as a time).

• PWM Period = [(PR2) + 1] • 4 • TOSC • (TMR2 Prescale Value)

• PWM Duty Cycle = (CCPR1L:CCP1CON<5:4>) • TOSC • (TMR2 Prescale


Value)

In these equations, TOSC is the PIC’s clock frequency.

Now, the PWM signal is completely defined. However, two other tasks must be
performed before the PWM signal is available on the CCP1 pin of the PIC. First: the
CCP1 pin is on port C<2>. Therefore, bit 2 of port C must be configured as an output.
This can be achieved with the statement:

TRISC = 0bxxxxx0xx;

In this statement, x’s are don’t cares—their configuration will depend on other features of
the controller program.

Next, the CCP1CON register must be configured for PWM mode. To do this, 11xx is
written to CCP1CON<3:0>.

Finally, A PWM signal will be available on port C<2> of the PIC.

3.1 Setting up Other Relevant Modules:

For the PIC to function as a complete control system, a few other modules must be
enabled and configured. As this application note deals primarily with PWM control, just
an overview of these systems is in order. The datasheet can provide more specific
details.

In order for the PIC to function as a control system, the ADC needs to receive feedback
data from the system. The code in Code Snippet 1 will enable and configure the ADC
module:

OpenADC(ADC_FOSC_32 & ADC_RIGHT_JUST & ADC_12_TAD, ADC_CH3 & ADC_INT_OFF, 0);


//open adc port for reading
ADCON1 = 0b00001010; //set VREF+ to VDD and VREF- to
//GND (VSS)
SetChanADC(ADC_CH3); //Set ADC to Pin 5

Code Snippet 1: One way of initializing the PIC’s ADC

Controlling PWM Signals with the PICF84 4


Specific details pertaining to the configuration of the ADC can be found in the datasheet,
but here are a few important specifics: first, the ADC is currently reading from Pin 5.
This can be changed throughout the program in order to accommodate multiple inputs.

Secondly, VREF+ is set to VDD, and VREF- is set to VSS. This limits the range of
values which can be read in to the system, and defines the system’s resolution.

To read the current value of the ADC into the PIC, the code in Code Snippet 2 should
be executed:

ConvertADC();
while(BusyADC());
adc_result = ReadADC();

Code Snippet 2: Obtaining input from the ADC.

Another module commonly used for controllers is the interrupt module. This module
allows for human input to the system. As with the ADC, this application note will give
only a brief introduction. Additionally, there are many interrupts available on the PIC,
and this note will only give an introduction to one of them—the INT0 interrupt.

First, before any interrupts are enabled, the PIC’s interrupt module must be configured to
recognize high priority interrupts:

RCONbits.IPEN = 1; /* enable interrupt priority levels */

Next, Port B, which contains the INT0 pin must be configured. To do this, the following
code can be used:

OpenRB0INT (PORTB_CHANGE_INT_ON & PORTB_PULLUPS_ON & FALLING_EDGE_INT);

At this point, creating a falling edge on the INT0 pin will set the INT0’s interrupt flag,
INTCONbits.INT0IF. Since the INT0 interrupt is a high priority interrupt by default, the
microcontroller will automatically break to address 0x08, the PICF18’s high priority
interrupt vector. At this address, a pointer to the interrupt service routine is held. The
program moves to the ISR, a user defined action is performed, and INT0’s interrupt flag
is cleared. Code Snippet 3 illustrates the basic structure of an INT0 interrupt:

Controlling PWM Signals with the PICF84 5


void my_ISR (void);
#pragma code HIGH_INTERRUPT_VECTOR = 0x8
void high_ISR (void)
{
_asm
goto my_ISR
_endasm
}
#pragma code

#pragma interrupt my_ISR


void my_ISR (void)
{

//interrupt body
INTCONbits.INT0IF = 0; /* clear flag to avoid another interrupt */
}

Code Snippet 3: Basic structure of an ISR for the INT0 interrupt

4. Example: Bang Bang Controller:

Bang Bang is perhaps the simplest control scheme. If the most recently recorded output
of the system is greater than the set point, the output is reduced by 1 increment; if the
most recent result is less than the set point, the output is increased by 1 increment. In
other words, the controller’s action depends only on the sign of the error.

In terms of microcontrollers, controllers require an infinite loop to constantly control the


system. The code snippet shown below shows a bang bang loop. The code shown in
Code Snippet 4 is for a system where decreasing the duty cycle increases the output,
such as a boost converter.

while(1) //forever
{

ConvertADC(); //perform ADC conversion


while(BusyADC()); //wait for result
adc_result = ReadADC();
current_error = adc_result - set_voltage;

/* if the error is greater than 0, we’ll increase the PWM signal by one
adjusted clock cycle. But the CCP1CON<5:4> are then add one to CCPR1L,
which is the 7 MSBs of the PWM signal, and reset CPP1CON<5:4>
*/

if((CCPR1L < 0b10000000) && (CCPR1L > 0b00000010)){//bound the duty cycle
if(current_error < 0){
if(CCP1CON == 0b00111100){
CCPR1L = CCPR1L + 0b00000001;
CCP1CON = 0b00001100;
}

else{
CCP1CON = CCP1CON + 0b00010000;

Controlling PWM Signals with the PICF84 6


}

/*Similar idea as above, but if CCPR1CON<5:4> = 00, we must borrow from


the 8 MSBS in CCPR1L.*/

if(current_error > 0){


if(CCP1CON == 0b00001100){
CCPR1L = CCPR1L - 0b00000001;
CCP1CON = 0b00111100;
}
else{
CCP1CON = CCP1CON - 0b00010000;
}

} //end while

Code Snippet 4: Control loop for a bang bang controller

The control loop works as follows: First, the ADC retrieves and converts the input from
the ADC converter. Then, this value is compared to the set point. The result of this
operation represents the error of the control system. Next, the program ensures that the
duty cycle is greater than 2, and less than 128 (reasoning for this can be found in the
next section).

Next, if the error is less than 0 (output < set point), the duty cycle is incremented by 1
(binary). In terms of time, this will be equal to 1 * TOSC * (prescale value). If the error is
greater than 0 (output > set point), the duty cycle is decremented by 1. In both cases
overflow and carries must be considered, as the complete value for the duty cyle is
CCPR1L:CCP1CON<5:4>. Details pertaining to these carry and overflow considerations
are commented into Code Snippet 4.

At this point, program control is returned to the top of the loop.

Code Snippet 5 is more complete version of a bang bang control program. It includes
much of the information covered in section 3.1.

#include <p18f452.h> /* for the special function register declarations */


#include <portb.h> /* for the RB0/INT0 interrupt */
#include <ADC.h>
/* Set configuration bits for use with ICD2 / PICDEM2 PLUS Demo Board:
* - set HS oscillator
* - disable watchdog timer
* - disable low voltage programming
* - enable background debugging
*/
int v_count = 1;
int status = 1;
long int set_voltage = 0x0139;
long int adc_result,current_error,count;
#pragma romdata CONFIG

Controlling PWM Signals with the PICF84 7


_CONFIG_DECL(_CONFIG1H_DEFAULT & _OSC_HS_1H,
_CONFIG2L_DEFAULT,
_CONFIG2H_DEFAULT & _WDT_OFF_2H,
_CONFIG3H_DEFAULT,
_CONFIG4L_DEFAULT & _LVP_OFF_4L & _DEBUG_ON_4L,
_CONFIG5L_DEFAULT,

_CONFIG5H_DEFAULT,
_CONFIG6L_DEFAULT,
_CONFIG6H_DEFAULT,
_CONFIG7L_DEFAULT,
_CONFIG7H_DEFAULT);
#pragma romdata
//above code written by Microchip

//***************************************
//interrupt code:
//***************************************
void change_voltage (void);

#pragma code HIGH_INTERRUPT_VECTOR = 0x8


void high_ISR (void)
{
_asm
goto my_ISR
_endasm
}
#pragma code

#pragma interrupt my_ISR


void my_ISR (void){
//interrupt code
INTCONbits.INT0IF = 0; /* clear flag to avoid another interrupt */

//**********************************************************
//main()
//
//This function constantly looks at the output voltage
//and compares it to the set voltage. Then, it adjusts
//the PWM signal accordingly.
//
//If the error is greater than
//**********************************************************
void main (void)
{

TRISC = 0b00000000;
TRISD = 0b00000000;
//The PWM signal is on port C
PORTC = 0;
PORTD = 0;

PR2 = 0b00011000 ;
T2CON = 0b00000101 ; //PWM frequency = 4 kHz
CCPR1L = 0b00001100 ; //PWM initial duty cycle = 50%
CCP1CON = 0b00001100 ;

Controlling PWM Signals with the PICF84 8


OpenADC(ADC_FOSC_32 & ADC_RIGHT_JUST & ADC_12_TAD, ADC_CH3 & ADC_INT_OFF,
0);
ADCON1 = 0b00001010;
SetChanADC(ADC_CH3);

OpenRB0INT (PORTB_CHANGE_INT_ON & PORTB_PULLUPS_ON & FALLING_EDGE_INT);

for(count = 1; count < 10000; count++); //wait a bit before controlling,


//so we don't control transients

while(1){

ConvertADC();
while(BusyADC());
adc_result = ReadADC();
current_error = adc_result - set_voltage;

if((CCPR1L < 0b01111000) && (CCPR1L > 0b00000010)){

if(current_error > 0){


if(CCP1CON == 0b00111100){
CCPR1L = CCPR1L + 0b00000001;
CCP1CON = 0b00001100;
}

else{
CCP1CON = CCP1CON + 0b00010000;
}
}

if(current_error < 0){


if(CCP1CON == 0b00001100){
CCPR1L = CCPR1L - 0b00000001;
CCP1CON = 0b00111100;
}

else{
CCP1CON = CCP1CON - 0b00010000;
}
}
} //end duty cycle check

for(count = 1; count < 5000; count++);

} //end while

} //end main

Code Snippet 5: Complete code for a bang bang controller with one input to the ADC, and
an INT0 interrupt.

Controlling PWM Signals with the PICF84 9


5. Issues:

When using the PICF84 as a PWM controller, the programmer must program carefully to
avoid coding errors or incorrect command interpretation. The system under control
should also be tested to ensure that it operates as supposed. In addition to these
general debugging techniques, a few issues are particularly interesting in regards to
using the PIC as a PWM controller:

• As mentioned earlier, it is extremely important that the difference between VDD


and VSS is constant. The difference between these voltages is used in
converting the analog signal from the system to a digital value used in the PIC’s
program. Variations in the ground or positive supply voltage will result inaccurate
conversions, which will lead to erroneous duty cycle changes by the controller
program.

• For any control scheme, the maximum degree of accuracy attainable is one bit.
For a positive power supply of 5 V, and negative supply of 0 V, this corresponds
to a minimum voltage error of 5 * 1/1024 = 4.9 mV. The minimum error can be
reduced by setting a different reference voltage for the AD converter module (see
references for details). For example, if the AD’s reference voltage is set to 3 V,
the minimum voltage error will be 3 * 1/1024 = 2.9 mV.

• Because of this minimum voltage error, the controller will often ‘bounce’ around
the set point of the system. For example, if the set voltage is 2.5 V, and the most
recent voltage read from the AD converter is 2.498 V, the program will see that
the output must be increased, but it must at minimum increase the duty cycle
such that the output is increased 4.9 mV. Now the voltage output should read
2.509 V. The controller will now try to reduce the voltage. Reducing the voltage
by ‘one bit’ would yield an output voltage of 2.498 V. This process will repeat

• This problem can be circumvented by adding a dead zone to the control system,
where the controller does not change the duty cycle if the voltage is within a
specified range, but this reduces the accuracy of the system.

• Care should be taken to make sure that it’s impossible for the duty cycle of the
PWM signal to be 100% or 0%. Both of these conditions can trigger a high
priority interrupt, and PIC may be powered down, depending on its configuration.

• As outlined in the example program, remember that the 2 LSBs of the PWM duty
cycle are stored in CCP1CON<5:4>. Thus, when incrementing or decrementing
this register it is important to realize that 0b0000001 may need to be added or
subtracted from CCPR1L, depending on whether CCP1CON<5:4> is about to
overflow, or is equal to 0b00.

Controlling PWM Signals with the PICF84 10


6. Recommendations:

The PICF84 can be used as a versatile, powerful PWM controller. The control program
can be programmed to accept multiple inputs from other systems from the PICF84’s
ADC module, and multiple human inputs from the interrupt module.

It is possible that using the PIC as a PWM controller may be seen as wasteful, in terms
of cost, time, and board space. These arguments are all true for certain conditions. For
example, A PWM voltage regulator, with a specific output voltage, and a preset mode of
control, costs about 2 – 7 USD. A PICF84 may cost up to 12 USD. In many cases, the
flexibility offered by the PICF84 is worth the increased costs. Furthermore, situations
often arise where an IC with the desired specifications is not available. In this case, the
PIC is the only suitable option.

PWM control with the PIC relies on the use of the ECCP module. Many smaller PIC
packages lack this module, so care should be taken to select a package on which the
ECCP module is available. It is possible to program a PWM controller without the ECCP
module, but it is rather difficult.

7. References:

PIC18F2420/2520/4420/4520 Data Sheet (Microchip)


http://ww1.microchip.com/downloads/en/DeviceDoc/39631a.pdf

Matching MOSFET Drivers to MOSFETs (Microchip)


http://www.semiconductorstore.com/pdf/newsite/microchip/TC4469COE_AN2.pdf

MPLAB C18 C COMPILER GETTING STARTED (Microchip)


https://www.egr.msu.edu/ece/Content_Management/read_tutorial.php?filename=MPLAB
_C18_Getting_Started_51295f.pdf

PIC PWM Calculator & Code Generator


http://www.micro-examples.com/public/microex-navig/doc/097-pwm-calculator.html

Bang-Bang vs. Proportional Control


http://www.fourmilab.ch/hackdiet/www/subsection1_2_3_0_5.html

Controlling PWM Signals with the PICF84 11

You might also like