1. Trang chủ
  2. » Giáo Dục - Đào Tạo

BÀI 1 làm QUEN với AVR

127 443 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 127
Dung lượng 3,7 MB

Nội dung

Chỉ thị .INCLUDE báo cho trình biên dịch bắt đầu đọc 1 file đính kèm, trong trường hợp trên là file “M8DEF.INC”, đây là file chứa các khai báo cho chip Atmega8 như thanh ghi, ngắt…cho v

Trang 1

Bài 1 - Làm quen AVR

hơn hẳn, hơn cả trong tính ứng dụng (dễ sử dụng) và đặc biệt là về chức năng:

• Gần như chúng ta không cần mắc thêm bất kỳ linh kiện phụ nào khi sử dụng AVR, thậm chí không cần nguồn tạo xung clock cho chip (thường là các khối thạch anh)

• Thiết bị lập trình (mạch nạp) cho AVR rất đơn giản, có loại mạch nạp chỉ cần vài điện trở là có thể làm được một số AVR còn hỗ trợ lập trình on – chip bằng bootloader không cần mạch nạp…

• Bên cạnh lập trình bằng ASM, cấu trúc AVR được thiết kế tương thích C

• Nguồn tài nguyên về source code, tài liệu, application note…rất lớn trên

internet

• Hầu hết các chip AVR có những tính năng (features) sau:

• Có thể sử dụng xung clock lên đến 16MHz, hoặc sử dụng xung clock

nội lên đến 8 MHz (sai số 3%)

• Bộ nhớ chương trình Flash có thể lập trình lại rất nhiều lần và dung

lượng lớn, có SRAM (Ram tĩnh) lớn, và đặc biệt có bộ nhớ lưu trữ lập

trình được EEPROM

• Bộ nhớ chương trình Flash có thể lập trình lại rất nhiều lần và dung

lượng lớn, có SRAM (Ram tĩnh) lớn, và đặc biệt có bộ nhớ lưu trữ lập

trình được EEPROM

• Nhiều ngõ vào ra (I/O PORT) 2 hướng (bi-directional)

• 8 bits, 16 bits timer/counter tích hợp PWM

• Các bộ chuyển đối Analog – Digital phân giải 10 bits, nhiều kênh

• Chức năng Analog comparator

Trang 2

• Giao diện nối tiếp USART (tương thích chuẩn nối tiếp RS-232)

• Giao diện nối tiếp Two –Wire –Serial (tương thích chuẩn I2C) Master

• AT90S2323 and AT90S2343

• AT90S2333 and AT90S4433

• AT90S4414 and AT90S8515

• AT90S4434 and AT90S8535

thể lập trình cho AVR bằng các phần mềm hỗ trợ ngôn ngữ cấp cao như BascomAVR (Basic) hay CodevisionAVR (C), tuy nhiên đó không phải là mục đích của bài viết này Để hiểu thấu đáo về AVR bạn phải lập trình bằng chính ngôn ngữ của nó, ASM Như vậy lập trình bằng ASM giúp bạn hiểu tường tận về AVR, và tất nhiên để lập

Trang 3

trình được bằng ASM bạn phải hiểu về cấu trúc AVR….Một lý do khác bạn mà tôi khuyên bạn nên lập trình bằng ASM là các trình dịch (compiler) ASM cho AVR là hoàn toàn miễn phí, và nguồn source code cho AVR viết bằng ASM là rất lớn Tuy nhiên một khi bạn đã thành thạo AVR và ASM bạn có thể sử dụng các ngôn ngữ cấp cao như C để viết ứng dụng vì ưu điểm của ngôn ngữ cấp cao là giúp bạn dễ dàng thực hiện các phép toán đại số 16 hay 32 bit (vốn là vấn đề khó khăn khi lập trình bằng ASM)

II Công cụ

của bạn thành file intel hex để nạp vào chip, một số trình dịch quen thuộc có thể kể đến như sau:

• AvrStudio: là trình biên dịch ASM chính thức cung cấp bởi Atmel, đây là trình biên dịch hoàn toàn miễn phí và tất nhiên là tốt nhất cho lập trình AVR bằng ASM Phiên bản hiện tại là 4.18 SP1, bạn có thể download phần mềm

AvrStudio tại trang web chính thức của Atmel hoặc bản 4.623 tại đây

• Wavrasm: cũng được cung cấp bởi Atmel, nó chính là tiền thân của AvrStudio Hiện tại wavrasm không còn được sử dụng nhiều vì so với AvrStudio trình biên dịch này có nhiều hạng chế, nếu bạn quan tâm có thể download tại đây

• WinAVR hay avr-gcc: là bộ trình dịch được phát triển bởi gnu, ngôn ngữ sử dụng là C và có thể được dùng tích hợp với AvrStudio (dùng Avrstudio làm trình biên tập – editor) Đặc biệt bộ biên dịch này cũng miễn phí và đa số nguồn source code C được viết bằng bộ này, vì vậy nó rất lí tưởng cho bạn khi viết các ứng dụng chuyên nghiệp Việc lập trình bằng avrgcc tôi sẽ đề cập trong những phần sau

• CodeVisionAvr: một chương trình bằng ngôn ngữ C rất hay cho AVR, hỗ trợ nhiều thư viện lập trình Tuy nhiên là chương trình thương mại Bạn có thể download bản demo (đầy đủ chức năng nhưng nhưng giới hạn dung lượng bộ nhớ chương trình 2KB) tại Website hpinfotech

• ICCAVR: lập trình C cho avr, download bản demo

• BascomAVR: lập trình cho AVR bằng basic, đây là trình biên dịch khá hay và

dễ sử dụng, hỗ trợ rất nhiều thư viện Tuy nhiên rất khó debug lỗi và không thích hợp cho việc tìm hiểu AVR Vì vậy tôi không bạn khuyến khích bạn sử dụng trình dịch này Bạn có thể download bản demo (4K limit)

• Và còn rất nhiều trình biên dịch khác cho AVR mà tôi không kể ra đây, nhìn chung tất cả các trình biên dịch này hỗ trợ C hoặc Basic hoặc thậm chí Pascal Việc chọn 1 trình biên dịch tùy thuộc vào mục đích, vào mức độ ứng dụng, vào kinh nghiệm sử dụng và nhiều lý do khác nữa Ví dụ tôi thường dùng Avrstudio

và avrgcc khi học sử dụng AVR và khi viết thư viện Nhưng khi cần viết

chương trình ứng dụng tôi thường chọn avrgcc và CodeVisionAVR

Trong bài viết này tôi hướng dẫn bạn sử dụng AvrStudio để viết chương trình cho AVR bằng ASM

Trang 4

Chương trình nạp (Chip Programmer): đa số các trình biên dịch (AvrStudio, CodeVisionAVR, Bascom…) đều tích hợp sẵn 1 chương trình nạp chip hỗ trợ nhiều loại mạch nạp nên bạn không quá lo lắng Trong trường hợp khác, bạn có thể sử dụng các chương trình nạp như Icprog hay Ponyprog…là các chương trình nạp miễn phí cho AVR Việc chọn và sử dụng chương trình nạp sẽ được giới thiệu trong các bài sau

hợp sẵn trong Avrstudio, avr simulator cho phép bạn quan sát trạng thái các thanh ghi bên trong AVR nên rất phù hợp để bạn debug chương trình Proteus là chương trình thứ hai tôi muốn nói đến, Proteus không những mô phỏng hoạt động bên trong chip

mà còn mô phỏng mạch điện tử Proteus mô phỏng rất trực quan, nó là 1 công cụ hữu ích khi các bạn chưa có điều kiện làm các mạch điện tử

III Ví dụ đầu tiên của bạn

Sau khi download AvrStudio, bạn hãy cài đăt phần mềm trên máy của bạn, quá trình cài đặt rất đơn giản, bạn hãy theo các mặc định và nhấn “next” để cài đặt Trong bài đầu tiên này chúng ta sẽ viết thử 1 chương trình đơn giản cho AVR sau đó chạy

mô phỏng bằng Proteus Có thể có một số câu lệnh các bạn sẽ không hiểu, nhưng đừng

lo lắng quá, trong bài thứ 2 chúng ta sẽ học về cấu trúc AVR các bạn sẽ được giải thich rõ hơn

Để thực hiện ví dụ này, bạn hãy tạo một Project bằng AVRStudio, phần hướng dẫn chi tiết cho việc tạo Project trong AVRStudio bạn hãy tham khảo ở bài hướng dẫn AVRStudio.Đoạn code ví dụ trong bài đầu tiên này được trình bày trong List1

List 1 Đoạn code đầu tiên của bạn

Trang 6

Trước khi tìm hiểu ý nghĩa đoạn code, hãy nhìn 1 lượt qua đoạn code Trước hết việc viết HOA hay viết thường là không quan trọng, bạn có thể viết đoạn code với bất

cứ hình thức nào miễn đúng cú pháp, từ khóa là được Trong đoạn code:

• Bạn thấy 1 số từ có màu BLUE (ví dụ LDI, OUT, RJMP, RCALL, RET…)đó là các INSTRUCTiON, tức là các câu lệnh của ngôn ngữ ASM, bạn có thể đọc tài liệu “AVR INSTRUCTION” để tìm hiểu tất cả các INSTRUCTION Các

