31
Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG Trần Thừa – 2010 93 BÀI 3: LẬP TRÌNH NGẮT TRÊN AVR ỨNG DỤNG NGẮT NGOÀI, BỘ ĐỊNH THỜI I. Ngắt trên AVR. 1. Khái niệm. Interrupts hay ngắt, là một tín hiệu khẩn cấp gởi đến bộ xử lí, yêu cầu bộ xử lí tạm ngừng tức khắc các hoạt động hiện tại để “nhảy” đến một nơi khác thực hiện một nhiệm vụ khẩn cấp nào đó, nhiệm vụ này gọi là trình phục vụ ngắt – ISR (interrupt service routine ). Sau khi kết thúc nhiệm vụ trong ISR, bộ đếm chương trình sẽ được trả về giá trị trước đó để bộ xử lí quay về thực hiện tiếp các nhiệm vụ còn dang dở. Như vậy, ngắt có mức độ ưu tiên xử lí cao nhất, ngắt thường được dùng để xử lí các sự kiện bất ngờ nhưng không tốn quá nhiều thời gian. Các tín hiệu dẫn đến ngắt có thể xuất phát từ các thiết bị bên trong chip (ngắt báo bộ đếm timer/counter tràn, ngắt báo quá trình gởi dữ liệu bằng RS232 kết thúc…) hay do các tác nhân bên ngoài (ngắt báo có 1 button được nhấn, ngắt báo có 1 gói dữ liệu đã được nhận…). 2. Ứng dụng. Hãy tưởng tượng bạn cần thiết kế một mạch điều khiển hoàn chỉnh thực hiện rất nhiều nhiệm vụ bao gồm: nhận thông tin từ người dùng qua các nút nhấn, nhận tín hiệu từ cảm biến, xử lí thông tin, xuất tín hiệu điều khiển, hiển thị thông tin trạng thái…rõ ràng trong các nhiệm vụ này việc nhận thông tin người dùng, hay tín hiệu cảm biến rất hiếm xảy ra so với các nhiệm vụ khác nhưng lại rất “khẩn cấp” cần được ưu tiên hàng đầu. Nhiệm vụ theo dõi các sự kiện “khẩn cấp” có thể thực hiện bằng 1 trong 2 cách: +Viết 1 hàm thăm dò sự kiện (ví dụ như hàm Button read đã giới thiệu ở bài 1) và phải gọi hàm này ra liên tục. Tuy nhiên, nếu chương trình chính có quá nhiều nhiệm vụ như đã nói ở trên thì cách này không hiệu quả. Lý do đưa ra là mỗi hàm cần 1 thời gian thực thi nhất định, nếu đang thực thi 1 hàm khác mà xảy ra sự kiện thì hàm thăm dò trở nên vô dụng và vi điều khiển bắt sót sự kiện. +Sử dụng ngắt, lúc này vi điều khiển sẽ không tốn thời gian cho hàm thăm dò nữa. Vi điều khiển sẽ thực thi nhiệm vụ xử lý sự kiện khi mà có ngắt xảy ra, khi đó mọi công việc khác được gác lại cho đến khi xử lý xong công việc mà ngắt giao cho(trình phục vụ ngắt). Một ví dụ khác cho ứng dụng của ngắt là bộ định thời (ngắt tràn timer/counter), cứ sau 1 khoảng thời gian cố định thì xảy ra 1 ngắt, ta ứng dụng vào nhiệm vụ quét led chẳng hạn, khi đó hàm quét led sẽ là trình phục vụ ngắt và nó sẽ được gọi ra theo 1 chu kỳ xác định với mức ưu tiên cao nhất. Như vậy

Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

  • Upload
    spy004

  • View
    1.649

  • Download
    2

Embed Size (px)

Citation preview

Page 1: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 93

BÀI 3: LẬP TRÌNH NGẮT TRÊN AVR

ỨNG DỤNG NGẮT NGOÀI, BỘ ĐỊNH THỜI

I. Ngắt trên AVR. 1. Khái niệm.

Interrupts hay ngắt, là một tín hiệu khẩn cấp gởi đến bộ xử lí, yêu cầu bộ xử lí tạm ngừng tức khắc các hoạt động hiện tại để “nhảy” đến một nơi khác thực hiện một nhiệm vụ khẩn cấp nào đó, nhiệm vụ này gọi là trình phục vụ ngắt – ISR (interrupt service routine ). Sau khi kết thúc nhiệm vụ trong ISR, bộ đếm chương trình sẽ được trả về giá trị trước đó để bộ xử lí quay về thực hiện tiếp các nhiệm vụ còn dang dở. Như vậy, ngắt có mức độ ưu tiên xử lí cao nhất, ngắt thường được dùng để xử lí các sự kiện bất ngờ nhưng không tốn quá nhiều thời gian.

Các tín hiệu dẫn đến ngắt có thể xuất phát từ các thiết bị bên trong chip (ngắt báo bộ đếm timer/counter tràn, ngắt báo quá trình gởi dữ liệu bằng RS232 kết thúc…) hay do các tác nhân bên ngoài (ngắt báo có 1 button được nhấn, ngắt báo có 1 gói dữ liệu đã được nhận…). 2. Ứng dụng. Hãy tưởng tượng bạn cần thiết kế một mạch điều khiển hoàn chỉnh thực hiện rất nhiều nhiệm vụ bao gồm: nhận thông tin từ người dùng qua các nút nhấn, nhận tín hiệu từ cảm biến, xử lí thông tin, xuất tín hiệu điều khiển, hiển thị thông tin trạng thái…rõ ràng trong các nhiệm vụ này việc nhận thông tin người dùng, hay tín hiệu cảm biến rất hiếm xảy ra so với các nhiệm vụ khác nhưng lại rất “khẩn cấp” cần được ưu tiên hàng đầu. Nhiệm vụ theo dõi các sự kiện “khẩn cấp” có thể thực hiện bằng 1 trong 2 cách:

+Viết 1 hàm thăm dò sự kiện (ví dụ như hàm Button read đã giới thiệu ở bài 1) và phải gọi hàm này ra liên tục. Tuy nhiên, nếu chương trình chính có quá nhiều nhiệm vụ như đã nói ở trên thì cách này không hiệu quả. Lý do đưa ra là mỗi hàm cần 1 thời gian thực thi nhất định, nếu đang thực thi 1 hàm khác mà xảy ra sự kiện thì hàm thăm dò trở nên vô dụng và vi điều khiển bắt sót sự kiện.

+Sử dụng ngắt, lúc này vi điều khiển sẽ không tốn thời gian cho hàm thăm dò nữa. Vi điều khiển sẽ thực thi nhiệm vụ xử lý sự kiện khi mà có ngắt xảy ra, khi đó mọi công việc khác được gác lại cho đến khi xử lý xong công việc mà ngắt giao cho(trình phục vụ ngắt).

Một ví dụ khác cho ứng dụng của ngắt là bộ định thời (ngắt tràn timer/counter), cứ sau 1 khoảng thời gian cố định thì xảy ra 1 ngắt, ta ứng dụng vào nhiệm vụ quét led chẳng hạn, khi đó hàm quét led sẽ là trình phục vụ ngắt và nó sẽ được gọi ra theo 1 chu kỳ xác định với mức ưu tiên cao nhất. Như vậy

Page 2: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 94

sẽ không có hiện tượng led bị chớp do vi điều khiển thực hiện quá nhiều công việc(thời gian giữa 2 lần quét bị giãn ra).

Tóm lại, nếu thực hiện các đoạn lệnh thì vi điều khiển thực hiện công việc 1 cách tuần tự ( từng dòng lệnh được thực thi), nếu sử dụng ngắt thì vi xử điều khiển thực hiện công việc 1 cách ngẫu nhiên (thực hiện công việc khi có sự kiện ngẫu nhiên).

Hình 1 minh họa cách tổ chức ngắt thông thường trong các chip AVR. Số lượng ngắt trên mỗi dòng chip là khác nhau, ứng với mỗi ngắt sẽ có vector ngắt, vector ngắt là các thanh ghi có địa chỉ cố định được định nghĩa trước nằm trong phần đầu của bộ nhớ chương trình.

Hình1. Tổ chức ngắt của AVR

Bảng 1 tóm tắt các vector ngắt có trên vi điều khiển Atmega16 STT

vector Địa chỉ Nguồn ngắt Mô tả

1 $000 RESET Chân ngoài, Power-on Reset, Brown-out

Reset, Watchdog Reset,và JTAG AVR Reset

2 $002 INT0 Ngắt ngoài 0 3 $004 INT1 Ngắt ngoài 1 4 $006 TIMER2 COMP So sánh bộ định thời 2 trùng khớp 5 $008 TIMER2 OVF Tràn bộ định thời 2 6 $00A TIMER1 CAPT Định thời 1 bắt sự kiện 7 $00C TIMER1 COMPA So sánh bộ định thời 1 trùng khớp A 8 $00E TIMER1 COMPB So sánh bộ định thời 1 trùng khớp B 9 $010 TIMER1 OVF Tràn bộ định thời 1

