You are on page 1of 138

MỤC LỤC

Chương 1: TỔNG QUAN VỀ VI ĐIỀU KHIỂN............................................................3


1.1. Giới thiệu về vi xử lý...............................................................................................3
1.2. Giới thiệu về vi điều khiển.......................................................................................4
1.3. Ứng dụng của vi điều khiển.....................................................................................6
1.4. Cấu trúc tổng quan của vi điều khiển.......................................................................6
1.5. Phân loại vi điều khiển.............................................................................................8
Chương 2: HỌ VI ĐIỀU KHIỂN 8051..........................................................................10
2.1. Giới thiệu chung....................................................................................................10
2.2. Cấu tạo và hoạt động..............................................................................................13
2.2.1. Sơ đồ khối.......................................................................................................13
2.2.2 Tổ chức bộ nhớ................................................................................................15
2.2.3. Các thanh ghi..................................................................................................16
2.3. Một số chức năng đặc biệt.....................................................................................17
2.3.1. Bộ định thời....................................................................................................17
2.3.2. Tổ chức ngắt...................................................................................................20
2.3.3. Truyền thông nối tiếp......................................................................................23
2.4. Lập trình với vi điều khiển 8051............................................................................28
2.4.1. Ngôn ngữ lập trình cho 8051...........................................................................28
2.4.2. Giới thiệu phần mềm lập trình Keil C và phần mềm mô phỏng Proteus.........30
2.4.3. Lệnh số học lệnh logic và các cổng vào ra......................................................39
2.4.4. Lập trình bộ đếm, bộ định thời........................................................................41
2.4.5. Lập trình truyền thông nối tiếp........................................................................45
2.4.6. Lập trình ngắt..................................................................................................47
Chương 3: HỌ VI ĐIỀU KHIỂN AVR.........................................................................53
3.1. Giới thiệu chung....................................................................................................53
3.2. Cấu tạo và hoạt động..............................................................................................56
3.2.1. Sơ đồ khối.......................................................................................................56
3.2.2. Sơ đồ và chức năng các chân..........................................................................56
3.2.3. Cấu trúc bộ nhớ...............................................................................................57
3.3. Một số chức năng đặc biệt.....................................................................................59
3.3.1. Timer/ Counter................................................................................................59
3.3.2. USART...........................................................................................................69
3.3.3. ADC................................................................................................................ 78
3.3.4. Chuẩn giao tiếp SPI.........................................................................................85
3.3.5. Chuẩn truyền thông I2C..................................................................................92
3.3.6. Ngắt............................................................................................................... 101
3.4. Lập trình cho vi điều khiển AVR........................................................................105
3.4.1. Giới thiệu phần mềm.....................................................................................105
3.4.2. Lập trình cho Timer......................................................................................106
3.4.3. UART............................................................................................................ 113
3.4.4. Ngắt...............................................................................................................119
3.5. Giới thiệu Arduino...............................................................................................123
3.5.1. Tổng quan về Arduino..................................................................................123
3.5.2. Sơ đô chân của Arduino................................................................................124
Chương 4: GIỚI THIỆU MỘT SỐ VI ĐIỀU KHIỂN TIÊN TIẾN..........................129
4.1. Vi điều khiển PIC.................................................................................................129
4.2. ARM....................................................................................................................132
4.3. FPGA...................................................................................................................135

2
Chương 1: TỔNG QUAN VỀ VI ĐIỀU KHIỂN

1.1. Giới thiệu về vi xử lý.


Vi xử lý (viết tắt là µP hay uP), đôi khi còn được gọi là bộ vi xử lý, là một linh
kiện điện tử được chế tạo từ các tranzito thu nhỏ tích hợp lên trên một vi mạch tích hợp
đơn.

Khối xử lý trung tâm (CPU) là một bộ vi xử lý được nhiều người biết đến nhưng
ngoài ra nhiều thành phần khác trong máy tính cũng có bộ vi xử lý riêng của nó, ví dụ trên
card màn hình (video card) ta cũng có một bộ vi xử lý. Trước khi xuất hiện các bộ vi xử
lý, các CPU được xây dựng từ các mạch tích hợp cỡ nhỏ riêng biệt, mỗi mạch tích hợp chỉ
chứa khoảng vào chục tranzito. Do đó, một CPU có thể là một bảng mạch gồm hàng ngàn
hay hàng triệu vi mạch tích hợp. Ngày nay, công nghệ tích hợp đã phát triển, một CPU có
thể tích hợp lên một hoặc vài vi mạch tích hợp cỡ lớn, mỗi vi mạch tích hợp cỡ lớn chứa
hàng ngàn hoặc hàng triệu tranzito. Nhờ đó công suất tiêu thụ và giá thành của bộ vi xử lý
đã giảm đáng kể.

Vi xử lý có các khối chức năng cần thiết để lấy dữ liệu, xử lý dữ liệu và xuất dữ
liệu ra ngoài sau khi đã xử lý. Và chức năng chính của vi xử lý chính là xử lý dữ liệu,
chẳng hạn như cộng, trừ, nhân, chia, so sánh.v.v... Vi xử lý không có khả năng giao tiếp
trực tiếp với các thiết bị ngoại vi, nó chỉ có khả năng nhận và xử lý dữ liệu mà thôi. Để vi
xử lý hoạt động cần có chương trình kèm theo, các chương trình này điều khiển các mạch
logic và từ đó vi xử lý xử lý các dữ liệu cần thiết theo yêu cầu.

Chương trình là tập hợp các lệnh để xử lý dữ liệu thực hiện từng lệnh được lưu trữ
trong bộ nhớ, công việc thực hành lệnh bao gồm: nhận lệnh từ bộ nhớ, giải mã lệnh và
thực hiện lệnh sau khi đã giải mã. Để thực hiện các công việc với các thiết bị cuối cùng,
chẳng hạn điều khiển động cơ, hiển thị kí tự trên màn hình .... đòi hỏi phải kết hợp vi xử
lý với các mạch điện giao tiếp với bên ngoài được gọi là các thiết bị I/O (nhập/xuất) hay
còn gọi là các thiết bị ngoại vi. Bản thân các vi xử lý khi đứng một mình không có nhiều
hiệu quả sử dụng, nhưng khi là một phần của một máy tính, thì hiệu quả ứng dụng của Vi

3
xử lý là rất lớn. Vi xử lý kết hợp với các thiết bị khác được sử trong các hệ thống lớn,
phức tạp đòi hỏi phải xử lý một lượng lớn các phép tính phức tạp, có tốc độ nhanh. Chẳng
hạn như các hệ thống sản xuất tự động trong công nghiệp, các tổng đài điện thoại, hoặc ở
các robot có khả năng hoạt động phức tạp v.v... Bộ vi xử lý có khả năng vượt bậc so với
các hệ thống khác về khả năng tính toán, xử lý, và thay đổi chương trình linh hoạt theo
mục đích người dùng, đặc biệt hiệu quả đối với các bài toán và hệ thống lớn. Tuy nhiên
đối với các ứng dụng nhỏ, tầm tính toán không đòi hỏi khả năng tính toán lớn thì việc ứng
dụng vi xử lý cần cân nhắc. Bởi vì hệ thống dù lớn hay nhỏ, nếu dùng vi xử lý thì cũng
đòi hỏi các khối mạch điện giao tiếp phức tạp như nhau. Các khối này bao gồm bộ nhớ để
chứa dữ liệu và chương trình thực hiện, các mạch điện giao tiếp ngoại vi để xuất nhập và
điều khiển trở lại, các khối này cùng liên kết với vi xử lý thì mới thực hiện được công
việc. Để kết nối các khối này đòi hỏi người thiết kế phải hiểu biết tinh tường về các thành
phần vi xử lý, bộ nhớ, các thiết bị ngoại vi. Hệ thống được tạo ra khá phức tạp, chiếm
nhiều không gian, mạch in phức tạp và vấn đề chính là trình độ người thiết kế. Kết quả
là giá thành sản phẩm cuối cùng rất cao, không phù hợp để áp dụng cho các hệ thống nhỏ.

1.2. Giới thiệu về vi điều khiển.


Vì một số nhược điểm trên nên các nhà chế tạo tích hợp một ít bộ nhớ và một số
mạch giao tiếp ngoại vi cùng với vi xử lý vào một IC duy nhất được gọi là
Microcontroller-Vi điều khiển. Vi điều khiển có khả năng tương tự như khả năng của vi
xử lý, nhưng cấu trúc phần cứng dành cho người dùng đơn giản hơn nhiều.

Khái niệm “vi xử lý” (microprocessor) và “vi điều khiển” (microcontroller). Về cơ


bản hai khái niệm này không khác nhau nhiều, “vi xử lý” là thuật ngữ chung dùng để đề
cập đến kỹ thuật ứng dụng các công nghệ vi điện tử, công nghệ tích hợp và khả năng xử
lý theo chương trình vào các lĩnh vực khác nhau. Vào những giai đoạn đầu trong quá trình
phát triển của công nghệ vi xử lý, các chip (hay các vi xử lý) được chế tạo chỉ tích hợp
những phần cứng thiết yếu như CPU cùng các mạch giao tiếp giữa CPU và các phần cứng
khác. Trong giai đoạn này, các phần cứng khác (kể cả bộ nhớ) thường không được tích
hợp trên chip mà phải ghép nối thêm bên ngoài. Các phần cứng này được gọi là các ngoại

4
vi (Peripherals). Về sau, nhờ sự phát triển vượt bậc của công nghệ tích hợp, các ngoại vi
cũng được tích hợp vào bên trong IC và người ta gọi các vi xử lý đã được tích hợp thêm
các ngoại vi là các “vi điều khiển”.

Năm 1976 Intel giới thiệu bộ vi điều khiển (microcontroller) 8748, một chip tương
tự như các bộ vi xử lý và là chip đầu tiên trong họ MCS-48. Độ phức tạp, kích thước và
khả năng của vi điều khiển tăng thêm một bậc quan trọng vào năm 1980 khi intel tung ra
chip 8051, bộ Vi điều khiển đầu tiên của họ MCS-51 và là chuẩn công nghệ cho nhiều họ
vi điều khiển được sản xuất sau này. Sau đó rất nhiều họ vi điều khiển của nhiều nhà chế
tạo khác nhau lần lượt được đưa ra thị trường với tính năng được cải tiến ngày càng
mạnh.

Vi điều khiển ra đời mang lại sự tiện lợi đối với người dùng, họ không cần nắm
vững một khối lượng kiến thức quá lớn như người dùng vi xử lý, kết cấu mạch điện dành
cho người dùng cũng trở nên đơn giản hơn nhiều và có khả năng giao tiếp trực tiếp với
các thiết bị bên ngoài. Vi điều khiển tuy được xây dựng với phần cứng dành cho người sử
dụng đơn giản hơn, nhưng thay vào lợi điểm này là khả năng xử lý bị giới hạn (tốc độ xử
lý chậm hơn và khả năng tính toán ít hơn, dung lượng chương trình bị giới hạn). Thay vào
đó, vi điều khiển có giá thành rẻ hơn nhiều so với vi xử lý, việc sử dụng đơn giản, do đó
nó được ứng dụng rộng rãi vào nhiều ứng dụng có chức năng đơn giản, không đòi hỏi tính
toán phức tạp. Vi điều khiển được ứng dụng trong các dây chuyền tự động loại nhỏ, các
robot có chức năng đơn giản, trong máy giặt, ôtô v.v...

Vi điều khiển là một máy tính được tích hợp trên một chíp, nó thường được sử
dụng để điều khiển các thiết bị điện tử. Vi điều khiển, thực chất, là một hệ thống bao gồm
một vi xử lý có hiệu suất đủ dùng và giá thành thấp (khác với các bộ vi xử lý đa năng
dùng trong máy tính) kết hợp với các khối ngoại vi như bộ nhớ, các mô đun vào/ra, các
mô đun biến đổi số sang tương tự và tương tự sang số,... Ở máy tính thì các mô đun
thường được xây dựng bởi các chíp và mạch ngoài.

Hầu hết các vi điều khiển ngày nay được xây dựng dựa trên kiến trúc Harvard, kiến
trúc này định nghĩa bốn thành phần cần thiết của một hệ thống nhúng. Những thành phần

5
này là lõi CPU, bộ nhớ chương trình (thông thường là ROM hoặc bộ nhớ Flash), bộ nhớ
dữ liệu (RAM), một hoặc vài bộ định thời và các cổng vào/ra để giao tiếp với các thiết bị
ngoại vi và các môi trường bên ngoài - tất cả các khối này được thiết kế trong một vi
mạch tích hợp. Vi điều khiển khác với các bộ vi xử lý đa năng ở chỗ là nó có thể hoạt
động chỉ với vài vi mạch hỗ trợ bên ngoài.

1.3. Ứng dụng của vi điều khiển


Vi điều khiển thường được dùng để xây dựng các hệ thống nhúng. Nó xuất hiện
khá nhiều trong các dụng cụ điện tử, thiết bị điện, máy giặt, lò vi sóng, điện thoại, đầu đọc
DVD, thiết bị đa phương tiện, dây chuyền tự động, v.v.

Vi điều khiển có thể dùng trong thiết kế các loại máy tính nhúng. Máy tính nhúng
có trong hầu hết các thiết bị tự động, thông minh ngày nay. ta có thể dùng vi điều khiển
để thiết kế bộ điều khiển cho các sản phẩm như:

Trong các sản phẩm dân dụng:

+ Nhà thông minh:

+ Trong quảng cáo:

+ Các máy móc dân dụng

+ Các sản phẩm giải trí

Trong các thiết bị y tế.

Trong các sản phẩm công nghiệp.

1.4. Cấu trúc tổng quan của vi điều khiển


a. CPU:

CPU là nơi quản lí tất cả các hoạt động của VI ĐIỀU KHIỂN.

Bên trong CPU gồm:

+ ALU là bộ phận thao tác trên các dữ liệu

+ Bộ giải mã lệnh và điều khiển, xác định các thao tác mà CPU cần thực hiện

6
+ Thanh ghi lệnh IR, lưu giữ opcode của lệnh được thực thi

+Thanh ghi PC, lưu giũ địa chỉ của lệnh kế tiếp cần thực thi

+ Một tập các thanh ghi dùng để lưu thông tin tạm thời

b. ROM:

ROM là bộ nhớ dùng để lưu giữ chương trình. ROM còn dùng để chứa số liệu các
bảng, các tham số hệ thống, các số liệu cố định của hệ thống. Trong quá trình hoạt động
nội dung ROM là cố định, không thể thay đổi, nội dung ROM chỉ thay đổi khi ROM ở
chế độ xóa hoặc nạp chương trình.

c. RAM:

RAM là bộ nhớ dữ liệu. Bộ nhớ RAM dùng làm môi trường xử lý thông tin, lưu
trữ các kết quả trung gian và kết quả cuối cùng của các phép toán, xử lí thông tin. Nó
cũng dùng để tổ chức các vùng đệm dữ liệu, trong các thao tác thu phát, chuyển đổi dữ
liệu.

d. BUS

BUS là các đường dẫn dùng để di chuyển dữ liệu. Bao gồm: bus địa chỉ, bus dữ
liệu, và bus điều khiển

e. Bộ định thời:

Được sử dụng cho các mục đích chung về thời gian.

f. Các khối truyền thông:

Khối này chính là các cổng tín hiệu để đưa tín hiệu từ bên ngoài vào vi điều khiển
cũng như từ vi điều khiển ra. Các cổng này bao gồm các cổng nối tiếp và song song theo
những chuẩn khác nhau tùy theo các loại vi điều khiển.

g. ADC:

Bộ phận chuyển tín hiệu analog sang tín hiệu digital. Các tín hiệu bên ngoài đi vào
vi điều khiển thường ở dạng analog. ADC sẽ chuyển tín hiệu này về dạng tín hiệu digital

7
mà vi điều khiển có thể hiểu được. Tuy nhiên một số loại vi điều khiển không có ADC.
Muốn sử dụng ADC ta phải dung bộ ADC được lắp rời bên ngoài.

1.5. Phân loại vi điều khiển


1.5.1. Phân loại theo độ dài thanh ghi

Dựa vào độ dài của các thanh ghi và các lệnh của vi điều khiển mà người ta chia ra
các loại vi điều khiển 8bit, 16bit, hay 32bit.... Các loại vi điều khiển 16 bit do có độ dài
lệnh lớn hơn nên các tập lệnh cũng nhiều hơn, phong phú hơn. Tuy nhiên bất cứ chương
trình nào viết bằng vi điều khiển 16bit ta đều có thể viết trên vi điều khiển 8bit với
chương trình thích hợp

1.5.2. Phân loại theo kiến trúc

Kiến trúc CISC và RISC

Vi xử lý hoặc vi điều khiển CISC là vi điều khiển có tập lệnh phức tạp. Các vi
điều khiển này có một số lượng lớn các lệnh nên giúp cho người lập trình có thể linh hoạt
và dễ dàng hơn khi viết chương trình. Vi điều khiển RISC là vi điều khiển có tập lệnh đơn
giản. Chúng có một số lương nhỏ các lệnh đơn giản. DO đó, chúng đòi hỏi phần cứng ít
hơn, giá thành thấp hơn, và nhanh hơn so với CISC. Tuy nhiên nó đòi hỏi người lập trình
phải viết các chương trình phức tạp hơn, nhiều lệnh hơn.

Kiến trúc Harvard và kiến trúc Vonneumann

Kiến trúc Harvard sử dụng bộ nhớ riêng biệt cho chương trình và dữ liệu. Bus địa
chỉ và bus dữ liệu độc lập với nhau nên quá trình truyền nhận dữ liệu đơn giản hơn Kiến
trúc Vonneumann sử dụng chung bộ nhớ cho chương trình và dữ liệu. Điều này làm cho
vi điều khiển gọn nhẹ hơn, giá thành nhẹ hơn.

1.5.3. Phân loại theo hãng sản xuất

+ Họ vi điều khiển AMCC (tập đoàn Applied Micro Circuits Corporation).

+ Họ vi điều khiển Atmel (8051, Atmel AT91, AT90, Tiny & Mega – AVR…).

+ Họ vi điều khiển Cypress MicroSystems (PSoC).

8
+ Họ vi điều khiển Freescale Semiconductor (Motorola).