INSTRUCTION sau đó sẽ được trình dịch dịch thành các mã tương ứng

• Một số từ bắt đầu bằng bằng dấu chấm “.” là các DIRECTIVE (ví dụ

.INCLUDE hay ORG )đó cũng là những từ khóa mặc định của ASM AVR, các DIRECTIVE không phải là mã lệnh mà chỉ là các chỉ dẫn về địa chỉ bộ nhớ, khởi động bộ nhớ, định nghĩa macro…và không được trình dịch dịch thành mã Chi tiết về DIRECTIVE có thể tìm thấy trong các tài liệu về ASM AVR, dưới đây tôi tóm tắt các DIRECTIVE và chức năng của chúng như sau:

• Thông thường 1 INSTRUCTION được theo sau bởi 2 toán hạng – operand (tuy nhiên có nhiều trường hợp chỉ có 1 toán hạng hoặc không có toán hạng), khi đó

toán hạng thứ nhất sẽ là các THANH GHI của AVR (như đã đề cập, chúng ta

sẽ khảo sát thanh ghi AVR trong các bài sau), ví dụ : “LDI R16, 0xFF;” trong

đó toán hạng “R16” là tên 1 thanh ghi trong AVR, và “0xFF” là 1 hằng số dạng hexadecimal có giá trị tương ứng là 255 dạng thập phân hay 11111111 nhị phân

Các từ theo sau bởi dấu “:” là các nhãn – label (ví dụ MAIN, DELAY…), đó là

từ do chúng ta tự đặt, nó thực chất là 1 vị trí trong bộ nhớ chương trình, có thể

sử dụng nhãn như 1 chương trình con

• Phần đi sau dấu “;” gọi là giải thích – comment, phần này không được biên dịch, bạn có thể ghi comment ở bất cứ đâu trong chương trình với yêu cầu phải

sử dụng dấu “;” trước nó

Trang 7

Giải thích đoạn code:có thể chia đoạn code trên thành 4 phần: phần đầu chứa các DIRECTIVE và lệnh RJMP dùng để xác định các địa chỉ bộ nhớ chương trình, phần 2

là khởi tạo một số điều kiện đầu cho Stack Pointer và PORT, phần 3 là chương trình chính, và phần 4 là chương trình con ( chú ý đây chỉ là cách bố trí của riêng tôi, một khi đã quen thuộc, bạn có thể bố trí chương trình theo cách riêng của bạn)

Chỉ thị INCLUDE báo cho trình biên dịch bắt đầu đọc 1 file đính kèm,

trong trường hợp trên là file “M8DEF.INC”, đây là file chứa các khai báo cho chip Atmega8 như thanh ghi, ngắt…cho việc truy xuất trong chương trình của bạn, đây là dòng bắt buộc, nếu bạn lập trình cho chip khác bạn hãy đổi tên file đính kèm, ví dụ “m32def.inc” cho chip ATmega32… bạn có thể tìm thấy các file này trong thư mục “C:\Program Files\Atmel\AVR

Tools\AvrAssembler2\Appnotes”

.ORG 0x000

Chỉ thị ORG: Set Program Origin, set vị trí trong bộ nhớ sẽ được tác động

đến, trong trường hợp trên, ORG 0x000 xác định phần code theo ngay sau sẽ nằm ở địa chỉ 000, vị trí đầu tiên, trong bộ nhớ chương trình Và dòng lênh trong vị trí đầu tiên đó là:

RJMP BATDAU

RJMP: Relative Jump là lệnh nhảy không điều kiện đến 1 vị trí trong bộ

nhớ, trong trường hợp trên là nhảy đến nhãn BATDAU, và nhãn BATDAU nằm

ở vị trí 0x020 (số hexadecimal, 0x020 =32 decimal) vì nó được khai báo ngay sau DIRECTIVE ORG 0x020

.ORG 0x020

BATDAU

Như thế phần bộ nhớ chương trình nằm giữa 0 và 0x020 không được sử dụng trong đoạn code của chúng ta, phần này được sử dụng cho mục đích khác,

đó là các vectơ ngắt ( không được đề cập ở đây) Tiếp theo:

; KHOI TAO CÁC DIEU KIEN DAU

LDI R16, HIGH(RAMEND)

Trang 8

Lời khuyên: các bạn nên khởi động 1 chương trình theo cách trên và chúng

ta sẽ hiểu chúng rõ hơn sau này !

LDI R16, 0xFF

OUT DDRB, R16

Bạn chú ý 2 dòng trên và những gì tôi giải thích sau đây, 2 dòng này có tác dụng khởi động PORTB của chip ATmega8 tác dụng như các ngõ xuất tín hiệu (OUTPUT) Trước hết hãy quan sát chip ATmega8 trong hình sau

Hình 1: chip ATmega8

Bạn có thể thấy chip này gồm 28 chân, trông đó có các chân được ghi là PB0(chân 14), PB1(chân 15),…,PB7(chân 10), đó là các chân của PORTB PORT là khái niệm chỉ các ngõ xuất nhập Trong AVR, PORT có thể giao tiếp theo 2 hướng (bi – directional), có thể dùng để xuất hoặc nhận thông tin, mỗi PORT có 8 chân Chip Atmega8 có 3 PORT có tên tương ứng là PORTB,

PORTC và PORTD (một số chip AVR khác có 4 hoặc 6 PORT) PORT được coi là “cửa ngõ” then chốt của vi điều khiển

Trong AVR, mỗi PORT liên quan đến 3 thanh ghi (8 bits) có tên tương ứng

là DDRx, PINx, và PORTx với “x” là tên của PORT, mỗi bit trong thanh ghi tương ứng với mỗi chân của PORT Trong trường hợp của Atmega8 “x” là B, C hoặc D Ví dụ chúng ta quan tâm đến PORTB thì 3 thanh ghi tương ứng có tên

là DDRB, PINB và PORTB, trong đó 2 thanh ghi PORTB và PINB được nối trực tiếp với các chân của PORTB, DDRB là thanh ghi điều khiển hướng ( Input hoặc Output) Viết giá trị 1 vào một bit trong thanh ghi DDRB thì chân tương

Trang 9

ứng của PORTB sẽ là chân xuất (Output), ngược lại giá trị 0 xác lập chân tương ứng là ngõ nhập Sau khi viết giá trị điều khiển vào DDRB, việc truy xuất

PORTB được thực hiện thông qua 2 thanh ghi PINB và PORTB

Quay lại với 2 dòng code của chúng ta, dòng đầu: “LDI R16, 0xFF”, với LDI – LoaD Immediately, dòng lệnh có ý nghĩa là load giá trị 0xFF vào thanh ghi R16, R16 là tên 1 thanh ghi trong bộ nhớ của AVR, 0xFF là 1 hằng số có dạng thập lục phân, ký hiệu “0x” nói lên điều đó, bạn cũng có thể dùng ký hiệu khác

là “$” để chỉ 1 số thập lục phân, ví dụ &FF, và 0xFF=255(thập

phân)=0B11111111 (nhị phân) Như thế sau dòng đầu thanh ghi R16 có giá trị

là 11111111 (nhị phân) Dòng thứ 2: “OUT DDRB, R16” nghĩa là xuất giá trị từ thanh ghi R16 ra thanh ghi DDRB, tóm lại sau 2 dòng trên giá trị DDRB như sau:

Có thể bạn sẽ hỏi tải sao chúng không sử dụng 1 dòng duy nhất là “LDI DDRB, 0xFF” hay “OUT DDRB, 0xFF”, chúng ta không thể vì lệnh LDI chỉ cho phép thực hiện trên các thanh ghi R16,…R31 và lệnh OUT không thực hiện được với các hằng số

Và vì DDRB=11111111 nên trong trường hợp này tất cả các chân của

PORTB đã sẵn sàng cho việc xuất dữ liệu Lúc này thanh ghi PINB không có tác dụng, thanh ghi PORTB sẽ là thanh ghi xuất, ghi giá trị vào thanh ghi này sẽ tác động đến các chân của PORTB.1

“OUT PORTB, R16” để xuất giá trị 0B00000001 có sẵn trong R16 ra thanh ghi PORTB, lúc này chân PB0 của chip sẽ lên 1 (5V) và các chân còn lại sẽ ở mức

0 (0V) Dòng thứ 3: “RCALL DELAY” là lệnh gọi chương trình con DELAY, tạm hoãn trước khi thực hiện các dòng lệnh tiếp theo:

LDI R16, 0B00000010

OUT PORTB, R16

RCALL DELAY

Trang 10

Ba dòng lệnh này cũng giống ba dòng trên, nhưng giá trị xuất ra lúc này là 0B00000010, chân PB1 sẽ lên 5V và các chân khác xuống mức 0V Và cứ như thế đến đoạn cuối:

• Bây giờ chắc bạn đã đoán được chương trình của chúng ta thực hiện việc gì, đó

là quét xoay vòng các chân của PORTB, nếu chúng ta kết nối các chân của PORTB với các LED, chúng ta sẽ có 1 hiệu ứng quét LED xoay vòng, chúng ta thực hiện điều này bằng phần mềm Proteus

• Phần 4: chương trinh con DELAY: đoạn chương trình này không làm gì cả ngoài việc trì hoãn 1 khoảng thời gian, tuy nhiên bạn chưa thể hiểu nó ngay được

