pic18 pulse width modulation

Upload: anoj-pahathkumbura

Post on 14-Apr-2018

256 views

Category:

Documents


0 download

TRANSCRIPT

  • 7/30/2019 PIC18 Pulse Width Modulation

    1/16

    PIC18 Pulse Width Modulation (PWM) DC Motor Speed Controller with the RPM

    Counter Project

    Equipped with sophisticated Enhanced Capture/Compare/PWM (ECCP) peripheral the Microchip PIC18F14K50

    microcontroller could produce up to four PWM channels output. The enhanced PWM (Pulse Width Modulation)

    mode in ECCP peripheral is capable to drive the full bridge DC Motor circuit directly both in forward or

    reverse direction. It also could generate single PWM output on the selectable PIC18F14K50 pins when it

    configured in pulse steering mode. In this tutorial we will take advantage of PIC18F14K50 pulse steering

    mode to drive the DC Motor and at the same time we will build the RPM (Rotation per Minute) counter to

    observe the PWM effect on the DC Motor speed and display it on the 216 LCD.

    The PWM and RPM Counter Project

    On this project we will use the HITEC C PRO PIC18 MCU Family Version 9.63PL3 and Microchip MPLAB IDE

    version 8.40 as our development tools platform. This project also serves as the learning tools of how to use

    many of the Microchip PIC18 advanced peripherals simultaneously to accomplish the project goal. You could

    see the complete project demonstrated on the video at the end of this tutorial; Ok now lets list down all the

    project interesting features:

    Using Advanced 8-bit Microchip PIC18F14K50 microcontroller withPICJazz 20PINdevelopment board Driving the HD44780U 216 LCD in 4-bit data mode Use DC Motor taken from discarded dual shock PS2 Playstation joystick and the Tamiya racing car tire for

    measuring the DC Motor RPM

    http://www.ermicro.com/blog/?p=1461http://www.ermicro.com/blog/?p=1461http://www.ermicro.com/blog/?p=1461http://www.ermicro.com/blog/?p=15http://www.ermicro.com/blog/?p=15http://www.ermicro.com/blog/?p=15http://www.flickr.com/photos/33738505@N05/4170763643/http://www.ermicro.com/blog/?p=15http://www.ermicro.com/blog/?p=1461http://www.ermicro.com/blog/?p=1461
  • 7/30/2019 PIC18 Pulse Width Modulation

    2/16

    Simple and easy to build RPM sensor with the infra red reflective object sensor Use the ADC peripheral to read the trimport value for adjusting the DC Motor Speed and display the PWM

    duty cycle on the LCD

    Use the PIC18F14K50 external interrupt and 16-bit TIMER0 counter to measure the RPM and display it onthe LCD.

    http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_02.jpghttp://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_01.jpghttp://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_02.jpghttp://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_01.jpg
  • 7/30/2019 PIC18 Pulse Width Modulation

    3/16

    The following is the C code that makes this thing happens:/* ***************************************************************************** File Name : pwmrpm.c** Version : 1.0** Description : PIC18 Pulse Width Modulation with RPM Counter** Author : RWB** Target : PICJazz 20PIN Board: PIC18F14K50** Compiler : HI-TECH C PRO PIC18 MCU Family(Lite) Version 9.63PL3** IDE : Microchip MPLAB IDE v8.40** Programmer : PICKit2** Last Updated : 28 Nov 2009** ***************************************************************************/#include /*** PIC18F14K50 Configuration Bit:**** FCMDIS - Fail-Safe Clock Monitor disabled** CPUDIV_0 - No CPU System Clock divide** RCIO - Internal RC Oscillator** PLLDIS - PLL is under software control** ----------------------------------------------------------------------** BORDIS - Brown-out Reset disabled in hardware and software** WDTDIS - WDT is controlled by SWDTEN bit of the WDTCON register

    ** ----------------------------------------------------------------------** MCLREN - MCLR pin enabled, RE3 input pin disabled** ----------------------------------------------------------------------** XINSTDIS - Disable extended instruction set (Legacy mode)** LVPDIS - Single-Supply ICSP disabled*/__CONFIG(1, FCMDIS & CPUDIV_0 & RCIO & PLLDIS);__CONFIG(2, BORDIS & WDTDIS);__CONFIG(3, MCLREN);__CONFIG(4, XINSTDIS & LVPDIS);__CONFIG(5, 0xFFFF);__CONFIG(6, 0xFFFF);__CONFIG(7, 0xFFFF);// LCD Definition#define LCD_HOME 0x02#define LCD_NEXT_LINE 0xC0

    #define LCD_CLEAR 0x01#define LCD_1CYCLE 0#define LCD_2CYCLE 1// RPM Counter Variablevolatile unsigned int rpm_value;char sdigit[6]={'0','0','0','0','0','\0'};/* Delay Function */#define FOSC 16000000UL // Using Internal Clock of 16 MHz#define delay_us(x) { unsigned char _dcnt; \

    _dcnt = (x)/(24000000UL/FOSC)|1; \while(--_dcnt != 0) continue; \

    }void delay_ms(unsigned int cnt){

    unsigned char i;do {

    i = 5;do {

    delay_us(164);} while(--i);

    } while(--cnt);}// PIC18 High-priority Interrupt Servicevoid interrupt high_isr(void){

    static unsigned char pulse_state=0;unsigned int rpm_timer;if (TMR0IF) { // Check for TIMER0 Overflow Interrupt

  • 7/30/2019 PIC18 Pulse Width Modulation

    4/16

    rpm_value = 0; // Reset the RPM ValueTMR0IF=0; // Clear TIMER0 interrupt flag

    }if (INT0IF){ // Check for External INT0 Interruptswitch(pulse_state) {

    case 0: // First Low to High PulseTMR0H = 0; // Zero the high byte in TMR0H BufferTMR0L = 0; // Clear 16-bit TIMER0 Counter

    pulse_state=1;break;

    case 1: // Second Low to High Pulserpm_timer=TMR0L; // Get the first 8-bit TIMER0 Counterrpm_timer+=(TMR0H E-Enable, RC6 -> RS-Register Select, R/W-Always 0*/void LCD_putcmd(unsigned char data,unsigned char cmdtype){

    // Put the Upper 4 bits dataPORTB = data & 0xF0;RC6=0; // RS = 0RC7=1; // E = 1// E=0; write dataRC7=0;delay_us(1); // Delay 1us for 16 MHz Internal Clock

    // cmdtype = 0; One cycle write, cmdtype = 1; Two cycle writesif (cmdtype) {// Put the Lower 4 bits dataPORTB = (data & 0x0F)

  • 7/30/2019 PIC18 Pulse Width Modulation

    5/16

    }void LCD_init(void){

    // Wait for more than 15 ms after VCC rises to 4.5 Vdelay_ms(30);// Send Command 0x30LCD_putcmd(0x30,LCD_1CYCLE);// Wait for more than 4.1 ms

    delay_ms(8);// Send Command 0x30LCD_putcmd(0x30,LCD_1CYCLE);// Wait for more than 100 usdelay_us(200); // Delay 250us for 16 MHz Internal Clock ;// Send Command 0x30LCD_putcmd(0x30,LCD_1CYCLE);// Function set: Set interface to be 4 bits long (only 1 cycle write).LCD_putcmd(0x20,LCD_1CYCLE);// Function set: DL=0;Interface is 4 bits, N=1; 2 Lines, F=0; 5x8 dots font)LCD_putcmd(0x28,LCD_2CYCLE);// Display Off: D=0; Display off, C=0; Cursor Off, B=0; Blinking OffLCD_putcmd(0x08,LCD_2CYCLE);// Display ClearLCD_putcmd(0x01,LCD_2CYCLE);// Entry Mode Set: I/D=1; Increament, S=0; No shift

    LCD_putcmd(0x06,LCD_2CYCLE);// Display On, Cursor OffLCD_putcmd(0x0C,LCD_2CYCLE);

    }void LCD_puts(const char *s){

    while(*s != 0) { // While not Nullif (*s == '\n')

    LCD_putcmd(LCD_NEXT_LINE,LCD_2CYCLE); // Goto Second Lineelse

    LCD_putch(*s);s++;

    }}// Implementing integer value from 0 to 65530char *num2str(unsigned int number,unsigned char start_digit){

    unsigned char digit;if (number > 65530) number = 0;

    digit = '0'; // Start with ASCII '0'while(number >= 10000) // Keep Looping for larger than 10000{

    digit++; // Increase ASCII characternumber -= 10000; // Subtract number with 10000

    }

    sdigit[0]='0'; // Default first Digit to '0'if (digit != '0') sdigit[0]=digit; // Put the first digitdigit = '0'; // Start with ASCII '0'while(number >= 1000) // Keep Looping for larger than 1000

    { digit++; // Increase ASCII characternumber -= 1000; // Subtract number with 1000

    }sdigit[1]='0'; // Default Second Digit to '0'if (digit != '0') sdigit[1]=digit; // Put the Second digitdigit = '0'; // Start with ASCII '0'while(number >= 100) // Keep Looping for larger than 100{

    digit++; // Increase ASCII characternumber -= 100; // Subtract number with 100

    }

  • 7/30/2019 PIC18 Pulse Width Modulation

    6/16

    sdigit[2]='0'; // Default Second Digit to '0'if (digit != '0') sdigit[2]=digit; // Put the Second digitdigit = '0'; // Start with ASCII '0'while(number >= 10) // Keep Looping for larger than 10{

    digit++; // Increase ASCII characternumber -= 10; // Subtract number with 10

    }

    sdigit[3]='0'; // Default Second Digit to '0'if (digit != '0') sdigit[3]=digit; // Put the Second digitsdigit[4]='0' + number;return(sdigit + start_digit);

    }void main(void){

    unsigned char motor_stat,duty_cycle;OSCCON=0x70; /* Select 16 MHz internal clock */// Initial PORTTRISA = 0x30; // Input for RA4 and RA5TRISC = 0x01; // Set RC0 as Input, RC on PORTC as OutputPORTC = 0x00; // Initial Port CTRISB = 0x00; // Set PORTB as OutputPORTB = 0x00; // Initial Port BTRISB = 0x00; // Set All on PORTB as Output

    ANSEL = 0x08; // Set PORT AN3 to analog inputANSELH = 0x00; // Set PORT AN8 to AN11 as Digital I/O// Initial LCD using 4 bits data interfaceLCD_init();LCD_puts("PICJazz 20-PIN\n");// Init ADCADCON0=0b00001101; // ADC port channel 3 (AN3), Enable ADCADCON1=0b00000000; // Use Internal Voltage Reference (Vdd and Vss)ADCON2=0b00101011; // Left justify result, 12 TAD, Select the FRC for 16 MHz

    // Init TIMER0: Period: 4 x Tosc x Prescale for each counter// Tosc = 1/16 Mhz = 0.0000000625// TIMER0 Period: 4 x 0.0000000625 x 128 = 0.000032 Second = 0.032 msT0CON = 0b10000110; // TIMER0 Enable, use 16-bit timer and prescale 1:128TMR0H = 0; // Zero the high byte in TMR0H BufferTMR0L = 0; // Clear 16-bit TIMER0 CounterTMR0IE = 1; // Enable TIMER0 Overflow Interrupt// Set the External Interrupt on INT0 (RC0) PortINT0IE = 1; // Enables the INT0 external interruptINTEDG0 = 1; // Interrupt on rising edge

    // Init PWM for Single OutputCCP1CON=0b00001100; // Single PWM mode; P1A, P1C active-high; P1B, P1D active-highCCPR1L=0; // Start with zero Duty CyclePSTRCON=0b00000100; // Enable PIC Pulse Steering PWM on RC3 Port// PWM Period = 4 x Tosc x (PR2 + 1) x TMR2 Prescale Value// Tosc = 1/16 Mhz = 0.0000000625// PWM Period = 4 x 0.0000000625 x 201 x 4 = 0.000201// PWM Frequency = 1/PWM Period = 1/0.000201 = 4.975 kHzT2CON=0b00000101; // Postscale: 1:1, Timer2=On, Prescale = 1:4PR2=200; // Frequency: 4.975 kHz

    TMR2=0; // Start with zero Counter

    // Initial Variable usedrpm_value=0;motor_stat=0; // Motor Off Conditionduty_cycle=0; // 0 Duty Cycle

    // Now Enable the InterruptIPEN = 1; // Enable High Priority InterruptGIEH = 1; // Global Interrupt Enable (High Priority)for(;;) {if (RA5 == 0) { // Read Switch

  • 7/30/2019 PIC18 Pulse Width Modulation

    7/16

    delay_ms(1);if (RA5 == 0) { // Read again for Simple Debouncemotor_stat ^= 0x01;

    }}

    if (motor_stat) {GODONE=1;

    while (GODONE) continue; // Wait conversion doneduty_cycle=ADRESH; // Get the High byte ADC 8-bit result

    } else {duty_cycle=0;

    }

    // Assign duty cycle to the PWM CCPR1L registerCCPR1L = duty_cycle;// Display the Information on the LCDLCD_putcmd(LCD_HOME,LCD_2CYCLE); // LCD HomeLCD_puts("Duty Cycle: "); LCD_puts(num2str((int)((duty_cycle/255.0) * 100.0),3));LCD_puts(" %");LCD_putcmd(LCD_NEXT_LINE,LCD_2CYCLE); // Goto Second LineLCD_puts("RPM: "); LCD_puts(num2str(rpm_value,1));

    // Put the delay here

    delay_ms(10);}

    }/* EOF: pwmrpm.c */

    The PIC18 Pulse Steering PWM mode

    The heart of the PIC18F14K50 pulse steering PWM mode is rely on the TIMER2 peripheral, where it used as

    the basic counter generator for the PWM signal. The TIMER2 counter clock (TMR2) is supplied by selectable

    prescale clock, this prescale circuit will divide the system clock by 1, 4 or 16 respectively. The prescale could

    be selected by assigning the T2CKPS1 and T2CKPS0 bits in the T2CON register.

    http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_15.jpg
  • 7/30/2019 PIC18 Pulse Width Modulation

    8/16

    The TMR2 register value is continuously compared to the PR2 register which determine the TOP value of

    the TMR2 counter register. When the TMR2 register value reach the PR2 value, then the TMR2 counter

    register value will be reset to 0.

    At the same time the value ofTMR2 counter register is also being compared to the CCPR1L register value

    (actually with the CCPR1H register value, since the CCPR1H equal to CCPR1L than we could say CCPR1L),

    when the TMR2 reach the CCPR1L 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 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 = 4 x Tosc x ( PR2 + 1) x (TMR2 prescale value) second

    http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_08.jpghttp://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_07.jpghttp://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_08.jpghttp://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_07.jpg
  • 7/30/2019 PIC18 Pulse Width Modulation

    9/16

    Where Tosc is the system clock period in second

    PWM frequency = 1 / PWM Period Hz

    By assigning the PR2 register with 200 and select the prescale to 4; and applying all these values to the

    formula above, we could determine the PWM frequency for our DC Motor base on the internal system

    oscillator of 16 MHz as follow:

    PWM period = 4 x (1 / 16.000.000) x 201 x 4 = 0.000201 secondTherefore the PWM frequency is:

    PWM frequency = 1 / 0.000201 = 4.975 kHz

    The T2CON (TIMER2 Control) register is used select the postscale (T2OUTPS), activate the TIMER2

    peripheral (TMR2ON) 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

    TMR2ONto logical 1 we activate the TIMER2 peripheral.

    The following is the C code to initialize the TIMER2 peripheral:// PWM Period = 4 x Tosc x (PR2 + 1) x TMR2 Prescale Value// Tosc = 1/16 Mhz = 0.0000000625// PWM Period = 4 x 0.0000000625 x 201 x 4 = 0.000201// PWM Frequency = 1/PWM Period = 1/0.000201 = 4.975 kHzT2CON=0b00000101; // Postscale: 1:1, Timer2=On, Prescale = 1:4PR2=200; // Frequency: 4.975 kHzTMR2=0; // Start with zero Counter

    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.

    On 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 yet). In single PWM mode we could select the PWM output to PIC18F14K50 RC3

    output port by setting the STRC bit on PSTRCON(Pulse Steer Control) register to the logical 1 while

    other bits is set to logical 0. The following is the C code:// Init PWM for Single OutputCCP1CON=0b00001100; // Single PWM mode; P1A, P1C active-high; P1B, P1D active-highCCPR1L=0; // Start with zero Duty CyclePSTRCON=0b00000100; // Enable PIC Pulse Steering PWM on RC3 Port

    http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_10.jpghttp://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_09.jpghttp://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_10.jpghttp://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_09.jpg
  • 7/30/2019 PIC18 Pulse Width Modulation

    10/16

    By applying the analog value read from the 10K trimport connected to the PIC18F14K50 RA4 pins, we could

    easily varying the PWM duty cycle by changing the voltage divider output formed by the 10K trimport. The

    following C code shows how we use the PIC18F14K50 microcontroller ADC peripheral to change the PWM

    duty cycle; for more information about using the ADC peripheral on PIC18 families you could read my

    previous posted blogPIC18 Microcontroller Analog to Digital Converter with Microchip C18 Compiler:// Init ADC

    ADCON0=0b00001101; // ADC port channel 3 (AN3), Enable ADCADCON1=0b00000000; // Use Internal Voltage Reference (Vdd and Vss)ADCON2=0b00101011; // Left justify result, 12 TAD, Select the FRC for 16 MHz......if (motor_stat) {

    GODONE=1;while (GODONE) continue; // Wait conversion doneduty_cycle=ADRESH; // Get the High byte ADC 8-bit result

    } else {duty_cycle=0;

    }

    // Assign duty cycle to the PWM CCPR1L registerCCPR1L = duty_cycle;

    The enhanced PWM feature on the PIC18 families actually is almost identical to the PIC16 families series,therefore you could read more about the enhanced PWM feature if you want to drive the H-bridge DC motor

    circuit from my previous posted blogH-Bridge Microchip PIC Microcontroller PWM motor Controller.

    The RPM Sensor

    To count the DC motor rotation per minute (RPM), I decided to use the infra red reflective object sensor

    Junye JY209-01 or you could replace it with Fairchild QRE00034; as this type of sensor will make sensing

    the DC motor rotation become easier.

    http://www.ermicro.com/blog/?p=1408http://www.ermicro.com/blog/?p=1408http://www.ermicro.com/blog/?p=1408http://www.ermicro.com/blog/?p=706http://www.ermicro.com/blog/?p=706http://www.ermicro.com/blog/?p=706http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_11.jpghttp://www.ermicro.com/blog/?p=706http://www.ermicro.com/blog/?p=1408
  • 7/30/2019 PIC18 Pulse Width Modulation

    11/16

    The infra red reflective object sensor work by simply emitting the infra red beam and when it encounter the

    white object surface than the infra red beam will be reflected back to the phototransistor; next the

    phototransistor and the 2N3904 transistor which formed the Darlington pair will start to conduct and will

    generate enough voltage across the 470 Ohm resistor to be considered by the PIC18F14K50 microcontroller

    build in Schmitt trigger RC0input port as the logical 1. When the infra red beam encounters the black tire

    surface than both of the phototransistor and 2N3904 transistor will turn off; and the voltage across 470Ohm resistor will drop to zero volt (logical 0).

    Therefore by timing the generated pulse period by the infra red reflective object sensor we could easily

    calculate the RPM using this following formula:

    Frequency = 1/T Hz; T is the generated pulse period in second.

    RPM (Rotation per Minute) = Frequency x 60

    The following pictures show in detail of how I put the PS2 Playstation dual shock DC motor, the Tamiya

    racing car tire with the white sticker and the infra red reflective object sensor for this project.

    http://www.flickr.com/photos/33738505@N05/4170762705/
  • 7/30/2019 PIC18 Pulse Width Modulation

    12/16

    The PIC18 External Interrupt

    As I mention before that in order to calculate the DC motor RPM, we could simply calculate the period of the

    pulse generated by the RPM sensor shown above. One of the methods to measure the pulse period is to use

    the PIC18F14K50 microcontroller ECCP (Enhanced Capture/Compare/PWM) peripheral in the capture mode

    to calculate the period; in the capture mode we could easily use the 16-bit TIMER1 or TIMER3 to count the

    pulse period by feeding the pulse directly to the CCP1 pins (RC5). The capture interrupt will be generated

    every rising edge of the pulse (or falling edge), therefore by knowing the exact TIMER1 or TIMER3 counter

    clock time period and get the timer 16-bit counted value between the two rising edge pulse we could

    calculate the RPM.

    Unfortunately we could not use the Microchip PIC18F14K50 microcontroller ECCP peripheral as this

    peripheral has already being used to generate the PWM signal for the DC motor in our project, but using the

    same principal we could make use of the PIC18F14K50 external interrupt peripheral on pin INT0 (RC0) or

    INT1 (RC1). This external interrupt peripheral will generate interrupt on every rising edge (or falling edge)

    of the pulse; therefore by combining it with the 16-bit TIMER0 counter mode now we could calculate the

    RPM as shown on the following diagram.

    http://www.flickr.com/photos/33738505@N05/4170765217/
  • 7/30/2019 PIC18 Pulse Width Modulation

    13/16

    As shown on the above picture, first we have to activate the PIC18F14K50 microcontroller external interrupt

    and configure it to detect the pulse rising edge; next we configure the TIMER0 peripheral for the RPM period

    counter.

    By setting the TMR0E and INT0IEbits to logical 1 on the PIC18F14K50 microcontroller interrupt control

    register (INTCON) and TMT0ONbits to logical 1 on the TIMER0 control register (T0CON), we activateboth the TIMER0 and External peripherals. Selecting the 1:256 prescale value we could calculate the time

    required to increase the TIMER0 16-bit counter.

    TIMER0 Clock period = 4 x Tosc x TMR2 prescale value second

    TIMER0 Clock period = 4 x (1/16.000.000) x 128 = 0.000032 second = 0.032 ms

    This mean the TIMER0 counter required 0.032 ms to increase the TMR0L and TMR0H registers counter

    value by one. The following C code shows the PIC18F14K50 microcontroller external interrupt and TIMER0

    peripherals initialization:// Init TIMER0: Period: 4 x Tosc x Prescale for each counter

    http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_13.jpghttp://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_12.jpghttp://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_13.jpghttp://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_12.jpg
  • 7/30/2019 PIC18 Pulse Width Modulation

    14/16

    // Tosc = 1/16 Mhz = 0.0000000625// TIMER0 Period: 4 x 0.0000000625 x 128 = 0.000032 Second = 0.032 msT0CON = 0b10000110; // TIMER0 Enable, use 16-bit timer and prescale 1:128TMR0H = 0; // Zero the high byte in TMR0H BufferTMR0L = 0; // Clear 16-bit TIMER0 CounterTMR0IE = 1; // Enable TIMER0 Overflow Interrupt// Set the External Interrupt on INT0 (RC0) PortINT0IE = 1; // Enables the INT0 external interrupt

    INTEDG0 = 1; // Interrupt on rising edge

    By setting the INTEDG0to logical 1 on INTCON2 (interrupt Control 2) register we choose the Rising

    Edge detection. The RPM value is being calculated inside the interrupt service routine as shown on this

    following C code:// PIC18 High-priority Interrupt Servicevoid interrupt high_isr(void){

    static unsigned char pulse_state=0;unsigned int rpm_timer;if (TMR0IF) { // Check for TIMER0 Overflow Interruptrpm_value = 0; // Reset the RPM ValueTMR0IF=0; // Clear TIMER0 interrupt flag

    }if (INT0IF){ // Check for External INT0 Interruptswitch(pulse_state) {

    case 0: // First Low to High PulseTMR0H = 0; // Zero the high byte in TMR0H BufferTMR0L = 0; // Clear 16-bit TIMER0 Counterpulse_state=1;break;

    case 1: // Second Low to High Pulserpm_timer=TMR0L; // Get the first 8-bit TIMER0 Counterrpm_timer+=(TMR0H

  • 7/30/2019 PIC18 Pulse Width Modulation

    15/16

    The PICKit2 Logic Analyzer tool could be used by running the PICKit2 programmer Version v2.61 and

    selecting the Logic Tools from the Tools menu; set the Rising Edge trigger on the channel 3 and 100 ms

    Sample Rate; next press the RUN button. After the pulse appears check the Cursor checkbox to activate the

    X and Y horizontal bar to measure the pulse period.

    As shown on the above picture, the channel 3 on the PICKit2 logic analyzer tool show that the measured

    pulse frequency is about 77.52 Hz; this mean the RPM is about 4651 (77.52 x 60) which is close enough to

    the RPM calculated value 4641 displayed on the LCD at 72% PWM duty cycle.

    The 216 LCD Display

    To display both of the PWM duty cycle and RPM value, I used the Hitachi HD44780U or the equivalent

    microcontroller 216 LCD with back light LED in 4-bit data mode. Most of the LCD function C routine I use in

    this project is taken from my previous posted blogAVR LCD Thermometer Using ADC and PWM Poject;

    where you could read more information about the principal of how to drive this kind of display. The following

    is the list of C function for driving the LCD:

    LCD_putch() function is used to display single character on the LCD LCD_putcmd() function is used to send LCD command (e.g. clear the LCD, move to second row, etc) LCD_init() function is used to initialized the 216 LCD; this function will initialized the 216 LCD into 4-bit

    data mode

    LCD_puts() function is used to display a string on the LCD num2str() function is used to convert a numeric value to a string, we use this function to display numeric

    value on the LCD.

    Inside the C Code

    The C program begins by selecting the 16 MHz internal clock and setting all the I/O ports used on this

    project. After doing the LCD, ADC, TIMER0, External Interrupt and PWM/TIMER2 peripherals setup. After

    enabling the high priority interrupt and activating the global interrupt the code enter the for(;;) endless

    http://www.ermicro.com/blog/?p=519http://www.ermicro.com/blog/?p=519http://www.ermicro.com/blog/?p=519http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_14.jpghttp://www.ermicro.com/blog/?p=519
  • 7/30/2019 PIC18 Pulse Width Modulation

    16/16

    loop. Inside this loop; first we read the users switch, this switch is attached to the PIC18F14K50

    microcontroller input port RA5 and work as the toggle switch to run or stop the DC motor:if (RA5 == 0) { // Read Switch

    delay_ms(1);if (RA5 == 0) { // Read again for Simple Debouncemotor_stat ^= 0x01;

    }

    }

    The PIC18F14K50 microcontroller ADC peripheral than read the analog input on channel 3 (RA4) port and

    assigned the value to the CCPR1L register where it used to control the PWM duty cycle.if (motor_stat) {

    GODONE=1;while (GODONE) continue; // Wait conversion doneduty_cycle=ADRESH; // Get the High byte ADC 8-bit result

    } else {duty_cycle=0;}// Assign duty cycle to the PWM CCPR1L registerCCPR1L = duty_cycle;Then next we display the duty cycle and the RPM value on the 2x16 LCD:// Display the Information on the LCDLCD_putcmd(LCD_HOME,LCD_2CYCLE); // LCD Home

    LCD_puts("Duty Cycle: "); LCD_puts(num2str((int)((duty_cycle/255.0) * 100.0),3));LCD_puts(" %");LCD_putcmd(LCD_NEXT_LINE,LCD_2CYCLE); // Goto Second LineLCD_puts("RPM: "); LCD_puts(num2str(rpm_value,1));

    Downloading and Running the Code

    After compiling and simulating your code hook up your PICKit2 programmer to the PICJazz 20PIN

    development and learning board ICSP port turn power on. 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 20PIN board with the

    Microchip PIC18F14K50 microcontroller on it.

    Now its time to run your PWM and RPM counter code, where you could enjoy this following video showing all

    of the process that weve been going through.