+ Họ vi điều khiển Fujitsu( F²MC Family (8/16 bit), FR Family (32 bit), FR-V
Family (32 bit RISC).

+ Họ vi điều khiển Intel.

+ Họ vi điều khiển Microchip (PIC).

+ Họ vi điều khiển National Semiconductor (COP8, CR16).

+ Họ vi điều khiển STMicroelectronics (ST 62, ST7STM8, STM32 (Cortex-Mx).

+ Họ vi điều khiển Philips Semiconductors.

9
Chương 2: HỌ VI ĐIỀU KHIỂN 8051

2.1. Giới thiệu chung

Vào năm 1981, hãng Intel giới thiệu một số bộ vi điều khiển được gọi là 8051. Bộ
vi điều khiển này có 128 byte RAM, 4K byte ROM trên chíp, hai bộ định thời, một cổng
nối tiếp và 4 cổng (đều rộng 8 bit) vào ra tất cả được đặt trên một chíp. 8051 là một bộ xử
lý 8 bit có nghĩa là CPU chỉ có thể làm việc với 8 bit dữ liệu tại một thời điểm. Dữ liệu
lớn hơn 8 bit được chia ra thành các dữ liệu 8 bit để cho xử lý. 8051 có tất cả 4 cổng vào
ra I/O mỗi cổng rộng 8 bit.

Hình 2.1 Một số bộ vi điều khiển họ 8051.

8051 đã trở nên phổ biến sau khi Intel cho phép các nhà sản xuất khác sản xuất và
bán bất kỳ dạng biến thế nào của 8051 mà họ thích với điều kiện họ phải để mã lại tương
thích với 8051. Điều này dẫn đến sự ra đời nhiều phiên bản của 8051 với các tốc độ khác
nhau và dung lượng ROM trên chíp khác nhau được bán bởi các nhà sản xuất. Điểm quan
trọng là mặc dù có nhiều biến thể khác nhau của 8051 về tốc độ và dung lượng nhớ ROM
trên chíp, nhưng tất cả các lệnh của chúng đều tương thích với 8051 ban đầu. Việc này có
nghĩa rất lớn vì khi ta viết chương trình của mình cho một phiên bản nào đó thì nó cũng sẽ
chạy với mọi phiên bản bất kỳ khác mà không phân biệt nó từ hãng sản xuất nào. Đây
chính là yếu tố quan trọng giúp cho 8051 phổ biến như ngày nay.

10
8051 được sản xuất dưới nhiều dạng kiểu bộ nhớ khác nhau như UV - PROM,
Flash và NV -RAM mà chúng đều có số đăng ký linh kiện khác nhau. Phiên bản UV-
PROM của 8051 là 8751. Chíp 8751 chỉ có 4K byte bộ nhớ UV-EPROM trên chíp. Để sử
dụng chíp này để phát triển yêu cầu truy cập đến một bộ đốt PROM cũng như bộ xoá UV-
EPROM để xoá nội dung của bộ nhớ UV-EPROM bên trong 8751 trước khi ta có thể lập
trình lại nó. Do một thực tế là ROM trên chíp đối với 8751 là UV-EPROM nên cần phải
mất 20 phút để xoá 8751 trước khi nó có thể được lập trình trở lại. Điều này đã dẫn đến
nhiều nhà sản xuất giới thiệu các phiên bản Flash Rom và NV-RAM của 8051. Ngoài ra
còn có nhiều phiên bản với các tốc độ khác nhau của 8751 từ nhiều hãng khác nhau.

Phiên bản Flash ROM được bán bởi nhiều hãng khác nhau chẳng hạn của Atmel
corp với tên gọi là AT89C51. Chíp 8051 phổ biến này có ROM trên chíp ở dạng bộ nhớ
Flash. Điều này là lý tưởng đối với những phát triển nhanh vì bộ nhớ Flash có thể được
xoá trong vài giây nhanh hơn nhiều so với 20 phút hoặc hơn giống như đối với 8751.

Còn phiên bản NV-RAM của 8051 do Dalas Semi Conductor cung cấp thì được
gọi là DS5000. Bộ nhớ ROM trên chíp của DS5000 ở dưới dạng NV-RAM. Khả năng
đọc/ghi của nó cho phép chương trình được nạp vào ROM trên chíp trong khi nó vẫn ở
trong hệ thống (không cần phải lấy ra). Điều này còn có thể được thực hiện thông qua
cổng nối tiếp của máy tính IBM PC. Việc nạp chương trình trong hệ thống (in-system)
của DS5000 thông qua cổng nối tiếp của PC làm cho nó trở thành một hệ thống phát triển
tại chỗ lý tưởng. Một điểm ưu việt của NV-RAM là khả năng thay đổi nội dung của ROM
theo từng byte tại một thời điểm. Điều này tương phản với bộ nhớ Flash và EPROM mà
bộ nhớ của chúng phải được xoá sạch trước khi lập trình lại cho chúng.

Ngoài ra còn có phiên bản OTP (khả trình một lần) của 8051 được sản xuất bởi rất
nhiều hãng. Các phiên bản Flash và NV-RAM thường được dùng để phát triển sản phẩm
mẫu. Khi một sản phẩm được thiết kế và được hoàn thiện tuyệt đối thì phiên bản OTP của
8051 được dùng để sản hàng loạt vì nó rẻ hơn rất nhiều theo giá thành một đơn vị sản
phẩm.

11
Một nhà sản xuất chính của họ 8051 khác nữa là Philips Corporation. Hãng này có
một dải lựa chọn rộng lớn cho các bộ vi điều khiển họ 8051. Nhiều sản phẩm của hãng đã
có kèm theo các đặc tính như các bộ chuyển đổi ADC, DAC, cổng I/0 mở rộng và cả các
phiên bản OTP và Flash. 

Các bộ vi điều khiển thành viên khác của họ 8051 được thể hiện trong bảng sau.

Bộ nhớ chương trình trên chip Bộ nhớ dữ liệu trên chip Bộ định thời

8051 4K ROM 128 byte 2

8031 0K ROM 128 byte 2

8052 8K ROM 256 byte 3

8032 0K ROM 256 byte 3

8751 4K EPROM 128 byte 2

8752 8K EPROM 256 byte 3

Bộ vi điều khiển 8031 không có ROM nội nên khi sử dụng chíp này ta phải bổ
sung ROM ngoài cho nó. So với 8051 mà chương trình được chứa trong ROM trên chíp
bị giới hạn bởi 4K byte, còn ROM ngoài chứa chương trình được gắn vào 8031 thì có thể
lớn đến 64K byte. Khi bổ sung cổng, như vậy chỉ còn lại 2 cổng để thao tác. Để giải quyết
vấn đề này ta có thể bổ sung cổng vào - ra cho 8031 ví dụ phối phép 8031 với bộ nhớ và
cổng vào - ra chẳng hạn với chíp 8255. Ngoài ra còn có các phiên bản khác nhau về tốc
độ của 8031 từ các hãng sản xuất khác nhau.

Các bộ vi điều khiển 8xx2 cũng tương tự như 8xx1 nhưng có dung lượng bộ nhớ
gấp đôi so với 8xx1 mặt khác nó có thêm 1 bộ định thời nữa.

Ngày nay, các bộ vi điều khiển 8051 tiếp tục được phát triển và ứng dụng rộng rãi
với những phiên bản tiêu thụ ít điện năng và có ứng dụng được chuyên môn hóa.

12
2.2. Cấu tạo và hoạt động

2.2.1. Sơ đồ khối

Hình 2.2. Cấu trúc vi điều khiển 8051

Như trong mục trước ta đã có những cái nhìn tổng quát về các vi điều khiển thuộc
họ 8051. Trong phần này ta sẽ đi tìm hiểu chi tiết về 8051 nói chung, vi điều khiển
AT89C51 nói riêng, là vi điều khiển điển hình của 8051.

13
Hình 2.3 Sơ đồ các chân của 8051

- P0.0 đến P0.7 là các chân của cổng 0.

- P1.0 đến P1.7 là các chân của cổng 1.

- P2.0 đến P2.7 là các chân của cổng 2.

- P3.0 đến P3.7 là các chân của cổng 3.

- RxD: Nhận tín hiệu kiểu nối tiếp.

- TxD: Truyền tín hiệu kiểu nối tiếp.

- ∫´ 0 : Ngắt ngoài 0.

- ∫´ 1: Ngắt ngoài 1.

- T0: Chân vào 0 của bộ Timer/Counter 0.

- T1: Chân vào 1 của bộ Timer/Counter 1.

- WR
´ : Ghi dữ liệu vào bộ nhớ ngoài.

- RD
´ : Đọc dữ liệu từ bộ nhớ ngoài.

- RST: Chân vào Reset, tích cực ở mức logic cao trong khoảng 2 chu kỳ máy.

14
- XTAL1: Chân vào mạch khuyếch đaị dao động

- XTAL2: Chân ra từ mạch khuyếch đaị dao động.

- PSEN
´ : Chân cho phép đọc bộ nhớ chương trình ngoài (ROM ngoài).

- ALE ((PROG)): Chân tín hiệu cho phép chốt địa chỉ để truy cập bộ nhớ ngoài, khi On-chip
xuất ra byte thấp của địa chỉ.Tín hiệu chốt được kích hoạt ở mức cao, tần số xung chốt = 1/6 tần số
dao động của bộ VI ĐIỀU KHIỂN. Nó có thể được dùng cho các bộ Timer ngoài hoặc cho mục
đích tạo xung Clock.Đây cũng là chân nhận xung vào để nạp chương trình cho Flash (hoặc
EEPROM) bên trong On-chip khi nó ở mức thấp.

- /EA/Vpp: Cho phép On-chip truy cập bộ nhớ chương trình ngoài khi /EA=0,
nếu/EA=1 thì On-chip sẽ làm việc với bộ nhớ chương trình nội trú (trường hợp cần truy
cập vùng nhớ lớn hơn dung lượng bộ nhớ chương trình nội trú, thì bộ nhớ chương trình
ngoài cũng được sử dụng). Khi chân này được cấp nguồn điện áp 12V (Vpp) thì On-chip
đảm nhận chức năng nạp chương trình cho Flash bên trong nó.

- Vcc: Cung cấp dương nguồn cho On-chip (+5V).

- GND: Nối đất

2.2.2 Tổ chức bộ nhớ


Bộ nhớ trong AT89C51 bao gồm ROM và RAM. RAM trong AT89C51 bao gồm
nhiều thành phần: phần lưu trữ đa dụng, phần lưu trữ địa chỉ hóa từng bit, các bank thanh
ghi và các thanh ghi chức năng đặc biệt.

AT89C51 có bộ nhớ theo cấu trúc Harvard: Có những vùng bộ nhớ riêng biệt cho
chương trình và dữ liệu. Các đặc tính cần chú ý là:

Các thanh ghi và các port xuất nhập đã được định vị (xác định) trong bộ nhớ và có
thể truy xuất trực tiếp giống như các cơ sở địa chỉ bộ nhớ khác.
Ngăn xếp bên trong Ram nội nhỏ hơn so với Ram ngoại.
RAM bên trong AT89C51 được phân chia như sau:
- Các bank thanh ghi có địa chỉ từ 00H đến 1FH.

15
- RAM địa chỉ hóa từng bit có địa chỉ từ 20H đến 2FH.
- RAM đa dụng từ 30H đến 7FH.
- Các thanh ghi chức năng đặc biệt từ 80H đến FFH.

AT89C51 có khả năng mở rộng bộ nhớ lên đến 64K byte bộ nhớ chương trình và
64K byte bộ nhớ dữ liệu ngoài. Do đó có thể dùng thêm RAM và ROM nếu cần. Bộ nhớ
dữ liệu ngoài là một bộ nhớ RAM được đọc hoặc ghi khi được cho phép của tín hiệu RD
và WR . Hai tín hiệu này nằm ở chân P3.7 (RD) và P3.6 (WR).

2.2.3. Các thanh ghi


Các thanh ghi nội của AT89C51 được truy xuất ngầm định bởi bộ lệnh. Các thanh
ghi trong AT89C51 được định dạng như một phần của RAM trên chip vì vậy mỗi thanh
ghi sẽ có một địa chỉ (ngoại trừ thanh ghi bộ đếm chương trình và thanh ghi lệnh vì các
thanh ghi này hiếm khi bị tác động trực tiếp). Cũng như R0 đến R7, AT89C51 có 21
thanh ghi có chức năng đặc biệt (SFR: Special Function Register) ở vùng trên của RAM
nội từ địa chỉ 80H đến FFH.

Tất cả 128 địa chỉ từ 80H đến FFH không được định nghĩa, chỉ có 21 thanh ghi có
chức năng đặc biệt được định nghĩa sẵn các địa chỉ.

Ngoại trừ thanh ghi A đa số các thanh ghi có chức năng đặc biệt SFR có thể địa chỉ
hóa từng bit hoặc byte.

+ Thanh ghi trạng thái chương trình (PSW: Prorgam Status Word).

+ Thanh ghi B.

+ Con trỏ Ngăn xếp SP (Stack Pointer):

+ Con trỏ dữ liệu DPTR (Data Pointer).

+ Các thanh ghi0 Port (Port Register).

+ Các thanh ghi Timer (Timer Register).

+ Các thanh ghi Port nối tiếp (Serial Port Register).

16
+ Các thanh ghi ngắt (Interrupt Register).

+ Thanh ghi điều khiển nguồn PCON (Power Control Register).

2.3. Một số chức năng đặc biệt

2.3.1. Bộ định thời


2.3.1.1. Thanh ghi điều khiển Timer TCON
Thanh ghi điều khiển bao gồm các bit trạng thái và các bit điều khiển bởi Timer 0
và Timer 1. Thanh ghi TCON có bit định vị. Hoạt động của từng bit được tóm tắt như sau:

Hình 2.4: Thanh ghi TCON

2.3.1.2. Thanh ghi mode timer (TMOD)


Thanh ghi TMOD gồm hai nhóm 4 bit là: 4 bit thấp đặt mode hoạt động cho Timer
0 và 4 bit cao đặt mode hoạt động cho Timer 1. 8 bit của thanh ghi TMOD được tóm tắt
như sau:

17
Hình 2.5: Thanh ghi TMOD

Với 2 bit M0 và M1 của TMOD để chọn mode cho Timer 0 hoặc Timer1.

TMOD không có bit định vị, nó thường được LOAD một lần bởi phần mềm ở đầu
chương trình để khởi động mode Timer. Sau đó sự định giờ có thể dừng lại và được khởi
động lại như thế bởi sự truy xuất các thanh ghi chức năng đặc biệt của Timer.

2.3.1.3. Các mode và cờ tràn


AT89C51 có hai Timer là Timer 0 và Timer 1. Ta dùng ký hiệu TLx và THx để chỉ
2 thanh ghi byte thấp và byte cao của Timer 0 hoặc Timer 1.

+ Mode Timer 13 bit (MODE 0):

18
Hình 2.6: Sơ đồ mode 0 của Timer

Mode 0 là mode Timer 13 bit, trong đó byte cao của Timer (THx) được đặt thấp và
5 bit trọng số thấp nhất của byte thấp Timer (TLx) đặt cao để hợp thành Timer 13 bit. 3
bit cao của TLx không dùng.

+ Mode Timer 16 bit (MODE 1):

Hình 2.7: Sơ đồ mode 1 của Timer

Mode 1 là mode Timer 16 bit, tương tự như mode 0 ngoại trừ Timer này hoạt
động như một Timer đầy đủ 16 bit, xung clock được dùng với sự kết hợp các thanh ghi
cao và thấp (TLx, THx). Khi xung clock được nhận vào, bộ đếm Timer tăng lên 0000H,
0001H, 0002H.... Và tràn sẽ xuất hiện khi có sự chuyển trên bộ đếm Timer từ FFFH sang
0000H và sẽ set cờ tràn Timer, sau đó Timer đếm tiếp. Cờ tràn là bit TFx trong thanh ghi
TCON mà nó sẽ được đọc hoặc ghi bởi phần mềm. Bit có trọng số lớn nhất (MSB) của
giá trị trong thanh ghi Timer là bit 7 của THx và bit có trọng số thấp nhất (LSB) là bit 0
của TLx. Nhược điểm là cờ tràn TF1 của Timer 1 không bị ảnh hưởng bởi các sự tràn của
Timer 1 bởi vì TF1 được nối với TH0. Khi timer 0 ở chế độ 3, timer 1 vẫn có thể sử dụng
bởi port nối tiếp như tạo tốc độ baud (vì nó không còn được nối với TF1).

+ Mode tự động nạp 8 bit (MODE 2):

Mode 2 là mode tự động nạp 8 bit, byte thấp TLx của Timer hoạt động như một
Timer 8 bit trong khi byte cao THx của Timer giữ giá trị Reload. Khi bộ đếm tràn từ FFH
sang 00H, không chỉ cờ tràn được set mà giá trị trong THx cũng được nạp vào TLx: Bộ
đếm được tiếp tục từ giá trị này lên đến sự chuyển trạng thái từ FFH sang 00H kế tiếp và
cứ thế tiếp tục.Mode này thì phù hợp bởi vì các sự tràn xuất hiện cụ thể mà mỗi lúc nghỉ
thanh ghi TMOD và THx được khởi động.

+ Mode tách timer (MODE 3) :

19
Mode 3 là mode Timer tách ra và là sự khác biệt cho mỗi Timer. Timer 0 ở mode 3
được chia làm 2 timer 8 bit. TL0 và TH0 hoạt động như những Timer riêng lẻ với sự tràn
sẽ set các bit TL0 và TF1 tương ứng. Timer 1 bị dừng lại ở mode 3, nhưng có thể được
khởi động bởi việc ngắt nó vào một trong các mode).

2.3.2. Tổ chức ngắt


Có 5 nguyên nhân tạo ra ngắt đối với 8051: hai ngắt do bên ngoài, hai ngắt do bộ
định thời và một ngắt do port nối tiếp. Khi ta thiết lập trạng thái ban đầu cho hệ thống, tất
cả các ngắt điều bị vô hiệu hóa và sau đó chúng được cho phép riêng lẻ bằng phần mềm.

Khi xảy ra hai hay nhiều ngắt đồng thời hoặc xảy ra một ngắt trong khi một ngắt
đang phục vụ, ta có hai sơ đồ xử lý ngắt: sơ đồ chuỗi vòng và sơ đồ hai mức ưu tiên.

2.3.2.1. Cho phép và không cho phép ngắt:


Mỗi một nguyên nhân ngắt được cho phép hoặc không cho phép riêng rẽ thông qua
thanh ghi chức năng đặc biệt định địa chỉ bit, thanh ghi cho phép ngắt IE (interrupt
enable) có địa chỉ byte là 0A8H. Mỗi một bit của thanh ghi này cho phép hoặc không cho
phép từng nguyên nhân ngắt riêng lẻ, thanh ghi IE đồng thời có một bit toàn cục (global)
cho phép hoặc không cho phép tất cả các ngắt.

Bảng 2.1 Thanh ghi cho phép ngắt IE

Bit Ký hiệu Địa chỉ bit Mô tả ( 0: không cho phép, 1: cho phép )

IE.7 EA AFH Cho phép/không cho phép ngắt toàn cục

IE.6 - AEH Không sử dụng

IE.5 ET2 ADH Cho phép ngắt do bộ định thời 2

IE.4 ES ACH Cho phép ngắt do port nối tiếp

IE.3 ET1 ABH Cho phép ngắt do bộ định thời 1

IE.2 EX1 AAH Cho phép ngắt từ bên ngoài ( ngắt ngoài 1 )

IE.1 ET0 A9H Cho phép ngắt do bộ định thời 0

20
IE.0 EX0 A8H Cho phép ngắt từ bên ngoài ( ngắt ngoài 0 )

2.3.2.2. Ưu tiên ngắt


Mỗi một nguyên nhân ngắt được lập trình riêng rẽ để có một trong hai mức ưu tiên
thông qua chức năng thanh ghi đặc biệt được định địa chỉ bit, thanh ghi ưu tiên ngắt IP
(interrupt priority), thanh ghi này có địa chỉ byte là 0B8H.

Bảng 2.2 Thanh ghi ưu tiên ngắt IP

K h i h ệ t h ố n g đ ư ợ c
Bit Ký hiệu Địa chỉ bit Mô tả (1: mức cao, 0: mức thấp)
ngắt ở
IP.7 - - Không sử dụng
mức ưu
tiên IP.6 - - Không sử dụng thấp.
IP.5 PT2 0BDH Ưu tiên ngắt do bộ định thời 2
C h ư ơ n g t r ì n h c h
nào IP.4 PS 0BCH Ưu tiên ngắt do port nối tiếp nên
luôn IP.3 PT1 0BBH Ưu tiên ngắt do bộ định thời 1 luôn
bị tạm
IP.2 PX1 0BAH Ưu tiên ngắt ngoài 1
dừng bởi
IP.1 PT0 0B9H Ưu tiên ngắt do bộ định thời 0
các ngắt.
Nếu IP.0 PX0 0B8H Ưu tiên ngắt ngoài 0 có
hai ngắt
xảy ra đồng thời thì ngắt nào có mức ưu tiên cao hơn sẽ được phục vụ trước.

Nếu có hai ngắt có cùng mức ưu tiên xuất hiện đồng thời, chuỗi vòng cố định sẽ
xác định ngắt nào được phục vụ trước. Chuỗi vòng này sẽ là: ngắt ngoài 0, ngắt do bộ
định thời 0, ngắt ngoài 1, ngắt do bộ định thời 1, ngắt do port nối tiếp, ngắt do bộ định
thời 2.

2.3.2.3. Xử lý ngắt
Khi có một ngắt xuất hiện và được CPU chấp nhận, chương trình chính bị ngắt.
Các thao tác sau đây xảy ra:

21
- Hoàn tất việc thực thi lệnh hiện hành

- Bộ đếm chương trình PC được cất vào stack

- Trạng thái của ngắt hiện hành được lưu giữ lại

- Các ngắt được chặn lại ở mức ngắt

- Bộ đếm chương trình PC được nạp địa chỉ vector của trình phục vụ ngắt ISR

- ISR được thực thi.

ISR được thực thi để đáp ứng công việc của ngắt. Việc thực thi ISR kết thúc khi
gặp lệnh RETI. Lệnh này lấy lại giá trị cũ của bộ đếm chương trình PC từ stack và phục
hồi trạng thái của ngắt cũ.

2.3.2.4. Các vector ngắt


Khi một ngắt được chấp nhận, giá trị được nạp cho bộ đếm chương trình PC
được gọi là vector ngắt. Vector ngắt là địa chỉ bắt đầu của trình phục vụ ngắt của
nguyên nhân ngắt tương ứng.

22
Bảng 2.3 Bảng véctor ngắt

Ngắt Địa chỉ vector Số thứ tự ngắt

Reset hệ thống 0000H -

Ngắt ngoài 0 0003H 0

Ngắt do bộ định thời 0 000BH 1

Ngắt ngoài 1 0013H 2

Ngắt do bộ định thời 1 001BH 3

Ngắt do port nối tiếp 0023H 4

Ngắt do bộ định thời 2 002BH 5

2.3.3. Truyền thông nối tiếp


Các máy tính truyền dữ liệu theo hai cách: Song song và nối tiếp. Trong truyền dữ
liệu song song thường cần rất nhiều đường dây dẫn chỉ để truyền dữ liệu đến một thiết bị
chỉ cách xa vài bước. Mặc dù trong các trường hợp như vậy thì nhiều dữ liệu được truyền
đi trong một khoảng thời gian ngắn bằng cách dùng nhiều dây dẫn song song, nhưng
khoảng cách thì không thể lớn được. Vì các đường cáp dài làm suy giảm thậm chí làm
méo tín hiệu. Ngoài ra, các đường cáp dài có giá thành cao. Vì những lý do này, để truyền
dữ liệu đi xa thì phải sử dụng phương pháp truyền nối tiếp.

Chức năng cơ bản của port nối tiếp là thực hiện việc chuyển đổi dữ liệu song song
thành nối tiếp khi phát và chuyển đổi dữ liệu nối tiếp thành song song khi thu.

Trong 8051 có một bộ truyền dữ liệu không đồng bộ (UART - Universal Asynchronous
serial Reveiver and Transmitter). Trước tiên ta sẽ tìm hiểu các khái niệm quan trọng
trong phương pháp truyền thông nối tiếp không đồng bộ (chỉ cần một đường truyền cho
một quá trình, khung dữ liệu đã được chuẩn hóa bởi các thiết bị nên không cần đường
xung nhịp báo trước dữ liệu).

23
Các mạch phần cứng bên ngoài truy xuất port nối tiếp thông qua các chân TxD
(phát dữ liệu) và RxD (thu dữ liệu), các chân này đa hợp với hai chân của port 3: P3.1
(TxD) và P3.0 (RxD).

Đặc trưng của port nối tiếp là hoạt động song công (full duplex), nghĩa là có khả
năng thu và phát đồng thời. Ngoài ra port nối tiếp còn có một đặc trưng khác, việc đệm dữ
liệu khi thu của port này cho phép một ký tự được nhận và lưu trữ trong bộ đệm thu trong
khi ký tự tiếp theo được nhận vào. Nếu CPU đọc ký tự thứ nhất trước khi ký tự thứ hai
được nhận đầy đủ, dữ liệu sẽ không bị mất.

Phần mềm sử dụng hai thanh ghi chức năng đặc biệt SBUF và SCON để truy xuất
port nối tiếp. Bộ đệm của port nối tiếp SBUF có địa chỉ byte là 99H, trên thực tế bao gồm
hai bộ đệm. Việc ghi lên SBUF sẽ nạp dữ liệu để phát và việc đọc SBUF sẽ truy xuất dữ
liệu đã nhận được. Điều này có nghĩa là ta có hai thanh ghi riêng rẽ và phân biệt: thanh
ghi phát (chỉ ghi) thanh ghi thu (chỉ đọc) hay bộ đệm thu.

Thanh ghi điều khiển port nối tiếp SCON có địa chỉ byte là 98H là thanh ghi được
định địa chỉ từng bit, thanh ghi này có các bit trạng thái và các bit điều khiển. Các bit điều
khiển sẽ thiết lập chế độ hoạt động cho port nối tiếp còn các bit trạng thái chỉ ra sự kết
thúc việc thu hoặc phát một ký tự. Các bit trạng thái được kiểm tra bởi phần mềm hoặc
được lập trình để tạo ra ngắt.

Tần số hoạt động của port nối tiếp hay còn gọi là tốc độ baud (baud rate) có thể cố
định hoặc thay đổi. Khi tốc độ baud thay đổi được sử dụng, bộ định thời 1 được sử dụng
để cung cấp xung clock tốc độ baud. Trên chip 8052 bộ định thời 2 cũng có thể được lập
trình để cung cấp xung clock tốc độ baud.

2.3.3.1. Thanh ghi điều khiển truyền thông nối tiếp (SCON)
Chế độ hoạt động của port nối tiếp được thiết lập bằng cách tác động lên thanh ghi
chế độ SCON của port nối tiếp ở địa chỉ byte 99H.

Bảng 2.4 Thanh ghi điều khiển port nối tiếp SCON

24
Bit Ký hiệu Địa chỉ Mô tả

SCON.7 SM0 9FH Bit 0 chọn chế độ của port nối tiếp.

SCON.6 SM1 9EH Bit 1 chọn chế độ của port nối tiếp.

Bit 2 chọn chế độ của port nối tiếp.


SCON.5 SM2 9DH
Bit này cho phép truyền thông đa xử lý.

Cho phép thu, bit này phải được set để nhận


SCON.4 REN 9CH
ký tự.

Bit phát 8, bit thứ 9 được phát ở chế độ 2 và 3,


SCON.3 TB8 9BH
được set và xoá bởi phần mềm.

SCON.2 RB8 9AH Bit thu 8, bit thứ 9 nhận được


Cờ ngắt phát, cờ này được set ngay khi kết
SCON.1 TI 99H
thúc việc phát một ký tự, xoá bởi phần mềm.

Cờ ngắt thu, cờ này được set ngay khi kết thúc


SCON.0 RI 98H
việc thu một ký tự, xoá bởi phần mềm.

Port nối tiếp của 8051 có 4 chế độ hoạt động, các chế độ được chọn bằng cách tác
động lên các bit SM0, SM1 trong thanh ghi SCON. Ba trong số các chế độ hoạt động cho
phép truyền không đồng bộ (asynchronous) trong đó mỗi ký tự được thu hoặc phát sẽ
cùng với một bit start và một bit stop tạo thành một khung (frame).

25
Bảng 2.5 Các chế độ hoạt động của port nối tiếp

SM0 SM1 Chế độ Mô tả Tốc độ baud

0 0 0 Thanh ghi dịch Cố định (tần số dao động/12)

0 1 1 UART 8 bit Thay đổi (thiết lập bởi bộ định thời)

1 0 2 UART 9 bit Cố định (tần số dao động /12, /64)

1 1 3 UART 9 bit Thay đổi (thiết lập bởi bộ định thời)

2.3.3.2. Hoạt động của UART


Ta thường dùng UART ở chế độ 1(có tốc độ baud thay đổi). Trong chế độ 1 port
nối tiếp hoạt động như một bộ thu phát không đồng bộ (universal asynchronous receiver
transmitter) có tốc độ baud thay đổi. UART là một bộ thu và phát dữ liệu nối tiếp với
mỗi một ký tự dữ liệu được đứng trước bởi một bit start (logic 0) và đứng sau bởi một bit
stop (logic 1). Thỉnh thoảng một bit chẵn lẻ được chèn vào giữa bit dữ liệu sau cùng và
bit stop.

Hoạt động chủ yếu của một UART là biến đổi dữ liệu phát từ song song thành nối
tiếp và biến đổi dữ liệu thu từ nối tiếp thành song song. Ở chế độ 1 ta có 10 bit được thu
trên chân RxD và 10 bit dược phát trên chân TxD cho mỗi ký tự dữ liệu, bao gồm 1 bit
start (luôn luôn là 0), 8 bit dữ liệu (bit LSB trước tiên) và 1 bit stop (luôn luôn là 1).

Khi hoạt động thu, bit stop được đưa đến bit RB8 của SCON. Với 8051 tốc độ
baud được thiết lập bởi tốc độ tràn (overflow rate) của bộ định thời 1 còn ở 8052 tốc độ
baud được thiết lập bởi tốc độ tràn của bộ định thời 1 hoặc 2 hoặc tổ hợp của cả hai (một
cho phát và một cho thu).

Việc phát được khởi động bằng cách ghi dữ liệu vào thanh ghi SBUF. Thời gian
phát của mỗi một bit là giá trị nghịch đảo của tốc độ baud (1/baud rate), cờ ngắt phát TI
được set bằng 1 ngay khi bit stop xuất hiện trên chân TxD.

Việc nhận được khởi động bởi một chuyển trạng thái từ 1 xuống 0 trên đường
RxD (bit start) và 8 bit dữ liệu thu được nạp vào thanh ghi SBUF.

26
2.3.3.3. Khởi động và truy xuất các thanh ghi
Cho phép thu:

Bit cho phép thu REN trong thanh ghi SCON phải được set bằng 1 bởi phần mềm
để cho phép nhận các ký tự.

Các cờ ngắt:

Các cờ ngắt thu RI và ngắt phát TI trong thanh ghi SCON đóng vai trò quan trọng
trong việc truyền dữ liệu nối tiếp của 8051. Cả hai bit đều được set bằng phần cứng
nhưng phải được xóa bằng phần mềm.

Cờ RI được set bằng 1 khi kết thúc việc nhận một ký tự và chỉ ra rằng bộ đệm thu
đầy. Nếu phần mềm muốn nhận một ký tự từ port nối tiếp, phần mềm phải chờ cho đến
khi RI được set bằng 1 kế đến phần mềm xóa RI và đọc ký tự từ SBUF.

Cờ TI được set bằng 1 khi kết thúc việc phát một ký tự và chỉ ra rằng bộ đệm phát
rỗng. Nếu phần mềm muốn phát một ký tự trước tiên phần mềm phải kiểm tra để biết
port nối tiếp đã sẵn sàng hay chưa.

2.3.3..4. Tốc độ Baud


Sử dụng bộ định thời 1 là xung clock tốc độ baud:

Kỹ thuật thường dùng để tạo xung clock tốc độ baud là thiết lập timer 1 hoạt động
ở chế độ 8 bit tự nạp lại (chế độ định thời 2) và đặt giá trị nạp thích hợp vào thanh ghi
TH1 để có tốc độ tràn đúng, từ đó tạo ra tốc độ baud.

Tốc độ baud = (Tốc độ tràn bộ định thời 1)/32

Ví dụ: Nếu tốc độ baud là 9600 thì tốc độ tràn của bộ định thời = 9600*32 =
307200 Hz. Và nếu mạch dao động là 11.059 MHz thì xung clock của bộ định thời =
11.059 M/12 = 921583 Hz và giá trị nạp cho TH1= 921583/307200 = 3

Bảng 2.6 Tốc độ baud của port nối tiếp

Tốc độ baud Tần số thạch anh Giá trị nạp cho TH1

27
9600 11.059 MHz -3(FDH)

2400 11.059 MHz -12(F4H)

1200 11.059 MHz -24(E8H)

2.4. Lập trình với vi điều khiển 8051

2.4.1. Ngôn ngữ lập trình cho 8051


Trong kỹ thuật vi xử lý nói chung, ngôn ngữ lập trình thường được chia làm 2 loại:
ngôn ngữ bậc thấp và ngôn ngữ bậc cao.

Ngôn ngữ bậc thấp là ngôn ngữ máy hoặc ngôn ngữ gần với máy. Ngôn ngữ máy
là ngôn ngữ ở bậc thấp nhất, chính là mã máy ở dạng nhị phân. Lập trình với ngôn ngữ
này đồng nghĩa với việc lập trình viên phải viết từng bit 0/1 cho từng mã lệnh cụ thể,
đương nhiên đó là việc rất vất vả và khó khăn. Kế đến là ngôn ngữ gần với máy, chính là
hợp ngữ (Assembly). Với ngôn ngữ này, lập trình viên có thể viết các lệnh cụ thể ở dạng
ký tự, tuân theo một tập hợp các ký tự nhất định gọi là tập lệnh. Nói cách khác, ở cấp độ
này, lập trình viên sẽ viết các lệnh ở dạng mã gợi nhớ (mnemonic) thay vì phải viết các
bit 0/1 cho các mã lệnh cụ thể. Trình hợp ngữ (Assembler) - một phần mềm trên máy tính
- sẽ đảm nhiệm việc dịch các lệnh do lập trình viên viết ở dạng mã gợi nhớ sang dạng mã
máy 0/1. Ngôn ngữ bậc cao là các ngôn ngữ gần với ngôn ngữ con người hơn, do đó việc
lập trình bằng các ngôn ngữ này trở nên dễ dàng và đơn giản hơn. Có thể kể đến một số
ngôn ngữ lập trình bậc cao như C, Basic, Pascal… trong đó C là ngôn ngữ thông dụng
hơn cả trong kỹ thuật vi xử lý. Về bản chất, sử dụng các ngôn ngữ này thay cho ngôn ngữ
bậc thấp là sự giảm tải cho lập trình viên trong việc nghiên cứu các tập lệnh và xây dựng
các cấu trúc giải thuật. Chương trình viết bằng ngôn ngữ bậc cao cũng sẽ được một phần
mềm trên máy tính gọi là trình biên dịch (Compiler) chuyển sang dạng hợp ngữ trước khi
chuyển sang mã máy. Mỗi loại ngôn ngữ có ưu và nhược điểm riêng.

Với hợp ngữ (đại diện cho ngôn ngữ bậc thấp):

28
- Ưu điểm: Mã máy sinh ra rất ngắn gọn, thời gian xửlý của CPU vì thế cũng được
giảm thiểu, trình hợp ngữ (Assembler) của các họ vi điều khiển đều miễn phí đối với
người sử dụng.

- Nhược điểm: Khó khăn trong việc tiếp cận với tập lệnh (tuy ở dạng mã gợi nhớ
nhưng vẫn chưa thực sự gần với ngôn ngữ con người), các cấu trúc giải thuật (if…else,
for…, switch…case…) hầu hết không có sẵn, vì vậy quá trình lập trình khó khăn, mất
nhiều thời gian và công sức, việc kế thừa và phát triển là gần như không thể.

Với ngôn ngữ C (đại diện cho ngôn ngữ bậc cao):

- Ưu điểm: Ngôn ngữ gần với ngôn ngữ con người, các cấu trúc giải thuật có sẵn,
do đó tạo sự thuận tiện, dễ dàng trong sự diễn đạt thuật toán, việc kế thừa và phát triển là
khả thi, tốn ít thời gian.

- Nhược điểm: Mã máy sinh ra thường dài hơn so với hợp ngữ (tất nhiên cũng còn
tùy vào năng lực của lập trình viên), thời gian xử lý của CPU vì thế cũng dài hơn, các
trình biên dịch (Compiler) tùy theo cấp độ tối ưu mà được thiết kế và bán với giá rất cao.

Trong thực tế hiện nay, các vi điều khiển có tài nguyên và bộ nhớ rất phong phú và
dồi dào. Mặt khác các trình biên dịch (Compiler) cũng được thiết kế ngày càng tối ưu, hỗ
trợ rất nhiều các thao tác xử lý giải thuật, cho phép trộn lệnh hợp ngữ vào những tình
huống yêu cầu khắt khe về mặt thời gian và lượng mã máy sinh ra. Chính vì thế yêu cầu
về tối giản mã máy khi lập trình không còn quá bức xúc như trước kia. Sử dụng ngôn ngữ
bậc cao giúp rút ngắn rất nhiều thời gian nghiên cứu, thiết kế sản phẩm nâng cao khả năng
kế thừa, phát triển, cải tiến các tính năng sản phẩm. Đó là lý do tại sao ngôn ngữ bậc cao
(điển hình là ngôn ngữ C) là sự lựa chọn của hầu hết những người tác nghiệp trên lĩnh vực
kỹ thuật vi xử lý.

2.4.2. Giới thiệu phần mềm lập trình Keil C và phần mềm mô phỏng Proteus
2.4.2.1. Phần mềm lập trình Keil C

29
Trong số các trình biên dịch C (C Compiler) cho họ vi điều khiển 8051, Keil C là
một trình biên dịch tối ưu, được sử dụng rộng rãi. Mục này chủ yếu hướng dẫn sử dụng
trình biên dịch này trong việc thiết kế phần mềm cho họ vi điều khiển 8051.

Tạo một Project mới

Tiếp đó gõ tên project vào hộp thoại. Chọn đường dẫn và bấm OK.

Chọn tiếp loại vi điều khiển sẽ sử dụng (trong trường hợp này là AT89C51).

Chọn câu trả lời “No” khi được hỏi “Copy Standart Startup Code to Project Folder
and Add File to Project?”

Tạo một file mới

Sau khi Keil tạo cho ta một file mặc định dạng Text, hãy Save File lại dưới dạng
mong muốn (*.c nếu là file mã nguồn, hoặc *.h nếu là file header). Một Project chủ yếu
sử dụng hai loại file nói trên. Tiếp đó thực hiện soạn thảo các file theo ý muốn.

Biên dịch Project

Để tạo ra file mã máy dạng *.hex nạp vào chip, click chuột phải như hình vẽ.

Trong hộp thoại hiện ra, hãy check vào Creat Hex File như chỉ dẫn.

Cuối cùng chọn theo Menu như hình ảnh, hoặc bấm phím F7. File mã máy *.hex
tạo ra sẽ nằm trong cùng một thư mục với các file khác của Project.

Khi đã có được file *.hex ta có thể dùng để nạp vào vi điều khiển bằng các mạch
nạp và phần mềm nạp tương ứng (phần mềm nạp thường đi kèm với từng mạch nạp tương
ứng). Ngoài ra file *.hex cũng có thể dùng để chạy mô phỏng bằng phần mềm mô phỏng
Protues, giúp ta kiểm soát lỗi trước khi tiến hành triển khai làm sản phẩm thực tế.

2.4.2.2. Lập trình 8051 với Keil C

a. Cấu trúc một chương trình

Gồm 4 phần chính (riêng lập trình theo hướng hệ điều hành sẽ trình bày ở phần
sau.)

30
1. Khai báo chỉ thị tiền xử lý

2. Khai báo các biến toàn cục

3. Khai báo nguyên mẫu các hàm

4. Xây dựng các hàm và chương trình chính

Ví dụ:

// Khai báo chỉ thị tiền xử lý:

#include<AT89x51.h>

#include<string.h>

#define Led1 P1_0

//*********************************

// Khai báo biến toàn cục:

Unsigned char data dem;

Unsigned int xdata X;

//*********************************

// Khai báo nguyên mẫu hàm

Void delay(unsigned int n);

bit tinh(unsigned int a);

//*********************************

// Xây dựng các hàm và chương trình chính:

 void delay(unsigned int n)

Khai báo biến cục bộ;

Mã chương trình trễ;

31
}