10 $012 TIMER0 OVF Tràn bộ định thời 0 11 $014 SPI, STC Truyền thông nối tiếp hoàn tất 12 $016 USART, RXC USART, nhận hoàn tất

Page 3: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 95

13 $018 USART, UDRE USART, dữ liệu thanh ghi trống 14 $01A USART, TXC USART, nhận hoàn tất truyền hoàn tất 15 $01C ADC Chuyển đổi ADC hoàn tất 16 $01E EE_RDY EEPROM sẵn sàng 17 $020 ANA_COMP Bộ so sánh tương tự 18 $022 TWI Giao diện 2 đường nối tiếp 19 $024 INT2 Ngắt ngoài 2 20 $026 TIMER0 COMP So sánh bộ định thời 0 trùng khớp 21 $028 SPM_RDY Lưu trữ bộ nhớ chương trình sẵn sàng

Dịch từ datasheet của Atmega16 trang 45. 3. Trình phục vụ ngắt – ISR.

Trong MikroC, trình phục vụ ngắt được định nghĩa như 1 hàm thông thường nhưng kèm theo khai báo địa chỉ của vector ngắt.

Cú pháp khai báo như sau: Kiểu_dữ_liệu_trả_về Tên_hàm() org địa_chỉ vector_ngắt { Khối lệnh; }

Địa chỉ vector ngắt được liệt kê trong bảng 1(bỏ đi $ vì đây là ký hiệu địa chỉ tuyệt đối, định dạng theo hệ thập lục phân trong C). Ví dụ: // Trình phục vụ ngắt khai báo trong phần mềm MikroC void Interrupt() org 0x16 { // chọn ngắt có địa chỉ là $016 RS485Master_Receive(dat); }

Do MikroC không hỗ trợ thư viện Interrupt nên việc khai báo không

giống như các trình biên dịch khác. Để biết cách khai báo của trình biên dịch khác, các bạn có thể tra cứu trong file Help của trình biên dịch đó với từ khóa Interrupt library.

Page 4: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 96

II. Ngắt ngoài (External Interrupt).

1. Giới thiệu Ngắt ngoài là loại ngắt duy nhất độc lập với các thiết bị của vi điều khiển, các ngắt khác thường gắn với hoạt động của 1 thiết bị nào đó như Timer/counter, USART, ADC…

Ngắt ngoài là cách rất hiệu quả để thực hiện giao tiếp giữa người dùng và vi điều khiển. Đối với Atmega16, ta có 3 ngắt ngoài là INT0, INT1 và INT2 tương ứng với các chân PD2, PD3, PB2.

Khi làm việc với các thiết bị ngoại vi của AVR, hầu như chúng ta chỉ thao tác trên các thanh ghi chức năng đặc biệt - SFR (Special Function Registers) trên vùng nhớ IO, mỗi thiết bị bao gồm một tập hợp các thanh ghi điều khiển, trạng thái, ngắt…khác nhau. Với ngắt ngoài, có 3 thanh ghi liên quan đến ngắt ngoài đó là MCUCR, GICR và GIFR. Cụ thể các thanh ghi được trình bày trong phần sau.

2. Các thanh ghi điều khiển ngắt ngoài. a. MCUCR (MCU Control Register) và MCUCSR (MCU Control

and Status Register) Là 2 thanh ghi xác lập chế độ ngắt cho ngắt ngoài, quan sát hình 2 trước

khi tìm hiểu 2 thanh ghi này.

Hình 2. Kết nối ngắt ngoài cho Atmega16

Giả sử chúng ta kết nối các ngắt ngoài trên Atmega16 như hình 2, các nút nhấn dùng tạo ra các ngắt. Có 4 khả năng có thể xảy ra khi chúng ta nhấn và thả các nút nhấn. Nếu không nhấn, trạng thái (logic) các chân INT là cao do điện trở kéo lên, khi vừa nhấn 1 nút, sẽ có sự chuyển trạng thái từ cao sang thấp, chúng ta gọi là cạnh xuống - Falling Edge, khi nút được nhấn và giữ, trạng thái các chân INT được xác định là thấp và cuối cùng khi thả các nút, trạng thái chuyển từ thấp sang cao, gọi là cạnh lên – Rising Edge. Trong những trường hợp cụ thể, 1 trong 4 khả năng trên đều hữu ích, ví dụ trong các ứng

PB2

PD3

PD2

VCC

Mức cao

Mức thấp

Cạnh xuống Cạnh lên

Page 5: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 97

dụng đếm xung (đếm encoder của servo motor chẳng hạn) thì 2 khả năng “cạnh” phải được dùng.

Thanh ghi MCUCR chứa các bits cho phép chúng ta chọn 1 trong 4 khả trên để kích hoạt ngắt ngoài INT0 và INT1. Dưới đây là cấu trúc thanh ghi MCUCR được trích ra từ datasheet của chip Atmega16.

MCUCR là một thanh ghi 8 bit nhưng đối với hoạt động ngắt ngoài,

chúng ta chỉ quan tâm đến 4 bit thấp của nó (4 bit cao dùng cho Power manager và Sleep Mode).

Bốn bit thấp là các bit Interrupt Sense Control (ISC) trong đó 2 bit ISC11:ISC10 dùng cho INT1 và 2 bit ISC01:ISC00 dùng cho INT0. Hãy nhìn vào bảng tóm tắt bên dưới để biết chức năng của các bit trên, đây là bảng sự thật của 2 bit ISC11, ISC10. Bảng sự thật cho các bit ISC01, ISC00 hoàn toàn tương tự.

Bảng 2: INT1 Sense Control

ISC11 ISC10 Mô tả 0 0 Khi chân INT1 ở mức thấp thì xuất hiện ngắt 0 1 Có bất kỳ sự thay đổi mức logic tại chân INT1 thì ngắt xuất hiện 1 0 Có cạnh xuống tại chân INT1 thì ngắt xuất hiện 1 1 Có cạnh lên tại chân INT1 thì ngắt xuất hiện

Bảng 2.1: INT0 Sense Control ISC01 ISC00 Mô tả

0 0 Khi chân INT0 ở mức thấp thì xuất hiện ngắt 0 1 Có bất kỳ sự thay đổi mức logic tại chân INT0 thì ngắt xuất hiện 1 0 Có cạnh xuống tại chân INT0 thì ngắt xuất hiện 1 1 Có cạnh lên tại chân INT0 thì ngắt xuất hiện

Thật dễ dàng để hiểu chức năng của các bit Sense Control, ví dụ bạn muốn set cho INT1 là ngắt cạnh xuống (Falling Edge) trong khi INT0 là ngắt cạnh lên (Rising Edge), hãy đặt dòng lệnh MCUCR =0x0B; (0x0B = 00001011 nhị phân) trong chương trình của bạn. Trong Atmega 16 còn có ngắt thứ 3 là INT2. Chế độ của ngắt này được xác lập qua thanh ghi MCUSR, cụ thể là bit 6 – ISC2

Nếu ISC2 được xóa (=0) thì ngắt xảy ra khi có cạnh xuống tại chân INT2. Nếu ISC2 được đặt (=1) thì ngắt xảy ra khi có cạnh lên tại chân INT2.

Page 6: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 98

Xung cấp cho INT2 phải có độ rộng lớn hơn 50ns thì mới xuất hiện ngắt, nếu nhỏ hơn thì không đảm bảo có ngắt xảy ra. Khi thay đổi bit ISC2, một ngắt có thể xảy ra, vì vậy trước khi thay đổi bit này, để tránh sai sót, ta cần vô hiệu hóa chức năng ngắt ngoài INT2 trong thanh ghi GICR sau đó mới thay đổi bit ISC2, cuối cùng, cờ ngắt INTF2 phải được xóa trong thanh ghi GIFR trước khi kích hoạt lại ngắt INT2.

b. Thanh ghi điều khiển ngắt chung – GICR (General Interrupt Control Register).

GICR cũng là 1 thanh ghi 8 bit nhưng chỉ có 3 bit cao (bit 5, bit 6 và bit 7) là được sử dụng cho điều khiển ngắt, cấu trúc thanh ghi như bên dưới (trích datasheet).

Bit 7: Điều khiển ngắt INT1, khi nó được đặt (=1) thì ngắt INT1 được cho phép hoạt động, ngược lại, nếu nó được xóa thì ngắt INT1 bị vô hiệu hóa. Bit 6: Tương tự bit 7, nó điều khiển ngắt INT0. Bit 5: Tương tự bit 7, nó điều khiển ngắt INT1.

c. Thanh ghi cờ ngắt chung – GIFR (General Interrupt Flag Register) Đây là thanh ghi trạng thái (cờ) của các ngắt INT0, INT1 và INT2. Nếu có 1

sự kiện ngắt phù hợp xảy ra trên chân INT0, bit INTF0 được tự động đặt (=1). Sau khi trình phục vụ ngắt kết thúc công việc thì cờ ngắt sẽ tự động xóa. Tương tự cho trường hợp của INTF1 và INTF2, chúng ta có thể sử dụng các bit này để nhận ra các ngắt, tuy nhiên điều này là không cần thiết nếu chúng ta cho phép ngắt tự động, vì vậy thanh ghi này thường không được quan tâm khi lập trình ngắt ngoài. Cấu trúc thanh ghi GIFR được trình bày trong hình dưới.

