Generating PWM using PIC Microcontroller with MPLAB and XC8 - Tạo PWM bằng Vi điều khiển PIC với MPLAB và XC8
Đây là hướng dẫn thứ 10 của chúng tôi về Học vi điều khiển PIC sử dụng MPLAB và XC8. Cho đến nay, chúng tôi đã đề cập đến nhiều hướng dẫn cơ bản như nhấp nháy LED với PIC, Bộ hẹn giờ trong PIC, giao diện LCD, giao tiếp 7 đoạn, ADC sử dụng PIC, v.v. Nếu bạn là người mới bắt đầu, vui lòng truy cập danh sách đầy đủ các hướng dẫn về PIC tại đây và bắt đầu học.
Trong hướng dẫn này, chúng ta sẽ học Cách tạo tín hiệu PWM bằng PIC PIC16F877A. MCU PIC của chúng tôi có một mô-đun đặc biệt được gọi là mô-đun So sánh Capture (CCP) có thể được sử dụng để tạo tín hiệu PWM. Ở đây, chúng tôi sẽ tạo ra PWM 5 kHz với chu kỳ nhiệm vụ thay đổi từ 0% đến 100%. Để thay đổi chu kỳ làm việc, chúng tôi đang sử dụng một chiết áp, do đó, chúng tôi khuyên bạn nên tìm hiểu hướng dẫn ADC trước khi bắt đầu với PWM. Mô-đun PWM cũng sử dụng bộ định thời để đặt tần số của nó, do đó hãy tìm hiểu cách sử dụng bộ định thời trước tại đây. Hơn nữa, trong hướng dẫn này, chúng tôi sẽ sử dụng mạch RC và đèn LED để chuyển đổi các giá trị PWM thành điện áp Analog và sử dụng nó để làm mờ đèn LED.
What is a PWM Signal?
Tần suất của một PWM:
Tần số của tín hiệu PWM xác định tốc độ PWM hoàn thành một khoảng thời gian. Một khoảng thời gian hoàn tất BẬT và TẮT tín hiệu PWM như thể hiện trong hình trên. Trong hướng dẫn của chúng tôi, chúng tôi sẽ đặt tần số 5KHz.
PWM using PIC16F877A:
Programming PIC to generate PWM signals:
PWM_Initialize() { PR2 = (_XTAL_FREQ/(PWM_freq*4*TMR2PRESCALE)) - 1; //Setting the PR2 formulae using Datasheet // Makes the PWM work in 5KHZ CCP1M3 = 1; CCP1M2 = 1; //Configure the CCP1 module T2CKPS0 = 1;T2CKPS1 = 0; TMR2ON = 1; //Configure the Timer module TRISC2 = 0; // make port pin on C as output }
Chức năng trên là chức năng khởi tạo PWM, trong chức năng này Mô-đun CCP1 được thiết lập để sử dụng PWM bằng cách đặt bit CCP1M3 và CCP1M2 lên cao.
Bộ đếm trước của mô-đun bộ hẹn giờ được đặt bằng cách đặt bit T2CKPS0 ở mức cao và T2CKPS1 ở mức thấp đối với bit TMR2ON được đặt để bắt đầu bộ định thời.
Bây giờ, chúng ta phải thiết lập Tần số của tín hiệu PWM. Giá trị của tần số phải được ghi vào thanh ghi PR2. Tần suất mong muốn có thể được đặt bằng cách sử dụng các công thức dưới đây
PWM Period = [(PR2) + 1] * 4 * TOSC * (TMR2 Prescale Value)
PR2 = (Period / (4 * Tosc * TMR2 Prescale )) - 1
Chúng ta biết rằng Kỳ = (1 / PWM_freq) và Tosc = (1 / _XTAL_FREQ). Vì vậy.....
PR2 = (_XTAL_FREQ/ (PWM_freq*4*TMR2PRESCALE)) – 1;
Khi tần số được thiết lập, hàm này không cần được gọi lại trừ khi và cho đến khi chúng ta cần thay đổi lại tần số. Trong hướng dẫn của chúng tôi, tôi đã gán PWM_freq = 5000; để chúng tôi có thể nhận được tần số hoạt động 5 KHz cho tín hiệu PWM của chúng tôi.
Bây giờ chúng ta hãy thiết lập chu kỳ nhiệm vụ của PWM bằng cách sử dụng hàm bên dưới
PWM_Duty(unsigned int duty) { if(duty<1023) { duty = ((float)duty/1023)*(_XTAL_FREQ/(PWM_freq*TMR2PRESCALE)); // On reducing //duty = (((float)duty/1023)*(1/PWM_freq)) / ((1/_XTAL_FREQ) * TMR2PRESCALE); CCP1X = duty & 1; //Store the 1st bit CCP1Y = duty & 2; //Store the 0th bit CCPR1L = duty>>2;// Store the remining 8 bit } }
Tín hiệu PWM của chúng tôi có độ phân giải 10 bit do đó giá trị này không thể được lưu trữ trong một thanh ghi vì PIC của chúng tôi chỉ có các đường dữ liệu 8 bit. Vì vậy, chúng tôi đã sử dụng hai bit khác của CCP1CON <5: 4> (CCP1X và CCP1Y) để lưu trữ hai LSB cuối cùng và sau đó lưu trữ 8 bit còn lại trong thanh ghi CCPR1L.
Thời gian chu kỳ nhiệm vụ PWM có thể được tính bằng cách sử dụng các công thức dưới đây:
PWM Duty Cycle = (CCPRIL:CCP1CON<5:4>) * Tosc * (TMR2 Prescale Value)
CCPRIL:CCP1Con<5:4> = PWM Duty Cycle / (Tosc * TMR2 Prescale Value)
Giá trị của ADC của chúng tôi sẽ là 0-1024, chúng tôi cần giá trị đó ở 0% -100%, do đó, PWM Duty Cycle = duty / 1023. Hơn nữa, để chuyển đổi chu kỳ nhiệm vụ này thành một khoảng thời gian, chúng ta phải nhân nó với khoảng thời gian (1 / PWM_freq)
Chúng ta cũng biết rằng Tosc = (1 / PWM_freq), do đó ..
Duty = ( ( (float)duty/1023) * (1/PWM_freq) ) / ( (1/_XTAL_FREQ) * TMR2PRESCALE) ;
Giải phương trình trên sẽ cho chúng ta:
Duty = ( (float)duty/1023) * (_XTAL_FREQ / (PWM_freq*TMR2PRESCALE));
Schematics and Testing:
Kết nối chiết áp vào chân thứ 7 để cấp điện áp 0-5. Mô-đun CCP1 có chân 17 (RC2), ở đây PWM sẽ được tạo ra và có thể được xác minh bằng cách sử dụng Máy hiện sóng kỹ thuật số. Hơn nữa để chuyển đổi điều này thành điện áp thay đổi, chúng tôi đã sử dụng bộ lọc RC và đèn LED để xác minh đầu ra mà không có phạm vi.
Bộ lọc RC là gì?
Bộ lọc RC hay bộ lọc thông thấp là một mạch đơn giản với hai phần tử thụ động là điện trở và tụ điện. Hai thành phần này được sử dụng để lọc tần số của tín hiệu PWM và biến nó thành điện áp DC có thể thay đổi được.
Nếu ta kiểm tra mạch điện, khi đặt một hiệu điện thế thay đổi được vào đầu vào R thì tụ điện C sẽ bắt đầu tích điện. Bây giờ dựa trên giá trị của tụ điện, tụ điện sẽ mất một khoảng thời gian để được sạc đầy, sau khi được sạc nó sẽ chặn dòng điện một chiều (Hãy nhớ tụ điện chặn dòng điện một chiều nhưng cho phép dòng điện xoay chiều) do đó điện áp một chiều đầu vào sẽ xuất hiện trên đầu ra. PWM tần số cao (tín hiệu AC) sẽ được nối đất qua tụ điện. Do đó thu được một điện một chiều thuần trên tụ điện. Giá trị 1000Ohm và 1uf được tìm thấy là thích hợp cho dự án này. Tính toán các giá trị của R và C liên quan đến phân tích mạch bằng cách sử dụng hàm truyền, nằm ngoài phạm vi của hướng dẫn này.
Đầu ra của chương trình có thể được xác minh bằng Máy hiện sóng kỹ thuật số như được hiển thị bên dưới, thay đổi chiết áp và chu kỳ nhiệm vụ của PWM sẽ thay đổi. Chúng ta cũng có thể nhận thấy điện áp đầu ra của mạch RC bằng cách sử dụng Vôn kế. Nếu mọi thứ hoạt động như mong đợi, chúng tôi có thể tiến hành với phần cứng của mình. Kiểm tra thêm Video ở cuối để biết toàn bộ quy trình.
Working on Hardware:
Thiết lập phần cứng của dự án rất đơn giản, chúng tôi chỉ sử dụng lại bảng PIC Perf của chúng tôi được hiển thị bên dưới.
Đó là chúng tôi đã lập trình để đọc điện áp Analog từ POT và chuyển đổi thành tín hiệu PWM, đến lượt nó đã được chuyển đổi thành Điện áp biến bằng bộ lọc RC và kết quả được xác minh bằng phần cứng của chúng tôi. Nếu bạn có một số nghi ngờ hoặc gặp khó khăn ở đâu đó, vui lòng sử dụng phần bình luận bên dưới, chúng tôi sẽ sẵn lòng trợ giúp bạn.
CODE :
// CONFIG
#pragma config FOSC = HS // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT enabled)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = ON // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3/PGM pin has PGM function; low-voltage programming enabled)
#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)
#define _XTAL_FREQ 20000000
#define TMR2PRESCALE 4
#include <xc.h>
long PWM_freq = 5000;
PWM_Initialize()
{
PR2 = (_XTAL_FREQ/(PWM_freq*4*TMR2PRESCALE)) - 1; //Setting the PR2 formulae using Datasheet // Makes the PWM work in 5KHZ
CCP1M3 = 1; CCP1M2 = 1; //Configure the CCP1 module
T2CKPS0 = 1;T2CKPS1 = 0; TMR2ON = 1; //Configure the Timer module
TRISC2 = 0; // make port pin on C as output
}
PWM_Duty(unsigned int duty)
{
if(duty<1023)
{
duty = ((float)duty/1023)*(_XTAL_FREQ/(PWM_freq*TMR2PRESCALE)); // On reducing //duty = (((float)duty/1023)*(1/PWM_freq)) / ((1/_XTAL_FREQ) * TMR2PRESCALE);
CCP1X = duty & 1; //Store the 1st bit
CCP1Y = duty & 2; //Store the 0th bit
CCPR1L = duty>>2;// Store the remining 8 bit
}
}
void ADC_Initialize()
{
ADCON0 = 0b01000001; //ADC ON and Fosc/16 is selected
ADCON1 = 0b11000000; // Internal reference voltage is selected
}
unsigned int ADC_Read(unsigned char channel)
{
ADCON0 &= 0x11000101; //Clearing the Channel Selection Bits
ADCON0 |= channel<<3; //Setting the required Bits
__delay_ms(2); //Acquisition time to charge hold capacitor
GO_nDONE = 1; //Initializes A/D Conversion
while(GO_nDONE); //Wait for A/D Conversion to complete
return ((ADRESH<<8)+ADRESL); //Returns Result
}
void main()
{
int adc_value;
TRISC = 0x00; //PORTC as output
TRISA = 0xFF; //PORTA as input
TRISD = 0x00;
ADC_Initialize(); //Initializes ADC Module
PWM_Initialize(); //This sets the PWM frequency of PWM1
do
{
adc_value = ADC_Read(4); //Reading Analog Channel 0
PWM_Duty(adc_value);
__delay_ms(50);
}while(1); //Infinite Loop
}