Đây chỉ là 1 ví dụ đơn giản, tôi cố gắng thực hiện nó theo cách dễ hiểu nhất cho bạn, vì thế đoạn code có vẻ hơi dài dòng, bạn hãy thực hiện lại đoạn chương trình chính bằng đoạn code của bạn

Phần cuối cùng là biên dịch đoạn code thành file intel hex để đổ vào chip,

Sau khi biên dịch bạn sẽ có 1 file tên “avr1.hex” trong thưc mục project, chúng

ta sẽ dùng file này đổ vào chip sau này

IV Mô phỏng bằng Proteus

Chúng ta hãy thử nghiệm đoạn chương trình của chúng ta bằng Proteus Nếu bạn thực hiện đúng kết quả sẽ như minh họa trong hình 2 Hướng dẫn cụ thể cách vẽ mạch điện và mô phỏng bằng phần mềm Proteus bạn hãy xem bài "Mô phỏng Proteus"

Trang 11

Hình 2 Mô phỏng

Trang 12

Bài 2 - Cấu Trúc AVR

• Hiểu được cấu trúc AVR, cấu trúc bộ nhớ và cách thức hoạt động của chip

• Hiểu về Stack và cách hoạt động

• Biết được một số instruction cơ bản truy xuất bộ nhớ

• Học các instruction rẽ nhánh và vòng lặp

• Chương trình con (Subroutine) và Macro

• Cải tiến ví dụ trong bài 1

• Viết 1 ví dụ minh họa cách sử dụng bộ nhớ và vòng lặp

II Tổ chức của AVR

AVR có cấu trúc Harvard, trong đó đường truyền cho bộ nhớ dữ liệu (data memory bus) và đường truyền cho bộ nhớ chương trình (program memory bus) được tách riêng Data memory bus chỉ có 8 bit và được kết nối với hầu hết các thiết bị ngoại vi, với register file Trong khi đó program memory bus có độ rộng 16 bits và chỉ phục vụ cho instruction registers Hình 1 mô tả cấu trúc bộ nhớ của AVR

các chip AVR cũ (như AT90S1200 hay AT90S2313…) bộ nhớ chương trình chỉ gồm

1 phần là Application Flash Section nhưng trong các chip AVR mới chúng ta có thêm phần Boot Flash setion Boot section sẽ được khảo sát trong các phần sau, trong bài này khi nói về bộ nhớ chương trình, chúng ta tự hiểu là Application section Thực chất, application section bao gồm 2 phần: phần chứa các instruction (mã lệnh cho hoạt động của chip) và phần chứa các vector ngắt (interrupt vectors) Các vector ngắt nằm

ở phần đầu của application section (từ địa chỉ 0x0000) và dài đến bao nhiêu tùy thuộc

Trang 13

vào loại chip Phần chứa instruction nằm liền sau đó, chương trình viết cho chip phải

được load vào phần này Xem lại phần đầu của ví dụ trong bài 1:

.ORG 0x000

RJMP BATDAU

.ORG 0x020

Trong ví dụ này, ngay sau khi set vị trí 0x000 bằng chỉ thị (DIRECTIVE) ORG

0x000 chúng ta dùng instruction RJMP để nhảy đến vị trí 0x020, như thế phần bộ nhớ

chương trình từ 0x00 đến 0x01F không được sử dụng (vì trong ví dụ này chúng ta

không sử dụng các vector ngắt) Chương trình chính được bắt đầu từ địa chỉ 0x020,

con số 0x020 là do người lập trình chọn, thật ra các vector ngắt của chip ATMEGA8

chỉ kéo dài đến địa chỉ 0x012, vì vậy chương trình chính có thể được bắt đầu từ bất cứ

vị trí nào sau đó Để biết độ dài các vector ngắt của từng chip bạn hãy tham khảo

datasheet của chip đó

Vì chức năng chính của bộ nhớ chương trình là chứa instruction, chúng ta không

có nhiều cơ hội tác động lên bộ nhớ này khi lập trình cho chip, vì thế đối với người lập

trình AVR, bộ nhớ này “không quá quan trọng” Tất cả các thanh ghi quan trọng cần

khảo sát nằm trong bộ nhớ dữ liệu của chip

Hình 1 Tổ chức bộ nhớ của AVR

của chip, việc lập trình cho chip phần lớn là truy cập bộ nhớ này Bộ nhớ dữ liệu trên

các chip AVR có độ lớn khác nhau tùy theo mỗi chip, tuy nhiên về cơ bản phần bộ

nhớ này được chia thành 5 phần:

Phần 1: là phần đầu tiên trong bộ nhớ dữ liệu, như mô tả trong hình 1, phần này

bao gồm 32 thanh ghi có tên gọi là register file (RF), hay General Purpose Rgegister –

GPR, hoặc đơn giản là các Thanh ghi Tất cả các thanh ghi này đều là các thanh ghi 8

bits như trong hình 2

Trang 14

Hình 2 Thanh ghi 8 bits

Tất cả các chip trong họ AVR đều bao gồm 32 thanh ghi Register File có địa chỉ tuyệt đối từ 0x0000 đến 0x001F Mỗi thanh ghi có thể chứa giá trị dương từ 0 đến 255 hoặc các giá trị có dấu từ -128 đến 127 hoặc mã ASCII của một ký tự nào đó…Các thanh ghi này được đặt tên theo thứ tự là R0 đến R31 Chúng được chia thành 2 phần, phần 1 bao gồm các thanh ghi từ R0 đến R15 và phần 2 là các thanh ghi R16 đến R31 Các thanh ghi này có các đặc điểm sau:

• Được truy cập trực tiếp trong các instruction

• Các toán tử, phép toán thực hiện trên các thanh ghi này chỉ cần 1 chu kỳ xung clock

• Register File được kết nối trực tiếp với bộ xử lí trung tâm – CPU của chip

• Chúng là nguồn chứa các số hạng trong các phép toán và cũng là đích chứa kết quả trả lại của phép toán

Để minh họa, hãy xét ví dụ thực hiện phép cộng 2 thanh ghi bằng instruction ADD như sau:

ADD R1, R2

Bạn thấy trong dòng lệnh trên, 2 thanh ghi R1 và R2 được sử dụng trực tiếp với tên của chúng, dòng lệnh trên khi được dịch sang opcode để download vào chip sẽ có dạng: 0000110000010010 trong đó 00001=1 tức thanh ghi R1 và 00010 = 2 chỉ thanh ghi R2 Sau phép cộng, kết quả sẽ được lưu vào thanh ghi R1

Tất cả các instruction sử dụng RF làm toán hạng đều có thể truy nhập tất cả các RF một cách trực tiếp trong 1 chu kỳ xung clock, ngoại trừ SBCI, SUBI, CPI, ANDI và LDI, các instruction này chỉ có thể truy nhập các thanh ghi từ R16 đến R31

Thanh ghi R0 là thanh ghi duy nhất được sử dụng trong instruction LPM (Load Program Memory) Các thanh ghi R26, R27, R28, R29, R30 và R31 ngoài chức năng thông thường còn được sử dụng như các con trỏ (Pointer register) trong một số

instruction truy xuất gián tiếp Chúng ta sẽ khảo sát vấn đề con trỏ sau này Hình 3 mô

tả các chức năng phụ của các thanh ghi

Trang 15

Hình 3 Register file

Tóm lại 32 RF của AVR được xem là 1 phần của CPU, vì thế chúng được CPU sử dụng trực tiếp và nhanh chóng, để gọi các thanh ghi này, chúng ta không cần gọi địa chỉ mà chỉ cần gọi trực tiếp tên của chúng RF thường được sử dụng như các toán hạng (operand) của các phép toán trong lúc lập trình

Phần 2: là phần nằm ngay sau register file, phần này bao gồm 64 thanh ghi được

gọi là 64 thanh ghi nhập/xuất (64 I/O register) hay còn gọi là vùng nhớ I/O (I/O

Memory) Vùng nhớ I/O là cửa ngõ giao tiếp giữa CPU và thiết bị ngoại vi Tất cả các thanh ghi điều khiển, trạng thái…của thiết bị ngoại vi đều nằm ở đây Xem lại ví dụ trong bài 1, trong đó tôi có đề cập về việc điều khiển các PORT của AVR, mỗi PORT liên quan đến 3 thanh ghi DDRx, PORTx và PINx, tất cả 3 thanh ghi này đều nằm trong vùng nhớ I/O Xa hơn, nếu muốn truy xuất các thiết bị ngoại vi khác như Timer, chuyển đổi Analog/Digital, giao tiếp USART…đều thực hiện thông qua việc điều khiển các thanh ghi trong vùng nhớ này

Vùng nhớ I/O có thể được truy cập như SRAM hay như các thanh ghi I/O Nếu sử dụng instruction truy xuất SRAM để truy xuất vùng nhớ này thì địa chỉ của chúng được tính từ 0x0020 đến 0x005F Nhưng nếu truy xuất như các thanh ghi I/O thì địa chỉ của chúng đựơc tính từ 0x0000 đến 0x003F

Xét ví dụ instruction OUT dùng xuất giá trị ra các thanh ghi I/O, lệnh này sử dụng địa chỉ kiểu thanh ghi, cấu trúc của lệnh như sau: OUT A, Rr, trong đó A là địa chỉ của thanh ghi trong vùng nhớ I/O, Rr là thanh ghi RF, lệnh OUT xuất giá trị từ thanh ghi

