giao trinh hop ngu pic - t.au
TRANSCRIPT
![Page 1: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/1.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
1
CHƯƠNG 1: CẤU TRÚC VI ĐIỀU KHIỂN PIC 16F
1. Cấu trúc chung:
Hình 1: Cấu trúc chung của vi điều khiển
Cấu tạo của vi điều khiển có thể chia làm 2 phần cơ bản như sau:
- Phần lõi: gồm bộ điều khiển trung tâm có chức năng chạy chương trình
(gồm các câu lệnh) đã được nạp vào trong bộ nhớ chương trình (program
memory) trước đó.
- Phần ngoại vi: gồm có các timer, bộ biến đổi tương tự số ADC và các
modun khác
![Page 2: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/2.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
2
Phần lõi của vi điều khiển chịu trách nhiệm chạy chương trình trong vi điều khiển
và quản lý toàn bộ các hoạt động khác bao gồm hoạt động của ngoại vi.
Vi điều khiển chạy chương trình gồm các lệnh trong bộ nhớ chương trình, địa chỉ
của lệnh nằm trong thanh ghi bộ đếm chương trình PC, lúc khởi động PC=0, sau
khi thực hiện một lệnh PC=PC+1 do đó vi điều khiển chạy lệnh kế tiếp trong
chương trình. Lệnh vi điều khiển trong bộ nhớ thực ra đã được mã hóa mỗi lệnh
thành 14 bit. Quá trình thực hiện một lệnh gồm các bước:
- Lệnh trong bộ nhớ chương trình được đưa vào thanh ghi lệnh (địa chỉ của lệnh
nằm trong thanh ghi PC). Sau đó lệnh đưa vào bộ giải mã và điều khiển để giải mã
lệnh. Trên cơ sở đó, vi điều khiển biết lệnh đó là lệnh gì, thao tác với dữ liệu nào,
phép thao tác v.v.v Trên cơ sở đó, nếu lệnh thao tác với dữ liệu chứa trong các
thanh ghi trong RAM, bộ điều khiển điều khiển đọc dữ liệu trong RAM đưa vào
bộ xử lý số học và logic ALU, các phép toán sẽ được thực hiện qua trung gian là
thanh ghi làm việc W, quá trình sẽ kết thúc khi kết quả trả dữ liệu về cho chương
trình, tiếp theo PC tăng lên 1 đơn vị, vi điều khiển nhảy đến lệnh kế tiếp, tiếp tục 1
chu kì thực hiện lệnh
![Page 3: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/3.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
3
CHƯƠNG 2: BỘ NHỚ VI ĐIỀU KHIỂN PIC 16F877A
Bộ nhớ vi điều khiển PIC chia làm 3 phần:
- Bộ nhớ chương trình-FLASH: chứa nội dung của chương trình chạy trong
vi điều khiển. Bộ đếm chương trình PC (Program counter) sẽ thực hiện các
lệnh chứa trong bộ nhớ chương trình này theo thứ tự từ trên xuống.
- Bộ nhớ dữ liệu tạm thời- RAM : Gồm 2 phần: các thanh ghi đặc biệt-SFR
(Special Function Register) - đây là các thanh ghi chức năng thể hiện hoặc
trạng thái, điều khiển của các khối bên trong vi điều khiển PIC (các thanh
ghi trạng thái các chân vi điều khiển như PORTA v.v, Các thanh ghi Status
v.vv, TMR0 cho timer v.v ). Các thanh ghi mục đích chung GPR (general
purpose register) là nơi lưu các giá trị tạm thời, nơi mà các biến chương
trình nằm đây
- Bộ nhớ dữ liệu không mất nội dung- EEPROM cho phép chứa các dữ liệu
và dữ liệu này không mất nội dung khi mất điện (phần này xem như thiết bị
ngoại vi)
2.1 Bộ nhớ chương trình:
Bộ nhớ chương trình là nơi chứa các lệnh đã được mã hóa. Quá trình mã hóa đã
được thực hiện trong khâu dịch chương trình trên máy tính ra file hex và nạp
chương trình vào bộ nhớ chương trình.
Mỗi một lệnh đã được mã hóa được chứa trong 1 thanh ghi 14 bit trong bộ nhớ
chương trình
Như vậy khi hình dung về bộ nhớ chương trình ta có hình ảnh sau:
Bit
13
Bit
12
Bit
11
Bit
10
Bit
9
Bit
8
Bit
7
Bit
6
Bit
5
Bit
4
Bit
3
Bit
2
Bit
1
Bit
0
0 0 0 1 1 1 0 1 0 1 1 0 1 0
0 0 0 1 0 1 1 0 1 1 1 1 1 0
--- --- -- -- -- -- -- -- -- -- -- -- -- --
![Page 4: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/4.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
4
-- -- -- -- -- -- -- -- -- -- -- -- -- --
- -- -- -- -- -- -- -- -- -- -- -- -- --
1 1 1 1 1 1 0 0 1 1 1 0 1 1
0 1 1 1 0 1 0 1 0 1 0 1 0 1
Chương trình của vi điều khiển chạy theo thứ tự từ địa chỉ thấp đến địa chỉ cao, địa
chỉ lệnh là nội dung của thanh ghi bộ đếm chương trình PC (Program Counter).
Đối với pic16f877a, thanh ghi PC có độ dài 13 bit.
Nếu dùng 2 bit địa chỉ ta phân biệt được 4 địa chỉ: 00,01,10,11
Nếu dùng 3 bit địa chỉ ta phân biệt được 8 địa chỉ:
000,001,010,011,100,101,110,111
-----
Suy ra, PC dùng 13 bit địa chỉ ta phân biệt được 2^13= 2^3 x 2^10=8K địa chỉ
Khi bật nguồn cho vi điều khiển (hay ấn nút reset chương trình), PC được xóa về 0
Sau khi thực hiện xong 1 lệnh nội dung của PC tăng lên 1 đơn vị: PC=PC+1 (trừ 1
số lệnh đặc biệt như gọi chương trình con, goto v.v.v)
Do vi điều khiển sẽ thực hiện lệnh tại địa chỉ chứa trong thanh ghi PC nên theo
phân tích trên có thể nói, vi điều khiển thực hiện lệnh tuần tự từ địa chỉ thấp đến
địa chỉ cao
2.1.1 Mã hóa và giải mã lệnh:
Như đã nói ở trên, khi chương trình đã nằm trong bộ nhớ (tức là đã được nạp vào),
các lệnh đã được mã hóa thành số nhị phân 14 bit chứa trong các thanh ghi của bộ
nhớ chương trình.
Việc mã hóa này phải tuân theo qui luật của từng loại vi điều khiển mà cụ thể đối
với PIC16F877a thì việc mã hóa phải tuân theo qui luật của nhà sản xuất
microchip qui định để trong quá trình thực hiện 1 lệnh, bộ điều khiển bên trong
của vi điều khiển PIC có thể giải mã (để hiểu) và thực thi lệnh đó được.
Để tiện cho việc theo dõi, ta đưa ra bảng tổng hợp các lệnh vi điều khiển 16f877a
như sau:
![Page 5: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/5.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
5
Bảng 1: Tập hợp tấc cả các lệnh
Toàn bộ tập lệnh chia làm 3 dạng:
- Lệnh thao tác theo từng byte (Byte-Oriented)
- Lệnh thao tác theo từng bit (Bit-Oriented)
- Lệnh thao tác với hằng số
Trong vi điều khiển pic16f877a không có lệnh thực hiện tương tác giữa 2 thanh
ghi, hay giữa thanh ghi và một số (chú ý ở đây, thanh ghi là thanh ghi nằm
trong bộ nhớ RAM, ví dụ như PORTA hoặc thanh ghi có địa chỉ 0x21 )
Để giải quyết vấn đề trên, trong vi điều khiển pic 16f877a có thanh ghi đặc biệt
làm chức năng trung gian cho các thao tác trên gọi là thanh ghi làm việc W
(work register)
Để dễ hiểu:
![Page 6: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/6.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
6
Giả sử ta có 2 biến a,b (tất nhiên là được khai báo và cấp phát trong bộ nhớ
RAM)
Ta muốn thực hiện phép toán:
a=a+b
Trong PIC không có lệnh thực hiện giữa 2 thanh ghi a và b. Để thực hiện lệnh
này ta phải qua các bước:
W=0
W= w+b (sau lệnh này w=b)
a=w+a (sau lện này a=w+a=b+a)
Các lệnh thực hiện chuỗi phép toán trên như sau:
CLRW
ADDWF b,0
ADDWF a,1
Phần cụ thể về lệnh sẽ được giới thiệu sau, ta trở lại vấn đề mã hóa lệnh
Cấu trúc một lệnh:
Cấu trúc một lệnh phụ thuộc vào 3 dạng thao tác theo byte, theo bit , thao tác
với hằng số
- Các lệnh thao tác theo byte
Cấu trúc lệnh:
o Mã lệnh thanhghi,d
Đây là các tác động giữa thanh ghi trong RAM và thanh ghi làm việc W, d
chỉ hướng kết quả được lưu trữ. d=0 kết quả chứa trong w, d=1 kết quả chứa trong
thanh ghi
Ví dụ: cộng thanh ghi a với w, kết quả chứa trong a
ADDWF a,1
cộng thanh ghi a với w, kết quả chứa trong w
ADDWF a,0
![Page 7: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/7.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
7
Câu hỏi đặt ra là khi vi điều khiển đọc mã lệnh trong bộ nhớ gồm các con số
nhị phân làm sao nó xác định được đâu là lệnh cộng (ADDWF) đâu là lệnh
AND (ANDWF) ?
Câu trả lời là trong lúc mã hóa lệnh một số bit đầu tiên của chuỗi 14 bit lệnh
dành để phân biệt các lệnh với nhau hay còn gọi là mã toán tử hay theo tiếng
anh là opcode
Đối với lệnh theo tác theo byte, vi điều khiển dùng 6 bit để mã hóa opcode
Để phân biệt khi nào kết quả chứa trong thanh ghi, khi nào thì chứa trong w, vi
điều khiển dành 1 bít hướng d: d=0 kết quả chứa trong thanh ghi w, d=1 kết
quả chứa trong thanh ghi.
7 bit còn lại trong để phân biệt lệnh tác động với thanh ghi nào trong bộ nhớ
RAM
13 8 7 6 0
OPCODE (toán tử) d(hướng) FILE (địa chỉ thanh ghi)
Ví dụ: ADDWF a, 0
Giải sử a địa chỉ là 0x30= 0b00110000
Mã lệnh sẽ là:
OPCODE (toán tử) d(hướng) FILE (địa chỉ thanh ghi)
00 0111 0 0110000
- Các lệnh thao tác theo bit
Cấu trúc lệnh gồm 3 phần:
o Mã lệnh thanh ghi, bit
Các lệnh bao gồm lệnh set 1 bit nào đó của một thanh ghi nào đó lên mức 1 hoặc
xóa bít đó về 0, hoặc kiểm tra 1 bit nào đó của một thanh ghi nào đó bằng 0 hoặc
bằng 1 v.v
Từ đây, ta có thể thấy, cần một số bit trong 14 bit của 1 lệnh dành để phân biệt các
lệnh với nhau, cụ thể ở đây là 4 bit, cần 3 bít để xác định vị trí bít nào trong thanh
![Page 8: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/8.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
8
ghi bị tác động (vì vị trí bít là 0-7), còn lại 7 bít để xác định thanh ghi nào trong
các thanh ghi bộ nhớ RAM bị tác động
13 10 9 7 6 0
OPCODE (toán tử) Vị trí bít FILE (địa chỉ thanh ghi)
Ví dụ: BCF a,3
Xóa bít 3=011 của thanh ghi a (giả sử a được khai báo trước và có địa chỉ
0x24=0b0010 0100 trong bộ nhớ RAM)
13 10 9 7 6 0
OPCODE (toán tử) Vị trí bít FILE (địa chỉ thanh ghi)
01 00 011 010 0100
- Các lệnh thao tác với hằng số và điều khiển rẽ nhánh chương trình:
Cấu trúc lệnh:
o Mã lệnh hằng số k
Các lệnh thông thường:
13 8 7 0
OPCODE (toán tử) Hằng số k
Trừ 2 lệnh Call k và goto k
13 11 10 0
OPCODE (toán tử) Hằng số k
Ví dụ: ADDLW 233
Miêu tả: cộng 233 vào thanh ghi w
13 8 7 0
OPCODE (toán tử) Hằng số k
11 111 1110 1001
![Page 9: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/9.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
9
Sau khi phân tích như trên, nhìn lại bảng 1: tập hợp tất cả các lệnh của vi điều
khiển pic16f877a ta đưa ra nhận xét sau:
- 2 bit đầu của 14 bit mã hóa lệnh xác định 3 dạng lệnh: thao tác theo byte
(00), thao tác theo bit (01), thao tác với hằng số (11 hoặc 10 hoặc 00)
- Có tấc cả 18 lệnh thao tác byte, như đã nêu ở trên, để mã hóa mã lệnh
(opcode-toán tử) dùng hết 6 bít: 2 bít phân biệt dạng thao tác theo byte (00)
vậy còn 4 bit để phân biệt 18 lệnh thao tác byte. Như ta biết với 4 bit chỉ
phân biệt được 2^4 =16 lệnh, làm sao phân biệt được 18 lệnh.
Thực ra vi điều khiển dùng 14 mã lệnh cho 14 lệnh, 2 mã lệnh còn lại, cụ
thể là 00 0001 cùng cho 2 lệnh CLRF (xóa nội dung thanh ghi) CLRW (xóa
nội dung thanh ghi W) và mã 00 0000 cùng cho 2 lệnh MOVWF (chuyển
nội dung của thanh ghi w sang thanh ghi F (có địa chỉ cụ thể trong ram) )
và lệnh NOP (lệnh không thực hiện nhiệm vụ gì). Thế làm sao phân biệt
được CLRF và CLRW? Đơn giản là khi
gặp mã lệnh 00 0001 vi điều khiển kiểm tra tiếp bit hướng d: rõ ràng nếu
d=0 (kết quả chứa trong w) thì đây là lệnh CLRW, nếu d=1 (kết quả chứa
trong thanh ghi f) thì đây là lệnh CLRF
Khi gặp mã 00 0000, vi điều khiển kiểm tra tiếp bit hướng d, d =1 thì đây
là lệnh MOVWF, d=0 là lệnh NOP
- Có 4 lệnh thao tác theo bit. 2 bit mã thao tác theo bit là 01, 2 bit còn lại
trong OPCODE (xem bên trên) để mã hóa 4 lệnh
- Có 13 lệnh thao tác với hằng số và điều khiển: một số lệnh có 2 bit mã thao
tác là 11 hoặc 10 hoặc 00. Kiểm tra các bit còn lại ta thấy đều có cách để vi
điều khiển thực hiện phân biệt được các lệnh với nhau
2.1.2 Cấu trúc bộ nhớ và stack:
![Page 10: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/10.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
10
Hình 2: Tổ chức bộ nhớ chương trình và Stack
2.1.2.1 Thanh ghi bộ đếm chương trình:
Tại mỗi thời điểm, vi điều khiển thực hiện 1 lệnh trong bộ nhớ chương trình có địa
chỉ cho bởi thanh ghi bộ đếm chương trình PC (Program Counter) gồm 13 bit.
Nhắc lại là với độ dài 13 bit, thanh ghi PC có thể phân biệt được tối đa 2^13=8K
địa chỉ.
Cấu trúc thanh ghi PC gồm 2 phần: phần thấp PCL (Program Counter Low) 8 bit
0-7, phần cao PCH (Program Counter High) 5 bit 8-12
12 11 10 9 8 7 6 5 4 3 2 1 0
PCH PCL
![Page 11: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/11.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
11
Trong đó các bit trong PCL là các bit có thể đọc ghi được
Các bit trong PCH<12-8> không thể đọc ghi và được cập nhật thông qua thanh ghi
PCLATCH<4-0>. Nghĩa là mỗi một lần tác động thay đổi 4 bit PCLATCH sẽ dẫn
đến thay đổi nội dung PCH.
Nhìn vào tổ chức bộ nhớ chương trình của vi điều khiển ta thấy rằng bộ nhớ
chương trình gồm 8 K chia thành 4 bank nhớ, mỗi bank có dung lượng 2 K từ
nhớ:
- Bank 0: 0000h-07FFh
- Bank 1: 0800h-0FFFh
- Bank 2: 1000h-17FFh
- Bank 3: 1800h-1FFFh
2 bit cao PCLATCH<4-3> sẽ qui định vi điều khiển đang truy cập bank nhớ nào
trong 4 bank nêu trên.
Khi bàn đến các lệnh CALL, GOTO, các lệnh trở về từ chương trình con, chương
trình ngắt ta sẽ quay lại vấn đề này
2.1.2.2 Reset vector:
Mỗi khi chương trình vi điều khiển bị reset lại (tắt nguồn, ấn nút reset), thanh ghi
PC bị xóa về 0, vậy vi điều khiển bắt đầu thực hiện lệnh chứa tại địa chỉ 0000h.
Do đó địa chỉ này gọi là địa chỉ vector reset
2.1.2.3 Stack:
Trong khi thực hiện chương trình, sẽ có những đoạn chương trình được thực hiện
nhiều lần, người lập trình để đơn giản chương trình sẽ đưa đoạn chương trình đó
thành chương trình con, mỗi lần cần thực hiện đoạn chương trình thì đơn giản là
gọi chương trình con đó.
Ví dụ chương trình con hay dùng nhất là chương trình delay ví dụ như các chương
trình con delay 100ms dưới đây
;************* 1msec Timer Subroutine
*****************
![Page 12: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/12.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
12
t1m movlw d'2' ;(1) Set loop
cnt1
movwf cnt1m ;(1) Save loop
cnt1
tm1lp1 movlw d'249' ;(1)*2 Set loop
cnt2
movwf cnt500u ;(1)*2 Save loop
cnt2
tm1lp2 nop ;(1)*249*2 Time adjust
nop ;(1)*249*2 Time adjust
decfsz cnt500u,f ;(1)*249*2 cnt500u-1=0
?
goto tm1lp2 ;(2)*248*2 No, continue
decfsz cnt1m,f ;(1)*2 cnt1m-1=0 ?
goto tm1lp1 ;(2) No. Continue
return ;(2) Yes. Cnt end
;Total
2501*0.4usec=1msec
;************* 100msec Timer Subroutine
***************
t100m movlw d'100' ;Set loop counter
movwf cnt100m ;Save loop counter
tm2lp call t1m ;1msec subroutine
decfsz cnt100m,f ;cnt100m - 1 = 0 ?
goto tm2lp ;No. Continue
return ;Yes. Count end
![Page 13: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/13.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
13
;************* 500msec Timer Subroutine
***************
t500m movlw d'5' ;Set loop counter
movwf cnt500m ;Save loop counter
tm3lp call t100m ;100msec subroutine
decfsz cnt500m,f ;cnt500m - 1 = 0 ?
goto tm3lp ;No. Continue
return ;Yes. Count end
chương trình chính:
-----
-----
bcf a,3
call t1m
bsf a,3
------
------
Như vậy sau khi thực hiện lệnh bcf a,3, gặp lệnh call t1m, thanh ghi PC được load
địa chỉ bắt đầu của chương trình t1m, sau khi gặp lệnh return vi điều khiển trở về
chương trình chính thực hiện lệnh bsf a,3. Câu hỏi đặt ra là làm sao vi điều khiển
nhớ được địa chỉ trở về? thực ra khi gặp lệnh CALL t1m, nội dung thanh ghi PC
tăng lên 1 đơn vị và giá trị này được lưu vào stack, sau đó PC được load địa chỉ
của lệnh đầu tiên trong chương trình con t1m và do đó vi điều khiển sẽ thực hiện
lệnh của chương trình con t100m, cho đến khi gặp lệnh return, PC load được chỉ
đã lưu trước đó ở stack ra, và do đó thực hiện tiếp lệnh bsf a,3
Qua ví dụ nói trên ta đã hình dung được nhiệm vụ của stack là lưu địa chỉ trở về từ
chương trình con, chương trình ngắt (sẽ đề cập sau)
![Page 14: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/14.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
14
Stack của vi điều khiển pic16f877a có thể quản lý đến 8 mức stack. Nếu sử dụng
đến mức stack thì 9 thì mức stack 9 này sẽ viết đè lên mức 1.
2.1.2.4 Vector ngắt:
Chưa bàn đến ngắt, nhưng chúng ta hình dung như thế này: mặc định vi điều khiển
thực hiện chương trình chính, khi có sự kiện ngắt xảy ra, nếu ngắt đó được cài đặt
trước trong chương trình thì vi điều khiển sẽ dừng thực hiện chạy chương trình
chính và nhảy vào địa chỉ 0004h, tại đó phần xử lý ngắt này do người lập trình viết
chương trình thực hiện
Và địa chỉ 0004h trong bộ nhớ chương trình được gọi là vector ngắt.
2.2 Tập lệnh vi điều khiển PIC:
2.2.1 Thời gian thực hiện 1 lệnh:
Chu kì thực hiện 1 lệnh gồm 4 bước, kí hiệu là Qi, i=1-4:
- Q1: thời gian giải mã lệnh
- Q2: thời gian đọc lệnh
- Q3: thời gian thực thi dữ liệu
- Q4: thời gian viết lệnh
Mỗi bước tương ứng với 1 chu kì xung của vi điều khiển.
![Page 15: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/15.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
15
Nếu dùng bộ dao động xung thạch anh có tần số f=4MHZ
Chu kì xung =1/tần số xung=1/4MHz
Chu kì lệnh = 4 * chu kì xung= 4/4MHZ= 1us (micro giây)
Hầu như tất cả các lệnh trong 35 lệnh của vi điều khiển PIC16F thực hiện trong 1
chu kì lệnh trừ 1 số lệnh đặc biệt như lệnh CALL, GOTO, RETURN,
RETFI,RETLW mất 2 chu kì lệnh
2.2.2 Tập lệnh:
Xem theo datasheet
![Page 16: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/16.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
16
2.3 Bộ nhớ dữ liệu tạm thời:
2.3.1 Tổ chức bộ nhớ:
Bộ nhớ dữ liệu tạm thời (RAM) của vi điều khiển gồm 2 phần chính, chia thành 4
bank nhớ:
- Các thanh ghi chức năng đặc biệt SFR (Special Function Register) điều
khiển quá trình hoạt động của các bộ phận chức năng trong vi điều khiển
cũng như các thiết bị ngoại vi được tích hợp trong vi điều khiển. Nhìn vào
hình vẽ tổ chức bộ nhớ như trên ta thấy, các thanh ghi SFR phân bố từ địa
![Page 17: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/17.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
17
chỉ 00h - 1Fh trong bank 0, phân bố rải rác từ 80F- 9Fh trong bank 1, từ
100h đến 11Fh trong bank 2, từ 180h-19Fh trong bank 3
- Các thanh ghi mục đích chung GPR (General Purpose Register) dùng để
chứa dữ liệu (dùng để đặt biến) từ 20h-7Fh trong bank 0, từ A0h-EFh trong
bank 1, từ 120h-16Fh trong bank 2, từ 1A0h-1F0h trong bank 3
Một số điểm chú ý khác là:
Khi truy cập các địa chỉ từ F0h-FFh trong bank 1, 170h-17Fh trong bank 2 và
1F0h-1FFh trong bank 3 tức là truy cập đến 70h-7Fh trong bank 0
Các vị trí màu xám không sử dụng.
Tại mỗi thời điểm, vi điều khiển làm việc trên một bank nhớ , việc lựa chọn
làm việc bank nhớ phụ thuộc 2 bit RP1, RP0 của thanh ghi STATUS<6-5>
Chú ý:
Trong chương trình viết cho vi điều khiển PIC, nếu sau ki thực hiện lệnh đối
với 1 thanh ghi (thanh ghi SFR hoặc GPR) ở bank i, muốn thực hiện l lệnh đối
với 1 thanh ghi khác ở bank j (j#i) ta phải chọn lại bank nhớ, tức là phải có
lệnh can thiệp đến 2 bit RP1 và RP0, nếu không lệnh sau sẽ không tác dụng:
Ví dụ: ta có chương trình như sau:
BCF TRISA,2
ADDWF PORTA,1
Lệnh thứ 2 sẽ không có tác dụng, vì lệnh đầu tiên thao tác với thanh ghi TRISA
nằm trên bank 1, trong khi lệnh thứ 2 tác động đến PORTA nằm trên bank 0
Chương trình đúng là
BSF STATUS,5 ; ĐƯA GIÁ TRỊ RP0 LÊN MỨC 1 CHỌN BANK 1
BCF TRISA,2
BCF STATUS,5 ; ĐƯA GIÁ TRỊ RP0 XUỐNG MỨC 0 CHỌN BANK 0
![Page 18: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/18.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
18
ADDWF PORTA,1
Lí do của việc phải chọn bank nhớ giải thích như sau:
Chúng ta xem lại bảng tổng hợp tất cả các lệnh của vi điều khiển PIC và để ý rằng
trong các lệnh thao tác với các thanh ghi (các thanh ghi nằm trong bộ nhớ RAM),
mỗi thanh ghi được mã hóa bằng 7 bit (tức là đánh số từ 00-7Fh)
Ví dụ:
Lệnh ADDWF f,d mã hóa lệnh như sau:
00 111 d fffffff
Như vậy: ADDWF PORTA,0 có mã lệnh: 00 111 0 000 0100 (do địa chỉ của
PORTA=0x05=000 0100)
ADDWF TRISA, 0 không thể viết bằng được vì địa chỉ TRISA=0x85=1000 0100
gồm 8 bit không thể mã hóa được bằng 7 bit như trong luật mã hóa lệnh
Để giải quyết vấn đề này, để mã hóa lệnh ADDWF TRISA,0 vi điều khiển mã
hóa thanh ghi TRISA bằng 7 bit như PORTA, việc phân biệt PORTA và TRISA
phụ thuộc vào các bit chọn bank nhớ RP1 và RP0.
Giải thích này cũng tương tự cho các lệnh thao tác trên thanh ghi của bank 1, bank
2 và bank 3.
Do đó, nếu khi thực hiện lệnh tiếp theo có thao tác với thanh ghi thuộc bank nhớ
khác với bank nhớ đang được tác động hiện tại cần phải có lệnh chọn lại bank nhớ
2.3.2 Địa chỉ gián tiếp:
Để hiểu về địa chỉ gián tiếp ta xem địa chỉ trực tiếp như thế nào
Để dễ hiểu ta cho ví dụ:
CLRF 0x30
Câu lệnh này thực hiện việc xóa thanh ghi có địa chỉ 30h trong bộ nhớ Ram. Rõ
ràng là địa chỉ ở đây là lấy trực tiếp trong RAM, địa chỉ được ghi trực tiếp trong
lệnh
Trong một số trường hợp ta dùng đến địa chỉ gián tiếp, cụ thể là: thanh ghi FSR
(File Select Register) chứa địa chỉ của thanh ghi trong RAM và thanh ghi INDF sẽ
![Page 19: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/19.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
19
ánh xạ vào thanh ghi RAM có địa chỉ là nội dung của FSR, mọi thao tác trên
INDF xem như là thao tác trên thanh ghi của RAM nêu trên
Ví dụ:
MOVLW 0x30
MOVWF FSR ; sau lệnh này FSR chứa 0x30 tức là chỉ đến thanh ghi có
địa chỉ 0x30 trong RAM
CLRF INDF; xóa INDF tức là xóa nội dụng của thanh ghi địa chỉ
0x30
Hình vẽ trên cho ta cách mà vi điều khiển xác định thanh ghi nào trong Ram được
thực hiện.
Trở lại ví dụ trên:
CLRF 0x30 lệnh này mã hóa như sau: 00 0001 1 fff ffff
Trong đó fff ffff= mã thanh ghi = 011 0000
Khi đó RP1=0, RP0=0 bank nhớ 0 được chọn
Rõ ràng là thông qua 7 byte thấp của opcode và giá trị RP1, RP0 vi điều khiển xác
định được thanh ghi trong bộ nhớ RAM
Đối với lệnh gián tiếp:
![Page 20: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/20.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
20
MOVLW 0x30
MOVWF FSR
CLRF INDF;
Vi điều khiển dựa vào bit IRP (là bít 7 của thanh ghi STATUS) và bít 7 của FSR
để xác định bank nhớ nơi chứa thanh ghi. 7 bít còn lại FSR<6-0> xác định chính
xác vị trí của thanh ghi đó
IRP FSR<7> Bank
0 0 0
0 1 1
1 0 2
1 1 3
![Page 21: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/21.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
21
CHƯƠNG 3: LẬP TRÌNH HỢP NGỮ
3.1 Dạng số trong chương trình hợp ngữ:
Các dạng số dùng trong chương trình hợp ngữ và cách viết trong hợp ngữ như
sau:
Dạng Cách viết Ví dụ
DECIMAL D’Số’
. ‘Số’
D’100’
HEXADECIMAL H ‘Số’
0xSố
H’20’
0x20
OCTAL O ‘Số’ O’10’
BINARY B’Số’ B’00011000’
ASCII A’kí tự’
‘kí tự’
A’X’
‘X’
3.2 Chú thích:
Chú thích nằm sau dấu chấm phẩy “;”
Ví dụ:
Movlw 0x20 ; đây là phần chú thích
3.3 Khai báo biến,hằng số:
Có một số phương pháp đặt biến, hằng số như sau:
- Dùng chỉ dẫn equ:
Tên hằng, biến equ giá trị
Ví dụ: count equ 0x20
- Dùng chỉ dẫn set:
Tên hằng, biến set giá trị
Ví dụ: count1 set 0x20
- Dùng chỉ dẫn variable:
![Page 22: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/22.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
22
Variable Tên hằng, biến = [biểu thức hoặc số]
Ví dụ: Variable count3=0x20
Hoặc Variable count3
- Dùng chỉ dẫn cblock:
Cblock địa chỉ
Biến1, biến 2, .....
Endc
Ví dụ: cblock 0x20
Count1, count2, count3
Endc
Khai báo cả một vùng các biến 1, 2 ..... bắt đầu từ địa chỉ
Một số chú ý:
- Khi đặt biến hằng bằng equ ta không thể định nghĩa lại
Ví dụ: viết như thế này là bị lỗi
Count equ 0x20
------------
----------
Count equ 0x23
Nhưng có thể đặt lại giá trị với set
Ví dụ: Viết như thế này không bị lỗi
Count set 0x20
------------
----------
Count set 0x23
- Biến hằng đi kèm với set và equ phải được khởi tạo giá trị (gán giá trị)
nhưng với variable thì không cần thiết
![Page 23: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/23.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
23
Ví dụ: Chương trình dịch sẽ báo lỗi:
Count equ
Count set
Nhưng thế này thì không báo lỗi
Variable count;
- Thực ra khi sử dụng set, variable, equ là ta khai báo hằng Chỉ có sử dụng
cblock thì đây mới thực sự là biến và được cấp phát bộ nhớ cho biến đó
Để hiểu rõ điều này ta cùng xem 2 ví dụ sau:
Ví dụ 1:
Count equ 0x21 ; khai báo hằng số count có giá trị 0x21
Movlw d’5 ; đưa giá trị 5 vào thanh ghi w: w=5
Movwf count ; chuyển giá trị w cho thanh ghi có địa chỉ bằng count tức
thanh
; ghi có địa chỉ 0x21 trong bộ nhớ RAM
Như vậy count đây xem như là một hằng số 0x21
Ví dụ 2:
CBLOCK 0x21
Count1, count2
Endc
Movlw d’5 ; đưa giá trị 5 vào thanh ghi w: w=5
Movwf count1 ; đưa giá trị w vào thanh ghi count1, tức thanh ghi có địa
chỉ
; 0x21 như đã khai báo
![Page 24: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/24.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
24
Rõ ràng trong trường hợp này count1 là biến, giá trị đưa vào trong lệnh là
địa chỉ của count1 không phải là giá trị của count1
- Khi khai báo các biến và hằng này, ta chú ý giá trị khởi tạo. Vì thực ra các
biến hằng này được sử dụng trong các lệnh như là địa chỉ các thanh ghi
nằm trong vùng nhớ RAM. Như ta biết địa chỉ dành cho các biến phải ở
trong vùng các thanh ghi mục đích chung:
Trong bank 0: 0x20->0x7f
Bank 1: 0xA0->0xEF
Bank 2: 0x120->0x16F
Bank 3: 0x1A0-0x1EF
Do đó giá trị khởi tạo cho các biến hằng khi khai báo cũng phải nằm trong
vùng này
- Khi làm việc với các biến hằng cần phải nhớ địa chỉ của thanh ghi trong
lệnh. Nếu đang thao tác với thanh ghi thuộc bank nhớ i (i=0-3) chuyển sang
lệnh tiếp theo làm việc với một thanh ghi khác thuộc bank nhớ j (j=0-3 và
j#i) cần phải có lệnh chuyển bank nhớ như trong chương 2 đã giới thiệu
3.4 Chỉ dẫn biên dịch chương trình ORG:
Cách thức:
ORG địa chỉ 1
Lệnh 1
Lệnh 2
--------
Lệnh n
ORG địa chỉ 2
Lệnh m
Lệnh m+1
-------
![Page 25: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/25.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
25
Miêu tả: chỉ dẫn biên dịch này để điều khiển chương trình dịch MPLAB phân
bố các lệnh nằm sau ORG (ở đây là lệnh 1, lệnh 2, ... lệnh n) và trước một chỉ
dẫn ORG tiếp theo (ORG địa chỉ 2) vào bộ nhớ chương trình từ địa chỉ bắt đầu
là địa chỉ 1
3.5 Nhãn:
Nhãn-label: là chuỗi kí tự do người lập trình đánh vào để đánh dấu một chuỗi thao
tác lệnh nào đó hoặc 1 chương trình con nào đó. Nhãn còn được dùng trong các
câu lệnh goto và call:
GOTO nhãn
CALL nhãn
Ví dụ 1: goto capnhat
Movf bien1,1
Movlw d’10
Movwf bien2
Capnhat:
Addlw d’2
Ví dụ 2:
Call ct1
Movf bien1,1
Movlw d’10
Movwf bien2
Ct1
Addlw d’2
Movwf bien2
Return
Qui định nhãn:
Nhãn có thể tập hợp các kí tự (số kí tự không giới hạn) và không được rơi vào một
số trường hợp sau:
- Bắt đầu bằng 2 kí tự gạch dưới (ví dụ: __abc là sai)
![Page 26: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/26.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
26
- Không được bắt đầu bằng các con số 0-9, *, & , giữa các kí tự không có các
kí tự đặc biệt
- Không được giống các từ đặc biệt của chương trình như ORG, các lệnh v.v
3.6 Cấu trúc của một chương trình hợp ngữ:
Cấu trúc cơ bản gồm các phần như sau:
; DUA FILE LIET KE VAO
#include p16f877a.inc ; chỉ dẫn bao gồm file định nghĩa chip vi điều khiển
; KHAI BAO CAU HINH
__CONFIG _HS_OSC & _WDT_OFF &_LVP_OFF
; KHAI BAO BIEN O DAY
; DIA CHI BIEN O VUNG NHO BIEN BANK0
cblock 0x21
vong1, vong2, vong3
endc
variable giatricong=0x25
;CHUONG TRINH VI DIEU KHIEN CHAY TAI DAY
org 0x000; CHI DAN BIEN DICH
; NHAY TOI CHUONG TRINH CHINH
goto main
; chi dan bien dich
; CHUONG TRINH CHINH BAT DAU TU DAY
org 0x005
main
; CHON BANK NHO CHUA THANH GHI TRISB
banksel trisb
; XOA THANH GHI TRISB, CHO PHEP CAC CHAN PORTB LA DAU RA
clrf trisb
; CHON BANK NHO CO THANH GHI PORTB
banksel PORTB
![Page 27: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/27.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
27
; BAT DAU XU LY
start:
; XOA BIT CO C
bcf status,c
; DUA GIA TRI 1 VAO BIEN GIATRICONG: GIATRICONG=0000 0001
movlw d'1
movwf giatricong
; DUA DU LIEU GIATRICONG VAO THANH GHI TRUNG GIAN W
loop1
movf giatricong,w
; CHUYEN GIA TRI CHO THANH GHI PORTB
movwf portb
; GOI CHUONG TRINH TAO TRE 200MS
call delay
; KIEM TRA BIT 7 CUA BIEN GIATRICONG: GIATRICONG=1000 0000?
btfss giatricong,7
; NEU SAI NHAY TOI UPDATE DE DICH BIT SANG TRAI
goto update
; NEU DUNG TRO LAI START BAT DAU LAI
goto start
; DOAN DICH BIT:
update: rlf giatricong,1
; XU LY TIEP
goto loop1
abc1234567890123456789012345678111111: movlw d'2
; CHUONG TRINH CON DELAY 200MS
DELAY:
nop
nop
![Page 28: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/28.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
28
movlw d'185
movwf vong1
movlw d'4
movwf vong2
movlw d'2
movwf vong3
decfsz vong1,f
goto $-1
decfsz vong2,f
goto $-3
decfsz vong3,f
goto $-5
; THOAT KHOI CHUONG TRINH CON
return
; KET THUC MOI CHUONG TRINH
End
- Phần thứ nhất, bắt buộc phải có là phần khai báo bao hàm file định nghĩa
vi điều
khiển, đây là phần bắt buộc
#include p16f877a.inc
Thực ra chỉ dẫn này ra lệnh cho MPLAB copy toàn bộ nội dung của file
“C:\Program Files\Microchip\MPASM Suite\p16f877a.inc” vào phần đầu
của chương trình vi điều khiển.
Nội dung của file này thực ra là định nghĩa các thanh ghi và các cài đặt trong
chương trình.
Chúng ta xem lệnh trong vi điều khiển như sau:
MOVLW B’00001111
MOVWF PORTA,1
![Page 29: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/29.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
29
Vi điều khiển không biết từ “PORTA” là gì cả, nó chỉ biết rằng thanh ghi đặc
biệt có địa chỉ 0x05 trong bộ nhớ RAM là nơi lưu trữ trạng thái và điều khiển
của các chân trên PORTA.
Như vậy để can thiệp đến các chân này người lập trình phải gửi lệnh
MOVLW B’00001111
MOVWF 0x05,1
Tuy nhiên, có rất nhiều thanh ghi đặc biệt trong vi điều khiển, người sử dụng
không thể nhớ được địa chỉ của nó để mà viết lệnh. Để tiện cho người lập trình,
chương trình biên dịch MPLAB chuẩn bị sẵn file định nghĩa p16f877a.inc
trong đó định nghĩa:
PORTA equ h’00005
Và người lập trình chỉ việc đưa dòng: #include p16f877a.inc
Toàn bộ nội dung của file trên sẽ được đưa vào chương trình và khi đó người
dùng viết các dòng lệnh có PORTA thì trình dịch sẽ tự động hiểu là 0x05 (vì
PORTA đã định nghĩa bằng 5)
Khi ta dùng vi điều khiển khác ví dụ như pic 18f4431 ta đơn giản thay đổi
dòng bao hàm bằng: #include p18f4431inc
- Phần thứ hai, khai báo cấu hình cho vi điều khiển
__CONFIG _HS_OSC & _WDT_OFF &_LVP_OFF
Mục đích của khai báo cấu hình là cài đặt một số chế độ hoạt động của vi điều
khiển như chọn nguồn xung dao động, tắt đồng hồ watchdog timer v.v
Các tham số cài đặt cho config xem ở file header
- Phần thứ ba, khai báo biến, sử dụng các phương pháp khai báo như đã giới
thiệu ở các mục trước
- Phần thứ tư, chương trình, kết thúc bởi nhãn END
- Các chương trình con phải đặt trước END, kết thúc chương trình con có
lệnh return
- Trong chương trình có sử dụng các chỉ dẫn biên dịch ORG để phân bố bộ
nhớ cho chương trình như đã đề cập trước đây
![Page 30: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/30.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
30
3.7 Dạng thức của 1 lệnh:
Có 3 loại lệnh:
- Lệnh thao tác với byte
- Lênh thao tác với bit
- Lệnh thao tác với số
3.7.1 Lệnh thao tác với byte:
Dạng lệnh: lệnh f,d
Trừ các lệnh: CLRW ( xóa thanh ghi W)
CLRF f (xóa thanh ghi f)
NOP (lệnh không làm gì)
Trong đó:
- Lệnh là từ gợi nhớ về phép toán thực hiện. Ví dụ: ADDWF là cộng thanh
ghi W và thanh ghi F
- F: là địa chỉ của thanh ghi (trong bộ nhớ RAM) được thao tác trong lệnh.
Ví dụ: ADDWF PORTA,1
ADDWF 0x05,1
Cả hai lệnh trên là giống nhau: Cộng thanh ghi w và thanh ghi có địa chỉ
0x05 trong bộ nhớ RAM. Vi điều khiển chỉ biết địa chỉ 0x05 không biết
PORTA là gì
- d: chỉ ra kết quả của lệnh chứa ở đâu.
o Nếu d=0: kết quả chứa trong w
o Nếu d=1: kết quả chứa trong thanh ghi f
o Mặc định: d=1, kết quả chứa trong thanh ghi f
Ví dụ: ADDWF 0x05,0
W=W+thanh ghi có địa chỉ 0x05
ADDWF 0x05,1
Thanh ghi có địa chỉ 0x05=W+ Thanh ghi có địa chỉ 0x05
3.7.2 Lệnh thao tác với bit:
Dạng lệnh: lệnh f,b
![Page 31: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/31.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
31
Trong đó:
- Lệnh là từ gợi nhớ về phép toán thực hiện
- f: địa chỉ thanh ghi
- b: vị trí của bit 0-7
Ví dụ: BSF 0x23,3
3.7.3 Lệnh thao tác với số:
Dạng lệnh: lệnh số
Trong đó:
- Lệnh là từ gợi nhớ về phép toán thực hiện
- Số là tham số trong phép toán
Ví dụ: ADDLW 0x30
3.8 Một số lệnh đặc biệt:
Tập lệnh của vi điều khiển PIC gồm 35 lệnh. Dạng lệnh và miêu tả dễ dàng
hiểu được qua datasheet. Ở đây chỉ giới thiệu một số lệnh đặc biệt
3.8.1 Lệnh kiểm tra trạng thái của bit:
Có 2 lệnh: BTFSS và BTFSC
- Dạng lệnh:
BTFSS địa chỉ thanh ghi, vị trí bít
Lệnh 1
Lệnh 2
Miêu tả: kiểm tra bít ở vị trí bít trên thanh ghi, nếu bít đó bằng 1 bỏ qua
lệnh 1 thực hiện lệnh 2, nếu bít đó bằng 0 thực hiện lệnh 1 (theo kiểu tuần tự)
Chú ý là khi bit =1, lệnh này mất 2 chu kì lệnh, khi bit =0 lệnh này mất 1 chu
kì lệnh
- Dạng lệnh:
BTFSC địa chỉ thanh ghi, vị trí bít
Lệnh 1
Lệnh 2
![Page 32: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/32.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
32
Miêu tả: kiểm tra bít ở vị trí bít trên thanh ghi, nếu bít đó bằng 0 bỏ qua
lệnh 1 thực hiện lệnh 2, nếu bít đó bằng 1 thực hiện lệnh 1 (theo kiểu tuần tự)
Chú ý là khi bit =0, lệnh này mất 2 chu kì lệnh, khi bit =1 lệnh này mất 1 chu
kì lệnh
3.8.2 Lệnh tăng giảm đồng thời kiểm tra thanh ghi:
Có 2 lệnh: DECFSZ, INCFSZ
- Dạng lệnh:
DECFSZ địa chỉ thanh ghi,hướng
Lệnh 1
Lệnh 2
Miêu tả: Lệnh này trước hết tự động giảm giá trị của thanh ghi đi 1 đơn vị
và sau đo kiểm tra nếu thanh ghi đó bằng 0 bỏ qua lệnh 1 thực hiện lệnh 2, nếu
khác 0 thực hiện lệnh 1 (theo kiểu tuần tự như bình thường)
Chú ý là khi thanhghi =1, lệnh này mất 2 chu kì lệnh, khi thanhghi#1 lệnh này
mất 1 chu kì lệnh
- Dạng lệnh:
INCFSZ địa chỉ thanh ghi,hướng
Lệnh 1
Lệnh 2
Miêu tả: Lệnh này trước hết tự động tăng giá trị của thanh ghi đi 1 đơn vị
và sau đo kiểm tra nếu thanh ghi đó bằng 0 bỏ qua lệnh 1 thực hiện lệnh 2, nếu
khác 0 thực hiện lệnh 1 (theo kiểu tuần tự như bình thường)
3.8.3 Lệnh nhảy không điều kiện GOTO:
Dạng lệnh: GOTO nhãn
Miêu tả: nhảy đến đoạn chương trình bắt đầu bởi nhãn
Để hiểu rõ đoạn chương trình trên ta có ví dụ sau:
START:
BSF PORTB,1
CALL DELAY
![Page 33: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/33.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
33
BCF PORB,1
CALL DELAY
GOTO START
Khi gặp lệnh GOTO START, vi điều khiển lập tức nhảy đến đoạn chương trình
bắt đầu bởi START tức là sẽ nhảy đến lệnh BSF PORTB,1
Thực ra, đằng sau lệnh GOTO START, vi điều khiển tính ra địa chỉ của lệnh bắt
đầu sau START giả sử đó là k, và câu lệnh thực sự là GOTO k
Chúng ta xem dạng dissampling của đoạn lệnh trên sẽ rõ:
29: ; BAT DAU XU LY
30: START:
000A 1486 BSF 0x6, 0x1 31: BSF PORTB,1
000B 200F CALL 0xf 32: CALL DELAY
000C 1086 BCF 0x6, 0x1 33: BCF PORTB,1
000D 200F CALL 0xf 34: CALL DELAY
000E 280A GOTO 0xa 35: GOTO START
Cột thứ nhất chứa địa chỉ của lệnh. Cột thứ 3 là lệnh thực sự đã được phân giải.
Ta thấy lệnh nằm ngay sau nhãn là BSF PORTB,1 có địa chỉ là 0x0a trong bộ nhớ
chương trình
Vì vậy trong cột 3, ta có lệnh GOTO 0xa
Như vậy dạng lệnh thực sự là GOTO k
Khi gặp lệnh này: PC<10:0>=k; PC<12:11>=PCLATCH<4:3>
Tóm lại có thể giải thích lại như sau:
Khi gặp lệnh: goto nhãn
Trong trường hợp trên là goto start
MPLAB tính ra địa chỉ của lệnh nằm ngay sau nhãn start, giả sử đó là k
Trong trường hợp trên là lệnh BSF PORTB,1 có địa chỉ k=0x0a
MPLAB điều khiển đưa giá trị k vào thanh ghi PC: PC=k
![Page 34: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/34.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
34
Do đó chương trình vi điều khiển sẽ chạy lệnh BSF PORTB,1
Mã hóa lệnh: 10 1kkk kkkk kkkk
Nhìn vào mã hóa lệnh ta thấy địa chỉ k gồm 11 bit kkk kkkk kkkk
Như vậy thực ra chỉ 11 bit đầu của thanh ghi PC là chứa giá trị k:
PC<10:0>=kkk kkkk kkkk
2 bit cao của thanh ghi PC lấy từ thanh ghi PCLATCH
PC<12:11>=PCLATCH<4:3>
Điều này sẽ dẫn đến một vấn đề!!!
Giả sử ta có đoạn chương trình sau:
org 0x005
main
banksel trisb
clrf trisb
banksel PORTB
START:
BSF PORTB,1
CALL DELAY
BCF PORTB,1
CALL DELAY
GOTO UPDATE
ORG 0x800
UPDATE:
MOVLW 0xFF
MOVWF PORTB
Như vậy lệnh GOTO UPDATE nằm ở địa chỉ 0x0E
Như vậy khi gặp lệnh này, 2 bit của thanh ghi PCLATCH<4:3>=00
Lệnh nằm sau nhãn UPDATE là MOVLW 0xFF nằm ở địa chỉ 0x800 (do có chỉ
dẫn biên dịch ORG 0x800)
Suy ra địa chỉ là: 0x800=1000 0000 0000
![Page 35: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/35.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
35
Như vậy khi gặp lệnh GOTO UPDATE, thanh ghi PC được nạp giá trị:
PC<10:0> = 000 0000 0000
PCLATCH vẫn không đổi: PCLATCH<4:3>=00
Suy ra, PC<12:11>=PC<4:3>=00
Suy ra: PC=0 0000 0000 0000 =0x00
Vậy chương trình nhảy đến địa chỉ 0x00!!!!!
Chắc chắn là chương trình sẽ không chạy được đoạn lệnh nằm sau UPDATE
Để chạy đúng, đơn giản là ta phải dùng lệnh cho PCLATCH<4:3>=01
Đoạn chương trình đúng sẽ như sau:
org 0x005
main
banksel trisb
clrf trisb
banksel PORTB
START:
BSF PORTB,1
CALL DELAY
BCF PORTB,1
CALL DELAY
PAGESEL UPDATE
GOTO UPDATE
ORG 0x800
UPDATE:
MOVLW 0xFF
MOVWF PORTB
3.8.4 Lệnh gọi chương trình con CALL:
Dạng lệnh: CALL nhãn
Cách làm việc của lệnh này tương tự như lệnh goto chỉ khác là trước khi nhảy đến
địa chỉ nhãn, vi điều khiển lưu lại địa chỉ của lệnh kế tiếp sau lệnh CALL tức
![Page 36: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/36.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
36
PC+1 vào ngăn xếp để sau khi thực hiện đoạn chương trình con, vi điều khiển
chạy về chương trình chính và thực hiện lệnh kế tiếp đó.
3.8.5 Các toán tử:
Các kí hiệu +,-,*, / v.v.v gọi là các toán tử.
Chương trình hợp ngữ MPLAB qui định một tập các toán tử như sau:
Qua bảng trên ta dễ dàng hiểu được chức năng của từng toán tử.
Ở đây chỉ lưu ý một số toán tử đặc biệt như sau:
- Toán tử $: thường đi kèm với lệnh goto
![Page 37: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/37.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
37
Goto $
Sau khi thực hiện lệnh này, thanh ghi PC giữ giá trị không đổi: PC=PC
(Thông thường sau khi thực hiện 1 lệnh, PC=PC+1, vi điều khiển thực hiện lệnh
tiếp theo)
Goto $-n
Sau khi thực hiện các lệnh này, PC=PC-$, như vậy sau khi thực hiện lệnh này, vi
điều khiển sẽ nhảy đến thực hiện lệnh trước lệnh hiện tại n lệnh
Tương tự với:
Goto $+n
Ví dụ:
decfsz bien1,F
goto $-1
decfsz bien2,F
goto $-3
Như vậy khi gặp lệnh goto $-1, vi điều khiển nhảy về thực hiện lệnh trước đó nằm
cách 1 lệnh decfsz bien1,f, khi gặp lệnh goto $-3, vi điều khiển nhảy về thực hiện
lệnh trướ đó nằm cách 3 lệnh tức là sẽ thực hiện lệnh decfsz bien1,f
- Toán tử !: hay được dùng trong câu điều kiện
If(!(a= =b)) nghĩa là nếu a!=b
3.9 Chu kì lệnh:
Chu kì thực hiện 1 lệnh gồm 4 bước, kí hiệu là Qi, i=1-4:
- Q1: thời gian giải mã lệnh
- Q2: thời gian đọc lệnh
![Page 38: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/38.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
38
- Q3: thời gian thực thi dữ liệu
- Q4: thời gian viết dữ liệu
Mỗi bước tương ứng với 1 chu kì xung của vi điều khiển. Xung dao động của vi
điều khiển được tạo ra từ mạch dao động bên ngoài như thạch anh, mạch RC hoặc
mạch dao động bên trong (Phần cấu hình cho dao động sẽ được đề cập ở mục
khác).
Nếu dùng bộ dao động xung thạch anh có tần số f=4MHZ
Chu kì xung =1/tần số xung=1/4MHz
Chu kì lệnh = 4 * chu kì xung= 4/4MHZ= 1us (micro giây)
Hầu như tất cả các lệnh trong 35 lệnh của vi điều khiển PIC16F thực hiện trong 1
chu kì lệnh trừ 1 số lệnh đặc biệt như lệnh CALL, GOTO, RETURN,
RETFI,RETLW mất 2 chu kì lệnh. Ngoài ra còn có một số lệnh khi thì thực hiện
trong 1 chu kì lệnh khi thì 2.
Đó là các lệnh DECFSZ, INCFSZ, BTFSZ, BTFSC. (Xem tập lệnh trang 158-
datasheet 16f877a)
Ta lấy lệnh DECFSZ để giải thích cho dễ hiểu:
Ví dụ: DECFSZ bien,1
GOTO nhan1
GOTO nhan2
Lệnh đầu tiên DECFSZ giảm thanh ghi bien đi 1 đơn 1: bien=bien-1
Sau đó lệnh này kiểm tra bien:
Nếu bien=0 nhảy qua 1 lệnh, tức là nhảy đến và thực hiện lệnh GOTO nhan2
Nếu bien#0 thì không nhảy tức là thực hiện lệnh GOTO nhan2
Như vậy nếu bien#0 (tức là trước khi gặp lệnh này bien #1)thì vi điều khiển mất
một chu kì lệnh để thực hiện lệnh(để thực hiện thao tác trừ)
Nếu bien=0 (tức là trước khi gặp lệnh này bien=1) thì vi điều khiển mất 2 chu kì
lệnh để thực hiện lệnh(1 chu kì lệnh để thao tác trừ + 1 chu kì lệnh để nhảy)
Thời gian thực hiện lệnh INCFSZ, BTFSC,BTFSS cũng tương tự như vậy
![Page 39: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/39.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
39
3.10 Chương trình con tạo thời gian trễ:
Trong thực tế viết chương trình điều khiển cho một số thiết bị ta hay tạo một
khoảng thời gian trễ.
Ví dụ: sự kiện 1
Chờ một khoảng thời gian
sự kiện 2
Vì vậy đòi hỏi ta phải có một chương trình con để tạo thời gian trễ này.
Ý tưởng để có một chương trình con tạo thời gian trễ là:
Muốn tạo ra một khoảng thời gian trễ n (micro giây) ta tạo một chương trình mà
thời gian để thực hiện xong nó là n (micro giây)
Ta đã biết: một lệnh thực hiện trong 1 chu kì lệnh (trừ một số lệnh đặc biệt), một
chu kì lệnh tính theo đơn vị micro giây.
Ví dụ: nếu dùng bộ dao động ngoài sử dụng thạch anh có tần số fosc=4Mhz
Suy ra, chu kì lệnh= 4*chu kì xung= 4/tần số xung= 4/4Mhz=1 mico giây
Như vậy để tạo ra khoảng thời gian n micro giây đơn giản ta tạo ra một chương
trình mà thời gian thực hiện nó là n chu kì lệnh
Ví dụ: để tạo thời gian trễ 20 mico giây
Ta dùng 20 lệnh NOP:
NOP
NOP
-------
NOP
Tuy nhiên cách làm đó thì hơi thủ công, và ta cũng không có thời gian để mà đánh
n dòng NOP như vậy (ví dụ: n=200.000!!!!)
Ta phải dùng các lệnh khác với thuật toán phức tạp hơn. Ta đi vào từng bước khảo
sát phương pháp này.
3.10.1 Vòng 1:
Ta có các câu lệnh sau:
![Page 40: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/40.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
40
decfsz vong1,F
goto $-1
Trong đó vong1 là biến đã được tạo ra trước đó
Ta tính thử thời gian thực hiện 2 lệnh trên:
Như đã bàn ở các mục trước lệnh goto mất 2 chu kì lệnh, lệnh DECFSZ f,z mất 1
chu kì lệnh khi f #0 và mất 2 chu kì lệnh khi f=0
Giả sử ban đầu:
Vong1=2.
Lần 1: decfsz vong1,f -> vong1=1 -> vong1#0 (mất 1 chu kì lệnh)
Goto $-1 (mất 2 chu kì lệnh)
Lần 2: dectsz vong1, f -> vong1=0 chương trình bỏ qua lệnh goto -> thoát (mất 2
chu kì lệnh)
Như vậy mất tổng cộng: (1+2)+2= 5 chu kì lệnh
Giả sử ban đầu:
Vòng 1=3:
Lần 1: decfsz vong1,f -> vong1=2 -> vong1#0 (mất 1 chu kì lệnh)
Goto $-1 (mất 2 chu kì lệnh)
Số chu kì lệnh mất= 3 chu kì lệnh
Lần 2: decfsz vong1,f -> vong1=1 -> vong1#0 (mất 1 chu kì lệnh)
Vong1=vong1-1
Vong1=0?
Thoát
YES
NO
![Page 41: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/41.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
41
Goto $-1 (mất 2 chu kì lệnh)
Số chu kì lệnh mất = 3 chu kì lệnh
Lần 2: dectsz vong1, f -> vong1=0 chương trình bỏ qua lệnh goto -> thoát
Số chu kì lệnh mất = 2 chu kì lệnh
Tổng số chu kì lệnh mất = 3*2 +2
Cứ như vậy thì có thể qui nạp lệnh
Số chu kì lệnh mất tổng cộng với một giá trị của biến vong1 là:
3* (vong1-1) +2 =3*vong1-1 (1)
(3 chu kì lệnh cho (vong1-1) lần đầu và 2 chu kì lệnh cho lần cuối)
Số chu kì lệnh tối thiểu và tối đa tạo trễ được.
- Khi vong1=1: số chu kì lệnh= 2
- Khi vong1=0: số chu kì lệnh= 3*256-1= 767
3.10.2 Vòng 2:
Ta có đoạn lệnh sau:
decfsz vong1,F
goto $-1
decfsz vong2,F
goto $-3
Thuật toán:
![Page 42: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/42.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
42
Giả sử ban đầu:
Vong1=3, vong2=3
Như đã phân tích trong mục 3.10.1, số chu kì lệnh mất cho đến khi chương trình
bắt đầu xử lý vong2 (tương đương với số chu kì lệnh mất cho đến khi nhảy đến
thoát trong mục 3.10.1) là: 3*vong1 -1 chu kì lệnh
Đến đây vong1=0, xử lý tiếp vong2
Lần 1: decfsz vong2,f : vong2=vong2-1=3-1=2, vong2#0 nhảy đến goto $-3 (mất
1 chu kì lệnh)
Goto $-3 nhảy đến lệnh decfsz vong1,f (mất 2 chu kì lệnh)
Tổng chu kì lệnh mất = 3 chu kì lệnh
Tới lệnh: decfsz vong1,f (vong1 bây giờ đang =0)
Xem như đây là chu trình giống mục 3.10.1 với vong1=0
Suy ra cho đến khi gặp lại decfsz vong2,f mất: 3*256-1=767 chu kì lệnh
Như vậy tổng số chu kì lệnh cho lần 1 là 767+3= 770 chu kì lệnh
Vong1=vong1-1
Vong1=0?
Thoát
YES
NO Vong1=0?
Vong2=0?
Vong2=vong2-1
NO
YES
![Page 43: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/43.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
43
Lần 2: decfsz vong2,f : vong2=vong2-1=2-1=1, vong2#0 nhảy đến goto $-3
Tương tự như trên:
tổng số chu kì lệnh cho lần 2 là 767+3= 770 chu kì lệnh
Lần 3: decfsz vong2,f : vong2=vong2-1=1-1=0, vong2=0 nhảy đến thoát (mất 2
chu kì lệnh)
Như vậy tổng chu kì lệnh mất đi là:
3*vong1-1 + 770*2 +2
Qui nạp lên:
3*vong1-1 + 770*(vong2-1) +2= 3*vong1 +770*vong2 -769
Vậy số chu kì lệnh = 3*vong1 +770*vong2 -769 (2)
3.10.3 Vòng 3:
Ta có đoạn lệnh sau:
decfsz vong1,F
goto $-1
decfsz vong2,F
goto $-3
decfsz vong3,F
goto $-5
Thuật toán:
Vong1=vong1-1
Vong1=0?
Thoát
YES
NO Vong1=0?
Vong2=0?
Vong2=vong2-1
NO
YES
Vong3=vong3-1
Vong3=0?
YES
NO
![Page 44: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/44.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
44
Tính toán tương tự như trên, ta có công thức:
Số chu kì lệnh= 3*vong1+ 770*vong2 + 197122*vong3 - 197889 (3)
Ta cũng cần chú ý ở đây nữa là để nạp giá trị đầu cho biến vong1 ta mất 2 chu kì
lệnh cho 2 lệnh sau:
Movlw d’ giá trị đầu (ví dụ: movlw d’100)
Movwf vong1
Tương tự như vậy nếu dùng vòng 2 ta sẽ mất thêm 2 lệnh cho việc khởi tạo giá trị
đầu biến vong2 như vậy mất 4 chu kì lệnh
Tương tự như vậy nếu dùng vòng 3 ta sẽ mất thêm 2 lệnh cho việc khởi tạo giá trị
đầu biến vong3 như vậy mất 6 chu kì lệnh
Ta phải gọi chương trình con tạo thời gian trễ: call delay mất thêm 2 chu kì lệnh
Trong chương trình con delay có lệnh trở về return mất thêm 2 chu kì lệnh.
Như vậy công thức tổng quát cuối cùng là:
- Nếu chỉ sử dụng vòng 1:
Số chu kì lệnh = 3*vong1-1+2 (cho khởi tạo biến vong1) +2 (cho lệnh
call delay) +2 (cho lệnh return)
Số chu kì lệnh= 3*vong1+5 (4)
- Nếu sử dụng vòng 1 và vòng 2:
Số chu kì lệnh = 3*vong1 +770*vong2 -769 +4 (cho khởi tạo biến
vong1, vong2) +2 (cho lệnh call delay) +2 (cho lệnh return)
Số chu kì lệnh= 3*vong1+770*vong2-761 (5)
- Nếu sử dụng cả 3 vòng 1, vòng 2 và vòng 3:
Số chu kì lệnh = 3*vong1+ 770*vong2 + 197122*vong3 - 197889 +6
(cho khởi tạo biến vong1, vong2, vong3) +2 (cho lệnh call delay) +2 (cho lệnh
return)
Số chu kì lệnh= 3*vong1+ 770*vong2 + 197122*vong3 -197879 (6)
Tính toán:
![Page 45: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/45.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
45
Giả sử ta dùng bộ dao động thạch anh f=4Mhz, cho chân rb0 lên mức 1, tạo thời
gian trễ 200ms, cho chân rb0 xuống mức 0.
Thời gian trễ ở đây là 200ms.
Giả sử ta áp dụng thuật toán 3 vòng.
200ms= 200.000 micro giây= 200.000 chu kì lệnh
Áp dụng công thức (6) ta có:
3*vong1+ 770*vong2 + 197122*vong3 -197879 = 200.000
Suy ra:
3*vong1+ 770*vong2 + 197122*vong3 = 397897
Vong3= 397897/197122= 2
3*vong1+770*vong2= 397897-197122*vong3= 397897-197122*2=3653
Suy ra:
Vong2= 3653/770=4
3*vong1=3643-770*vong2=563
Suy ra: vong1=563/3=187
Số dư= 563-3*187=2
Như vậy:
200.000= 3*187 +770*4+197122*2 +2
Số 2 dư ra ta dùng 2 lệnh NOP
Chương trình chính và chương trình con delay sẽ như sau:
; DUA FILE LIET KE VAO
#include p16f877a.inc
; KHAI BAO CAU HINH
__CONFIG _HS_OSC & _WDT_OFF &_LVP_OFF
; KHAI BAO BIEN O DAY
; DIA CHI BIEN O VUNG NHO BIEN BANK0
cblock 0x21
vong1, vong2, vong3
![Page 46: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/46.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
46
endc
variable giatricong=0x25
;CHUONG TRINH VI DIEU KHIEN CHAY TAI DAY
org 0x000; CHI DAN BIEN DICH
; NHAY TOI CHUONG TRINH CHINH
goto main
; chi dan bien dich
; CHUONG TRINH CHINH BAT DAU TU DAY
org 0x005
main
banksel trisb
clrf trisb
banksel PORTB
START:
BSF PORTB,0
CALL DELAY
BCF PORTB,0
CALL DELAY
GOTO START
DELAY:
nop
nop
movlw d'187
movwf vong1
movlw d'4
movwf vong2
movlw d'2
movwf vong3
decfsz vong1,f
![Page 47: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/47.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
47
goto $-1
decfsz vong2,f
goto $-3
decfsz vong3,f
goto $-5
; THOAT KHOI CHUONG TRINH CON
return
; KET THUC MOI CHUONG TRINH
end
![Page 48: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/48.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
48
CHƯƠNG 4: CÁC PHƯƠNG PHÁP HIỂN THỊ TRONG CÁC THIẾT BỊ
DÙNG VI ĐIỀU KHIỂN
PHẦN LÝ THUYẾT:
Hiện nay, trong hầu hết các thiết bị nhúng đều có sử dụng các khối hiển thị. Mục
đích cho người dùng giám sát, cài đặt và hiển thị các thông số của thiết bị cũng
như đối tượng cần giám sát điều khiển.
Có rất nhiều phương pháp hiển thị, có thể kể ra như sau:
- Hiển thị cảnh báo, báo lỗi: thông thường dùng led đơn. Có thể hiển thị theo
kiểu dùng nhiều màu khác nhau hoặc bật tắt v.v
- Hiển thị số liệu: dùng led 7 đoạn, LCD hoặc LCD đồ họa v.v
- Hiển thị trên máy tính: dùng các phần mềm điều khiển giám sát, kết nối
thiết bị và máy tính thông qua chuẩn RS232 hoặc các chuẩn mạng (giám sát
từ xa)
Trong bài này sẽ giới thiệu 2 cách hiển thị đầu, phần hiển thị bằng máy tính sẽ
được đề cập trong bài học về chuẩn giao tiếp RS232.
4.1 Hiển thị bằng led đơn:
Đây là cách hiển thị đơn giản nhất.
Thông thường cách hiển thị này dùng để báo một trạng thái nào đấy của thiết bị
như trạng thái làm việc của nguồn (lỗi hoặc không lỗi), cũng như các khối chức
năng khác.
Có rất nhiều loại led đơn dùng để hiển thị. Phương pháp đơn giản như sau:
![Page 49: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/49.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
49
Hình 2.1: Hiển thị led đơn
Các led này sáng khi được cấp áp cỡ 2 V, dòng 10-20mA.
Nếu dùng một chân ra từ vi điều khiển để bật tắt led, phải dùng thêm điện trở hạn
dòng,hạn áp.
Tính toán như sau:
Muốn bật đèn, ta cho chân ra vi điều khiển lên mức cao nối với đầu vào của mạch
trên. Như ta biết, chân ra vi điều khiển ở mức logic cao có điện áp 5V.
Cho điện áp rơi trên led là 2V, dòng qua là 15mA.
Suy ra, điện áp rơi trên trở là 3V. Dòng qua led chính là dòng qua điện trở và bằng
15mA.
Suy ra, điện trở dùng: R=3/15mA=200 ohm.
Chọn điện trở tiêu chuẩn 220 ohm
(Điện trở tiêu chuẩn: 10, 11, 12, 13, 15, 16, 18, 20, 22, 24, 27, 30, 33, 36, 39, 43,
47, 51, 56, 62, 68, 72, 82, 91 và các bội số)
4.2 Hiển thị bằng led bảy đoạn- 7 segment led:
4.2.1 Cấu tạo của led 7 đoạn:
Hình 2.2: Led 7 đoạn
Một led 7 đoạn thực ra là gồm 7 led đơn nối với nhau (8 led đơn nêu có thêm dấu
chấm-dp).
Có 2 loại:
![Page 50: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/50.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
50
- Chung catod: các đầu catod (cực âm) được nối chung với nhau và nối với
đất, các đầu anod a,b,c,d,e,f,g,h được đưa ra ngoài (các chân) nhận tín hiệu
điều khiển. Khi cấp điện áp 5v cho mỗi đầu anod, led tương ứng với đầu đó
sẽ sáng
- Chung anod: các đầu anod (cực âm) được nối chung với nhau và nối với
nguồn, các đầu catod a,b,c,d,e,f,g,h được đưa ra ngoài (các chân) nhận tín
hiệu điều khiển. Muốn led đơn nào sáng chỉ việc đưa chân catod của led
tương ứng xuống mức 0V.
Hình 2.3: Cấu tạo của 2 loại led 7 đoạn
4.2.2 Hiển thị 1 led 7 đoạn dùng vi điều khiển:
![Page 51: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/51.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
51
Như đã giới thiệu ở phần trên, thực chất led 7 đoạn gồm 8 hoặc 7 led đơn nối với
nhau. Vì vậy để điều khiển thanh led đơn sáng, cách thực hiện phần cứng như hình
2.1.
Cụ thể hơn, như dùng led chung anod như hình vẽ trên. Mỗi đầu vào a,b,c,d,e,f,g,h
được nối với một chân của vi điều khiển, tương ứng là RB0, RB1, ..RB7, thông
qua các điện trở phân áp 200 ohm, đầu anod chung được nối với nguồn. Để led
đơn sáng đơn giản ta đưa chân vi điều khiển nối với led đó xuống mức thấp.
Như trên hình 2.4 trên, để led 7 đoạn hiển thị số 2 thì các led a,b,d,e,g sáng; các
led c, f tắt. Giá trị sáng tương ứng chân vi điều khiển nối vào ở mức 0, giá trị tắt
tương ứng với chân vi điều khiển nối với ở mức 1.
Do đó nội dung của thanh ghi PORTB là:
0 0 1 0 0 1 0 0
Đây là mã led 7 đoạn của số 2
Như vậy, chúng ta lưu ý một điều rằng, dữ liệu xuất ra led 7 đoạn là mã led
tương ứng với số cần xuất
Mã led tương ứng với các số từ 0 đến 9 là:
0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90
Cách điều khiển led 7 đoạn chung catod thì ngược lại.
![Page 52: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/52.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
52
4.2.2.1 Chương trình điều khiển hiển thị 1 led 7 đoạn:
Xem theo file đính kèm
4.2.3 Hiển thị nhiều led 7 đoạn dùng vi điều khiển:
Trong thực tế, ta phải dùng nhiều led 7 đoạn để hiển thị.
Vậy giải quyết việc hiển thị nhiều led như thế nào?
Ví dụ: để hiển thị số 35 bằng 2 led 7 đoạn.
Đối chiếu với cách hiển thị 1 led 7 đoạn, ta nghĩ đơn giản chỉ là dùng 1 cổng hiển
thị số 3, 1 cổng khác hiển thị số 5.
Như vậy ta mất 2 cổng. Hiển thị 4 led thì mất 4 cổng => toàn bộ chân trên vi điều
khiển dùng cho việc hiển thị led…Không còn chân để giao tiếp với các thiết bị
khác như bàn phím, đầu vào số khác v.v Không khả thi!
Ta có phương pháp tiết kiệm chân hơn để giải quyết:
Hình 2.5: Hiển thị 2 led 7 đoạn
Các chân dữ liệu (chân sẽ nhận mã led từ vi điều khiển) được nối tương ứng với
nhau và nối vào 1 cổng của vi điều khiển, chẳng hạn như cổng B
Chân nguồn của 2 led được điều khiển bởi 2 chân trên vi điều khiển, chằng hạn
chân RA4 và RA5 như trên hình, thông qua cực B của 2 transistor pnp.
![Page 53: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/53.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
53
Quá trình hiển thị con số 35 trên 2 led sẽ như sau:
- Cho chân RA4 (chân nối với led hàng chục) xuống mức thấp, transistor thứ
nhất mở do tiếp giáp BE thuận, chân RA5 lên mức cao (chân nối với led
hàng đơn vị), transistor thứ hai không mở. Vậy chỉ có led hàng chục được
cấp nguồn.
- Cho cổng B xuất dữ liệu mã led của số 3. Chỉ có led hàng chục được cấp
nguồn nên chỉ có led này sáng
- Tạo thời gian trễ 10-20ms
- Điều khiển tương tự cho led hàng đơn vị được cấp nguồn, led hàng chục
không cấp nguồn, xuất dữ liệu mã led số 5 ra cổng B. Led đơn vị hiển thị số
5.
- Tạo thời gian trễ 10-20ms
- Quay lại bước thứ nhất
Như vậy, số 3 hiển thị 10ms, số 5 hiển thị 10ms và quay vòng như vậy. Thời gian
này rất nhanh, do hiệu ứng của mắt, ta cảm giác như số 35 hiển thị cùng lúc. Bài
toán được giải quyết, ta chỉ mất có 10 chân để điều khiển 2 led.
Cách hiển thị nhiều led cũng tương tự như vậy.
Cũng giải thích thêm lí do dùng transistor nối vào RA4, RA5. Do chân vi điều
khiển có dòng khoảng vài chục mA, đây là chân cấp nguồn cho led 7 đoạn, mỗi
led đơn trong Led 7 đoạn mất 20mA vây cả led 7 đoạn mất trên 100mA. Vì vậy ta
phải dùng transistor để khuếch đại dòng.
4.2.3.1 Chương trình điều khiển hiển thị 2 led 7 đoạn:
Xem theo file đính kèm
4.3 Hiển thị dùng LCD:
![Page 54: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/54.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
54
D7
14D
613
D5
12D
411
D3
10D
29
D1
8D
07
E6
RW5
RS
4
VSS
1
VD
D2
VEE
3
LM016L
Hình 2.6: Các chân LCD
4.3.1 Các chân cơ bản của LCD 2 dòng 16 kí tự:
• VSS: Chân đất
• VCC: Chân nguồn
• VEE: Chân hiệu chỉnh độ sáng của LCD
• RS:
– =0: LCD sẽ nhận lệnh từ vi điều khiển
– =1: LCD sẽ nhận kí tự từ vi điều khiển để hiển thị
• R/W:
– =1: Vi điều khiển đọc dữ liệu từ LCD
– =0: Vi điều khiển ghi dữ liệu lên LCD
Thông thường Vi điều khiển chủ yếu ghi dữ liệu lên LCD nên chân này thường
nối đất
![Page 55: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/55.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
55
• E: Chân cho phép (Enable). Sau khi lệnh hoặc dữ liệu đã được chuẩn bị trên
đường dữ liệu, tạo xung mức cao-mức thấp sẽ bắt đầu quá trình LCD nhận
dữ liệu hoặc lệnh từ vi điều khiển.
• D0-D7: các chân dữ liệu, nơi vi điều khiển truyền lệnh hoặc dữ liệu lên
LCD.
4.3.2 Khởi tạo LCD:
LCD có nhiều độ làm việc, có thể kể ra như sau:
- Chế độ 1 dòng hay 2 dòng
- Chế độ giao tiếp 4 bit hay 8 bit
- Chế độ font 5*8 hoặc 5*10
- Ngoài ra còn có thể thay đổi vị trí hiển thị kí tự v.v
Vì vậy, trước khi bắt đầu quá trình hiển thị một chuỗi kí tự nào đó, ta cần quá trình
khởi tạo để
cài đặt các chế độ này. Vi điều khiển thực hiện quá trình khởi tạo này bắt cách ghi
đến LCD một chuỗi các lệnh.
Căn cứ vào chức năng của các chân vi điều khiển được giới thiệu ở trên, ta đưa ra
qui trình của việc gửi một lệnh từ Vi điều khiển đến LCD:
- Cho chân R/W=0 để xác định đây là ghi xuống LCD (thông thường chân
này được nối đất, nên mặc định chân này ở mức 0, ta không cần quan tâm
đến nữa)
- Cho chân RS=0 để xác định đây là lệnh mà vi điều khiển gửi xuống LCD
(phân biệt với RS=1, gửi kí tự hiển thị)
- Gửi mã lệnh xuống LCD theo các đường dữ liệu (RD0-RD7 nếu dùng chế
độ 8 bit, R4-R7 nếu dùng chế độ 4 bit)
- Đưa chân E (chân cho phép- Enable) lên mức cao, mức 1
- Tạo trễ vài chu kì lệnh
- Đưa chân E xuống mức thấp, mức 0
![Page 56: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/56.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
56
Mã lệnh như đã giới thiệu trong phần trên tùy thuộc vào từng lệnh, ở đấy giới
thiệu một số lệnh cơ bản như sau:
. Lệnh cài đặt chế độ làm việc:
0 0 1 DL N F - -
• DL:
– = 1: 8 bit
– = 0: 4 bit
• N:
– = 1: 2 dòng
– = 0 1 dòng
• F:
– = 1: font 5x10 dot
– = 0: font 5x8 dot
. Lệnh đặt chế độ tăng giảm địa chỉ:
0 0 0 0 0 1 I/D S
• I/D:
– = 1 tăng địa chỉ
– = 0 giảm địa chỉ
• S:
– =1: Cài đặt di chuyển cùng địa chỉ
. Lệnh đặt chế độ hiển thị:
0 0 0 0 1 D C B
![Page 57: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/57.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
57
• D: Cho phép hiển thị
• C: cài đặt hiển thị con trỏ
• B: nhấp nháy vị trí kí tự
. Lệnh đặt vị trí hiển thị của kí tự:
1 ĐC ĐC ĐC ĐC ĐC ĐC ĐC
• Địa chỉ dòng 1: 00- 0F
• Địa chỉ dòng 2: 40-4F
Vì vậy, muốn hiển thị đầu dòng thứ nhất, mã lệnh sẽ là 0x80
muốn hiển thị đầu dòng thứ hai, mã lệnh sẽ là 0xC0
. Lệnh xóa màn hình: mã lệnh 0x01
. Lệnh trở về đầu dòng thứ nhất: mã lệnh 0x02
Chi tiết có thề xem datasheet đi kèm
4.3.2 Ghi kí tự lên LCD để hiển thị:
Sau khi thực hiện quá trình khởi tạo để gửi các lệnh cài đặt chế độ làm việc cùa
LCD, kí tự sẽ được hiển thị lên LCD bất kì khi nào vi điều khiển muốn gửi.
Quá trình gửi kí tự gồm các bước sau:
- Cho chân R/W=0 để xác định đây là ghi xuống LCD (thông thường chân
này được nối đất, nên mặc định chân này ở mức 0, ta không cần quan tâm
đến nữa)
- Cho chân RS=1 để xác định đây là kí tự mà vi điều khiển gửi xuống LCD
(phân biệt với RS=0, gửi lệnh)
- Gửi mã ascii của kí tự cần hiển thị xuống LCD theo các đường dữ liệu
(RD0-RD7 nếu dùng chế độ 8 bit, R4-R7 nếu dùng chế độ 4 bit)
- Đưa chân E (chân cho phép- Enable) lên mức cao, mức 1
![Page 58: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/58.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
58
- Tạo trễ vài chu kì lệnh
- Đưa chân E xuống mức thấp, mức 0
Câu hỏi 1: Lý do vì sao chúng ta lại dùng transistor trong cách nối vi điều
khiển với 2 led 7 đoạn và cách tính toán mạch như thế nào?:
Nếu chúng ta không dùng transistor. Sơ đồ như sau:
Trong đó các chân a,b,c,d,e,f,g,h của cả 2 led 7 đoạn sẽ nối tương ứng với nhau
cùng nối vào rb0,rb1..rb7 thông qua điện trở 220 ohm
Như ta đã biết, 1 led 7 đoạn gồm 8 led đơn bên trong. Với loại led chung anod
như trên ta vẽ lại sơ đồ điện của từng led 7 đoạn như sau:
![Page 59: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/59.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
59
Như vậy dòng đi từ chân RA0 lớn nhất khi tấc cả các led đơn a,b,...g,h của led
7 đoạn đều sáng.
Khi đó các chân a,b,c...g,h nối đất
![Page 60: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/60.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
60
Ta tính dòng chảy ra từ RA0:
Giả sử điện áp đổ trên mỗi led là 1.5 V. RA0 mức 1 tương ứng 5 V
Suy ra áp đổ trên R1 là : U(R1)=5-1.5=3.5V
Suy ra dòng chảy qua R1 và led là: I (led)= U(R1)/R1=3.5/220=15.9mA
Dòng chảy ra từ chân RA0:
I(RA0)= 8 I(led)=8*15.9= 127mA
Điều này không thể được vì mỗi chân của vi điều khiển pic16f877a cho dòng
ra tối đa cho phép là vài chục mA.
Để giải quyết vấn đề này, ta phải dùng transistor để khuếch đại, khi đó tín hiệu
ra từ RA0 chỉ là tín hiệu điều khiển. Dòng đổ đến led sẽ do nguồn trực tiếp
cấp
Sơ đồ như sau:
Sơ đồ chi tiết sẽ như sau:
![Page 61: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/61.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
61
Như vậy khi RA0 ở mức 0 (0V) transistor dẫn. Dòng đổ từ nguồn 5 V đi vào
led qua trở xuống chân a,b,c,...e,f,g của led 7 đoạn (các chân này nối với
RB0,RB1..RB6,RB7)
Muốn led tương ứng sáng ta cho các chân a,b...g,h (tức là
RB0,RB1,...RB6,RB7) xuống mức 0.
Khi RA0 ở mức 1 (5V), transistor không dẫn, không có dòng đổ vào các led
nên các led không sáng (dù cho các chân a,b,...g,h) có ở mức 0 hay không
Như vậy sơ đồ mạch như hình trên là thõa mãn.
Vấn đề là đi tìm giá trị của các điện trở ở ngõ ra của từng led nối với các chân
a,b,c...
Và điện trở phân cực tại RA0- nối với cực B của transistor.
Để các led trong led 7 đoạn sáng đều nhau rõ ràng các điện trở ở ngõ ra của
từng led phải bằng nhau. R1=R2=...=R7
![Page 62: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/62.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
62
Ta cũng biết là mỗi led có sáng hay không phụ thuộc vào dữ liệu ở các chân
a,b,..g,h tức là các chân RB0,RB1,...RB6,RB7 ở mức 0 hay mức 1 (5V)
Trong các ứng dụng của ta dùng led 7 đoạn hiển thị các con số từ 0-9. như vậy
số lượng led sáng là thây đổi
Trường hợp có số lượng led sáng ít nhất là khi hiển thị số 1, có 2 led đơn sáng
Trường hợp số lượng led sáng nhiều nhất khi hiển thị số 8. (số 8 có dấu chấm)
Ta cũng muốn rằng dù là hiển thị bất cứ số nào trong các số từ 0-9 thì đòi hỏi
led phải sáng đều và mức sáng là như nhau khi thể hiện các số khác nhau
Suy ra là điện áp ở cực C của transistor là không đổi đối với tải khác nhau (do
mỗi led sáng thì số lượng led là khác nhau)
Trường hợp này chỉ xẩy ra khi transistor nếu như mở thì phải mở bão hòa trong
tấc cả các trường hợp hiển thị các số khác nhau.
Khi đó Vc=Ve-0.1=5V-0.1V=4.9V.
Từ đó ta tính ra điện trở của R1=R2=..=R7 theo cách tính của led đơn đã đề
cập trong chương 4 (phần 4.1)
Ở đây ta chọn: R1=R2=..=R7=220 ohm
Giả sử điện áp đổ trên mỗi led là 1.5V. Ta tính được dòng điện đổ qua mỗi led
khi sáng:
Iled=(4.9V-1.5)/220= 15.5 mA
Như đã đề cập như trên, ta phải chọn R10- điện áp phân cực cho cực B của
transistor để transistor mở bão hòa khi led 7 đoạn hiển thị bất kì số gì từ 0-9.
Trường hợp số led sáng ít nhất khi led 7 đoạn hiển thị số 1, tức led b và c sáng,
tấc cả các led khác tắt, ta có hình sau:
![Page 63: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/63.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
63
Trường hợp số led sáng nhiều nhất khi led 7 đoạn hiển thị số 8 có chấm, tức tấc
cả các led đều sáng, ta có hình sau:
Khi đó: Ic = 8 Iled = 8*15.4 = 123 mA
Vì transistor bão hòa nên: Ic=Icbaohoa=123mA
Điều kiện để transistor bão hòa là:
![Page 64: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/64.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
64
Ib>Icbaohoa/k
K là hệ số khuếch đại 1 chiều. Giả sử ở đây: K=100
Suy ra: Ib > Icbaohoa/100
=> Vb/R10 > Icbaohoa/100
Vb=Ve-Veb=5-0.7=4.3 V
Suy ra:
4.3/R10>123/100
Suy ra: R10<(100*4.3)/123= 3.5K
Rõ ràng là với giá trị này thì transistor luôn mở bão hòa cho tấc cả các trường
hợp hiển thị số từ 0-9 của led 7 đoạn
R10 < 3.5K
Câu hỏi 2: Thuật toán của chương trình hiển thị 1 led 7 đoạn (xem lại
chương trình đã được up lên)?
Start
Chọn bank nhớ chứa TRISB Xóa thanh ghi TRISB
(Cho phép các chân PORTB đầu ra)
Chọn bank nhớ 0 chứa PORTB
Gán: W=9
Gọi chương trình con Bảng mã led 7 đoạn
Gọi chương trình con Delay 200ms
Gán: W=0
Gọi chương trình con Bảng mã led 7 đoạn
Gọi chương trình con Delay 200ms
![Page 65: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/65.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
65
Trong chương trình này phần quan trọng là gọi chương trình con bảng mã
led 7 đoạn.Ta cùng khảo sát chương trình này.
Với sơ đồ kết nối 8 chân a,b,c..g,h của led 7 đoạn với 8 chân
RB0,RB1,..RB6,RB7 của vi điều khiển, đầu anod chung của led 7 đoạn nối với
nguồn . Quay lại sơ đồ chân của led 7 đoạn để dễ hình dung:
Ta biết rằng: Muốn hiển thị số 1 ta phải đưa ra PORTB mã led 7 đoạn của số
1 (chỉ có b và c sáng, còn lại tắt):
RB7 RB6 RB5 RB4 RB3 RB2 RB1 RB0
1 1 1 1 1 0 0 1
(nhớ rằng: để led sáng thì chân RB phải ở mức 0)
Vậy mã led 7 đoạn của số 1 là: 0xF9
Như vậy để led 7 đoạn hiển thị số 1, đơn giản ta dùng lệnh:
MOVLW 0xF9
MOVWF PORTB
Đối với 10 số từ 0-9 sẽ có 10 mã led tương ứng:
0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90
Vấn đề ở đây là: trong các ứng dụng thông thường con số cần hiển thị là ngẫu
nhiên do một quá trình mang đến
Ví dụ: ta dùng led 7 đoạn để hiển thị số lần tác động của phím bấm.
![Page 66: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/66.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
66
Nếu phím chưa bấm thì led hiển thị số 0
Nếu bấm 3 lần thì hiển thị số 3
Vậy con số này là không xác định
Như vậy chương trình sẽ không thể biết là 1 hay 9 lần để mà hiển thị xuất mã led
0xf9 hay 0x90 ra cổng PORTB để hiển thị
Ta phải giải quyết vấn đề này 1 cách tự động. Sử dụng kĩ thuật bảng:
Trước khi đi vào ta giới thiệu lại lệnh RETLW:
Chương trình con có 2 loại:
- loại không trả về giá trị - kết thúc bằng RETURN
- Loại trả về giá trị- kết thúc bằng RETLW (trả giá trị về cho thanh ghi W)
(Cũng giống như Procedure và Function trong ngôn ngữ C)
Cách gọi chương trình như sau:
MOVLW D’số
CALL BANGMA
MOVWF PORTB
Ta xem chương trình con bảng mã led 7 đoạn như sau:
BANGMA
ADDWF PCL,F
RETLW 0XC0
RETLW 0XF9
RETLW 0XA4
RETLW 0XB0
RETLW 0X99
RETLW 0X92
RETLW 0X82
RETLW 0XF8
RETLW 0X80
RETLW 0X90
Giả sử ta cần hiển thị số 1:
![Page 67: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/67.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
67
MOVLW D’1
CALL BANGMA
MOVWF PORTB
Ta xem chương trình chạy như thế nào:
Sau lệnh: MOVLW D’1: W=1
Sau lệnh: CALL BANGMA: nhảy đến chương trình con BANGMA
Sau lệnh: ADDWF PCL,F : PCL=PCL +W=PCL+1
Và sau 1 lệnh thì PCL=PCL+1 nên sau lệnh ADDWF PCL,F ta có: PCL=PCL+2
Do chương trình vi điều khiển chạy theo PC tức PCL nên con trỏ chương trình
nhảy đến địa chỉ lệnh RETLW 0XC0 (2 lệnh kể từ lệnh ADDWF PCL,F)
Khi đó W=0xC0 là mã led 7 đoạn của số 1
Vậy nếu đưa W=1 và gọi chương trình con BANGMA ta có W=mã led 7 đoạn của
số 1
Tương tự như vậy đối với W=0-9
Rõ ràng với cách dùng KĨ THUẬT BẢNG cho phép ta tự động lấy giá trị của mã
led
Ví dụ: giả sử muốn hiển thị số lần phím được bấm ta dùng biến solanbam, giả sử
solanbam đã được xử lý bằng đoạn chương trình xử lý phím bấm phía trước.
Để hiển thị ta dùng đoạn chương trình sau:
MOVF solanbam,f
CALL BANGMA
MOVWF PORTB
Rõ ràng với đoạn chương trình trên thì không cần biết solanbam là bao nhiêu
chương trình cũng tự động cập nhật được
Câu hỏi 3: Hạn chế của chương trình con bảng mã led 7 đoạn:
Ta xem lại chương trình đã được listting với cột đầu tiên là địa chỉ của lệnh trong
bộ nhớ chương trình với thay đổi nhỏ là bangma được bắt đầu ở địa chỉ 0x100:
![Page 68: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/68.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
68
--- E:\ADP\TEACHER\VIDIEUKHIEN\BAITAP\LED7DOAN0\test.asm --------
-------------------------------
1: ; DUA FILE LIET KE VAO
2: #include p16f877a.inc
3: ; KHAI BAO CAU HINH
4: __CONFIG _HS_OSC & _WDT_OFF
&_LVP_OFF
5: ; KHAI BAO BIEN O DAY
6: ; DIA CHI BIEN O VUNG NHO BIEN
BANK0
7: cblock 0x20
8: vong1,
9: vong2,
10: vong3
11: endc
12:
13: ;
14: ;CHUONG TRINH VI DIEU KHIEN CHAY
TAI DAY
15: org 0x000; CHI DAN BIEN DICH
16: ; NHAY TOI CHUONG TRINH CHINH
0000 2805 GOTO 0x5 17: goto main
18:
19: ; chi dan bien dich
20: ; CHUONG TRINH CHINH BAT DAU TU
DAY
21: org 0x005
22:
23: main
![Page 69: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/69.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
69
24: ; CHON BANK NHO CHUA THANH GHI
TRISB
0005 1683 BSF 0x3, 0x5 25: banksel trisb
0006 1303 BCF 0x3, 0x6
26: ; XOA THANH GHI TRISB CHO PHEP
CAC CHAN PORTB LA DAU RA
0007 0186 CLRF 0x6 27: clrf trisb
28: ; CHON BANK NHO CO THANH GHI
PORTB
0008 1283 BCF 0x3, 0x5 29: banksel PORTB
0009 1303 BCF 0x3, 0x6
30:
31: ; BAT DAU XU LY
32: start:
33: LOOP:
000A 3009 MOVLW 0x9 34: movlw d'9
000B 2100 CALL 0x100 35: CALL BANGMA
36:
000C 0086 MOVWF 0x6 37: MOVWF PORTB
38:
000D 2015 CALL 0x15 39: CALL DELAY
40:
000E 3000 MOVLW 0 41: movlw d'0
000F 2100 CALL 0x100 42: CALL BANGMA
43:
0010 0086 MOVWF 0x6 44: MOVWF PORTB
0011 3000 MOVLW 0 45: movlw high (DELAY)
0012 008A MOVWF 0xa 46: movwf pclath
0013 2015 CALL 0x15 47: CALL DELAY
![Page 70: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/70.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
70
0014 280A GOTO 0xa 48: GOTO LOOP
49: ; DUA DU LIEU GIATRICONG VAO
THANH GHI TRUNG GIAN W
50:
51:
52: ; CHUONG TRINH CON DELAY 200MS
53: DELAY:
54:
0015 0000 NOP 55: nop
0016 0000 NOP 56: nop
0017 30B9 MOVLW 0xb9 57: movlw d'185
0018 00A0 MOVWF 0x20 58: movwf vong1
0019 3004 MOVLW 0x4 59: movlw d'4
001A 00A1 MOVWF 0x21 60: movwf vong2
001B 3002 MOVLW 0x2 61: movlw d'2
001C 00A2 MOVWF 0x22 62: movwf vong3
001D 0BA0 DECFSZ 0x20, F 63: decfsz vong1,f
001E 281D GOTO 0x1d 64: goto $-1
001F 0BA1 DECFSZ 0x21, F 65: decfsz vong2,f
0020 281D GOTO 0x1d 66: goto $-3
0021 0BA2 DECFSZ 0x22, F 67: decfsz vong3,f
0022 281D GOTO 0x1d 68: goto $-5
0023 0008 RETURN 69: return
70:
71:
72: ORG 0X100
73: BANGMA
0100 0782 ADDWF 0x2, F 74: ADDWF PCL,F
0101 34C0 RETLW 0xc0 75: RETLW 0XC0
![Page 71: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/71.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
71
0102 34F9 RETLW 0xf9 76: RETLW 0XF9
0103 34A4 RETLW 0xa4 77: RETLW 0XA4
0104 34B0 RETLW 0xb0 78: RETLW 0XB0
0105 3499 RETLW 0x99 79: RETLW 0X99
0106 3492 RETLW 0x92 80: RETLW 0X92
0107 3482 RETLW 0x82 81: RETLW 0X82
0108 34F8 RETLW 0xf8 82: RETLW 0XF8
0109 3480 RETLW 0x80 83: RETLW 0X80
010A 3490 RETLW 0x90 84: RETLW 0X90
85:
2007 3490 RETLW 0x90 86: end
Trước khi đi vào phân tích các khả năng có thể xảy ra, ta xem kĩ lại nguyên lý làm
việc của thanh ghi PC.
Thanh ghi PC có độ dài 13 bit chia làm 2 thanh ghi PCL (tức 8 bit thấp của thanh
ghi PC PC<7:0>) và PCH (5 bit cao của thanh ghi PC- PC<12-8>):
PCH PCL
Bit
12
Bit
11
Bit
10
Bit
9
Bit
8
Bit
7
Bit
6
Bit
5
Bit
4
Bit
3
Bit
2
Bit
1
Bit
0
Trong đó thanh ghi PCL có thể đọc /ghi được
Thanh ghi PCH không thể đọc ghi được. Tuy nhiên có thể ghi được thông qua
thanh ghi PCLATH.
Nghĩa là 5 bit cao của thanh ghi PC, PC<12:8> có thể cập nhật được bằng cách
viết vào 5 bit thấp của thanh ghi PCLATH.
Khi chương trình được reset lại từ đầu, các bit PC<12:8> được xóa về 0
Có điều chú ý là:
Nếu có lệnh can thiệp vào thanh ghi PCL thì lập tức các bit cao của thanh ghi PC
sẽ được cập nhật từ thanh ghi PCLATH
![Page 72: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/72.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
72
Như vậy, các bit cao của thanh ghi PC được cập nhật khi xẩy ra 1 trong 2
điều kiện:
- Cập nhật PCL
- Cập nhật PCLATH
Ví dụ 1: khi thực hiện lệnh CALL nhãn
Nếu sau nhãn là lệnh ở địa chỉ k (0<k<2047)
Lệnh trên được phân giải dưới dạng CALL k
Khi thực hiện lệnh này:
11 bit cao của PC được cập nhật: PC<10:0> =k
Lập tức 2 bit còn lại của PC cũng được cập nhật qua PCLATH:
PC<12:11>=PCLATH<4:3>
Ví dụ 2: khi thực hiện lệnh ADDWF PCL,F
Lập tức PCL=PCL+W
Do PCL được cập nhật nên các bit cao của PC cũng được cập nhật qua PCLATH:
PCH=PC<12:8>=PCLATH<4:0>
Từ đây trở lại chương trình hiển thị led 7 đoạn sử dụng kĩ thuật bảng.
Để ý đoạn chương trình con BANGMA được khai báo sử dụng bộ nhớ từ địa chỉ
0x100 (ORG 0x100). Xét lệnh ở dòng 34 và 35:
000A 3009 MOVLW 0x9 34: movlw d'9
000B 2100 CALL 0x100 35: CALL BANGMA
Khi thực hiện lệnh ở dòng 34, W=9
Trước thực hiện lệnh ở dòng 35, PCL=PC<7:0>=0x0B, PCLATH=0x00,
PCH=PC<12:8>=00000
Nhãn BANGMA ở địa chỉ 0x100
Như vậy lệnh trên phân giải như sau: CALL 0x100
Khi thực hiện lệnh này, 11 bit thấp của PC, PC<10:0>=0x100= 01 0000 0000
2 bit cao của PC, PC<12:11>=PCLATH<43>=00
Vậy PC= 0001 0000 0000=0x100
Và chương trình nhảy đến thực hiện lệnh ở địa chỉ 0x100
![Page 73: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/73.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
73
0100 0782 ADDWF 0x2, F 74: ADDWF PCL,F
Khi thực hiện lệnh này PCL=PCL+W=0x00 +0x09=0x09=1001
Do PCL được cập nhật nên PCH cũng được cập nhật qua PCLATH
PCH=PC<12:8>=PCLATH<4:0>=00000
Vậy PC=0000 0000 1001=0x09
Sau khi thực hiện 1 lệnh PC=PC+1=0x0A
Chương trình nhảy đến thực hiện lệnh có địa chỉ 0x0A:
000A 3009 MOVLW 0x9 34: movlw d'9
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Như vậy là không đúng ý đồ của ta, chương trình chạy sai!!!!!!!!!!!!!!
Nhìn lại quá trình phân tích như trên, ta thấy mấu chốt của vấn đề là:
Chương trình con bảng mã bắt đầu từ địa chỉ >0xff
Do đó lệnh CALL làm PC thây đổi nội dung các bit cao
Khi gặp lệnh tác động đến thanh ghi PCL: ADDWF PCL,F
Thì đồng thời PCH cũng được cập nhật qua PCLATH
Tuy nhiên PCLATH lại không được cập nhật trước đó như vậy địa chỉ PC bây giờ
<0xff
Để giải quyết vấn đề trên ta thấy chỉ cần cập nhật PCLATH bằng các bit cao của
địa chỉ của lệnh đầu tiên trong chương trình con bảng mã
; DUA FILE LIET KE VAO
#include p16f877a.inc
; KHAI BAO CAU HINH
__CONFIG _HS_OSC & _WDT_OFF &_LVP_OFF
; KHAI BAO BIEN O DAY
; DIA CHI BIEN O VUNG NHO BIEN BANK0
cblock 0x20
vong1,
vong2,
vong3
![Page 74: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/74.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
74
endc
;
;CHUONG TRINH VI DIEU KHIEN CHAY TAI DAY
org 0x000; CHI DAN BIEN DICH
; NHAY TOI CHUONG TRINH CHINH
goto main
; chi dan bien dich
; CHUONG TRINH CHINH BAT DAU TU DAY
org 0x005
main
; CHON BANK NHO CHUA THANH GHI TRISB
banksel trisb
; XOA THANH GHI TRISB CHO PHEP CAC CHAN PORTB LA DAU RA
clrf trisb
; CHON BANK NHO CO THANH GHI PORTB
banksel PORTB
; BAT DAU XU LY
start:
LOOP:
movlw high (BANGMA)
movwf pclath
movlw d'9
CALL BANGMA
MOVWF PORTB
movlw high (DELAY)
![Page 75: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/75.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
75
movwf pclath
CALL DELAY
movlw high (BANGMA)
movwf pclath
movlw d'0
CALL BANGMA
MOVWF PORTB
movlw high (DELAY)
movwf pclath
CALL DELAY
GOTO LOOP
; DUA DU LIEU GIATRICONG VAO THANH GHI TRUNG GIAN W
; CHUONG TRINH CON DELAY 200MS
DELAY:
nop
nop
movlw d'185
movwf vong1
movlw d'4
movwf vong2
movlw d'2
movwf vong3
decfsz vong1,f
goto $-1
decfsz vong2,f
goto $-3
decfsz vong3,f
![Page 76: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/76.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
76
goto $-5
return
ORG 0X100
BANGMA
ADDWF PCL,F
RETLW 0XC0
RETLW 0XF9
RETLW 0XA4
RETLW 0XB0
RETLW 0X99
RETLW 0X92
RETLW 0X82
RETLW 0XF8
RETLW 0X80
RETLW 0X90
end
Câu hỏi 4: Thuật toán chương trình chia cho 10?
Trong chương trình hiển thị số có 2 chữ số bằng led 7 đoạn ta có dùng thuật toán
chia 10 để xác định con số hàng chục và con số hàng đơn vị, sau đó hiển thị từng
con số này bởi 1 led 7 đoạn.
Thuật toán:
![Page 77: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/77.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
77
Cho con số ví dụ để hiểu thuật toán:
Số =35
Bước 1: W=số=35
Hangdonvi=W=35
Bước 2: W=10
Số = số -W=Số - 10= 25
Bước 3: Số >0, nhảy theo nhánh No
Hangchuc=hangchuc+1=1
Bước 4: hangdonvi=số=25
Bước 5: W=10
Số = số -W=Số - 10= 15
Bước 6: Số >0, nhảy theo nhánh No
Hangchuc=hangchuc+1=1+1=2
START
W=SỐ Hangdonvi=W
W=10 SỐ=SỐ-W=SỐ-10
Số<0
Hangchuc= hangchuc+1
Yes
Thoát
No
![Page 78: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/78.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
78
Bước 7: hangdonvi=số=15
Bước 8: W=10
Số = số -W=Số - 10= 15-10=5
Bước 9: Số >0, nhảy theo nhánh No
Hangchuc=hangchuc+1=2+1=3
Bước 10: hangdonvi=số=5
Bước 11: W=10
Số = số -W=Số - 10= 5-10=-
Bước 12: Số <, nhảy theo nhánh Yes thoát
Rõ ràng lúc này hangchuc=3 và hangdonvi=5 đúng yêu cầu
Trong thuật toán trên chỉ có một khúc mắc nho nhỏ là làm sao để kiểm tra số<0
hay chưa
Ta sử dụng cờ nhớ C của thanh ghi status
C phản ánh trạng thái dương hay âm của phép toán
Nếu kết quả phép toán là dương (>=0) thì cờ C=1
Nếu kết quả phép toán là âm (<0) thì cờ C=0
Do đó lệnh sử dụng ở đây là:
MOVLW D'10
subwf so,f
btfsc status,c
goto No
goto Yes
CHƯƠNG 5: BỘ ĐỊNH THỜI - TIMER
5.1 Giới thiệu:
Định thời là tạo một khoảng thời gian giữa 2 sự kiện
Trong các ứng dụng của vi điều khiển trong thực tế, việc định thời là việc thường
xuyên xảy ra.
Để thực hiện việc này, ta có 2 cách:
![Page 79: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/79.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
79
- Dùng các lệnh thực hiện các vòng lặp để tạo ra khoảng thời gian. Nguyên
tắc tạo ra khoảng thời gian này đơn giản như sau: nếu vi điều khiển mất
một khoảng thời gian x để thực hiện một lệnh, việc lặp lại một lệnh n lần sẽ
mất n*x thời gian.
Trong chương trình, cách này được dùng nhiều với thể hiện là các chương
trình con Delay. Giải thích và thuật toán làm việc của chương trình con
delay đã được trình bày khá kĩ ở các chương trước
- Dùng các bộ định thời Timer để tạo ra khoảng thời gian trễ.
Trong bài này, ta sẽ đi vào nguyên cứu các bộ timer.
Một chế độ quan trọng nữa của Timer là khi ta cài đặt cho nó hoạt động như bộ
đếm. Trong ứng dụng này, timer hoạt động như một bộ đếm, có nhiệm vụ đếm số
các xung đi vào một chân cụ thể trên vi điều khiển (Đối với timer0 chân đầu vào
của tín hiệu xung là RA4, với timer1 là RC0). Chế độ bộ đếm này có nhiều ứng
dụng trong thực tế như đếm số vòng quay của động cơ (phản hồi từ bộ đo tốc độ
động cơ- encoder), đếm số sản phẩm trên một dây chuyền v.v.
Vi điều khiển PIC16F877A có 3 bộ Timer:
- Timer0: 8 bit (số đếm tối đa của nó là 255), hoạt động ở 2 chế độ định thời
và bộ đếm.
- Timer1: 16 bit (số đếm tối đa của nó là 65535), hoạt động ở 2 chế độ định
thời và bộ đếm.
- Timer2: 8 bit, hoạt động phục vụ chức năng PWM (Pulse Width
Modulation- Điều chế độ rộng xung)
Trong bài này ta đi vào khảo sát 2 bộ Timer0 và Timer1, Timer2 sẽ được khảo sát
trong bài về PWM.
Để tiện cho việc khảo sát, ta đi vào nguyên lý hoạt động cơ bản của các bộ timer ở
hai chế độ: định thời và bộ đếm.
5.2 Nguyên lý hoạt động cơ bản của một bộ Timer:
5.2.1 Chế độ định thời:
![Page 80: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/80.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
80
Mỗi bộ timer có một hoặc nhiều thanh ghi chứa giá trị đếm của nó (tùy thuộc vào
độ dài của timer), ta giả sử tên thanh ghi là TMR có độ dài là n byte (n=1 với
timer0, n=2 với timer1), hay giá trị đếm tối đa là 0 (Chuyển từ 128 n về 0). Khi
giá trị của TMR đạt đến giá trị này, vi điều khiển sẽ set bit cờ của bộ timer đó lên
mức 1. Người dùng sẽ biết được thời điểm này bằng cách kiểm tra bit cờ.
Khi được cài đặt hoạt động trong chế độ định thời:
- Giá trị của thanh ghi TMR sẽ tự động tăng lên 1 đơn vị sau mỗi chu kì
lệnh của vi điều khiển: TMR=TMR+1
- Khi giá trị của TMR đạt đến giá trị tối đa (Đối với timer0: từ 255 cộng
thêm 1 chuyển về 0, đối với timer1: từ 65535 cộng 1 chuyển về 0) , bit cờ
của Timer (Đối với Timer0, tên bit cờ là TMR0IF, vị trí là bit số 2 của
thanh ghi INTCON; Đối với Timer1, tên bit cờ là TMR1IF, vị trí là bit số 0
của thanh ghi PIR1) sẽ được set lên mức 1
Giả sử vi điều khiển dùng thạch anh tần số 4MHz, như vậy:
Chu kỳ lệnh= 4 chu kì thạch anh= =1µs
Vậy TMR sẽ tự động tăng lên 1 đơn vị sau một chu kì lệnh tức sau 1µs.
Nếu ban đầu ta gán cho TMR= x. Thì sau khoảng thời gian xn 82 (µs) giá trị
TMR sẽ đạt giá trị tối đa của nó là 0 (chuyển từ 255+1->0) . Thời điểm này được
xác định thông qua trạng thái của bit cờ.
Ngược lại, ta muốn thực hiện định thời khoảng thời gian t sau một sự kiện 1 như
sau:
- Sự kiện 1
- Tạo khoảng thời gian trễ t
- Sự kiện 2
Ta làm các bước:
- Sự kiện 1
- Gán giá trị ban đầu cho TMR = tn 82
- Kiểm tra bit cờ
![Page 81: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/81.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
81
- Khi bit cờ = 1, thực hiện sự kiện 2.
Thật vậy, sau 1 (µs) TMR tăng lên 1 đơn vị
Để tăng giá trị cho TMR từ tn 82 đến giá trị 0 (chuyển từ 255 +1 về 0) (khi bit
cờ được set lên 1) mất )2(2 88 tnn = t (µs)
Vậy khoảng thời gian từ sau sự kiện 1 (khi TMR bắt đầu được gán) đến sự kiện 2
(ngay sau khi bit cờ được set) là t (µs) đúng như yêu cầu của ta.
Ví dụ: Để định thời 200 (µs) dùng Timer0 (8 bit; n=1; giá trị tối đa là 255) ta cho
TMR0= 256-200=56
Như vậy:
Sau 1 µs đầu tiên. TMR0=TMR0+1=57
Sau 2 µs . TMR0=TMR0+1=58
-------
Sau 200 µs, TMR0=TMR0+1= 255+1= 0 (TMR0 có giá trị lớn nhất là 255)
5.2.2 Chế độ bộ đếm:
Khi được cài đặt trong chế độ này, một chân chức năng trên vi điều khiển sẽ trở
thành chân đầu vào xung của bộ đếm. Ví dụ: chân RA4 đối với Timer0 và RC0
đối với Timer1. Hoạt động của nó có nét giống với chế độ định thời.
Khi được cài đặt hoạt động trong chế độ bộ đếm, Giá trị của thanh ghi TMR sẽ tự
động tăng lên 1 đơn vị khi và chỉ khi có một xung đi vào chân đầu vào xung
của timer đó. Khi giá trị của TMR0 đạt đến giá trị tối đa (chuyển về 0 do
255+1=256 ), bit cờ của Timer sẽ được set lên mức 1 và TMR bị xóa, TMR=0.
Như vậy, về cách hoạt động trong chế độ này chỉ khác với chế độ định thời ở chỗ,
thay vì TMR tự động tăng lên sau mỗi chu kì lệnh, thì TMR tăng lên khi có một
xung đi vào chân đầu vào xung của Timer đó.
Dạng xung được xác định là sườn âm (từ mức cao -> mức thấp) hay sườn dương
(mức thấp->mức cao) phụ thuộc vào việc cài đặt bit chọn dạng xung tương ứng .
Đối với Timer0: Để chọn dạng xung dung bit T0SE – bít số 4 của thanh ghi
OPTION_REG
T0IE=0: Chọn dạng xung dương (L->H)
![Page 82: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/82.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
82
T0IE=1: Chọn dạng xung âm (H->L)
Đối với Timer1: chỉ có dạng xung đầu vào là xung dương
Nguyên lý hoạt động định thời và bộ đếm này cũng đúng với các bộ vi điều khiển,
vi xử lý khác.
Ta đi vào khảo sát cụ thể 2 Timer0 và Timer1 của vi điều khiển PIC.
5.3 Timer 0:
5.3.1 Đặc điểm:
- Thanh ghi chứa giá trị đếm là TMR0 có độ dài 8 bit (số đếm tối đa là 255).
- Nội dung của thanh ghi TMR0 có thể đọc và ghi được
- Hoạt động ở 2 chế độ: bộ định thời và bộ đếm
- Trong chế độ bộ đếm: dạng xung đầu vào có thể lựa chọn (sườn dương
hoặc sườn âm)
- Có bộ chia tần số có thể thay đổi tỉ lệ chia bằng chương trình
5.3.2 Các bit điều khiển:
a. Bit chọn chế độ:
Timer0 hoạt động ở 2 chế độ: bộ định thời và bộ đếm
Để lựa chọn chế độ làm việc, ta gán giá trị cho bit T0CS (Timer0 Clock Select bit-
Bit lựa chọn nguồn xung cho Timer), vị trí của bit này là bit 5 của thanh ghi
OPTION_REG:
T0CS=1: Timer0 hoạt động ở chế độ bộ đếm (nguồn xung là xung đầu đi vào chân
RA4)
T0CS=0: Timer0 hoạt động ở chế độ định thời (nguồn xung là dao động thạch anh,
tần số=Fosc/4)
b. Bit chọn dạng xung đầu vào:
Khi hoạt động ở chế độ bộ đếm, thanh ghi TMR0=TMR0+1 mỗi lần có 1 xung đi
vào chân RA4
Vấn đề là dạng xung là xung sườn lên hay sườn xuống
Để cài đặt dạng xung ta gán giá trị cho bit T0SE (Timer0 Edge Select Bit- Bít
chọn dạng sườn xung), vị trí của bit là bít 4 của thanh ghi OPTION_REG:
![Page 83: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/83.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
83
T0SE=1: TMR0=TMR0+1 Khi có đổi trạng thái từ cao xuống thấp trên chân RA4
(sườn âm)
T0SE=0: TMR0=TMR0+1 Khi có đổi trạng thái từ thấp lên cao trên chân RA4
(sườn dương)
c. Các bít cài đặt tỉ lệ chia tần số:
Như trong phần a đã khảo sát, nguồn xung tác động có thể xung đi vào chân RA4
(trong chế độ bộ đếm)
Hoặc nguồn xung từ dao động thạch anh (đã được chia 4- trong chế độ bộ định
thời)
Trong thực tế, dao động từ nguồn xung trên phải đi qua bộ chia tần số trước khi đi
vào tác động đến
Timer0.
Bộ chia tần số này có thể cài đặt tỉ lệ chia tần số
Cụ thể ta hiểu đơn giản như sau:
Trong chế độ bộ đếm, TMR0=TMR0+1 sau mỗi xung của tín hiệu đi vào chân
RA4
Vậy tần số cập nhật của TMR0 là bằng tần số xung
Bây giờ do tín hiệu xung từ chân RA4 đi qua bộ chia tần số trước khi tác động
thay đổi TMR0
Giả sử tỉ lệ chia là 1:2 nghĩa là tần số của xung đầu ra của bộ chia bằng ½ tần số
của xung vào RA4
Mỗi xung đầu ra của bộ chia làm cho TMR0=TMR0+1
Vậy tần số xung của RA4=2 tần số cập nhật TMR0
Do đó, sau khi có 2 tác động vào RA4 thì TMR0=TMR0+1
Tương tự, đối với chế độ bộ định thời
Sau 2 chu kì lệnh, TMR0=TMR0+1
Điều đặc biệt là tỉ lệ chia này có thể thay đổi được bằng cách gán giá trị cho các
bit PS2,PS1,PS0- Vị trí các bít là bit 2,1,0 của thanh ghi OPTION_REG
Cụ thể tỉ lệ chia tương ứng với giá trị của PS2,PS1,PS0 như sau:
![Page 84: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/84.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
84
PS2 PS1
PS0
Tỉ lệ chia
000 1:2
001 1:4
010 1:8
011 1:16
100 1:32
101 1:64
110 1:128
111 1:256
d. Bít chọn bộ chia tần số cho TIMER0:
Thực ra bộ chia tần số có thể được lựa chọn làm bộ chia cho Timer0 hoặc Watch
Dog Timer
Vì vậy, để cài đặt bộ chia tần số làm việc với Timer0 ta có gán giá trị cho bít PSA
, vị trí bit là bít số 3 của thanh ghi OPTION_REG.
PSA=0, bộ chia làm việc cho TIMER0
PSA=1, bộ chia làm việc cho Watch Dog Timer
e. Bít cờ trạng thái:
Do độ dài của thanh ghi TMR0 là 8 bit, như vậy tốt đa viết được 255
Nếu như TMR0=255, nếu tiếp tục có xung vào (từ nguồn dao động thạch anh
trong chế độ định thời hoặc từ chân RA4 trong chế độ bộ đếm), TMR0 = 255+1 =
256 bị tràn và TMR0=0
Mỗi lần TMR0 bị tràn, bit cờ trạng thái sẽ tự động set lên 1 để báo trạng thái tràn
đó
Bit cờ này có tên là TMR0IF (Timer0 Interrupt Flag- Bít cờ ngắt), vị trí bit là
bít 2 của thanh ghi INTCON.
Chú ý là khi có tràn giá trị của thanh ghi TMR0, TMR0IF=1, TMR0=0
Ta phải xóa TMR0IF bằng chương trình.
![Page 85: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/85.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
85
Toàn bộ hoạt động của timer0 có thể tổng kết theo sơ đồ sau:
5.3.3 Các thanh ghi liên quan:
Thanh ghi TMR0 (Địa chỉ 01h):
Chứa giá trị đếm hiện tại của Timer 0
Thanh ghi Option_Reg (Địa chỉ 81h):
T0CS: Bit chọn chế độ
= 0: TMR0 hoạt động chế độ định thời
= 1: TMR0 hoạt động chế độ bộ đếm
T0SE: Bit chọn dạng xung cho chế độ bộ đếm
= 0: Xung sườn lên
= 1: Xung sườn xuống
PSA: Bit chọn chế độ cho bộ chia tần số PresCale là WatchDog_Timer hay Timer
0.
= 0: Bộ chia tần số dành cho Timer 0
= 1: Bộ chia tần số dành cho Watch_Dog Timer
PS2-PS0: 3 bit chọn tỉ lệ chia tần số như đã giới thiệu ở phần trên
Thanh ghi INTCON (0Bh):
![Page 86: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/86.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
86
Bit TMR0IE: Bit này bằng 1 cho phép ngắt Timer 0. Sự kiện ngắt xảy ra khi có sự
tràn TMR0 từ 255 xuống 0.
Bit TMR0IF: Bit cờ xác nhận giá trị TMR0 đã bị tràn từ 255 về 0
![Page 87: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/87.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
87
BÀI 6: NGẮT
6.1 Ngắt là gì:
Ngắt hiểu theo nghĩa đơn giản là các sự kiện ngẫu nhiên làm gián đoạn quá trình
đang xảy ra. Để có thể dễ hiểu khái niệm mới này ta cùng đưa ra một ví dụ trong
thực tế như sau:
Ví dụ: Trong giờ học trên lớp, ta đang học bài, có chuông điện thoại hoặc có bạn
gọi, ta phải dừng hoạt động học bài lại để trả lời điện thoại hoặc ra gặp bạn. Sự
kiện điện thoại reo chuông, hay bạn bè gọi được gọi là sự kiện ngắt, việc ta trả lời
điện thoại hay ra gặp bạn là chương trình phục vụ ngắt. Việc đang học bài được
xem là chương trình chính.
Ngắt được thực hiện khi và chỉ khi cho phép nó. Như trong ví dụ trên, nếu sự kiện
ngắt- điện thoại reo xảy ra, nếu giáo viên và bản thân ta cho phép mình trả lời điện
thoại khi đang học bài thì khi có điện thoại ta mới nghe.
Vi điều khiển cũng có ngắt. Cách xử lý của nó cũng tương tự như ví dụ trên.
Cụ thể hoạt động của vi điều khiển khi có sự kiện ngắt xảy ra và ngắt đó đã được
cho phép:
- Thực hiện nốt lệnh đang thực hiện
- Dừng chương trình đang thực hiện
- Lưu lại địa chỉ của lệnh kế tiếp trong chương trình đang thực hiện vào bộ
nhớ stack
- Nhảy tới địa chỉ 0x04 trong bộ nhớ chương trình
- Tại đây, vi điều khiển sẽ thực hiện chương trình con phục vụ ngắt do người
lập trình đã lập trình từ trước.
![Page 88: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/88.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
88
- Sau khi thực hiện xong chương trình con phục vụ ngắt, vi điều khiển lấy lại
địa chỉ của lệnh kế tiếp đã được lưu và thực hiện tiếp chương trình đang
thực hiện dở lúc chưa có ngắt
Như vậy, cách phản ứng của vi điều khiển là khá tương đồng với cách xử lý của
con người trong thực tế. Như trong ví dụ trên, khi ta đang học bài, khi có ngắt, tức
có điện thoại-sự kiện ngắt, ta đọc nốt từ cuối cùng, nhớ dòng đang đọc ở trang thứ
mấy, đánh dấu, trả lời điện thoại (chương trình con phục vụ ngắt), trả lời xong ta
trở lại học bài ở dòng, trang đã được đánh dấu.
Tới đây ta tổng hợp lại các thuật ngữ dùng cho xử lý ngắt trong vi điều khiển:
- Nguồn ngắt: nguồn ngắt là nguyên nhân gây ra ngắt. Như trong ví dụ trên,
nguồn ngắt có thể
Là điện thoại gọi hoặc bạn gọi
- Sự kiện ngắt: khi nguồn ngắt xảy ra
- Chương trình con phục vụ ngắt: là chương trình vi điều khiển xử lý khi
có sự kiện ngắt xảy ra do người lập trình lập trình ra
Ví dụ như ta trả lời hoặc chạy ra khỏi phòng gặp bạn
- Vecto ngắt: tức địa chỉ 0x04 nơi vi điều khiển chạy tới sau khi lưu địa chỉ
trả về
- Bit cho phép ngắt: tức việc cho phép vi điều khiển chạy chương trình con
phục vụ ngắt khi có sự kiện ngắt xảy ra. Trong vi điều khiển PIC, mỗi ngắt
có bit cho phép của nó. Bit này tận cùng bằng chữ E (enable), nằm trong
các thanh ghi chuyên dụng. Muốn cho phép ngắt đó, ta phải đưa bit cho
phép ngắt tương ứng lên giá trị 1. Ngắt chỉ thực sự được cho phép ngắt khi
ta cho bit cho phép ngắt toàn cục GIE (Global Interrupt Enable) lên mức 1.
Ta hình dùng như sau: khi có sự kiện ngắt- điện thoại gọi, nếu ta cho phép
mình nghe điên thoại (tức bit cho phép ngắt của ngắt đó được set lên 1)
![Page 89: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/89.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
89
đồng thời thầy giáo cho phép (bit cho phép ngắt toàn cục GIE được lên mức
1) thì ta mới nghe điện thoại (cho chương trình con phục vụ ngắt hoạt
động).
Một số các ngắt khác, như các ngắt ngoại vi bao gồm ADC, PWM v.v
Muốn cho phép nó còn phải đưa bit cho phép ngắt ngoại vi lên mức 1.
- Cờ ngắt: là bit phản ánh trạng thái của sự kiện ngắt. Mỗi ngắt có một bit
cờ. Khi bit cờ này bằng 1 nghĩa là sự kiện ngắt tương ứng với cờ đó xảy ra.
Ta hình dung như tiếng chuông của điện thoại là cờ ngắt, chuông rung báo
có sự kiện ngắt- có điện thoại xảy ra. Các bit này tận cùng bằng từ F (Flag-
cờ). Lưu ý là dù một ngắt có được cho phép hay không thì cờ ngắt vẫn được
set lên 1 khi có sự kiện ngắt xảy ra. (Dù ta có được phép nghe điện thoại
hay không thì chuông điện thoại vẫn cứ reo).
6.2 Các ngắt trong vi điều khiển PIC16F877A:
Vi điều khiển PIC16F877A có 15 nguồn ngắt. Được chia làm 2 lớp ngắt:
- Lớp ngắt cơ bản: bao gồm các ngắt cơ bản như ngắt tràn timer 0, ngắt
ngoài, ngắt thay đổi trạng thái của các chân PortB (RB4-RB7). Bit cho
phép ngắt và bit cờ tương ứng là TMR0IE,TMR0IF; INTE, INTF; RBIE và
RBIF. Để ý là để cho phép ngắt thực sự xảy ra phải có bit cho phép ngắt
toàn cục GIE.
- Lớp ngắt ngoại vi: bao gồm các ngắt ngoại vi như ngắt tràn timer 1
(TMR1IE, TMR1IF), ngắt tràn Timer 2(TMR2IE, TMR2IF), ngắt hoàn
thành việc chuyển đổi ADC (ADCIE, ADCIF), ngắt hoàn thành việc nhận
kí tự trong truyền thông RS232 (RCIE, RCIF), ngắt hoàn thành việc truyền
kí tự trong truyền thông RS232 (TXIE, TXIF) v.v Để ý là muốn thực sự
cho phép các ngắt này ngoài bit cho phép ngắt toàn cục được set phải set cả
bỉt cho phép ngắt ngoại vi PEIE.
![Page 90: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/90.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
90
6.3 Cấu trúc một chương trình hợp ngữ có xử lý ngắt:
6.3.1 Cấu trúc chương trình:
; DUA FILE LIET KE VAO
#INCLUDE P16F877A.INC
; KHAI BAO CAU HINH
__CONFIG _HS_OSC & _WDT_OFF &_LVP_OFF
![Page 91: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/91.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
91
; KHAI BAO BIEN O DAY
; DIA CHI BIEN O VUNG NHO BIEN BANK0
CBLOCK 0x20
ENDC
;CHUONG TRINH VI DIEU KHIEN CHAY TAI DAY
ORG 0x000; CHI DAN BIEN DICH
; NHAY TOI CHUONG TRINH CHINH
GOTO MAIN
; DIA CHI VECTOR NGAT
ORG 0x004
; THUC HIEN LENH NHAY DEN CHUONG TRINH CON
GOTO INTERRUPT_SUB
; CHUONG TRINH CHINH BAT DAU TU DAY
ORG 0x005
MAIN
![Page 92: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/92.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
92
; CAC LENH CUA CHUONG TRINH CHINH VIET O DAY
; Dùng ngắt nào thì phải cài đặt và cho phép ngắt đó
; CHUONG TRINH CON PHUC VU NGAT
INTERRUPT_SUB
; CÁC LỆNH VIẾT Ở ĐÂY
; KET THUC CHUONG TRINH CON PHUC VU NGAT
RETFIE
END
6.3.2 Các điểm chú ý trong chương trình có sử dụng ngắt:
ORG 0x000; CHI DAN BIEN DICH
; NHAY TOI CHUONG TRINH CHINH
GOTO MAIN
; DIA CHI VECTOR NGAT
ORG 0x004
; THUC HIEN LENH NHAY DEN CHUONG TRINH CON
![Page 93: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/93.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
93
GOTO INTERRUPT_SUB
; CHUONG TRINH CHINH BAT DAU TU DAY
ORG 0x005
MAIN
; CAC LENH CUA CHUONG TRINH CHINH VIET O DAY
Như đã phân tích ở các mục bên trên, địa chỉ 0x004 trong bộ nhớ chương trình là
vector ngắt của vi điều khiển, nghĩa là khi có một ngắt nào đó xảy ra (với điều
kiện là ngắt đó và ngắt toàn cục đã được cho phép) thì cờ ngắt cho ngắt đó được
set lên 1, vi điều khiển lập tức nhảy tới địa chỉ 0x004, tại đây người lập trình đưa
ra các lệnh xử lý ngắt đó.
Như vậy, các lệnh của phần xử lý ngắt này phải bắt đầu từ địa chỉ 0x004. Tuy
nhiên, thông thường để cho tiện quản lý và theo dõi ngắt, phần chương trình xử lý
ngắt được bố trí ở một nơi khác. Ví dụ ở đây là chương trình con phục vụ ngắt:
INTERRUPT_SUB
; KET THUC CHUONG TRINH CON PHUC VU NGAT
RETFIE
Và tại địa chỉ 0x004 ta có lệnh nhảy đến chương trình con này:
ORG 0x004
; THUC HIEN LENH NHAY DEN CHUONG TRINH CON
![Page 94: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/94.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
94
GOTO INTERRUPT_SUB
Ở đây cũng lưu ý thêm 1 điều là chương trình chính phải bố trí bắt đầu ít
nhất là từ địa chỉ 0x005 trở đi. Vì rõ ràng nếu nằm trong khoảng từ 0x000 đến
0x0004 thì các lệnh của chương trình chính sẽ nằm đè lên chương trình con phục
vụ ngắt mà cụ thể ở đây là lệnh GOTO INTERRUPT_SUB
6.3.3 Chương trình con phục vụ ngắt:
Cấu trúc:
Tên chương trình con
RETFIE
Khi có ngắt xảy ra, vi điều khiển lưu địa chỉ của lệnh tiếp theo sẽ được thực hiện ở
chương trình chính vào stack, sau đó nhảy đến địa chỉ 0x004, tại đây có lệnh nhảy
đến chương trình con phục vụ ngắt. Chương trình con phục vụ ngắt được thực hiện
cho đến khi gặp lệnh RETFIE, vi điều khiển lấy địa chỉ trong stack đưa vào PC để
có lệnh trở về chương trình chính, và bit cờ GIE được set lên 1.
Trong thực tế, trước khi nhảy vào phần thực hiện các xử lý ngắt trong chương
trình con phục vụ ngắt ta cần lưu lại giá trị thanh ghi W và thanh ghi STATUS tại
thời điểm khi xảy ra ngắt để khi xử lý xong ta trở về thực hiện tiếp chương trình
chính.
Để thực hiện điều này, chương trình con phục vụ ngắt có cấu trúc như sau:
Tên chương trình con
MOVWF W_TEMP
![Page 95: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/95.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
95
SWAPF STATUS,W
MOVWF STATUS_TEMP
; các lệnh xử lý ngắt ở đây
SWAPF STATUS_TEMP,W
MOVWF STATUS
SWAPF W_TEMP,F
SWAPF W_TEMP,W
RETFIE
Về cơ bản ở đây là trước khi xử lý ngắt cần đưa nội dung của thanh ghi W, thanh
ghi STATUS vào các thanh ghi tạm thời trong RAM: W_TEMP, STATUS_TEMP
Và sau khi xử lý xong thì đưa các giá trị này về thanh ghi W và STATUS
Chỉ chú ý một điều là thay vì dùng các lệnh movf để xử lý ở đây sử dụng các lệnh
swapf vì lệnh này khác lệnh movf ở chỗ là nó không làm ảnh hưởng đến thanh ghi
STATUS.
6.4 Ngắt ngoài:
6.4.1 Hoạt động:
- Nguồn ngắt: là xung đi vào chân RB0 của vi điều khiển PIC
![Page 96: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/96.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
96
- Sự kiện ngắt: sự kiện ngắt xảy ra khi có xung đi vào chân RB0 của vi điều
khiển. Xung là xung sườn dương hay sườn âm phụ thuộc bit cài đặt chọn
dạng xung, bit INTEDG ( bit 6 của thanh ghi OPTION_REG) là 1 hay 0.
- Bit cho phép ngắt: Để cho phép ngắt ngoài, bit cho phép ngắt ngoài INTIE
(bit 4 của thanh ghi INTCON) phải được set lên 1. Ngoài ra, bit cho phép
ngắt toàn cục GIE (bit 7 của thanh ghi INTCON) cũng phải được set lên 1.
- Cờ ngắt: bit cờ ngắt ngoài là bit INTIF (bit 1 của thanh ghi INTCON) được
tự động set lên 1 khi có sự kiện ngắt ngoài xảy ra. Cờ này phải được xóa
bằng chương trình (cụ thể là trong chương trình con phục vụ ngắt) để vi
điều khiển quản lý chính xác các lần ngắt kế tiếp.
6.4.2 Quản lý ngắt ngoài trong chương trình hợp ngữ:
;CHUONG TRINH VI DIEU KHIEN CHAY TAI DAY
ORG 0x000; CHI DAN BIEN DICH
; NHAY TOI CHUONG TRINH CHINH
GOTO MAIN
; DIA CHI VECTOR NGAT
ORG 0x004
; THUC HIEN LENH NHAY DEN CHUONG TRINH CON
GOTO INTERRUPT_SUB
; CHUONG TRINH CHINH BAT DAU TU DAY
ORG 0x005
![Page 97: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/97.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
97
MAIN
; CAC LENH CUA CHUONG TRINH CHINH VIET O DAY
; CAI DAT DANG XUNG SUONG AM HAY SUON DUONG
BCF OPTION_REG,6 ; CHON SUON AM
; CHO PHEP NGAT
BSF INTCON,4; CHO PHEP NGAT NGOAI
BSF INTCON,7; CHO PHEP NGAT TOAN CUC
; PHAN CHUONG TRINH CHINH O DAY
; CHUONG TRINH NGAT
INTERRUPT_SUB
MOVWF W_TEMP
SWAPF STATUS,W
MOVWF STATUS_TEMP
BANKSEL INTCON
BCF INTCON,1 ; XOA CO NGAT NGOAI
; PHAN XU LY NGAT O DAY
![Page 98: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/98.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
98
; PHAN KET THUC NGAT O DAY
SWAPF STATUS_TEMP,W
MOVWF STATUS
SWAPF W_TEMP,F
SWAPF W_TEMP,W
; KET THUC CHUONG TRINH CON PHUC VU NGAT
RETFIE
Xem thêm chương trình xử lý ngắt ngoài trong file đính kèm
6.5 Ngắt Timer 0:
6.5.1 Hoạt động:
- Nguồn ngắt: là trạng thái tràn của thanh ghi bộ đếm timer 0 - TMR0 của vi
điều khiển PIC
- Sự kiện ngắt: sự kiện ngắt xảy ra khi có sự tràn của TMR0, tức là khi
TMR0=255 +1 và bị xóa
- Bit cho phép ngắt: Để cho phép ngắt này, bit cho phép ngắt TMR0IE (bit 5
của thanh ghi INTCON) phải được set lên 1. Ngoài ra, bit cho phép ngắt
toàn cục GIE (bit 7 của thanh ghi INTCON) cũng phải được set lên 1.
- Cờ ngắt: bit cờ ngắt ngoài là bit TMR0IF (bit 2 của thanh ghi INTCON)
được tự động set lên 1 khi có sự kiện ngắt ngoài xảy ra. Cờ này phải được
xóa bằng chương trình (cụ thể là trong chương trình con phục vụ ngắt) để vi
điều khiển quản lý chính xác các lần ngắt kế tiếp.
6.5.2 Quản lý ngắt Timer 0 trong chương trình hợp ngữ:
![Page 99: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/99.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
99
Trong chương trình chính, cài đặt ngắt:
- Cài đặt chế độ cho Timer0
- Gán giá trị ban đầu cho thanh ghi TMR0, tùy thuộc vào thời gian mà người
lập trình đưa ra theo yêu cầu
- Cho phép ngắt timer 0: TMR0IE=1 (bít thứ 5 của thanh ghi INTCON)
- Cho phép ngắt toàn cục: GIE=1 (bít thứ 5 của thanh ghi INTCON)
Trong chương trình con phục vụ ngắt:
- Nhớ xóa cờ ngắt timer0: TMR0IF=0 (bít thứ 2 của thanh ghi INTCON)
; DUA FILE LIET KE VAO
#INCLUDE P16F877A.INC
; KHAI BAO CAU HINH
__CONFIG _HS_OSC & _WDT_OFF &_LVP_OFF
; KHAI BAO BIEN O DAY
; DIA CHI BIEN O VUNG NHO BIEN BANK0
CBLOCK 0x20
vong1,
vong2,
vong3,
so,SOLANNGAT
hangchuc,hangdonvi,trunggian, STATUS_TEMP,W_TEMP
ENDC
;CHUONG TRINH VI DIEU KHIEN CHAY TAI DAY
ORG 0x000; CHI DAN BIEN DICH
; NHAY TOI CHUONG TRINH CHINH
GOTO MAIN
; DIA CHI VECTOR NGAT
![Page 100: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/100.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
100
ORG 0x004
; THUC HIEN LENH NHAY DEN CHUONG TRINH CON
GOTO INTERRUPT_SUB
; CHUONG TRINH CHINH BAT DAU TU DAY
ORG 0x005
MAIN
; CAC LENH CUA CHUONG TRINH CHINH VIET O DAY
; CAI DAT DANG XUNG SUONG AM HAY SUON DUONG
; CHO PHEP NGAT
BANKSEL OPTION_REG
MOVLW B'00000111'; CHON CHE DO BO DINH THOI, TI LE
BO CHIA TAN SO LA 1:256
MOVWF OPTION_REG
BANKSEL TMR0
MOVLW D'237'; CAI DAT DE KHI TMR0 BI TRAN VUA DU
5MS
MOVWF TMR0
BANKSEL INTCON
BSF INTCON,5; CHO PHEP NGAT TIMER0
BSF INTCON,7; CHO PHEP NGAT TOAN CUC
BANKSEL TRISB
; XOA THANH GHI TRISB, CHO PHEP CAC CHAN PORTB LA DAU
RA
CLRF TRISD
CLRF TRISA
![Page 101: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/101.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
101
MOVLW B'00000001'
MOVWF TRISB
BANKSEL ADCON1
MOVLW 0X06
MOVWF ADCON1
; CHON BANK NHO CO THANH GHI PORTB
; BAT DAU XU LY
start:
banksel PORTB
clrf porta
movlw d'12
MOVWF SO
clrf hangchuc
clrf hangdonvi
call chia10
MOVLW D'2
MOVWF SOLANNGAT
GOTO $
; DUA DU LIEU GIATRICONG VAO THANH GHI TRUNG GIAN W
; CHUONG TRINH CON PHUC VU NGAT
INTERRUPT_SUB
MOVWF W_TEMP
![Page 102: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/102.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
102
SWAPF STATUS,W
MOVWF STATUS_TEMP
; XOA CO NGAT NGOAI
BCF INTCON,2
DECFSZ SOLANNGAT,F
GOTO BATLED1
GOTO BATLED2
BATLED1:
bcf porta,1
bsf porta,0
movF HANGCHUC,W
CALL BANGMA
MOVWF PORTD
GOTO THOAT
BATLED2:
MOVLW D'2
MOVWF SOLANNGAT
bcf porta,0
bsf porta,1
MOVF HANGDONVI,W
CALL BANGMA
MOVWF PORTD
![Page 103: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/103.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
103
THOAT:
SWAPF STATUS_TEMP,W
MOVWF STATUS
SWAPF W_TEMP,F
SWAPF W_TEMP,W
; KET THUC CHUONG TRINH CON PHUC VU NGAT
RETFIE
chia10:
;tru cho 10
BANKSEL SO
MOVF SO,W
MOVWF HANGDONVI
MOVLW D'10
subwf so,f
btfsc status,c
goto capnhat
; dua gia tri ve cho hang donvi
goto EXIT
capnhat:
incf hangchuc,f
goto chia10
EXIT:
return
BANGMA
![Page 104: Giao Trinh Hop Ngu PIC - t.au](https://reader033.vdocuments.pub/reader033/viewer/2022051418/55cf9919550346d0339b8c7b/html5/thumbnails/104.jpg)
TTrrầầnn TThhááii AAnnhh ÂÂuu –– KKhhooaa ĐĐiiệệnn –– ĐĐạạii hhọọcc BBáácchh KKhhooaa -- ĐĐạạii hhọọcc ĐĐàà NNẵẵnngg
104
ADDWF PCL,F
RETLW 0XC0
RETLW 0XF9
RETLW 0XA4
RETLW 0XB0
RETLW 0X99
RETLW 0X92
RETLW 0X82
RETLW 0XF8
RETLW 0X80
RETLW 0X90
END