Void main() // Chương trình chính

Khai báo biến cụ bộ;

Mã chương trình chính;

Bit tinh (unsigned int a)

Khai báo biến cục bô;

Mã chương trình tinh với tham số a;

b. Một số lưu ý khi lập trình với Keil C

1. Khi sử dụng loại vi điều khiển 8051 nào (đã lựa chọn trong khi tạo Project mới)
thì phải sử dụng file header của loại đó. Trong trường hợp này ta sử dụng file
“AT89X51.H” cho vi điều khiển AT89C51. Các file header được tìm trong thư mục …
C51\INC\ của Keil C đã cài ra.

2. Định nghĩa hằng số trong bộ nhớ chương trình

unsigned char code<tên biến>;

Ví dụ định nghĩa một mảng 3 hằng số:

unsigned char array[3] = {1,2,3};

3. Định nghĩa các chương trình con phục vụ ngắt

void<tên chương trình> (void) interrupt<tên Vector ngắt>

<tên chương trình> do lập trình viên tùy ý đặt.

<tên Vector ngắt> được tra ở phần cuối file header (AT89X52.H).

32
4. Không nên viết lệnh ở dạng biểu thức dài mà nên tách ra thành từng phép tính
nhỏ thực hiện lần lượt.

5. Nên dùng nhiều chú giải ở sau kí hiệu “ // ” hoặc ở giữa “ /* ” và “ */ ” sao cho
hợp lý để dễ theo dõi và sửa chữa khi viết code.

2.4.2.3. Phần mềm mô phỏng Proteus

Sau khi cài đặt Proteus trên máy, ta sử dụng phần mềm tiến hành mô phỏng kết
quả thu được của việc lập trình. Dưới đây là hướng dẫn một bài lập trình đơn giản sử
dụng vi điều khiển AT89C51 để điều khiển đèn LED.

Bước 1: Khởi động chương trình ISIS bằng cách chọn START -> All Program -> Proteus
7 Professional -> ISIS 7 Professional từ màn hình desktop của Window . Màn hình làm
việc của ISIS xuất hiện với đầy đủ các menu lệnh cũng như các thanh công cụ hỗ trợ cho
việc tạo và mô phỏng mạch điện.

Bước 2: Lấy linh kiện từ thanh công cụ nhấp chọn vào Component Mode chọn tiếp Pick
from Libraries cửa sổ Pick Devices hiện ra. Tại Key words gõ "AT89" để lấy IC
AT89C51. Chọn AT89C51và nhấp đúp để lấy linh kiện ra ngoài.

33
Tiếp theo lấy thạch anh làm tương tự, trong ô Keywords gõ "Crystal" chọn Crystal,
nhấp đúp để lấy linh kiện. Với LED trong Keywords gõ LED, kéo xuống dưới, chọn LED
tùy ý sao cho dễ quan sát, lấy trở, tụ cũng tương tự như vậy.

Sau khi hoàn tất lấy linh kiện các linh kiện đã được chọn sẽ nằm bên trái vùng làm
việc để đưa linh kiện ra ngoài vùng làm việc ta nhấp vào linh kiện đó rồi đưa chuột ra
vùng làm việc, nhấp trái chuột linh kiện đó sẽ xuất hiện, khi hoàn thành ta đc một vùng
làm việc với các linh kiện.

34
Tiếp theo tiến hành lấy nguồn, đất. Từ thanh công cụ, ta chọn Terminal Mode tại
cửa sổ nhỏ bên trái vùng làm việc ta chọn GROUND (GND) hoặc POWER (VCC).

Bước 3: Di chuyển và nối dây linh kiện. Để di chuyển ta nhấp phải chuột một lần vào linh
kiện đó, nó sẽ sáng lên, tiếp đến nhấp trái chuột, giữ và kéo rê chuột qua chỗ cần di
chuyển tới rồi thả chuột. Nếu không muốn chọn linh kiện đó ta nhấp phải chuột 2 lần lên
linh kiện hoặc nhấn phải chuột và nhấn phím Delete trên bàn phím.

Để xoay linh kiện ta nhấp phải chuột vào linh kiện đó rồi chọn một trong các mục
để xoay linh kiện.

35
Sau khi hoàn thành việc sắp xếp linh kiện ta tiến hành việc nối dây, để nối dây hãy
di chuyển con trỏ chuột đến chân linh kiện thứ nhất, một ô vuông màu đỏ bằng nét đứt
xuất hiện tại đầu chân linh kiện. Nhấn giữ chuột và di chuyển chuột tới chân linh kiện thứ
2 sao cho tại đầu chân linh kiện này cũng xuất hiện một ô vuông, thả chuột ra thì một dây
nối được tự động kết nối và sắp xếp đường đi của dây nối. Nếu nối dây bị sai ta nhấp phải
chuột hai lần vào dây đó để xóa bỏ.

Bước 4: Nạp chương trình vào vi điều khiển và chạy mô phỏng. Sau khi hoàn thành việc
nối dây ta được một mạch điện hoàn chỉnh:

36
Để tiến hành mô phỏng ta phải nạp chương trình vào vi điều khiển, nhấp phải
chuột vào IC AT89C52 rồi nhấp trái chuột cửa sổ Edit Component hiện ra:

Component Reference (tham chiếu linh kiện) mỗi linh kiện lấy ra ngoài vùng làm
việc ngoài tên chính thức của linh kiện (VD: AT89C52) chương trình còn đặt thêm một
tên khác để quản lý, tên này bắt đầu từ U1, rồi tới U2, U3... Component Value là tên linh
kiện Clock Frequency là tần số bộ tạo dao động cho IC, nó ảnh hưởng tới tốc độ xử lý dữ
liệu của vi điều khiển, ở đây mặc định là 12MHz, có thể thay đổi giá trị này nhưng cần

37
phải tính toán. PCB Package hình dáng chân linh kiện, liên quan tới việc vẽ mạch in.
Program File chứa tên chương trình cần chạy mô phỏng, chương trình này phải ở dạng
file HEX, ở mục này ta cần chỉ đường dẫn đến file HEX cần chạy mô phỏng.

Trở về màn hình chính của chương trình ISIS và thực hiện mô phỏng bằng cách
chuột trái vào các biểu tượng dưới góc trái màn hình.

Sau quá trình chạy mô phỏng ta sẽ kiểm tra được tính chính xác của phần code từ
đó quay lại phần mềm lập trình để hiệu chỉnh lại cho đến khi đạt được kết quả mong
muốn.

38
2.4.3. Lệnh số học lệnh logic và các cổng vào ra
Khác với khi lập trình bằng ngôn ngữ Assembly ta thường dùng các lệnh số học,
logic trực tiếp với các thanh ghi thì khi lập trình bằng ngôn ngữ C cho 8051 ta thường
xuyên làm việc với các biến. Các lệnh logic và số học cũng được thay bằng các kí hiệu,
phép tính trực quan, thân thiện với người sử dụng hơn các lệnh gợi nhớ trong ngôn ngữ
Assembly.

Phép toán Ý nghĩa Ví dụ

+ Phép cộng  X = a+b;

- Phép trừ  X = a-b;

* Phép nhân  X = a*b;

/ Phép chia lấy phần nguyên X = a/b;

% Phép chia lấy phần dư  X = a%b;

Phép toán Logic

Chức năng Phép toán

AND &&

OR ||

NOT !

Các phép toán so sánh:

Phép toán ý nghĩa Ví dụ

>  So sánh lớn hơn a>b

4>5 sẽ trả ra giá trị 0

>= So sánh lớn hơn hoặc bằng a>=b

6>=2 sẽ trả ra giá trị 1

39
<  So sánh nhỏ hơn a<b 

6<7 sẽ trả ra giá trị 1

<= So sánh nhỏ hơn hoặc bằng a<=b

8<=5 sẽ trả ra giá trị 0

== So sánh bằng nhau a==b

6==6 sẽ trả ra giá trị 1

!= So sánh khác nhau a!=b

9!=9 sẽ trả ra giá trị 0

Phép toán thao tác Bit

Phép toán Ý nghĩa Ví dụ

& Phép và (AND) Bit_1 & Bit_2

| Phép hoặc (OR)  Bit_1 | Bit_2

! Phép đảo (NOT)  !Bit_1

^ Phép hoặc loại trừ (XOR) Bit_1 ^ Bit_2

<<  Dịch trái  a<<3

>>  Dịch phải  a>>4

~ Lấy bù theo bit ~a

Phép toán kết hợp

Phép toán Ví dụ

+= a+=5 <=> a=a+5

-= a-=5 <=> a=a-5

*= a*=5 <=> a=a*5

/= a/=5 <=> a=a/5

40
%= a%=5 <=> a=a%5

Các cổng vào ra từ P0 đến P3 thường xuyên được sử dụng theo cổng hoặc riêng rẽ
từng bit. Ví dụ: P1^1=0; P2=0x01;

2.4.4. Lập trình bộ đếm, bộ định thời


2.4.4.1. Các bước lập trình ở chế độ 1 và chế độ 0

Để tạo ra một độ trễ thời gian dùng chế độ 1 của bộ định thời thì cần phải thực hiện các
bước dưới đây:

1. Nạp giá trị TMOD cho thanh ghi báo độ định thời nào (Timer0 hay Timer1)
được sử dụng và chế độ nào được chọn.

2. Nạp các thanh ghi TL và TH với các giá trị đếm ban đầu.

3. Khởi động bộ định thời.

4. Duy trì kiểm tra cờ bộ định thời TF bằng một vòng lặp để xem nó được bật lên 1
không. Thoát vòng lặp khi TF được lên cao.

5. Dừng bộ định thời.

6. Xoá cờ TF cho vòng kế tiếp.

7. Quay trở lại bước 2 để nạp lại TL và TH.

Chế độ 0 hoàn toàn giống chế độ 1 chỉ khác là bộ định thời 16 bit được thay
bằng13 bit. Bộ đếm 13 bit có thể giữ các giá trị giữa 0000 đến FFFF trong TH - TL. Do
vậy khi bộ định thời đạt được giá trị cực đại của nó là FFFFH thì nó sẽ quay trở về 0000
và cờ TF được bật lên.

2.4.4.2. Các bước lập trình cho chế độ 2

Để tạo ra một thời gian trễ sử dụng chế độ 2 của bộ định thời cần thực hiện các
bước sau:

41
1. Nạp thanh ghi giá trị TMOD để báo bộ định thời gian nào (Timer0 hay Timer1)
được sử dụng và chế độ làm việc nào của chúng được chon.

2. Nạp lại thanh ghi TH và TL với giá trị đếm ban đầu.

3. Khởi động bộ định thời.

4. Duy trì kiểm tra cờ bộ định thời TF bằng cách sử dụng một vòng lặp để xem nó
đã được bật chưa. Thoát vòng lặp khi TF lên cao.

5. Dừng bộ định thời.

6. Xoá cờ TF.

7. Quay trở lại bước 3. Vì chế độ 2 là chế độ tự nạp lại.

2.4.4.3. Một số điểm lưu ý

a. Tần số thạch anh

Một khái niệm quan trọng cần phải nói đến là sự kiện “tràn” (overflow). Nó được
hiểu là sự kiện bộ đếm đếm vượt quá giá trị tối đa mà nó có thể biểu diễn và quay trở về
giá trị 0. Với bộ đếm 8 bit, giá trị tối đa là 255 (tương đương với FFH) và là 65535
(FFFFH) với bộ đếm 16 bit.

Nguồn đồng hồ cho chế độ trễ thời gian là tần số thạch anh của 8051. Điều đó có
nghĩa là độ lớn của tần số thạch anh đi kèm với 8051 quyết định tốc độ nhịp của các bộ
định thời trên 8051. Tần số của bộ định thời luôn bằng 1/12 tần số của thạch anh gắn với
8051.

Tần số thạch anh Tần số bộ định thời Chu kỳ bộ định thời

20MHz 20MHz/12=1,6666MHz 1/1,6666MHz=0,6us

12MHz 12MHz/12=1MHz 1/1MHz=1us

11,0592MHz 11,0592MHz/12=0,9216MHz 1/0,9216MHz=1,085us

Mặc dù các hệ thống 8051 có thể sử dụng tần số thạch anh từ 10 đến 40MHz, song
ta chỉ tập trung vào tần số thạch anh 11,0592MHz. Lý do đằng sau một số lẻ như vậy là

42
tốc độ baud đối với truyền thông nối tiếp của 8051. Tần số XTAL = 11,0592MHz cho
phép hệ thống 8051 truyền thông với PC mà không có lỗi.

b. Tìm các giá trị cần được nạp vào bộ định thời

Giả sử rằng ta biết lượng thời gian trễ mà ta cần thì câu hỏi đặt ra là làm thế nào
để tìm ra được các giá trị cần thiết cho các thanh thi TH và TL. Để tính toán các giá trị
cần được nạp vào các thanh ghi TH và TL ta hãy nhìn vào ví dụ sau với việc sử dụng tần
số dao động XTAL = 11. 0592MHz đối với hệ thống 8051.

Các bước để tìm các giá trị của các thanh ghi TH và TL:

1. Chia thời gian trễ cần thiết cho 1.085us

2. Thực hiện 65536 - n với n là giá trị thập phân nhận được từ bước 1.

3. Chuyển đổi kết quả ở bước 2 sang số Hexa: ta có YYXX là giá trị Hexa ban đầu
cần phải nạp vào các thanh ghi bộ định thời.

4. Đặt TL = XX và TH = YY.

c. Công thức tính toán độ trễ sử dụng chế độ 1 đối với tần số thạch anh

f tính theo MHz

Tính theo số Hexa:

(FFFF - YYXX + 1)*12/f (us) trong đó YYXX là các giá trị khởi tạo của TH, TL
tương ứng. Lưu ý rằng các giá trị YYXX là theo số Hexa.

Tính theo số thập phân:

Chuyển đổi các giá trị YYXX của TH, TL về số thập phân để nhận một số thập
phân NNNNN sau đó lấy (65536 – NNNNN)*12/f (us).

d. Trường hợp khi bit GATE = 1 trong TMOD

Tất cả nội dung ta vừa trình bày trong bài này đều giả thiết GATE = 0. Khi GATE = 0
thì bộ đếm/bộ định thời được khởi động bằng các lệnh Set bit TR0 hoặcTR1.

43
Nếu GATE = 1 thì việc khởi động và dừng bộ đếm/bộ định thời được thực hiện từ
bên ngoài qua chân P3.2 (INT0) và P3.3 (INT1) đối với Timer/ counter 0 và Timer/
counter 1 tương ứng. Phương pháp điều khiển bằng phần cứng để dừng và khởi động bộ
đếm/ bộ định thời này có thể có rất nhiều ứng dụng.

44
Ví dụ:

Giả sử tần số XTAL = 11.0592MHz. Hãy tìm các giá trị cần được nạp vào các thanh
ghi vào các thanh ghi TH và TL nếu ta muốn độ thời gian trễ là 5ms.

Lời giải:

Vì tần số XTAL = 11.0592MHz nên bộ đếm tăng sau mỗi chu kỳ 1.085s. Điều đó có
nghĩa là phải mất rất nhiều khoảng thời gian 1,085s để có được một xung 5ms. Để có
được ta chia 5ms cho 1.085s và nhận được số n = 4608 nhịp. Để nhận được giá trị cần
được nạp vào TL và TH thì ta tiến hành lấy 65536 trừ đi 4608 bằng 60928. Ta đổi số này
ra số hex thành EE00H. Do vậy, giá trị nạp vào TH là EE Và TL là 00.

void delay(void) //định nghĩa hàm delay

TMOD=0x01;   //chọn timer0 chế độ 1 16Bit

TL0=0x00; //nạp giá trị cho TL0

TH0=0xEE; //nạp giá trị cho TH0

TR0=1;   //khởi động timer0

while(!TF0){} //vòng lặp kiểm tra cờ TF0

TR0=0;   //ngừng timer0

TF0=0;   //xóa cờ TF0

2.4.5. Lập trình truyền thông nối tiếp


2.4.5.1. Lập trình 8051 để truyền dữ liệu nối tiếp

1. Nạp thanh ghi TMOD giá trị 20H: báo rằng sử dụng Timer1 ở chế độ 2 để thiết
lập chế độ baud.

45
2. Nạp thanh ghi TH1 các giá trị phù hợp để thiết lập chế độ baud truyền dữ liệu
nối tiếp.

3. Nạp thanh ghi SCON giá trị 50H báo chế độ nối tiếp 1 để đóng khung 8 bit dữ
liệu, 1 bit Start và 1 bit Stop.

4. Bật TR1=1 để khởi động Timer1.

5. Xoá bit cờ truyền dữ liệu: TI=0.

6. Byte ký tự cần phải truyền được ghi vào SBUF.

7. Bit cờ truyền TI được kiểm tra bằng một vòng lặp để đợi đến lúc dữ liệu được
truyền xong (cờ TI=1).

8. Để truyền ký tự tiếp theo quay trở về bước 5.

2.4.5.2. Để lập trình 8051 nhận các byte ký tự nối tiếp

1. Nạp giá trị 20H vào thanh ghi TMOD: báo sử dụng bộ Timer1, chế độ 2 (8 bit,
tự động nạp lại) để thiết lập tốc độ baud.

2. Nạp TH1 các giá trị phù hợp để thiết lập tốc độ baud.

3. Nạp giá trị 50H vào thanh ghi SCON để báo sử dụng chế độ truyền nối tiếp 1:
dữ liệu được đóng gói bởi 8 bit dữ liệu, 1 bit Start và 1 bit Stop.

4. Bật TR1=1 để khởi động Timer1.

5. Xoá cờ nhận RI: RI=0.

6. Bit cờ nhận RI được kiểm tra bằng một vòng lặp để đảm bảo toàn bộ ký tự đã
được nhận đủ (khi RI=1).

7. Khi RI được thiết lập thì trong SBUF đã có 1 byte. Các nội dung của nó cần
được đọc ngay để tránh mất mát.

8. Để nhận một ký tự tiếp theo quay trở về bước 5.

46
2.4.6. Lập trình ngắt
2.4.6.1. Khai báo ngắt trong chương trình Keil

Trong lập trình C trên Keil c cho 8051, ta khai báo trình phục vụ ngắt theo cấu
trúc sau:

Void  Name (void) interrupt X   //( X: là số thứ tự của ngắt )

// chương trình phục vụ ngắt

Khi đó địa chỉ ngắt sẽ được tự động tính.

2.4.6.2. Lập trình ngắt bộ định thời

ta đã biết rằng cờ bộ định thời TF được bật lên cao khi bộ định thời đạt giá trị cực
đại và quay về 0 (Roll - over). Nếu ta kiểm tra cờ TF bằng một vòng lặp. Trong khi thăm
dò cờ TF thì ta phải đợi cho đến khi cờ TF được bật lên. Vấn đề với phương pháp này là
trong khi chờ cờ TF được bật vi điều khiển không thể làm được bất kỳ việc gì khác. Hoặc
nếu ta dùng lệnh “ if ” thì sự kiện cờ TF được bật cũng không được xử lý ngay.

Sử dụng các ngắt sẽ giải quyết được vấn đề này. Nếu bộ ngắt định thời trong thanh
ghi IE được phép thì mỗi khi nó quay trở về 0 bộ vi điều khiển sẽ bị ngắt, bất chấp nó
đang thực hiện việc gì và nhảy tới bảng vector ngắt để phục vụ ISR. Bằng cách này thì bộ
vi điều khiển có thể làm những công việc khác cho đến khi nó được thông báo rằng bộ
định thời đã quay về 0.

Ví dụ:

Hãy viết chương trình nhận liên tục dữ liệu 8 Bit ở cổng P0 và gửi nó đến cổng P1
trong khi nó cùng lúc tạo ra một sóng vuông chu kỳ 200ms trên chân P2.1. Hãy sử dụng
bộ Timer0 để tạo ra sóng vuông, tần số của 8051 là XTAL = 11.0592MHz.

Lời giải:

47
Chu kỳ 200ms, vậy nửa chu kỳ là 100ms.

Ta có: 100ms/1,085ms=92.

Suy ra giá trị cần nạp cho timer0 là: -92 <=> A4H. Ta sử dụng timer0 8 bit.

#include<at89x51.h>  //khai báo thu viện cho VÐK 89x51

main()

TMOD=0x02;   //chọn timer0, chế độ 2, 8Bit tự nạp lại

TL0=0xA4; //nạp giá trị cho TL0

TH0=0xA4;   //nạp giá trị cho TH0

TR0=1; //khởi động timer0

IE=0x82; //cho phép ngắt timer0

while(1)   //vòng lặp vô hạn

P1=~P0; //Cập nhật giá trị cho cổng P1 từ P0.

void songvuong(void) interrupt 1 //Khai báo trình phục vụ ngắt cho timer0

TR0=0;   //Ngừng timer0

P2_1=~P2_1; //Đảo trạng thái chân P2_1.

TR0=1;   //Khởi động timer0

//Không cần xóa cờ TF0, 8051 tự động xóa.}

48
2.4.6.3. Lập trình các ngắt phần cứng bên ngoài

  Bộ vi điều khiển 8051 có 2 ngắt phần cứng bên ngoài ở chân P3.2 và chân P3.3 gọi là
ngắt INT0 và INT1. Và chúng được phép và bị cấm bằng việc sử dụng thanh ghi IE. Có
hai mức kích hoạt cho các ngắt phần cứng ngoài: ngắt theo mức và ngắt theo sườn.

a. Ngắt theo mức

  Ở chế độ ngắt theo mức thì các chân INT0 và INT1 bình thường ở mức cao và nếu một
tín hiệu ở mức thấp được cấp tới thì chúng ghi nhãn ngắt. Sau đó bộ vi điều khiển dừng
tất cả mọi công việc nó đang thực hiện và nhảy đến bảng vector ngắt để phục vụ ngắt.
Đây là chế độ ngắt mặc định khi cấp nguồn cho 8051.

Tín hiệu mức thấp tại chân INTx phải được lấy đi trước khi thực hiện lệnh cuối
cùng của trình phục vụ ngắt, nếu không một ngắt khác sẽ lại được tạo ra, và vi điều khiển
sẽ thực hiện ngắt liên tục.

Có một cách khác để giải quyết triệt để vấn đề trên: đó là sử dụng ngắt theo
sườn.Khi đó với mỗi 1 lần ấn phím, dù thế nào ngắt cũng chỉ thực hiện 1 lần.

Do vậy, để giải quyết vấn đề này thì chân INT1 phải được đưa lên cao trước thời điểm
lệnh cuối cùng của ngắt được thực hiện.

Có một cách khác để giải quyết triệt để vấn đề trên: đó là sử dụng ngắt theo
sườn.Khi đó với mỗi 1 lần ấn phím, dù thế nào ngắt cũng chỉ thực hiện 1 lần.

b. Ngắt theo sườn

Ngắt theo sườn là ngắt sẽ xảy ra khi có một sườn âm xuất hiện trên các chân ngắt
của vi điều khiển. Điều này làm cho ngắt theo sườn khắc phục được nhược điểm của ngắt
theo mức. Để kích hoạt chế độ ngắt theo sườn thì ta phải viết chương trình cài đặt cho
các bit IT0 và IT1 của thanh ghi TCON. Đây là các bit xác định kiểu ngắt theo sườn xung
hay theo mức xung của các ngắt phần cứng trên chân INT0 và INT1 tương ứng. Khi bật
lại nguồn cả 2 bit này đều có mức 0 để biến chúng thành ngắt theo tín hiệu mức thấp. Ta

49
có thể điều khiển một trong số chúng lên cao để chuyển ngắt phần cứng bên ngoài
thành ngắt theo sườn.

Ví dụ:

Giả sử chân INT1 được nối đến công tắc, chân còn lại của công tắc nối đất. Mỗi khi
nhấn công tắc phải bật một đèn LED ở chân P1.3 sáng (bình thường Led tắt). Nếu công
tắc được giữ ở trạng thái thấp đèn LED phải sáng liên tục. Biết đèn Led được nối chân âm
vào vi điều khiển.

Lời giải:

#include<at89x51.h>      //Khai báo thư viện cho VĐK 89x51

main() //Chương trình chính

IE=0x84 //cho phép ngắt ngoài 1

while(1)   //vòng lặp vô hạn

//không làm gì

void congtac(void) interrupt 2 //Khai báo trình phục vụ ngắt ngoài 1