Sau khi đã thiết lập xong các bit của các thanh ghi điều khiển ngắt ngoài,

việc sau cùng là cho phép ngắt toàn cục thông qua việc đặt bit 7(bit I) của thanh ghi trạng thái chung(SREG) đã giới thiệu ở chương 1. Xin được nhắc lại cấu trúc thanh ghi này ở hình dưới.

Chú ý: Các chân ngắt PD2, PD3, PB2 khi sử dụng là các chân ngắt ngoài

thì phải thiết lập các chân này là ngõ vào thông qua thanh ghi DDRx. Tóm tắt quá trình thiết lập ngắt ngoài. +Bước 1: Chọn chế độ ngắt thông qua thanh ghi MCUCR và MCUCSR.

Page 7: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 99

+Bước 2: Khởi động ngắt ngoài thông qua thanh ghi GICR. +Bước 3: Cho phép ngắt toàn cục thông qua bit 7 của thanh ghi SREG.

Thực hành ngắt ngoài Ví dụ : Viết chương trình sử dụng ngắt điều khiển 1 led 7 đoạn bằng

phương pháp chốt. Tác động vào các chân INT bằng nút nhấn, tác động bằng cạnh xuống. Khi tác động vào chân INT0 thì giá trị hiển thị tăng 1, tác động vào chân INT1 thì giá trị hiển thị giảm 1, tác động vào chân INT2 thì giá trị hiển thị bằng 0.

Sơ đồ thí nghiệm như sau:

Hình 3. Sơ đồ thí nghiệm ngắt ngoài. Chương trình gợi ý:

Bai3_1_A : Thực hành ngắt ngoài. Sử dụng file mô phỏng BAI3_1.DSN

signed char count;// Biến đếm

//Mã hiển thị led 7 đoạn loại anode chung với a là LSB

char Bitmap[]={0XC0,0XF9,0XA4,0XB0,0X99,0X92,0X82,0XF8,0X80,0X90};

// Chương trình chính

void main(){

DDRB=0x00; // Cấu hình PORTB là ngõ vào

DDRD=0x00; // Cấu hình PORTD là ngõ vào

DDRC=0xFF; // Cấu hình PORTC là ngõ ra

PORTD=0xFF; // Sử dụng nội trở kéo lên

PORTB=0xFF; // Sử dụng nội trở kéo lên

// Cài đặt chế độ ngắt

ISC00_bit=0; // Cài đặt bit ISC00 của thanh ghi MCUCR

Page 8: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 100

ISC01_bit=1; // Cài đặt bit ISC01 của thanh ghi MCUCR

ISC10_bit=0; // Cài đặt bit ISC10 của thanh ghi MCUCR

ISC11_bit=1; // Cài đặt bit ISC11 của thanh ghi MCUCR

ISC2_bit=0; // Cài đặt bit ISC2 của thanh ghi MCUCSR

// Cho phép các ngắt ngoài thông qua 3 bit của thanh ghi GICR

INT0_bit=1;

INT1_bit=1;

INT2_bit=1;

// Cho phép ngắt toàn cục

SREG_I_bit=1;

while(1){

count++;

if (count>9) count=0;

PORTC=Bitmap[count];

delay_ms(6000);

}

}

void Interrupt() org 0x02 { //Trình phục vụ ngắt INT0

count++;

if (count>9) count=0;

PORTC=Bitmap[count];

}

void Interrupt1() org 0x04 {//Trình phục vụ ngắt INT1

count--;

if (count<0) count=9;

PORTC=Bitmap[count];

}

void Interrupt2() org 0x24 {//Trình phục vụ ngắt INT2

count=0;

PORTC=Bitmap[count];

}

Lưu ý: Nếu không biết MikroC định nghĩa các bit của các thanh ghi điều khiển ngắt như thế nào, ta có thể làm theo cách sau: +Thao tác byte trực tiếp trên các thanh ghi (1 hoặc nhiều byte) bằng các toán tử logic. +Chọn Start debugger (F9) để gọi trình gỡ rối, sau đó trong ô search for variable by assembly name ta gõ tên của bit cần thao tác, tên này chính là tên được định nghĩa trong datasheet. Ví dụ ta tìm bit INT0 của thanh ghi GICR thì ta gõ INT0. Lập tức trong cửa sổ sổ xuống sẽ gợi ý cho ta kết quả

Page 9: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 101

tên định nghĩa của bit cần tìm trong MikroC. Cách tìm này áp dụng cho tất cả các bit, thanh ghi và biến của AVR trong MikroC.

III. Timer/Counter – Bộ đếm, định thời. 1. Giới thiệu.

Timer/Counter là các module độc lập với CPU. Chức năng chính của các bộ Timer/Counter là bộ định thời thông qua việc đếm thời gian hoặc đơn giản chỉ là bộ đếm sự kiện. Trên các chip AVR, các bộ Timer/Counter còn có thêm chức năng tạo ra các điều biến bề rộng xung PWM (Pulse Width Modulation), ở một số dòng AVR, một số Timer/Counter còn được dùng như các bộ canh chỉnh thời gian (calibration) trong các ứng dụng thời gian thực. Các bộ Timer/Counter được chia theo độ rộng thanh ghi chứa giá trị định thời hay giá trị đếm của chúng, số lượng và chức năng các bộ Timer/Counter trên mỗi chip cũng có thể khác nhau (ví dụ bộ Timer/Counter0 trên Atmega8 ít chức năng hơn bộ Timer/Counter trên Atmega16). Chế độ hoạt động và phương pháp điều khiển của từng Timer/Counter cũng không hoàn toàn giống nhau. Ví dụ ở chip Atmega16 ta có 3 bộ Timer/Counter :

+ Timer/Counter0: là một bộ định thời, đếm với độ rộng thanh ghi 8 bit. Các chức năng chính bao gồm : Đếm sự kiện ngoài, tạo dao động, tạo PWM, tạo ngắt tràn... Các chức năng của các bộ Timer/Counter thiết lập như là các chế độ làm việc.

Lưu ý : ở Atmega8 bộ Timer/Counter0 không có chức năng tạo PWM. +Timer/Counter1: là bộ định thời, đếm với độ rộng thanh ghi 16 bit. Bộ

Timer/Counter này có 5 chế độ hoạt động chính. Ngoài các chức năng thông thường, ta thể tạo 2 tín hiệu PWM độc lập trên các chân OC1A và OC1B bằng Timer/Counter1.

+Timer/Counter2: tuy là một module 8 bit như Timer/Counter0, ngoài ra nó nó còn được sử dụng như một module canh chỉnh thời gian cho các ứng dụng thời gian thực (chế độ không đồng bộ). Chế độ không đồng bộ của Timer/Counter2 sẽ được bỏ qua vì có thể chế độ này không phổ biến.

Một số định nghĩa và quy ước: Timer/Counter: viết tắt thành T/C

BOTTOM: là giá trị thấp nhất mà một T/C có thể đạt được, giá trị này luôn là 0.

MAX: là giá trị lớn nhất mà một T/C có thể đạt được, giá trị này được quy định bởi bởi giá trị lớn nhất mà thanh ghi đếm của T/C có thể chứa được. Ví dụ với một bộ T/C 8 bit thì giá trị MAX luôn là 0xFF (tức 255 trong hệ thập phân), với bộ T/C 16 bit thì MAX bằng 0xFFFF (65535). Như thế MAX là giá trị không đổi trong mỗi T/C.

TOP: bộ đếm đạt giá trị TOP khi nó bằng với giá trị cao nhất trong trình tự đếm. Giá trị TOP có thể thiết lập cố định bằng giá trị MAX hoặc thiết lập khác thông qua các thanh ghi thích hợp của từng T/C.

Page 10: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 102

2. Timer/Counter0. a/. Các thanh ghi điều khiển T/C0: Thanh ghi điều khiển – Timer Counter Control Register – TCCR0

Đây là thanh ghi thiết lập các chế độ làm việc của T/C0 Bit 0-2: (CS-Clock Select) là 3 bit dùng để chọn nguồn xung đếm cho T/C0, chức năng của 3 bit này được tóm tắt qua bảng sau:

CS02 CS01 CS00 Mô tả 0 0 0 Không dùng nguồn xung (T/C) không làm việc 0 0 1 Lấy trực tiếp nguồn xung nuôi chip 0 1 0 Lấy tần số của nguồn xung nuôi chip chia 8 0 1 1 Lấy tần số của nguồn xung nuôi chip chia 64 1 0 0 Lấy tần số của nguồn xung nuôi chip chia 256 1 0 1 Lấy tần số của nguồn xung nuôi chip chia 1024 1 1 0 Lấy nguồn xung ngoài tại chân T0, tác động bằng

cạnh xuống 1 1 1 Lấy nguồn xung ngoài tại chân T0, tác động bằng

cạnh lên

