Está en la página 1de 17

One of the advantages using the Microchip PIC microcontroller Pulse Width Modulation or

PWM for short is; this PWM peripheral circuit is designed to control the DC motor using the full
bridge mode PWM feature. The PWM peripheral works by supplying the correct signal to the HBridge DC motor circuit such as speed controlling and changing the DC motor direction.
Therefore on this tutorial we will learn to use this sophisticated feature offered by Microchip PIC
PWM. For those with the AVR microcontroller background this is also a good chance to learn
the beauty of the different between AVR and PIC microcontroller especially in the PWM
peripheral features.
PWM is used in many industrial mostly for controlling the motor speed. How this PWM signal
could change the DC motor speed is showed on this following PWM signal timing diagram:

From the PWM timing diagram above we could see that by changing the pulse width we could
change the average voltage receipt by the DC motor; the wider the pulse width; the higher the
average voltage receipt by the DC motor. The shorter the pulse width, the lower the average
voltage receipt by the DC motor. Therefore by varying the pulse width we could vary the DC
motor speed. The ratio between the pulse width and the total length of the pulse (time on plus
time off) is called duty cycle, so by saying 100% duty cycle means the DC motor is in its full
speed and 10% duty cycle the DC motor is in its 10% of speed. We are going to use this
following H-Bridge circuit schema on our project:

The circuit above basically is the H-Bridge transistor circuit which connected to the PIC 16F690
PWM pins through the PIC PWM output ports P1A, P1B, P1C and P1D. For more detail how
the H-Bridge circuit works you could refer to the Using Transistor as a Switch posted on this
blog.
The following is the list of hardware and software used in this tutorial:
1. PICJazz 16F690 learning board from ermicro (the PICJazz 16F690 schema)
2. One 5 to 6 volt DC Motor (in this project Iam using geared DC Motor)
3. Four 2K2 Ohm watt resistor for the H-Bridge circuit
4. Four BC639 transistors (or equivalent) for the H-Bridge circuit
5. Four 1N4148 diodes for the H-Bridge circuit
6. One 100nF (0.1uF) 16 volt for the H-Bridge circuit
7. JazzMate 2576-5V power board, the 5 volt switching power supply from ermicro for the HBridge circuit.
8. Microchip MPLAB IDE v8.0 or higher
9. HITEC PICC-Lite PICC-Lite Version 9.60PL1
10. Microchip PICKit2 Programmer
In this tutorial first we will learn how to use the basic single output PWM mode and later on we
will use the full bridge PWM mode for controlling the H-Bridge motor circuit.
1. The PIC16F690 Microcontroller PWM peripheral
The heart of PIC16F690 PWM lays on the TIMER2 peripheral, this timer is used as the basic
counter generator used by the PWM peripheral to generating the PWM pulse, the following is the

PIC 16F690 simplify PWM peripheral diagram (for complete explanation please refer to the
datasheet):

The TMR2 counter register clock is supplied by the prescale circuit which can be selected using
the T2CKPS1 and T2CKPS0 bits in the T2CON register, the TMR2 register value is compared
to the PR2 register which determine the TOP value of TMR2 counter register. When the TMR2
value is equal to the PR2 value, then the TMR2 counter register will be reset to 0.

At the same time the value of TMR2 counter register is compared to the CCPR1L register value
(actually with the CCPR1H register value, but since the CCPR1H equal to the CCPR1L than
we could just say CCPR1L), when the TMR2 value equal to the CCPR1L register value than
the PWM peripheral circuit will reset the CCP1 output (logical 0) and when the TMR2
counter register equal to the PR2 register value than it will set the CCP1 output (logical 1).
Therefore the PR2 register determine the PWM period, by changing the PR2 value we could
change the PWM period and this mean changing the PWM frequency as well. The PWM period
could be calculated using this following formula:
PWM period = (PR2 + 1) x 4 x Tosc x (TMR2 prescale value) second
Where Tosc is the system clock period in second
PWM frequency = 1 / PWM Period Hz
While the PWM pulse width is depend on the CCPR1L register value, therefore by varying the
CCPR1L register value, we could change the PWM pulse width. The PWM width can be
calculated using this following formula:
PWM width = (CCPR1L:CCP1CON<5:4>) x Tosc x (TMR2 prescale value) second
This following table shows the PWM frequency and resolution with the 8 MHz clock:

The PWM output behavior is controlled by the CCP1CON, PWM1CON and PSTRCON
registers, we will talk about these three registers shortly, but the important think to remember
that all the TRIS (three states input/output) register for each of the PWM output ports: P1A,
P1B, P1C and P1D should be set to the output mode.

