You are on page 1of 83

Chương 8

Cổng nối tiếp (Serial port)


Tài liệu tham khảo:
1. Chương 8: Cổng nối tiếp (Giáo trình VXL)
2. Muhammad Ali Mazidi, AVR Microcontroller and Embedded Systems: Using Assembly and C,
Pearson New International Edition, 2014.
3. Datasheet ATmega324P
4. https://nicerland.com/avr/
5. http://www.hocavr.com/
6. https://www.youtube.com/watch?v=Fr2K9pzec8g&list=PLgwJf8NK-
2e55CdbY_WnY6pejPHoojCkJ
Nguyễn Lý Thiên Trường 1
8.1 Giới thiệu
8.2 Giao thức USART trên ATmega324P
8.2.1 Cài đặt tốc độ truyền nối tiếp (baud rate)
8.2.2 Khối truyền/nhận dữ liệu
8.2.3 Các thanh ghi điều khiển và trạng thái UCSRnA, UCSRnB
và UCSRnC
8.2.4 Lập trình giao tiếp UART với ATmega324P
8.3 Giao thức SPI trên ATmega324P
8.4 Giao thức TWI trên ATmega324P

Nguyễn Lý Thiên Trường 2


8.1 Giới thiệu
▪ Việc truyền dữ liệu giữa các vi điều khiển hoặc giữa vi điều khiển với
thiết bị ngoại vi được thực hiện theo hai cách: song song và nối tiếp.
▪ Trong truyền dữ liệu song song: nhiều bit được phát (truyền)/thu
(nhận) cùng lúc trên đường truyền (được gọi là bus).
▪ Trong truyền dữ liệu nối tiếp: từng bit được phát/thu trên đường
truyền (sử dụng 1 hoặc một vài đường dây).

Nguyễn Lý Thiên Trường 3


8.1 Giới thiệu
▪ Truyền thông nối tiếp sử dụng hai phương thức: đồng bộ (synchronous) và bất
đồng bộ (asynchronous).
▪ Trong phương thức đồng bộ thì mỗi lần truyền/nhận một khối dữ liệu (các ký tự
liên tiếp nhau) với tốc độ phát/thu dữ liệu không đổi. Phương pháp này yêu cầu
giữa phía phát và phía thu phải được đồng bộ bởi tín hiệu xung clock (còn gọi là tín
hiệu xung nhịp, hay xung đồng hồ).
▪ Trong phương thức truyền nối tiếp bất đồng bộ chỉ truyền một byte cho mỗi lần
truyền. Phương pháp này không sử dụng xung clock để đồng bộ mà sử dụng định
dạng khung (frame).

4
8.1 Giới thiệu
▪ Một định dạng khung cơ bản trong truyền nối tiếp bất đồng bộ gồm:
o Start bit (St): luôn là bit 0, dùng để báo hiệu bắt đầu khung truyền.
o Tiếp theo là các bit dữ liệu (từ 5 đến 9 bit, LSB được truyền/nhận trước).
o Kết thúc là stop bit (Sp) luôn là bit 1 (có thể là 1 hoặc 2 bit 1), dùng để báo
hiệu kết thúc khung dữ liệu.
▪ Để tăng thêm độ tin cậy của dữ liệu trong quá trình phát, 1 bit kiểm tra chẵn
lẻ (gọi là parity-check bit) P có thể được thêm vào giữa bit dữ liệu cuối cùng
(MSB) và stop bit.

▪ Bit rate = 1/Tb với Tb là thời gian truyền 1 bit.


▪ Bit rate = N x Baud rate với N là số bit để biểu diễn cho một tín hiệu (hay một symbol).
Ví dụ: điều chế BPSK: N = 1, QPSK: N = 2. Trong chương này, sử dụng N = 1.
5
8.1 Giới thiệu
▪ ATmega324P hỗ trợ 3 giao thức nối tiếp gồm USART (USART0 và USART1), SPI
và TWI (Two-wire Serial Interface) (tương đương với giao thức I2C).
o Chân XCK0/XCK1: có chức năng phát xung clock (chế độ master) hoặc
nhận xung clock (chế độ slave) khi giao thức USART hoạt động ở chế độ
truyền nối tiếp đồng bộ.
o Các chân /SS, MOSI, MISO, SCK: cung cấp các tín hiệu trong giao thức SPI.
o Các chân RXD0/TXD0, RXD1/TXD1: dùng để thu/nhận (trên chân RXD) và
truyền/phát (trên chân TXD) khi giao thức USART hoạt động ở chế độ
truyền nối tiếp bất đồng bộ/đồng bộ. Lưu ý: trong trường hợp truyền nối
tiếp bất đồng bộ, không sử dụng chân XCK0/XCK1 vì trong giao tiếp nối
tiếp bất đồng bộ không cần đến tín hiệu xung clock đồng bộ XCK0/XCK1.
o Các chân SCL, SDA: cung cấp các tín hiệu trong giao thức TWI.

▪ USART: Universal Synchronous-Asynchronous Receiver-Transmitter.


▪ SPI: Serial Peripheral Interface.
▪ TWI: Two-Wire Serial Interface.
6
Có chức năng phát xung clock
(chế độ master) hoặc nhận xung
clock (chế độ slave) khi giao
thức USART hoạt động ở chế độ
truyền nối tiếp đồng bộ.

Cung cấp các tín

ATmega324P
hiệu trong giao
thức SPI.

Dùng để thu/nhận
(trên chân RXD) và
truyền/phát (trên
chân TXD) khi giao
thức USART hoạt
động ở chế độ
truyền nối tiếp bất
đồng bộ/đồng bộ.

▪ USART: Universal Synchronous-Asynchronous Receiver-Transmitter. Cung cấp các tín hiệu


▪ SPI: Serial Peripheral Interface. trong giao thức TWI
▪ TWI: Two-Wire Serial Interface. Nguyễn Lý Thiên Trường 7
8.2 Giao thức USART trên ATmega324P
❖ Một số đặc điểm giao thức USART trên ATmega324P:
▪ Hỗ trợ hai USART: USART0 và USART1. Ký hiệu là USARTn, với n = 0, 1.
▪ Hỗ trợ giao tiếp chế độ song công toàn phần (full duplex) (thu và phát độc lập nhau).
▪ Hỗ trợ chế độ giao tiếp nối tiếp bất đồng bộ và đồng bộ.
▪ Hỗ trợ chế độ giao tiếp nối tiếp đồng bộ Master – Slave.
▪ Tốc độ truyền thông (baud rate) thay đổi được.
▪ Hỗ trợ các khung truyền nối tiếp với 5, 6, 7, 8, hoặc 9 bit dữ liệu và 1 hoặc 2 stop bit.
▪ Khối tạo bit kiểm tra chẵn lẻ (bên phát) và khối kiểm tra lỗi bit này (bên thu) bằng
phần cứng.
▪ Phát hiện chồng chập dữ liệu (Data OverRun).
▪ Phát hiện lỗi khung truyền (Frame Error).
▪ Khối lọc nhiễu gồm khối phát hiện lỗi start bit và bộ lọc số thông thấp.
▪ 3 ngắt: phát USARTn_TX hoàn thành, bộ đệm phát USARTn_UDRE rỗng, thu USARTn_
RX hoàn thành.
▪ Chế độ truyền thông đa xử lý (Multi-processor).
▪ Chế độ tăng gấp đôi tốc độ baud khi truyền nối tiếp bất đồng bộ và đồng bộ.

Nguyễn Lý Thiên Trường 8


❖ Sơ đồ khối của USART trên ATmega324P
❖ Khối tạo xung nhịp/baud
rate:
▪ UBRRn: USART Baud
Rate Register
▪ …
❖ Khối phát dữ liệu:
▪ UDRn: USART Data
Register (bộ đệm phát)
▪ Tạo kiểm tra chẵn lẻ
▪ …
❖ Khối thu dữ liệu:
▪ UDRn: USART Data
Register (bộ đệm thu)
▪ Kiểm tra lỗi khung,…
▪ …
Các thanh ghi điều khiển
và trạng thái
9
❖ Các thanh ghi sử dụng trong chế độ truyền bất đồng bộ (UART)
Địa chỉ MEM Tên thanh ghi Truy xuất bit Chức năng
0xC0 UCSR0A Có Cờ báo trạng thái UART
0xC1 UCSR0B Có Điều khiển trạng thái UART
USART0 0xC2 UCSR0C Có Cài đặt định dạng truyền UART
0xC4 UBRR0L Không Cài đặt baudrate byte thấp
0xC5 UBRR0H Không Cài đặt baudrate byte cao
0xC6 UDR0 Không Đệm dữ liệu phát/thu nối tiếp
0xC8 UCSR1A Có Cờ báo trạng thái UART
0xC9 UCSR1B Có Điều khiển trạng thái UART
0xCA UCSR1C Có Cài đặt định dạng truyền UART
USART1
0xCC UBRR1L Không Cài đặt baudrate byte thấp
0xCD UBRR1H Không Cài đặt baudrate byte cao
0xCE UDR1 Không Đệm dữ liệu phát/thu nối tiếp
▪ USART0 và USART1 hoạt động độc lập nhau.
Nguyễn Lý Thiên Trường 10
8.2.1 Cài đặt tốc độ truyền nối tiếp (baud rate)
UBRRn[11:8] UBRRnH
UBRRn[7:0] UBRRnL
▪ Thanh ghi UBRRn là thanh ghi 16 bit (UBRRnH là byte cao và UBRRnL là byte thấp)
(thuộc vùng I/O mở rộng), nhưng chỉ có 12 bit thấp (UBRRn[11:0]) được sử dụng để
điều chỉnh tốc độ baud.
▪ Chú ý là nếu ghi giá trị vào thanh ghi UBRRnL thì tốc độ baud sẽ được cập nhật tức
thời. Do đó, khi lập trình, chúng ta phải nạp giá trị vào thanh ghi UBRRnH trước khi
nạp vào thanh ghi UBRRnL.
▪ Thanh ghi UBRRn được kết nối với một bộ đếm xuống đặt trước hệ số (PDC:
Prescaling Down-Counter) từ giá trị là nội dung của thanh ghi UBRRn.

Baud rate

11
8.2.1 Cài đặt tốc độ truyền nối tiếp (baud rate)
▪ Tốc độ baud trong truyền nối tiếp bất đồng bộ (UART) và đồng bộ (USRT)
được xác định như sau:

2U2Xn × 𝑓𝑜𝑠𝑐
Tốc độ baud =
16 × 𝑋 + 1
Trong đó:
o 𝑋 là giá trị (không dấu) được nạp vào thanh ghi UBRRn.
o fosc là tần số clock hệ thống
o U2Xn là bit 1 của thanh ghi UCSRnA. Lưu ý: khi U2Xn = 1: tăng gấp đôi tốc độ baud.

▪ Xác định giá trị nạp vào thanh ghi UBRRn để cài đặt tốc độ baud:

2U2Xn × 𝑓𝑜𝑠𝑐
𝑋= −1
16 × Tốc độ baud

Nguyễn Lý Thiên Trường 12