Bit 3 và 6 (WGM): Là 2 bit điều khiển chế độ tạo dạng sóng Waveform Generation Mode (WGM). Các bit này giúp ta chọn giá trị TOP của bộ đếm và dạng sóng tạo ra. Các chế độ làm việc mà 2 bit này hỗ trợ bao gồm: Chế độ thông thường, chế độ xóa bộ đếm khi so sánh trùng khớp(CTC), và 2 chế độ điều biến bề rộng xung (PWM). Bảng sau tóm tắt các chế độ làm việc được thiết lập bởi 2 bit này.

STT WGM01 WGM00 Chế độ làm việc TOP Cập nhật vào

thanh ghi ORC0 Ảnh hưởng đến cờ tràn

0 0 0 Thông thường 0xFF Giá trị tức thời MAX

1 0 1 PWM, pha chính xác

0xFF TOP BOTTOM

2 1 0 CTC OCR0 Giá trị tức thời MAX

3 1 1 PWM, tốc độ

cao 0xFF BOTTOM MAX

Dịch từ datasheet của Atmega16 trang 83.

Bit 4 và 5 (COM): Các bit này điều khiển vận hành của chân OC0. Nếu 1 trong 2 bit này được đặt thì chân OC0 không hoạt động như là 1 chân I/O mà

Page 11: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 103

phục vụ cho việc xuất tín hiệu. Tuy nhiên, lưu ý rằng thanh ghi hướng dữ liệu (DDR) phải thiết lập sao cho chân OC0 là ngã ra.

Sau đây là các bảng thiết lập tùy chọn tương quan giữa các bit COM và WGM.

Khi các bit WGM được thiết lập với chế độ CTC (WGM01=1, WGM00=0) ta có bảng thiết lập các bit COM sau: COM01 COM00 Mô tả

0 0 Chế độ nhập xuất thông thường. 0 1 Đảo trạng thái chân OC0 khi có so sánh trùng khớp.

1 0 Xóa bit OC0 khi có so sánh trùng khớp. Chân OC0 xuống mức thấp.

1 1 Đặt bit OC0 khi có so sánh trùng khớp. Chân OC0 lên mức cao.

Khi các bit WGM được thiết lập với chế độ PWM tốc độ cao

(WGM01=0, WGM00=1) ta có bảng thiết lập các bit COM sau: COM01 COM00 Mô tả

0 0 Chế độ nhập xuất thông thường. 0 1 Reserved.

1 0 Xóa bit OC0 khi so sánh trùng khớp. Đặt OC0 tại BOTTOM. Chế độ thuận.

1 1 Đặt bit OC0 khi so sánh trùng khớp. Xóa OC0 tại BOTTOM. Chế độ nghịch.

Khi các bit WGM được thiết lập với chế độ PWM pha chính xác

(WGM01=1, WGM00=1) ta có bảng thiết lập các bit COM sau: COM01 COM00 Mô tả

0 0 Chế độ nhập xuất thông thường. 0 1 Reserved.

1 0 Xóa OC0 khi so sánh trùng khớp và đang đếm lên. Đặt OC0 khi so sánh trùng khớp và đang đếm xuống.

1 1 Đặt OC0 khi so sánh trùng khớp và đang đếm lên. Xóa OC0 khi so sánh trùng khớp và đang đếm xuống.

Bit7 – FOC0: Đây là bit so sánh trùng khớp cưỡng bức, bit này chỉ hoạt động khi WGM00 được xóa (chế độ không PWM). Tuy nhiên, để đảm bảo khả năng tương thích với các thiết bị trong tương lai, khi chọn chế độ PWM, ta phải xóa bit này. Khi ghi 1 vào bit này thì lập tức có 1 so sánh trùng khớp (trong chế độ PWM). Thanh ghi giá trị bộ đếm, định thời: Timer Counter Registor – TCNT0

Page 12: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 104

Thanh ghi này chứa giá trị hiện tại của bộ đếm, với T/C0 thì nó có độ rộng là 8 bit nên có giá trị đếm từ 0 đến 255. Ta có thể ghi trực tiếp giá trị vào thanh ghi này. Thanh ghi so sánh : Output Compare Registor – OCR0

Thanh ghi này chứa giá trị để so sánh với giá trị của bộ đếm chứa trong thanh ghi TCNT0, do đó nó có độ rộng bằng với độ rộng của thanh ghi TCNT0. Thanh ghi cờ ngắt của bộ đếm: Timer/Counter Interrupt Flag Register -TIFR

Đây là thanh ghi dùng chung cho 3 bộ đếm và nó có đủ các bit cờ ngắt thể hiện trạng thái của từng bộ đếm. Bit1- Output Compare Flag 0 – OCF0: bit này được đặt khi có sự so sánh trùng khớp xuất hiện giữa giá trị thanh ghi của bộ đếm và giá trị chứa trong thanh ghi so sánh. Khi muốn có 1 ngắt xảy ra khi có sự so sánh trùng khớp(OCF0=1), ta phải set bit I trong SREG và bit OCIE0 trong thanh ghi TIMSK. Bit 2 –Timer/Counter0 Overflow Flag – TOV0: đây là cờ tràn của bộ đếm, tràn là hiện tượng khi bộ đếm đếm đến giá trị cao nhất và đếm thêm 1 lần nữa. Khi đó cờ tràn sẽ được đặt. Khi muốn có 1 ngắt xảy ra khi có cờ tràn, ta phải set bit I trong SREG và bit TOIE0 trong thanh ghi TIMSK. Thanh ghi mặt nạ của bộ đếm: Timer/Counter Interrupt Mask Register – TIMSK

Đây cũng là thanh ghi dùng chung cho cả 3 bộ đếm. Bit 0 : là TOIE0, bit cho phép ngắt tràn cho T/C0. Bit 1 : là OCIE1 là bit cho phép ngắt khi có so sánh trùng khớp xảy ra trong việc so sánh TCNT0 với OCR0.

Trên đây là những giới thiệu sơ qua về các thanh ghi có liên quan đến việc điều khiển T/C0. Đối với T/C1 và T/C2, các bit và thanh ghi có chức năng tương tự sẽ không được nhắc lại.

Page 13: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 105

Phần sau ta sẽ tìm hiểu kỹ hơn về chức năng của các chế độ hoạt động chính của T/C0 b/. Bộ định thời với TIMER/COUNTER0.

Với các bộ định thời, ta có thể cài đặt một khoảng thời gian tùy ý để dùng vào một công việc nào đó. Ví dụ, đối với chương trình quét led 7 đoạn: ta muốn rằng cứ sau 10ms thì vi điều khiển sẽ quét led 1 lần, bất kể công việc nào đang thực thi cũng phải gác lại. Ở đây ta sẽ không dùng hàm delay để tạo thời gian ngắt quãng giữa các lần quét vì nhược điểm của hàm delay là khoảng thời gian delay bị bỏ phí vô ích trong khi nó có thể dùng để xử lý tác vụ khác. Do đó, các bộ định thời là lý tưởng cho các công việc cần thực hiện thường xuyên và tốn ít thời gian xử lý.

Hình 3. So sánh phương pháp delay và timer/counter.

(MCU nop: trong khoảng thời gian này MCU không làm gì cả)

Nguyên tắc hoạt động của bộ định thời thực chất vẫn là việc đếm, bộ đếm sẽ đếm xung, xung được cấp bởi nguồn xung, nguồn xung của T/C0 được chọn bởi 3 bit CS00, CS01, CS02 của thanh ghi TCCR0. Như vậy, chọn nguồn xung là chọn khoảng giá trị thời gian cho bộ đếm.

Giả sử nguồn xung clock “nuôi” chip của chúng ta là clkI/O=1MHz tức là 1 nhịp mất 1us. Để tạo khoảng thời gian dài hơn, ta chia bớt tầng số này cho 1 hệ số định sẵn. Nếu chúng ta lấy trực tiếp nguồn xung nuôi chip làm xung đếm, tức là tần số của T/C0 (tạm gọi là fT/C0) cũng bằng clkI/O=1MHz, cứ 1us T/C0 được kích và TCNT0 sẽ tăng 1 đơn vị. Khi đó giá trị lớn nhất mà T/C0 có thể đạt được là 256 x 1us=256us, giá trị này nhỏ hơn 10ms mà ta mong muốn. Nếu lấy nguồn xung nuôi chip chia 64, nghĩa là cứ sau 64 nhịp (64us) thì TCNT0 mới tăng 1 đơn vị, khả năng lớn nhất mà T/C0 đếm được là 256 x 8us=16384us=16ms, lớn hơn 10ms, vậy ta hoàn toàn có thể sử dụng nguồn xung của chip đã chia 64 làm nguồn xung cho bộ đếm.

Page 14: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 106

Mặc định, bộ đếm sẽ đếm từ 0 đến giá trị cao nhất, trong trường hợp T/C0, do thanh ghi chứa giá trị của nó có 8 bit nên bộ đếm sẽ đếm từ 0-255. Ta có 2 cách đặt số nhịp đếm:

+Cách 1: Dùng ngắt tràn, cách này đơn giản, ta chỉ việc đặt trước 1 giá trị cho bộ đếm và ghi vào thanh ghi TCNT0. Khi đó bộ đếm sẽ đếm từ giá trị này đến 255 sẽ xảy ra ngắt, trong trình phục vụ ngắt ta sẽ khởi tạo lại giá trị ban đầu cho TCNT0 sau đó mới thực hiện công việc cần làm. Như vậy, số nhịp đếm sẽ bằng 255 trừ đi giá trị khởi tạo ban đầu.

Tóm lại:

XTALf

prescalerTCNTT 0255

Với prescaler là hệ số chia, XTALf là tần số thạch anh dùng cho chip, TCNT0

là giá trị đặt trước, T là chu kỳ lặp lại công việc (chu kỳ này không thể chính xác hoàn toàn do sai số phép tính và thời gian quét led…).

+Cách 2: Dùng bộ so sánh (ngắt so sánh trùng khớp), ta sẽ ghi giá trị số nhịp cần đếm vào thanh ghi so sánh ORC0, khi đếm từ 0 đến giá trị so sánh trùng khớp thì sẽ xảy ra 1 ngắt giúp ta thực hiện 1 công việc nào đó.

Tóm lại:

XTALf

prescalerTCNTT 0

Sau đây là ví dụ minh họa dùng 2 cách trên. Viết chương trình điều khiển 2 led 7 đoạn bằng phương pháp quét, giá

trị hiển thị tăng lên sau mỗi giây.

Hình 4. Sơ đồ thực hành ngắt tràn và ngắt so sánh.

Trong mạch này, ta cần thiết lập lại thông số thạch anh cần dùng là 1 Mhz trong trình biên dịch MikroC và trình mô phỏng ISIS.

Page 15: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 107

Chương trình tham khảo:

Bai3_2_A : Thực hành Timer – Ngắt tràn. Sử dụng file mô phỏng BAI3_2.DSN

//MÃ LED 7 ĐOẠN LOẠI ANODE CHUNG, A LÀ LSB

const char table[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};

char var;

void main(){

// KHAI BÁO HƯỚNG DỮ LIỆU CHO PORTC VÀ PORTD LÀ NGÕ RA

DDRC=0xFF;

DDRD=0xFF;

// CHỌN NGUỒN XUNG LẤY TỪ XUNG NHỊP CẤP CHO CHIP CHIA 64 HAY PRESCALER =64

CS00_bit=1;

CS01_bit=1;

CS02_bit=0;

// CHO PHÉP NGẮT TRÀN – bit TOIE0 của thanh ghi TIMSK

TOIE0_bit=1;

// CHO PHÉP NGẮT TOÀN CỤC – bit I của thanh ghi trạng thái SREG

SREG_I_bit=1;

// THIẾT LẬP GIÁ TRỊ ĐẦU CHO BỘ ĐẾM

TCNT0=159;

while(1){

var++;

if(var>99) var=0;

delay_ms(100);

}

}

//TRÌNH PHỤC VỤ NGẮT TRÀN CỦA BỘ ĐẾM T/C0

void LEDSCAN_ISR() org 0x12 {

// THIẾT LẬP LẠI GIÁ TRỊ ĐẦU CHO BỘ ĐẾM

TCNT0=159;

//HIỂN THỊ SỐ HÀNG CHỤC

PORTC=table[var/10];

PORTD=0x02;

delay_ms(1);// THỜI GIAN TRỄ PHÙ HỢP ĐỘ TRỄ TRUYỀN CỦA LINH KIỆN

PORTD=0x00;

//HIỂN THỊ SỐ HÀNG ĐƠN VỊ

PORTC=table[var%10];

PORTD=0x01;

delay_ms(1); // THỜI GIAN TRỄ PHÙ HỢP ĐỘ TRỄ TRUYỀN CỦA LINH KIỆN

Page 16: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 108

PORTD=0x00;

}

Các linh kiện cần 1 khoảng thời gian để chuyển từ mức thấp sang mức

cao mà vi điều khiển có tốc độ xử lý rất cao. Vì vậy để đảm bảo các linh kiện hoạt động bình thường, ta có thể tạo 1 khoảng delay tương đối nhỏ để khắc phục độ trễ truyền của linh kiện.

Lưu ý: Khi dùng ngắt tràn để gọi chương trình quét led 7 đoạn, nếu số lượng led nhiều (nhiều hơn 2) thì ta không quét 1 lần tất cả các led mà mỗi lần có ngắt chỉ quét 1 led (xuất dữ liệu và cấp địa chỉ) và lần ngắt sau sẽ quét led tiếp theo. Như vậy sẽ đảm bảo thời gian thực hiện trình phục vụ ngắt không quá lâu cũng như đảm bảo thời gian đáp ứng cho các linh kiện mà không cần dùng hàm delay.

Bai3_2_B : Thực hành Timer – Ngắt so sánh trùng khớp. Sử dụng file mô phỏng BAI3_2.DSN

//MÃ LED 7 ĐOẠN LOẠI ANODE CHUNG, A LÀ LSB

const char table[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};

char var=0;

void main(){

// KHAI BÁO HƯỚNG DỮ LIỆU CHO PORTC VÀ PORTD LÀ NGÕ RA

DDRC=0xFF;

DDRD=0xFF;

//CHỌN NGUỒN XUNG LẤY TỪ XUNG NHỊP CẤP CHO CHIP CHIA 64 HAY PRESCALER =64

CS00_bit=1;

CS01_bit=1;

CS02_bit=0;

//CHỌN CHẾ ĐỘ CTC – BỘ ĐẾM SẼ BỊ XÓA MỖI LẦN CÓ NGẮT TRÙNG KHỚP

WGM00_bit=0;

WGM01_bit=1;

//CHO PHÉP NGẮT KHI SO SÁNH TRÙNG KHỚP

OCIE0_bit=1;

//CHO PHÉP NGẮT TOÀN CỤC

SREG_I_bit=1;

// THIẾT LẬP GIÁ TRỊ SO SÁNH

OCR0=96;

while(1){

var++;

if(var>99) var=0;

delay_ms(100);

}

}

Page 17: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 109

//TRÌNH PHỤC VỤ NGẮT SO SÁNH TRÙNG KHỚP

void LEDSCAN_ISR() org 0x26 {

// HIỂN THỊ SỐ HÀNG CHỤC

PORTC=table[var/10];

PORTD=0x02;

delay_ms(1);

PORTD=0x00;

// HIỂN THỊ SỐ HÀNG ĐƠN VỊ

PORTC=table[var%10];

PORTD=0x01;

delay_ms(1);

PORTD=0x00;

}

Các bước cơ bản sử dụng ngắt do hoạt động của bộ T/C bao gồm: +Chọn nguồn xung theo khoảng thời gian cần thiết, thông qua 3 bit CS của thanh ghi TCCR. +Chọn loại ngắt phù hợp thông qua thanh ghi mặt nạ ngắt. +Cho phép ngắt toàn cục ở thanh ghi SREG. +Viết trình phục vụ ngắt theo địa chỉ ngắt tương ứng. (Xem lại trang 95 - 96).

Ngoài ra nếu dùng ngắt tràn thì phải khởi tạo lại giá trị đầu sau mỗi lần có ngắt. Nếu dùng ngắt so sánh thì phải chọn chế độ CTC (xóa bộ đếm mỗi lần xảy ra ngắt – xem lại trang 103) hoặc xóa bộ đếm thủ công trong trình phục vụ ngắt. Hai ví dụ về sử dụng bộ định thời sử dụng file mô phỏng BAI3_2.DSN c/. Bộ đếm sự kiện với TIMER/COUNTER0.

Ngoài cách dùng nguồn xung nhịp của chip để tạo xung đếm, ta còn có thể dùng lợi dụng sự thay đổi mức logic tại chân T0 của vi điều khiển làm xung cấp cho bộ đếm. Lúc này, bộ đếm T/C0 của chúng ta trở thành bộ đếm sự kiện với sự kiện là sự thay đổi mức logic tại chân T0. Bằng cách đặt giá trị cho thanh ghi TCCR0 (CS02=1, CS01=1, CS00=0) cho phép đếm “cạnh xuống” trên chân T0, nếu TCCR0 (CS02=1, CS01=1, CS00=1) thì “cạnh lên” trên chân T0 sẽ được đếm (Xem lại trang 103).

Page 18: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 110

Hình 5. Sơ đồ thực hành bộ đếm sự kiện với T/C0.

Viết chương trình sử dụng sơ đồ trên sao cho khi nhấn nút thì giá trị hiển thị tăng 1 đơn vị.

Bai3_2_C : Thực hành Timer – Bộ đếm sự kiện. Sử dụng file mô phỏng BAI3_2_B.dsn

//MÃ LED 7 ĐOẠN LOẠI ANODE CHUNG A LÀ LSB

const char table[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};

void main(){

// KHAI BÁO HƯỚNG DỮ LIỆU

DDRC=0XFF;

DDRB=0x00;

// CHỌN XUNG ĐẾM LẤY TỪ CHÂN T0, TÁC ĐỘNG BẰNG CẠNH XUỐNG

CS00_bit=0;

CS01_bit=1;

CS02_bit=1;

while(1){

//GIỚI HẠN GIÁ TRỊ ĐẾM

if(TCNT0>9) TCNT0=0;

PORTC=table[TCNT0];

}

}