{   //(mặc định là ngắt theo mức)

int a=50000; //Biến đếm trễ

P1_3=0;   //Cho Led sáng

while(a--){}  //Trễ cho Led sáng

P1_3=1;   //Tắt Led

//Không cần xóa cờ ngắt}

50
2.4.6.4. Lập trình ngắt truyền thông nối tiếp

Trong phương pháp thăm dò thì ta phải đợi cho cờ (TI hay RI) bật lên và trong lúc
chờ đợi thì ta không thể làm gì được cả.

Còn trong phương pháp ngắt thì ta được báo khi 8051 đã nhận được một byte hoặc
nó sẵn sàng truyền byte kế tiếp và ta có thể làm các công việc khác trong khi chờ truyền
thông nối tiếp được thực hiện.

Trong 8051 chỉ có một ngắt dành riêng cho truyền thông nối tiếp. Ngắt này được dùng
cho cả truyền và nhận dữ liệu. Nếu bit ngắt truyền thông ES - IE.4 trong thanh
ghi IE được phép, thì khi 1 trong 2 cờ RI hoặc TI bật lên, 8051 sẽ nhận được ngắt và nhảy
đến địa chỉ trình phục vụ ngắt dành cho truyền thông nối tiếp 0023H trong bảng vector
ngắt để thực hiện nó. Trong trình ISR này ta phải kiểm tra các cờ TI và RI để xem cờ nào
gây ra ngắt để đáp ứng một cách phù hợp.

Ví dụ:

Hãy viết chương trình ngắt để 8051 nhận dữ liệu từ cổng nối tiếp COM và gửi đến
cổng P0. Giả thiết tần số XTAL là 11.0592MHz và tốc độ baud 9600.

Lời giải:

#include<at89x51.h> //Khai báo thư viện cho 89c51

main() //Chương trình chính

TMOD=0x20; //Chọn Timer1, chế độ 2

TH1=0xFD; //Cài đặt tốc độ baud 9600

SCON=0x50; //0101 0000: Chọn chế độ 1, Cho phép nhận

TR1=1; //Khởi động Timer1

IE=0x90; //Cho phép ngắt truyền thông nối tiếp

while(1) //Vòng lặp vô hạn

51
{

void nhandulieu(void) interrupt 4 //Khai báo ISR truyền thông nối tiếp

if(RI==1)    //Kiểm tra có phải là ngắt nhận dữ liệu không

P0=SBUF; //Gửi dữ liệu đến cổng P0

RI=0; //Xóa cờ nhận dữ liệu nối tiếp RI

Lưu ý:

Điều gì xảy ra nếu 8051 đang thực hiện một trình phục vụ ngắt thuộc một ngắt nào đó
thì lại có một ngắt khác được kích hoạt? Trong những trường hợp như vậy thì 1 ngắt có
mức ưu tiên cao hơn có thể ngắt 1 ngắt có mức ưu tiên thấp hơn. Đây gọi là ngắt trong
ngắt. Trong 8051 một ngắt ưu tiên thấp có thể bị ngắt bởi một ngắt có mức ưu tiên cao
hơn chứ không bị ngắt bởi một ngắt có mức ưu tiên thấp hơn. Mặc dù tất cả mọi ngắt đều
được chốt và giữ bên trong nhưng không có ngắt mức thấp nào được CPU quan tâm ngay
tức khắc, nếu 8051 chưa kết thúc phục vụ các ngắt mức cao.

52
Chương 3: HỌ VI ĐIỀU KHIỂN AVR

3.1. Giới thiệu chung

Hình 3.1: Vi điều khiển AVR

Vi điều khiển AVR do hãng Atmel (Hoa Kỳ) sản xuất được giới thiệu lần đầu tiên
năm 1996. AVR có rất nhiều dòng khác nhau bao gồm dòng Tiny (như ATtiny 13, ATtiny
22...) có kích thước bộ nhớ nhỏ, ít bộ phận ngoại vi, rồi đến dòng AVR (AT90S8535,
AT90S8515...) có kích thước bộ nhớ vào loại trung bình và mạnh hơn là dòng Mega
(ATmega 8, ATmega 16, Atmega 32, ATmega 128…) với bộ nhớ có kích thước vài
Kbyte đến vài trăm Kbyte cùng với bộ ngoại vi đa dạng được tích hợp cả bộ LCD trên
chip (dòng LCD AVR). Tốc độ của dòng Mega cũng cao hơn so với các dòng khác. Sự
khác nhau cơ bản giữa các dòng chính là cấu trúc ngoại vi, còn nhân thì vẫn như nhau.

Trong tài liệu này ta sử dụng vi điều khiển ATmega16 để làm đối tượng tìm hiểu
vi điều khiển AVR, từ đó nắm được cách làm việc nói chung với những vi điều khiển
khác trong dòng AVR.

ATmega16 là một loại vi điều khiển có nhiều tính năng đặc biệt thích hợp cho việc
giải quyết những bài toán điều khiển trên nền vi xử lý. Đây là vi điều khiển 8bit dựa trên
kiến trúc RISC. Với khả năng thực hiện mỗi lệnh nhanh, tiêu thụ năng lượng thấp.
ATmega16 có cấu trúc RISC (Reduced Instructions Set Computer) với:

+ 131 lệnh, hầu hết được thực thi trong 1 chu kì xung nhịp.

53
+ 32x8 thanh ghi đa dụng.

+ Tốc độ làm việc 16MPIS, với thạch anh 16MHz.

+ Trong chip có 2 chức năng hỗ trợ gỡ rối và lập trình.

Bộ nhớ:

+ 16 KB ISP Flash với khả năng 10000 lần ghi/xóa.

+ 512Byte EEROM.

+ 1KB SRAM ngoài.

Giao tiếp JTAG:

+ Khả năng quét toàn diện theo chuẩn JTAG

+ Hỗ trợ khả năng gỡ rối

+ Hỗ trợ lập trình Flash, EEROM, fuse...

+ Lock bit qua giao tiếp JTAG

Ngoại vi:

+ 2 timer/counter 8 bit với các chế độ: so sánh và chia tần số.

+ 1 timer/counter 16 bit với các chế độ: so sánh, chia tần số, capture, PWM.

+ 1 timer thời gian thực (Real time clock) với bộ dao động riêng biệt.

+ 4 kênh PWM

+ 8 kênh biến đổi ADC lObit

+ HỖ trợ giao tiếp I2C

+ Bộ giao giao tiếp nối tiếp lập trình được USART

+ Giao tiếp SPI

+ Watch Dog timer với bộ dao động on-chip riêng biệt

Những thuộc tính đặc biệt:

54
+ Chế độ hiệu chính bộ sai số cho bộ dao động RC On-chip

+ Các chế độ ngắt ngòai và trong đa dạng

+ 6 mode sleep: Idle, ADC noise reduction, tiết kiệm năng lượng, power-down,
standby, extended standby.

I/O port:

+ 32 chân I/0(Atmegal6) lập trình được

+ Vỏ 40 chân (ATmega16).

Nguồn cấp:

+ 2.7->5.5 V với ATmega16L

+ 4.5->5.5V với ATmega16H

Tiêu hao năng lượng:

+ Khi hoạt động tiêu thụ dòng 1,1 mA

+ Ở mode Idle tiêu thụ dòng 0.35mA

+ Ở chế độ Power down tiêu thụ dòng nhỏ hơn luA

Đây là những chức năng cơ bản thường thấy trong các vi điều khiển AVR, ngoài ra
trong các vi điều khiển khác thuộc dòng vi điều khiển này thì thường được hỗ trợ thêm
những chức năng đặc biệt.

55
3.2. Cấu tạo và hoạt động

3.2.1. Sơ đồ khối

Hình 3.2: Sơ
đồ khối vi
điều khiển
AVR

3.2.2. Sơ đồ
và chức
năng các
chân

+
VCC: Điện áp
nguồn nuôi.

+ GND: Nối mass.

+ PortA (PA7...PA0): PortA nhận vào tín hiệu Analog và chuyển đổi qua tín hiệu
Digital. Ngoài ra PortA có thể được tách ra làm vào ra 2 hướng 2 bit nếu bộ chuyển đổi
A/D không được sử dụng. Khi các chân PA0 đến PA7 là các lối vào và được đặt xuống
chế độ thấp từ bên ngoài, chúng sẽ là nguồn dòng nếu các điện trở nối lên nguồn dương
được kích hoạt. Các chân của Port A ở vào trạng thái có điện trở cao khi tín hiệu Reset ở
chế độ tích cực hoặc ngay cả khi không có tín hiệu xung đồng hồ.

Port A cung cấp các đường địa chỉ/dữ liệu vào/ra hoạt động theo kiểu đa hợp kênh khi
dùng bộ nhớ SRAM ở bên ngoài.

+ PortB,D : tương tự như PortA.

+ Porte (PC7...PC0): tương tự như PortA. Nhưng nếu cho phép giao diện JTAG,
thì các chân PC5, PC3, PC2 sẽ hoạt động ngay cả khi reset lại tín hiệu.

56
+ Reset: Lối vào đặt lại. Bộ vi điều khiển sẽ được đặt lại khi chân này ở chế độ
thấp trong hơn 50ns, các xung ngắn hơn không tạo ra tín hiệu đặt lại.

+ XTAL1: Lối vào bộ khuếch đại đảo và lối vào mạch tạo xung nhịp bên trong.

+ XTAL2: Lối ra bộ khuếch đại đảo : XTAL1 và XTAL2 lần lượt là lối vào và lối
ra của một bộ khuếch đại đảo. Bộ khuếch đại này được bố trí để làm bộ tạo dao động trên
chip. Một bộ tinh thể thạch anh hoặc một bộ cộng hưởng gốm có thể được sử dụng. Để
điều khiển bộ vi điều khiển từ một nguồn xung nhịp bên ngoài, chân XTAL2 để trống,
còn chân XTAL1 được nối với bộ dao động bên ngoài.

+ AREF : Là chân chuyển đổi tín hiệu analog cho bộ chuyển đổi A/D.

+ AVCC : Là chân nguồn cho Port A và cho bộ chuyển đổi A/D.

Nó có thể tự kết nối với nguồn chính ngay cả khi ADC không được sử dụng.

3.2.3. Cấu trúc bộ nhớ

AVR có cấu trúc Havard 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, các
Register File. Trong khi đó, program memory bus có độ rộng 16 bit và chỉ phục vụ cho
thanh ghi lệnh (instruction register).

Bộ nhớ chương trình (program memory) là bộ nhớ Flash lập trình được. Trong các
loại AVR cũ như AT90S1200 bộ nhớ chương trình chỉ gồm một phần là Application
Flash Section còn trong các loại AVR mới có thêm phần Boot Flash Section. Chức năng
chính của bộ nhớ chương trình là chứa các lệnh (instruction) nên ta không có nhiều cơ
hội tác động nên bộ nhớ này khi lập trình cho chip. Cũng chính vì vậy mà đối với người
lập trình thì 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.

57
Hình 3.3: Sơ đồ bộ nhớ vi điều khiển AVR

Bộ nhớ dữ liệu (data memory): đây là phần chứa các thanh ghi quan trọng nhất của
chip. 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 bộ nhớ này được chia làm các phần:

+ Tệp thanh ghi (Register file): gồm 32 thanh ghi 8 bit có địa chỉ tuyệt đối từ
0x0000 đến 0x001F. Các thanh ghi này được đặt tên là từ R0 đến R31. Chúng có đặc
điểm:

Đượ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 một xung Clock. Tệp thanh ghi được kết nối trực tiếp với bộ xử
lý trung tâm, CPU 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ả.

+ Các thanh ghi vào ra (thanh ghi I/O hay còn gọi là vùng nhớ I/O) là cổng giao
tiếp giữa CPU với 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.

58
+ RAM ngoại (External RAM) : các chip vi điều khiển AVR cho phép người dùng
có thể gắn thêm RAM 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.

+ EEPROM (Electrical Erasable 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 cấp
nguồn nuôi chip, rất thích hợp cho các ứng dụng lưu trữ dữ liệu.

3.3. Một số chức năng đặc biệt

3.3.1. Timer/ Counter

a. 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, 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:

Timer/Counter0: là một bộ định thời, đếm đơn giản với 8 bit. Gọi là đơn giản vì
bộ này chỉ có 1 chế độ hoạt động (mode) so với 5 chế độ của bộ Timer/Counter1. Chế độ
hoat động của Timer/Counter0 thực chất có thể coi như 2 chế độ nhỏ (và cũng là 2 chức
năng cơ bản) đó là tạo ra một khoảng thời gian và đếm sự kiện. Chú ý là trên các chip
AVR dòng mega sau này như Atmega16,32,64…chức năng của Timer/Counter0 được
nâng lên như các bộ Timer/Counter1…

Timer/Counter1: là bộ định thời, đếm đa năng 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, Timer/Counter1 còn được

59
dùng để tạo ra xung điều rộng PWM dùng cho các mục đích điều khiển. Có thể tạo 2 tín
hiệu PWM  độc lập trên các chân OC1A (chân 15) và OC1B (chân 16) bằng
Timer/Counter1. Các bộ Timer/Counter kiểu này được tích hợp thêm khá nhiều trong các
chip AVR sau này, ví dụ Atmega128 có 2 bộ, Atmega2561 có 4 bộ…

Timer/Counter2: tuy là một module 8 bit như Timer/Counter0 nhưng


Timer/Counter2 có đến 4 chế độ hoạt động như Timer/Counter1, 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ế độ
asynchronous).

Trước khi khảo sát hoạt động của các Timer/Counter, ta thống nhất cách gọi tắt
tên gọi của các Timer/Counter là T/C, ví dụ T/C0 để chỉ Timer/Counter0…

b. Timer/Counter0:
ATmega16 có 4 thanh ghi được thiết kế riêng cho hoạt động và điều khiển T/C0,
đó là:

TCNT0 (Timer/Counter Register): 

Là 1 thanh ghi 8 bit chứa giá trị vận hành của T/C0. Thanh ghi này cho phép đọc
và ghi giá trị một cách trực tiếp.

TCCR0 (Timer/Counter Control Register): 

Là thanh ghi điều khiển hoạt động của T/C0. Tuy là thanh ghi 8 bit nhưng thực
chất chỉ có 3 bit có tác dụng đó là CS00, CS01 và CS02.

       Các bit CS00, CS01 và CS02 gọi là các bit chọn nguồn xung nhịp cho T/C0 (Clock
Select). Chức năng các bit này được mô tả trong bảng 3.1.

60
Bảng 3.1: chức năng các bit CS0X

TIMSK (Timer/Counter Interrupt Mask Register):

Là thanh ghi mặt nạ cho ngắt của tất cả các T/C trong Atmega16, trong đó chỉ có
bit TOIE0 tức bit số 0 (bit đầu tiên) trong thanh ghi này là liên quan đến T/C0, bit này có
tên là bit cho phép ngắt khi có tràn ở T/C0. Tràn (Overflow) là hiện tượng xảy ra khi bộ
giá trị trong thanh ghi TCNT0 đã đạt đến MAX (255) và lại đếm thêm 1 lần nữa.

       Khi bit TOIE0=1, và bit I trong thanh ghi trạng thái được set (xem lại bài 3 về điều
khiển ngắt), nếu một “tràn” xảy ra sẽ dẫn đến ngắt tràn.

TIFR (Timer/Counter Interrupt Flag Register):

Là thanh ghi cờ nhớ cho tất cả các bộ T/C. Trong thanh ghi này bit số 0, TOV0 là
cờ chỉ thị ngắt tràn của T/C0. Khi có ngắt tràn xảy ra, bit này tự động được set lên 1.
Thông thường trong điều khiển các T/C vai trò của thanh ghi TIFR không quá quan trọng.

Hoạt động của Timer/Counter0:

T/C0 hoạt động rất đơn giản, hoạt động của T/C được “kích” bởi một tín hiệu
(signal), cứ mỗi lần xuất hiện tín hiệu “kích” giá trị của thanh ghi TCNT0 lại tăng thêm 1
đơn vị, thanh ghi này tăng cho đến khi nó đạt mức MAX là 255, tín hiệu kích tiếp theo sẽ

61
làm thanh ghi TCNT0 trở về 0 (tràn), lúc này bit cờ tràn TOV0 sẽ tự động được set bằng
1. Với cách thức hoạt động như thế có vẻ T/C0 vô dụng vì cứ tăng từ 0 đến 255 rồi lại
quay về 0, và quá trình lặp lại. Tuy nhiên, yếu tố tạo sự khác biệt chính là tín hiệu kích và
ngắt tràn, kết hợp 2 yếu tố này ta có thể tạo ra 1 bộ định thời gian hoặc 1 bộ đếm sự kiện.
Trước hết hãy nhìn lại bảng 1 về các bit chọn xung nhịp cho T/C0. Xung nhịp cho T/C0
chính là tín hiệu kích cho T/C0. Xung nhịp này có thể tạo bằng nguồn tạo dao động của
chip (thạch anh, dao động nội trong chip…). Bằng cách đặt giá trị cho các bit CS00, CS01
và CS02 của thanh ghi điều khiển TCCR0, ta sẽ quyết định bao lâu thì sẽ kích T/C0 một
lần. Ví dụ mạch ứng dụng có nguồn dao động clk = 1MHz tức  chu kỳ 1 nhịp là 1us (1
micro giây), đặt thanh ghi TCCR0=5 (tức SC02=1, CS01=0, CS00=1). Căn cứ theo bảng
1, tín hiệu kích cho T/C0 sẽ bằng clk/1024 nghĩa là sau 1024us thì T/C0 mới được kích 1
lần, nói cách khác giá trị của TCNT0 tăng thêm 1 sau 1024us (chú ý là tần số được chia
cho 1024 thì chu kỳ sẽ tăng 1024 lần). Quan sát 2 dòng cuối cùng trong bảng 1 sẽ thấy
rằng tín hiệu kích cho T/C0 có thể lấy từ bên ngoài (External clock source), đây chính là ý
tưởng cho hoạt động của chức năng đếm sự kiện trên T/C0. Bằng cách thay đổi trạng thái
chân T0 (chân 6 trên chip Atmega8) ta sẽ làm tăng giá trị thanh ghi TCNT0 hay nói cách
khác T/C0 có thể dùng để đếm sự kiện xảy ra trên chân T0. Dưới đây ta sẽ xem xét cụ thể
cách điều khiển T/C0 theo 1 chế độ định thời gian và đếm.

ta có thể dùng T/C0 như một bộ đếm (counter) để đếm các sự kiện (sự thay đổi
trạng thái) xảy ra trên chân T0. Bằng cách đặt giá trị cho thanh ghi TCCR0 = 6 (CS02=1,
CS01=1, CS00=0) cho phép đếm “cạnh xuống” trên chân T0, nếu TCCR0 = 7 (CS02=1,
CS01=1, CS00=1) thì “cạnh lên” trên chân T0 sẽ được đếm. Có sử dụng ngắt hay không
phụ thuộc vào mục đích sử dụng.

c. Timer/Counter1:
       Timer/Counter1 là bộ T/C 16 bits, đa chức năng. Đây là bộ T/C rất lý tưởng cho lập
trình đo lường và điều khiển vì có độ phân giải cao (16 bits) và có khả năng tạo xung điều
rộng PWM (Pulse Width Modulation – thường dùng để điều khiển động cơ).

62
Có khá nhiều thanh ghi liên quan đến T/C1. Vì là T/C 16 bits trong khi độ rộng bộ
nhớ dữ liệu của AVR là 8 bit (xem lại bài 2) nên đôi khi cần dùng những cặp thanh ghi 8
bits tạo thành 1 thanh ghi 16 bit, 2 thanh ghi 8 bits sẽ có tên kết thúc bằng các ký tự L và
H trong đó L là thanh ghi chứa 8 bits thấp (LOW) và H là thanh ghi chứa 8 bits cao
(High) của giá trị 16 bits mà chúng tạo thành.

TCNT1H và TCNT1L (Timer/Counter Register):

Là 2 thanh ghi 8 bit tạo thành thanh ghi 16 bits (TCNT1) chứa giá trị vận hành của
T/C1. Cả 2 thanh ghi này cho phép đọc và ghi giá trị một cách trực tiếp. 2 thanh ghi được
kết hợp như sau:

TCCR1A và TCCR1B (Timer/Counter Control Register):

Là 2 thanh ghi điều khiển hoạt động của T/C1. Tất cả các mode hoạt động của
T/C1 đều được xác định thông qua các bit trong 2 thanh ghi này. Tuy nhiên, đây không
phải là 2 byte cao và thấp của một thanh ghi mà là 2 thanh ghi hoàn toàn độc lập. Các bit
trong 2 thanh ghi này bao gồm các bit chọn mode hay chọn dạng sóng (Waveform
Generating Mode – WGM), các bit quy định dạng ngõ ra (Compare Output Match –
COM), các bit chọn giá trị chia prescaler cho xung nhịp (Clock Select – CS)…Cấu trúc
của 2 thanh ghi được trình bày như bên dưới.

63
Nhìn chung để “thuộc” hết cách phối hợp các bit trong 2 thanh ghi TCCR1A và
TCCR1B là tương đối phức tạp vì T/C1 có rất nhiều mode hoạt động, ta sẽ khảo sát chúng
trong phần các chế độ hoạt động của T/C1 bên dưới. Ở đây, trong thanh ghi TCCR1B có
3 bit khá quen thuộc là CS10, CS11 và CS12. Đây là các bit chọn xung nhịp cho  T/C1
như truong T/C0. Bảng 2 sẽ tóm tắt các chế độ xung nhịp trong T/C1.

Bảng 3.2: Chức năng các bit CS12, CS11 và CS10.

OCR1A và OCR1B (Ouput Compare Register A và B):

Có một số khái niệm mới mà ta cần biết khi làm việc với T/C1, một trong số đó là
Ouput Compare. Trong lúc T/C hoạt động, giá trị thanh ghi TCNT1 tăng, giá trị này được
liên tục so sánh với các thanh ghi OCR1A và OCR1B (so sánh độc lập với từng thanh
ghi), việc so sánh này trên AVR gọi là gọi là Ouput Compare. Khi giá trị so sánh bằng
nhau thì 1 “Match” xảy ra, khi đó một ngắt hoặc 1 sự thay đổi trên chân OC1A (hoặc/và
chân OC1B) xảy ra (đây là cách tạo PWM bởi T/C1. A và B đại diện cho 2 kênh
(channel) và B. Cũng vì điều này mà ta có thể tạo 2 kênh PWM bằng T/C1. Tóm lại, cơ
bản 2 thanh ghi này chứa các giá trị để so sánh, chức năng và các chế độ hoạt động cụ thể
của chúng sẽ được khảo sát trong các phần sau.

64
ICR1 (InputCapture Register 1):

Khái niệm mới thứ 2 của T/C1 là Input Capture. Khi có 1 sự kiện trên chân ICP1
(chân 14 trên Atmega8), thanh ghi ICR1sẽ “capture” 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.

TIMSK (Timer/Counter Interrupt Mask Register):

Các bộ T/C trên AVR dùng chung thanh ghi mặt nạ ngắt, vì thế TIMSK cũng được
dùng để quy định ngắt cho T/C1. Có điều lúc này ta chỉ quan tâm đến các bit từ 2 đến 5
của TIMSK. Có tất cả 4 loại ngắt trên T/C1 (nhớ lại T/C0 chỉ có 1 loại ngắt tràn)

+ Bit 2 trong TIMSK là TOIE1, bit quy định ngắt tràn cho thanh T/C1 (tương tự
trường hợp của T/C0).

+ Bit 3, OCIE1B là bit cho phép ngắt khi có 1 “Match” xảy ra trong việc so sánh
TCNT1 với OCR1B.

+ Bit 4, OCIE1A là bit cho phép ngắt khi có 1 “Match” xảy ra trong việc so sánh
TCNT1 với OCR1A.

65
+ Bit 5, TICIE1 là bit cho phép ngắt trong trường hợp Input Capture được dùng.

Cùng với việc set các bit trên, bit I trong thanh ghi trạng thái phải được set nếu
muốn sử dụng ngắt.

TIFR (Timer/Counter Interrupt Flag Register):

Là thanh ghi cờ nhớ cho tất cả các bộ T/C. Các bit từ 2 đến 5 trong thanh ghi này
là các cờ trạng thái của T/C1.

Các mode hoạt động: có tất cả 5 chế độ hoạt động chính trên T/C1. Các chế độ
hoạt động cơ bản được quy định bởi 4 bit Waveform Generation Mode (WGM13,
WGM12, WGM11 WGM10) và một số bit phụ khác. 4 bit Waveform Generation Mode
lại được bố trí nằm trong 2 thanh ghi TCCR1A và TCCR1B (WGM13 là bit 4, WGM12
là bit 3 trong TCCR1B trong khi WGM11 là bit 1 và WGM10 là bit 0 trong thanh ghi
TCCR1A) vì thế cần phối hợp 2 thanh ghi TCCR1 trong lúc điều khiển T/C1. Các chế độ
hoạt động của T/C1 được tóm tắt trong bảng sau 3.3:

66
Bảng 3.3: các bit WGM và các chế độ hoạt động của T/C1.

Các chế độ hoạt động của Timer/Counter1:

Normal mode (Chế độ thường).

       Đây là chế độ hoạt động đơn giản nhất của T/C1. Trong chế độ này, thanh ghi đếm
TCNT1 được tăng giá trị từ 0 (BOTTOM) đến 65535 hay 0xFFFF (TOP) và quay về 0.
Chế độ này hoàn toàn giống cách mà Timer0 hoạt động chỉ có khác là giá trị đếm cao
nhất là 65535 thay vì 255 như trong timer0. Nhìn vào bảng 3, để set T/C1 ở Normal mode
ta cần set 4 bit WGM về 0, vì 0 là giá trị mặc định của các thanh ghi nên thực tế ta không
cần tác động đến các bit WGM. Duy nhất một việc quan trọng cần làm là set các bit Clock
Select (CS12, SC11, CS10) trong thanh ghi TCCR1B (xem thêm bảng 2).

Clear Timer on Compare Match (xóa timer nếu xảy ra bằng trong so sánh)-CTC. 
       Một cách gọi tắt của chế độ hoạt động này là CTC, một chế độ hoạt động mới trên
T/C1. Nhìn vào bảng 3 sẽ thấy có 2 mode CTC (mode 4 và mode 12). Tôi lấy ví dụ mode
4 để giải thích hoạt động của CTC. Khi set các bit Waveform Generation Mode tuong
ứng: WGM13=0, WGM12=1, WGM11=0, WGM10=0 thì mode 4 được chọn. Trong

67
mode này, thanh ghi OCR1A chứa giá trị TOP (giá trị so sánh do người dùng đặt), thanh
ghi đếm TCNT1 tăng từ 0, khi TCNT1 bằng giá trị chứa trong OCR1A thì một “Compare
Match” xảy ra. Khi đó, một ngắt có thể xảy ra nếu ta cho phép ngắt Compare Match (set
bit OCF1A trong thanh ghi TIMSK lên 1). Mode này cũng tương đối đơn giản, một ứng
dụng cơ bản của mode này là đơn giản hóa việc đếm các sự kiện bên ngoài. 

Fast PWM (PWM tần số cao). 

       Trong chế độ Fast PWM, 1 chu kỳ được tính trong 1 lần đếm từ BOTTOM lên TOP
(single-slope), vì thế mà chế độ này gọi là Fast PWM (PWM nhanh). Có tất cả 5 mode
trong Fast PWM tương ứng với 5 cách chọn giá trị TOP khác nhau (tham khảo bảng 3).  
Việc xác lập chế độ hoạt động cho Fast PWM thực hiện thông qua 4 bit WGM và các bit
chọn dạng xung ngõ ra, Compare Output Mode trong thanh ghi TCCR1A, nhìn lại 2 thanh
ghi TCCR1A và TCCR1B.

       Chú ý các bit COM1A1, COM1A0 và COM1B1, COM1B0 là các bit chọn dạng tín
hiệu ra của PWM (Compare Output Mode bits). COM1A1, COM1A0 dùng cho kênh A
và  COM1B1, COM1B0 dùng cho kênh B. Hãy đối chiếu bảng 4.

Bảng 3.4: mô tả các bit COM trong chế độ fast PWM.

68
3.3.2. USART

a. Giới thiệu
Đây là bộ truyền nhận nối tiếp đồng bộ và bất đồng bộ phổ dụng. Đây là khối chức
năng dùng cho việc truyền thông giữa vi điều khiển với các thiết bị khác. Trong vấn đề
truyền dữ liệu số, có thể phân chia cách thức truyền dữ liệu ra 2 chế độ mode cơ bản là:
chế độ đồng bộ (Shynchronous) và chế độ không đồng bộ (Asynchronous). Ngoài ra, nếu
có góc độ phần cứng thì có thể phân chia theo cách khác đó là: Truyền nhận dữ liệu theo
kiểu nối tiếp (serial) và song song (paralell).

Truyền đồng bộ là kiểu truyền dữ liệu trong đó bộ truyền (Transmitter) và bộ nhận


(Receiver) sử dụng chung một xung đồng hồ (clock). Do đó hoạt động truyền và nhận dữ
liệu diễn ra đồng thời. Xung clock có vai trò là tín hiệu đồng bộ cho hệ thống (gồm khối
truyền và khối nhận), ưu điểm của kiểu truyền đồng bộ là tốc độ nhanh, thích hợp khi
truyền dữ liệu khối block).

Truyền không đồng bộ là kiểu truyền dữ liệu trong đó mỗi bộ truyền (transmitter)
và bộ nhận (receiver) có tạo bộ xung (clock) riêng, tốc độ xung clock ở 2 khối này có thể
khác nhau, nhưng thưòng không quá 10%. Do không dùng chung xung clock, nên để
đồng bộ quá trình truyền và nhận dữ liệu, người ta phải truyền các bit đồng bộ (Start,
stop,...) đi kèm với các bit dữ liệu. Các bộ truyền và bộ nhận sẽ dựa vào các bit đồng bộ
này quyết định khi nào sẽ thực hiện hay kết thúc quá trình truyền hoặc nhận dữ liệu. Do
đó, hệ thống truyền không đồng bộ còn được gọi là hệ thống truyền “tự đồng bộ”.

b. Hoạt động.
Bộ USART trong Atmega 16 được chia làm ba phần chính:

+ Khối tạo xung

+ Bộ truyền

+ Bộ nhận

Bộ tạo xung clock: có chức năng thiết lập tốc độ Baud. Bộ truyền bao gồm 1 thanh

69
ghi đệm và một thanh ghi dịch, việc ghi dữ liệu vào bộ đếm cho phép quá trình truyền liên
tục mà không có độ trễ giữa các khung. Bộ nhận có cấu tạo phức tạp, bao gồm việc kiểm
tra chẵn lẻ, điều khiển logic, thanh ghi dịch, đồng thời có 2 cấp độ trong bộ đếm nhận.
Ngoài việc định dạng khung như bộ truyền thì bộ nhận có khả năng phát hiện lỗi khung,
lỗi chẵn lẻ, lỗi tràn dữ liệu.

Định dạng khung: Trong chế độ truyền không đồng bộ, khung dữ liệu truyền đi
không có tín hiệu clock để đồng bộ hoá dữ liệu (vì thế mà gọi là không đồng bộ), quá
trình đồng bộ hoá giữa bộ thu và bộ phát được thực hiện nhờ các bit đồng bộ là start bit và
stop bit. Một khung nối tiếp bao giờ cũng được định dạng theo thứ tự 1 start bit, các bit
giữ liệu (data bit) 1 bit pairty (tùy chọn) và kết thúc bằng 1 hoặc 2 stop bit.

Quá trình truyền USART:

Việc truyền dữ liệu nối tiếp ra ngoài thông qua chân TxD. Một quá trình truyền dữ
liệu từ MCU đi được khởi tạo bằng việc viết dữ liệu vào thanh ghi đệm dữ liệu UDR, sau
đó dữ liệu được chuyển tới thanh ghi dịch bộ phát khi thanh ghi dịch đã sẵn sàng truyền
một byte mới. Các bit start và stop được bổ sung vào khung dữ liệu trong thanh ghi này
với thiết đặt từ thanh ghi điều khiển bộ phát. Cũng như vậy bit thứ 9 (nếu có) có thể được
thêm vào TXB8 trong thanh ghi UCSRB trước khi byte thấp của ký tự được viết vào
UDR. Khi thanh ghi dịch dịch hết dữ liệu (đã được điều chế) ra ngoài thông qua chân
TxD, nó sẽ sẵn sàng nhận dữ liệu mới nếu nó đang ở trạng thái rỗi hoặc ngay lập tức sau
khi bit stop cuối cùng của khung trước đó được truyền đi. Lưu ý rằng dữ liệu được dịch ra
ngoài với bit LSB trước, cuối cùng là MSB.

Quá trình nhận USART:

Phần mềm cho phép thanh ghi dịch nhận dữ liệu nối tiếp từ bên ngoài thông qua
chân RxD (PDO). Bộ thu bắt đầu tiếp nhận dữ liệu khi dò được một bit Start. Sau khi dò
được bit Stop đầu tiên, dữ liệu được chuyển đến thanh ghi UDR (bộ đệm dữ liệu bộ thu)
không có các Start và Stop bits theo dạng song song để vào CPU.

70
USART có 5 thanh ghi:

UDR

Hay còn gọi là thanh ghi dữ liệu, là 1 thanh ghi 8 bit chứa giá trị nhận được và phát
đi của USART. Thực chất thanh ghi này có thể coi như 2 thanh ghi TXB (Transmit data
Buffer) và RXB (Reveive data Buffer) có chung địa chỉ. Đọc UDR thu được giá trị thanh
ghi đệm dữ liệu nhận, viết giá trị vào UDR tương đương đặt giá trị vào thanh ghi đệm
phát, chuẩn bị để gởi đi. Chú ý trong các khung truyền sử dụng 5, 6 hoặc 7 bit dữ liệu, các
bit cao của thanh ghi UDR sẽ không được sử dụng

UCSRA (USART Control and Status Register A):

Là 1 trong 3 thanh ghi điều khiển hoạt động của module USART.

       Thanh ghi UCSRA chủ yếu chứa các bit trạng thái như bit báo quá trình nhận kết
thúc (RXC), truyền kết thúc (TXC), báo thanh ghi dữ liệu trống (UDRE), khung truyền có
lỗi (FE), dữ liệu tràn (DOR), kiểm tra parity có lỗi (PE)… chú ý một số bit quan trọng của
thanh ghi này:

+ UDRE (USART Data Register Empty)

Khi bit bày bằng 1 nghĩa là thanh ghi dữ liệu UDR đang trống và sẵn sàng cho một
nhiệm vụ truyền hay nhận tiếp theo. Vì thế nếu muốn truyền dữ liệu đầu tiên phải kiểm
tra xem bit UDRE có bằng 1 hay không, sau khi chắc chắn rằng UDRE=1 hãy viết dữ liệu
vào thanh ghi UDR để truyền đi.

71
+ U2X:

Là bit chỉ định gấp đôi tốc độ truyền, khi bit này được set lên 1, tốc độ truyền so
cao gấp 2 lần so với khi bit này mang giá trị 0.

+ MPCM:

Là bit chọn chế độ hoạt động đa xử lí (multi-processor).

UCSRB (USART Control and Status Register B):

Đây là thanh ghi quan trọng điều khiển USART. Vì thế ta sẽ khảo sát chi tiết từng
bit của thanh ghi này.

+ RXCIE (Receive Complete Interrupt Enable)

Là bit cho phép ngắt khi quá trình nhận kết thúc. Việc nhận dữ liệu truyền bằng
phương pháp nối tiếp không đồng bộ thường được thực hiện thông qua ngắt, vì thế bit này
thường được set bằng 1 khi USART được dung nhận dữ liệu.

+ TXCIE (Transmit Complete Interrupt Enable)

Bit cho phép ngắt khi quá trình truyền kết thúc.

+ UDRIE (USART Data Register Empty Interrupt Enable)

Là bit cho phép ngắt khi thanh ghi dữ liệu UDR trống.

+ RXEN (Receiver Enable)

Là một bit quan trọng điều khiển bộ nhận của USART, đề kích hoạt chức năng nhận
dữ liệu phải set bit này lên 1.

+ TXEN (Transmitter Enable)

Là bit điều khiển bộ phát. Set bit này lên 1 sẽ khởi động bộ phát của USART.
+ UCSZ2 (Chracter size)

72
Bit này kết hợp với 2 bit khác trong thanh ghi UCSRC quy định độ dài của dữ liệu
truyền/nhận. ta sẽ khảo sát chi tiết khi tìm hiểu thanh ghi UCSRC.

+ RXB8 (Receive Data Bit 8)

Gọi là bit dữ liệu 8. nhớ lại rằng USART trong AVR có hỗ trợ truyền dữ liệu có độ
dài tối đa 9 bit, trong khi thanh ghi dữ liệu là thanh ghi 8 bit. Do đó, khi có gói dữ liệu 9
bit được nhận, 8 bit đầu sẽ chứa trong thanh ghi UDR, cần có 1 bit khác đóng vai trò bit
thứ chín, RXD8 là bit thứ chín này. chú ý là các bit được đánh số từ 0, vì thế bit thứ chín
sẽ có chỉ số là 8, vì lẽ đó mà bit này có tên là RXD8 (không phải RXD9).

+ TXB8 (Transmit Data Bit 8)

Tương tự như bit RXD8, bit TXB8 cũng đóng vai trò bit thứ 9 truyền thông, nhưng
bit này được dung trong lúc truyền dữ liệu.    

UCSRC (USART Control and Status Register C):

Thanh ghi này chủ yếu quy định khung truyền và chế độ truyền. Tuy nhiên, có một
rắc rối nho nhỏ là thanh ghi này lại có cùng địa chỉ với thanh ghi UBRRH (thanh ghi chứa
byte cao dùng để xác lập tốc độ baud), nói một cách khác 2 thanh ghi này là 1. Vì thế bit 7
trong thanh ghi này, tức bit URSEL là bit chọn thanh ghi. Khi URSEL=1, thanh ghi này
được chip AVR hiểu là thanh ghi điều khiển UCSRC, nhưng nếu bit URSEL=0 thì thanh
ghi UBRRH sẽ được sử dụng.

       Các bit còn lại trong thanh ghi UCSRC được mô tả như sau:

+ UMSEL (USART Mode Select)

là bit lựa chọn giữa 2 chế độ truyền thông đồng bộ và không đồng bộ.
Nếu UMSEL=0, chế độ không đồng bộ được chọn, ngược lại nếu UMSEL=1, chế độ
đồng bộ được kích hoạt.

73
+ Hai bit UPM1 và UPM0( Parity Mode)

được dùng để quy định kiểm tra pariry. Nếu UPM1:0=00, parity không được sử
dụng (mode này khá thông dụng), UPM1:0=01 không được sử dụng, UPM1:0=10 thì
parity chẵn được dùng, UPM1:0=11 parity lẻ được sử dụng (xem thêm bảng 3.5).

Bảng 3.5: Bảng chọn kiểm tra parity.

+ USBS (Stop bit Select),

Bit Stop trong khung truyền bằng AVR USART có thể là 1 hoặc 2 bit, nếu
USBS=0 thì Stop bit chỉ là 1 bit trong khi USBS=1 sẽ có 2 Stop bit được dùng.

+ Hai bit UCSZ1 và UCSZ2 (Character Size)

Kết hợp với bit UCSZ2 trong thanh ghi UCSRB tạo thành 3 bit quy định độ dài dữ
liệu truyền. Bảng 3.6 tóm tắt các giá trị có thể có của tổ hợp 3 bit này và độ dài dữ liệu
truyền tương ứng.

Bảng 3.6: Độ dài dữ liệu truyền.

74
+ UCPOL (Clock Pority)

Là bit chỉ cực của xung kích trong chế độ truyền thông đồng bộ. nếu UCPOL=0,
dữ liệu sẽ thay đổi thay đổi ở cạnh lên của xung nhịp, nếu UCPOL=1, dữ liệu thay đổi ở
cạnh xuống xung nhịp. Nếu sử dụng chế độ truyền thông không đồng bộ, hãy set bit này
bằng 0..

UBRRL và UBRRH (USART Baud Rate Register):

2 thanh ghi thấp và cao quy định tốc độ baud.

Nhắc lại là thanh ghi UBRRH dùng chung địa chỉ thanh ghi UCSRC, phải set bit
này bằng 0 nếu muốn sử dụng thanh ghi UBRRH. Như quan sát trong hình trên, chỉ có 4
bit thấp của UBRRH được dùng, 4 bit này kết hợp với 8 bit trong thanh ghi UBRRL tạo
thành thanh ghi 12 bit quy định tốc độ baud. Chú ý là nếu viết giá trị vào thanh ghi
UBRRL, tốc độ baud sẽ tức thì được cập nhật, vì thế phải viết giá trị vào thanh ghi
UBRRH trước khi viết vào thanh ghi UBRRL.

Giá trị gán cho thanh ghi UBRR không phải là tốc độ baud, nó chỉ được USART
dùng để tính tốc độ baud. Bảng 3.7 hướng dẫn cách tính tốc độ baud dựa vào giá trị của
thanh ghi UBRR và ngược lại, cách tính giá trị cần thiết gán cho thanh ghi UBRR khi đã
biết tốc độ baud.

75
Bảng 3.7: Bảng tính tốc độ baud

Trong các công thức trong bảng 3.8, fOSC là tốc tần số xung nhịp của hệ thống
(thạch anh hay nguồn xung nội…).

Bảng 3.8: Một số tốc độ baud mẫu.

76
77
3.3.3. ADC

a. Giới thiệu
Chip AVR ATmega16 của Atmel có tích hợp sẵn các bộ chuyển đổi ADC với độ
phân giải 10 bit. Có tất cả 8 kênh đơn (các chân ADC0 đến ADC7), 16 tổ hợp chuyển đổi
dạng so sánh, trong đó có 2 kênh so sánh có thể khuyếch đại. Bộ chuyển đổi ADC trên
AVR là loại chuyển đổi xấp xỉ lần lượt (successive approximation ADC).

ADC trên AVR cần được “nuôi” bằng nguồn điện áp riêng ở chân AVCC, giá trị
điện áp cấp cho AVCC không được khác nguồn nuôi chip (VCC) quá +/-0.3V. Nhiễu
(noise) là vấn đề rất quan trọng khi sử dụng các bộ ADC, để giảm thiểu sai số chuyển đổi
do nhiễu, nguồn cấp cho ADC cần phải được “lọc” (filter) kỹ càng. Một cách đơn giản để
tạo nguồn AVCC là dùng một mạch LC kết nối từ nguồn VCC của chip, đây là cách được
gợi ý bởi nhà sản xuất AVR.

78
Hình 3.4 Tạo nguồn AVCC
từ VCC.

Điện áp tham chiếu cho


ADC trên AVR có thể được
tạo bởi 3 nguồn: dùng điện áp
tham chiếu nội 2.56V (cố định),
dùng điện áp AVCC hoặc điện
áp ngoài đặt trên chân VREF.
Ta cần chú ý đến nhiễu khi đặt
điện áp tham chiếu, nếu
dùng điện áp ngoài đặt trên
chân VREF thì điện áp này phải
được lọc thật tốt, nếu dùng
điện áp tham chiếu nội 
2.56V hoặc AVCC thì chân VREF cần được nối với một tụ điện.
      Các chân trên PORTA của chip ATmega32 được dùng cho bộ ADC, chân PA0
tương ứng kênh ADC0 và chân PA7 tương ứng với kênh ADC7.

b. Hoạt động
Có 4 thanh ghi trong bộ ADC trên AVR trong đó có 2 thanh ghi data chứa dữ liệu
sau khi chuyển đổi, 2 thanh ghi điều khiển và chứa trạng thái của ADC.

ADMUX (ADC Multiplexer Selection Register): 

Là 1 thanh ghi 8 bit điều khiển việc chọn điện áp tham chiếu, kênh và chế độ hoạt
động của ADC. Chức năng của từng bit trên thanh ghi này sẽ được trình bày cụ thể như
sau:

79
+ Bit 7:6- REFS1:0 (Reference Selection Bits): 

Là các bit chọn điện áp tham chiếu cho ADC, 1 trong 3 nguồn điện áp tham chiếu
có thể được chọn là: điện áp ngoài từ chân VREF, điện áp tham chiếu nội 2.56V hoặc
điện áp AVCC. Bảng 2 tóm tắt giá trị các bit và điện áp tham chiếu tương ứng.

Bảng 3.9: Chọn điện áp tham chiếu

+ Bit 5-ADLAR (ADC Left Adjust Result): 

Là bit cho phép hiệu chỉnh trái kết quả chuyển đổi. Sở dĩ có bit này là vì ADC trên
AVR có độ phân giải 10 bit, nghĩa là kết quả thu được sau chuyển đổi là 1 số có độ dài 10
bit (tối đa 1023), AVR bố trí 2 thanh ghi data 8 bit để chứa giá trị sau chuyển đổi. Như thế
giá trị chuyển đổi sẽ không lắp đầy 2 thanh ghi  data, trong một số trường hợp muốn 10
bit kết quả nằm lệch về phía trái trong khi cũng có trường hợp người dùng muốn kết quả
nằm về phía phải. Bit ADLAR sẽ quyết định vị trí của 10 bit kết quả trong 16 bit của 2
thanh ghi data. Nếu ADLAR=0 kết quả sẽ được hiệu chỉnh về phía phải (thanh ghi ADCL
chứa trọn 8 bit thấp và thanh ghi ADCH chứa 2 bit cao trong 10 bit kết quả), và nếu
ADLAR=1 thì kết quả được hiệu chỉnh trái (thanh ghi ADCH chứa trọn 8 bit cao nhất,
các bit từ 9 đến 2, và thanh ADCL chứa 2 bit thấp nhất trong 10 bit kết quả ( xem hình
cách bố trí 2 thanh ghi ADCL và ADCH bên dưới để hiểu rõ hơn).

+ Bits 4:0-MUX4:0 (Analog Channel and Gain Selection Bits): 

80
Là 5 bit cho phép chọn kênh, chế độ và cả hệ số khuyếch đại cho ADC. Do bộ
ADC trên AVR có nhiều kênh và cho phép thực hiện chuyển đổi ADC kiểu so sánh (so
sánh điện áp giữa 2 chân analog) nên trước khi thực hiện chuyển đổi, ta cần set các bit
MUX để chọn kênh và chế độ cần sử dụng. Bảng 3 tóm tắt các chế độ hoạt động của
ADC thông qua các giá trị của các bit MUX. Trong bảng này, ứng với các giá trị từ 00000
đến 00111 (nhị phân), các kênh ADC được chọn ở chế độ đơn kênh (tín hiệu input lấy
trực tiếp từ các chân analog và so sánh với 0V), giá trị từ 01000 đến 11101 tương ứng với
chế độ chuyển đổi so sánh.

81
Bảng 3.10: Chọn chế độ chuyển đổi.

ADCSRA (ADC Control and Status RegisterA): 

Là thanh ghi chính điều khiển hoạt động và chứa trạng thái của module ADC.

82
      Từng bit của thanh ghi ADCSRA được mô tả như sau:

+ Bit 7 - ADEN(ADC Enable): 

Viết giá trị 1 vào bit này tức đã cho phép module ADC được sử dụng. Tuy nhiên
khi ADEN=1 không có nghĩa là ADC đã hoạt động ngay, cần set một bit khác lên 1 để
bắt đầu quá trình chuyển đổi, đó là bit ADSC.

+ Bit 6 - ADSC(ADC Start Conversion): 

Set bit này lên 1 là bắt đầu khởi động quá trình chuyển đổi. Trong suốt quá trình
chuyển đổi, bit ADSC sẽ được giữ nguyên giá trị 1, khi quá trình chuyển đổi kết thúc (tự
động), bit này sẽ được trả về 0. Vì vậy không cần và cũng không nên viết giá trị 0 vào bit
này ở bất kỳ tình huống nào. Để thực hiện một chuyển đổi, thông thường ta sẽ set bit
ADEN=1 trước và sau đó set ADSC=1.

+ Bit 4 – ADIF(ADC Interrupt Flag): 

Cờ báo ngắt. Khi một chuyển đổi kết thúc, bit này tự động được set lên 1, vì thế
người dùng cần kiểm tra giá trị bit này trước khi thực hiện đọc giá trị chuyển đổi để đảm
bảo quá trình chuyển đổi đã thực sự hoàn tất.

+ Bit 3 – ADIE(ADC Interrupt Enable): 

Bit cho phép ngắt, nếu bit này được set bằng 1 và bit cho phép ngắt toàn cục (bit I
trong thanh ghi trạng thái của chip) được set, một ngắt sẽ xảy ra khi một quá trình chuyển
đổi ADC kết thúc và các giá trị chuyển đổi đã được cập nhật (các giá trị chuyển đổi chứa
trong 2 thanh ghi ADCL và ADCH).

+ Bit 2:0 – ADPS2:0(ADC Prescaler Select Bits): 

Các bit chọn hệ số chia xung nhịp cho ADC. ADC, cũng như tất cả các module
khác trên AVR, cần được giữ nhịp bằng một nguồn xung clock. Xung nhịp này được lấy

83
từ nguồn xung chính của chip thông qua một hệ số chia. Các bit ADPS cho phép người
dùng chọn hệ số chia từ nguồn clock chính đến ADC. Tham khảo bảng 4 để biết cách
chọn hệ số chia.

Bảng 3.11: Hệ số chia xung nhịp cho ADC.

ADCL và ADCH (ADC Data Register):

Hai thanh ghi chứa giá trị của quá trình chuyển đổi. Do  module ADC trên AVR có
độ phân giải tối đa 10 bits nên cần 2 thanh ghi để chứa giá trị chuyển đổi. Tuy nhiên tổng
số bít của 2 thanh ghi 8 bit là 16, con số này nhiều hơn 10 bit của kết quả chuyển đổi, vì
thế ta được phép chọn cách ghi 10 bit  kết quả vào 2 thanh ghi này. Bit ADLAR trong
thanh ghi ADMUX quy định cách mà kết quả được ghi vào.

ADLAR=0:

ADLAR=1:

84
Thông thường, 2 thanh ghi data được sắp xếp theo định dạng ADLAR=0, ADCL
chứa 8 bit thấp và 2 bit thấp của ADCH chứa 2 bit cao nhất của giá trị thu được. Chú ý
thứ tự đọc giá trị từ 2 thanh ghi này, để tránh đọc sai kết quả, ta cần đọc thanh ghi ADCL
trước và ADCH sau, vì sau khi ADCH được đọc, các thanh ghi data có thể được cập nhật
giá trị tiếp theo.

SFIOR (Special FunctionIO Register C): 

Thanh ghi chức năng đặc biệt, 3 bit cao trong thanh ghi này quy định nguồn kích
ADC nếu chế độ Auto Trigger được sử dụng. Đó là các bit ADTS2:0 (Auto Trigger
Source 2:0). Các loại nguồn kích được trình báy trong bảng 5.

Bảng 3.12: Nguồn kích ADC trong chế độ Auto Trigger.

3.3.4. Chuẩn giao tiếp SPI.

a. Giới thiệu
SPI (Serial Peripheral Bus) là một chuẩn truyền thông nối tiếp tốc độ cao do hãng
Motorola đề xuất. Đây là kiểu truyền thông Master-Slave, trong đó có 1 chip Master điều
phối quá trình tuyền thông và các chip Slaves được điều khiển bởi Master vì thế truyền
thông chỉ xảy ra giữa Master và Slave. SPI là một cách truyền song công (full duplex)
nghĩa là tại cùng một thời điểm quá trình truyền và nhận có thể xảy ra đồng thời. SPI đôi

85
khi được gọi là chuẩn truyền thông “4 dây” vì có 4 đường giao tiếp trong chuẩn này đó là
SCK (Serial Clock), MISO (Master Input Slave Output), MOSI (Master Ouput Slave
Input) và SS (Slave Select). Hình 1 thể hiện một kết SPI giữa một chip Master và 3 chip
Slave thông qua 4 đường.

       SCK: Xung giữ nhịp cho giao tiếp SPI, vì SPI là chuẩn truyền đồng bộ nên cần 1
đường giữ nhịp, mỗi nhịp trên chân SCK báo 1 bit dữ liệu đến hoặc đi. Đây là điểm khác
biệt với truyền thông không đồng bộ mà ta đã biết trong chuẩn UART. Sự tồn tại của
chân SCK giúp quá trình tuyền ít bị lỗi và vì thế tốc độ truyền của SPI có thể đạt rất cao.
Xung nhịp chỉ được tạo ra bởi chip Master. 

       MISO– Master Input / Slave Output: nếu là chip Master thì đây là đường Input còn
nếu là chip Slave thì MISO lại là Output. MISO của Master và các Slaves được nối trực
tiếp với nhau.

       MOSI – Master Output / Slave Input: nếu là chip Master thì đây là đường Output
còn nếu là chip Slave thì MOSI là Input. MOSI của Master và các Slaves được nối trực
tiếp với nhau. 

       SS – Slave Select: SS là đường chọn Slave cần giap tiếp, trên các chip Slave đường
SS sẽ ở mức cao khi không làm việc. Nếu chip Master kéo đường SS của một Slave nào
đó xuống mức thấp thì việc giao tiếp sẽ xảy ra giữa Master và Slave đó. Chỉ có 1 đường
SS trên mỗi Slave nhưng có thể có nhiều đường điều khiển SS trên Master, tùy thuộc vào
thiết kế của người dùng.

86
.

Hình 3.5. Giao diện SPI.

b. Hoạt động
Mỗi chip Master hay Slave có một thanh ghi dữ liệu 8 bits. Cứ mỗi xung nhịp do
Master tạo ra trên đường giữ nhịp SCK, một bit trong thanh ghi dữ liệu của Master được
truyền qua Slave trên đường MOSI, đồng thời một bit trong thanh ghi dữ liệu của chip
Slave cũng được truyền qua Master trên đường MISO. Do 2 gói dữ liệu trên 2 chip được
gởi qua lại đồng thời nên quá trình truyền dữ liệu này được gọi là “song công”. Hình 2 mô
tả quá trình truyền 1 gói dữ liệu thực hiện bởi module SPI trong AVR, bên trái là chip
Master và bên phải là Slave.

87
 

Hình 3.6. Truyền dữ liệu SPI.

       Cực của xung giữ nhịp, phase và các chế độ hoạt động: cực của xung giữ nhịp (Clock
Polarity) được gọi tắt là CPOL là khái niệm dùng chỉ trạng thái của chân SCK ở trạng thái
nghỉ. Ở trạng thái nghỉ (Idle), chân SCK có thể được giữ ở mức cao (CPOL=1) hoặc thấp
(CPOL=0). Phase (CPHA) dùng để chỉ cách mà dữ liệu được lấy mẫu (sample) theo xung
giữ nhịp. Dữ liệu có thể được lấy mẫu ở cạnh lên của SCK (CPHA=0) hoặc cạnh xuống
(CPHA=1). Sự kết hợp của SPOL và CPHA làm nên 4 chế độ hoạt động của SPI. Nhìn
chung việc chọn 1 trong 4 chế độ này không ảnh hưởng đến chất lượng truyền thông mà
chỉ cốt sao cho có sự tương thích giữa Master và Slave.

Module SPI trong các chip AVR hầu như hoàn toàn giống với chuẩn SPI mô tả
trong phần trên. Vì thế, nếu đã hiểu cách truyền thông SPI thì sẽ khống quá khó để thực
hiện việc truyền thông này với AVR. Phần bên dưới tôi trình bày một số điểm quan trọng
khi điều khiển SPI trên AVR.

Các chân SPI: Các chân giao tiếp SPI cũng chính là các chân PORT thông thường,
vì thế nếu muốn sử dụng SPI ta cần xác lập hướng cho các chân này. Trên chip
ATmega32, các chân SPI như sau:

SCK    – PB7 (chân 8)


MISO  – PB6 (chân 7)
MOSI  – PB5 (chân 6)
SS       – PB4 (chân 5)

88
Khi chip AVR được sử dụng làm Slave, cần set các chân SCK input, MOSI input,
MISO output và SS input. Nếu là Master thì SCK output, MISO output, MOSI input và
khi này chân SS không quan trọng, ta có thể dùng chân này để điều khiển SS của Slaves
hoặc bất kỳ chân PORT thông thường nào.

       Thanh ghi: SPI trên AVR được vận hành bởi 3 thanh ghi bao gồm thanh ghi điều
khiển SPCR , thanh ghi trạng thái SPSR và thanh ghi dữ liệu SPDR. 

       SPCR (SPI Control Register): là 1 thanh ghi 8 bit điều khiển tất cả hoạt động của
SPI.

      * Bit 7- SPIE (SPI Interrupt Enable) bit cho phép ngắt SPI. Nếu bit này được set bằng
1 và bit I trong thanh ghi trạng thái được set bằng 1 (sei), 1 ngắt sẽ xảy ra sau khi một gói
dữ liệu được truyền hoặc nhận. ta nên dùng ngắt (nhất là đối với chip Slave) khi truyền
nhận dữ liệu với SPI.

     * Bit 6 – SPE (SPI Enable). set bit này lên 1 để cho phép bộ SPI hoạt động. Nếu
SPIE=0 thì module SPI dừng hoạt động.

     * Bit 5 – DORD (Data Order)  bit này chỉ định thứ tự dữ liệu các bit được truyền và
nhận trên các đường MISO và MOSI, khi DORD=0 bit có trọng số lớn nhất của dữ liệu
được truyền trước (MSB) ngược lại khi DORD=1, bit LSB được truyền trước. Thật ra khi
giao tiếp giữa 2 AVR với nhau, thứ tự này không quan trọng nhưng phải đảm bảo các bit
DORD giống nhau trên cả Master và Slaves.

     * Bit 4 – MSTR (Master/Slave Select) nếu MSTR =1 thì chip được nhận diện là
Master, ngược lại MSTR=0 thì chip là Slave.

     * Bit 3 và 2 – CPOL và CPHA đây chính là 2 bit xác lập cực của xung giữ nhịp và
cạnh sample dữ liệu mà ta đã khảo sát trong phần đầu. Sự kết hợp 2 bit này tạo thành 4
chế độ hoạt động của SPI. Một lần nữa, chọn chế độ nào không quan trọng nhưng phải

89
đảm bảo Master và Slave cùng chế độ hoạt động. Vì thế có thể để 2 bit này bằng 0 trong
tất cả các chip. Hình trình bày cách sample dữ liệu trong 4 chế độ của SPI trên AVR. Khi
giao tiếp giữa 2 AVR với nhau, thứ tự này không quan trọng nhưng phải đảm bảo các bit
DORD giống nhau trên cả Master và Slaves.

 CPHA=0

  CPHA=1

   

Hình 3.7: Các chế độ hoạt động của SPI. 

        * Bit 1:0 – CPR1:0 hai bit này kết hợp với bit SPI2X trong thanh ghi SPSR cho phép
chọn tốc độ giao tiếp SPI, tốc độ này được xác lập dựa trên tốc độ nguồn xung clock chia

90
cho một hệ số chia. Bảng 3.12 tóm tắt các tốc độ mà SPI trong AVR có thể đạt. Thông
thường, tốc bộ này không được lớn hơn 1/4 tốc độ xung nhịp cho chip.

Bảng 3.12 Tóm tắt các tốc độ SPI trong AVR

 SPSR (SPI Status Register): là 1 thanh ghi trạng thái của module SPI. Trong thanh
ghi này  chỉ có 3 bit được sử dụng. Bit 7 – SPIF là cờ báo SPI, khi một gói dữ liệu đã
được truyền hoặc nhận từ SPI, bit SPIF sẽ tự động được set len 1. Bit 6 – WCOL là bít
báo va chạm dữ liệu (Write Colision), bit này được AVR set lên 1 nếu ta cố tình viết 1
gói dữ liệu mới vào thanh ghi dữ liệu SPDR trong khi quá trình truyền nhận trước chưa
kết thúc. Bit 0 – SPI2X gọi là bit nhân đôi tốc độ truyền, bit này kết hợp với 2 bit SPR1:0
trong thanh ghi điều khiển SPCR xác lập tốc độ cho SPI.

SPDR (SPI Data Register):  là thanh ghi dữ liệu của SPI. Trên chip Master, ghi
giá trị vào thanh ghi SPDR sẽ kích quá trình tuyền thông SPI. Trên chip Slave, dữ liệu
nhận được từ Master sẽ lưu trong thanh ghi SPDR, dữ liệu được lưu sẵn trong SPDR sẽ
được truyền cho Master.

Sử dụng SPI trên AVR: SPI trên AVR hoạt động không khác nguyên lý chung
của chuẩn SPI là mấy. Vận hành SPI trên AVR được thực hiện dựa trên việc ghi và đọc 3
các thanh ghi SPCR, SPSR và SPDR. Trước khi truyền nhận bằng SPI ta cần khởi động
SPI, quá trình khởi động thường bao gồm chọn hướng giao tiếp cho các chân SPI, chọn

91
loại giao tiếp: Master hay Slave, chọn chế độ SPI (SPOL, SPHA) và chọn tốc độ giao
tiếp. Truyền thông SPI luôn được khởi xướng bởi chip Master, khi Master muốn giao tiếp
với 1 Slave nào đó, nó sẽ kéo chân SS của Slave xuống mức thấp (gọi là chọn địa chỉ) và
sau đó viết dữ liệu cần truyền vào thanh ghi dữ liệu SPDR, khi dữ liệu vừa được viết vào
SPDR xung giữ nhịp sẽ được tự động tạo ra trên SCK và quá trình truyền nhận bắt đầu.
Đối với các chip Slave, khi chân SS bị kéo xuống nó sẽ sẵn sàng cho quá trình truyền
nhận. Khi phát hiện xung giữ nhịp trên SCK, Slave sẽ bắt đầu sample dữ liệu đến trên
đường MOSI và gởi dữ liệu di trên MISO.

3.3.5. Chuẩn truyền thông I2C


TWI (Two-Wire Serial Interface) là một module truyền thông nối tiếp đồng bộ trên
các chip AVR dựa trên chuẩn truyền thông I 2C. I2C là viết tắc của từ Inter-Integrated
Circuit là một chuẩn truyền thông do hãng điện tử Philips Semiconductor sáng lập và xây
dựng thành chuẩn năm 1990. Về cơ bản TWI trong AVR hoàn toàn tương thích  I2C, do
đó tìm hiểu TWI của AVR không chỉ giúp giao tiếp giữa các AVR với nhau mà có thể
dùng TWI để điều khiển bất kỳ một thiết bị nào theo chuẩn I2C (các chip nhớ, bộ chuyển
đổi ADC, DCA, đồng hồ thời gian thực…).

TWI (I2C) là một truyền thông nối tiếp đa chip chủ được hiểu là trong trên cùng
một bus có thể có nhiều hơn một thiết bị làm Master, đồng thời một Slave có thể trở thành
một Master nếu nó có khả năng. Ví dụ trong một mạng TWI của nhiều AVR kết nối với
nhau, bất kỳ một AVR nào đều có thể trở thành Master ở một thời điểm nào đó. Tuy
nhiên nếu một mạng dùng một AVR điều khiển các chip nhớ (như EEPROM AT24C1024
chẳng hạn) thì khái niệm “multi-master” không tồn tại vì các chip nhớ được thiết kế sẵn là
Slave, không có khả năng trở thành master. TWI (I2C) được thực hiện trên 2 đường SDA
(Serial DATA) và SCL (Serial Clock) trong đó SDA là đường truyền/nhận dữ liệu và
SCL là đường xung nhịp. Căn cứ theo chuẩn I2C, các đường SDA và SCL trên các thiết
bị có cấu hình “cực góp mở”, nghĩa là cần có các “điện trở kéo lên” (pull-up resistor) cho
các đường này. Ở trạng thái nghỉ (Idle), 2 chân SDA và SCL ở mức cao. Hình 1 mô tả
một mô hình mạng TWI (I2C) cơ bản.

92
Hình 3.8: Mạng TWI (I2C) với nhiều thiết bị và 2 điện trở kéo lên cho SDA, SCL.

Master: là chip khởi động quá trình truyền nhận, phát đi địa chỉ của thiết bị cần giao tiếp
và tạo xung giữ nhịp trên đường SCL.

Slave: là chip có một địa chỉ cố định, được gọi bởi Master và phục vụ yêu cầu từ Master.
SDA- Serial Data: là đường dữ liệu nối tiếp, tất cả các thông tin về địa chỉ hay dữ liệu
đều được truyền trên đường này theo thứ tự từng bit một. Chú ý là trong chuẩn I2C, bit có
trọng số lớn nhất (MSB) được truyền trước nhất, đặc điểm này ngược lại với chuẩn
UART.

SCL –Serial Clock: là đường giữ nhịp nối tiếp. TWI (I2C) là chuần truyền thông nối tiếp
đồng bộ, cần có 1 đường tạo xung giữ nhịp cho quá trình truyền/nhận, cứ mỗi xung trên
đường giữ nhịp SCL, một bit dữ liệu trên đường SDA sẽ được lấy mẫu (sample). Dữ liệu
nối tiếp trên đường SDA được lấy mẫu khi đường SCL ở mức cao trong một chu kỳ giữ
nhịp, vì thế đường SDA không được đổi trạng thái khi SCL ở mức cao (trừ START và
STOP condition). Chân SDA có thể được đổi trạng thái khi SCL ở mức thấp.

START Condition-Điều kiện bắt đầu:

Từ trạng thái nghỉ, khi cả SDA và SCL ở mức cao nếu Master muốn thực hiện một
“cuộc gọi”, Master sẽ kéo chân SDA xuống thấp trong khi SCL vẫn cao. Trạng thái này
gọi là START Condition ( ta gọi tắt là S).

93
STOP Condition:

Điều kiện kết thúc: sau khi thực hiện truyền/nhận dữ liệu, nếu Master muốn kết
thúc quá trình nó sẽ tạo ra một STOP condition. STOP condition được Master thực hiện
bằng cách kéo chân SDA lên cao khi đường SCL đang ở mức cao. STOP condition chỉ
được tạo ra sau khi địa chỉ hoặc dữ liệu đã được truyền/nhận.

REPEAT START – Bắt đầu lặp lại:

Khoảng giữa START và STOP condition là khoảng bận của đường truyền, các
Master khác không tác động được vào đường truyền trong khoảng này. Trường hợp sau
khi kết thúc truyền/nhận mà Master không gởi STOP condition lại gởi thêm 1 START
condition gọi là REPEAT START. Khả năng này thường được dùng khi Master muốn lấy
dữ liệu liên tiếp từ các Slaves. Hình bên dưới mô tả các Master tạo ra START, STOP và
REPEAT START.

Address Packet Format – Định dạng gói địa chỉ:

Trên mạng TWI (I2C), tất cả các thiết bị (chip) đều có thể là Master hay Slave.
Mỗi thiết bị có một địa chỉ cố định gọi là Device address. Khi một Master muốn giao tiếp
với một Slave nào đó, nó trước hết tạo ra một START condition và tiếp theo là gởi địa chỉ
Device address của Slave cần giao tiếp trên đường truyền, vì thế xuất hiện khái niệm “gói
địa chỉ” (Address Packet). Gói địa chỉ trong TWI (I2C) có định dạng 9 bits trong đó 7 bit
đầu (gọi là SLA, được gởi liền sau START condition) chứa địa chỉ Slave, một bit
READ/WRITE và một bit ACK-Ackknowledge (xác nhận).  Do bit địa chỉ có độ dài 7
bits nên về mặt lý thuyết, trên 1 mạng TWI (I2C) có thể tồn tại tối đa 2^7=128 thiết bị có
địa chỉ riêng biệt. Tuy nhiên, có một số địa chỉ không được sử dụng như các địa chỉ có

94
định dạng 1111xxx (tức các địa chỉ lớn hơn hoặc bằng 120 không được dùng). Riêng địa
chỉ 0 được dùng cho “cuộc gọi chung” (General call). Bit READ/WRITE (R/W) được
truyền tiếp sau 7 bit địa chỉ là bit báo cho Slave biết Master muốn “đọc” hay “ghi” vào
Slave. Nếu bit này bằng 0 (gọi là W) thì quá trình “Ghi” dữ liệu từ Master đến Slave được
yêu cầu, nếu bit này bằng 1 (gọi là R) thì Master muốn “đọc” dữ liệu từ Slave về. Tám
bits trên (SLA+R/W) được Master phát ra sau khi phát START condition, nếu một Slave
trên mạng nhận ra rằng địa chỉ mà Master yêu cầu trùng khớp với Device address của
chính mình, nó sẽ “đáp trả” lại Master bằng cách phát ra 1 tín hiệu “xác nhận” ACK bằng
cách kéo chân SDA xuống thấp trong xung thứ 9. Ngược lại, nếu không có Slave đáp ứng
lại, chân SDA vẫn ở mức cao trong xung giữ nhịp thứ 9 thì gọi là tín hiệu “không xác
nhận” – NOT ACK, lúc này Master cần có những ứng xử phù hợp tùy theo mỗi trường
hợp cụ thể, ví dụ Master có thể gởi STOP condition và sau đó phát lại địa chỉ Slave
khác…Như vậy, trong 9 bit của gói địa chỉ thì chỉ có 8 bit được gởi bởi Master, bit còn lại
là do Slave. Ví dụ Master muốn yêu cầu “đọc” dữ liệu từ Slave có địa chỉ 43,  nó cần phát
đi một byte như sau trên đường truyền: (43<<1)+1, trong đó (43<<1) là dịch số 43 về bên
trái 1 vị trí vì 7 bit địa chỉ nằm ở các vị trí cao trong gói địa chỉ, sau đó cộng giá trị này
với “1” tức là quá trình “đọc” được yêu cầu.

General call – Cuộc gọi chung:

Khi Master phát đi gói địa chỉ có dạng 0 (thực chất là 0+W) tức nó muốn thực hiện
một cuộc gọi chung đến tất cả các Slave. Tất nhiên, cho phép hay không cho phép cuộc

95
gọi chung là do Slave quyết định. Nếu các Slave được cài đặt cho phép cuộc gọi chung,
chúng sẽ đáp lại Master bằng ACK. Cuộc gọi chung thường xảy ra khi Master muốn gởi
dữ liệu chung đến các Slaves. Chú ý là cuộc gọi chung có dạng 0+R là vô nghĩa vì không
thể có chuyện Master nhận dữ liệu từ tất cả các Slave cùng thời điểm.

Data Packet Format – Định dạng gói dữ liệu:

Sau khi địa chỉ đã được phát đi, Slave đã đáp lại Master bằng ACK thì quá trình
truyền/nhận dữ liệu sẽ diễn ra giữa cặp Master/Slave này. Tùy vào bit R/W trong gói địa
chỉ, dữ liệu có thể được truyền theo hướng từ Master đến Slave hay từ Slave đến Master.
Dù di chuyển theo hướng nào, gói dữ liệu luôn bao gồm 9 bits trong đó 8 bits đầu là dữ
liệu và 1 bit cuối là bit ACK. Tám bits dữ liệu do thiết bị phát gởi và bit ACK do thiết bị
nhận tạo ra. Ví dụ khi Master thực hiện quá trình gởi dữ liệu đến Slave, nó sẽ phát ra 8
bits dữ liệu, Slave nhận và phát lại ACK (kéo SDA xuống 0 ở xung thứ 9), sau đó Master
sẽ quyết định gợi tiếp byte dữ liệu khác hay không. Nếu Slave phát tín hiệu NOT ACK
(không tác động SDA ở xung thứ 9) sau khi nhận dữ liệu thì Master sẽ kết thúc quá trình
gởi bằng cách phát đi STOP condition. Hình bên dưới mô tả định dạng gói dữ liệu trong
TWI (I2C).

Phối hợp gói địa chỉ và dữ liệu:

Một quá trình truyền/nhận TWI (I2C) thường được bắt đầu từ Master, Master phát
đi một START condition sau đó gởi gói địa chỉ SLA+R/W trên đường truyền. Tiếp theo
nếu có một Slave đáp ứng lại, dữ liệu có thể truyền/nhận liên tiếp trên đường truyền (1
hoặc nhiều byte liên tiếp). Khung truyền thông thường được mô tả như hình bên dưới.

96
Multi-Master Bus –Đường truyền đa chip chủ:

Như đã trình bày ở trên, TWI (I2C) là chuẩn truyền thông đa chip chủ, nghĩa là tại
một thời điểm có thể có nhiều hơn 1 chip làm Master nếu các chip này phát ra START
condition cùng lúc. Nếu các Master có cùng yêu cầu và thao tác đối với Slave thì chúng
có thể “cùng tồn tại” và quá trình truyền/nhận có thể thành công. Tuy nhiên, trong đa số
trường hợp sẽ có một số Master bị “thất lạc” (lost). Một Master bị lost khi nó truyền/nhận
1 mức cao trên SDA trong khi các Master khác truyền/nhận 1 mức thấp. Truyền thông đa
chip chủ tương đối phức tạp và vì thế tôi sẽ không đề cập trường hợp này trong lúc thực
hiện ví dụ giao tiếp trong bài học này.

Nắm được các khái niệm và đặc điểm trên của truyền thông TWI (I2C) là đã sẵn
sàng để điều khiển module TWI trên AVR. Phần tiếp theo tôi sẽ hướng dẫn cách thao tác
module TWI trên AVR thông qua một ví dụ cụ thể.

b. Hoạt động của TWI trên AVR.

TWI trên AVR được vận hành bởi 5 thanh ghi bao gồm thanh ghi tốc độ giữ nhịp
TWBR, thanh ghi điều khiển TWCR , thanh ghi trạng thái TWSR, thanh ghi địa chỉ
TWAR và thanh ghi dữ liệu TWDR.

TWBR (TWI Bit Rate Register): 

Là 1 thanh ghi 8 bit quy định tốc độ phát xung giữ nhịp trên đường SCL của chip
Master.

      

97
Tốc độ phát xung giữ nhịp được tính theo công thức:

Trong đó CPU Clock frequency là tần số hoạt động chính của AVR, TWBR là giá
trị thanh thi TWBR và TWPS là giá trị của 2 bits TWPS1 và TWPS0 nằm trong thanh thi
trạng thái TWSR. Hai bits này được gọi là bit prescaler, thông thường người ta hay set
TWPS1:0 =00 để chọn Prescaler là 1 (40=1). Bảng 1 tóm tắt tốc độ xung giữ nhịp tạo ra
trên SCL đối với các giá trị của tham số:

Bảng 3.13. Tốc độ xung giữ nhịp tham khảo.

       

TWCR (TWI Control Register): là thanh ghi 8 bit điều khiển hoạt động của TWI.

         

Bit 7- TWINT (TWI Interrupt Flag): 

Là một cờ báo rất quan trọng. TWINT được tự động set lên 1 khi TWI kết thúc
một quá trình bất kỳ nào đó (như phát/nhận START, phát nhận địa chỉ…). Chú ý là bit

98
này không tự động được xóa bởi phần cứng như các cờ báo trong các module khác. Vì
thế, khi lập trình điều khiển TWI ta luôn phải xóa TWINT trước khi muốn thực hiện một
quá trình nào đó. Một điểm quan trọng cần lưu ý là bit TWINT được xóa khi ta viết giá
trị 1 vào nó. Trong khi lập trình cho TWI, ta thường xóa TWINT bằng cách viết 1 vào
nó, sau đó liên tục kiểm tra TWINT, nếu bit này được set lên 1 thì quá trình đã hoàn
thành.

+ Bit 6 – TWEA (TWI Enable Acknowledge Bit):

Tạm hiểu là bit kích hoạt tín hiệu xác nhận. Đối với  chip Slave, nếu bit này được
set thì tín hiệu xác ACK sẽ được gởi trong các trường hợp sau: địa chỉ do Master phát ra
trùng khớp với địa chỉ của Slave; một cuộc gọi chung đang xảy ra và Slave này cho phép
cuộc gọi chung; dữ liệu đã được Slave nhận từ Master. Như thế, khi set một chip ở chế độ
Slave, ta cần set bit này để nó có thể đáp ứng lại Master bất cứ khi nào được gọi. Đối với
chip Master, tín hiệu ACK chỉ được phát trong 1 trường hợp duy nhất đó là khi Master
nhận dữ liệu từ Slave, Master phát ACK để báo cho Slave là mình đã nhận được và muốn
tiếp tục nhận từ Slave.

+ Bit 5 – TWSTA (TWI START Condition Bit):

Là bit tạo START condition. Khi một chip muốn trở thành Master để thực hiện 1
cuộc gọi, bit này cần được set và một START condition được tạo ra trên đường truyền
nếu đường truyền đang rảnh. Nếu đường truyền không rảnh, TWI sẽ chờ cho đến khi nó
rảnh (nhận ra 1 STOP condition) và tiếp tục gởi START condition. Chú là là bit nay cần
được xóa bởi phần mềm sau khi START condition đã được gởi (viết 0 vào bit này để xóa
nó).

+ Bit 4 – TWSTO (TWI STOP Condition Bit): 

Là bit tạo STOP condition cho TWI. Khi Master muốn kết thúc một cuộc gọi, nó
sẽ phát STOP condition bằng cách viết giá trị 1 vào bit TWSTO. Slave cũng có thể tác
động vào bit này, nếu một cuộc gọi bị lỗi, viết 1 vào TWSTO trên Slave sẽ reset đường
truyền về trạng thái rảnh ban đầu.

99
+ Bit 3 – TWWC (TWI Write Collision Flag): 

Khi cờ TWINT đang ở mức thấp tức TWI đang bận, nếu ta viết dữ liệu vào thanh
ghi dữ liệu (TWDR) thì một lỗi xảy ra, khi đó bit TWWC tự động được set lên 1. Vì thế,
trong quá trình truyền dữ liệu, bit TWINT cần được giữ mức cao khi ghi dữ liệu vào
thanh ghi TWDR và sau đó xóa khi dữ liệu đã sẵn sàng.

+ Bit 2 – TWEN (TWI Enable Bit): 

Bit kích hoạt TWI trên AVR, khi TWEN được set lên 1, TWI sẵn sàng hoạt động.

+ Bit 1 – Reserve:

Không sử dụng.

+ Bit 0 – TWIE (TWI Interrupt Enable Bit): 

Bit cho phép ngắt TWI, khi bit nay được set bằng 1 đồng thời bit I trong thanh ghi
trạng thái chung được set, một ngắt TWI xảy ra khi bit TWINT được set bởi phần cứng.
Ngắt TWI có thể xảy ra  sau bất kỳ hoạt động nào liên quan đến TWI.  Do đó cần sử dụng
ngắt hợp lý. Thông thường, ngắt chỉ được sử dụng cho Slave, đối với Master ngắt không
cần thiết vì Master chủ động khởi động một cuộc gọi. 

Một điều cần chú ý là các bit trong thanh ghi TWCR không cần được set cùng lúc,
tùy vào từng giai đoạn trong quá trình giao tiếp TWI các bit có thể được set riêng lẻ.    

TWSR (TWI Status Register): 

Đây là 1 thanh ghi 8 bit trong đó có 5 bit chứa code trạng thái của TWI  và 2 bit
chọn prescaler.

      

Có rất nhiều bước, nhiều tình huống xảy ra khi giao tiếp bằng TWI cho cả Master
và Slave. Ứng với mỗi trường hợp TWI sẽ tạo ra 1 code trong thanh ghi TWSR . Lập

100
trình cho TWI cần xét code trong 5 bit cao của thanh ghi TWSR và đưa ra các ứng xử hợp
lý ứng với từng code.

TWDR (TWI Data Register): 

Là thanh ghi dữ liệu chính của TWI. Trong quá trình nhận, dữ liệu nhận về sẽ được
lưu trong TWDR. Trong quá trình gởi, dữ liệu chứa trong TWDR sẽ được chuyển ra
đường SDA.

TWAR (TWI Address Register): 

Là thanh ghi chứa device address của chip Slave. Cấu trúc thanh ghi được trình
bày trong hình dưới.

       

       Nhớ lại địa chỉ Slave được tạo thành từ 7 bits, trên thanh ghi TWAR 7 bits địa chỉ
này nằm ở 7 vị trí cao. Trước khi sử dụng TWI như Slave, ta phải gán địa chỉ cho chip,
việc viết địa chỉ thường được thực hiện bằng lệnh TWAR =
(Device_address<<1)+TWGCE. Trong đó TWGCE (TWI General Call Enable) là bit cho
phép cuộc gọi chung. Như tôi đề cập bên trên, Slave co quyền cho phép Master thực hiện
cuộc gọi chung với nó hay không. Nếu TWGCE=1, Slave  sẽ đáp ứng lại cuộc gọi chung
nếu có, nếu TWGCE=0 thì Slave sẽ bỏ qua cuộc gọi chung. 

3.3.6. Ngắt

a. Giới thiệu
Ngắt là sự kiện bên trong hay bên ngoài làm ngắt bộ vi điều khiển để báo cho nó
biết rằng thiết bị cần dịch vụ của nó.

Một bộ vi điều khiển có thể phục vụ một vài thiết bị, có 2 cách để thực hiện điều
này đó là sử dụng các ngắt Interrup và thăm dò (polling). Trong phương pháp sử dụng các
ngắt thì mỗi khi có thiết bị bất kỳ cần đến dịch vụ của nó thì nó báo cho bộ vi điều khiển
bằng cách gửi 1 tín hiệu ngắt. Khi nhận được tín hiệu ngắt thì bộ vi điều khiển ngắt tất cả

101
những gì nó đang thực hiện để chuyển sang phục vụ thiết bị. Chương trình kết hợp cùng
ngắt được gọi là trình phục vụ ngắt ISR hay còn gọi là chương trình quản lý ngắt. Còn
trong phương pháp thăm dò thì bộ vi điều khiển hiển thị liên tục tình trạng của 1 thiết bị
đã cho và điều khiển thỏa mãn thì nó phục vụ thiết bị. Sau đó chuyển sang hiển thị tình
trạng của thiết bị kế tiếp cho đến khi tất cả đều được phục vụ

Điểm mạnh của phương pháp ngắt là bộ vi điều khiển là phục vụ rất nhiều thiết bị.

Trình phục vụ ngắt

Đối với mỗi ngắt thì phải có 1 trình phục vụ ngắt ISR hay trình ngắt. Khi 1 ngắt
được gọi thì bộ vi điều khiển phục vụ ngắt. Đối với mỗi ngắt thì có 1 vị trí cố định trong
bộ nhớ giữa địa chỉ ISR của nó. Nhóm các vị trí nhớ được dành riêng để gửi các địa chí
của các ISR được gọi là bảng vectơ ngắt.

Khi kích hoạt 1 ngắt bộ vi điều khiển đi qua các bước sau:

+ Vi điều khiển kết thúc lệnh đang thực hiện và lưu trữ địa chỉ của lệnh kế tiếp vào
ngăn xếp.

+ Nó nhảy đến 1 vị trí cố định trong bộ nhớ được gọi là bảng vectơ ngắt nơi lưu
giữ địa chỉ của 1 trình phục vụ ngắt.

+ Bộ vi điều khiển nhận địa chỉ ISR từ bảng vectơ ngắt và nhảy tới đó. Nó bắt đầu
thực hiện trình phục vụ ngắt cho đến lệnh cuối cùng của ISR và RETI (trở về từ ngắt).

+ Khi thực hiện lệnh RETI, vi điều khiển quay trở về nơi nó đã bị ngắt. Trước hết
nó nhận địa chỉ của bộ đếm chương trình PC ngăn xếp bằng cách kéo 2 byte trên đỉnh của
ngăn xếp vào PC sau đó bắt đầu thực hiện các lệnh từ địa chỉ đó.

102
Bảng 3.14.Bảng vectơ ngắt của Atmega16

Vector Program Source Interrupt Definition


No Address
1 S000 RESET External Pin, Power-on Reset. Brown-
out Reset. Watchdog Reset, and JTAG
AVR Reset
2 S002 INTO External Interrupt Request 0
3 S004 INT1 External Interrupt Request 1
4 S006 TIMER2 COMP Timer/Counter 2 Compare Match
5 S008 TIMER2 OVF Timer/Counter 2 Overflow
6 S00A TIMER 1 CAPT Timer/Counter l Capture Event
7 S00C TIMER 1 COM PA Timer/Counter l Compare Match A
8 S00E TIMER 1 COMPB Timer/Counter l Compare Match B
9 S010 TIMER 1 OVF Timer/Counter l Overflow
10 S012 TIMERO OVF Timer/Counter0 Overflow
11 S014 SPI. STC Serial Transfer Complete
12 S016 US ART, RXC USART, Rx Complete
13 S018 USART, UDRE USART Data Register Empty
14 S01A US ART, TXC USART, Tx Complete
15 S01C ADC ADC Conversion Complete
16 S01E EE_RDY EEPROM Ready
17 S020 ANA_COMP Analog Comparator
18 S022 TWI Two-wire Serial Interface
19 S024 INT2 External Interrupt Request 2
20 S026 TIMERO COMP Timer/Counter0 Compare Match
21 S028 SPM_RDY Store Program, Memory Ready
b. Hoạt động
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 atmega16 có 2 ngắt ngoài có tên là INT0 và INT1 tương ứng 2 chân (PD2) và
(PD3). Khi làm việc 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.

      Thanh ghi điều khiển MCU – MCUCR (MCU Control Register) là thanh ghi xác
lập chế độ ngắt cho ngắt ngoài:

103
Hình 3.9. Kết nối ngắt ngoài cho atmega16.

Thanh ghi MCUCR chứa các bits cho phép ta chọn 1 trong 4 MODE trên cho các
ngắt ngoài.

MCUCR là một thanh ghi 8 bit nhưng đối với hoạt động ngắt ngoài, 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. Dưới đây là bảng giá trị của 2 bit ISC11, ISC10.
Bảng giá trị cho các bit ISC01, ISC00 hoàn toàn tương tự.

Bảng 3.14: Bảng giá trị của 2 bit ISC11, ISC10

     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ạ

104
ngắt thông thường GIMSK, 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
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), 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 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.

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 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). Một chú ý khác là vì các chân PD2, PD3 là các chân ngắt nên phải set các
chân này là Input (set thanh ghi DDRD).

3.4. Lập trình cho vi điều khiển AVR

3.4.1. Giới thiệu phần mềm


Phần mềm CodeVision

Phần mềm CodeVision là phần mềm hỗ trợ lập trình cho vi xử lý AVR bằng ngôn

105
ngữ C. Các ví dụ lập trình sau đây sẽ đều được thực hiện trên phần mềm này:

Phần mềm mô phỏng Proteus

Proteus là phần mềm mô phỏng mạch điện trên máy tính, có giao diện trực quan,
với các linh kiện điện tử được sắp xếp trên màn hình và được nối dây, đặt các thông số để
có thể chạy thử mạch điện trên máy tính.

Proteus có sẵn rất nhiều các linh kiện điện tử có thể tìm thấy trên thị trường, bao
gồm cả các vi xử lý AVR. Các lệnh theo ngôn ngữ C sau khi được viết bằng Code Vision
có thể được đưa vào Proteus để chạy vi xử lý AVR tương ứng. Dùng Proteus ta có thể
kiểm tra xem lập trình viết trong CodeVision đã đúng hay chưa, như vậy không cần thiết
phải thử trên mạch thật, tiết kiệm thời gian, tiền bạc.

Tuy nhiên do là mô phỏng trên máy tính, nên một số phần quan trọng trong mạch
thực (nguồn điện, sự tương thích về công suất giữa các phần tử...) không được tính đến,
dẫn tới mạch điện mô phỏng chỉ có giá trị tham khảo, kiểm tra nguyên lý hoạt động trước
khi tiến hành làm mạch thật.

Mạch nạp và phần mềm nạp

Sau khi thử trên Proteus, và làm một mạch điện thật bên ngoài có vi xử lý AVR, để
cho vi xử lý AVR chạy được những gì đã lập trình, phải nạp chương trình cho nó (giống
như cài phần mềm cho máy tính), muốn làm điều đó ta cần mạch nạp và phần mềm nạp.

Mạch nạp là một mạch điện nối giữa vi xử lý và máy tính, còn phầm mềm nạp
được cài trên máy tính để điều khiển mạch nạp. Sau khi nạp, chương trình ta viết sẽ
được nhớ trong bộ nhớ của vi xử lý, và vi xử lý sẽ hoạt động theo như chương trình đó.
Ta có thể dùng các chương trình nạp như PonyProg2000, Winpic800, ISPpro…

3.4.2. Lập trình cho Timer.


Các bộ T/C trên ATmega16 có thể đảm nhiệm các vai trò khác nhau với cách thức
hoạt động và thiết lập khác nhau, nên cấu trúc và cách sử dụng tương đối phức tạp. Vì
vậy ví dụ cho lập trình T/C được trình bày sau đây là chức năng đơn giản nhất của bộ

106
T/C, chức năng định thời.

Đề bài: Dùng bộ T/C điều khiển bật/tắt đèn led theo chu kỳ 0,5s hoặc 2s theo tác
động của người dùng.

a. Phân tích, xây dựng nguyên lý điều khiển và mạch mô phỏng

Do yêu cầu hoạt động của mạch giống với mạch ví dụ Lập trình ngắt ngoài INT,
nên các bước phân tích mạch, xây dựng nguyên lý điều khiển và mạch mô phỏng trên
Proteus được tiến hành giống như trong ví dụ Lập trình ngắt ngoài.

b. Lập trình cho vi xử lý

Vi xử lý điều khiển thay đổi mức trạng thái 0 và 1 của chân PB5 theo chu kỳ 0,5s
hoặc 2s theo tác động ngoài. Chu kỳ thay đổi được tạo ra nhờ bộ T/C.

Người đọc sau khi đọc phân giới thiệu chữ năng IN/OUT, ngắt ngoài, T/C và làm
ví dụ. Lập trình ngắt ngoài sẽ thấy rằng với bài tập này chỉ cần dùng hàm delay_ms() đơn
giản để tạo chu kỳ thay đổi trạng thái chân điều khiển (PB5). Tuy nhiên chức năng định
thời của bộ T/C khác với hàm delay_ms() ở chỗ T/C tạo khoảng thời gian theo các ngắt
trong, còn hàm delay_ms() tạo các khoảng thời gian bằng cách dừng chương trình chính
trong vòng lặp while chính. Với các bài toán sử dụng nhiều chức năng của vi xử lý hơn,
tính dừng chương trình của hàm delay_ms() có thể ảnh hưởng xấu và làm sai lệch hoạt
động của vi xử lý so với ý đồ lập trình ban đầu.

Như vậy có thể thấy nếu dùng T/C, thì chương trình trong vòng lặp while chính
vẫn được tiếp tục thực hiện khi vi xử lý đang đếm khoảng thời gian. Điều này rất quan
trọng khi lập trình sử dụng cùng lúc nhiều chức năng khác nhau của vi xử lý, vì việc xác
định khoảng thời gian không làm gián đoạn tới các tiến trình hoạt động khác.

Sau khi hiểu sự khác nhau cơ bản giữa việc định thời gian bằng T/C và hàm delay,
ta đi xây dựng thuật toán điều khiển. Như trong phần giới thiệu chức năng T/C của
ATmega16, bộ T/C1, để thay đổi quãng thời gian xảy ra ngắt của bộ T/C1, ta cần thay
đổi giá trị ban đầu của biến TCNT1 ứng với các khoảng thời gian 0,5s và 2s. Kết hợp với
các ví dụ trên, ta có thể đưa ra sơ đồ làm việc của bộ Int0 và T/C1 như sau:

107
Hình 3.10: Sơ đồ làm việc bộ INT0 và T/C1

Việc thay đổi giá trị của biến TCNT1 ứng với chu kỳ 0,5s và 2s được thực hiện trong
chương trình ngắt ngoài INT0 tương tự như thay đổi biến x trong ví dụ Lập trình ngắt
ngoài INT.

Bộ T/C1 sẽ liên tục định ra các khoảng thời gian dựa theo biến TCNT1.

Khi đếm được đủ khoảng thời gian tương ứng, chương trình ngắt của bộ T/C1 sẽ
chuyển trạng thái chân PB5 để điều khiển bật/tắt led.

Việc xác định giá trị cần gán cho biến TCNT1 ứng với các chu kỳ 0,5s và 2s cho vi
xử lý ATmega16, tần số thạch anh 4Mhz như sau:

Chuẩn bị lập trình bằng phần mềm CodeVisionAVR

Các bước chọn vi xử lý, tần số thạch anh, thiết lập In/Out chân PB5, thiết lập ngắt
ngoài INT0 giống như trong ví dụ Lập trình ngắt ngoài. Ngoài ra vì ví dụ này sử dụng bộ
T/C1 nên ta phải thiết lập các thông số ban đầu cho T/C1.

108
Hình 3.11: Thiết lập ban đầu cho bộ T/C1

Để kích hoạt bộ T/C1, trong cửa sổ CodeWizardAVR, ta vào mục Timers ->
Timerl. Chế độ mặc định của T/Cl là chức năng định thời: Normal top=0xFFFF (chế độ
định thời bình thường, giá trị TOP = FFFF theo hệ cơ số 16, tức là bằng 65535 trong hệ
sơ số thập phân). Để chọn Prescale, kích hoạt ngắt của T/Cl, giá trị ban đầu của biến
TCNT1, ta làm qua 3 bước như trên hình 46.

Bước 1 - Chọn Prescale: Ta đã chọn tần số thạch anh là 4Mhz = 4000 Khz,
4000
Prescale = 256, tức là tần số làm việc của bộ T/C1 là = 15.625Khz. Trong 256mục
Clock value, ta click chọn tần số làm việc của bộ T/C1 là 15,625 Khz.

Bước 2 - Kích hoạt ngắt T/C1: Để kích hoạt ngắt T/C1 khi đếm được khoảng thời
gian mong muốn (0,5s hoặc 2s ứng với thời điểm biến TCNT1 đếm tới 65535), ta click
chọn “Timerl Overflow” trong mục Interrupt on.

Bước 3 - Chọn giá trị ban đầu cho biến TCNT1 viết theo hệ sơ số thập phân trong
ô Value. Ta chọn giá trị “elac” ứng với 0,5s. Trong chương trình, ta có thể thay đổi giá trị
này thành “85ed” tương ứng với chu kỳ 2s.

Viết chương trình điều khiển

Như ở phần xây dựng thuật toán điều khiển, ta thấy rằng quá trình điều khiển trạng
thái chân PB5 không nằm trong vòng lặp while chính. Việc chọn giá trị TCNT1 ban đầu
được thực hiện trong chương trình ngắt ngoài INT0 và thay đổi trạng thái chân PB5 được

109
thực hiện trong chương trình ngắt của T/C1.

Do không sử dụng hàm delay_ms() cũng như các tính năng nào khác cần có file
header, nên ta không cần include thêm file header nào khác.

Khai báo biến x kiểu unsigned int để gán giá trị biến TCNT1 ban đầu. Như đã nói
kiểu int có giá trị từ -32768 tới +32767, còn kiểu unsigned int có giá trị từ 0 tới +65535,
vì vậy ta chọn kiểu dữ liệu unsigned int.

Tương tự như trong ví dụ lập trình ngắt ngoài, chương trình ngắt ngoài trong ví dụ
này cũng có chức năng thay đổi giá trị của biến x. Như đã trình bày ở trên, ứng với chu kỳ
0,5s hoặc 2s ta có giá trị của biến x là x = 0xE1AC và x = 0x85ED.

Hai ký tự 0x ở trên là hai ký tự dùng để thông báo cho vi xử lý biết rằng giá trị viết
đằng sau theo hệ cơ số 16. Nếu muốn viết theo hệ cơ số 2, thì hai ký tự tương ứng sẽ là
0b. Còn nếu viết theo hệ sơ số 10 thì không cần chèn 2 ký tự vào trước giá trị.

Ví dụ số 214 trong hệ thập phân ứng với 11010110 trong hệ cơ số 2 và D6 trong


hệ cơ số 16. Khi viết vào chương trình, để cho vi xử lý hiểu thì ta phải viết 214 hoặc
0b11010110 hoặc 0xD6

110
Sau khi làm ví dụ Lập trình ngắt ngoài, ta thấy chương trình con trong ví dụ này cũng
tương tự như sau:

Chương trình ngắt


của T/C1 xảy ra khi biến TCNT1 đếm tới giá trị TOP (ở đây là 65535). Theo như thuật
toán đã xây dựng, ngắt của T/C1 sẽ nghịch đảo trạng thái của chân PB5 và gán lại giá trị
của biến TCNT1 bằng giá trị của biến x.

Khi tạo project, cấu trúc trong chương trình ngắt của T/C1 đã có sẵn như sau.

Như hình trên, phần gán lại giá trị cho biến TCNT1 đã được viết sẵn, và luôn luôn
gán lại giá trị E1AC đã thiết lập trong phần chuẩn bị T/C1. Vì vậy phải sửa lại đoạn code
này để gán giá trị x cho biến TCNT1 thay vì giá trị E1AC. Người đọc có thể dễ dàng thấy
rằng chỉ cần thay giá trị E1AC ở đoạn code trên bằng giá trị x như sau:

TCNT1H=x >> 8;

TCNT1L=x & 0xff;

Để hiểu rõ đoạn code trên, người đọc cần phải có kiến thức về cấu trúc các thanh ghi,
cũng như các phép dịch bit, các phép toán logic trong lập trình C. Để đơn giản hơn, có thể

111
dùng chỉ một lệnh duy nhất sau đây để thay cho đoạn code trên.

TCNT1=x;

Như vậy đoạn code dùng để gán lại giá trị cho biến TCNT1 sau khi sửa lại như sau.

Tiếp theo ta cần viết đoạn code điều khiển trạng thái của chân PB5 vào trong chương
trình ngắt của T/C1. Trong các ví dụ trước, người đọc đã biết cách gán trạng thái cho chân
PB5 trực tiếp bằng 0 hoặc 1. Tuy nhiên có một cách ngắn hơn để thực hiện việc nghịch
đảo trạng thái chân PB5 bằng dòng lệnh sau:

PORTB.5 = ~PORTB.5;

Ký hiệu ~ ở trên là ký hiệu của phép nghịch đảo, dùng trong các phép toán logic. Câu
lệnh trên có nghĩa là giá trị trạng thái chân PB5 sẽ được gán bằng nghịch đảo của giá trị
trạng thái chân PB5 hiện tại. Nếu trạng thái chân PB5 đang là 1, thì sau khi qua câu lệnh
này, trạng thái chân PB5 sẽ được gán bằng 0 và ngược lại.

Như vậy chương trình ngắt của T/C1 sẽ là.

Sau khi viết code, kiểm tra và complie thành công, chạy thử trên mạch mô phỏng
Proteus, người đọc sẽ thấy mạch hoạt động như trong ví dụ Lập trình ngắt ngoài. Ban đầu
led sẽ nháy theo chu kỳ 0,5s, khi nhấn nút, chu kỳ nháy là 2s, nhấn nút lần nữa, chu kỳ
nháy sẽ trở về 0,5s...

112
3.4.3. UART.
Thông thường, để sử dụng module USART trên AVR phải thực hiện 3 việc quan
trọng, đó là: cài đặt tốc độ baud (thanh ghi UBRR), định dạng khung truyền (UCSRB,
UCSRC) và cuối cùng kích hoạt bộ truyền, bộ nhận, ngắt…Như đã đề cập, trong tài liệu
này tôi chủ yếu đề cập đến phương pháp truyền thông không đồng bộ, việc xác lập các
thông số hoạt động chủ yếu dựa trên chế độ này. Trong hầu hết các ứng dụng, tốc độ baud
và khung truyền thường không đổi, trong trường hợp này ta có thể khởi tạo trực tiếp
USART ở phần đầu trong main và sau đó chỉ cần truyền hoặc nhận dữ liệu mà không cần
thay đổi các cài đặt. Tuy nhiên, nếu trường hợp giao tiếp “linh hoạt” ví dụ đang chế tạo
một thiết bị có khả năng giao tiếp với một thiết bị đầu cuối khác (như máy tính chẳng
hạn), lúc này nên cho phép người dùng thay đổi tốc độ baud hoặc các thông số khác để
phù hợp với thiết bị đầu cuối. Đối với những ứng dụng kiểu này nên viết 1 chương trình
con để khởi động USART và có thể gọi lại nhiều lần khi cần thay đổi. 

Trong ví dụ này sẽ ta thiết lập và sử dụng chức năng truyền nhận dữ liệu với máy tính
bằng USART ở mức độ đơn giản.

Đề bài: Lập trình sử dụng chức năng USART của Atmega16, đo điện áp từ mạch
phân áp (0~+5V) và gửi về máy tính.

Phân tích

Từ đề bài, ta thấy mạch điện của ví dụ có các khối sau.

+ Vi xử lý.

+ Mạch phân áp.

+ Thiết bị nhận dữ liệu từ vi xử lý.

Vi điều khiển trong ví dụ vẫn dùng Atmega16, mạch phân áp và ngắt ngoài giống như
các ví dụ đã trình bày. Thiết bị nhận dữ liệu truyền về từ vi xử lý có thể là máy vi tính
(qua cổng COM) hoặc thiết bị VIRTUAL TERMINAL khi mô phỏng trên Proteus.

Mạch điện hoạt động như sau: vi xử lý tiến hành đo điện áp từ mạch phân áp đặt trên

113
kênh ADC và gửi giá trị đo về và hiển thị trên thiết bị Terminal.

Nguyên lý điều khiển và mạch mô phỏng

Nguyên lý điều khiển của bài ví dụ này được cho như hình dưới.

Điện áp từ đầu ra mạch phân áp được đưa vào kênh ADC của vi xử lý Atmega16. Khi
có tín hiệu bàn phím từ trình Terminal, vi xử lý sẽ đo điện áp đặt vào kênh ADC và gửi
giá trị đo được tới trình Terminal.

Từ nguyên lý điều khiển, ta xây dựng mạch mô phỏng như sau.

Lập trình cho vi xử lý

Xác định yêu cầu làm việc của vi xử lý

Từ đề bài và nguyên lý điều khiển, các chức năng của Atmega16 cần dùng trong ví
dụ này là:

114
+ Chức năng ADC.

+ Chức năng USART.

+ Chức năng ngắt ngoài sẽ nhận biết tác động nhấn nút của người dùng. Các bước
trong chương trình điều khiển như sau.

Bộ ADC sẽ đo và vi xử lý tính toán giá trị điện áp.

Vi xử lý lại dùng chức năng USART để truyền giá trị đo được về trình Terminal.

Giá trị điện áp đo được sau khi tính toán có thể là giá trị thập phân với nhiều chữ
số sau dấu phẩy, trong khi giá trị cần đo để xử lý (xử lý và vẽ đồ thị trên máy tính...) chỉ
cần khoảng 2 chữ số saudấu phẩy. Vì vậy chỉ cần gửi giá trị điện áp sau khi đã được làm
tròn.

Đồng thời trong lập trình vi xử lý, quá trình xử lý các biến có giá trị thập phân(có dấu
phẩy) tương đối phức tạp, phải sử dụng kiểu biến tốn bộ nhờ hơn, cũng như nhiều câu
lệnh các phép toán và chuỗi., vì vậy trong ví dụ này sẽ không hiển thị dấu phẩy (ví dụ giá
trị 3,25 sẽ được hiển thị thành 325).

Trong đo đạc thực tế, giá trị “325” ở trên sau khi gửi về máy tính, sẽ được phần mềm
xử lý trên máy tính quy đổi về giá trị thực (325 chuyển về 3,25 bằng cách chia cho 100
chẳng hạn). Cách làm này khiến cho việc lập trình trên vi xử lý đơn giản hơn nhiều.

b) Xây dựng thuật toán điều khiển