8.2.1 Cài đặt tốc độ truyền nối tiếp (baud rate)
Ví dụ 1: Cho fosc = 8MHz, CKDIV8 = 1, U2X0 = 0, chế độ truyền nối
tiếp bất đồng bộ. Tìm giá trị nạp vào thanh ghi UBRR0 để đạt
được tốc độ baud sau: a) 9600 và b) 4800.
Giải:
▪ Tốc độ baud = 2U2Xnfosc/[16(X+1)] → giá nạp vào thanh ghi UBRRn được
tính bởi: X = [(2U2Xn.fosc/ Tốc độ baud)]/16 – 1
a) Với fosc = 8MHz, U2X0 = 0, tốc độ baud = 9600:
X = [(20x8x106/9600)]/16 – 1 = 51.08
Vì nội dung thanh ghi UBRR0 là số nguyên, do đó chọn X = 51 hay 33H
b) Với fosc = 8MHz, U2X0 = 0, tốc độ baud = 4800:
X = [(20x8x106/4800)]/16 – 1 = 103.17
Vì nội dung thanh ghi UBRR0 là số nguyên, do đó chọn X = 103 hay 67H
Nguyễn Lý Thiên Trường 13
▪ Từ ví dụ 1, chúng ta thấy rằng, giá trị nạp vào thanh ghi UBRR0 phải là số
nguyên. Điều này dẫn đến sai số khi tính tốc độ baud. Sai số được tính như
sau:
Sai số (%) = [(Tốc độ baud thực tế/Tốc độ baud mong muốn) – 1] x 100
▪ Để đảm bảo dữ liệu truyền và nhận trong truyền thông nối tiếp đáng tin cậy,
sai số cho phép không được vượt quá 2% (theo thực tế).
Ví dụ 2: Tính sai số tốc độ baud trong chế độ truyền nối tiếp bất đồng bộ ứng
với tốc độ baud mong muốn là 19200. Cho fosc = 4MHz, CKDIV8 = 1, U2X0 = 0.
Giải:
▪ Với fosc = 4MHz, U2X0 = 0, tốc độ baud = 19200:
X = [(20x4x106/19200)]/16 – 1 = 12.02 → Chọn X = 12
▪ Với X = 12: Tốc độ baud thực tế là: 2U2Xnfosc/[16(X+1)] = 20x4x106/[16(12+1)]
= 19230
▪ Sai số (%) = [(Tốc độ baud thực tế/Tốc độ baud mong muốn) – 1] x 100
= [(19230/19200) – 1] x 100 = 0.2% < 2% (chấp nhận được).
Nguyễn Lý Thiên Trường 14
8.2.1 Cài đặt tốc độ truyền nối tiếp (baud rate)
Ví dụ tốc độ baud theo giá trị thanh ghi UBRRn, fosc và sai số tương ứng
fosc = 8.0000MHz fosc = 11.0592MHz fosc = 14.7456MHz
Baud Rate
[bps] U2X = 0 U2X = 1 U2X = 0 U2X = 1 U2X = 0 U2X = 1
UBRRn Error UBRRn Error UBRRn Error UBRRn Error UBRRn Error UBRRn Error
2400 207 0.2% 416 -0.1% 287 0.0% 575 0.0% 383 0.0% 767 0.0%
4800 103 0.2% 207 0.2% 143 0.0% 287 0.0% 191 0.0% 383 0.0%
9600 51 0.2% 103 0.2% 71 0.0% 143 0.0% 95 0.0% 191 0.0%
14.4k 34 -0.8% 68 0.6% 47 0.0% 95 0.0% 63 0.0% 127 0.0%
19.2k 25 0.2% 51 0.2% 35 0.0% 71 0.0% 47 0.0% 95 0.0%
28.8k 16 2.1% 34 -0.8% 23 0.0% 47 0.0% 31 0.0% 63 0.0%
38.4k 12 0.2% 25 0.2% 17 0.0% 35 0.0% 23 0.0% 47 0.0%
57.6k 8 -3.5% 16 2.1% 11 0.0% 23 0.0% 15 0.0% 31 0.0%
76.8k 6 -7.0% 12 0.2% 8 0.0% 17 0.0% 11 0.0% 23 0.0%
115.2k 3 8.5% 8 -3.5% 5 0.0% 11 0.0% 7 0.0% 15 0.0%
230.4k 1 8.5% 3 8.5% 2 0.0% 5 0.0% 3 0.0% 7 0.0%
250k 1 0.0% 3 0.0% 2 -7.8% 5 -7.8% 3 -7.8% 6 5.3%
0.5M 0 0.0% 1 0.0% - - 2 -7.8% 1 -7.8% 3 -7.8%
1M - - 0 0.0% - - - - 0 -7.8% 1 -7.8%
Max. (1) 0.5Mbps 1Mbps 691.2kbps 1.3824Mbps 921.6kbps 1.8432Mbps
(1) UBRRn = 0, Error = 0.0%

Nguyễn Lý Thiên Trường 15


8.2.2 Khối truyền/nhận dữ liệu

UDREn = 1: bộ đệm phát trống (rỗng) và


sẵn sàng để ghi dữ liệu mới vào.
RXCn = 1: Bộ đệm thu đầy và sẵn sàng
Lưu ý: sau khi reset hệ thống, UDREn = 1
để đọc từ bộ đệm thu.

Bộ đệm thu: Cấu tạo Bộ đệm phát: Cấu tạo


gồm một FIFO (First gồm một FIFO (First
In, First Out) 2 mức. Bộ đệm phát (chỉ ghi) In, First Out) 1 mức.
Bộ đệm thu (chỉ đọc)
TXCn = 1: toàn bộ dữ liệu của một frame
trong thanh ghi dịch phát (Transmit Shift
Register) được dịch hết ra ngoài trên chân
TxD và không có dữ liệu mới nào được ghi
vào UDRn.

Thanh ghi dịch thu Thanh ghi dịch phát

RXB[7:0] UDRn (Read)

TXB[7:0] UDRn (Write)

Nguyễn Lý Thiên Trường 16


8.2.3 Các thanh ghi điều khiển và trạng thái UCSRnA, UCSRnB và UCSRnC
RXCn TXCn UDREn FEn DORn UPEn U2Xn MPCMn UCSRnA
= 1: cho phép truyền thông đa
xử lý (1 Master – nhiều Slave).

= 1: cho phép tăng gấp 2 tốc độ baud trong


chế độ truyền nối tiếp bất đồng bộ/đồng bộ.

= 1: nếu ký tự tiếp theo trong bộ đệm thu có lỗi


kiểm tra chẵn lẻ khi thu dữ liệu.

= 1: nếu bộ đệm thu đầy (lưu 2 ký tự: byte1, byte2), 1 ký tự


(byte3) đang chờ trong thanh ghi dịch thu (Receive Shift
Register) và xuất hiện start bit của ký tự mới (byte4).

= 1: nếu ký tự tiếp theo trong bộ đệm thu bị lỗi khung truyền. Một
khung truyền bị lỗi nếu stop bit đầu tiên là 0.

= 1: khi dữ liệu trong bộ đệm phát được chuyển đến thanh ghi dịch phát hay
nói cách khác là bộ đệm phát đang trống và sẵn sàng nhận dữ liệu mới. Cờ
UDREn được tự động xóa sau khi ghi dữ liệu mới vào thanh ghi UDRn.

= 1: khi toàn bộ dữ liệu của một frame (start bit, data bit, [parity bit], stop bit) trong
thanh ghi dịch phát (Transmit Shift Register) được dịch hết ra ngoài trên chân TxD và
không có dữ liệu mới nào được ghi vào bộ đệm phát UDRn.

= 1: khi có dữ liệu mới trong bộ đệm thu chưa được đọc, hay nói cách khác là bộ đệm thu đầy. Cờ
RXCn sẽ bị xóa về 0 bằng phần cứng sau khi đọc dữ liệu từ thanh ghi UDRn.
17
8.2.3 Các thanh ghi điều khiển và trạng thái UCSRnA, UCSRnB và UCSRnC
RXCIEn TXCIEn UDRIEn RXENn TXENn UCSZn2 RXB8n TXB8n UCSRnB
Bit dữ liệu phát 8 (hay bit phát thứ 9)
trong chế độ phát/thu 9 bit, được ghi
vào bit TXB8n trước khi ghi các bit dữ
liệu thấp vào bộ đệm phát UDRn.

Bit dữ liệu thu 8 (hay bit thu thứ 9) trong chế độ


phát/thu 9 bit và được đọc trước khi đọc các bit
dữ liệu thấp trong bộ đệm thu UDRn.

Kết hợp với 2 bit UCSZn1 và UCSZn0 trong thanh ghi UCSRnC
quy định độ dài của dữ liệu phát/thu.

= 1: cho phép phát. Bit TXENn = 0 không cho phép phát. Quá trình phát chỉ
bị vô hiệu hóa sau khi toàn bộ dữ liệu trong thanh ghi dịch phát và bộ đệm
phát (UDRn) được phát hoàn toàn ra đường truyền TxD.
= 1: cho phép thu. Khi RXENn = 0 thì việc không cho phép bộ thu sẽ có tác dụng ngay
lập tức. Dữ liệu đang được thu sẽ bị mất.
= 1: cho phép ngắt bộ đệm phát USART trống (USART Data Register Empty Interrupt) khi bit cho
phép ngắt toàn cục I = 1 và cờ UDREn trong thanh ghi UCSRnA bằng 1.

= 1: cho phép ngắt phát USART hoàn thành (USART Transmit Complete Interrupt) khi bit cho phép ngắt toàn
cục I = 1 và cờ TXCn trong thanh ghi UCSRnA bằng 1.

= 1: cho phép ngắt thu USART hoàn thành (USART Receive Complete Interrupt) khi bit cho phép ngắt toàn cục I =
1 và cờ RXCn trong thanh ghi UCSRnA bằng 1.
18
8.2.3 Các thanh ghi điều khiển và trạng thái UCSRnA, UCSRnB và UCSRnC
UCSZn1/ UCSZn0/ UCSRnC
UMSELn1 UMSELn0 UPMn1 UPMn0 USBSn UCPOLn
UDORDn UCPHAn

▪ Bit 7:6 – UMSELn[1:0]: USART Mode Select


UMSELn1 UMSELn0 Chế độ (Mode)
0 0 USART bất đồng bộ (hay UART)
0 1 USART đồng bộ (hay USRT)
1 0 Bit dành riêng (Chưa định nghĩa)
1 1 USART Master SPI (hay MSPIM)
▪ Bit 5:4 – UPMn[1:0]: Parity Mode
UPMn1 UPMn0 Chế độ kiểm tra chẵn lẻ (Parity Mode)
0 0 Không sử dụng chức năng kiểm tra chẵn lẻ
0 1 Bit dành riêng (Chưa định nghĩa)
1 0 Cho phép kiểm tra chẵn
1 1 Cho phép kiểm tra lẻ