2. Single Output Mode PWM


With single output PWM mode we could sent the same PWM signal to each of available PWM
pins (i.e. P1A, P1B, P1C and P1D) or to all of them at the same time. Our first C program
example will show you how this single output PWM mode works. Ok lets pasting this following
code into your Microchip MPLAB IDE:
//
***************************************************************************
// File Name
: pwm.c
// Version
: 1.0
// Description : Pulse Width Modulation (PWM)
//
Single Output, Steering Mode
// Author(s)
: RWB
// Target(s)
: PICJazz 16F690 Board
// Compiler
: HITECT PICC-Lite Version 9.60PL1
// IDE
: Microchip MPLAB IDE v8.00
// Programmer
: PICKit2
// Last Updated : 03 Jan 2009
//
***************************************************************************
#include <pic.h>
/*
PIC Configuration Bit:
**
INTIO
- Using Internal RC No Clock
**
WDTDIS
- Wacthdog Timer Disable
**
PWRTEN
- Power Up Timer Enable
**
MCLREN
- Master Clear Enable
**
UNPROTECT - Code Un-Protect

**
UNPROTECT - Data EEPROM Read Un-Protect
**
BORDIS
- Borwn Out Detect Disable
**
IESODIS
- Internal External Switch Over Mode Disable
**
FCMDIS
- Monitor Clock Fail Safe Disable
*/
__CONFIG(INTIO & WDTDIS & PWRTEN & MCLREN & UNPROTECT \
& UNPROTECT & BORDIS & IESODIS & FCMDIS);
// Using Internal Clock of 8 MHz
#define FOSC 8000000L
// Delay Function
#define _delay_us(x) { unsigned char us; \
us = (x)/(12000000/FOSC)|1; \
while(--us != 0) continue; }
void _delay_ms(unsigned int ms)
{
unsigned char i;
do {
i = 4;
do {
_delay_us(164);
} while(--i);
} while(--ms);
}
void main(void)
{
unsigned int ipwm;
unsigned char state;
OSCCON=0x70;
// Select 8 MHz internal clock
TRISC = 0x00;
// Set All on PORTC as Output
ANSEL = 0x00;
// Set PORT AN0 to AN7 digital I/O
ANSELH = 0x00;
// Set PORT AN8 to AN11 as Digital I/O
PORTC = 0x00;
// Turn Off all PORTC
/* Init PWM for Single Output */
CCP1CON=0b00001100; // Single PWM mode; P1A, P1C active-high; P1B, P1D
active-high
CCPR1L=0;
// Start with zero Duty Cycle
T2CON=0b00000101;
// Postscale: 1:1, Timer2=On, Prescale = 1:4
PR2=0x65;
// Frequency: 4.90 kHz
TMR2=0;
// Start with zero Counter
PSTRCON=0b00000100; // Enable Pulse Steering on P1C (RC3)
state=0;
// Start with state 1
for(;;) {
ipwm=0;
while (ipwm < 255) {
CCPR1L=++ipwm;
_delay_ms(5);
// Delay 5 millisecond
}
ipwm=0xff;
while (ipwm > 0) {
CCPR1L=--ipwm;
_delay_ms(5);
}
_delay_ms(100);
if (state == 0) {

// Delay 5 millisecond
// Delay 100 millisecond

state=1;
PSTRCON=0b00001000; // Enable Pulse Steering on P1D (RC2)
} else if (state == 1) {
state=2;
PSTRCON=0b00001100; // Enable Pulse Steering on P1C and P1D (RC3 and
RC2)
} else {
state=0;
PSTRCON=0b00000100;
}

// Enable Pulse Steering on P1C (RC3)

}
}
/* EOF: pwm.c */

This C program basically will use only P1C and P1D PWM output ports, since these ports are
attached to the LEDs on the PICJazz 16F690 board. The program will use the PWM pulse
steering mode to make these two LEDs to slowly turn to bright and slowly turn to dark by
changing the PWM duty cycle.
2.1. CCP1CON: Enhance CCP1 Control Register
This register is use to select the PWM output configuration bit and PWM mode.

By setting P1M1=0 and P1M0=0 bits in the CCP1CON register we select the single output
PWM; setting the CCP1M3=1, CCP1M2=1, CCP1M1=0 and CCP1M0=0 in the CCP1CON
register we select the PWM mode with P1A, P1C active-high; P1B, P1D active-high as this
following code:
/* Init PWM for Single Output */
CCP1CON=0b00001100; // Single PWM mode; P1A, P1C active-high; P1B, P1D
active-high
CCPR1L=0;
// Start with zero Duty Cycle