Trang 16

thanh ghi điều khiển hướng của PORTD, tức thanh ghi DDRD, địa chỉ tính theo vùng I/O của thanh ghi DDRD là 0x0011, như thế câu lệnh của chúng ta sẽ có dạng: OUT 0x0011, R6 Tuy nhiên trong 1 trường hợp khác, nếu muốn truy xuất DDRD theo dạng SRAM, ví dụ lệnh STS hay LDS, thì phải dùng địa chỉ tuyệt đối của thanh ghi này, tức giá trị 0x0031, khi đó lệnh OUT ở trên được viết lại là STS 0x0031, R6

Để thống nhất cách sử dụng từ ngữ, từ bây giờ chúng ta dùng khái niệm “địa chỉ I/O” cho các thanh ghi trong vùng nhớ I/O để nói đến địa chỉ không tính phần

Register File, khái niệm “địa chỉ bộ nhớ” của thanh ghi là chỉ địa chỉ tuyệt đối của chúng trong SRAM Ví dụ thanh ghi DDRD có “địa chỉ I/O” là 0x0011 và “địa chỉ bộ nhớ” của nó là 0x0031, “địa chỉ bộ nhớ” = “địa chỉ I/O” + 0x0020

Vì các thanh ghi trong vùng I/O không được hiểu theo tên gọi như các Register file, khi lập trình cho các thanh ghi này, người lập trình cần nhớ địa chỉ của từng thanh ghi, đây là việc tương đối khó khăn Tuy nhiên, trong hầu hết các phần mềm lập trình cho AVR, địa chỉ của tất cả các thanh ghi trong vùng I/O đều được định nghĩa trước trong

1 file Definition, bạn chỉ cần đính kèm file này vào chương trình của bạn là có thể truy xuất các thanh ghi với tên gọi của chúng Giả sử trong ví dụ ở bài 1, để lập trình cho chip Atmega8 bằng AVRStudio, dòng thứ 2 chúng ta sử dụng INCLUDE

"M8DEF.INC" để load file định nghĩa cho chip ATMega8, file M8DEF.INC Vì vậy, trong sau này khi muốn sử dụng thanh ghi DDRD bạn chỉ cần gọi tên của chúng, như:

OUT DDRD,R6

Phần 3: RAM tĩnh, nội (internal SRAM), là vùng không gian cho chứa các biến

(tạm thời hoặc toàn cục) trong lúc thực thi chương trình, vùng này tương tự các thanh RAM trong máy tính nhưng có dung lượng khá nhỏ (khoảng vài KB, tùy thuộc vào loại chip)

Phần 4: RAM ngoại (external SRAM), các chip AVR cho phép người sử dụng gắn

thêm các bộ nhớ ngoài để chứa biến, vùng này thực chất chỉ tồn tại khi nào người sử dụng gắn thêm bộ nhớ ngoài vào chip

Phần 5: EEPROM (Electrically Ereasable Programmable ROM) là một phần quan

trọng của các chip AVR mới, vì là ROM nên bộ nhớ này không bị xóa ngay cả khi không cung cấp nguồn nuôi cho chip, rất thích hợp cho các ứng dụng lưu trữ dữ liệu Như trong hình 1, phần bộ nhớ EEPROM được tách riêng và có địa chỉ tính từ

0x0000

Câu hỏi bây giờ là AVR hoạt động như thế nào?

Hình 4 biểu diễn cấu trong bên trong của 1 AVR Bạn thấy rằng 32 thanh ghi trong Register File được kết nối trực tiếp với Arithmetic Logic Unit -ALU (ALU cũng được xem là CPU của AVR) bằng 2 line, vì thế ALU có thể truy xuất trực tiếp cùng lúc 2 thanh ghi RF chỉ trong 1 chu kỳ xung clock (vùng được khoanh tròn màu đỏ trong hình 4)

Trang 17

Hình 4 Cấu trúc bên trong AVR

Các instruction được chứa trong bộ nhớ chương trình Flash memory dưới dạng các thanh ghi 16 bit Bộ nhớ chương trình được truy cập trong mỗi chu kỳ xung clock và

1 instruction chứa trong program memory sẽ được load vào trong instruction register, instruction register tác động và lựa chọn register file cũng như RAM cho ALU thực thi Trong lúc thực thi chương trình, địa chỉ của dòng lệnh đang thực thi được quyết định bởi một bộ đếm chương trình – PC (Program counter) Đó chính là cách thức hoạt động của AVR

AVR có ưu điểm là hầu hết các instruction đều được thực thi trong 1 chu kỳ xung clock, vì vậy có thể nguồn clock lớn nhất cho AVR có thể nhỏ hơn 1 số vi điều khiển khác như PIC nhưng thời gian thực thi vẫn nhanh hơn

III Stack

Stack được hiểu như là 1 “tháp” dữ liệu, dữ liệu được chứa vào stack ở đỉnh “tháp”

và dữ liệu cũng được lấy ra từ đỉnh Kiểu truy cập dữ liệu của stack gọi là LIFO (Last

In First Out – vào sau ra trước) Hình 5 thể hiện cách truy cập dữ liệu của stack

Trang 18

Hình 5 Stack

Khái niệm và cách thức hoạt động của stack có thể được áp dụng cho AVR, bằng cách khai báo một vùng nhớ trong SRAM là stack ta có thể sử dụng vùng nhớ này như một stack thực thụ

Để khai báo một vùng SRAM làm stack chúng ta cần xác lập địa chỉ đầu của stack bằng cách xác lập con trỏ stack-SP (Stack Pointer) SP là 1 con trỏ 16 bit bao gồm 2 thanh ghi 8 bit SPL và SPH (chữ L là LOW chỉ thanh ghi mang giá trị byte thấp của

SP, và H = HIGH), SPL và SPH nằm trong vùng nhớ I/O Giá trị gán cho thanh ghi SP

sẽ là địa chỉ khởi động của stack Quay lại ví dụ ở bài 1, phần khởi tạo các điều kiện đầu

; KHOI TAO CÁC DIEU KIEN DAU

Có thể giải thích như sau: stack trong AVR hoạt động từ trên xuống, sau khi dữ liệu được đẩy vào stack, SP sẽ giảm giá trị vì thế khởi động SP ở vị trí cuối cùng của

SRAM sẽ tránh được việc mất dữ liệu do ghi đè Bạn có thể khởi động stack với 1 địa chỉ khác, tuy nhiên vì lý do an toàn, nên khởi động stack ở RAMEND

Hai instruction dùng cho truy cập stack là PUSH và POP, trong đó PUSH dùng đẩy dữ liệu vào stack và POP dùng lấy dữ liệu ra khỏi stack Dữ liệu được đẩy vào và lấy ra khỏi stack tại vị trí mà con trỏ SP trỏ đến Ví dụ cho chip ATMega8,

RAMEND=0x045F, sau khi khởi động, con trỏ SP trỏ đến vị trí 0x045F trong SRAM, nếu ta viết các câu lệnh sau:

Trang 19

Khi đó nội dung của stack sẽ như trong hình 6

Hình 6 Nội dung stack trong ví dụ

Sau mỗi lần PUSH dữ liệu, SP sẽ giảm 1 đơn vị và trỏ vào vị trí tiếp theo

Bây giờ nếu ta dùng POP để lấy dữ liệu từ stack, POP R2, thì R2 sẽ mang giá trị của ngăn nhớ 0x045D, tức R2=8 Trước khi instruction POP được thực hiện, con trỏ SP được tăng lên 1 đơn vị, sau đó dữ liệu sẽ được lấy ra từ vị trí mà SP trỏ đến trong stack

Stack trong AVR không phải là “vô đáy”, nghĩa là chúng ta chỉ có thể PUSH dữ liệu vào stack ở 1 độ sâu nhất định nào đấy (phụ thuộc vào chip) Sử dụng stack không đúng cách đôi khi sẽ làm chương trình thực thi sai hoặc tốn thời gian thực thi vô ích

Vì thế không nên sử dụng stack chỉ để lưu các biến thông thường Ứng dụng phổ biến nhất của stack là sử dụng trong các chương trình con (Subroutine), khi chúng ta cần

“nhảy” từ một vị trí trong chương trình chính đến 1 chương trình con, sau khi thực hiện chương trình con lại muốn quay về vị trí ban đầu trong chương trình chính thì Stack là phương cách tối ưu dùng để chứa bộ đếm chương trình trong trường hợp này Xem lại ví dụ trong bài 1, trong chương trình chính chúng ta dùng lệnh RCALL

DELAY để nhảy đến đoạn chương trình con DELAY, RCALL là lệnh nhảy đến 1 vị trí trong bộ nhớ chương trình, trước khi nhảy, PC được cộng thêm 1 và PUSH một cách tự động vào stack Cuối chương trình con DELAY, chúng ta dùng instruction RET, instruction này POP dữ liệu từ stack ra PC một cách tự động, bằng cách này chúng ta có thể quay lại vị trí trước đó Chính vì các lệnh RCALL và RET sử dụng stack một cách tự động nên ta phải khởi động stack ngay từ đầu, nếu không chương trình sẽ thực thi sai chức năng