Từ yêu cầu làm việc của vi xử lý, ta xây dựng thuật toán điều khiển như sau.

115
Bộ ADC tiến hành đo giá trị điện áp, sau đó giá trị sau khi chuyển về dạng ký tự sẽ
được bộ USART truyền tới trình Terminal. Quá trình này lặp đi lặp lại liên tục để đo và
gửi giá trị điện áp, như vậy toàn bộ chương trình nằm trong vòng lặp while chính. Việc
dừng chương trình trong 0,25s với mục đích để mắt người có thể theo dõi kết quả mô
phỏng trên proteus. Trong thực tế đo đạc, chu kỳ đo-chuyển-truyền trên chỉ mất khoảng
10ms.

Lý do phải chuyển giá trị số sang dạng ký tự là vì bộ USART chỉ truyền/nhận dữ


liệu dạng các ký tự riêng lẻ, ví dụ như ký tự a, b, c,. ký tự 1, 2, 3.. Chẳng hạn muốn truyền
giá trị điện áp 12 V về máy tính, trước tiên phải chuyển về dạng ký tự '12' (gọi là một
chuỗi, bao gồm ký tự '1' và ký tự '2' ), sau đó bộ USART sẽ truyền lần lượt ký tự '1' và ký
tự '2' này về máy tính. Phần mềm xử lý trên máy tính sẽ ghép hai ký tự trên thành một
chuỗi, sau đó chuyển lại thành dạng số.