o 𝑃even = 𝑑𝑚−1 ⊕ 𝑑𝑚−2 ⊕ ⋯ ⨁𝑑1 ⨁𝑑0 ⨁0 ▪ Peven: bit kiểm tra chẵn
▪ Podd: bit kiểm tra lẻ
o 𝑃odd = 𝑑𝑚−1 ⊕ 𝑑𝑚−2 ⊕ ⋯ ⨁𝑑1 ⨁𝑑0 ⨁1 ▪ d : bit i của dữ liệu
i
19
8.2.3 Các thanh ghi điều khiển và trạng thái UCSRnA, UCSRnB và UCSRnC
UCSZn1/ UCSZn0/ UCSRnC
UMSELn1 UMSELn0 UPMn1 UPMn0 USBSn UCPOLn
UDORDn UCPHAn
▪ Bit 3 – USBSn: Stop Bit Select
Bit USBSn dùng để thiết lập số stop bit là 1 stop bit (USBSn = 0) hay 2 stop bit (USBSn = 1).
Bộ thu sẽ bỏ qua stop bit trong quá trình thu.
▪ Bit 2:1 – UCSZn[1:0]: Character Size
Các bit UCSZn1 và UCSZn0 kết hợp với bit UCSZn2 (bit 2 trong thanh ghi UCSRnB) để thiết
lập số bit dữ liệu (hay kích thước ký tự) trong một khung truyền.
UCSZn[2:0] Kích thước ký tự (bit) Lưu ý: trong chế độ master SPI (MSPIM),
000 5 bit 2 là UDORDn (Data Order) dùng để
001 6 thiết lập thứ tự bit được phát/thu.
010 7 UDORDn = 0: MSB được phát/thu trước.
011 8 (mặc định) UDORDn = 1: LSB được phát/thu trước.
100 Bit dành riêng (Chưa định nghĩa) Bit 1 là UCPHAn (Clock Phase) dùng để
thiết lập dữ liệu được lấy mẫu tại cạnh
101 Bit dành riêng (Chưa định nghĩa)
trước (leasing edge) hay cạnh sau (tailing
110 Bit dành riêng (Chưa định nghĩa) edge) của xung XCKn.
111 9
Lưu ý: giá trị mặc định UCSZn[2:0] là 011 (khung truyền 8 bit data).
20
8.2.3 Các thanh ghi điều khiển và trạng thái UCSRnA, UCSRnB và UCSRnC
UCSZn1/ UCSZn0/ UCSRnC
UMSELn1 UMSELn0 UPMn1 UPMn0 USBSn UCPOLn
UDORDn UCPHAn
▪ Bit 0 – UCPOLn: Clock Polarity
Bit UCPOLn chỉ được sử dụng trong chế độ truyền nối tiếp đồng bộ. Nếu UCPOLn = 0:
từng bit dữ liệu được phát trên chân TxD ở cạnh lên xung XCKn, thu trên chân RxD ở
cạnh xuống xung XCKn. Ngược lại, Nếu UCPOLn = 1: từng bit dữ liệu được phát trên
chân TxD ở cạnh xuống xung XCKn, thu trên chân RxD ở cạnh lên xung XCKn. Lưu ý: nếu
sử dụng chế độ truyền nối tiếp bất đồng bộ thì bit UCPOLn được thiết lập bằng 0.
Dữ liệu phát thay đổi Dữ liệu thu được lấy mẫu
UCPOLn
(Ngõ ra chân TxD) (Ngõ vào chân RxD)
0 Cạnh lên xung XCKn Cạnh xuống xung XCKn
1 Cạnh xuống xung XCKn Cạnh lên xung XCKn

Nguyễn Lý Thiên Trường 21


8.2.4 Lập trình giao tiếp UART với ATmega324P
❖ Khởi tạo UART
Quá trình khởi tạo thông thường bao gồm:
▪ Chọn chế độ: UMSELn[1:0]
▪ Chọn chức năng phát hay thu hay thu phát đồng thời: TXENn,
RXENn
▪ Thiết lập định dạng khung truyền (start bit, số bit data, bit kiểm
tra chẵn lẻ, stop bit): UCSZn[2:0], UPMn[1:0], USBSn
▪ Thiết lập tốc độ baud: UBRRn
➢ Lưu ý: Trong quá trình thu phát dữ liệu, phải đảm bảo giá trị baud
rate và định dạng khung không đổi.
RXCn TXCn UDREn FEn DORn UPEn U2Xn MPCMn UCSRnA
RXCIEn TXCIEn UDRIEn RXENn TXENn UCSZn2 RXB8n TXB8n UCSRnB
UCSZn1/ UCSZn0/ UCSRnC
UMSELn1 UMSELn0 UPMn1 UPMn0 USBSn UCPOLn
UDORDn UCPHAn
22
8.2.4 Lập trình giao tiếp UART với ATmega324P
❖ Khởi tạo UART
Ví dụ 3: Khởi tạo USART0, cho phép phát. Khung dữ liệu gồm 8 bit data, không
sử dụng bit kiểm tra chẵn lẻ, 1 stop bit. Tốc độ phát 9600. Tần số dao động 8MHz
(CKDIV8 = 1)
USART_INIT:
LDI R16,(1<<TXEN0) ;cho phép bộ phát
STS UCSR0B,R16
LDI R16,(1<<UCSZ01)|(1<<UCSZ00) ;8-bit data
STS UCSR0C, R16 ;không sử dụng kiểm tra chẵn lẻ, 1 bit dừng (1 stop bit)
LDI R16,0x00 ;ghi UBRR0H trước
STS UBRR0H,R16
LDI R16,51 ;baud rate = 9600 ứng với Fosc = 8 MHz
STS UBRR0L,R16
RET

Nguyễn Lý Thiên Trường 23


8.2.4 Lập trình giao tiếp UART với ATmega324P
❖ Chương trình con phát dữ liệu từ 5 đến 8 bit
▪ Kiểm tra bộ đệm phát trống chưa bằng cách kiểm tra cờ UDREn = 1?
▪ Nếu bộ đệm phát trống thì nạp dữ liệu cần phát vào bộ đệm phát (ghi vào thanh ghi
UDRn).
➢ Lưu ý: Phần cứng tự động đặt cờ UDREn=1 khi bộ đệm phát UDRn trống và xóa cờ
UDREn=0 khi ghi dữ liệu cần phát vào thanh ghi này.
;----------------------------------------
;CTCon phát dữ liệu từ R16 ra cổng nối tiếp sử dụng USART0
;kiểm tra cờ UDRE0 = 1?
;UDR0 = 1: bộ đệm phát đang trống (rỗng): sẵn sàng được ghi data vào
;----------------------------------------
USART_TRANS:
LDS R17,UCSR0A
SBRS R17,UDRE0 ;kiểm tra UDR0 có trống không (UDRE0 = 1)?
RJMP USART_TRANS ;nếu UDR0 chưa trống thì tiếp tục kiểm tra lại
STS UDR0,R16 ;khi UDR0 trống thì chép dữ liệu cần phát vào UDR0
RET
24
❖ Chương trình con phát dữ liệu 9 bit
▪ Nếu phát 9 bit dữ liệu (UCSZn = 7) thì bit phát thứ 9 phải được ghi vào bit TXB8n (bit 0
trong thanh ghi UCSRnB) trước khi byte thấp của dữ liệu được ghi vào UDRn.
Ví dụ 4: Viết chương trình con USART_TRANS_9BIT có chức năng truyền 9 bit data, giả sử 9
bit cần phát được lưu trong thanh ghi R17 (bit thứ 9 là LSB của R17) và R16 (8 bit còn lại).
USART_TRANS_9BIT: ;CTCon phát dữ liệu 9 bit sử dụng USART0
;Chờ bộ đệm phát trống
LDS R18,UCSR0A
SBRS R18,UDRE0
RJMP USART_TRANS_9BIT RXCIEn TXCIEn UDRIEn RXENn TXENn UCSZn2 RXB8n TXB8n UCSRnB
;Xử lý copy bit phát thứ 9 từ R17 (bit LSB) vào TXB80 (trước khi xử lý 8 bit thấp data)
LDS R18,UCSR0B ;bảo toàn các bit trong thanh ghi UCSR0B
BST R17,0 ;cất bit R17(0) vào cờ T
BLD R18,0 ;nạp cờ T vào bit R18(0)
STS UCSR0B,R18 ;TXB80 = R17(0): bit phát thứ 9 = LSB của R17
;Chép 8 bit thấp (R16) vào bộ đếm phát
STS UDR0,R16
RET
25
❖ Chương trình con thu dữ liệu từ 5 đến 8 bit
▪ Kiểm tra bộ đệm thu đầy chưa bằng cách kiểm tra cờ RXCn = 1?
▪ Nếu bộ đệm thu đầy thì có thể đọc dữ liệu từ bộ đệm thu UDRn.
➢ Lưu ý: Phần cứng tự động đặt cờ RXCn=1 báo bộ đệm thu UDRn thu đủ số bit dữ liệu
và chờ đọc. Khi đọc bộ đệm thu UDRn, phần cứng tự động xóa cờ RXCn=0.
;----------------------------------------
;CTCon thu dữ liệu từ cổng nối tiếp cất vào R16 sử dụng USART0
;kiểm tra cờ RXC0 = 1?
;RXC0 = 1: bộ đệm thu đang đầy (sẵn sàng được đọc data)
;----------------------------------------
USART_REC:
LDS R17,UCSR0A
SBRS R17,RXC0 ;kiểm tra cờ RXC0=1?
RJMP USART_REC ;chờ cờ RXC0=1
LDS R16,UDR0 ;đọc data từ bộ đệm thu về R16
RET

Nguyễn Lý Thiên Trường 26


❖ Chương trình con thu dữ liệu 9 bit
▪ Nếu 9 bit dữ liệu được dùng (UCSZn = 7) thì bit thứ 9 phải được đọc từ bit RXB8 (bit 1
trong thanh ghi UCSRnB) trước khi đọc các bit thấp từ bộ đệm thu UDRn. Quy tắc này
cũng áp dụng cho các cờ trạng thái như FEn, DORn và UPEn: đọc các bit trạng thái
trong UCSRnA trước khi đọc dữ liệu từ UDRn.
Ví dụ 5: Viết chương trình con USART_REC_9BIT có chức năng thu 9 bit data, giả sử dữ liệu
thu được được lưu trong thanh ghi R17 (bit thứ 9 là bit LSB của R17) và R16 (8 bit còn lại).
USART_REC_9BIT:
LDS R17,UCSR0A
SBRS R17,RXC0 ;cờ RXC0=1?
RJMP USART_REC_9BIT ;chờ cờ RXC0=1
LDS R17,UCSR0B ;đọc bit data thứ 9 trước
LDS R16,UDR0 ;đọc 8 bit data thấp sau
LSR R17 ;dịch phải lấy bit data thứ 9
ANDI R17,0x01 ;lọc lấy bit data thứ 9 cất vào R17
RET

RXCIEn TXCIEn UDRIEn RXENn TXENn UCSZn2 RXB8n TXB8n UCSRnB


27
❖ Chương trình con thu dữ liệu có kiểm tra các bit trạng thái
FEn, DORn, UPEn
Ví dụ 6: Viết chương trình con USART_REC_9BIT có chức năng thu 9 bit data và các bit trạng thái
khung truyền (FEn, DORn, UPEn), giả sử dữ liệu thu được được lưu trong thanh ghi R17 (bit thứ 9 là
bit LSB của R17) và R16 (8 bit còn lại). Nếu xảy ra lỗi khung truyền thì đặt R17 và R16 lên FFH.
USART_REC_9BIT:
RXCn TXCn UDREn FEn DORn UPEn U2Xn MPCMn UCSRnA
LDS R17,UCSR0A
SBRS R17,RXC0 ;cờ RXC0=1?
RJMP USART_REC_9BIT ;chờ cờ RXC0=1
LDS R18,UCSR0A ;đọc các bit trạng thái (FE0, DOR0, UPE0)
LDS R17,UCSR0B ;đọc bit data thứ 9 trước khi đọc 8 bit data thấp
LDS R16,UDR0 ;đọc 8 bit data thấp sau
ANDI R18, (1<<FE0)|(1<<DOR0)|(1<<UPE0);kiểm tra các bit trạng thái
BREQ USART_REC_NO_ERROR;bằng 0: không lỗi
LDI R17,HIGH(-1) ;khác 0: báo lỗi R17=$FF
LDI R16,LOW(-1) ;R16=$FF
RJMP EXIT
USART_REC_NO_ERROR:
LSR R17 ;dịch phải lấy bit data thứ 9
ANDI R17,0x01 ;lọc lấy bit data thứ 9 cất vào R17
EXIT: RET
28
❖ Một số ví dụ về giao thức nối tiếp bất đồng bộ (UART)
Ví dụ 7: Viết chương trình phát liên tục ký tự A ra cổng nối tiếp USART0. Sử dụng khung truyền 8 bit
dữ liệu, 1 stop bit, không dùng bit kiểm tra chẵn lẻ, tốc độ baud 9600, tần số fosc = 8MHz, CKDIV8 = 1.
Hướng dẫn giải:
Yêu cầu Cài đặt Thanh ghi Ghi chú
Cho phép phát TXEN0 = 1 UCSR0B UCSR0B  (1<<TXEN0) (Các bit khác bị xóa bằng 0).
UCSR0B: UCSZ02
8 bit dữ liệu UCSZ0[2:0] = 011 UCSR0C  (1<<UCSZ01)|(1<<UCSZ00)
UCSR0C: UCSZ01, UCSZ00
Các bit khác trong UCSR0C bị xóa bằng 0.
1 stop bit USBS0 = 0 UCSR0C UCSZ02 = 0 (vì UCSR0B  (1<<TXEN0) đã xóa các bit còn lại
trong UCSR0B bằng 0)
Không dùng kiểm
UPM0[1:0] = 00 UCSR0C
tra chẵn lẻ
Baud rate = 9600, Giá trị nạp cho UBRR0:
Fosc = 8MHz, UBRR0 = 51 UBRR0 X = [(2U2Xn.fosc/ Tốc độ baud)]/16 – 1
CKDIV8 = 1, U2X0 = U2X0 = 0 UCSR0A UBRRH0 (nạp byte cao trước), UBRRL51 (nạp byte thấp sau)
0 (mặc định) Sau khi reset, U2X0 = 0 → không cần cài đặt bit này
Kiểm tra bộ đệm LDS Rd,UCSR0A ;d = 0:31
UDRE0 = 1? UCSR0A
phát trống chưa? SBRS Rr,UDRE0 ;r = 0:31