Tóm lại cần khởi động stack ở đầu chương trình và không nên sử dụng stack một cách tùy thích nếu chưa thật cần thiết

Trang 20

IV Thanh ghi trạng thái - SREG (STATUS REGISTRY)

Nằm trong vùng nhớ I/O, thanh ghi SREG có địa chỉ I/O là 0x003F và địa chỉ bộ nhớ là 0x005F (thường đây là vị trí cuối cùng của vùng nhớ I/O) là một trong số các thanh ghi quan trọng nhất của AVR, vì thế mà tôi dành phần này để giới thiệu về thanh ghi này Thanh ghi SREG chứa 8 bit cờ (flag) chỉ trạng thái của bộ xử lí, tất cả các bit này đều bị xóa sau khi reset, các bit này cũng có thể được đọc và ghi bởi

chương trình Chức năng của từng bit được mô tả như sau:

Hình 7 Thanh ghi trạng thái

dụ thanh ghi R1 chứa giá trị 200, R2 chứa 70, chúng ta thực hiện phép cộng có nhớ: ADC R1, R2, sau phép cộng, kết quả sẽ được lưu lại trong thanh ghi R1, trong khi kết quả thực là 270 mà thanh ghi R1 lại chỉ có khả năng chứa tối đa giá trị 255 (vì có 8 bit) nên trong trường hợp này, giá trị lưu lại trong R1 thực chất chỉ là 14, đồng thời cờ C được set lên 1 (vì 270=100001110, trong đó 8 bit sau 00001110 =14 sẽ được lưu lại trong R1)

phép Logic bằng 0

số hay phép Logic là số âm

của cờ này có vẻ sẽ khó hiểu cho bạn vì nó liên quan đến kiến thức số nhị phân (phần bù), chúng ta sẽ đề cập đến khi nào thấy cần thiết

S=N xor V

toán đại số và phép Logic, cờ này hiệu quả đối với các phép toán với số BCD

LoaD) và BST (Bit STorage) Tôi sẽ giải thích chức năng Bit T trong phần giới thiệu về BLD và BST

được set lên 1 nếu trong chương trình có sử dụng ngắt Sau khi set bit này, bạn muốn kích hoạt loại ngắt nào cần set các bit ngắt riêng của ngắt đó Hai

instruction dùng riêng để Set và Clear bit I là SEI và CLI

Chú ý: tất cả các bit trong thanh ghi SREG đều có thể được xóa thông qua các instruction không toán hạng CLx và set bởi SEx, trong đó x là tên của Bit.Ví dụ CLT

là xóa Bit T và SEI là set bit I

Trang 21

Tôi chỉ giải thích ngắn gọn chức năng của các bit trong thanh ghi SREG, cụ thể chức

năng và cách sử dụng của từng bit chúng ta sẽ tìm hiểu trong các trường hợp cụ thể

sau này, người đọc có thể tự tìm hiểu thêm trong các tài liệu về INSTRUCTION cho

AVR

Tôi cung cấp thêm 1 bảng tóm tắt sự ảnh hưởng của các phép toán đại số, logic lên

các Bit trong thanh ghi SREG

Hình 8 Ảnh hưởng của các phép toán lên SREG

IV Macro và chương trình con

nếu có 1 đoạn code nào đó mà bạn rất hay sử dụng khi lập trình thì bạn nên dùng

macro để tránh việc phải viết đi viết lại đoạn code đó Lập trình ASM cho AVR cho

phép bạn sử dụng Macro, để tạo 1 Macro bạn sử dụng DIRECTIVE

Đoạn Macro trên có tên delay4 thực hiện việc delay 4 chu kỳ máy bằng 4 lệnh

NOP, nếu trong chương trình bạn cần dùng Macro này thì chỉ cần gọi delay4 ở bất kỳ

Trang 22

Subroutine (chương trình con)

Tuy nhiên khác với Macro, mỗi khi gọi chương trình con, con trỏ chương trình nhảy đến chương trình con đề thực thi chương trình con và sau đó quay về chương trình chính Như thế chương trình con chỉ được biên dịch 1 lần và có thể sử dụng nhiều lần,

nó làm giảm dung lượng chưong trình Đây là ưu điểm và cũng là điểm khác biệt lớn nhất giữa chương trình con và Macro Tuy nhiên cần chú ý là việc nhảy đến chương trình con và nhảy về chương trình chính cần vài chu kỳ máy, có thể làm chậm chương trình, đây là nhược điểm của chương trình con so với macro

Chương trình con cho AVR luôn được bắt đầu bằng 1 Label, đó cũng là tên và địa chỉ của chương trình con Chương trình con thường được kết thúc với câu lệnh RET (Return) Chúng ta đã biết về chương trình con qua ví dụ của bài 1, trong đó DELAY

là 1 chương trình con

Để gọi chương trình con từ 1 vị trí nào đó trong chương trình, chúng ta có thể dùng lệnh CALL hoặc RCALL (Relative CALL) (xem lại ví dụ bài 1 về cách sử dụng RCALL) Mỗi khi các lệnh này được gọi, bộ đếm chương trình được tự động được PUSH vào stack và khi chương trình con kết thúc bằng lệnh RET, bộ đếm chương trình được POP trở ra và quay về chương trình chính Lệnh CALL có thể gọi 1 chương trình con ở bất kỳ vị trí nào trong khi RCALL chỉ gọi trong khoảng bộ nhớ 4KB,

nhưng RCALL cần ít chu kỳ xung clock hơn khi thực thi

Hai instruction khác có thể được dùng để gọi chương trình con đó là JMP (Jump)

và RJMP (Relative Jump) Khác với các lệnh call, các lệnh jump không cho phép quay lại vì không tự động PUSH bộ đếm chương trình vào Stack, để sử dụng các lệnh này gọi chương trình con bạn cần một số lệnh jump khác ở cuối chương trình con

Tóm lại bạn nên viết 1 chương trình con đúng chuẩn và dùng CALL hoặc RCALL

để gọi chương các chương trình này, chỉ những trường hợp đặc biệt hoặc bạn hiểu rất

rõ về chúng thì có thể dùng các lệnh jump

V Ví dụ minh họa

Nếu bạn đã đọc và hiểu đến thời điểm này thì bạn đã có thể hiểu hết hoạt động của chương trình ví dụ trong bài 1, thật sự ví dụ đó rất đơn giản và dễ hiểu Tuy nhiên, bạn có thề tối ưu hóa ví dụ đó theo hướng làm giảm dung lượng chương trình và tất nhiên, chương trình sẽ khó hiểu hơn cho người khác Các phần khởi động vị trí bộ nhớ, stack và chương trình con DELAY chúng ta không thay đổi, chỉ thay đổi phần chương trình chính, 1 trong những cách viết chương trình chính như cách sau:

; CHUONG TRINH CHINH , BAI 1, VI DU 1, VERSION 2/////////////////////////////// LDI R16, $1 ;LOAD GIA TRI KHOI DONG CHO R16

Trang 23

MAIN:

OUT PORTB, R16 ; XUAT GIA TRI TRONG R16 RA PORTB

RCALL DELAY ; GOI CHUONG TRINH CON DELAY

ROL R16 ; XOAY THANH GHI R16 SANG TRAI 1 VI TRI

RJMP MAIN ; NEU R16 ≠0, NHAY VE MAIN, TIEP TUC QUET

;/////////////////////////////////////////////////////////////////////////////////////////

Có thể không cần giải thích bạn cũng đã có thể hiểu đoạn code trên, đây chỉ là 1 trong những cách có thể, bạn hãy viết lại theo cách của riêng bạn với yêu cầu là

chương trình phải thực hiện đúng chức năng và ngắn gọn

Bây giờ chúng ta sẽ thực hiện một ví dụ minh họa cho những gì chúng ta đã học trong bài 2 này Nội dung của ví dụ thể hiện trong mạch điện hình 9 Hoạt động của mạch điện tử như sau: 1 chip ATMega8 được sử dụng như một counter, có thể dùng

để đếm lên và đếm xuống, 2 button trong mạch điện tác động như 2 “kicker”, nhấn button 1 để đếm lên và button để đếm xuống, giá trị đếm nằm trong khoảng từ 0 đến 9 Giá trị đếm được hiển thị trên 1 LED 7 đoạn loại anod chung (dương chung), chip

7447 được dùng để giải mã từ giá trị BCD xuất ra bởi ATMega8 sang tín hiệu cho LED 7 đoạn anod chung, chúng ta cần sử dụng 7447 vì tín hiệu xuất ra từ chip

ATMega8 là dạng nhị phân hoặc BCD , tín hiệu này không thể hiển thị trực tiếp trên các LED 7 đoạn, chip 7447 có nhiệm vụ chuyển 1 dữ liệu dạng digit BCD sang mã phù hợp cho LED 7 đoạn

Để thực hiện ví dụ, trước hết bạn hãy vẽ mạch điện như trong hình 9 bằng phần mềm Proteus (xem cách vẽ mạch điện bằng Proteus), mạch điện chỉ có 5 loại linh kiện

là chip ATMega8 (từ khóa mega8), 1 LED 7 đoạn anod chung với tên đầy đủ trong Proteus là 7SEG-COM-AN-GRN (từ khóa 7SEG), 1 chip 7447 (từ khóa 7447), 1 điện trở 10 Ω và 2 button (từ khóa button)