Trong chương trình trên, ta hoàn toàn có thể sử dụng ngắt tràn hoặc ngắt so sánh trùng khớp, tùy theo mục đích sử dụng.

Ví dụ bạn muốn đếm số người đi vào 1 phòng, khi đếm đủ 10 người thì phát 1 thông báo. Để thực hiện việc này, ta chỉ cần mắc cảm biến vào chân T0, sau đó thiết lập giá trị so sánh của thanh ghi OCR0, cuối cùng là cho phép ngắt so sánh trùng khớp và ngắt toàn cục, thông báo sẽ được thực hiện bằng trình phục vụ ngắt.

Lưu ý, các bạn có thể thiết lập chế độ CTC(Clear Timer on Compare Match) bởi các bit WGM của thanh ghi TCCR0 (xem lại trang 103). Khi đó, bộ đếm sẽ khởi tạo lại mỗi khi có ngắt so sánh trùng khớp.

d. Điều biến bề rộng xung – PWM với TIMER/COUNTER0. Trước tiên, ta cần tìm hiểu về khái niệm PWM.

Nếu ta muốn điều khiển tốc độ 1 động cơ hay nói cách khác là công suất cấp cho động cơ, thông thường ta dùng 1 biến trở mắc nối tiếp với động cơ, điều chỉnh biến trở chính là thay đổi công suất cấp cho động cơ. Tuy nhiên, nhược điểm của cách này là làm tiêu tán một phần năng lượng qua biến trở. Vì vậy cách này không hiệu quả.

Page 19: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 111

Người ta thay biến trở bằng 1 nút nhấn, khi ta nhấn thì động cơ quay, khi thôi nhấn thì động cơ dừng. Khi tần số nhấn-nhả cao thì động cơ quay như được cấp dòng liên tục. Ta tiếp tục thử nghiệm với thời gian nhấn và nhả khác nhau. Khi thời gian nhấn dài hơn thời gian nhả thì động cơ quay nhanh hơn khi thời gian nhả dài hơn thời gian nhấn, ta thấy thời gian nhấn chính là bề rộng của xung tạo ra. Bề rộng xung càng lớn thì công suất cấp cho động cơ càng lớn. Đó là ý tưởng cơ bản để sử dụng PWM điều khiển vận tốc động cơ (và điều khiển nhiều thứ khác nữa). Trong thực tế, nút nhấn được thay bằng khóa điện tử ( transistor, thường là FET), việc còn lại là tạo xung có bề rộng thay đổi được. Nhiệm vụ này thực hiện dễ dàng với vi điều khiển thông qua việc sử dụng chế độ PWM của các bộ Timer/Counter có hỗ trợ.

Hình 6. Mô tả các phương pháp điều khiển công suất động cơ.

Hình 7. Dạng xung PWM.

Trong điều chế độ rộng xung, ta cần chú ý các thông số sau: +Tần số hoặc chu kỳ (Time Period) của xung. +Độ rộng xung (Duty Cycle). Như vậy, để tạo xung PWM, ta sẽ thay đổi các thông số trên bằng các chức năng trong chế độ PWM của AVR. Ta trở lại các khái niệm ban đầu của bộ đếm và liên kết sự tương quan của các khải niệm này với PWM. BOTTOM: là giá trị nhỏ nhất của bộ đếm, luôn bằng 0.

Page 20: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 112

MAX: là giá trị lớn nhất của bộ đếm, MAX=255(0xff) đối với bộ đếm 8 bit như T/C0 và T/C2, MAX=65535 với bộ đếm 16 bit như T/C1. TOP: trong các ứng dụng PWM, giá trị này là cố định đối với T/C0 và T/C2 (TOP=0xFF), riêng T/C1 giá trị này có thể cố định(0x00FF, 0x01FF, hoặc 0x03FF) hoặc do người dùng tự định nghĩa thông qua thanh ghi OCR1A hoặc ICR1. Tham khảo thêm tại trang 83, 112 và 128 datasheet của ATMEGA16. OUTPUT COMPARE(OC) là giá trị so sánh chứa trong thanh ghi OCRx, nó có thể tạo ra ngắt so sánh hoặc tạo sự thay đổi mức logic ở chân OCx(x có thế là 0, 1A,1B hoặc 2) Các giá trị trên chính là các mốc thời gian mà mức logic thay đổi trên chân tạo xung của vi điều khiển. Các bạn có thể theo dõi hình sau để có sự liên tưởng.

Hình 8. So sánh hình dạng xung và các mốc thời gian của T/C.

Có thể giải thích sự tương quan giữa các mốc thời gian của T/C và dạng của xung PWM như sau: Trong chu kỳ đếm từ BOTTOM đến TOP, khi giá trị thanh ghi TCNTx của bộ đếm tăng đến giá trị OC thì có sự thay đổi mức logic tại chân OCx(x có thế là 0, 1A,1B hoặc 2). Khi đếm đến TOP thì lại có sự thay đổi mức logic ở chân OCx lần nữa và lại bắt đầu chu kỳ mới. Như vậy: từ BOTTOM đến TOP là chu kỳ của 1 xung PWM, từ TOP đến OC là độ rộng của xung. Đây là tương quan theo chế độ PWM thuận. Chế độ PWM nghịch sẽ được giới thiệu sau. Ta bắt đầu tìm hiểu cách sử dụng chức năng PWM của T/C mà cụ thể là T/C0. Fast PWM: PWM tần số cao với T/C0. Chế độ FAST PWM được thiết lập trong thanh ghi TCCR0 bởi 2 bit WGM (xem lại trang 103) với WGM00=1, WGM01=1. Sau khi khởi tạo chế độ FAST PWM, ta tiến hành chọn dạng tín hiệu PWM ở ngã ra. Dạng tín hiệu ngã ra (chân OC0) được quy định bởi các bit COM00 và COM01. COM01 COM00 Mô tả

0 0 Chế độ nhập xuất thông thường. 0 1 Reserved.

1 0 Xóa bit OC0 khi so sánh trùng khớp. Đặt OC0 tại BOTTOM. Chế độ thuận.

Page 21: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 113

1 1 Đặt bit OC0 khi so sánh trùng khớp. Xóa OC0 tại BOTTOM. Chế độ nghịch.

Ví dụ: Ta có cấu hình các bit như sau: WGM00=1; WGM01=1;Chế độ Fast PWM được chọn COM01=1; COM00=0; Chế độ PWM thuận: Từ BOTTOM đến OC, ngã ra ở mức cao (5V), Từ OC đến TOP ngã ra ở mức thấp(0V). Như vậy khoảng thời gian từ BOTTOM đến OC chính là độ rộng xung PWM. Dạng tín hiệu giống như hình 8.

Ngược lại, nếu chọn COM01=1; COM00=0; Từ BOTTOM đến OC, ngã ra ở mức thấp (0V), Từ OC đến TOP ngã ra ở mức cao(5V). Như vậy khoảng thời gian từ OC đến TOP chính là độ rộng xung PWM. Dạng tín hiệu giống như hình 9.

Hình 9. Dạng PWM nghịch.

Đối với PWM của T/C0, giá trị TOP luôn là 0xFF(255), do đó tốc độ PWM chỉ phụ thuộc vào hệ số chia xung nhịp (Prescaler) quyết định bởi các bit CS00 và CS01 của thanh ghi TCCR0. Độ rộng xung liên quan tới giá trị OC chứa trong thanh ghi OCR0 và tùy vào ta chọn chế độ PWM thuận hay nghịch. Ví dụ: Viết chương trình điều khiển độ rộng xung. Có 2 nút nhấn nối với 2 chân ngắt ngoài INT0 và INT1. Khi nhấn nút nối với INT0 thì độ rộng xung tăng, khi nhấn nút nối với INT1 thì độ rộng xung giảm. Chương trình tham khảo:

Bai3_2_D : Thực hành Timer – Fast PWM với T/C0. Sử dụng file mô phỏng BAI3_2_C.DSN

void main(){

//CẤU HÌNH HƯỚNG DỮ LIỆU

DDRB=0xFF;

DDRD=0x00;

//SỬ DỤNG ĐIỆN TRỞ NỘI KÉO LÊN

PORTD=0xFF;

//KHAI BÁO CHẾ ĐỘ NGẮT – NGẮT TÁC ĐỘNG BẰNG CẠNH LÊN TẠI CÁC CHÂN INTx

Page 22: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 114

ISC00_bit=0;

ISC01_bit=1;

ISC10_bit=0;

ISC11_bit=1;

//CHO PHÉP NGẮT NGOÀI

INT0_bit=1;

INT1_bit=1;

//CHO PHÉP NGẮT TOÀN CỤC

SREG_I_bit=1;

//CHỌN NGUỒN XUNG ĐẾM - PRESCALER = 1024

//FREQUENCY=16MHz/(256*1024)=61HZ

CS00_bit=1;

CS01_bit=0;

CS02_bit=1;

//CHỌN CHẾ ĐỘ FAST PWM CHO T/C0

WGM00_bit=1;

WGM01_bit=1;

//CHỌN DẠNG XUNG TẠI CHÂN OC0 – PWM THUẬN

COM01_bit=1;

COM00_bit=0;

//ĐẶT GIÁ TRỊ ĐỘ RỘNG XUNG BAN ĐẦU LÀ 50%

OCR0=127;

while(1){

//VÒNG LẶP VÔ TẬN

}

}