In this tutorial we just set the additional 2 LSB extended bits (DC1B1 and DC1B0) to all zero
(logical 0) for the CCPR1L register (10-bit wide). We start by setting the CCPR1L to zero
mean we start with zero duty cycle (no PWM output). The next step is to configure the TIMER2
control register:

This register is used to set the postscale, activate the TIMER2 peripheral and set the prescale
clock used by the TMR2 counter register. BY setting the T2CKPS1=0 and T2CKPS0=1 in the
T2CON register we select the 1:4 prescale; and by setting the TMR2ON to logical 1 we
activate the TIMER2 peripheral.
T2CON=0b00000101;
PR2=0x65;
TMR2=0;

// Postscale: 1:1, Timer2=On, Prescale = 1:4


// Frequency: 4.90 kHz
// Start with zero Counter

On the PWM mode, we are not using the postscaller output therefore we just set all the postscale
bits (TOUTPS3, TOUTPS2, TOUTPS1 and TOUTPS0) to all zero. Setting the PR2=065 (or
101 decimal) with the 1:4 prescale and system clock of 8 MHz, then we could calculate the
PWM output frequency according to the formula above as follow:
PWM period = (PR2 + 1) x 4 x Tosc x (TMR2 prescale value) second
PWM period = (101 + 1) x 4 x (1 / 8000000) x 4 = 0.000204 second
PWM frequency = 1 / PWM period = 1 / 0.000204 = 4901.96 Hz = 4.90 kHz

2.2. PSTRCON: Pulse Steering Control Register


In the single output PWM mode this register is use to control where the PWM ports to be used as
the PWM output signal.

By activating the desire steering bit in the PSTRCON register, we could control the PWM
output signal whether to use just one port or simultaneously use all the available PWM output
ports. On the example program above we use the program state to change these bits. First we
activate only the P1C (RC3) port:
PSTRCON=0b00000100;
state=0;

// Enable Pulse Steering on P1C (RC3)


// Start with state 1

Then inside the endless loop we change these bits according to the state status as this following
code:

if (state == 0) {
state=1;
PSTRCON=0b00001000;
} else if (state == 1)
state=2;
PSTRCON=0b00001100;
} else {
state=0;
PSTRCON=0b00000100;
}

// Enable Pulse Steering on P1D (RC2)