Trang 24

OUT DDRB, R16 ; DDRB=0, PORTB LA NGO NHAP

LDI R16, 0xFF ; SET TAT CA CAC BIT CUA R16 LEN 1

OUT PORTB,R16 ;DDRB=0, PORTB =0xFF, KEO LEN CAC CHAN PORTB OUT DDRD, R16 ;DDRD=0xFF, PORTD LA NGO XUAT

CLR R25 ;XOA R25, R25 LA THANH GHI DUNG CHUA SO DEM

SER R20 ; R20 LA THANH GHI TAM CHUA GIA TRI TRUOC DO CUA PINB MAIN:

IN R21,PINB ;DOC GIA TRI TU PINB, TUC TU CAC BUTTON

RCALL SOSANH ;GOI CHUONG TRINH CON SOSANH

OUT PORTD, R25 ;XUAT GIA TRI DEM RA PORTD

SBRS R21,0 ;NEU BIT 0 CUA R21 (TUC CHAN PB0) =1 THI BO QUA DONG ; TIEP THEO

RCALL TANG ;NHAY DEN CHUONG TRINH CON TANG GIA TRI DEM SBRS R21,1 ;NEU BIT 1 CUA R21 (TUC CHAN PB1) =1 THI BO QUA DONG ; TIEP THEO

RCALL GIAM ;NHAY DEN CHUONG TRINH CON GIAM GIA TRI DEM MOV R20,R21 ;LUU LAI TRANG THAI PINB

RJMP MAIN

;**********************CHUONG TRINH CON************************

; **************subroutine kiem tra gioi hang (tu 0 den 9) cua so dem

SOSANH:

CPI R25, 10

BREQ RESET0 ;NEU GIA TRI DEM=10 THI TRA VE 0

CPI R25, 255

BREQ RESET9 ;NEU GIA TRI DEM =255 THI TRA VE 9

RJMP QUAYVE ;NHAY DEN NHAN QUAYVE

Trang 25

Hoạt động của cac PORT và việc xác lập 1 PORT như các ngõ xuất chúng ta đã khảo sát trong bài 1 Ở đây chúng ta khảo sát thêm về xác lập PORT như 1 ngõ nhập, trước hết bạn hãy quan sát mạch điện tương đương của 1 chân trong các PORT xuất nhập của AVR trong hình 10

Hình 10 Cấu trúc chân trong PORT của AVR

Trong mạch điện hình 10, các diode và tụ điện chỉ có chức năng bảo vệ chân

PORT, nhưng điện trở Rpu (R Pull up) đóng vai trò quan trọng như là điện trở kéo lên khi chân của PORT làm nhiệm vụ nhận tín hiệu (ngõ nhập) Tuy nhiên trong AVR, điện trở kéo lên này không phải luôn kích hoạt, chúng ta biết rằng mỗi PORT của AVR có 3 thanh ghi: DDRx, PORTx và PINx, nếu DDRx=0 thì PORT x là ngõ nhập, lúc này thanh ghi PINx là thanh ghi chứa dữ liệu nhận về, đặc biệt thanh ghi PORTx vẫn được sử dụng trong mode này, đó là thanh ghi xác lập điện trở kéo lên, như thế nếu DDRx=0 và PORTx=0xFF thì các chân PORTx là ngõ nhập và được kéo lên bởi 1 điện trở trong chip, nghĩa là các chân của PORTx luôn ở mức cao, muốn kích để thay đồi trạng thái chân này chúng ta cần nối chân đó trực tiếp với GND, đấy là lý do tại

Trang 26

sao các button trong mạch điện của chúng ta có 1 đầu nối với chân của chip còn đầu kia được nối với GND Đây cũng là ý nghĩa của khái niệm điện trở kéo lên (Pull up resistor) trong kỹ thuật điện tử Đoạn code trong phần “KHOI DONG CAC PORT” của ví dụ này xác lập PORTD là ngõ xuất (DDRD=0xFF) , PORTB là ngõ nhập có sử dụng điện trở kéo lên (DDRB=0, PORTB=0xFF)

Chúng ta sẽ giải thích hoạt động của đoạn chương trình chính và các đoạn chương trình con Trước hết, trong chương trình này, chúng ta sử dụng 3 thanh ghi chính là R20, R21 và R25, trong đó R25 là thanh ghi chứa số đếm, giá trị của thanh ghi R25 sẽ được xuất ra PORTD của chip, thanh ghi R21 chứa trạng thái của thanh ghi PINB và cũng là trạng thái của các button, thanh ghi R20 kết hợp với thanh ghi R21 tạo thành 1

“bộ đếm cạnh xuống” của các button Để hiểu thấu đáo hoạt động đếm (cũng là hoạt động chính của ví dụ này) chúng ta xét trạng thái chân PB0 như trong hình 11

Hình 11 Thay đổi trạng thái ở các chân I/O

Trong trạng thái bình thường (button không được nhấn), chân PB0 ở mức cao (do điện trở kéo lên), bộ đếm không hoạt động, giá trị đếm không thay đổi, bây giờ nếu nhấn button, chân PB0 được nối trực tiếp với GND, chân này sẽ bị kéo xuống mức thấp, bằng cách kiểm tra trạng thái chân PB0, nếu PB0=0 ta tăng giá trị đếm 1 đơn vị

Ý tưởng như thế có vẻ hợp lý, tuy nhiên nếu áp dụng thì chương trình sẽ hoạt động không đúng chức năng, khi bạn nhấn 1 lần giá, trị đếm có thể tăng đến cả trăm hoặc không kiểm soát được, hiệu ứng này tương tự khi bạn nhấn và giữ 1 phím trên bàn phím máy tính, lý do là vì chúng ta sử dụng phương pháp kiểm tra mức để đếm, thời gian quét của chương trình rất ngắn so với thời gian chúng ta giữ button Để khắc phục, chúng ta dùng phương pháp kiểm tra cạnh xuống, chỉ khi nào phát hiện chân PB0 thay đổi từ 1 xuống 0 thì mới tăng giá trị đếm 1 đơn vị, kết quả là mỗi lần nhấn button thì giá trị đếm chỉ tăng 1 (ngay cả khi ta nhấn và giữ button), thanh ghi R20 được sử dụng để lưu trạng thái trước đó của PINB (cũng là trạng thái của các button) Trong chương trình, tôi sử dụng 2 istruction mới là SBRC và SBRS để kiểm tra trạng thái các chân của PORTB (button) SBRC – Skip if Bit in Register is Clear, lệnh này sẽ bỏ qua 1 dòng lệnh ngay sau đó (chỉ bỏ qua 1 dòng duy nhất) nếu 1 bit trong thanh ghi ở mức 0, SBRC – Skip if Bit in Register is Set- hoạt động tương tự SBRC nhưng skip sẽ xảy ra nếu bit trong thanh ghi ở mức 1 Dựa vào đây chúng ta giải thích

4 dòng sau:

SBRS R21,0 ;NEU BIT 0 CUA R21 (TUC CHAN PB0) =1 THI BO QUA DONG

;TIEP THEO

RCALL TANG ;NHAY DEN CHUONG TRINH CON TANG GIA TRI DEM

SBRS R21,1 ;NEU BIT 1 CUA R21 (TUC CHAN PB1) =1 THI BO QUA DONG

;TIEP THEO

RCALL GIAM ;NHAY DEN CHUONG TRINH CON GIAM GIA TRI DEM

Dòng 1 dùng kiểm tra trạng thái bit 0 trong R21 (chú ý R21 chứa giá trị của

PINB), nếu bit này bằng 1 (set), tức chân PB0=1 hay button không được nhấn, thì

Trang 27

nhảy bỏ qua dòng lệnh tiếp theo để đến dòng 3 Ở dòng 3 chương trình kiểm tra trạng thái chân PB1 (button thứ 2) Quay lại dòng 1, nếu chương trình kiểm tra phát hiện chân PB0=0 (button thứ nhất được nhấn) thì dòng lệnh thứ 2 được thực thi, kết quả là chương trình nhảy đến chương trình con TANG

từ 1->0 ở chân này, giá trị đếm sẽ được tăng thêm 1 nhờ INC R25, sau đó quay về chương trình chính

Tóm lại muốn tăng giá trị đếm thêm 1 đơn vị cần thỏa mãn 2 điều kiện: chân PB0 hiện tại =0 (button đang được nhấn) và trạng thái trước đó của PB0 phải là 1 (tránh trường hợp tăng liên tục) Phương pháp này có thể áp dụng cho rất nhiều trường hợp đếm dạng đếm xung

Quá trình giảm giá trị đếm được hiểu tương tự, phần còn lại của ví dụ này bạn đọc hãy tự giải thích theo những gợi ý trên

Trang 28

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…)