RXCn TXCn UDREn FEn DORn UPEn U2Xn MPCMn UCSRnA


RXCIEn TXCIEn UDRIEn RXENn TXENn UCSZn2 RXB8n TXB8n UCSRnB
UCSZn1/ UCSZn0/
UMSELn1 UMSELn0 UPMn1 UPMn0 USBSn UCPOLn UCSRnC
UDORDn UCPHAn
29
❖ Một số ví dụ về giao thức nối tiếp bất đồng bộ (UART)
Ví dụ 7: Viết chương trình phát liên tục ký tự A ra cổng nối tiếp USART0. Sử dụng khung truyền 8 bit
dữ liệu, 1 stop bit, không dùng bit kiểm tra chẵn lẻ, tốc độ baud 9600, tần số fosc = 8MHz, CKDIV8 = 1.
Giải:
.ORG 0x00
RJMP MAIN
.ORG 0x40
MAIN: LDI R16,(1<<TXEN0) ;cho phép bộ phát
STS UCSR0B,R16
LDI R16,(1<<UCSZ01)|(1<<UCSZ00);8-bit data
STS UCSR0C,R16 ;không parity, 1 stop bit
LDI R16,0x00
STS UBRR0H,R16
LDI R16,51 ;9600 baud rate ứng với Fosc = 8 MHz
STS UBRR0L,R16
AGAIN: LDS R17,UCSR0A
SBRS R17,UDRE0 ;kiểm tra xem UDR0 có trống không?
RJMP AGAIN
LDI R16,'A' ;’A’ : mã ASCII của ký tự A
STS UDR0,R16 ;ghi dữ liệu vào bộ đệm phát
RJMP AGAIN
30
Ví dụ 8: Viết chương trình phát liên tục chuỗi “YES” ra cổng nối tiếp USART0. Sử dụng khung
truyền 8 bit dữ liệu, 1 stop bit, không dùng bit kiểm tra chẵn lẻ, tốc độ baud 9600, tần số fosc
= 8MHz, CKDIV8 = 1.
Giải:
.ORG 0x00
RJMP MAIN
.ORG 0x40
MAIN: LDI R21,HIGH(RAMEND) ;khởi tạo byte địa chỉ cao cho SP
OUT SPH,R21
LDI R21,LOW(RAMEND) ; khởi tạo byte địa chỉ thấp cho SP
OUT SPL,R21
;khởi tạo USART0
LDI R16,(1<<TXEN0) ;cho phép phát
STS UCSR0B,R16
LDI R16,(1<<UCSZ01)|(1<<UCSZ00);8-bit data
STS UCSR0C,R16 ;không parity, 1 stop bit
LDI R16,0x00
STS UBRR0H,R16
LDI R16,51 ;9600 baud rate ứng với Fosc = 8 MHz
STS UBRR0L,R16
31
AGAIN: LDI R17,'Y'
CALL USART_TRANS
LDI R17,'E'
CALL USART_TRANS
LDI R17,'S'
CALL USART_TRANS
RJMP AGAIN
;Chương trình con phát ký tự
USART_TRANS:
LDS R19, UCSR0A
SBRS R19,UDRE0
RJMP USART_TRANS
STS UDR0,R17
RET

Nguyễn Lý Thiên Trường 32


Ví dụ 9: Viết chương trình phát liên tục chuỗi “VI XU LY ” ra cổng nối tiếp USART1. Sử dụng
khung truyền 8 bit dữ liệu, 2 stop bit, bit kiểm tra lẻ, tốc độ baud 38.4K, tần số fosc = 8MHz,
CKDIV8 = 1.
Giải:
.EQU NUMB = 9 ;số byte trong mảng
.DEF DATA_TX = R18 ;định nghĩa thanh ghi chứa dữ liệu phát
.DEF LOOPCT = R19 ;định nghĩa thanh ghi chứa số vòng lặp
.ORG 0x00
RJMP MAIN
.ORG 0x40
MAIN: LDI ZL,LOW(PMEM << 1);khởi tạo con trỏ Z truy cập bộ nhớ chương trình
LDI ZH,HIGH(PMEM << 1)
LDI LOOPCT,NUMB ;khởi tạo số vòng lặp (truy cập bộ nhớ chương trình)
RCALL USART_INIT
ARRLP: LPM DATA_TX,Z+ ;load dữ liệu từ bộ nhớ chương trình
RCALL USART_TRANS
DEC LOOPCT ;giảm số vòng lặp đi 1
BRNE ARRLP ;lặp lại quá trình cho đến hết byte trong mảng
RJMP MAIN
Nguyễn Lý Thiên Trường 33
;Chương trình con khởi tạo USART1
USART_INIT:
LDI R16,(1<<TXEN1) ;cho phép phát
STS UCSR1B,R16
LDI R16,(1<<UPM11)|(1<<UPM10)|(1<<USBS1)|(1<<UCSZ11)|(1<<UCSZ10)
STS UCSR1C,R16 ;8 bit data, parity lẻ, 2 stop bit
LDI R16,0x00
STS UBRR1H,R16
LDI R16,12 ;baud rate =38.4K ứng với Fosc = 8 MHz
STS UBRR1L,R16
RET
;Chương trình con phát ký tự
USART_TRANS:
LDS R17,UCSR1A
SBRS R17,UDRE1
RJMP USART_TRANS
STS UDR1,DATA_TX
RET
;định nghĩa data trong bộ nhớ chương trình
PMEM: .DB "VI XU LY " ;mảng dữ liệu lưu trong bộ nhớ chương trình
34
Ví dụ 10: Viết chương trình liên tục phát dữ liệu 9 bit ra cổng nối tiếp USART0 với bit phát thứ 9 là LSB
của R17 và 8 bit thấp dữ liệu là mã ASCII của ký tự A. Sử dụng USART 9 bit, 1 stop bit, không dùng bit
kiểm tra chẵn lẻ, tốc độ baud 9600, tần số fosc = 8MHz, CKDIV8 = 1.
Giải:
.ORG 0x00 1
RJMP MAIN
.ORG 0x40
MAIN: LDI R21,HIGH(RAMEND) ;khởi tạo byte địa chỉ cao cho SP
OUT SPH,R21
LDI R21,LOW(RAMEND) ; khởi tạo byte địa chỉ thấp cho SP
OUT SPL,R21
RCALL USART_INIT ;khởi tạo USART
;=====================
;9 bit dữ liệu phát
;bit thứ 9 = LSB (R17)
;byte thấp = R16
;=====================
LDI R17,0x01 ;ví dụ bit phát thứ 9 = 1
LDI R16,'A' ;phát mã ASCII ký tự 'A'
AGAIN: RCALL USART_TRANS_9BIT ;gọi CTC phát 9 bit data
RJMP AGAIN
35
;=====================================
;CTC khởi tạo USART 9 bit, 1 stop bit, no parity
;baudrate = 9600
;===================================== 2
USART_INIT:
LDI R16,(1<<TXEN0)|(1<<UCSZ02);cho phép phát, 9 bit data
STS UCSR0B,R16
LDI R16,(1<<UCSZ01)|(1<<UCSZ00);9 bit data
STS UCSR0C,R16 ;không parity, 1 stop bit
LDI R16,0x00
STS UBRR0H,R16
LDI R16,51 ;9600 baud rate ứng với Fosc = 8 MHz
STS UBRR0L,R16
RET

Nguyễn Lý Thiên Trường 36


