Lý thuyết về PWM thì chắc các bạn nhiều bạn đã được học rồi nên mình xin phép không nhắc ở đây "thực ra mình có biết đâu mà nhắc "
ở đây mình xin trình bày các phương pháp thực hiện PWM 1 bằng phần mềm cho họ 89xxx. Đầu tiên phải nói đến tạo xung (dao động) bằng 89xxx
sau đây là chương trình đơn giản tạo dao động xung vuông tần số 1000Hz bằng 89xx code cho "Phần cơ bản về timer thì tự bạn đọc sách nhé"
Không biết các bạn được học tạo PWM như thế nào, nhưng theo mình thì có ít nhất 2 cách để tạo PWM bằng phần mềm
cách 1: xê dịch thời gian cho mỗi nửa chu kỳ sao cho vẫn đảm bảo tần số cần tạo mà tạo được sự khác nhau giữa 2 nửa chu kỳ của, với cách này số lần tràn của timer = 2 lần tần số dao động cần tạo
cách 2: tăng tần số giao động gấp nhiều lần hơn so với cách 1 sau đó "bạt bớt đỉnh dương hoặc đỉnh âm để tạo duty cần thiết"
Mỗi cách sẽ có ưu điểm và trường hợp áp dụng riêng tùy trường hợp nên dùng cách nào, với cách 1 thì dùng cho hầu hết mọi trường hợp nhưng tính toán và code sẽ khổ hơn 1 chút và độ chính xác sẽ phụ thuộc vào khả năng tối ưu của các bạn vì nếu tối ưu không tốt thì sai số rất nhiều.
Với cách 2 thì độ chính xác cao hơn nhưng không áp dụng cho mọi trường hợp được. Dưới đây là code theo cách 1 còn cách 2 thì xin phép chỉ các bạn đến 1 thread mà mình viết bài ở đó cho đỡ phải ngồi đăng lại.
ở đây mình xin trình bày các phương pháp thực hiện PWM 1 bằng phần mềm cho họ 89xxx. Đầu tiên phải nói đến tạo xung (dao động) bằng 89xxx
sau đây là chương trình đơn giản tạo dao động xung vuông tần số 1000Hz bằng 89xx code cho "Phần cơ bản về timer thì tự bạn đọc sách nhé"
PHP Code:
#include <at89x52.h>
sbit LED=P1.0;
#define FREQ 1000ul
#define TEMP (65536-(1000000ul/(FREQ*2)))
#define tiHigh (TEMP>>8)
#define tiLow (TEMP & 0xff)
void conf_main(void);
void timer0_int(void);
void main(void)
{
conf_main();
TR0=1;
EA=1;
while(1)
{
PCON=IDL_;
}
}
void conf_main(void)
{
TMOD&=0xf0;
TMOD|=0x01;
TH0=tiHigh;
TL0=tiLow;
ET0=1;
}
void timer0_int(void) interrupt TF0_VECTOR
{
// cặp TRx={0,1} có thể có có thể không, cái này để dừng đếm timer
// Nếu bạn dùng timer với số lần tràn thấp thì đừng cho vào để timer không bị sai số
// Nếu bạn để timer tràn với tốc độ cao khoảng cỡ 10k trở nên và thực hiện nhiều tính toán trong timer
// thì hãy cho vào để tránh bị ngắt đè ngắt.
// Ở đây mình mạn phép bỏ đi vì tính toán trong timer này rất ít và tốc độ tràn thấp -> độ chính xác rất cao
// Mình đã thử tạo dao động 40Khz (80.000) lần tràn timer mà sai số chỉ là 1 xung (39.999 - 40.001)
// Cái này đã được mình đo bằng thực tế dùng chính 89xx hiển thị lên ma trận 16x32
//TR0=0;
LED=~LED;
TH0=tiHigh;
TL0=tiLow;
//TR0=1;
}
cách 1: xê dịch thời gian cho mỗi nửa chu kỳ sao cho vẫn đảm bảo tần số cần tạo mà tạo được sự khác nhau giữa 2 nửa chu kỳ của, với cách này số lần tràn của timer = 2 lần tần số dao động cần tạo
cách 2: tăng tần số giao động gấp nhiều lần hơn so với cách 1 sau đó "bạt bớt đỉnh dương hoặc đỉnh âm để tạo duty cần thiết"
Mỗi cách sẽ có ưu điểm và trường hợp áp dụng riêng tùy trường hợp nên dùng cách nào, với cách 1 thì dùng cho hầu hết mọi trường hợp nhưng tính toán và code sẽ khổ hơn 1 chút và độ chính xác sẽ phụ thuộc vào khả năng tối ưu của các bạn vì nếu tối ưu không tốt thì sai số rất nhiều.
Với cách 2 thì độ chính xác cao hơn nhưng không áp dụng cho mọi trường hợp được. Dưới đây là code theo cách 1 còn cách 2 thì xin phép chỉ các bạn đến 1 thread mà mình viết bài ở đó cho đỡ phải ngồi đăng lại.
PHP Code:
#include <at89x52.h>
// CLOCK_FREQ được tính bằng tần số thạch anh bạn dùng /12 (đối với họ 89xx)
// Ví dụ nếu bạn dùng thạch anh 24Mhz-> CLOCK_FREQ=24.000.000/12=2.000.000
// CLOCK_FREQ là số chu kỳ máy thực tế mà bạn có
#define CLOCK_FREQ 1000000ul
// Đây là tần số dao động bạn cần tạo
#define FREQ 10000ul
/*
Để tạo ra tần số dao động là bao nhiêu thì bạn lấy số đó x2 sẽ được
Tần số tràn thực tế của timer do vậy tần số thực tế bạn cần tạo là
gấp 2 lần so với tần số mong muốn tạo ra.
Chỗ này dùng để tính thời gian giữa 2 lần tràn của timer (2 lần tràn -> 1 xung)
*/
#define TEMP1 (CLOCK_FREQ/(FREQ*2))
// Đây là thời gian để nạp vào timer
#define TEMP2 (65536-TEMP1)
// Tách lấy phần cao để nạp vào cho THx
#define tiHigh (TEMP2>>8)
// Lấy phần thấp để nạp vào TLx
#define tiLow (TEMP2 & 0xff)
// Cổng nhận xung PWM
#define LED P1_0
// Lưu thời gian cần nạp vào timer để dùng tính toán sau này
int time=TEMP2;
// Thời gian 1% duty
float _1percent=(TEMP1/100.0);
// Duty bạn muốn tạo
char duty=50;
// Biến này lưu giá trị để nạp thêm hoặc giảm đi tạo duty
int plustime;
// Những cài đặt cơ bản nhất
void conf_main(void);
void timer0(void);
int main(void)
{
conf_main();
TR0=1;
EA=1;
while(1)
{
PCON=IDL_;
}
}
void timer0(void) interrupt TF0_VECTOR
{
volatile int tmp;
;
LED=~LED;
TR0=0;
if(LED)
{
tmp=time+plustime;
TH0=tmp >> 8;
TL0=tmp & 0xff;
}
else
{
tmp=time-plustime;
TH0=tmp >> 8;
TL0=tmp & 0xff;
}
TR0=1;
}
void conf_main(void)
{
TMOD=0x01;
ET0=1;
TH0=tiHigh;
TL0=tiLow;
LED=0;
// Tính thời gian cần cộng thêm hoặc trừ bớt đi để tạo duty
plustime=(100-duty) * _1percent;
}
Comment