Ngắt là một trong 2 kỹ thuật “bắt” sự kiện cơ bản là hỏi vòng (Polling) và ngắt 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 button hay keypad (hoặc keyboard), 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 lên các LCD…(bạn hoàn toàn có thể làm được với AVR), rõ ràng trong các nhiệm vụ này việc nhận thông tin người dùng (start, stop, setup, change,…) 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 ưu tiên hàng đầu Nếu dùng Polling nghĩa là bạn cần viết 1 đoạn chương trình chuyên thăm dò trạng thái của các button (tôi tạm gọi đoạn chương trình đó là Input()) và bạn phải chèn đoạn chương trình Input() này vào rất nhiều vị trí trong chương trình chính để tránh trường hợp bỏ sót lệnh từ người dùng, điều này thật lãng phí thời gian thực thi Giải pháp cho vấn đề này là sử dụng ngắt, bằng cách kết nối các button với đường ngắt của chip và sử dụng chương trình Input() làm trình phục vụ ngắt - isr của ngắt đó, bạn không cần phải chèn Input() trong lúc đang thực thi và vì thế không tốn thời gian cho nó, Input() chỉ được gọi khi người dùng nhấn các button Đó là ý tưởng sử

dụng ngắt

Trang 29

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 Ví dụ vector ngắt ngoài 0 (external interrupt 0) của chip atmega8 có

địa chỉ là 0x001 (theo datasheet từ Atmel) Trong lúc chương trình chính đang thực

thi, nếu có một sự thay đổi dẫn đến ngắt xảy ra ở chân INT0 (chân 4), bộ đếm chương

trình (Program Counter) nhảy đến địa chỉ 0x009, giả sử ngay tại địa chỉ 0x001 chúng

ta có đặt 1 lệnh RJMP đến một trình phục vụ ngắt (IRS1 chẳng hạn), một lần nữa bộ

đếm chương trình nhảy đến IRS1 để thực thi trình phục vụ ngắt, kết thúc ISR1, bộ

đếm chương trình lại quay về vị trí trước đó trong chương trình chính, quá trình ngắt

kết thúc Không mang tính bắt buộc nhưng tôi khuyên bạn nên tổ chức chương trình

ngắt theo cách này để tránh những lỗi liên quan đến địa chỉ chương trình

Hình 1 Ngắt

Bảng 1 tóm tắt các vector ngắt có trên chip atmega8, cho các chip khác bạn hãy

tham khảo datasheet để biết thêm

Trang 30

Bảng 1 các vector ngắt và Reset trên chip Atmega8

II Ngắt ngoài (External Interrupt)

Phần này tôi dành giới thiệu các bạn cách cài đặt và sử dụng ngắt ngoài vì đây là

loại ngắt duy nhất độc lập với các thiết bị của chip, 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, giao tiếp nối tiếp USART, chuyển đổi

ADC…chúng ta sẽ khảo sát cụ thể khi tìm hiểu về hoạt động của các thiết bị này

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à chip

Trên chip atmega8 có 2 ngắt ngoài có tên là INT0 và INT1 tương ứng 2 chân số 4

(PD2) và số 5 (PD3) Như tôi đã đề cập trong bài AVR2, 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

Trang 31

- 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, điều này đồng nghĩa chúng

ta phải nhớ tất cả các thanh ghi của AVR Lúc này datasheet phát huy tác dụng, bạn

phải nhanh chóng download file datasheet của chip mình đang sử dụng, có rất nhiều

nơi để download như tại www.atmel.com hay trên các trang web chuyên cung cấp IC

datasheet miễn phí (www.alldatasheet.com là 1 ví dụ) Quay về 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 bên dưới

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 thanh ghi này

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

Giả sử chúng ta kết nối các ngắt ngoài trên AVR mega8 như phía trái hình 2, các

button dùng tạo ra các ngắt Có 4 khả năng (tạm gọi là các MODES) có thể xảy ra khi

chúng ta nhấn và thả các button Nếu không nhấn, trạng thái các chân INT là HIGH do

điện trở kéo lên, khi vừa nhấn 1 button, sẽ có chuyển trạng thái từ HIGH sang LOW,

chúng ta gọi là cạnh xuống - Falling Edge, khi button được nhấn và giữ, trạng thái các

chân INT được xác định là LOW và cuối cùng khi thả các button, trạng thái chuyển từ

LOW sang HIGH, gọi là cạnh lên – Rising Edge Trong những trường hợp cụ thể, 1

trong 4 MODES trên đều hữu ích, ví dụ trong các ứng dụng đếm xung (đếm encoder

của servo motor chẳng hạn) thì 2 MODE “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 MODE trên cho các ngắt ngoài Dưới

đây là cấu trúc thanh ghi MCUCR được trích ra từ datasheet của chip atmega8

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

Trang 32

để biết chức năng của các bit trên, đây là bảng “chân trị” của 2 bit ISC11, ISC10

Bảng chân trị cho các bit ISC01, ISC00 hoàn toàn tương tự

Bảng 2: INT1 Sense Control

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

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

(chú ý trên các chip AVR cũ, như các chip AT90Sxxxx, thanh ghi này có tên là thanh

ghi mặt nạ ngắt thông thường GIMSK, bạn tham khảo thêm datasheet của các chip này

nếu cần sử dụng đến) GICR cũng là 1 thanh ghi 8 bit nhưng chỉ có 2 bit cao (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 – INT1 gọi là bit cho phép ngắt 1(Interrupt Enable), set bit này bằng 1 nghĩa

bạn cho phép ngắt INT1 hoạt động, tương tự, bit INT0 điều khiển ngắt INT0

Thanh ghi cờ ngắt chung – GIFR (General Interrupt Flag Register) có 2 bit INTF1

và INTF0 là các bit trạng thái (hay bit cờ - Flag) của 2 ngắt INT1 và INT0 Nếu có 1

sự kiện ngắt phù hợp xảy ra trên chân INT1, bit INTF1 được tự động set bằng 1

(tương tự cho trường hợp của INTF0), 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 ngay bên dưới

Trang 33

Sau khi đã xác lập các bit sẵn sàng cho các ngắt ngoài, việc sau cùng chúng ta

cần làm là set bit I, tức bit cho phép ngắt toàn cục, trong thanh ghi trạng thái chung

của chip (thanh ghi SREG, xem lại bài AVR2) Một chú ý khác là vì các chân PD2,

DDRD) Quá trình thiết lập ngắt ngoài được trình bày trong hình 10

Hình 3 Thiết lập ngắt ngoài

ngoài bằng ngôn ngữ ASM, đối với các ngắt khác bạn chỉ cần thêm các DIRECTIVE

để định vị các vector ngắt tương ứng và viết chương trình phục vụ ngắt tương ứng

List 1 Ngắt với ASM

.ORG 0x001; Định vị vector ngắt ngoài 0 - INT0 (xem bảng vector)

RJMP INT0_ISR ; Nhảy đến INT0_ISR nếu có ngắt INT0 xảy ra

.ORG 0x002 ; Định vị vector ngắt ngoài 1 – INT1 (xem bảng vector)

RJMP INT1_ISR ; Nhảy đến INT1_ISR nếu có ngắt INT1 xảy ra

;Tương tự, định vị các vector ngắt khác ở đây………

;………

.ORG 0x020 ; Định vị chương trình chính

BATDAU:

Trang 34

; set chân PD2 và PD3 như các chân input

LDI R16, 0Bxxxx00xx ; x là trạng thái do bạn tự chọn, 0 hoặc 1

OUT DDRD, R16 ; PD2 và PD3 là input

LDI R16, 0Bxxxx11xx ; x là trạng thái do bạn tự chọn, 0 hoặc 1

OUT PORTD, R16 ; mắc điện trở kéo lên cho PD2, PD3

; khởi động ngắt

LDI R16, $0B ; $0B=00001011, INT1: ngắt cạnh xuống, INT0: ngắt cạnh lên OUT MCUCR, R16 ; xuất giá trị điều khiển ra thanh ghi MCUCR

LDI R16, $C0 ;$C0=11000000: Enable INT1 và INT0

OUT GICR, R16 ;xuất giá trị điều khiển ra thanh ghi GICR

SEI ;set bit cho phép ngắt toàn cục

RETI ; phải dùng lệnh RETI để quay về chương trình chính

;và đây là định nghĩa trình phục vụ ngắt INT1_ISR………

INT1_ISR:

; các công việc cần thực hiện khi có ngắt ………

;………

RETI ; phải dùng lệnh RETI để quay về chương trình chính

Bạn thấy các các ngắt được định vị nằm giữa vị trí 0x0000, khi mới khởi động, tại

ví trí 0x000 là lệnh “RJMP BATDAU”, như thế các lệnh RJMP tại các vector ngắt và các ISR đều không được thực hiện, chúng chỉ được thực hiện một cách tự động khi có ngắt

dụng ngắt trong chương trình viết bằng C (avr-gcc) bạn chỉ cần include file

“interrupt.h” nằm trong thư mục con “avr” là xong file header interrupt.h chứa định

Trang 35

nghĩa các hàm và phương thức phục vụ cho viết trình phục vụ ngắt, các vector ngắt không được định nghĩa trong file này mà trong file iom8.h (cho atmega8) Nếu bạn vô tình tìm thấy 1 chương trình ngắt nào đó không include file interrupt.h mà include file signal.h thì bạn đừng ngạc nhiên, đó là cách viết cũ trong avr-gcc, thật ra bạn hoàn toàn có thể sử dụng cách viết cũ vì các phiên bản mới của avr-libc (đi cùng với các bản WinAVR mới) vẫn hỗ trợ cách viết này nhưng không khuyên khích bạn dùng

Trong C, các trình phục vụ ngắt có dạng là ISR(vector_name) Trong các phiên