//TRÌNH PHỤC VỤ NGẮT INT0 – TĂNG ĐỘ RỘNG XUNG 10%

void Interrupt() org 0x02 {

OCR0+=25;

if(OCR0>=225) OCR0=225;//GIỚI HẠN ĐỘ RỘNG XUNG TỐI ĐA

}

//TRÌNH PHỤC VỤ NGẮT INT1 – GIẢM ĐỘ RỘNG XUNG 10%

void Interrupt1() org 0x04 {

OCR0-=25;

if(OCR0<=50) OCR0=50;//GIỚI HẠN ĐỘ RỘNG XUNG TỐI THIỂU

}

Nhắc lại : Chân Ocx phải được khai báo là ngã ra mới có thể xuất được tín hiệu PWM. Phase correct PWM: PWM với pha chính xác.

Page 23: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 115

Phase correct PWM cung cấp một chế độ tạo xung PWM có độ phân giải cao (high resolution) nên được gọi là Phase correct PWM. Về cách điều khiển, Phase correct hầu như giống fast PWM, nghĩa là nếu bạn đã biết cách sử dụng các mode của fast PWM thì bạn sẽ hoàn toàn điều khiển được Phase correct PWM. Khác nhau cơ bản của 2 chế độ này là trong cách hoạt động, nếu Fast PWM có chu kỳ hoạt động trong 1 single-slope (một sườn) thì Phase correct PWM lại dual-slope (hai sườn).

Trong chế độ Fast PWM, T/C đếm từ 0 đến giá trị OC (chứa trong thanh ghi OCR) thì đảo trạng thái ngã ra chân OC. T/C tiếp tục đếm đến giá trị TOP thì chân OC lại đảo trạng thái lần nữa đồng thời bộ đếm được xóa về 0.

Trong chế độ Phase correct PWM, Trong chế độ Fast PWM, T/C đếm từ 0 đến giá trị OC (chứa trong thanh ghi OCR) thì đảo trạng thái ngã ra chân OC. T/C tiếp tục đếm đến giá trị TOP, lúc này chân OC không đảo trạng thái và bộ đếm cũng không được xóa về 0 mà bắt đầu đếm ngược từ TOP. Khi đếm ngược về đến giá trị OC thì chân OC mới đảo trạng thái lần nữa, bộ đếm tiếp tục đếm về 0 để hoàn tất chu kỳ đếm. Để dễ so sánh 2 kiểu PWM, ta hãy quan sát sơ đồ sau:

Page 24: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 116

Hình 10. So sánh 2 chế độ PWM.

Nhắc lại: BOTTOM: là giá trị nhỏ nhất của bộ đếm, luôn bằng 0. MAX: là giá trị lớn nhất của bộ đếm, MAX=255(0xff) đối với bộ đếm 8 bit như T/C0 và T/C2, MAX=65535 với bộ đếm 16 bit như T/C1. TOP: trong các ứng dụng PWM, giá trị này là cố định đối với T/C0 và T/C2 (TOP=0xFF), riêng T/C1 giá trị này có thể cố định(0x00FF, 0x01FF, hoặc 0x03FF) hoặc do người dùng tự định nghĩa thông qua thanh ghi OCR1A hoặc ICR1 (tùy theo chế độ mà ta chọn qua các bit WGM). 3. Timer/Counter 1 Nhìn chung cách điều khiển T/C1 khá giống với T/C0. Khác nhau cơ bản là các thanh ghi chứa dữ liệu như bộ đếm TCNT hoặc OCR có độ rộng 16 bit. Các bit điều khiển cũng có nhiều hơn do T/C1 có rất nhiều chế độ hoạt động. Sau đây xin giới thiệu các thanh ghi điều khiển T/C1. Thanh ghi điều khiển: Gồm có 2 thanh ghi TCCRA và TCCRB

Ta có thể thấy các bit quen thuộc như CS, WGM, COM với chức năng tương tự T/C0:

Page 25: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 117

Bit 0-2 của TCCT1B: Các bit chọn nguồn xung cho bộ đếm (CS-Clock Select)

CS12 CS11 CS10 Mô tả 0 0 0 Không dùng nguồn xung (T/C) không làm việc 0 0 1 Lấy trực tiếp nguồn xung nuôi chip 0 1 0 Lấy tần số của nguồn xung nuôi chip chia 8 0 1 1 Lấy tần số của nguồn xung nuôi chip chia 64 1 0 0 Lấy tần số của nguồn xung nuôi chip chia 256 1 0 1 Lấy tần số của nguồn xung nuôi chip chia 1024 1 1 0 Lấy nguồn xung ngoài tại chân T1, tác động bằng

cạnh xuống 1 1 1 Lấy nguồn xung ngoài tại chân T1, tác động bằng

cạnh lên Bit 3 và 4 của TCCR1B và bit 0 và 1 của TCCT1A: Các bit chọn dạng sóng (chọn chế độ làm việc của T/C1) (WGM-WAVE FORM GENERATION MODE) T/C1 có đến 15 chế độ tạo dạng sóng, nhưng thực chất nếu sử dụng hết chức năng của T/C1 ta chỉ cần 7 chế độ. Bảng sau liệt kê tất cả các chế độ tạo dạng sóng của T/C1.

Page 26: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 118

Trong đó, khi dùng chế độ PWM ta chỉ cần nắm chế độ 10, 14 (hoặc 11, 15). Khi dùng chế độ CTC có thể dùng chế độ 4 hoặc 12. Bit 6-7 và bit 4-5: Các bit này điều khiển sự vận hành của chân OC1A và OC1B. Do T/C1 có 2 kênh PWM độc lập nhau và việc xuất dữ liệu của 2 kênh sẽ do 2 chân tương ứng là OC1A và OC1B đảm nhiệm nên khi dùng kênh nào thì phải cấu hình chân OCR1x tương ứng làm ngã ra. Sau đây là các bảng thiết lập tùy chọn tương quan giữa các bit COM và WGM. Chế độ không dùng PWM COM1A1/COM1B1 COM1A0/COM1B0 Mô tả

0 0 Chế độ nhập xuất thông thường.

0 1 Đảo trạng thái chân OC1A/OC1B khi có so sánh trùng khớp.

1 0 Xóa bit OC1A/OC1B khi có so sánh trùng khớp. Chân OC1A/OC1B xuống mức thấp.

1 1 Đặt bit OC1A/OC1B khi có so sánh trùng khớp. Chân OC1A/OC1B lên mức cao.

Chế độ PWM tốc độ cao COM1A1/COM1B1 COM1A0/COM1B0 Mô tả

0 0 Chế độ nhập xuất thông thường.

0 1

WGM13:0 = 15: Đảo trạng thái OCnA khi so sánh trùng khớp, OCnB chế độ nhập xuất thông thường. Mọi thiết lập khác đối với WGM13:0: Chế độ nhập xuất thông thường.

1 0 Xóa bit OC1A/OC1B khi so sánh trùng khớp. Đặt OC1A/OC1B tại BOTTOM. Chế độ thuận.

1 1 Đặt bit OC1A/OC1B khi so sánh trùng khớp. Xóa OC1A/OC1B tại BOTTOM. Chế độ nghịch.

Chế độ PWM pha và tần số chính xác. COM1A1/COM1B1 COM1A0/COM1B0 Mô tả

0 0 Chế độ nhập xuất thông thường.

0 1

WGM13:0 = 9 hoặc 14: Đảo trạng thái OCnA khi so sánh trùng khớp, OCnB chế độ nhập xuất thông thường. Mọi thiết lập khác đối với WGM13:0: Chế độ nhập xuất thông thường.

1 0 Xóa bit OC1A/OC1B khi so sánh trùng khớp và đang đếm lên. Đặt OC1A/OC1B khi so sánh trùng khớp và đang đếm

Page 27: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 119

xuống.

1 1

Đặt bit OC1A/OC1B khi so sánh trùng khớp và đang đếm lên. Xóa OC1A/OC1B khi so sánh trùng khớp và đang đếm xuống.

Thanh ghi giá trị bộ đếm : TCNT1 – 16 bit gồm 1 byte cao (TCNT1H) và 1 byte thấp(TCNT1L).

Hai byte này truy xuất độc lập. Thanh ghi so sánh kênh A: OCR1A – 16bit gồm 1 byte cao (OCR1AH) và 1 byte thấp(OCR1AL).

Hai byte này truy xuất độc lập, chứa giá trị so sánh kênh A. Thanh ghi so sánh kênh B: OCR1B – 16bit gồm 1 byte cao (OCR1BH) và 1 byte thấp(OCR1BL).

