Nick Chung gửi vào
- 97845 lượt xem
Tiếp tục chuỗi bài: Điều khiển pin bằng ngôn ngữ chính thống.
Bài viết này sẽ giúp bạn tạo một xung PWM có tần số và độ rộng xung theo ý muốn.
Bài 3: Sử dụng thanh ghi như một bộ định thời và đếm sự kiện (timer/counter+interrupt)
Chủ đề này đã được bạn NTP_PRO viết rất kĩ, hãy tìm hiểu về nó nhé.
Link: http://arduino.vn/bai-viet/411-timercounter-tren-avrarduino
Nếu thấy chưa rõ phần nào hãy chủ động search goocle nha.
Tương lai sẽ có các bài viết với ví dụ minh họa về ngắt, các bạn hãy ủng hộ nhé ^^.
Bài 4: Chính là tiêu đề của bài viết này “Tạo xung theo ý muốn”
1: Làm sao để tạo xung?
Yêu cầu: Tạo xung vuông với tần số khoảng 1khz, độ rộng xung là 10%.
Dùng digitalWrite
|
Dùng analogWrite
|
void setup() { pinMode(13, OUTPUT); } void loop() { digitalWrite(13, HIGH); delayMicroseconds(100); digitalWrite(13, LOW); delayMicroseconds(900); }
|
void setup() { analogWrite(5, 25); // 25/256=10% } void loop() { }
|
Về bản chất hàm analogWrite
- Không cần khai báo pin.
- Tần số mặc định trên pin 5,6 là 976 Hz (uno r3), trên pin 3, 11 là 976 Hz (Leonardo).
- Tần số mặc định trên các pin còn lại là 490 hz.
- Nó truy cập trực tiếp lên các thanh ghi nên không ảnh hưởng đến tốc độ sử lý . (bạn sẽ biết điều đó ngay bây giờ).
Chạy mô phỏng trên proteus
void setup() { pinMode(13, OUTPUT); analogWrite(5, 25); } void loop() { digitalWrite(13, HIGH); delayMicroseconds(100); digitalWrite(13, LOW); delayMicroseconds(900); }
So sánh
|
DigitalWrite/port
|
AnalogWrite
|
Ưu điểm
|
Có tính ổn định cao.
Có thể tùy chỉnh tần số và độ rộng bằng delay.
|
Điều khiển chính xác, tần số cao,
Hoạt động độc lập và song song với các tiến trình.
|
Nhược điểm
|
Tần số thấp.
Ảnh hưởng tới tốc độ sử lý 1 luồng.
Phụ thuộc delay.
|
Tần số cố định là 976 hz (490 hz).
|
2: Truy cập thanh ghi điều khiển để thay đổi tần số
2.1: Cách tạo ra xung PWM.
Cấp một xung nhịp P_clock vào bộ đếm Counter, Xung P_clock được lấy ra từ bộ chia tần số hệ thống F_clock. Mỗi một xung P_clock sẽ làm bộ đếm Counter tăng thêm một giá trị, khi giá trị đạt max (tràn số) thì counter lại được đặt về 0 (Fast PWM) hoặc lại đếm lùi về 0 (Phase correct), quá trình này sẽ được lặp đi lặp lại .
Tiếp đến, bộ so sánh sẽ kiểm tra giá trị của bộ đếm Counter với một giá trị đặt trước Value, giá trị lấy ra từ bộ so sánh cũng chính là dạng xung PWM có tần số P_clock, độ rộng xung là tỷ số của Value với Counter.
2.2: Truy cập cài đặt các thanh ghi
Trên AVR 168/328, Việc tạo xung được hỗ trợ bởi 3 timer là TIMER 0 (8 bit), TIMER 1 (16 bit), TIMER 2 (8 bit). Mỗi Timer bao gồm 2 thanh ghi dữ liệu A-B, Bằng cách truy cập và thay đổi thông số của các thanh ghi, chúng ta sẽ cài đặt pin ra, bộ chia tần, đặt ngắt Value, lựa chọn kiểu Counter (xung răng cưa/ tam giác), kiểu so sánh, đặt ngắt tràn cho Counter…
AVR là mình gọi tắt của con Atmega - cái con vi điều khiển của con arduino đó các bạn
Trên AVR 328, Chúng ta có 6 pin hỗ trợ xuất xung.
Một vài thuật ngữ
Lấy ví dụ trên TIMER 1:
- TCCR_1_A(Timer/Counter_1 _Control Register_ A)
- TCCR_1_B(Timer/Counter_1 _Control Register _B)
- TCNT_1(Timer/Counter Register )
- ICR_1(Input Capture Register)
- OCR_1_A/B(Output Compare Register)
2.3 Đầu ra phụ thuộc vào kiểu so sánh “inverted” hay "none-inverted":
Ví dụ chọn kiểu đếm răng cưa (FAST PWM), kiểu so sánh thường ”none-inverted”. Tín hiệu ra là HIGH khi Value lớn hơn Counter, là LOW khi Value nhỏ hơn Counter. Khi chọn là “inverted”, tín hiệu đầu ra sẽ bị đảo lại so với trường hợp trên.
Kiểu đếm Counter tam giác cũng như vậy.
Tần số xung phụ thuộc vào xung nhịp P_clock (sau bộ chia tần) và dạng đếm counter.
Như đã biết, mỗi P_clock thì counter tăng lên 1, nếu bộ đếm có cỡ là 8 bit thì cần 256 xung để đếm từ BOTTOM lên TOP (đếm răng cưa). Nếu chọn kiểu tam giác, khi Counter đạt TOP nó sẽ tiếp tục đếm lùi, khi đó nó cần 256*2 xung P_clock để đi hết một chu kỳ đếm.
Khi đó công thức tổng quát để tính tần số PWM là :
F_pwm =P_clock / (Top_value+1). Fast Pwm.
Hoặc
F_pwm=P_clock / (2*( Top_value+1)) . Phase Cerrect Pwm.
Với P_clock =F_clock / Prescaller.
Ví dụ
Chọn kiểu Fast Pwm, tần số thạch anh F_clock=16mhz, sử dụng bộ đếm timer 0 (8 bit = 1 byte) tương đương TOP_value=255+1. Giảm tầm số Counter xuống còn P_clock = 16mhz/64=250kHz. (Prescller=64).
Khi đó tần số của xung ra:
F_pwm=250khz/256=976.562 Hz.
Cũng như trên, nhưng chọn kiểu Phase Cerrect Pwm, tần số xung ra F_pwm=488.28 Hz.
1: TIMER 0 (8 BIT)
|
7 bit
|
6 bit
|
5 bit
|
4 bit
|
3 bit
|
2 bit
|
1 bit
|
0 bit
|
TCCR0A
|
COM0A1
|
COM0A0
|
COM0B1
|
COM0B0
|
-
|
-
|
WGM01
|
WGM00
|
Timer/Counter Control Register 0 A
|
7 bit
|
6 bit
|
5 bit
|
4 bit
|
3 bit
|
2 bit
|
1 bit
|
0 bit
|
TCCR0B
|
FOC0A
|
FOC0B
|
-
|
-
|
WGM02
|
CS02
|
CS01
|
CS00
|
Timer/Counter Control Register 0 B
Timer/ counter 0 hỗ trợ 2 đầu ra là OC0A và OC0B tương ứng với (PD 5- pin ~5) (PD6 –pin ~6).
Chọn kiểu so sánh (thường / đảo)
Cài cho PD5
COM0A1
|
COM0A0
|
DESCRIPTION
|
0
|
0
|
OC0A disabled
|
0
|
1
|
WGM02 = 0: Normal Port Operation, OC0A Disconnected WGM02 = 1: Toggle OC0A on Compare Match
|
1
|
0
|
None-inverted mode (HIGH at bottom, LOW on Match)
|
1
|
1
|
Inverted mode (LOW at bottom, HIGH on Match)
|
Applies only to PWM modes
Cài cho PD6
COM0B1
|
COM0B0
|
DESCRIPTION
|
0
|
0
|
OC0B disabled
|
0
|
1
|
Reserved
|
1
|
0
|
None-inverted mode (HIGH at bottom, LOW on Match)
|
1
|
1
|
Inverted mode (LOW at bottom, HIGH on Match)
|
Applies only to PWM modes
Cài bộ chia tần số
CS12
|
CS11
|
CS10
|
DESCRIPTION
|
0
|
0
|
0
|
Chọn để không kích hoạt TIMER này
|
0
|
0
|
1
|
No Prescaling =F_Clock
|
0
|
1
|
0
|
F_Clock / 8
|
0
|
1
|
1
|
F_Clock / 64
|
1
|
0
|
0
|
F_Clock / 256
|
1
|
0
|
1
|
F_Clock / 1024
|
1
|
1
|
0
|
Không dùng F_clock, đặt một xung P_clock vào pin T1 (PD5), xung kích clock có chuyển mức trạng thái 5v->0v. (Sườn xuống -Falling)
|
1
|
1
|
1
|
Không dùng F_clock, đặt một xung P_clock vào pin T1 (PD5), xung kích clock có chuyển mức trạng thái 0v->5v. (Sườn lên-Rasing)
|
CS bits
Chọn kiểu Counter (răng cưa/ tam giác)
MODE
|
WGM02
|
WGM01
|
WGM00
|
TOP
|
DESCRIPTION
|
0
|
0
|
0
|
0
|
|
Normal
|
1
|
0
|
0
|
1
|
0xFF
|
PWM Phase Corrected
|
2
|
0
|
1
|
0
|
OCRA
|
CTC
|
3
|
0
|
1
|
1
|
0xFF
|
Fast PWM
|
4
|
1
|
0
|
0
|
-
|
Reserved
|
5
|
1
|
0
|
1
|
OCR0A
|
PWM Phase Corrected
|
6
|
1
|
1
|
0
|
-
|
Reserved
|
7
|
1
|
1
|
1
|
OCR0A
|
Fast PWM
|
Waveform Generator Mode bits
<pin 6> <PD6> | <pin 5><PD5> |
void setup(){ //B1:
TCCR0A=0;TCCR0B=0;
// reset lại 2 thanh ghi
//B2:
DDRD |= (1 << PD6);
// pin 6 là output
//B3:
TCCR0A |= (1 << WGM01) | (1 << WGM00);
// chọn Fast Mode
TCCR0A |= (1 << COM0A1);
// đầu ra kiểu thường (none-inverting)
TCCR0B |= (1 << CS01);
//prescaler = 8, P_clock=16mhz/8=2mhz
// Tần số xung F_pwm=2 mhz/256=7812,5 hz
OCR0A = 128;
// Value=128
// Độ rộng xung= 128/256=50%
}
void loop(){
}
|
void setup(){
//B1:
TCCR0A=0;TCCR0B=0;
// reset lại 2 thanh ghi
//B2:
DDRD |= (1 << PD5);
// pin 5 là output
//B3:
TCCR0A |= (1 << WGM01) | (1 << WGM00);
// chọn Fast Mode TCCR0A |= (1 << COM0B1);
// đầu ra kiểu thường (none-inverting)
TCCR0B |= (1 << CS02)|(1 << CS00);
//prescaler = 1024, P_clock=16mhz/1024=15,625 khz
// Tần số xung F_pwm=15,625 khz/256=61,0351 hz
OCR0B = 25;
// Value=25
// Độ rộng xung= 25/256=10%
}
void loop(){
}
|
Kết quả: F=7813 Hz. Độ rộng xung = 50%. Chu kỳ xung: 0,129 ms. |
Kết quả: F=61 Hz Độ rộng xung: 10% Chu kỳ xung: 16,20ms |
CODE Tương đương
<pin 6> <PD6>
|
<pin 5><PD5>
|
void setup() { TCCR0B = 0; // reset lại thanh ghi TCCR0B |= (1 << CS01); analogWrite(6, 128); // pin 6 là output, Value=128 } void loop() { } |
void setup() { TCCR0B = 0; // reset lại thanh ghi TCCR0B |= (1 << CS02) | (1 << CS00); analogWrite(5, 25); // pin5, value=25 } void loop() { } |
Cảnh báo : Việc thay đổi tần số xung nhịp trên TIMER 0 sẽ gây ảnh hưởng đến các hàm delay(), delayMicroseconds(), millis(), micros(); , để an toàn hơn bạn hãy giữ nguyên và dùng hàm analogWrite là đủ rồi
TIMER 1 (16 BIT)
Timer/Counter1 có 2 đầu ra là OC1A và OC1B tương ứng với pin ~9 và pin ~10 trên arduino.
|
7 bit
|
6 bit
|
5 bit
|
4 bit
|
3 bit
|
2 bit
|
1 bit
|
0 bit
|
TCCR1A
|
COM1A1
|
COM1A0
|
COM1B1
|
COM1B0
|
-
|
-
|
WGM11
|
WGM10
|
Timer/Counter Control Register 1 A
|
7 bit
|
6 bit
|
5 bit
|
4 bit
|
3 bit
|
2 bit
|
1 bit
|
0 bit
|
TCCR1B
|
ICNC1
|
ICES1
|
-
|
WGM13
|
WGM12
|
CS12
|
CS11
|
CS10
|
Timer/Counter Control Register 1 B
Kiểu so sánh
COM1A1, COM1B1
|
COM1A0, COM1B0
|
DESCRIPTION
|
0
|
0
|
Normal port operation, OC1A/OC1B disconnected.
|
0
|
1
|
Mode 9,11,14,15 only: Enable OCR1A only (OC1B disconnected)
|
1
|
0
|
None-inverted mode (HIGH at bottom, LOW on Match)
|
1
|
1
|
Inverted mode (LOW at bottom, HIGH on Match)
|
Applies only to PWM modes
Bộ chia tần
CS12
|
CS11
|
CS10
|
DESCRIPTION
|
0
|
0
|
0
|
Chọn để không kích hoạt TIMER này
|
0
|
0
|
1
|
No Prescaling= F_Clock.
|
0
|
1
|
0
|
F_Clock / 8
|
0
|
1
|
1
|
F_Clock / 64
|
1
|
0
|
0
|
F_Clock / 256
|
1
|
0
|
1
|
F_Clock / 1024
|
1
|
1
|
0
|
Không dùng F_clock, đặt một xung P_clock vào pin T1 (PD5), xung kích clock có chuyển mức trạng thái 5v->0v. (Sườn xuống -Falling)
|
1
|
1
|
1
|
Không dùng F_clock, đặt một xung P_clock vào pin T1 (PD5), xung kích clock có chuyển mức trạng thái 0v->5v. (Sườn lên-Rasing)
|
CS bits
Kiểu counter
MODE
|
WGM13
|
WGM12
|
WGM11
|
WGM10
|
DESCRIPTION
|
TOP
|
0
|
0
|
0
|
0
|
0
|
Normal
|
0xFFFF
|
1
|
0
|
0
|
0
|
1
|
PWM, Phase Corrected, 8bit
|
0x00FF
|
2
|
0
|
0
|
1
|
0
|
PWM, Phase Corrected, 9bit
|
0x01FF
|
3
|
0
|
0
|
1
|
1
|
PWM, Phase Corrected, 10bit
|
0x03FF
|
5
|
0
|
1
|
0
|
1
|
Fast PWM, 8bit
|
0x00FF
|
6
|
0
|
1
|
1
|
0
|
Fast PWM, 9bit
|
0x01FF
|
7
|
0
|
1
|
1
|
1
|
Fast PWM, 10bit
|
0x03FF
|
8
|
1
|
0
|
0
|
0
|
PWM, Phase and Frequency Corrected
|
ICR1
|
9
|
1
|
0
|
0
|
1
|
PWM, Phase and Frequency Corrected
|
OCR1A
|
10
|
1
|
0
|
1
|
0
|
PWM, Phase Correct
|
ICR1
|
11
|
1
|
0
|
1
|
1
|
PWM, Phase Correct
|
OCR1A
|
14
|
1
|
1
|
1
|
0
|
Fast PWM
|
ICR1
|
15
|
1
|
1
|
1
|
1
|
Fast PWM
|
OCR1A
|
Waveform Generator Mode bits (Abbreviated)
Ưu điểm của timer 1 so với timer 0 là nó độ phân giải cao hơn, bộ đếm Counter 0 sẽ bị tràn sau 256 xung P_clock, còn Counter 1 sẽ tràn sau 512 xung Clock (9 bit) hoặc 1024 xung (10 bit), hoặc 65536 xung (16 bit). Điều này giúp ta xuất xung PWM có rộng xung cực kỳ chính xác.
Ưu điểm nữa của Timer 1 là nó cho phép thay đổi giá trị TOP, ví dụ bạn có thể cài đặt ngưỡng tràn là 500 xung P_clock ( thay vì mặc định là 512 xung). Khi đó độ phân giải mặc định là 16 bit.
Còn lại cách dùng thì vẫn như cũ.
PB1 – pin 9
|
PB2-pin10
|
void setup(){
TCCR1A=0; TCCR1B=0;
// RESET lại 2 thanh ghi
DDRB |= (1 << PB1);
// Đầu ra PB1 là OUTPUT ( pin 9)
TCCR1A |= (1 << WGM11);
TCCR1B |= (1 << WGM12)|(1 << WGM13);
// chọn Fast PWM, chế độ chọn TOP_value tự do ICR1
TCCR1A |= (1 << COM1A1);
// So sánh thường( none-inverting)
ICR1 = 65535;
// xung răng cưa tràn sau 65535 P_clock
OCR1A =16838;
// Value=16838 -> độ rộng 25 %
TCCR1B |= (1 << CS10)|(1 << CS11);
// F_clock/64=16mhz/64=250 khz
//F_pwm=250khz/65536=3.81469 hz
}
void loop(){
}
|
void setup(){
TCCR1A=0; TCCR1B=0;
// RESET lại 2 thanh ghi
DDRB |= (1 << PB2);
// Đầu ra PB2 là OUTPUT ( pin 10)
TCCR1A |= (1 << WGM11);
TCCR1B |= (1 << WGM12)|(1 << WGM13);
// chọn Fast PWM chế độ chọn TOP_value tự do ICR1
TCCR1A |= (1 << COM1B1);
// So sánh thường( none-inverting)
ICR1 = 30000;
// xung răng cưa tràn sau 30000 P_clock
OCR1B = 15000;
// Value=15000 -> độ rộng 50 %
TCCR1B |= (1 << CS10);
// F_clock/1=16mhz
//F_pwm=16mhz/30001=533.315 hz
}
void loop(){
}
|
Kết quả: F_pwm=3.814hz Độ rộng xung: 25 % Chu kỳ xung: 262,50 ms. |
Kết quả: F_Pwm=533.315 hz Độ rộng: 50 % Chu kỳ: 1.87 ms. |
Như vậy việc sử dụng TIMER 1 -16 BIT cũng rất dễ dàng.
TIMER 2 (8 BIT)
Timer/Counter 2 phụ trách 2 đầu ra OC2A và OC2B, tương ứng pin ~11 (PB3) và ~3 (PD3).
Timer 0 – 8 bit.
Timer 1 – 16 bit + khả năng thay đổi TOP value
Timer 2 thì lại có thể chia tần ở PRESCALER= 32 hoặc 128. Đó là ưu điểm của timer 2.
|
7 bit
|
6 bit
|
5 bit
|
4 bit
|
3 bit
|
2 bit
|
1 bit
|
0 bit
|
TCCR2A
|
COM2A1
|
COM2A0
|
COM2B1
|
COM2B0
|
-
|
-
|
WGM21
|
WGM20
|
Timer/Counter Control Register 2 A
|
7 bit
|
6 bit
|
5 bit
|
4 bit
|
3 bit
|
2 bit
|
1 bit
|
0 bit
|
TCCR2B
|
FOC2A
|
FOC2B
|
-
|
-
|
WGM22
|
CS22
|
CS21
|
CS20
|
Timer/Counter Control Register 2 B
Chọn kiểu so sánh
CÀI CHO PB3 (pin ~11)
COM2A1
|
COM2A0
|
DESCRIPTION
|
0
|
0
|
OC2A disabled
|
0
|
1
|
WGM22 = 0: Normal Port Operation, OC2A Disconnected WGM22 = 1: Toggle OC2A on Compare Match
|
1
|
0
|
None-inverted mode (HIGH at bottom, LOW on Match)
|
1
|
1
|
Inverted mode (LOW at bottom, HIGH on Match)
|
Applies only to PWM modes
CÀI CHO PD3 (pin ~3)
COM2B1 |
COM2B0 |
DESCRIPTION |
0 |
0 |
OC2B disabled |
0 |
1 |
Reserved |
1 |
0 |
None-inverted mode (HIGH at bottom, LOW on Match) |
1 |
1 |
Inverted mode (LOW at bottom, HIGH on Match) |
Applies only to PWM modes
Bộ chia tần
CS22 |
CS21 |
CS20 |
DESCRIPTION |
0 |
0 |
0 |
Chọn để không kích hoạt TIMER này |
0 |
0 |
1 |
No Prescaling =F_Clock |
0 |
1 |
0 |
F_Clock / 8 |
0 |
1 |
1 |
F_Clock / 32 |
1 |
0 |
0 |
F_Clock / 64 |
1 |
0 |
1 |
F_Clock / 128 |
1 |
1 |
0 |
F_Clock / 256 |
1 |
1 |
1 |
F_Clock / 1024 |
CS bits
Chọn kiểu Counter
MODE |
WGM22 |
WGM21 |
WGM20 |
TOP |
DESCRIPTION |
0 |
0 |
0 |
0 |
0xFF |
Normal |
1 |
0 |
0 |
1 |
0xFF |
PWM Phase Corrected |
2 |
0 |
1 |
0 |
OCRA |
CTC |
3 |
0 |
1 |
1 |
0xFF |
Fast PWM |
4 |
1 |
0 |
0 |
- |
Reserved |
5 |
1 |
0 |
1 |
OCR0A |
PWM Phase Corrected |
6 |
1 |
1 |
0 |
- |
Reserved |
7 |
1 |
1 |
1 |
OCR0A |
Fast PWM |
Waveform Generator Mode bits
KHI đó , giá trị TOP_value tất nhiên là bằng 255, vì thanh đếm chỉ có 8 bit.
PD3 (pin ~3)
|
PB3 ( pin ~11)
|
void setup(){
TCCR2A =0; TCCR2B=0;
//reset
DDRD |= (1 << PD3);
// PD3 output, (pin ~3)
TCCR2A |= (1 << COM2B1);
// so sánh thường ( none-inverting)
TCCR2A |= (1 << WGM21) | (1 << WGM20);
// PWM Mode (răng cưa)
TCCR2B |= (1 << CS22)|(1 << CS20);
// prescaler= 128, P_clock=16mhz/128=125 khz
// F_pwm=125 khz/256=488.28 hz
OCR2B = 25;
// độ rộng xung =25/256=10%
}
void loop(){
}
|
void setup(){
TCCR2A =0; TCCR2B=0;
//reset
DDRB |= (1 << PB3);
// PB3 output, (pin ~11)
TCCR2A |= (1 << COM2A1);
// so sánh thường ( none-inverting)
TCCR2A |= (1 << WGM21) | (1 << WGM20);
// PWM Mode (răng cưa)
TCCR2B |= (1 << CS21)|(1 << CS20);
// prescaler= 32, P_clock=16mhz/32=500 khz
// F_pwm=500 khz/256=1953.215 hz
OCR2A = 128;
// độ rộng xung =128/256=50%
}
void loop(){
}
|
Kết quả: Tần số: 488 hz Độ rộng :10 %. Chu kỳ: 2,03 ms |
Kết quả: Độ rộng xung 50 %. Tần số 1953 hz Chu kỳ: 520 ms |
Các bạn hết sức chú ý: Phân biệt DDRB và DDRD khi khai báo chọn PINOUT cho các chân PORT.
Mình chèn Code dạng text nhằm nhấn mạnh điều này!
Ứng dụng
1. Xuất xung 8mhz ra ngoài
Đơn giản là ta sẽ chia cho mẫu số nhỏ nhất là 1.
Kĩ thuật này chỉ áp dụng cho TIMER1 vì nó có thể thay đổi tràn TOP_value.
Có nghĩa chỉ có 2 pin ~9 và ~10 mới có khả năng này
void setup() { TCCR1A = 0; TCCR1B = 0; // RESET lại 2 thanh ghi DDRB |= (1 << PB1); // Đầu ra PB1 là OUTPUT ( pin 9) TCCR1A |= (1 << WGM11); TCCR1B |= (1 << WGM12) | (1 << WGM13); // chọn Fast PWM, chế độ chọn TOP_value tự do ICR1 TCCR1A |= (1 << COM1A1); // So sánh thường( none-inverting) ICR1 = 1; // xung răng cưa tràn sau 1 P_clock OCR1A = 0; // xuất mọi lúc TCCR1B |= (1 << CS10); // F_clock=16mhz, P_clock=16/1=16mhz. //F_pwm=16mhz/(ICR1+1)=8mhz } void loop() { }
2. Xuất xung 16mhz ra ngoài
Theo công thức ngay ở đầu bài viết:
F_pwm = P_clock / (Top_value+1).
Để F_pwm=16mhz, thì TOP_value=0, và P_clock=16mhz.
Điều này là không thể, bởi vì OCR1A (value) nhỏ nhất =0, khi đó nếu ICR1 (TOP_value) cho bằng 0 thì nó sẽ không đếm được sự kiện.
3. Điều khiển SERVO với góc siêu chính xác
Thư viện SERVO.h mà chúng ta đang dùng có số góc điều khiển SERVO là số nguyên.(0->180) độ.
Vậy bạn có muốn điều khiển nó nhích 0.5 độ không ? Hãy đón đọc bài tiếp theo nhé.
4. Hàm AnalogWrite!
Tới đây việc tự xây dựng một hàm AnalogWrite sẽ không còn khó nữa. Hiểu lầm lớn nhất của beginner khi sử dụng AnalogWrite là đã “đánh đồng” cả 3 bộ TIMER là như nhau, khiến ta không khai thác được nhiều ưu điểm của từng TIMER.
Dưới đây là mã nguồn cho hàm analogWrite:
http://http://garretlab.web.fc2.com/en/arduino/inside/arduino/wiring_analog.c/analogWrite.html
Chế độ COUNTER tam giác (PHASE CERRECT PWM.)
Từ đầu đến giờ mình không sử dụng chế độ này, mà chỉ nói đến counter răng cưa. (FAST PWM)
Đó là vì hai chế độ này gần như tương đồng với nhau, PHASE CERRECT chỉ khác ở chỗ : Chu kì đếm tăng lên gấp 2 , tức là tần số P_clock của PHASE CERRECT nhỏ hơn 2 lần so với FAST PWM.
Sử dụng 2 đầu ra cùng lúc.
Các ví dụ trên là sử dụng độc lập các đầu ra của timer, tinh ý sẽ thấy, ta luôn phải reset lại 2 thanh ghi trước khi muốn đặt lại bằng phép hợp (toán tử “ |”). Lý do reset vì nó đã được với các thông số mặc định trước đó. Tiếp nữa là tần số trên hai đầu ra là luôn bằng nhau ( chung một Counter, chung tần số P_clock).
TIMER 0:
void setup(){ //////////////////////////////////////////////////// TCCR0A=0;TCCR0B=0; // reset lại 2 thanh ghi //B2: DDRD |= (1 << PD6); DDRD |= (1 << PD5); // pin 6, 5 là output //B3: TCCR0A |= (1 << WGM01) | (1 << WGM00); // chọn Fast Mode TCCR0B |= (1 << CS01); //prescaler = 8, P_clock=16mhz/8=2mhz // Tần số xung F_pwm=2 mhz/256=7812,5 hz /////////////////////////////////////////////////////// TCCR0A |= (1 << COM0A1); // đầu ra kiểu thường cho PD6 (none-inverting) TCCR0A |= (1 << COM0B1); // đầu ra kiểu thường cho PD5 (none-inverting) OCR0A = 128; //(chân PD6 ) Độ rộng xung= 128/256=50% OCR0B = 64;// (chân PD5) Độ rộng xung 64/256=25% } void loop(){ }
TIMER 1:
void setup(){ ///////////////////////////////////////////////// TCCR1A=0; TCCR1B=0; // RESET lại 2 thanh ghi DDRB |= (1 << PB1)|(1 << PB2); // Đầu ra PB1 và PB2 là OUTPUT ( pin ~9, ~10) TCCR1A |= (1 << WGM11); TCCR1B |= (1 << WGM12)|(1 << WGM13); // chọn Fast PWM, chế độ chọn TOP_value tự do ICR1 ICR1 = 10000; // xung răng cưa tràn sau 10001 P_clock TCCR1B |= (1 << CS10)|(1 << CS11); // F_clock/64=16mhz/64=250 khz //F_pwm=250khz/10001=25hz /////////////////////////////////////////////////// TCCR1A |= (1 << COM1A1); // So sánh thường cho PB1 ( none-inverting) TCCR1A |= (1 << COM1B1); // So sánh thường cho PB2 ( none-inverting) OCR1A =1000;//(PB1) Value=1000 -> độ rộng 10% OCR1B = 5000; //(PB2) Value=5000 -> độ rộng 50 % } void loop(){ }
TIMER 2:
void setup(){ //////////////////////////////////////////////////////// TCCR2A =0; TCCR2B=0; //reset DDRD |= (1 << PD3);// PD3 output, (pin ~3) DDRB |= (1 << PB3);// PB3 output, (pin ~11) TCCR2A |= (1 << WGM21) | (1 << WGM20); // PWM Mode (răng cưa) TCCR2B |= (1 << CS22)|(1 << CS20); // prescaler= 128, P_clock=16mhz/128=125 khz // F_pwm=125 khz/256=488.28 hz ////////////////////////////////////////////////// TCCR2A |= (1 << COM2B1);// so sánh thường (PD3) (pin 3) ( none-inverting) TCCR2A |= (1 << COM2A1);// so sánh thường (PB3) (pin 11) ( none-inverting) OCR2B = 25; // (PD3) (pin 3)độ rộng xung =25/256=10% OCR2A = 128;//(PB3) (pin 11) độ rộng 50% } void loop(){ }
\
Kết
Tất cả các code trên đều được mình kiểm tra & chạy thử, mô phỏng thành công. Yêu cầu bạn thông thạo các phép toán BIT (BIT MATH).
Bài viết có thể chưa đủ với bạn, dưới đây là các trang web được mình sưu tầm về phần PWM+TIMER.
- http://garretlab.web.fc2.com/en/arduino/inside/arduino/wiring_analog.c/analogWrite.html
- http://robotika.yweb.sk/skola/AVR/visionrobo%20com/PWM%20in%20AVR%20v1.0.pdf
- http://arduino.stackexchange.com/questions/16698/arduino-constant-clock-output
- https://www.arduino.cc/en/Tutorial/SecretsOfArduinoPWM
- http://provideyourown.com/2011/analogwrite-convert-pwm-to-voltage/
- http://www.avrfreaks.net/forum/tut-c-newbies-guide-avr-pwm-incomplete?page=all
- http://www.ermicro.com/blog/?p=1971
- http://www.avrfreaks.net/forum/duty-cycle-adjustment-fast-pwm-mode
- https://sites.google.com/site/qeewiki/books/avr-guide/pwm-on-the-atmega328
- http://www.effusiontech.com/pwm-in-avr.html
VIDEO HƯỚNG DẪN dùng Proteus để đo tần số.
Chúng ta lại vượt qua một chướng ngại vật nữa! ^^
Việc đào sâu về phần cứng sẽ giúp ta sử dụng arduino với một hiệu suất cao nhất, điều mà những cải tiến phần mềm sẽ không thể với tới được.
Chúc bạn sớm có những ứng dụng từ bài viết này .
<Đừng quên đón đọc bài tiếp theo về điều khiển SERVO siêu chuẩn nhé..>
Tác giả THÁI SƠN.