bản cũ trình phục vụ ngắt có tên SIGNAL(vector_name), nhưng cũng như file header signal.h, cách viết này vẫn được hỗ trợ trong phiên bản mới nhưng không được

manual” Bảng 10 tóm tắt các vector_name của một số ngắt thông dụng trên atmega8, bạn chú ý rằng các vector_name trong avr-libc được định nghĩa rất khác nhau cho từng loại chip, bạn nhất thiết phải sử dụng tài liệu “avr-libc manual” để biết chính xác các vector_name cho loại chip mà bạn đang dùng

Bảng 3: vector_name cho atmega8

Vector name Old vector name Description

ANA_COMP_vect SIG_COMPARATOR Analog Comparator

EE_RDY_vect SIG_EEPROM_READY EEPROM Ready

INT0_vect SIG_INTERRUPT0 External Interrupt 0

Trang 36

SPI_STC_vect SIG_SPI Serial Transfer Complete

SPM_RDY_vect SIG_SPM_READY Store Program Memory Ready

TIMER0_OVF_vect SIG_OVERFLOW0 Timer/Counter0 Overflow

TIMER1_CAPT_vect SIG_INPUT_CAPTURE1 Timer/Counter Capture Event

TIMER1_COMPA_vect SIG_OUTPUT_COMPARE1A Timer/Counter1 Compare Match A TIMER1_COMPB_vect SIG_OUTPUT_COMPARE1B Timer/Counter1 Compare MatchB TIMER1_OVF_vect SIG_OVERFLOW1 Timer/Counter1 Overflow

TIMER2_COMP_vect SIG_OUTPUT_COMPARE2 Timer/Counter2 Compare Match TIMER2_OVF_vect SIG_OVERFLOW2 Timer/Counter2 Overflow

TWI_vect SIG_2WIRE_SERIAL 2-wire Serial Interface

USART3_UDRE_vect SIG_USART3_DATA USART3 Data register Empty

III Ví dụ ngắt ngoài với C

Để thực hiện ví dụ sử dụng ngắt ngoài bằng C, tôi sẽ viết lại chương trình ví dụ của bài "cấu trúc AVR" nhưng bằng ngôn ngữ C và sử dụng ngắt Trong chương trình

ví dụ của bài AVR2, chúng ta thực hiện việc đếm lên và đếm xuống dùng 2 button, chúng ta sẽ vẫn thực hiện trên ý tưởng này nhưng có chút thay đổi trong kết nối, trước hết bạn vẽ 1 mạch điện mô phỏng trong Proteus như hình 4

Hình 4 Mạch điện mô phỏng ngắt

Kết nối button đếm lên với ngắt INT0, button đếm xuống với INT1, PORTB được chọn làm PORT xuất Hãy chạy Programmer Notepad, tạo 1 Project mới tên AVR2-INT, type đoạn code bên dưới vào 1 file new và lưu với tên main.c, add file này vào Project của bạn, sau đó tạo một Makefile cho Project

Trang 37

DDRD=0x00; //khai báo PORTD là Input để sử dụng 2 chân ngắt

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

DDRB=0xFF; //PORTB là Output để xuất LED 7 đoạn

MCUCR|=(1<<ISC11)|(1<<ISC01); //cả 2 ngắt là ngắt cạnh xuống

GICR |=(1<<INT1)|(1<<INT0); //cho phép 2 ngắt hoạt động

sei(); //set bit I cho phép ngắt toàn cục

val++; //nếu có ngắt INT0 xảy ra, tăng val thêm 1

if (val>9) val=0; //giới hạn không vượt quá 9

PORTB=val;

}

//Trình phục vụ ngắt của INT1

ISR(INT1_vect){

val ; //nếu có ngắt INT1 xảy ra, giảm val đi 1

if (val<0) val=9; //giới hạn không nhỏ hơn 0

Trang 38

dùng để kiểm tra rằng chương trình trong vòng lặp vô tận vẫn đang hoạt động Có lẽ

phần khó hiểu nhất trong đoạn code là cách mà tôi dùng để khai báo cho 2 thanh ghi

điều khiển ngắt MCUCR và GICR

Nếu xem lại bảng tóm tắt các toán tử của C, toán tử “<<” được gọi là toán tử “dịch trái” dùng trên dạng nhị phân của các con số, nếu bạn thấy x=5<<3 nghĩa là dịch các

bit nhị phân của 5 sang trái 3 vị trí và gán cho x, như mô tả như sau:

Bạn thấy toàn bộ các bit của 5 đã dịch sang trái 3 vị trí và giá trị của số mới thu

được là x=40, chú ý 40=5x8=5x2^3 Hãy nhìn câu lệnh

MCUCR|=(1<<ISC11)|(1<<ISC01), giờ thì bạn đã hiểu (1<<ISC11) nghĩa là dịch

số 1 sang trái ISC11 vị trí, và (1<<ISC01) là dịch số 1 sang trái ISC01 vị trí, nhưng

ISC11 và ISC01 ở đâu ra và giá trị của chúng là bao nhiêu? Bạn chú ý, khi bạn

include file “io.h” thì file “iom8.h” được chèn vào, và trong file này chứa khai báo

địa chỉ các thanh ghi của chip atmega8, các tên bit cũng được khai báo sẵn trong

file này, nếu bạn mở file iom8.h (thường nằm trong thư mục

~\WinAVR\avr\include\avr) bằng 1 chương trình text editor như notepad, dùng

chức năng find bạn sẽ thấy các dòng định nghĩa như sau:

Đây là định nghĩa vị trí các bit trong thanh ghi MCUCR, vậy là đã rõ,

ISC11=3, ISC01=1, do đó: (1<<ISC11) tương đương (1<<3) = 00001000 (Binary)

và (1<<ISC01) = 00000010, bạn hãy tưởng tượng rằng bạn đã mang số 1 đến các

vị trí của ISC11 và ISC01 trong thanh ghi MCUCR Bây giờ đến lượt toán tử OR

Trang 39

Gán giá trị này cho MCUCR, đối chiếu với bảng các giá trị của các bit ISC (bảng 9) bạn sẽ thấy chúng ta đang set cho 2 ngắt là falling edge Điều cuối cùng của câu lệnh set MCUCR là cách rút gọn câu lệnh MCUCR|=(1<<ISC11)|(1<<ISC01) thực chất là MCUCR= MCUCR| ((1<<ISC11)|(1<<ISC01)), đây là cách set một số bit trong một thanh ghi mà không muốn làm ảnh hưởng đến các bit khác (nhưng bạn phải thật cẩn thận với cách làm này vì có thể sẽ phản tác dụng nếu bạn không nắm rõ), bạn

có thể gán trực tiếp MCUCR=(1<<ISC11)|(1<<ISC01), hay nhanh hơn

MCUCR=0x0A (0x0A=00001010) Vậy lí do nào khiến tôi biến 1 câu lệnh gán đơn giản thành một “bài toán” khó hiểu, câu trả lời chính là tính tổng quát Trong các chip AVR khác nhau, vị trí các bit trong các thanh ghi là rất khác nhau, câu lệnh

MCUCR=0x0A đúng cho atmega8 nhưng không áp dụng được cho các chip khác trong khi câu lệnh MCUCR=(1<<ISC11)|(1<<ISC01) thì hoạt động tốt, một lí do khác

là cách viết gián tiếp này giúp người khác (hay chính bạn sau này) khi đọc code có thể

dễ dàng hiểu được ý đồ người viết…

Tôi nghĩ bạn đã quá hiểu dòng lệnh tiếp theo, GICR |=(1<<INT1)|(1<<INT0) Tôi dừng giải thích đoạn code ở đây và cũng dừng bài AVR3, bạn hãy thực tập bằng cách viết lại đoạn code trên bằng ASM

Trang 40

Bài 4 - Timer - Counter

(Timer/Counter) trong AVR Công cụ phục vụ cho bài này vẫn là bộ công cụ

WinAVR và phần mềm mô phỏng Proteus Tôi vẫn dùng chip Atmega8 để làm ví dụ Một điều không may mắn là không phải tất cả các bộ Timer/Counter trên tất cả các dòng chip AVR là như nhau, vì thế những gì tôi trình bày trong bài này có thể sẽ

không đúng với các dòng AVR khác như AT90S…Tuy nhiên tôi cũng sẽ cố gắng chỉ

ra một số điểm khác biệt cơ bản để các bạn có thể tự mình điều khiển các chip khác Nội dung bài học này bao gồm:

• Nắm bắt cơ bản các bộ Timer/Counter có trên AVR

• Sử dụng các Timer/Counter như các bộ định thời

• Sử dụng các Timer/Counter như các bộ đếm

• Sử dụng các Timer/Counter như các bộ tạo xung điều rộng PWM

• Viết một ví dụ điều khiển động cơ RC servo bằng PWM

II Tổng quan các bộ Timer/Counter trên chip Atmega8

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, như tên gọi của chúng, là định thì (tạo ra một khoảng thời gian, đếm thời gian…) và đế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 xung điều rộng 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, cụ thể trên chip Atmega8 có 2 bộ Timer 8 bit (Timer/Counter0 và Timer/Counter2) và 1 bộ 16 bit (Timer/Counter1) 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 Atmega8:

Ngày đăng: 28/11/2015, 01:05

TỪ KHÓA LIÊN QUAN

TRÍCH ĐOẠN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

w