;=====================================
;CTC phát dữ liệu 9 bit
;LSB của R17 lưu bit thứ 9
;R16 lưu byte thấp 3
;=====================================
USART_TRANS_9BIT:
;Chờ bộ đệm phát trống
LDS R18, UCSR0A
SBRS R18, UDRE0
RJMP USART_TRANS_9BIT
;Xử lý copy bit phát thứ 9 từ R17 (bit LSB) vào TXB80 (trước khi xử lý 8 bit thấp data)
LDS R18,UCSR0B
BST R17,0 ;cất bit R17(0) vào cờ T
BLD R18,0 ;nạp cờ T vào bit R18(0)
STS UCSR0B,R18
;Chép 8 bit thấp (R16) vào bộ đệm phát
STS UDR0,R16
RET
Nguyễn Lý Thiên Trường 37
Ví dụ 11: Viết chương trình liên tục thu 1 byte dữ liệu từ cổng nối tiếp USART0 và xuất ra Port A. Biết
rằng bên phát sử dụng khung 8 bit dữ liệu, 1 stop bit, không dùng bit kiểm tra chẵn lẻ, tốc độ baud 9600,
tần số fosc = 8MHz, CKDIV8 = 1.
.ORG 0x00
Giải:
RJMP MAIN
.ORG 0x40
MAIN: LDI R16,(1<<RXEN0) ;cho phép thu
STS UCSR0B,R16
LDI R16,(1<<UCSZ01)|(1<<UCSZ00) ;8-bit data
STS UCSR0C,R16 ;không parity, 1 stop bit
LDI R16,0x00
STS UBRR0H,R16
LDI R16,51 ;9600 baud rate fosc=8MHz
STS UBRR0L,R16
LDI R21,0xFF
OUT DDRA,R21 ;Port A là port xuất
AGAIN: LDS R17,UCSR0A
SBRS R17,RXC0 ;cờ RXC0=1: thu xong
RJMP AGAIN ;chờ cờ RXC0=1
LDS R18,UDR0
OUT PORTA,R18
RJMP AGAIN
38
Ví dụ 12: Viết chương trình liên tục thu dữ liệu từ cổng nối tiếp USART0. Nếu dữ liệu thu được là các chữ
thường từ a đến z thì đổi qua chữ in tương ứng và phát ngược lại. Biết rằng bên phát sử dụng khung 8 bit
dữ liệu, 1 stop bit, không dùng bit kiểm tra chẵn lẻ, tốc độ baud 9600, tần số fosc = 8MHz, CKDIV8 = 1.
Giải: .ORG 0x00
RJMP MAIN
.ORG 0x40
1
MAIN: LDI R21,HIGH(RAMEND) ;khởi tạo byte địa chỉ cao cho SP
OUT SPH,R21
LDI R21,LOW(RAMEND) ;khởi tạo byte địa chỉ thấp cho SP
OUT SPL,R21
RCALL USART_INIT
AGAIN: RCALL USART_REC
CPI R16,'a' ;so sánh dữ liệu thu được với mã ASCII của ký tự ‘a’
BRCC GR_EQ ;cờ C = 0 → dữ liệu ≥ ‘a’
RJMP AGAIN
GR_EQ: CPI R16,'z'+1 ;so sánh dữ liệu thu được với mã ASCII của ký tự ‘z’ + 1
BRCS LOWCASE ;cờ C = 1 → dữ liệu < ‘z’ + 1
RJMP AGAIN
LOWCASE:SUBI R16,0x20 ;chuyển chữ thường sang chữ in: chữ in = chữ thường – 20H
MOV R18,R16 ;chuẩn bị dữ liệu chép vào UDR0 phát
RCALL USART_TRANS
RJMP AGAIN
39
;Chương trình con khởi tạo USART
USART_INIT:
LDI R16,(1<<TXEN0)|(1<<RXEN0) ;cho phép thu/phát USART0
STS UCSR0B,R16
LDI R16,(1<<UCSZ01)|(1<<UCSZ00);8-bit data
STS
LDI
UCSR0C, R16
R16,0
;không parity, 1 stop bit
2
STS UBRR0H,R16
LDI R16,51 ;baudrate=9600,fosc=8MHz
STS UBRR0L,R16
RET
;Chương trình con phát ký tự
USART_TRANS:
LDS R17,UCSR0A
SBRS R17,UDRE0 ;cờ UDRE0=1: bộ đệm phát trống
RJMP USART_TRANS ;chờ cờ UDRE0=1
STS UDR0,R18 ;phát ký tự
RET
;Chương trình con thu ký tự
USART_REC:
LDS R17,UCSR0A
SBRS R17,RXC0 ;cờ RXC0=1?
RJMP USART_REC ;chờ cờ RXC0=1
LDS R16,UDR0 ;thu ký tự
RET
40
Ví dụ 13: Viết chương trình liên tục thu dữ liệu 9 bit từ cổng nối tiếp USART0. Nếu bit thứ 9 của dữ
liệu thu được là 0 thì xuất 8 bit thấp dữ liệu ra PortA, ngược lại xuất 8 bit thấp dữ liệu ra PortB. Sử
dụng USART 9 bit, 1 stop bit, không dùng bit kiểm tra chẵn lẻ, tốc độ baud 9600, tần số fosc = 8MHz,
CKDIV8 = 1.
Giải: .ORG 0x00
RJMP MAIN 1
.ORG 0x40
MAIN: LDI R21,0xFF
OUT DDRA,R21 ;Port A là port xuất
OUT DDRB,R21 ;Port B là port xuất
LDI R21,HIGH(RAMEND) ;khởi tạo byte địa chỉ cao cho SP
OUT SPH,R21
LDI R21,LOW(RAMEND) ; khởi tạo byte địa chỉ thấp cho SP
OUT SPL,R21
RCALL USART_INIT ;khởi tạo USART
AGAIN: RCALL USART_REC_9BIT ;gọi CTC thu 9 bit data
SBRC R17,0
RJMP LSB_1
OUT PORTA,R16 ;bit thứ 9 = 0: xuất 8 bit dữ liệu ra Port A
RJMP AGAIN
LSB_1: OUT PORTB,R16 ;bit thứ 9 = 1: xuất 8 bit dữ liệu ra Port B
RJMP AGAIN
41
;=====================================
;CTC khởi tạo USART 9 bit, 1 stop bit, no parity
;baudrate = 9600
;===================================== 2
USART_INIT:
LDI R16,(1<<RXEN0)|(1<<UCSZ02); cho phép thu, 9 bit data
STS UCSR0B, R16
LDI R16,(1<<UCSZ01)|(1<<UCSZ00); 9 bit data
STS UCSR0C, R16 ;không parity, 1 stop bit
LDI R16,0x00
STS UBRR0H,R16
LDI R16,51 ;9600 baud rate ứng với Fosc = 8 MHz
STS UBRR0L,R16
RET

Nguyễn Lý Thiên Trường 42


;=====================================
;CTC thu dữ liệu 9 bit (không sử dụng các bit kiểm tra lỗi khung truyền)
;LSB của R17 lưu bit thứ 9
;R16 lưu byte thấp 3
;=====================================
USART_REC_9BIT:
LDS R17,UCSR0A
SBRS R17,RXC0 ;cờ RXC0=1?
RJMP USART_REC_9BIT ;chờ cờ RXC0=1
LDS R17,UCSR0B ;đọc bit data thứ 9 trước
LDS R16,UDR0 ;đọc 8 bit data thấp sau
LSR R17 ;dịch phải lấy bit data thứ 9
ANDI R17,0x01 ;lọc lấy bit data thứ 9 cất vào R17
RET

Nguyễn Lý Thiên Trường 43


Ví dụ 14: Viết chương trình liên tục thu dữ liệu gửi từ bàn phím máy tính đến ngõ vào cổng nối tiếp
USART0 và hiển thị lên LCD. Nếu đang ở hàng 1 mà nhận được phím ENTER (có mã phím là 13) hoặc
đủ 16 ký tự thì xuống hàng 2. Trường hợp ghi hết hàng 2 thì chờ phím ENTER để xóa LCD và bắt đầu
ghi lại đầu hàng 1. Sử dụng USART 8 bit, 1 stop bit, không dùng bit kiểm tra chẵn lẻ, tốc độ baud 9600,
tần số fosc = 8MHz, CKDIV8 = 1.
❖ Lưu ý:
▪ @ Dòng 1: quá 16 ký tự thì xuống dòng 2,
kiểm tra có nhấn phím ENTER không?
▪ @ Dòng 2: kiểm tra phím ENTER khi đã ghi
đủ 16 ký tự.

PD0 (RXD0)

44
.EQU OUTPORT=PORTB ;PORTB data 1
.EQU INPORT=PINB
.EQU IOSETB=DDRB
.EQU CONT=PORTC ;PORTC điều khiển
.EQU CONT_DR=DDRC
.EQU CONT_IN=PINC
.EQU RS=0 ;bit RS
.EQU RW=1 ;bit RW
.EQU E=2 ;bit E
.EQU ENTER=$0D ;mã xuống dòng
.EQU CHR_ROW=16
.DEF CNT_ROW=R20 ;đếm số ký tự mỗi hàng

Nguyễn Lý Thiên Trường 45


.ORG 0 2
RJMP MAIN
.ORG 0X40
MAIN: LDI R16,HIGH(RAMEND) ;đưa stack lên vùng địa chỉ cao
OUT SPH,R16
LDI R16,LOW(RAMEND)
OUT SPL,R16
LDI R16,0X07
OUT CONT_DR,R16 ;khai báo PC0,PC1,PC2 là output
CBI CONT,RS ;RS=PC0=0
CBI CONT,RW ;RW=PC1=0 truy xuất ghi
CBI CONT,E ;E=PC2=0 cấm LCD
LDI R16,0XFF
OUT IOSETB,R16 ;khai báo outport

Nguyễn Lý Thiên Trường 46


;--------------------------------
;khởi tạo LCD gồm: reset cấp nguồn và cấu hình LCD hoạt động chế độ 8 bit 3
;--------------------------------
RCALL POWER_RESET_LCD8 ;reset cấp nguồn LCD 8 bit
RCALL INIT_LCD8 ;ctc khởi động LCD 8 bit
;--------------------------------
RCALL USART_INIT ;ctc khởi tạo cổng nối tiếp
;--------------------------------
LOOP: LDI CNT_ROW,CHR_ROW ;số ký tự trên mỗi hàng
AGAIN1: RCALL USART_REC ;bắt đầu thu ký tự
CPI R16,ENTER ;kiểm tra mã xuống dòng (phím ENTER)
BREQ DOWN ;Nhấn phím ENTER thì xuống dòng 2
SBI CONT,RS ;RS=1 ghi data hiển thị LCD
MOV R17,R16 ;copy dữ liệu xuất ra LCD
RCALL OUT_LCD ;ghi mã ASCII ký tự ra LCD
LDI R16,1 ;chờ 100μs
RCALL DELAY_US
DEC CNT_ROW
BRNE AGAIN1
Nguyễn Lý Thiên Trường 47
DOWN: LDI CNT_ROW,CHR_ROW ;nạp lại số ký tự trên mỗi hàng
CBI CONT,RS ;RS=0 ghi lệnh 4
LDI R17,0xC0 ;con trỏ bắt đầu ở đầu dòng 2
RCALL OUT_LCD
LDI R16,1 ;chờ 100μs
RCALL DELAY_US
AGAIN2: RCALL USART_REC ;bắt đầu thu ký tự dòng 2
SBI CONT,RS ;RS=1 ghi data hiển thị LCD
MOV R17,R16 ;copy dữ liệu xuất ra LCD
RCALL OUT_LCD ;ghi mã ASCII ký tự ra LCD
LDI R16,1 ;chờ 100μs
RCALL DELAY_US
DEC CNT_ROW
BRNE AGAIN2
WAIT: RCALL USART_REC ;chờ nhận phím ENTER
CPI R16,ENTER
BRNE WAIT
CBI CONT,RS ;RS=0 ghi lệnh
LDI R17,0x01 ;xóa màn hình
RCALL OUT_LCD
LDI R16,20 ;chờ 2ms sau lệnh Clear display
RCALL DELAY_US
RJMP LOOP
48
;----------------------------------------------------------------- 5
;Các lệnh reset cấp nguồn LCD 8 bit
;Chờ hơn 15ms
;Ghi mã lệnh 30H lần 1, chờ ít nhất 4.1ms ;Ghi mã lệnh 30H lần 2, chờ 200μs
;Ghi mã lệnh 30H lần 2, chờ ít nhất 100μs CBI CONT,RS ;RS=0 ghi lệnh
;Ghi mã lệnh 30H lần 3, chờ ít nhất 100μs LDI R17,$30 ;mã lệnh=$30 lần 2
;----------------------------------------------------------------- RCALL OUT_LCD
POWER_RESET_LCD8: LDI R16,2 ;delay 200μs
LDI R16,200 ;delay 20ms RCALL DELAY_US
RCALL DELAY_US ;ctc delay 100μsxR16 ;Ghi mã lệnh 30H lần 3, chờ 200μs
;Ghi mã lệnh 30H lần 1, chờ 4.2ms CBI CONT,RS ;RS=0 ghi lệnh
CBI CONT,RS ;RS=0 ghi lệnh LDI R17,$30
LDI R17,$30 ;mã lệnh=$30 lần 1,RS=RW=E=0 RCALL OUT_LCD
RCALL OUT_LCD ;ctc ghi ra LCD LDI R16,2 ;delay 200μs
LDI R16,42 ;delay 4.2ms RCALL DELAY_US
RCALL DELAY_US RET 6

Nguyễn Lý Thiên Trường 49