Hai byte này truy xuất độc lập. chứa giá trị so sánh kênh B. Thanh ghi bắt sự kiện vào - ICR1 (InputCapture Register 1): ICR1 - 16bit gồm 1 byte cao (ICR1H) và 1 byte thấp(ICR1L).

Hai byte này truy xuất độc lập. Khi có 1 sự kiện trên chân ICP1, thanh ghi ICR1 sẽ ghi lại giá trị của thanh ghi đếm TCNT1. Một ngắt có thể xảy ra trong trường hợp này, vì thế Input Capture có thể được dùng để cập nhật giá trị “TOP” của T/C1. Ghi chú :

Page 28: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 120

Trong MikroC, các thanh ghi 16 bit được chia thành 2 byte độc lập và có tên gọi giống như trong datasheet.

Ví dụ : thanh ghi ICR1 sẽ được chia thành ICR1L (byte thấp) và ICR1H (byte cao).

+Để ghi dữ liệu vào thanh ghi 16 bit, ta cần chia dữ liệu ra thành 2 phần 8 bit và ghi dữ liệu vào 8 bit cao trước, 8 bit thấp sau.

Ví dụ : Ghi dữ liệu 0x1387 vào thanh ghi ICR1 ta làm như sau : ICR1H=0x13 ; ICR1L=0x87 ; +Để đọc dữ liệu từ thanh ghi 16 bit, ta chỉ cần thao tác như sau : unsigned int bien16bit; bien16bit = (( thanh_ghi_16bitH * 256) + thanh_ghi_16bitL);

Thanh ghi mặt nạ của bộ đếm: Timer/Counter Interrupt Mask Register – TIMSK

Bit2: Cho phép ngắt tràn bộ đếm T/C1 Bit3 : Cho phép ngắt so sánh trùng khớp kênh B. Bit4 : Cho phép ngắt so sánh trùng khớp kênh A. Bit5: Cho phép ngắt khi bắt sự kiện vào.

Trên đây là những giới thiệu sơ qua về các thanh ghi có liên quan đến việc điều khiển T/C1. Do T/C1 có rất nhiều chế độ làm việc, các bạn có thể tham khảo thêm datasheet để sử dụng hết chức năng của bộ T/C này.

Trong phần tiếp theo tôi sẽ giới thiệu ứng dụng quan trọng của bộ T/C1 là tạo PWM với việc điều khiển cả độ rộng xung và tần số (T/C0 và T/C2 không điều khiển được tần số chính xác do giá trị TOP cố định). Nhắc lại: BOTTOM: là giá trị nhỏ nhất của bộ đếm, luôn bằng 0. MAX: là giá trị lớn nhất của bộ đếm, MAX=255(0xff) đối với bộ đếm 8 bit như T/C0 và T/C2, MAX=65535 với bộ đếm 16 bit như T/C1. TOP: trong các ứng dụng PWM, giá trị này là cố định đối với T/C0 và T/C2 (TOP=0xFF), riêng T/C1 giá trị này có thể cố định(0x00FF, 0x01FF, hoặc 0x03FF) hoặc do người dùng tự định nghĩa thông qua thanh ghi OCR1A hoặc ICR1 (tùy theo chế độ mà ta chọn qua các bit WGM). Điều khiển servo với Phase correct PWM của bộ T/C1:

Servo là một tổ hợp gồm 1 động cơ DC công suất nhỏ, hộp giảm tốc và bộ điều khiển góc quay. Có 2 loại chính là Servo thường và digital Servo, trong ví dụ này tôi giới thiệu Servo thường (phổ biến). Servo thường có 3 dây, dây màu đen là dây GND, dây đỏ là dây nguồn (thường là 5V) và 1 dây trắng hoặc vàng và dây tín hiệu (có một số loại Servo có màu dây khác, bạn cần tham khảo datasheet của chúng). Vì các Servo đã có sẵn mạch điều khiển góc quay bên trong nên chúng ta không cần bất cứ giải thuật gì mà chỉ cần cấp tín hiệu PWM thích hợp cho dây điều khiển là Servo có thể xoay đến 1 vị trí nào đó (chú ý là

Page 29: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 121

Servo thường chỉ xoay nữa vòng, điều khiển servo là điều khiển góc xoay chứ không phải điều khiển vận tốc xoay). Hình 11 là hình ảnh servo và cách điều khiển servo.

Hình 11. Servo và cách điều khiển servo.

Ví dụ: Viết chương trình điều khiển 2 servo với 2 ngắt ngoài INT0, INT1 kết hợp chế độ Fast PWM của T/C1. Khi có ngắt ngoài tại INT0 thì servo 1 đổi vị trí (độ rộng xung thay đổi từ 1.5ms đến 2ms và xoay vòng). Khi có ngắt ngoài tại INT4 thì servo 2 đổi vị trí (độ rộng xung thay đổi từ 1.5ms đến 2ms và xoay vòng). Chu kỳ xung điều khiển servo là 20ms. Xem lại công thức tính tần số PWM ở hình 10 trang 116. Chương trình tham khảo.

Bai3_2_E : Thực hành Timer 16 bit– Fast PWM 2 kênh. Sử dụng file mô phỏng BAI3_2_D.DSN.

//KHAI BÁO BIẾN CHỨA GIÁ TRỊ ĐỘ RỘNG XUNG

int duty1, duty2 ;

//PROTOTYPE HÀM THAY ĐỔI ĐỘ RỘNG XUNG

void PWM_change_duty(int duty,char channel );

void main(){

// CẤU HÌNH HƯỚNG DỮ LIỆU

DDRB=0x00;

DDD4_bit=1;

DDD5_bit=1;

PORTD2_bit=1;

PORTD3_bit=1;

PORTB2_bit=1;

Page 30: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 122

// CHỌN CHẾ ĐỘ NGẮT TÁC ĐỘNG BẰNG CẠNH XUỐNG

ISC00_bit=0;

ISC01_bit=1;

ISC10_bit=0;

ISC11_bit=1;

ISC2_bit=0;

// CHO PHÉP NGẮT NGOÀI

INT0_bit=1;

INT1_bit=1;

INT2_bit=1;

// CHO PHÉP NGẮT TOÀN CỤC

SREG_I_bit=1;

// CHỌN MODE 14 – CHẾ ĐỘ FAST PWM 2 KÊNH

WGM10_bit=0;

WGM11_bit=1;

WGM12_bit=1;

WGM13_bit=1;

// CHỌN DẠNG XUNG PWM CHẾ ĐỘ THUẬN CHO KÊNH A

COM1A0_bit=0;

COM1A1_bit=1;

// CHỌN DẠNG XUNG PWM CHẾ ĐỘ THUẬN CHO KÊNH B

COM1B0_bit=0;

COM1B1_bit=1;

// CHỌN TẦN SỐ 50Hz (20ms) CHO PWM => ICR1=4999=0X1387

ICR1H=0x13; // Ghi byte cao trước

ICR1L=0x87; // Ghi byte thấp sau

// CHỌN GIÁ TRỊ ĐẦU CHO ĐỘ RỘNG XUNG LÀ 1ms => OCR1A/B=249

OCR1AH=0; // Ghi byte cao trước

OCR1AL=249; // Ghi byte thấp sau

OCR1BH=0; // Ghi byte cao trước

OCR1BL=249; // Ghi byte thấp sau

// CHỌN NGUỒN XUNG HỆ THỐNG VỚI HỆ SỐ CHIA LÀ 64

CS10_bit=1;

CS11_bit=1;

CS12_bit=0;

while(1){

//VÒNG LẶP VÔ TẬN

}

}

void EX_INT0() org 0x02 {

Page 31: Lap Trinh Avr Voi c Www.eeelabs.org Chuong4 Bai3

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Trần Thừa – 2010 123

duty1+=125;

if(duty1>499) duty1=249;

PWM_change_duty(duty1,1);

}

void EX_INT01() org 0x04 {

duty2+=125;

if(duty2>499) duty2=249;

PWM_change_duty(duty2,2);

}

//HÀM THAY ĐỔI ĐỘ RỘNG XUNG

void PWM_change_duty(int duty,char channel ){

switch(channel){

case 1:

OCR1AH=duty>>8;

OCR1AL=duty;

break;

case 2:

OCR1BH=duty>>8;

OCR1BL=duty;

break;

}

}

Hàm thay đổi độ rộng xung PWM_change_duty được xây dựng dựa trên

cách ghi dữ liệu vào thanh ghi 16 bit đã giới thiệu ở trang 120. MikroC cung cấp cho ta 2 thư viện PWM dành cho T/C 8 bit và T/C 16

bit. Riêng với thư viện PWM 16 bit, hàm khởi tạo chế độ PWM chỉ khởi tạo được các chế độ định sẵn nên rất khó có thể thay đổi tần số PWM như ý muốn. Do đó khuyến khích các bạn nắm vững cách khởi tạo thủ công, tuy hơi phức tạp nhưng sẽ khai thác được hết các tính năng của T/C 16 bit. Các bạn có thể tham khảo 2 thư viện này trong file help của Mikroc ( ấn F1).