Quá trình gửi dữ liệu từ vi xử lý về máy tính

116
Chuẩn bị lập trình bằng phần mềm CodeVision

Trong bài ví dụ này chỉ sử dụng chức năng ADC và USART của Atmega16, vì vậy
trong bước chuẩn bị ta sẽ kích hoạt và thiết lập ban đầu cho hai chức năng này.

Với chức năng ADC, việc kích hoạt và thiết lập ban đầu được tiến hành giống như
trong ví dụ Chuyển đổi ADC, người đọc hãy xem lại và tự tiến hành bước này.

Để kích hoạt và thiết lập chức năng USART, làm như hình sau.

Kích hoạt USART

Trong hộp thoại CodeWizardAVR, chọn mục USART và làm các bước.

Để kích hoạt chức năng truyền dữ liệu, click chọnvào ô “Transmitter”.

Để chọn tốc độ Baud Rate, click và chọn tốc độ Baud Rate trong ô “Baud Rate”.

Để chọn kiểu khung truyền (Frame), chọn trong ô “Communication Parameters”.

Như trên hình 80, ta thấy trong bài ví dụ kích chỉ sử dụng chức năng truyền dữ liệu
của bộ USART. Giống như trong phần giới thiệu tính năng USART, tốc độ Baud Rate =
9600 và khung truyền 8 Data, 1 Stop, không Parity được chọn như trên hình 80.