;-----------------------------------------------------------------
;INIT_LCD8 khởi động LCD ghi 4 byte mã lệnh
;Function set: 0x38: 8 bit, 2 dòng font 5x8
;Clear display: 0x01: xóa màn hình
7
;Display on/off control: 0x0C: màn hình on, con trỏ off
;Entry mode set: 0x06: dịch phải con trỏ, địa chỉ DDRAM tăng 1 khi ghi data
;----------------------------------------------------------------
INIT_LCD8: CBI CONT,RS ;RS=0 ghi lệnh
LDI R17,0x38 ;chế độ giao tiếp 8 bit, 2 dòng font 5x8
RCALL OUT_LCD
LDI R16,1 ;chờ 100μs
RCALL DELAY_US
CBI CONT,RS ;RS=0 ghi lệnh
LDI R17,0x01 ;xóa màn hình
RCALL OUT_LCD
LDI R16,20 ;chờ 2ms sau lệnh Clear display
RCALL DELAY_US
CBI CONT,RS ;RS=0 ghi lệnh
LDI R17,0x0C ;màn hình on, con trỏ off
RCALL OUT_LCD
LDI R16,1 ;chờ 100μs
RCALL DELAY_US
CBI CONT,RS ;RS=0 ghi lệnh
LDI R17,0x06 ;dịch phải con trỏ, địa chỉ DDRAM tăng 1 khi ghi data
RCALL OUT_LCD
LDI R16,1 ;chờ 100μs
RCALL DELAY_US
RET 50
;--------------------------------------------------
;OUT_LCD ghi mã lệnh/data ra LCD
;Input: R17 chứa mã lệnh/data
8
;--------------------------------------------------
OUT_LCD: OUT OUTPORT,R17 ;1MC,ghi lệnh/data ra LCD
SBI CONT,E ;2MC,xuất xung cho phép LCD
CBI CONT,E ;2MC
RET
;-------------------------------------------------------
;DELAY_US tạo thời gian trễ =R16x100μs(Fosc=8MHz, CKDIV8 = 1)
;Input:R16 hệ số nhân thời gian trễ 1 đến 255
;-------------------------------------------------------
DELAY_US: MOV R15,R16 ;1MC nạp data cho R15
LDI R16,200 ;1MC sử dụng R16
L1: MOV R14,R16 ;1MC nạp data cho R14
L2: DEC R14 ;1MC
NOP ;1MC
BRNE L2 ;2/1MC
DEC R15 ;1MC
BRNE L1 ;2/1MC
RET ;4MC
Nguyễn Lý Thiên Trường 51
USART_INIT:
LDI R16,(1<<RXEN0) ;cho phép bộ thu 9
STS UCSR0B,R16
LDI R16,(1<<UCSZ01)|(1<<UCSZ00);8-bit data
STS UCSR0C, R16 ;không sử dụng kiểm tra chẵn lẻ, 1 bit dừng (1 stop bit)
LDI R16,0x00 ;ghi UBRR0H trước
STS UBRR0H,R16
LDI R16,51 ;baud rate = 9600 ứng với Fosc = 8 MHz
STS UBRR0L,R16
RET
;----------------------------------------
;CTCon thu dữ liệu từ cổng nối tiếp cất vào R16 sử dụng USART0
;kiểm tra cờ RXC0 = 1?
;RXC0 = 1: bộ đệm thu đang đầy (sẵn sàng được đọc data)
;----------------------------------------
USART_REC:
LDS R17,UCSR0A
SBRS R17,RXC0 ;kiểm tra cờ RXC0=1?
RJMP USART_REC ;chờ cờ RXC0=1
LDS R16,UDR0 ;đọc data từ bộ đệm thu về R16
RET 52
8.3 Giao thức SPI trên ATmega324P
▪ SPI (Serial Peripheral Interface) là một chuẩn truyền thông nối tiếp đồng bộ
do hãng Motorola đề xuất vào năm 1980. SPI là một chuẩn truyền song
công (full duplex) nghĩa là tại cùng một thời điểm quá trình phát và thu có
thể xảy ra đồng thời.
▪ Trong cấu hình mạng SPI trên ATmega324P gồm có duy nhất 1 Master và 1
hoặc nhiều Slave. Để giao tiếp giữa Master và Slave, chúng ta sử dụng 4
đường dây hay 4 tín hiệu gồm: SCK (Serial Clock), MISO (Master Input Slave
Output), MOSI (Master Ouput Slave Input) và /SS (Slave Select), do đó, SPI
còn được gọi là chuẩn truyền thông “4 dây”.
Tín I/O port trên Cấu hình I/O Cấu hình I/O
Ý nghĩa
hiệu ATmega324P trên Master trên Slave
SCK PB7 Tín hiệu clock đồng bộ. Output Input
Ngõ vào data Master, ngõ ra data
MISO PB6 Input Output
Slave.
Ngõ ra data Master, ngõ vào data
MOSI PB5 Output Input
Slave.
Tín hiệu lựa chọn Slave (tích cực
/SS PB4 Output hoặc Input Input
mức thấp).
53
Ví dụ cấu trúc mạng Master – Slave trong giao tiếp SPI

▪ Lưu ý: trường hợp trong mạng có nhiều Slave, để tiết kiệm


các ngõ I/O, chúng ta có thể sử dụng IC giải mã để tạo tín
hiệu chọn Slave (/SS). Ví dụ: IC giải mã 74138 có thể tạo ra 8
ngõ ra chọn Slave tích cực thấp chỉ với 3 tín hiệu ngõ vào.
Nguyễn Lý Thiên Trường 54
❖ Sơ đồ khối giao tiếp SPI trên ATmega324P
❖ Các thành phần chính của
module SPI:
▪ Khối tạo xung nhịp.
▪ Khối điều khiển và trạng thái.
▪ Khối dịch dữ liệu.

Nguyễn Lý Thiên Trường 55


Sơ đồ kết nối các tín hiệu giữa Master và Slave trong giao tiếp SPI

▪ Thành phần chính của hệ thống gồm 2 thanh ghi dịch 8 bit (1 thanh ghi dịch cho Master và 1 thanh ghi dịch
cho Slave) và khối tạo xung clock của Master.
▪ Quá trình giao tiếp giữa Master và Slave được bắt đầu bằng cách Master kéo chân /SS của Slave cần muốn
giao tiếp xuống mức thấp.
▪ Master và Slave chuẩn bị dữ liệu được gửi trong thanh ghi dữ liệu SPDR0.
▪ Master sẽ tạo ra xung clock trên chân SCK cho việc dịch chuyển dữ liệu đồng thời giữa Master và Slave.
Cứ mỗi xung nhịp do Master tạo ra trên chân SCK sẽ tương ứng với một bit trong thanh ghi dịch của Master
được truyền đến Slave trên đường MOSI và một bit trong thanh ghi dịch của chip Slave cũng được truyền
đến Master trên đường MISO.
▪ Master kết thúc quá trình giao tiếp với Slave bằng cách kéo tín hiệu trên chân /SS của Slave lên mức cao.
56
Sơ đồ kết nối các tín hiệu giữa Master và Slave trong giao tiếp SPI

https://electrosome.com/spi/

57
❖ Các thanh ghi sử dụng trong giao thức SPI:
Địa chỉ MEM Địa chỉ I/O Ký hiệu Truy xuất bit Chức năng
0x4C 0x2C SPCR0 Có Điều khiển SPI
0x4D 0x2D SPSR0 Có Trạng thái SPI
0x4E 0x2E SPDR0 Không Dữ liệu phát/thu SPI
▪ SPI trên ATmega324P được vận hành bởi 3 thanh ghi bao gồm:
o Thanh ghi điều khiển SPCR0.
o Thanh ghi trạng thái SPSR0.
o Thanh ghi dữ liệu SPDR0.

Nguyễn Lý Thiên Trường 58


❖ Thanh ghi điều khiển SPCR0 – SPI Control Register
SPIE0 SPE0 DORD0 MSTR0 CPOL0 CPHA0 SPR10 SPR00 SPCR0 (I/O: 0x2C)

Hai bit này kết hợp với bit SPI2X0


trong thanh ghi SPSR0 cho phép
cấu hình 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 cho một hệ
số chia. Để đảm bảo dữ liệu được
lấy mẫu chính xác thì tốc độ này
không được lớn hơn 1/4 tốc độ xung
nhịp cho chip.

Sự kết hợp 2 bit này cấu hình 4 chế độ hoạt động của SPI. Chúng
ta cấu hình các bit này sao cho phải đảm bảo Master và Slave
cùng chế độ hoạt động.

Cấu hình chip hoạt động với vai trò là Master hay Slave. Nếu MSTR0 = 1 thì chip là
Master, ngược lại MSTR0 = 0 thì chip là Slave.

Quy định thứ tự dữ liệu các bit được truyền và nhận trên các đường MISO và MOSI. DORD0 = 0:
MSB được truyền/nhận trước, DORD0 = 1: LSB được truyền/nhận trước.

= 1: cho phép giao tiếp SPI. Nếu SPE0 = 0 thì SPI dừng hoạt động.

= 1: cho phép ngắt SPI. Nếu bit này được đặt bằng 1 và bit I trong thanh ghi SREG được đặt bằng 1, ngắt SPI sẽ
xảy ra sau khi một gói dữ liệu (8 bit) được phát hoặc thu (cờ SPIF0 = 1).
59
❖ Thanh ghi điều khiển SPCR0 – SPI Control Register
▪ Các chế độ hoạt động SPI
Chế độ SPI Điều kiện Cạnh trước xung clock Cạnh sau xung clock
0 CPOL0 = 0, CPHA0 = 0 Lấy mẫu dữ liệu ở cạnh lên. Dữ liệu thay đổi ở cạnh xuống.
1 CPOL0 = 0, CPHA0 = 1 Dữ liệu thay đổi ở cạnh lên. Lấy mẫu dữ liệu ở cạnh xuống.
2 CPOL0 = 1, CPHA0 = 0 Lấy mẫu dữ liệu ở cạnh xuống. Dữ liệu thay đổi ở cạnh lên.
3 CPOL0 = 1, CPHA0 = 1 Dữ liệu thay đổi ở cạnh xuống. Lấy mẫu dữ liệu ở cạnh lên.

▪ CPHA0 = 0 ▪ CPHA0 = 1

Nguyễn Lý Thiên Trường 60


❖ Thanh ghi điều khiển SPCR0 – SPI Control Register
SPIE0 SPE0 DORD0 MSTR0 CPOL0 CPHA0 SPR10 SPR00 SPCR0 (I/O: 0x2C)

▪ Tần số clock SCK theo tần số clock hệ thống Fosc


SPI2X0 SPR10 SPR00 Tần số chân SCK
0 0 0 Fosc/4 SPI2X0 (Double SPI Speed Bit):
0 0 1 Fosc/16 Bit 0 trong thanh ghi SPSR0.
0 1 0 Fosc/64
0 1 1 Fosc/128
1 0 0 Fosc/2
1 0 1 Fosc/8
1 1 0 Fosc/32
1 1 1 Fosc/64
Nhận xét: SPI2X0 = 1: tăng gấp đôi tốc độ truyền/nhận dữ liệu.

Nguyễn Lý Thiên Trường 61


❖ Thanh ghi trạng thái SPSR0 – SPI Status Register
SPIF0 WCOL0 – – – – – SPI2X0 SPSR0 (I/O: 0x2D)
Bit 7 – SPIF0 (SPI Interrupt Flag)
Cờ báo ngắt SPI. Khi hoàn thành việc phát hoặc thu một gói dữ liệu thì bit SPIF0 sẽ tự động được đặt
lên 1 bằng phần cứng. Khi bit SPIF0 = 1, ngắt SPI được thực thi nếu bit cho phép ngắt SPIE0 = 1 và bit
cho phép ngắt toàn cục I = 1. Nếu tín hiệu /SS được thiết lập là ngõ vào và được kéo xuống mức thấp ở
chế độ Master bởi một thiết bị khác thì cờ SPIF0 cũng được đặt lên 1. Cờ SPIF0 sẽ được xóa bằng phần
cứng khi chương trình phục vụ ngắt SPI được thực thi. Ngoài ra, cờ SPIF0 còn được xóa bằng cách đọc
thanh ghi trạng thái SPSR0 (với bit SPIF0 = 1), sau đó truy cập vào thanh ghi dữ liệu SPDR0.
Bit 6 – WCOL0 (Write COLlision Flag)
Bit báo xung đột dữ liệu. Bit WCOL0 = 1 nếu chúng ta ghi dữ liệu mới vào thanh ghi dữ liệu SPDR0
trong khi quá trình phát/thu dữ liệu trước chưa kết thúc. Bit WCOL0 được xóa bằng cách đọc thanh ghi
trạng thái SPSR0 (với bit WCOL0 = 1), sau đó truy cập vào thanh ghi dữ liệu SPDR0 (tương tự như bit
SPIF0).
Bit 5:1 – Res: Reserved Bits
Các bit dành riêng, chưa được định nghĩa. Giá trị các bit này sau khi reset hệ thống bằng 0.
Bit 0 – SPI2X0 (Double SPI Speed Bit)
Bit nhân đôi tốc độ phát (chế độ Master). Bit này kết hợp với 2 bit SPR10 và SPR00 trong thanh ghi
điều khiển SPCR0 để cấu hình tốc độ cho SPI.

