PWM 2 channel with XC8

9:52:00 PM
I. GIỚI THIỆU
Vi điều khiển PIC họ 16F877A có 2 bộ PWM dùng để điều khiển tốc độ động cơ DC.
Phần này sẽ khảo sát chi tiết khối PWM của PIC và tập lệnh lập trình C cho PWM.
II. KHẢO SÁT PWM


 1. SƠ ĐỒ KHỐI PWM của PIC16F877A có sơ đồ khối như hình 7-1:







Khối PWM gồm có 2 mạch so sánh: mạch so sánh 8 bit với mạch so sánh 10 bit.
Mạch so sánh 8 bit sẽ so sánh giá trị đếm của timer2 với giá trị của thanh ghi PR2 (period register),
giá trị trong timer2 tăng từ giá trị đặt trước cho đến khi bằng giá trị của PR2 thì mạch so sánh sẽ set flip flop
RS làm ngõ ra CCPx lên mức 1. Đồng thời nạp giá trị 10 bit từ thanh ghi CCPRxL sang thanh ghi CCPRxH,
timer2 bị reset và bắt đầu đếm lại cho đến khi giá trị của timer2 bằng giá trị của CCPRxH thì mạch so sánh
sẽ reset flip flop RS làm ngõ ra CCPx về mức 0. Quá trình này lặp lại.
Dạng sóng điều chế PWM như hình 7-2:



Chu kỳ không thay đổi, muốn thay đổi thời gian xung ở mức 1 thì ta thay đổi hệ số chu kỳ (duty
cycle). Khi hệ số chu kỳ thay đổi thì điện áp hay dòng trung bình thay đổi.
Hệ số chu kỳ càng lớn thì dòng trung bình càng lớn, nếu điều khiển động cơ sẽ làm thay đổi tốc độ.
2. TÍNH CHU KỲ XUNG PWM
Chu kỳ PWM của PIC16F877A được tính theo công thức:
PERIODPWM = [(PR2) +1] * 4 * TOSC * PVTMR2
TOSC Trong đó: là chu kỳ của tụ thạch anh tạo dao động
PVTMR2 (prescale value) giá trị chia trước của timer2

Khi giá trị của timer 2 (TMR2) bằng giá trị của thanh ghi PR2 thì 3 sự kiện theo sau sẽ xảy ra:
· Thanh ghi TMR2 bị xóa
· Tín hiệu ngõ ra CCPx lên mức 1, ngoại trừ hệ số chu kỳ bằng 0% thì CCPx vẫn ở mức 0.
· Hệ số chu kỳ PWM được chuyển từ thanh ghi CCPRxL sang thanh ghi CCPRxH. 3. TÍNH HỆ SỐ CHU KỲ XUNG PWM
Hệ số chu kỳ được thiết lập bởi giá trị lưu trong thanh ghi 10 bit gồm CCPRxL 8 bit và 2 bit còn lại là
bit thứ 4 và thứ 5 ở trong thanh ghi CCPxCON – kí hiệu là CCPxCON<5:4>.
Giá trị của hệ số chu kỳ là 10 bit nên có thể thay đổi từ 0 đến 1023 tạo ra 1024 cấp giá trị điều khiển.
Giá trị 10 bit thì 8 bit có trọng số lớn lưu trong thanh ghi CCPRxL và 2 bit còn lại có trọng số thấp thì
ở CCPxCON<5:4>.
Hệ số chu kỳ của PIC16F877A được tính theo công thức:
DUTY_ CYCLEPWM = (CCPRxL : CCPxCON < 5 : 4 >) * TOSC * PVTMR2


Ảnh mô phỏng protues.

pwm pic16f877a xc8

Đây là code.

#define _XTAL_FREQ 20000000
#define TMR2PRESCALE 4

#include <xc.h>
// CONFIG
#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = ON       // Brown-out Reset Enable bit (BOR enabled)
#pragma config LVP = OFF        // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF        // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)
long freq;

int PWM_Max_Duty()
{
  return(_XTAL_FREQ/(freq*TMR2PRESCALE);
}

PWM1_Init(long fre)
{
  PR2 = (_XTAL_FREQ/(fre*4*TMR2PRESCALE)) - 1;
  freq = fre;
}

PWM2_Init(long fre)
{
  PR2 = (_XTAL_FREQ/(fre*4*TMR2PRESCALE)) - 1;
  freq = fre;
}

PWM1_Duty(unsigned int duty)
{
  if(duty<1024)
  {
    duty = ((float)duty/1023)*PWM_Max_Duty();
    CCP1X = duty & 2;
    CCP1Y = duty & 1;
    CCPR1L = duty>>2;
  }
}

PWM2_Duty(unsigned int duty)
{
  if(duty<1024)
  {
    duty = ((float)duty/1023)*PWM_Max_Duty();
    CCP2X = duty & 2;
    CCP2Y = duty & 1;
    CCPR2L = duty>>2;
  }
}

PWM1_Start()
{
  CCP1M3 = 1;
  CCP1M2 = 1;
  #if TMR2PRESCALAR == 1
    T2CKPS0 = 0;
    T2CKPS1 = 0;
  #elif TMR2PRESCALAR == 4
    T2CKPS0 = 1;
    T2CKPS1 = 0;
  #elif TMR2PRESCALAR == 16
    T2CKPS0 = 1;
    T2CKPS1 = 1;
  #endif
  TMR2ON = 1;
  TRISC2 = 0;
}

PWM1_Stop()
{
  CCP1M3 = 0;
  CCP1M2 = 0;
}

PWM2_Start()
{
  CCP2M3 = 1;
  CCP2M2 = 1;
  #if TMR2PRESCALE == 1
    T2CKPS0 = 0;
    T2CKPS1 = 0;
  #elif TMR2PRESCALE == 4
    T2CKPS0 = 1;
    T2CKPS1 = 0;
  #elif TMR2PRESCALE == 16
    T2CKPS0 = 1;
    T2CKPS1 = 1;
  #endif
    TMR2ON = 1;
    TRISC1 = 0;
}

PWM2_Stop()
{
  CCP2M3 = 0;
  CCP2M2 = 0;
}

void main()
{
  unsigned int i=0,j=0;
  PWM1_Init(5000);
  PWM2_Init(5000);
  TRISD = 0xFF;
  TRISB = 0;
  PWM1_Duty(0);
  PWM2_Duty(0);
  PWM1_Start();
  PWM2_Start();
  do
  {
    if(RD0 == 0 && i<1000)
      i=i+10;
    if(RD1 == 0 && i>0)
      i=i-10;
    if(RD2 == 0 && j<1000)
      j=j+10;
    if(RD3 == 0 && j>0)
      j=j-10;
    PWM1_Duty(i);
    PWM2_Duty(j);

    __delay_ms(50);
  }while(1);
}

Share this

Related Posts

Previous
Next Post »