Viết chương trình điều khiển

Khai báo biến và include các file header

Trong chương trình này dùng các biến sau.

117
Biến V kiểu unsigned int, biến này dùng để chứa giá trị điện áp không có dấu phẩy
(đã nhân với 100).

Biến chuoi dạng chuỗi 10 ký tự. Khai báo biến chuoi theo cú pháp sau.

char chuoi[10];

Include các file header sau (nếu phần mềm CodeVision đã include sẵn thì không cần viết
thêm vào nữa).

delay.h dùng cho chức năng ngừng chương trình.

stdlib.h dùng cho chức năng chuyển giá trị số thành dạng chuỗi ký tự.

stdio.h dùng cho chức năng USART.

Từ phần xây dựng thuật toán điều khiển, ta thấy rằng toàn bộ chương trình nằm
trong vòng lặp while chính có cấu trúc như sau.

Đo giá tri điện áp

Sau khi xem bài ví dụ Chuyển đổi ADC, người đọc đã có thể tự viết đoạn code đo
giá trị điện áp trên hình 81. Sau khi viết, hãy so sánh với mẫu tham khảo sau.

= 5*read_adc(0)/10.23;

Chuyển giá tri số thành chuỗi ký tự

Mục đích của đoạn code này là chuyển giá trị số trong biến V thành chuỗi ký tự và
chữa trong biễn chuoi. Để chuyển một giá trị nguyên (int) của biến V thành chuỗi ký tự
của biến chuoi, sử dụng dòng lệnh sau.

itoa(V, chuoi);

Truyền chuỗi ký tư qua bô USART

Để truyền một chuỗi nhiều ký tự (biến chuoi) qua bộ USART, ta dùng hàm puts()
sau.

puts(chuoi);

118
Khi gặp lệnh puts(chuoi); vi xử lý sẽ gửi lần lượt các ký tự trong biến chuoi qua bộ
USART như đã trình bày.

Còn lệnh dừng chương trình 0,25s người đọc hãy tự viết và tự kiểm tra.

Sau khi viết xong, toàn bộ code trong vòng lặp while chính như sau.

Vòng lặp while chính

Khi viết xong, kiểm tra và complie thành công, tiến hành chạy trên mạch mô phỏng.
Người đọc hãy nhớ lại rằng trong truyền/nhận USART, thiết bị truyền và nhân phải được
thiết lập giống nhau. Từ đầu ví dụ ta mới chỉ thiết lập USART trên vi xử lý, để thiết lập
USART trên trình Terminal, mở hộp thoại Edit Component của trình Terminal và chọn
các thông số giống với USART trên vi xử lý.

Ý nghĩa có dòng code: putchar(13); là gửi một ký tự thứ 13 trong bảng mã ASCII qua
bộ USART. Trong bảng mã ASCII, ký tự thứ 13 là “\r”. Khi gặp ký tự này, trình
Terminal sẽ xuống dòng.

Sau khi sửa xong, hãy chạy lại mạch mô phỏng trên Proteus, thay đổi giá trị biến trở
và quan sát dữ liệu gửi về trên trình Terminal.

3.4.4. Ngắt.
3.4.4.1. Lập trình ngắt ngoài (INT)

Ngắt ngoài là một tính năng của vi xử lý ATmega16 cho phép vi xử lý giao tiếp với
người dùng (qua thao tác bấm nút) hoặc nhận biết sự kiện bên ngoài (đếm sự kiện).

Bài ví dụ lập trình ngắt ngoài sẽ trình bày trong tài liệu này như sau: Điều khiển
tốc độ nhấp nháy của đèn led (0,5s hoặc 2s) qua thao tác nhấn nút điều khiển.

119
a. Phân tích mạch điện

Ban đầu khi cấp điện, vi xử lý điều khiển sáng/tắt đèn led theo chu kỳ 0,5s. Khi
người dùng nhấn nút điều khiển, chu kỳ sáng/tắt đèn led được điều chỉnh thành 2s.

Khi người dùng nhấn nút điều khiển lần nữa, chu kỳ sáng/tắt đèn led lại được điều
chỉnh về 0,5s.

Như vậy ta thấy về cơ bản, phần lập trình điều khiển sáng/tắt led tương tự như ví
dụ Điều khiển thiết bị ngoài, chỉ khác ở chỗ chu kỳ sáng/tắt (trong hàm delay_ms() ) được
thay đổi theo tác động bấm nút của người dùng nhờ tính năng ngát ngoài.

b. Nguyên lý điều khiển và xây dựng mạch mô phỏng trên Proteus

Nguyên lý điều khiển giống như trong bài ví dụ Điều khiển thiết bị ngoài.

Mạch mô phỏng trên Proteus cũng tương tự ví dụ Điều khiển thiết bị ngoài, trừ
việc có thêm một nút bấm đưa vào chân PD2/INT0 để điều khiển ngắt INT0.

c. Lập trình cho vi điều khiển

Xác định yêu cầu làm việc của vi điều khiển

Vi điều khiển bật/tắt đèn Led theo chu kỳ 0,5s và 2s theo tác động bấm nút của
người dùng.

Ý tưởng của thuật toán này như sau

Chu kỳ nháy được đặt bằng một biến x, trong chương trình, x hoặc có giá trị 500
(0,5s) hoặc 2000 (2s).

Khi ngắt ngoài được kích hoạt, chương trình ngắt gán giá trị x = 2000 (nếu giá trị
hiện thời của x = 500), hoặc gán giá trị x = 500 (nếu giá trị hiện thời của x = 2000).

Chương trình chính sẽ thực hiện điều khiển nháy led với chu kỳ x.

Như vậy khi người dùng nhấn nút, sẽ kích hoạt ngắt ngoài, đổi giá trị biến x, đèn
led sẽ được điều khiển nhấp nháy theo giá trị biến x tương ứng.

120
Chuẩn bị lập trình bằng phần mềm CodeVision.

Các bước chọn vi xử lý, tần số thạch anh, chức năng I/O cho chân PB5 được thực
hiện giống như ví dụ Điều khiển thiết bị ngoài.

Để kích hoạt tính năng INT0, trong cửa sổ CodeWizardAVR, chọn thẻ External
IRQ (ngắt ngoài) .

Viết chương trình điều khiển.

Trong ví dụ này có sử dụng biến x để đặc trưng cho chế độ làm việc của mạch.
Biến là một khái niệm trong lập trình, để chỉ các giá trị số, giá trị logic, chuỗi ký tự,
matrận... Biến có thể có giá trị không đổi hoặc thay đổi trong lập trình tùy thuộc vào cách
sử dụng.

Mỗi kiểu biến có đặc trưng khác nhau (để chỉ giá trị số, giá trị logic...), muốn sử
dụng biến trong lập trình cần phải có bước “khai báo”. Bao gồm khai báo tên biến (x, y,
bien1, bien2,...) và kiểu dữ liệu(char, str, ...). Các kiểu và thuộc tính của kiểu dữ liệu được
cho trong bản phần phụ lục.

Trong ví dụ này ta khai báo biến x là biến kiểu int (giá trị số nguyên từ -32768 ~
+32767).

Include thêm file header sau: delay.h.

Khai báo biến bằng dòng lệnh: char x=500; Ở đây ta gán cho x giá trị x=500 ngay
từ đầu, để khi cấp điện, đèn led nhấp nháy ngay với chu kỳ 0,5s.

Khai báo biến và nơi viết chương trình ngắt

Viết chương trình phục vu ngắt ngoài: Trình phục vụ ngắt ngoài được viết tại vị trí

121
như trong hình trên. Cấu trúc chương trình ngắt có thể được hình dung như sau:

interrupt [EXT_INT0] void ext_int0_isr(void)

{ Nếu x=500, gán giá trị x=2000;

Nếu x không bằng 500, gán giá trị x=500;}

Phép so sánh điều kiện “nếu” ở trên trong lập trình được gọi là phép so sánh if, cấu
trúc của lệnh if như sau:

if (biểu thức so sánh)