Nguyễn Lý Thiên Trường 62


❖ Thanh ghi dữ liệu SPDR0 (SPI Data Register)
MSB LSB SPDR0 (I/O: 0x2E)
Đây là thanh ghi 8 bit để chứa dữ liệu phát/thu. Đối với Master, nếu chúng ta ghi giá trị
vào thanh ghi SPDR0 sẽ kích quá trình truyền SPI. Đối với Slave, dữ liệu nhận được từ
Master sẽ được lưu trong thanh ghi SPDR0, dữ liệu được lưu sẵn trong SPDR0 sẽ được gửi
đến Master.
Ví dụ 15: Viết đoạn lệnh SPI làm việc như Master, tần số SCK=1MHz, lấy mẫu dữ liệu cạnh
lên ở cạnh trước xung CK, MSB truyền trước. Cho Fosc=8MHz.
Giải:
▪ Cho phép SPI : SPE0=1
▪ MSB truyền trước: DODR0=0
▪ Master: MSTR0=1
▪ Lấy mẫu dữ liệu cạnh lên: CPOL0=0,CPHA0=0
▪ Tốc độ SCK=1MHz: hệ số chia N=8, SPI2X0=1, SPR10=0, SPR00=1
LDI R16,0B01010001 0 1 0 1 0 0 0 1
OUT SPCR0,R16 SPIE0 SPE0 DORD0 MSTR0 CPOL0 CPHA0 SPR10 SPR00 SPCR0
LDI R16,0X01
SPIF0 WCOL0 – – – – – SPI2X0 SPSR0
OUT SPSR0,R16
0 0 0 0 0 0 0 1
63
Lập trình giao tiếp SPI trên ATmega324P
❖ Đối với Master
1. Khởi động cổng SPI chế độ Master bao gồm:
▪ Khai báo các chân MOSI (PB5), SCK (PB7), /SS (PB4): output, MISO (PB6): input
▪ Cho phép SPI: SPE0=1, bit MSTR0=1; xác định bit truyền trước: bit DORD0; cạnh
xung lấy mẫu dữ liệu: CPOL0 và CPHA0; tốc độ xung CK: SPR10 và SPR00, SPI2X0.
Các bit đầu thuộc thanh ghi SPCR0, bit SPI2X0 thuộc thanh ghi SPSR0.
2. Xóa /SS=0 bắt đầu truyền:
▪ Trong trường hợp giao tiếp 1 Slave, chân /SS kết nối trực tiếp chân /SS hay /CS
của Slave.
▪ Trong trường hợp nhiều Slave, kết hợp thêm mạch giải mã chọn Slave như đã
trình bày ở chương 6.
3. Ghi dữ liệu cần phát vào thanh ghi SPDR0.
4. Đọc thanh ghi SPSR0, chờ cờ SPIF0=1 báo đã truyền xong.
5. Đọc thanh ghi SPDR0 thu dữ liệu từ Slave (tác vụ bước 4 và 5 tương ứng xóa cờ
SPIF0).
6. Lặp lại từ bước 3 nếu muốn tiếp tục truyền dữ liệu.
7. Đặt /SS=1 kết thúc truyền.
64
Ví dụ 16: Viết một chương trình con SPI_TRANS truyền byte dữ liệu cất trong
R17 ra SPI và đọc byte dữ liệu thu từ SPI cất vào R18. Giả sử đã khởi động cổng
SPI.

Giải:
Các bước phát và thu dữ liệu từ SPI như mô tả từ bước 3 đến 5. Chương trình
con SPI_TRANS phát dữ liệu từ R17 và thu dữ liệu vào R18.
SPI_TRANS:
OUT SPDR0,R17 ;ghi data ra SPI
WAIT_SPI:
IN R16,SPSR0 ;đọc cờ SPIF0
SBRS R16,SPIF0 ;cờ SPIF0=1: truyền SPI xong
RJMP WAIT_SPI ;chờ cờ SPIF0=1
IN R18,SPDR0 ;đọc data từ SPI → xóa cờ SPIF0
RET
Trong chương trình chính phải xóa chân /SS=0 trước khi gọi chương trình con
trên, và đặt /SS=1 khi muốn kết thúc truyền SPI.
65
❖ Đối với Slave
▪ Trong chế độ Slave, cổng SPI hoàn toàn phụ thuộc vào tín hiệu /SS.
o Nếu /SS=1: Slave ngưng làm việc, chân MISO=hi-Z (tổng trở cao), chỉ có thể cập nhật
dữ liệu thanh ghi SPDR0.
o Khi /SS=0: xung CK từ Master sẽ dịch dữ liệu phát từ Slave ra chân MISO và dữ liệu
thu từ Master vào chân MOSI. Cấu hình fCK, cạnh lấy mẫu, bit truyền trước do
Master quyết định.
1. Khởi động chế độ Slave gồm:
▪ Khai báo chân MISO (PB6) output, các chân /SS(PB4), MOSI(PB5), SCK(PB7) input
▪ Đặt bit SPE0=1 cho phép SPI, MSTR0=0 mô thức Slave, DORD0, CPOL0, CPHA0
đặt theo cấu hình Master, SPR10:00=xx fCK do Master quyết định (thuộc thanh
ghi SPCR0).
2. Ghi dữ liệu cần phát vào thanh ghi SPDR0.
3. Đọc thanh ghi SPSR0,chờ cờ SPIF0=1 báo đã truyền xong.
4. Đọc thanh ghi SPDR0 thu dữ liệu từ Master (tác vụ bước 3 và 4 tương ứng xóa cờ
SPIF0).
5. Lặp lại từ bước 2 cho đến khi thu dữ liệu từ Master báo kết thúc hay chân /SS=1.

Nguyễn Lý Thiên Trường 66


Ví dụ 17: Viết một chương trình giao tiếp giữa 2 MCU324P qua cổng SPI thực hiện như sau:
▪ Mỗi lần nhấn SW, MCU Master sẽ truyền 1 ký tự trong chuỗi ký tự (kết thúc bằng mã
NULL) cất trong Flash ROM sang MCU Slave, đồng thời hiển thị ký tự thu về từ Slave ra
PortA. MCU Slave thu ký tự từ Master hiển thị ra PortA, đồng thời truyền ký tự trong
chuỗi ký tự (kết thúc bằng mã NULL) cất trong Flash ROM (chiều dài bằng với chuỗi ký tự
của Master) sang Master.
▪ Lặp vòng lại từ đầu.
▪ Cho tốc độ truyền SPI = 500KHz, MSB truyền trước, lấy mẫu cạnh lên ở cạnh trước.

Nguyễn Lý Thiên Trường 67


Giải:
❖ MCU Master:
▪ Khai báo Master, tốc độ truyền SCK = 500KHz, lấy mẫu cạnh lên, cho phép SPI làm việc.
▪ Khai báo các ngõ ra: /SS, SCK, MOSI, ngõ vào MISO.
▪ Bắt đầu truyền SPI: xóa /SS=0, dừng truyền SPI: đặt /SS = 1.
▪ Mỗi lần SW nhấn, lấy ký tự từ chuỗi ký tự đặt trong Flash ROM gọi chương trình con
SPI_TRANS phát/thu dữ liệu và kết thúc khi phát xong ký tự NULL, hiển thị ký tự thu được
ra PortA.
❖ MCU Slave:
▪ Khai báo Slave, cho phép SPI làm việc.
▪ Khai báo ngõ ra: MISO, các ngõ còn lại của SPI là ngõ vào.
▪ Lấy ký tự từ chuỗi ký tự đặt trong Flash ROM gọi chương trình con SPI_TRANS phát/thu
dữ liệu và kết thúc khi thu được ký tự NULL, hiển thị ký tự thu được ra PortA.
▪ Để đồng bộ bên Master, chờ chân /SS=0 mới thực hiện việc truyền SPI.
▪ Để dễ theo dõi, ta có thể sử dụng PortA lái bar LED.
▪ Sử dụng chương trình con SPI_TRANS ghi/đọc dữ liệu chung cho cả Master và Slave.
▪ Ghi dữ liệu vào SPDR0.
▪ Chờ cờ SPIF0=1.
▪ Đọc dữ liệu từ SPDR0.
▪ Sử dụng chương trình con SW_RD đọc trạng thái SW có chống rung (xem lại chương 6).
68
❖ Chương trình cho Master 1
.EQU OUTPORT=PORTA ;PORTA output
.EQU OUTPORT_DR=DDRA
.EQU CONT=PORTC ;PortC điều khiển
.EQU CONT_DR=DDRC
.EQU CONT_IN=PINC
.EQU SW=0 ;ký hiệu chân SW
.EQU SS=4 ;ký hiệu chân /SS
.EQU MOSI=5 ;ký hiệu chân MOSI
.EQU MISO=6 ;ký hiệu chân MISO
.EQU SCK=7 ;ký hiệu chân SCK
.EQU NULL=$00 ;mã kết thúc chuỗi ký tự

Nguyễn Lý Thiên Trường 69


❖ Chương trình cho Master 2
.ORG 0
RJMP MAIN
.ORG 0X40
MAIN: LDI R16,HIGH(RAMEND) ;đưa stack lên vùng đ/c cao
OUT SPH,R16
LDI R16,LOW(RAMEND)
OUT SPL,R16
CBI CONT_DR,SW ;khai báo SW input
SBI CONT,SW ;điện trở kéo lên ngõ SW
LDI R16,0XFF
OUT OUTPORT_DR,R16 ;khai báo Port hiển thị output
LDI R16,0X00
OUT OUTPORT,R16 ;xóa hiển thị
LDI R16,(1<<SS)|(1<<SCK)|(1<<MOSI);khai báo các output SPI
OUT DDRB,R16
SBI PORTB,SS ;dừng truyền SPI
;SPI Master,SCK=Fosc/16,cho phép SPI
LDI R16,(1<<SPE0)|(1<<MSTR0)|(1<<SPR00)
OUT SPCR0,R16
70
❖ Chương trình cho Master 3
;---------------------------------------------------------
START: SBI PORTB,SS ;dừng truyền SPI
LDI ZH,HIGH(MSG_OUT<<1) ;Z trỏ địa chỉ đầu bảng MSG trong Flash ROM
LDI ZL,LOW(MSG_OUT<<1)
SPI_NXT: RCALL SW_RD ;chờ SW nhấn
LPM R17,Z+ ;lấy ký tự từ chuỗi ký tự
CBI PORTB,SS ;cho phép truyền SPI
RCALL SPI_TRANS ;truyền SPI
OUT OUTPORT,R18 ;hiển thị ký tự thu từ SPI
CPI R17,NULL ;ký tự=NULL
BRNE SPI_NXT ;khác tiếp tục truyền SPI
RJMP START ;lặp vòng lại từ đầu

Nguyễn Lý Thiên Trường 71


❖ Chương trình cho Master 4
;-----------------------------------------------------------
;SPI_TRANS truyền data SPI giữa Master và Slave
;Input: R17 chứa data ghi ra Slave
;Output: R18 chứa data đọc từ Slave
;Sử dụng R16
;----------------------------------------------------------
SPI_TRANS:
OUT SPDR0,R17 ;ghi data ra SPI
WAIT_SPI:
IN R16,SPSR0 ;đọc cờ SPIF0
SBRS R16,SPIF0 ;cờ SPIF0=1 truyền SPI xong
RJMP WAIT_SPI ;chờ cờ SPIF0=1
IN R18,SPDR0 ;đọc data từ SPI
RET

