Đăng vào

MPLAB XC8: Lập trình PWM

Pulse Width Modulation - PWM (Điều biến độ rộng xung) là một trọng những phương pháp đơn giản và phổ biến được sử dụng để tạo ra tín hiệu điện áp analog (tương tự)  từ tín hiệu digital (số). Phương pháp này được sử dụng trong nhiều ứng dụng: Digital to Analog Coverter (DAC), điều khiển tốc độ động cơ, điều khiển độ sáng…

Tín hiệu PWM có dạng ON – OFF, sau một khoảng thời gian tín hiệu chuyển từ trạng thái ON qua OFF hoặc ngược lại. Khoảng thời gian tín hiệu giữ một trạng thái gọi là Modulation, sự kiện thay đổi trạng thái tín hiệu gọi là Pulse, khoảng thời gian giữa hai tín hiệu ON (hoặc OFF) liên tiếp gọi là Duty Cycle.

PWM

Hầu hết các loại vi điều khiển PIC đều có thể xuất xung PWM bằng module CCP (Capture / Compare / PWM). Để sử dụng các module đó chúng ta cần học cách lập trình trên MPLAB XC8, trong ví dụ này chúng ta sử dụng PIC 16F877A.

CCP – Capture / Compare / PWM Module

Micrichip PIC 16F877A có 2 module CCP là CCP1 và CCP2, mỗi module bao gồm 2 thanh ghi 8 bit kết hợp chúng ta sẽ có được:

  • 16 bit Capture Register
  • 16 bit Compare Register
  • PWM Master / Slave Duty Cycle register

Bài hướng dẫn này chỉ sử dụng module CCP để xuất xung PWM với độ phân giải là 10bit.

This tutorial deals only with PWM operation of CCP module. Using this we can generate PWM output having resolution up to 10 bit. Module CCP sử dụng chân RC1 và RC2 của PORTC nên bit TRISC1 và TRISC2 phải về mức 0 để chân RC1 và RC2 là output.

Working

Sơ đồ khối PWM của module CCP

So do PWM

Timer 2 được sử dụng  trong module CCP, giá trị của thanh ghi TMR2 tăng từ 0 cho đến giá trị lớn nhất với mỗi input clock. Input clock được xác định với tần số làm việc của vi điều khiển (Fosc) và giá trị bộ chia (prescaler) của timer 2.

Module PWM
  • Prescaler Input Clock : Fosc / 4
  • Timer 2 Input Clock : (Fosc / 4) / Prescaler Value = Fosc / (4*Prescaler)

Time period của xung PWM được xác định thông qua giá trị của thanh ghi PR2. Bộ Comparator so sánh giá trị của thanh ghi PR2 và TMR2, khi chúng bằng nhau thì bộ Comparator thiết lập xung PWM ở mức High, nó cũng reset giá trị của Timer 2 về 0.

Giá trị duty cycle của PWM được xác định bởi thanh ghi CCPR1L và CCP1CON<5:4> và có thể thay đổi trong quá trình hoạt động. Giá trị duty cycle được lưu để thanh ghi CCPR1H và 2 bit nội chốt lại khi giá trị PR2 và Timer 2 bằng nhau. Điều này nhằm tránh sự mất ổn định của ngõ ra PWM khi thay đổi giá trị duty cycle.

Lưu ý rằng:

  • Giá trị Duty Cycle nên nhỏ hơn PR2 để phù hợp với việc tạo xung PWM
  • Không thể sử dụng 2 tần số PWM khác nhau cho cả 2 module CCP do chúng đều dùng Timer 2 để hoạt động

Độ phân giải PWM

Độ phân giải của PWM được tính bởi công thức

Do phan giai PWM

Cách sử dụng

  • Đặt giá trị PWM Period bằng cách set giá trị cho thanh ghi PR2
  • Đặt giá trị PWM Duty Cycle bằng cách set giá trị thanh ghi CCPR1L và CCP1CON<5:4>
  • Đặt chân CCP là output bằng cách set bit TRIS<2> và TRIS<1>
  • Đặt giá trị bộ chia của Timer 2 và enable Timer 2 bằng cách set giá trị cho thanh ghi T2CON
  • Cuối cùng cấu hình module CCP hoạt động ở chế độ PWM bằng cách set giá trị cho thanh ghi CCP

Sơ đồ mạch điện

Code MPLAB XC8

#define _XTAL_FREQ 20000000
#define TMR2PRESCALE 4
#include // BEGIN CONFIG
#pragma config FOSC = HS   // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF  // Watchdog Timer Enable bit (WDT enabled)
#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)
//END CONFIG
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);
}

Ghi chú:

  • Hàm PWM1_Init(frequency) & PWM2_Init(frequency) dùng để khởi tạo chế độ PWM cho module CCP1 và CCP2 với giá trị tần số xung là frequency.
  • Hàm PWM1_Duty(duty) & PWM2_Duty(duty) dùng để đặt giá tri duty cycle của xung PWM. Giá trị duty chạy từ 0->1023 tương ứng với duty của PWM từ 0%->100%
  • Hàm PWM1_Start() & PWM2_Start() dùng để tiến hành việc xuất xung PW.
  • Hàm PWM1_Stop() & PWM2_Stop() dùng để dừng việc xuất xung PWM