{Các dòng lệnh A;}

else { Các dòng lệnh B;}

Trong đó “biểu thức so sánh” là một biểu thức logic, giá trị trả về là 0 hoặc 1. Ví
du khi muốn so sánh giá trị của x (có giá trị hiện thời là 2000) xem có bằng 500 hay
không, ta viết: x == 500;

Ký hiệu hai dấu bằng “==” là ký hiệu của phép so sánh, như dòng lệnh trên, giá trị
trả về sẽ bằng 0, vì giá trị hiện thời của x là 2000 khác 500

Nếu biểu thức so sánh trả về giá trị 1, các dòng lệnh a sẽ được thực hiện, nếu giá
trị biểu thức so sánh trả về bằng không, các dòng lệnh B sẽ được thực hiện.

Chương trình trong vòng lặp while chính tương tự như trong ví dụ Điều khiển

122
thiết bị ngoài, chỉ khác ở chỗ giá trị 500 ms chu kỳ nháy được thay bằng biến x.

3.5. Giới thiệu Arduino


3.5.1. Tổng quan về Arduino
Arduino thực chất là một bo mạch vi xử lý được dùng để lập trình tương tác với
các thiết bị phần cứng như cảm biến, động cơ, đèn hoặc các thiết bị khác. Đặc điểm nổi
bật của Arduino là môi trường phát triển ứng dụng cực kỳ dễ sử dụng, với một ngôn ngữ
lập trình có thể học một cách nhanh chóng ngay cả với người ít am hiểu về điện tử và lập
trình. Và điều làm nên hiện tượng Arduino chính là mức giá rất thấp và tính chất nguồn
mở từ phần cứng tới phần mềm.

Ở đây ta chọn Arduino Uno R3 làm đối tượng nghiên cứu.

Arduino Uno là sử dụng chip Atmega328. Nó có 14 chân digital I/O, 6 chân đầu
vào (input) analog, thạch anh dao động 16Mhz. Một số thông số kỹ thuật như sau :

Bảng 3.15. Bảng thông số kỹ thuật như Arduino Uno R3:

Chip ATmega328
Điện áp cấp nguồn 5V
Điện áp đầu vào (input) (kiến nghị ) 7-12V
Điện áp đầu vào(giới hạn) 6-20V
Số chân Digital I/O 14 (có 6 chân điều chế độ rộng xung PWM)
Số chân Analog (Input ) 6
DC Current per I/O Pin 40 mA
DC Current for 3.3 V Pin 50 mA
Flash Memory 32KB (ATmega328) với 0.5KB sử dụng bootloader
SRAM 2 KB (ATmega328)
EEPROM 1 KB (ATmega328)
Xung nhịp 16 MHz

123
3.5.2. Sơ đô chân của Arduino.

Hình 3.12. Arduino Uno R3

a.USB (1).

Arduino sử dụng cáp USB để giao tiếp với máy tính. Thông qua cáp USB ta có
thể Upload chương trình cho Arduino hoạt động, ngoài ra USB còn là nguồn cho
Arduino.

b.Nguồn ( 2 và 3 ).

Khi không sử dụng USB làm nguồn thì ta có thể sử dụng nguồn ngoài thông qua
jack cắm 2.1mm ( cực dương ở giửa ) hoặc có thể sử dụng 2 chân Vin và GND để cấp
nguồn cho Arduino.

Bo mạch hoạt động với nguồn ngoài ở điện áp từ 5 - 20 volt. Ta có thể cấp một áp
lớn hơn tuy nhiên chân 5V sẽ có mực điện áp lớn hơn 5 volt. Và nếu sử dụng nguồn lớn
hơn 12 volt thì sẽ có hiện tượng nóng và làm hỏng bo mạch, nên dùng nguồn ổn định là 5
đến dưới 12 volt.

Chân 5V và chân 3.3V (Output voltage): các chân này dùng để lấy nguồn ra từ
nguồn mà ta đã cung cấp cho Arduino. Lưu ý không được cấp nguồn vào các chân này
vì sẽ làm hỏng Arduino. GND: chân mass.

124
c. Chip Atmega328.

Chip Atmega328 Có 32K bộ nhớ flash trong đó 0.5k sử dụng cho bootloader. Ngoài ra
còn có 2K SRAM, 1K EEPROM.

d. Input và Output ( 4, 5 và 6).

Arduino Uno có 14 chân digital với chức năng input và output sử dụng các hàm
pinMode(), digitalWrite() và digitalRead() để điều khiển các chân này tôi sẽ đề cập chúng
ở các phần sau.

Cũng trên 14 chân digital này ta còn một số chân chức năng đó là:

+ Serial: chân 0 (Rx ), chân 1 ( Tx). Hai chân này dùng để truyền (Tx) và nhận
(Rx) dữ liêu nối tiếp TTL. ta có thể sử dụng nó để giao tiếp với cổng COM của một số
thiết bị hoặc các linh kiện có chuẩn giao tiếp nối tiếp.

+ PWM (pulse width modulation): các chân 3, 5, 6, 9, 10, 11 trên bo mạch có dấu
là các chân PWM ta có thể sử dụng nó để điều khiển tốc độ động cơ, độ sáng của đèn...

+ SPI : 10 (SS), 11 (MOSI), 12 (MISO), 13 (SCK), các chân này hỗ trợ giao tiếp
theo chuẩn SPI.

+ I2C: Arduino hỗ trợ giao tiếp theo chuẩn I2C. Các chân A4 (SDA) và A5 (SCL)
cho phép tao giao tiếp giửa Arduino với các linh kiện có chuẩn giao tiếp là I2C.

+ Reset (7): dùng để reset Arduino.

Giới thiệu phần mềm

Ta truy cập vào trang web http://arduino.cc/en/Main/Software và tải về chương


trình Arduino IDE phù hợp với hệ điều hành của máy mình bao gồm Windown, Mac
OS hay Linux. Đối với Windown có bản cài đặt (.exe) và bản Zip, đối với Zip thì chỉ
cần giải nén và chạy chương trình không cần cài đặt.

Sử dụng cáp USB kết nối Arduino với máy tính, lúc này sẽ thấy đèn led power
của bo sáng. Máy tính sẽ nhận dạng thiết bị và sẽ nhận được thông báo:

125
“Device driver software was not successfully installed"

Click vào Start Menu chọn Control Panel chọn System and Security, click System
và sau đó chọn Device Manager. Click chọn “Browse my computer for driver
software”. Chọn đường dẫn tới folder “driver” nơi mà phần mềm Arduino được lưu
trữ. Click “Next”Windown tự động cài đặt driver, qua trình cài đặt driver hoàn
tất.Arduino IDEArduino IDE là nơi để soạn thảo code, kiểm tra lỗi và upload code cho
arduino.

Hình 3.13. Giao diện chương trình Arduino IDE

Arduino Toolbar.

Verify: kiểm tra code có lỗi hay không

Upload: nạp code đang soạn thảo vào Arduino

New, Open, Save: Tạo mới, mở và Save sketch

Serial Monitor: Đây là màn hình hiển thị dữ liệu từ Arduino gửi lên máy tính

Arduino IDE Menu:

126
Trong file menu ta quan tâm tới mục Examples đây là nơi chứa code mẫu ví dụ
như: cách sử dụng các chân digital, analog, sensor ...

Verify/Compile CtrkR Show Sketch Folder Ctrl+K

Sketch menu:

Verify/ Compile: chức năng kiểm tra lỗi code.

Show Sketch Folder: hiển thị nơi code được lưu.

Add File: thêm vào một Tap code mới. v' Import Library: thêm thư viện cho IDE

Tool menu.

Trong Tool menu ta quan tâm các mục Board và Serial Port

Mục Board: các cần phải lựa chọn bo mạch cho phù hợp với loại bo mà sử dụng.

Nếu các sử dụng loại bo khác thì phải chọn đúng loại bo mà mình đang có nếu sai
thì code Upload vào chip sẽ bị lỗi.

Serial Port: đây là nơi lựa chọn cổng Com của Arduino. Khi ta cài đặt driver thì
máy tính sẽ hiện thông báo tên cổng Com của Arduino là bao nhiêu, ta chỉ việc vào Serial
Port chọn đúng cổng Com để nạp code, nếu chọn sai thì không thể nạp code cho Arduino
được.

Hướng dẫn cài đặt bản mô phỏng Arduino trên Proteus.

Để mô phỏng được Arduino trên proteus thì ta cần phải download thư viện arduino
cho proteus. Để có được thư viên này các cần truy cập vào trang web:

http://blogembarcado.blogspot.com/search/label/Proteus

Sau khi download về các chép 2 file ARDUINO.IDX và ARDUINO.LIB vào thư
mục Proteus 7:

Trong thư viện này hỗ trợ 5 loại board Arduino khác nhau trong đó gồm có Arduino Uno,
MEGA, NANO, LILYPAD và UNO SMD và một cảm biến siêu âm Untrasonic.

127
Sau khi chép xong ta khởi động Proteus lên vào thư viện linh kiện bằng cách bấm
phím P và gõ từ khoá là ARDUINO chúng sẽ hiện ra danh sách các board hiện có ở đây ta
chọn Arduino Uno

Hình 3.14. Mô phỏng Arduino bằng Proteus.

Lưu ý ta cần phải cấp nguồn vào 2 chân 5V và Gnd trên mạch như hình trên.

128
Chương 4: GIỚI THIỆU MỘT SỐ VI ĐIỀU KHIỂN TIÊN TIẾN

4.1. Vi điều khiển PIC

Hình 4.1: Vi điều khiển PIC

PIC là một họ vi điều khiển RISC được sản xuất bởi công ty Microchip
Technology. Dòng PIC đầu tiên là PIC1650 được phát triển bởi Microelectronics Division
thuộc General Instrument . PIC bắt nguồn là chữ viết tắt của "Programmable Intelligent
Computer" (Máy tính khả trình thông minh) là một sản phẩm của hãng General
Instruments đặt cho dòng sản phẩm đầu tiên của họ là PIC1650. Lúc này, PIC1650 được
dùng để giao tiếp với các thiết bị ngoại vi cho máy chủ 16bit CP1600, vì vậy, người ta
cũng gọi PIC với cái tên "Peripheral Interface Controller" (Bộ điều khiển giao tiếp ngoại
vi). CP1600 là một CPU tốt, nhưng lại kém về các hoạt động xuất nhập, và vì vậy PIC 8-
bit được phát triển vào khoảng năm 1975 để hỗ trợ hoạt động xuất nhập cho CP1600. PIC
sử dụng microcode đơn giản đặt trong ROM, và mặc dù, cụm từ RISC chưa được sử dụng
thời bây giờ, nhưng PIC thực sự là một vi điều khiển với kiến trúc RISC, chạy một lệnh
một chu kỳ máy (4 chu kỳ của bộ dao động). Năm 1985 General Instruments bán bộ phận
vi điện tử của họ, và chủ sở hữu mới hủy bỏ hầu hết các dự án - lúc đó đã quá lỗi thời.
Tuy nhiên PIC được bổ sung EEPROM để tạo thành 1 bộ điều khiển vào ra khả trình.
Ngày nay rất nhiều dòng PIC được xuất xưởng với hàng loạt các module ngoại vi tích hợp
sẵn (như USART, PWM, ADC...), với bộ nhớ chương trình từ 512 Word đến 32K Word.

129
PIC sử dụng tập lệnh RISC, với dòng PIC low-end (độ dài mã lệnh 12 bit, ví dụ:
PIC12Cxxx) và mid-range (độ dài mã lệnh 14 bit, ví dụ: PIC16Fxxxx), tập lệnh bao gồm
khoảng 35 lệnh, và 70 lệnh đối với các dòng PIC high-end (độ dài mã lệnh 16 bit, ví dụ:
PIC18Fxxxx). Tập lệnh bao gồm các lệnh tính toán trên các thanh ghi, với các hằng số,
hoặc các vị trí bộ nhớ, cũng như có các lệnh điều kiện, lệnh nhảy/gọi hàm, và các lệnh để
quay trở về, nó cũng có các tính năng phần cứng khác như ngắt hoặc sleep (chế độ hoạt
động tiết kiện điện). Microchip cung cấp môi trường lập trình MPLAB, nó bao gồm phần
mềm mô phỏng và trình dịch ASM.

Một số công ty khác xây dựng các trình dịch C, Basic, Pascal cho PIC. Microchip
cũng bán trình dịch "C18" (cho dòng PIC high-end) và "C30" (cho dòng dsPIC30Fxxx).
Họ cũng cung cấp các bản "student edition/demo" dành cho sinh viên hoặc người dùng
thử, những version này không có chức năng tối ưu hoá code và có thời hạn sử dụng giới
hạn. Những trình dịch mã nguồn mở cho C, Pascal, JAL, và Forth, cũng được cung cấp
bởi PicForth. GPUTILS là một kho mã nguồn mở các công cụ, được cung cấp theo công
ước về bản quyền của GNU General Public License. GPUTILS bao gồm các trình dịch,
trình liên kết, chạy trên nền Linux, Mac OS X, OS/2 và Microsoft Windows. GPSIM
cũng là một trình mô phỏng dành cho vi điều khiển PIC thiết kế ứng với từng module
phần cứng, cho phép giả lập các thiết bị đặc biệt được kết nối với PIC, ví dụ như LCD,
LED...

Hiện nay có khá nhiều dòng PIC và có rất nhiều khác biệt về phần cứng, nhưng ta
có thể điểm qua một vài nét như sau:

+ 8/16 bit CPU, xây dựng theo kiến trúc Harvard có sửa đổi

+ Flash và ROM có thể tuỳ chọn từ 256 byte đến 256 Kbyte

+ Các cổng Xuất/Nhập (I/O ports) (mức logic thường từ 0V đến 5.5V, ứng với
logic 0 và logic 1)

+ 8/16 Bit Timer

+ Công nghệ Nanowatt

130
+ Các chuẩn Giao Tiếp Ngoại Vi Nối Tiếp Đồng bộ/Không đồng bộ USART,
AUSART, EUSARTs

+ Bộ chuyển đổi ADC Analog-to-digital converters, 10/12 bit

+ Bộ so sánh điện áp (Voltage Comparators)

+ Các module Capture/Compare/PWM

+ LCD

+ MSSP Peripheral dùng cho các giao tiếp I²C, SPI, và I²S

+ Bộ nhớ nội EEPROM - có thể ghi/xoá lên tới 1 triệu lần

+ Module Điều khiển động cơ, đọc encoder

+ Hỗ trợ giao tiếp USB

+ Hỗ trợ điều khiển Ethernet

+ Hỗ trợ giao tiếp CAN

+ Hỗ trợ giao tiếp LIN

+ Hỗ trợ giao tiếp IrDA

+ Một số dòng có tích hợp bộ RF (PIC16F639, và rfPIC)

+ KEELOQ Mã hoá và giải mã

+ DSP những tính năng xử lý tín hiệu số (dsPIC)

Vi điều khiển 8-bit

+ PIC10, PIC12, PIC14, PIC16, PIC17, PIC18

Vi điều khiển 16-bit:

+ PIC24

Bộ điều khiển xử lý tín hiệu số 16-bit (dsPIC):

+ dsPIC30

131
+ dsPIC33F

Bộ điều khiển xử lý tín hiệu số 32-bit (PIC32):

+ PIC32

4.2. ARM

Hình 4.2: Vi điều khiển PIC

Cấu trúc ARM (viết tắt từ tên gốc là Acorn RISC Machine) là một loại cấu trúc vi
xử lý 32-bit kiểu RISC được sử dụng rộng rãi trong các thiết kế nhúng. Do có đặc điểm
tiết kiệm năng lượng, các bộ CPU ARM chiếm ưu thế trong các sản phẩm điện tử di động,
mà với các sản phẩm này việc tiêu tán công suất thấp là một mục tiêu thiết kế quan trọng
hàng đầu.

Ngày nay, hơn 75% CPU nhúng 32-bit là thuộc họ ARM, điều này khiến ARM trở
thành cấu trúc 32-bit được sản xuất nhiều nhất trên thế giới. CPU ARM được tìm thấy
khắp nơi trong các sản phẩm thương mại điện tử, từ thiết bị cầm tay (PDA, điện thoại di
động, máy đa phương tiện, máy trò chơi cầm tay, và máy tính cầm tay) cho đến các thiết
bị ngoại vi máy tính (ổ đĩa cứng, bộ định tuyến để bàn.) Một nhánh nổi tiếng của họ ARM
là các vi xử lý Xscale của Intel. Trụ sở chính của công ty ARM tại Cambridge Anh)

Việc thiết kế ARM được bắt đầu từ năm 1983 trong một dự án phát triển của công
ty máy tính Acorn. Nhóm thiết kế, dẫn đầu bởi Roger Wilson và Steve Furber, bắt đầu
phát triển một bộ vi xử lý có nhiều điểm tương đồng với Kỹ thuật MOS 6502 tiên tiến.

132
Acorn đã từng sản xuất nhiều máy tính dựa trên 6502, vì vậy việc tạo ra một chip như vậy
là một bước tiến đáng kể của công ty này.

Nhóm thiết kế hoàn thành việc phát triển mẫu gọi là ARM1 vào năm 1985, và vào
năm sau, nhóm hoàn thành sản phẩm “thực” gọi là ARM2. ARM2 có tuyến dữ liệu 32-bit,
không gian địa chỉ 26-bit tức cho phép quản lý đến 64 Mbyte địa chỉ và 16 thanh ghi 32-
bit. Một trong những thanh ghi này đóng vai trò là bộ đếm chương trình với 6 bit cao nhất
và 2 bit thấp nhất lưu giữ các cờ trạng thái của bộ vi xử lý. Có thể nói ARM2 là bộ vi xử
lý 32-bit khả dụng đơn giản nhất trên thế giới, với chỉ gồm 30.000 transistor (so với bộ vi
xử lý lâu hơn bốn năm của Motorola là 68000 với khoảng 68.000 transistor). Sự đơn giản
như vậy có được nhờ ARM không có vi chương trình (mà chiếm khoảng ¼ đến 1/3 trong
68000) và cũng giống như hầu hết các CPU vào thời đó, không hề chứa cache. Sự đơn
giản này đưa đến đặc điểm tiêu thụ công suất thấp của ARM, mà lại có tính năng tốt hơn
cả 286. Thế hệ sau, ARM3, được tạo ra với 4KB cache và có chức năng được cải thiện tốt
hơn nữa. Vào những năm cuối thập niên 80, hãng máy tính Apple Computer bắt đầu hợp
tác với Acorn để phát triển các thế hệ lõi ARM mới . Công việc này trở nên quan trọng
đến nỗi Acorn nâng nhóm thiết kế trở thành một công ty mới gọi là Advanced RISC
Machines. Vì lý do đó thường được giải thích ARM là chữ viết tắt của Advanced RISC
Machines thay vì Acorn RISC Machine. Advanced RISC Machines trở thành công ty
ARM Limited khi công ty này được đưa ra sàn chứng khoán London và NASDAQ năm
1998.

Kết quả sự hợp tác này là ARM6. Mẫu đầu tiên được công bố vào năm 1991 và
Apple đã sử dụng bộ vi xử lý ARM 610 dựa trên ARM6 làm cơ sở cho PDA hiệu Apple
Newton. Vào năm 1994, Acorn dùng ARM 610 làm CPU trong các máy vi tính RiscPC
của họ. Trải qua nhiều thế hệ nhưng lõi ARM gần như không thay đổi kích thước. ARM2
có 30.000 transistors trong khi ARM6 chỉ tăng lên đến 35.000. Ý tưởng của nhà sản xuất
lõi ARM là sao cho người sử dụng có thể ghép lõi ARM với một số bộ phận tùy chọn nào
đó để tạo ra một CPU hoàn chỉnh, một loại CPU mà có thể tạo ra trên những nhà máy sản
xuất bán dẫn cũ và vẫn tiếp tục tạo ra được sản phẩm với nhiều tính năng mà giá thành

133
vẫn thấp. Thế hệ thành công nhất có lẽ là ARM7TDMI với hàng trăm triệu lõi được sử
dụng trong các máy điện thoại di động, hệ thống video game cầm tay, và Sega Dreamcast.
Trong khi công ty ARM chỉ tập trung vào việc bán lõi IP, cũng có một số giấy phép tạo ra
bộ vi điều khiển dựa trên lõi này. Dreamcast đưa ra bộ vi xử lý SH4 mà chỉ mượn một số
ý tưởng từ ARM (tiêu tán công suất thấp, tập lệnh gọn …) nhưng phần còn lại thì khác
với ARM. Dreamcast cũng tạo ra một chip xử lý âm thanh được thiết kế bởi Yamaha với
lõi ARM7. Bên cạnh đó, Gameboy Advance của Nintendo, dùng ARM7TDMI ở tần số
16,78 MHz. Hãng DEC cũng bán giấy phép về lõi cấu trúc ARM (đôi khi ta có thể bị
nhầm lẫn vì họ cũng sản xuất ra DEC Alpha) và sản xuất ra thế hệ Strong ARM. Hoạt
động ở tần số 233 MHz mà CPU này chỉ tiêu tốn khoảng 1 watt công suất (những đời sau
còn tiêu tốn ít công suất hơn nữa). Sau những kiện tụng, Intel cũng được chấp nhận sản
xuất ARM và Intel đã nắm lấy cơ hội này để bổ sung vào thế hệ già cỗi i960 của họ bằng
Strong ARM. Từ đó, Intel đã phát triển cho chính họ một sản phẩm chức năng cao gọi tên
là Xscale.

Để đạt được một thiết kế gọn, đơn giản và nhanh, các nhà thiết kế ARM xây dựng
nó theo kiểu nối cứng không có vi chương trình, giống với bộ vi xử lý 8-bit 6502 đã từng
được dùng trong các máy vi tính trước đó của hãng Acorn.

Cấu trúc ARM bao gồm các đặc tính của RISC như sau:

- Cấu trúc nạp/lưu trữ.

- Không cho phép truy xuất bộ nhớ không thẳng hàng (bây giờ đã cho phép trong
lõi Arm v6).

- Tập lệnh trực giao.

- File thanh ghi lớn gồm 16 x 32-bit.

- Chiều dài mã máy cố định là 32 bit để dễ giải mã và thực hiện pipeline, để đạt
được điều này phải chấp nhận giảm mật độ mã máy.

134
- Hầu hết các lệnh đều thực hiện trong vòng một chu kỳ đơn. So với các bộ vi xử lý
cùng thời như Intel 80286 và Motorola 68020, trong ARM có một số tính chất khá độc
đáo như sau.

- Hầu hết tất cả các lệnh đều cho phép thực thi có điều kiện, điều này làm giảm
việc phải viết các tiêu đề rẽ nhánh cũng như bù cho việc không có một bộ dự đoán rẽ
nhánh.

- Trong các lệnh số học, để chỉ ra điều kiện thực hiện, người lập trình chỉ cần sửa
mã điều kiện

- Có một thanh ghi dịch đóng thùng 32-bit mà có thể sử dụng với chức năng hoàn
hảo với hầu hết các lệnh số học và việc tính toán địa chỉ.

- Có các kiểu định địa chỉ theo chỉ số rất mạnh

- Có hệ thống con thực hiện ngắt hai mức ưu tiên đơn giản nhưng rất nhanh, kèm
theo cho phép chuyển từng nhóm thanh ghi.

4.3. FPGA

Hình 4.3: Chip FPGA

Field Programmable Gate Arrays là một chip logic số có thể lập trình được, tức là
có thể sử dụng chúng để lập trình cho hầu hết các chức năng của bất kỳ một thiết kế số
nào. FIELD nghĩa là nơi sử dụng con chip. Field Programmable nghĩa là có thể lập trình
được tại nơi của người sử dụng khác với một số chip là phải lập trình tại nơi sản xuất.

135
FPGA được tạo thành từ một mảng (matrix hay array) các phần tử khả trình nên được gọi
là Programmable Gate Array.

Bộ nhớ tĩnh đầu tiên dựa trên FPGA (thường được gọi là SRAM trên nền FPGA)
được đề xuất bởi Wahlstrom vào năm 1967. Sau đó bản thương mại của FPGA được
Xilinx giới thiệu vào năm 1984. Lúc này nó gồm có một mảng của các khối logic có thể
tái cấu hình – Configurable Logic Blocks (CLBs) và các đầu vào ra – I/O (input/output).
Chip FPGA đầu tiên chứa 64 CLBs và 58 I/Os. Ngày nay, FPGA có thể chứa khoảng
330,000 CLBs và khoảng 1100 I/Os. Phần lớn các sản phẩm FPGA trên thị trường hiện
nay đều dựa trên công nghệ SRAM với 2 hãng sản xuất lớn nhất là Xilinx và Altera.
Ngoài ra còn có các hãng khác sản xuất FPGA nhưng với mục đích chuyên dụng (Atmel,
Actel, Lattice, SiliconBlue,..).

Kiến trúc cơ bản của FPGA bao gồm 3 thành phần chính: khối logic có thể tái cấu
hình, Configurable Logic Blocks (CLBs) thực hiện các chức năng logic; các kết nối bên
trong, Porgrammable Interconnect có thể lập trình để kết nối các đầu vào và đầu ra của
các CLB và các khối I/O bên trong; các khối I/O cung cấp giao tiếp giữa các ngoại vi và
các được tín hiêu bên trong.

Dưới đây là một chip FPGA điển hình và từng khối của nó.

Hình 4.4: Cấu trúc cơ bản của FPGA

136
Khối logic có thể tái cấu hình.

Mục đích của việc lập trình khối logic trong FPGA là để cung cấp các tính toán và
các phần tử nhớ cơ bản được sử dụng trong hệ thống số. Một phần tử logic cơ bản gồm
một mạch tổ hợp có thể lập trình, một Flip-Flop hoặc một chốt (latch). Ngoài khối logic
cơ bản đó, nhiều Chip FPGA hiện nay gồm một hỗn hợp các khối khác nhau, một số trong
đó chỉ được dùng cho các chức năng cụ thể, chẳng hạn như các khối bộ nhớ chuyên dụng,
các bộ nhân (multipliers) hoặc các bộ ghép kênh (multiplexers). Tất nhiên, cấu hình bộ
nhớ được sử dụng trên tất cả các khối logic để điều khiển các chức năng cụ thể của mỗi
phần tử bên trong khối đó.

Kết nối có thể lập trình.

Các liên kết trong một FPGA dùng để liên kết các khối logic và I/O lại với nhau để
tạo thành một thiết kế. Bao gồm các bộ ghép kênh, các transistor và cổng đệm ba trạng
thái. Nhìn chung, các transistor và bộ ghép kênh được dùng trong một cụm logic để kết
nối các phần tử logic lại với nhau, trong khi đó cả ba đều được dùng cho các cấu trúc định
tuyến bên trong FPGA. Một số FPGA cung cấp nhiều kết nối đơn giản giữa các khối
logic, một số khác cung cấp ít kết nối hơn nên định tuyến phức tạp hơn.

Khối I/O khả trình.

I/O cung cấp giao tiếp giữa các khối logic và kiến trúc định tuyến đến các thành
phần bên ngoài. Một trong những vấn đề quan trọng nhất trong thiết kế kiến trúc I/O là
việc lựa chọn các tiêu chuẩn điện áp cung cấp và điện áp tham chiếu sẽ được hỗ trợ.

Theo thời gian, các kiến trúc FPGA cơ bản đã được phát triển hơn nữa thông qua
việc bổ sung các khối chức năng đặc biệt có thể lập trình, như bộ nhớ trong (Block
RAMs), logic số học (ALU), bộ nhân, DSP-48 và thậm chí là bộ vi xử lý nhúng được
thêm vào do nhu cầu của các nguồn tài nguyên cho một ứng dụng. Kết quả là nhiều FPGA
ngày nay có nhiều nguồn tài nguyên hơn so với các FPGA trước đó.

137
Hình 4.5: Cấu trúc cơ bản của FPGA

Ngôn ngữ mô tả phần cứng (Hardware Description Language).

Ngôn ngữ mô tả phần cứng bao gồm VHDL, Verilog, SystemC và Handle-C
thường được sử dụng để lập trình FPGA. VHDL và Verilog phát triển như là một chuẩn
công nghiệp và là hai ngôn ngữ phổ biến nhất hiện nay. Xilinx ISE và Altera Quartus đều
hỗ trợ VHDL và Verilog.

138

You might also like