Nguyễn Lý Thiên Trường 72


❖ Chương trình cho Master 5
;----------------------------------------------------------
;SW_RD đọc trạng thái nút nhấn có chống rung
;Chỉ thoát khi có SW nhấn
;Sử dụng R16
;-----------------------------------------------------------
SW_RD: LDI R16,50 ;kiểm tra SW nhấn 50 lần liên tục
WAIT0: SBIC CONT_IN,SW
RJMP SW_RD
DEC R16
BRNE WAIT0
BACK1: LDI R16,50 ;kiểm tra SW nhả 50 lần liên tục
WAIT1: SBIS CONT_IN,SW
RJMP BACK1
DEC R16
BRNE WAIT1
RET
;-----------------------------------------------------------
.ORG 0X200
MSG_OUT:.DB "ABCDEFGHIJKLMNO",$00
;----------------------------------------------------------- 73
❖ Chương trình cho Slave 1
.EQU OUTPORT=PORTA ;PORTA giao tiếp BUS LCD16x2
.EQU OUTPORT_DR=DDRA
.EQU SS=4 ;ký hiệu chân /SS
.EQU MISO=6 ;ký hiệu chân MISO
.EQU NULL=$00 ;mã kết thúc chuỗi ký tự
.ORG 0
RJMP MAIN
.ORG 0X40
MAIN: LDI R16,HIGH(RAMEND) ;đưa stack lên vùng đ/c cao
OUT SPH,R16
LDI R16,LOW(RAMEND)
OUT SPL,R16
LDI R16,0XFF
OUT OUTPORT_DR,R16 ;khai báo Port hiển thị output
LDI R16,0X00
OUT OUTPORT,R16 ;xóa hiển thị
LDI R16,(1<<MISO) ;khai báo chân MISO output SPI
OUT DDRB,R16
LDI R16,(1<<SPE0) ;SPI Slave,cho phép SPI
OUT SPCR0,R16
Nguyễn Lý Thiên Trường 74
;---------------------------------------------------------
START: LDI ZH,HIGH(MSG_OUT<<1) ;Z trỏ địa chỉ đầu bảng MSG trong Flash ROM 2
LDI ZL,LOW(MSG_OUT<<1)
SPI_NXT: LPM R17,Z+ ;lấy ký tự từ chuỗi ký tự
SPI_SS: SBIC PINB,SS ;chờ cho phép truyền SPI
RJMP SPI_SS
RCALL SPI_TRANS ;truyền SPI
OUT OUTPORT,R18 ;hiển thị ký tự thu từ SPI
CPI R18,NULL ;ký tự=NULL
BRNE SPI_NXT ;khác tiếp tục truyền SPI
RJMP START ;lặp vòng lại từ đầu
;-----------------------------------------------------------
;SPI_TRANS truyền data SPI giữa Master và Slave
;Input: R17 chứa data ghi ra Slave
;Output: R18 chứa data đọc từ Slave
;Sử dụng R16
;----------------------------------------------------------
SPI_TRANS: OUT SPDR0,R17 ;ghi data ra SPI
WAIT_SPI: IN R16,SPSR0 ;đọc cờ SPIF0
SBRS R16,SPIF0 ;cờ SPIF0=1 truyền SPI xong
RJMP WAIT_SPI ;chờ cờ SPIF0=1
IN R18,SPDR0 ;đọc data từ SPI
RET
;-----------------------------------------------------------
.ORG 0X200
MSG_OUT: .DB "abcdefghijklmno",$00
;------------------------------------------------------------ 75
8.4 Giao thức TWI trên ATmega324P
▪ TWI (Two-Wire Serial Interface) là chuẩn giao tiếp nối tiếp đồng bộ được hỗ trợ trên
ATmega324P. Chuẩn này hoàn toàn tương thích với chuẩn I2C (Inter-Integrated Circuit
được phát triển bởi hãng điện tử Philips Semiconductor).
▪ Các thiết bị I2C sử dụng 2 chân SCL (Serial Clock) và SDA (Serial Data) để truyền tải dữ
liệu. Tín hiệu SCL giúp đồng bộ dữ liệu giữa 2 chip (Master và Slave). Tín hiệu SDA là
đường dữ liệu nối tiếp. Lưu ý: trong chuẩn I2C, MSB được truyền trước.

76
▪ Module TWI trên ATmega324P bao gồm 4 thành phần: khối tạo bit rate, khối
giao tiếp bus, khối kiểm tra địa chỉ Slave và khối điều khiển.

Khối giao tiếp bus Khối tạo bit rate

Khối điều khiển

Khối kiểm tra địa chỉ Slave


77
❖ Các thanh ghi sử dụng trong giao tiếp TWI:
Địa chỉ MEM Ký hiệu Truy xuất bit Chức năng
0xB8 TWBR Không Đặt tốc độ bit(bit rate) TWI
0xB9 TWSR Có Trạng thái TWI
0xBA TWAR Có Đặt địa chỉ TWI
0xBB TWDR Không Dữ liệu TWI
0xBC TWCR Có Điều khiển TWI
▪ Có 5 thanh ghi được dùng trong giao thức TWI gồm:
o TWBR: TWI Bit rate Register.
o TWSR: TWI Status Register.
o TWCR: TWI Control Register.
o TWDR: Data Register.
o TWAR: TWI Address Register.
Nguyễn Lý Thiên Trường 78
❖ TWBR (TWI Bit rate Register)
TWBR7 TWBR6 TWBR5 TWBR4 TWBR3 TWBR2 TWBR1 TWBR0 TWBR (0xB8)
▪ TWBR lựa chọn hệ số chia để điều khiển tần số clock SCL trong chế độ
Master. Tần số xung clock trên chân SCL được điều khiển bằng cách thiết lập
trong TWBR và các bit hệ số đặt trước (prescaler) trong TWSR (thanh ghi
trạng thái).
𝑓osc
𝑓SCL =
16 + 2 × TWBR × 4TWPS
o Giá trị TWBR nên lớn hơn hoặc bằng 10 nếu TWI hoạt động ở chế độ Master.
o TWPS: là giá trị của 2 bit TWPS1 và TWPS0 (trong thanh ghi TWSR).

TWS7 TWS6 TWS5 TWS4 TWS3 – TWPS1 TWPS0 TWSR (0xB9)

Ví dụ 18: Với TWPS1:TWPS0 = 01 và giá trị của TWBR = 38, Fosc =


8MHz
8MHz thì 𝑓SCL = 16+2×38×41 = 25KHz.
Nguyễn Lý Thiên Trường 79
❖ TWSR (TWI Status Register)
TWS7 TWS6 TWS5 TWS4 TWS3 – TWPS1 TWPS0 TWSR (0xB9)
TWS7..3: 5 bit chỉ ra trạng thái TWI. Khi giao tiếp bằng giao thức TWI, giữa
Master và Slave xảy ra nhiều tình huống khác nhau. Ứng với mỗi tình huống này,
TWI sẽ tạo ra 1 mã trong thanh ghi TWSR. Người lập trình cho TWI cần xét mã
trong 5 bit cao của thanh ghi TWSR để từ đó đưa ra các quyết định phù hợp ứng
với từng mã →Xem thêm trong data sheet để biết thêm ý nghĩa các mã này.
TWPS1..0: 2 bit chọn giá trị đặt trước để cấu hình bit rate.
TWPS1 TWPS0 Giá trị đặt trước 𝑓osc
0 0 1 𝑓SCL =
16 + 2 × TWBR × 4TWPS
0 1 4
Giá trị đặt trước
1 0 16
1 1 64 ▪ Giá trị đặt trước = 4TWPS
▪ Ví dụ: TWPS1:TWPS0 = 10 (Nhị phân)
→ TWPS = 2
→ Giá trị đặt trước = 4TWPS = 42 = 16
80
❖ TWCR (TWI Control Register)
TWINT TWEA TWSTA TWSTO TWWC TWEN – TWIE TWCR (0xBC)
Bit cho phép ngắt TWI.

Bit cho phép giao tiếp TWI. Khi bit TWEN được
đặt lên 1, TWI sẵn sàng hoạt động và kiểm soát
các chân I/O chuyển sang chức năng SCL, SDA
tương ứng.

Khi cờ TWINT đang ở mức thấp (tức TWI đang bận), nếu
chúng ta ghi dữ liệu vào thanh ghi dữ liệu (TWDR) thì xảy
ra lỗi xung đột dữ liệu. Khi đó bit TWWC tự động được
đặt lên 1 bằng phần cứng. Bit TWWC được xóa khi ghi dữ
liệu vào thanh ghi TWDR trong khi TWINT=1.

Bit tạo điều kiện STOP. Khi bit TWSTO = 1, Master muốn kết thúc
một cuộc gọi, nó sẽ phát ra điều kiện STOP. Bit này được xóa bằng
phần cứng khi điều kiện STOP được gửi.

Bit tạo điều kiện START. Khi một chip muốn trở thành Master để thực hiện 1 cuộc
gọi cho Slave, chúng ta đặt bit này lên 1. Lưu ý là bit TWSTA cần được xóa bằng
phần mềm sau khi điều kiện START đã được gửi (ghi 0 vào bit này để xóa).

Dùng để kích hoạt tín hiệu xác nhận ACK. Đối với chip Slave, cần đặt bit TWEA bằng 1 để Slave
có thể đáp ứng lại Master bất cứ khi nào được gọi.

Cờ báo ngắt TWI. Xem thêm trong giáo trình VXL các trường hợp cờ TWINT = 1.
81
❖ (TWI Data Register)
TWD7 TWD6 TWD5 TWD4 TWD3 TWD2 TWD1 TWD0 TWDR (0xB8)
▪ Thanh ghi dữ liệu 8 bit, địa chỉ MEM = 0xB8, đọc/ghi truy xuất bit được. Trong chế độ
thu, byte nhận được sau cùng được ghi vào TWDR. Trong chế độ phát, chúng ta ghi
byte cần phát vào TWDR để phát đi trên đường SDA. Lưu ý là TWDR chỉ được phép
truy cập khi bit TWINT được đặt lên 1, nếu không xảy ra xung đột dữ liệu.

❖ TWAR (TWI Address Register)


TWA6 TWA5 TWA4 TWA3 TWA2 TWA1 TWA0 TWGCE TWAR (0xBA)
▪ Thanh ghi địa chỉ, địa chỉ MEM = 0xBA, đọc/ghi truy xuất bit được. Trong đó, 7 bit cao
được dùng để gán địa chỉ cho Slave, bit TWGCE (TWI General Call Enable) dùng để
cho phép cuộc gọi chung. Nếu TWGCE = 1, Slave đáp ứng lại cuộc gọi chung từ
Master. Nếu TWGCE = 0, Slave bỏ qua cuộc gọi chung từ Master.

Nguyễn Lý Thiên Trường 82


❖ Các chế độ hoạt động TWI
▪ Trong giao thức TWI có tất cả 4 chế độ trong hoạt động. Các
chế độ hoạt động này gồm:
o Master phát dữ liệu (Master Transmitter).
o Master thu dữ liệu (Master Receiver).
o Slave phát dữ liệu (Slave Transmitter).
o Slave thu dữ liệu (Slave Reicever).
 Xem chi tiết các bước lập trình các chế độ hoạt động TWI
trong mục 8.5 Giáo trình VXL.
 Xem Video I2C (mục Video hỗ trợ).

Nguyễn Lý Thiên Trường 83

You might also like