{
// Enable Pulse Steering on P1C and P1D (RC3 and RC2)
// Enable Pulse Steering on P1C (RC3)

The steering sync bit (STRSYNC) in the PSTRCON register is set to zero, means the output
steering update occur at the beginning of the instruction cycle.
2.3. Inside the C Program
The C program begins by selecting the 8 MHz internal clock and setting all the I/O ports used on
this examples. After doing the PWM and TIMER2 peripherals setup, we go the endless loop (the
for(;;) C statement with no argument); inside this loop we use the CCPR1L register to change
the PWM duty cycle by assigning the value from 0 to 255 for turning the LEDs slowly from dark
to bright and assigning the CCPR1L register value from 255 to 0 for turning the LEDs from
bright to dark.
ipwm=0;
while (ipwm < 255) {
CCPR1L=++ipwm;
_delay_ms(5);
// Delay 5 millisecond
}
ipwm=0xff;
while (ipwm > 0) {
CCPR1L=--ipwm;
_delay_ms(5);
}

// Delay 5 millisecond

2.4. Downloading the Code


After compiling and simulating your code hook up your PICKit2 programmer to the PICJazz
16F690 board ICSP port turn the PICJazz 16F690 power. From the MPLAB IDE menu select
Programmer -> Select Programmer -> Pickit2 it will automatically configure the connection
and display it on the PICkit2 tab Output windows:

Now you are ready to down load the code from MPLAB IDE menu select Programmer ->
Program; this will down load the HEX code into the PICJazz 16F690 board:

3. Full Bridge Mode PWM


After learning the PWM principal by using the single output mode, now we come to the best
part; the ability to easily control the H-Bridge DC motor circuit is what makes the PIC
microcontrollers unique among the other microcontrollers types. Ok lets take a look at the
following C program code:
//
***************************************************************************
// File Name
: pwm2.c
// Version
: 1.0
// Description : Pulse Width Modulation (PWM)
//
H-Bridge DC Motor Controller
// Author(s)
: RWB
// Target(s)
: PICJazz 16F690 Board
// Compiler
: HITECT PICC-Lite Version 9.60PL1
// IDE
: Microchip MPLAB IDE v8.00
// Programmer
: PICKit2
// Last Updated : 03 Jan 2009
//
***************************************************************************
#include <pic.h>
/*
PIC Configuration Bit:
**
INTIO
- Using Internal RC No Clock
**
WDTDIS
- Wacthdog Timer Disable
**
PWRTEN
- Power Up Timer Enable
**
MCLREN
- Master Clear Enable
**
UNPROTECT - Code Un-Protect
**
UNPROTECT - Data EEPROM Read Un-Protect
**
BORDIS
- Borwn Out Detect Disable
**
IESODIS
- Internal External Switch Over Mode Disable
**
FCMDIS
- Monitor Clock Fail Safe Disable
*/
__CONFIG(INTIO & WDTDIS & PWRTEN & MCLREN & UNPROTECT \
& UNPROTECT & BORDIS & IESODIS & FCMDIS);
// Using Internal Clock of 8 MHz
#define FOSC 8000000L
// Delay Function
#define _delay_us(x) { unsigned char us; \
us = (x)/(12000000/FOSC)|1; \
while(--us != 0) continue; }
void _delay_ms(unsigned int ms)
{
unsigned char i;
do {
i = 4;
do {
_delay_us(164);
} while(--i);
} while(--ms);
}
void main(void)
{
unsigned int ipwm;
unsigned char direction;

OSCCON=0x70;
// Select 8 Mhz internal clock
TRISC = 0x00;
// Set All on PORTC as Output
TRISA = 0x03;
// Input for RA0 and RA1
ANSEL = 0x01;
// Set PORT AN0 to analog input AN1 to AN7 digital I/O
ANSELH = 0x00;
// Set PORT AN8 to AN11 as Digital I/O
PORTC = 0x00;
// Turn Off all PORTC
/* Init PWM for Full Bridge Output */
CCP1CON=0b01001100; // Full Bridge Forward; P1A, P1C active-high; P1B, P1D
active-high
CCPR1L=0;
// Start with zero Duty Cycle
T2CON=0b00000101;
// Postscale: 1:1, Timer2=On, Prescale = 1:4
PR2=0x65;
// Frequency: 4.90 KHz
TMR2=0;
// Start with zero Counter
/* Init ADC */
ADCON0=0b00000000;
// Select Left justify result. ADC port channel 0
ADCON1=0b00110000;
// Select the FRC for 8 MHz
ADON=1;
// turn on the ADC conversion module
direction=0;
// Start with Forward Direction
ipwm=0;
for(;;) {
if (RA1 == 0) {
// Change the Motor Direction when pressed
_delay_ms(1);
if (RA1 == 0) {
// Read again for simple debounce
if (direction == 0) {
direction=1;
// Reverse direction
P1M1=1;
P1M0=1;
} else {
direction=0;
// Forward direction
P1M1=0;
P1M0=1;
}
}
}
GODONE=1;
// initiate conversion on the channel 0
while(GODONE) continue; // Wait conversion done
ipwm = ADRESH;
// Get the highest 8 bit MSB result, ignore the
2 bit LSB
CCPR1L=ipwm;
// Set the Duty Cycle base on the ADC result
/* Blink the LED attached on RC0 */
RC0=1;
// Turn On
_delay_ms(ipwm);
RC0=0;
// Turn Off
_delay_ms(ipwm);
}
}
/* EOF: pwm2.c */

The basic PWM setup routines is almost the same with our previous program, but in this
program I used the PIC 16F690 ADC peripheral for adjusting the DC motor speed; the motor
speed is depend on the voltage value read by the PIC 16F690 ADC peripheral from the PICJazz
16F690 board users trimpot that works as the voltage divider circuit. This value will be assigned
to the CCPR1L duty cycle register.

I also use the PICJazz 16F690 users switch that works as a toggle switch to change the DC
motor direction; and to make it more interesting, I use the LED attached to the RC0 port as the
program life beacon; this LED will blink according to the value we assigned to the CCPR1L
register.
3.1. Full Bridge PWM Mode Setup
The full bridge PWM mode is selected by setting the P1M1 and P1M0 bits in the CCP1CON
register, there are 2 available modes for the full bridge PWM mode; the first one is called full
bridge output forward, this can be selected by setting the P1M1=0 and P1M0 = 1 in the
CCP1CON register. In this mode the P1D pin will be the modulated port; P1A port will active;
while P1B, P1C will inactive:

On the forward full bridge PWM mode the TR1 transistor will on saturate and the TR4 transistor
will on/off base on the PWM signal supplied by P1D port, while the TR2 and TR3 transistors
inactive. The current will flow from TR1 transistor through DC Motor and go to the TR4
transistor, this make the DC motor to turn on the forward direction.
The second full bridge PWM mode is the reverse full bridge PWM mode, this mode can be
selected by setting the P1M1=1 and P1M0 = 1 in the CCP1CON register. In this mode the P1B
pin will be the modulated port; P1C port will active; while P1A, P1D will inactive:

On the reverse full bridge PWM mode the TR3 transistor will on saturate and the TR2 transistor
will on/off base on the PWM signal supplied by P1B port, while the TR1 and TR4 transistors
inactive. The current will flow from TR3 transistor through DC Motor and go to the TR2
transistor, this make the DC motor to turn on the reverse direction.

The following is the C program code for the PWM setup we use in this project:
/* Init PWM for Full
CCP1CON=0b01001100;
active-high
CCPR1L=0;
T2CON=0b00000101;
PR2=0x65;
TMR2=0;

Bridge Output */
// Full Bridge Forward; P1A, P1C active-high; P1B, P1D
//
//
//
//

Start with
Postscale:
Frequency:
Start with

zero
1:1,
4.90
zero

Duty Cycle
Timer2=On, Prescale = 1:4
KHz
Counter

The other register that I mention before is the PWM1CON register (enhanced PWM control
register). This register is used in half bridge PWM mode to put a time delay to the actual PWM
signal transition from zero to active; we are not using this feature on the full bridge PWM mode.
3.2. The PIC 16F690 ADC Peripheral Setup
The ADC peripheral is used to read the voltage value on the analog input port RA0, this voltage
value is supplied by the users trimpot on the PICJazz 16F690 board. Adjusting the trimpot
means changing the voltage value as well; by converting this voltage value to the digital
equivalent value and assign this value to the CCPR1L (duty cycle) register, we could change the
PWM duty cycle and this will effecting the DC motor speed.
The complete PIC microcontroller ADC setup explanation could be read on the article PIC
Analog to Digital Converter C Programming posted on this blog. The following is the C
program code for setting the PIC 16F690 ADC peripheral:
/* Init ADC */
ADCON0=0b00000000;
ADCON1=0b00110000;
ADON=1;

// Select Left justify result. ADC port channel 0


// Select the FRC for 8 MHz
// Turn on the ADC conversion module

By setting the ADFM bit to logical 0 in the ADCON0 register we use the left justified
result. This mean the higher 8 bits ADC result value will be placed in the ADRESH register and
the lower 2 bits ADC result value in the ADRESL register, because we just need the 8 bit value
for the CCPR1L register, so the lowest 2 bits value in ADRESL register can be ignored:
ipwm = ADRESH;
bit LSB
CCPR1L=ipwm;

// Get the highest 8 bit MSB result, ignore the 2


// Set the Duty Cycle base on the ADC result

3.3. Inside the C Program


The C program begins by selecting the 8 MHz internal clock and setting all the I/O ports used on
this examples. After doing the PWM, TIMER2 and ADC peripherals setup, we go the endless
loop (the for(;;) C statement with no argument); inside this loop first we read the users switch,
this switch is attached to the input port RA1 and work as the toggle switch to change the DC
motor direction:
if (RA1 == 0) {

// Change the Motor Direction when pressed

_delay_ms(1);
if (RA1 == 0) {
if (direction == 0) {
direction=1;
P1M1=1;
P1M0=1;
} else {
direction=0;
P1M1=0;
P1M0=1;
}
}

// Read again for simple debounce


// Reverse direction

// Forward direction

By setting the P1M1=1 and P1M0=1 bits in the CCP1CON register, we select the reverse full
bridge PWM mode; setting the P1M1=0 and P1M0=1 bits in the CCP1CON register, we select
the forward full bridge PWM mode.
The PIC ADC peripheral than read the analog input channel 0 (RA0) port and assigned the value
to the CCPR1L register and as the _delay_ms() function argument to the life beacon LED
attached to the output port RC0.
GODONE=1;
// initiate conversion on the channel 0
while(GODONE) continue; // Wait conversion done
ipwm = ADRESH;
// Get the highest 8 bit MSB result, ignore the 2
bit LSB
CCPR1L=ipwm;
// Set the Duty Cycle base on the ADC result
/* Blink the LED attached on RC0 */
RC0=1;
// Turn On
_delay_ms(ipwm);
RC0=0;
// Turn Off
_delay_ms(ipwm);

También podría gustarte