Người báo cáo:
Nguyễn Quang Nam
Tài liệu:
TUT02.01
Ngày:
5/3/2006
Trang:
1/19
Tutorial n
o
02.01
Gửi đến: Đoàn Hiệp, www.picvietnam.com
Nội dung: Chương trình hợp ngữ hiển thị LED với dsPIC
MICROSOFT WORD
Tóm tắt:
Hướng dẫn viết chương trình hợp ngữ hiển thị LED cho dsPIC: Khung cơ bản của một chương
trình hợp ngữ cho dsPIC, một số lệnh và thao tác cơ bản.
1. Giới thiệu
Thông qua các ví dụ về hiển thị LED, bộ khung cơ bản của chương trình hợp ngữ
cho dsPIC cùng với một số lệnh và thao tác cơ bản của dsPIC được phân tích. Khung cơ
bản sẽ giới thiệu các phần như: thông tin cơ bản, lệnh gộp (include), đặt cấu hình cho
chip, khai báo hằng số, khai báo điểm bắt đầu chương trình, chương trình con, chương
trình xử lý ngắt. Các lệnh được giới thiệu (không đầy đủ) thuộc các nhóm: lệnh sao
chép dữ liệu, lệnh số học, lệnh luận lý, lệnh dịch/xoay, lệnh thao tác bit, lệnh so sánh,
lệnh chuyển điều khiển. Thao tác truy xuất cổng, cài đặt và kích hoạt bộ định thời
(timer), cũng như các thao tác thiết lập ngăn xếp (stack) sẽ được đề cập. Ngoài ra, một
số kiểu định vị cũng sẽ được nói đến.
2. Các quy ước trong tài liệu
Mô tả Biểu thị Ví dụ
Font Palatino Linotype:
In nghiêng Tài liệu tham khảo
dsPIC30F/33F
Programmer’s Reference
Manual
Viết hoa chữ đầu Chọn một menu chọn Project Wizard
Đặt trong dấu nháy kép
Một tên trường trong cửa
sổ hay hộp thoại
“Save project before
build”
Một nút nhấn Nhấn Next
In đậm
Một nhãn Chọn nhãn Power
Văn bản giữa các dấu
ngoặc nhọn < >
Một phím trên bàn phím Nhấn <Enter>, <F1>
Font Courier:
Courier thường Mã nguồn
mov #0x8010, W0
Người báo cáo:
Nguyễn Quang Nam
Tài liệu:
TUT02.01
Ngày:
5/3/2006
Trang:
2/19
Từ khóa
blcr, bra
Tên tập tin, đường dẫn
D:\Microchip\
Tùy chọn ở dòng lệnh
pic30-as –-version
3. Các ví dụ về LED
Trước khi bắt đầu theo dõi các ví dụ, bạn hãy chép các tập tin nguồn đã được đính
kèm vào một thư mục nào đó mà bạn sẽ dùng để tạo ra project “Vidu2”.
3.1. Ví dụ 2-1
Bạn hãy làm theo các bước như trong tutorial 1 để tạo một project có tên là “Vidu2”,
bạn thêm vào project tập tin nguồn “Vidu2-1.s” (được đính kèm) và kịch bản liên kết
“p30f4012.gld”. Sau khi hoàn tất việc tạo project, cửa sổ cây quản lý tập tin của
project hẳn sẽ trông giống như hình minh họa dưới đây.
Hình 3.1: Cây quản lý tập tin của project Vidu2
Vì đây là tutorial thứ hai nên các ví dụ được đặt tên bắt đầu bằng “Vidu2”, nhưng
“Vidu2-1.s” thực chất là “Vidu.s” đã được giới thiệu trong tutorial 1. Trong ví dụ
này, một chương trình đơn giản sẽ thực hiện việc làm nhấp nháy một LED nối vào chân
RD0 của dsPIC. Tôi giả thiết rằng bạn biết cách nối một LED vào chân RD0 của dsPIC
trên mạch thử nghiệm của bạn, một khi bạn đọc tài liệu này (tất nhiên các chân nguồn
và chân MCLR cần phải được kết nối thích hợp, chương trình ví dụ sẽ sử dụng bộ dao
động nội của dsPIC, do đó không cần có mạch tạo dao động để cấp xung clock ở bên
ngoài). Để tiện theo dõi, chương trình nguồn “Vidu2-1.s” được liệt kê dưới đây, với
một số phần chú thích được cắt bỏ cho thuận tiện.
Liệt kê mã nguồn “Vidu2-1.s”:
1 .equ __30F4012, 1
2 .include "p30fxxxx.inc"
3 config __FOSC, CSW_FSCM_OFF & FRC_PLL4
4 config __FWDT, WDT_OFF
5 config __FBORPOR, MCLR_EN & PBOR_OFF
Người báo cáo:
Nguyễn Quang Nam
Tài liệu:
TUT02.01
Ngày:
5/3/2006
Trang:
3/19
6 config __FGS, CODE_PROT_OFF
;---------------------------------------------------------------
;Cac hang so (gia tri tuc thoi) cua chuong trinh
7 .equiv LED, 0 ;LED noi vao RD0
;---------------------------------------------------------------
;Cac khai bao toan cuc
8 .global __reset ;Khai bao nhan bat dau chuong trinh (bat buoc)
9 .global __T1Interrupt ;Khai bao toan cuc c/t xu ly ngat Timer 1
;---------------------------------------------------------------
;Doan ma trong vung nho chuong trinh
10 .text ;Bat dau doan ma chuong trinh
__reset:
11 mov #__SP_init, W15 ;Khoi tao con tro ngan xep (stack)
12 mov #__SPLIM_init, W0
13 mov W0, SPLIM ;Khoi tao thanh ghi gioi han con tro (stack)
14 nop ;Can mot lenh NOP sau khi ghi vao SPLIM
15 clr W0 ;Xoa thanh ghi lam viec W0
16 mov W0, W14 ;Xoa cac thanh ghi W1 den W14
17 repeat #12
18 mov W0,[++W14] ;Dia chi cua cac thanh ghi: 0x0002 - 0x001C
19 clr W14
;---------------------------------------------------------------
20 rcall Init_PORTS ;Khoi tao cac cong I/O
21 rcall Init_TMR1 ;Khoi tao cho TMR1 (tran moi 0,5 giay)
main_loop:
22 nop
23 nop
24 bra main_loop ;Vong lap chinh, khong lam gi ca, chi cho ngat
;---------------------------------------------------------------
;Chuong trinh con khoi tao TMR1
;TMR1 duoc khoi tao de tran sau moi 0,5 giay tai muc xung 5 Mips
; (fcy = 5 MHz)
;---------------------------------------------------------------
Init_TMR1:
25 clr TMR1
26 mov #0x9894, W0 ;TMR1 tran moi 0,5 giay
27 mov W0, PR1 ;Dat nguong tran vao PR1
28 bclr IFS0, #T1IF ;Xoa co ngat cua TMR1
29 mov #0x8020, W0 ;TMR1 dung fcy lam clock, prescale la 1:64,
30 mov W0, T1CON ;va khoi dong TMR1
31 bset IEC0, #T1IE ;cho phep ngat khi TMR1 tran
32 return
;---------------------------------------------------------------
;Chuong trinh con khoi tao cac cong I/O, de noi voi LED
;---------------------------------------------------------------
Init_PORTS:
33 clr LATD ;Xoa thanh ghi chot cho cong D
34 mov #0xFFFE, W0 ;LED noi vao cong D, chan RD0
35 mov W0, TRISD
36 return
;---------------------------------------------------------------
Người báo cáo:
Nguyễn Quang Nam
Tài liệu:
TUT02.01
Ngày:
5/3/2006
Trang:
4/19
;Trinh phuc vu ngat cho TMR1
;Moi 0,5 giay ngat nay duoc thuc thi. No se lat trang thai (toggle)
;cua LED noi vao chan RD0
;---------------------------------------------------------------
__T1Interrupt: ;Ten nay da duoc dinh nghia truoc trong tap tin
;lien ket
37 bclr IFS0, #T1IF ;Xoa co ngat
38 btg PORTD, #LED ;Lat trang thai LED
39 retfie ;Tro ve tu ngat
;---------------------------------------------------------------
40 .end ;Ket thuc phan ma trong tap tin nay
Ở đây các dòng lệnh được đánh số để thuận tiện cho việc giải thích, và các con số
này không nên xuất hiện trong chương trình thực của bạn (hay nói cách khác, bạn
không nên đánh số giống như vậy trong chương trình của bạn).
Trước khi thực sự viết bất kỳ một dòng lệnh nào, bạn nên dành thời gian mô tả các
thông tin về chương trình trong phần khung thông tin ở đầu tập tin nguồn. Các
chương trình ví dụ ở đây chỉ là mẫu rút gọn, mẫu chính thức sẽ được chúng tôi cung
cấp sau khi đã chuẩn hóa xong.
Dòng 1 và 2 sẽ thực hiện việc gộp tập tin tương ứng với chip được định nghĩa vào
tập tin nguồn. Bạn có thể chỉ dùng một lệnh .inc để gộp tập tin “p30f4012.inc”
dành cho dsPIC đang được dùng, ở đây chỉ đơn giản là giới thiệu một cách khác để
gộp tập tin vào tập tin nguồn. Tập tin gộp (include) chứa các định nghĩa của các
thanh ghi, các bit trong các thanh ghi, và các hằng số khác được sử dụng trong
chương trình, chẳng hạn như các fuse trong các word cấu hình. Như bạn sẽ thấy dọc
theo chiều dài của chương trình, các dẫn hướng (directive) của ASM30 thường bắt
đầu bằng một dấu chấm, khác với đa số các dẫn hướng của MPASM (dành cho các
dòng PIC 8-bit).
Dòng 3 đến dòng 6 đặt các word cấu hình cho chip. Chúng ta xét một lệnh đặt cấu
hình tiêu biểu dưới đây.
config __FOSC, CSW_FSCM_OFF & FRC_PLL4
config là một macro được định nghĩa sẵn trong tập tin gộp, dùng để đặt giá trị cho
các từ cấu hình một cách thuận tiện. Sau đó chúng ta dùng tên của word cấu hình,
rồi đến dấu phẩy, sau đó là các bit hay các nhóm bit được tập hợp lại thông qua
phép toán logic AND. Các hằng số này được định nghĩa trong tập tin gộp, như đã
nói. Nếu bạn cảm thấy khó khăn trong việc nhớ các hằng số này, có một cách đơn
giản hơn (được chính hãng Microchip khuyên dùng) là mở tập tin gộp của chip
đang được dùng (ở đây là “p30f4012.inc”) và chép các lệnh đặt cấu hình mà bạn
mong muốn vào tập tin nguồn của bạn. Các lệnh đặt cấu hình hợp lệ đã được tạo ra
sẵn trong tập tin gộp, bạn chỉ cần chép vào và gỡ bỏ chú thích (uncomment). Như
vậy lệnh đặt cấu hình ở trên tắt các chức năng chuyển xung clock (Clock Switching)
Người báo cáo:
Nguyễn Quang Nam
Tài liệu:
TUT02.01
Ngày:
5/3/2006
Trang:
5/19
và giám sát sự cố clock (Fail-Safe Clock Monitor), đồng thời cấu hình chip dùng bộ
dao động nội tốc độ cao (FRC) với PLLx4 được kích hoạt.
Dòng 7 là một ví dụ về cách khai báo hằng dùng chỉ dẫn .equiv. Điểm khác biệt
giữa .equiv và .equ là .equiv sẽ đưa ra thông báo lỗi nếu hằng số đã được định
nghĩa trước đó, trong khi .equ sẽ định nghĩa lại hằng số và không báo lỗi.
Dòng 8 và 9 là các khai báo toàn cục cho các đoạn chương trình xử lý reset và ngắt
timer 1. Cần lưu ý là khai báo cho đoạn chương trình xử lý reset là bắt buộc, vì nó
dùng để định nghĩa điểm bắt đầu chương trình (là nơi mà bộ xử lý sẽ nhảy đến thực
thi sau khi thoát khỏi trạng thái reset). Các tên như __reset, __T1Interrupt đã
được định nghĩa sẵn trong tập tin liên kết (ở đây là “p30f4012.gld”). Bạn chú ý là
các tên này được bắt đầu bằng hai dấu gạch dưới (underscore) liên tiếp (để tương
thích với trình biên dịch C). __reset là nhãn bắt đầu chương trình chính, còn
__T1Interrupt là nhãn của chương trình xử lý ngắt timer 1. (Thực tế thì bạn vẫn
có thể dùng tên khác cho các phần này, nhưng bạn sẽ phải mô tả nó trong tập tin
kịch bản liên kết, mà tôi sẽ không đề cập đến trong tutorial này. Do đó, tạm thời
chúng ta xem như đây là những tên bắt buộc phải dùng để đảm bảo chương trình
hoạt động như dự định).
Chương trình chính gồm các lệnh từ dòng 10 đến dòng 24. Tại dòng 10 là một khai
báo bắt đầu một đoạn mã thuộc bộ nhớ chương trình, .text là một đoạn đã được
định nghĩa trước đối với trình hợp dịch ASM30. Tương tự, bạn sẽ dùng .bss cho
đoạn chứa các dữ liệu không khởi tạo giá trị và .data cho đoạn chứa các dữ liệu
được khởi tạo giá trị. Ngoài ra, còn có những đoạn được định nghĩa sẵn cho các
vùng nhớ X, Y, EEPROM, ..., bạn tham khảo thêm trong tài liệu hướng dẫn online
của ASM30, hoặc tài liệu MPLAB ASM30, MPLAB LINK30, and Utilities User’s Guide
(DS51317).
Chương trình chính của chúng ta thực sự bắt đầu ở dòng 11. Từ dòng 11 đến dòng
14 là các lệnh khởi tạo ngăn xếp (stack), với các hằng số __SP_init và
__SPLIM_init được ASM30 xác định khi biên dịch, dựa vào thông tin về chip
được dùng. Thanh ghi W15 mặc định là con trỏ ngăn xếp, do đó bạn không nên sử
dụng thanh ghi này, mặc dù nó là một thanh ghi đa dụng. Các lệnh được sử dụng ở
đây là mov và nop. mov là lệnh sao chép dữ liệu, một lệnh không thể thiếu trong bất
kỳ tập lệnh của vi điều khiển nào. Quy tắc viết của lệnh là từ khóa mov, sau đó là
toán hạng nguồn, dấu phẩy rồi đến toán hạng đích. dsPIC hỗ trợ 8 lệnh sao chép dữ
liệu 16-bit, 1 lệnh sao chép dữ liệu 8-bit (định vị tức thời), và 2 lệnh sao chép dữ liệu
32-bit (định vị thanh ghi). Với dữ liệu 16-bit, dsPIC hỗ trợ sao chép dữ liệu giữa các
thanh ghi làm việc Wn và các thanh ghi chức năng, và sao chép dùng định vị tức
thời và định vị thanh ghi. Bạn tham khảo tài liệu dsPIC30F/33F Programmer’s
Reference Manual (DS70157) để có thông tin đầy đủ về tập lệnh của dsPIC. Bạn chắc
hẳn có thể đoán ra nop là lệnh không làm gì cả (chỉ khiến bộ xử lý tiêu tốn một chu
kỳ máy).
Người báo cáo:
Nguyễn Quang Nam
Tài liệu:
TUT02.01
Ngày:
5/3/2006
Trang:
6/19
Các dòng 15 đến 19 dùng để xóa các thanh ghi làm việc W0 đến W14. Tất nhiên
chúng ta có thể dùng 15 lệnh clr Wn, với n=0-14, để thực hiện việc đó. Tuy nhiên, ở
đây tôi giới thiệu đôi chút kỹ thuật định vị gián tiếp thanh ghi và lệnh lặp phần
cứng repeat. Các dòng 15 và 16 hẳn không có gì khó hiểu đối với bạn, dùng để xóa
thanh ghi W0 và W14. Bạn có thể dùng lệnh clr hay lệnh mov để xóa các thanh ghi
này. Lệnh clr chỉ có một toán hạng, đó là toán hạng đích. Tiếp theo, hãy để ý rằng
các thanh ghi Wn cũng là các thanh ghi chức năng, có địa chỉ liên tiếp nhau và bắt
đầu từ 0x0000. Do đó, có thể lợi dụng thanh ghi W14 làm con trỏ đến vùng địa chỉ
này, và thông qua lệnh lặp để xóa 13 thanh ghi còn lại. Con trỏ W14 hiện đang trỏ
đến địa chỉ 0x0000 (vì vừa được xóa), do đó cần được cập nhật trước khi sử dụng, và
chúng ta dùng toán tử tăng trước (preincrement) như trong tập tin nguồn đã thể
hiện. Lệnh repeat cho phép bạn thực hiện lặp lại lệnh ngay sau nó một số lần được
quy định bởi một hằng số (như trong ví dụ này) hay bởi một thanh ghi làm việc Wn.
Cụ thể thì chúng ta mô tả giá trị hằng số bằng số lần cần lặp trừ đi 1. Do vậy, lệnh
repeat #12 sẽ lặp lại 13 lần lệnh ngay sau nó (ở đây là mov W0,[++W14]). Lệnh
repeat cho phép lặp tối đa 16384 lần, nếu bạn dùng một thanh ghi Wn thì chỉ có 14
bit thấp nhất của Wn được quan tâm. Vì bộ nhớ dữ liệu của dsPIC cho phép truy
xuất cả byte lẫn word, do đó Microchip đã đánh địa chỉ bộ nhớ dữ liệu của dsPIC
theo byte. Như vậy, toán tử tăng trước ++ trong lệnh mov ở dòng 18 sẽ khiến thanh
ghi con trỏ W14 tăng 2 sau mỗi lần lặp (để trỏ đến word kế tiếp). Sau cùng, vì W14
đã có giá trị khác 0, lệnh clr W14 ở dòng 19 hoàn tất việc xóa các thanh ghi làm
việc W0 đến W14.
Ở các dòng 20 và 21, chúng ta gọi các chương trình con để khởi tạo các cổng xuất
nhập và timer 1. Lệnh gọi chương trình con tương đối rcall cho phép gọi các
chương trình con nằm trong phạm vi -32768 đến +32767 từ lệnh so với lệnh kế tiếp
(vì khi thực thi lệnh này thì thanh ghi đếm lệnh PC đã chỉ đến lệnh kế tiếp). Nếu
chương trình con nằm ngoài phạm vi này thì cần phải dùng lệnh call, cho phép gọi
bất kỳ chương trình con nào trong phạm vi không gian địa chỉ 24-bit của dsPIC.
Dòng 22 đến dòng 24 là vòng lặp chính, với 2 lệnh nop và một lệnh rẽ nhánh không
điều kiện bra. Lệnh bra cũng có tầm địa chỉ tương tự như lệnh rcall đã nói ở
trên. Vì các thao tác cập nhật ngõ ra được thực hiện trong chương trình xử lý ngắt
timer 1, do đó chương trình sẽ không có việc gì để thực thi.
Chương trình con khởi tạo timer 1 gồm các lệnh từ dòng 25 đến 32. Ngoài các lệnh
đã gặp, ở đây chúng ta thấy có 2 lệnh mới dùng để thao tác bit là bset và bclr. Hai
lệnh này có toán hạng đầu là thanh ghi bị tác động, và toán hạng kế tiếp là chỉ số của
bit bị tác động. Chỉ số của bit chỉ có thể được mô tả bằng một hằng số. Dòng lệnh 25
xóa số đếm của timer 1 (chứa trong thanh ghi TMR1) trước khi khởi động bộ đếm,
do đó chúng ta sẽ không đếm thiếu trong chu kỳ đầu tiên vì dữ liệu không xác định
đang có trong TMR1. Hai dòng lệnh 26 và 27 đặt ngưỡng tràn tương ứng với 0,5
giây ở tần số thực thi lệnh là 5 MHz (tức là chip có clock bằng 20 MHz). Ở dòng 28,
Người báo cáo:
Nguyễn Quang Nam
Tài liệu:
TUT02.01
Ngày:
5/3/2006
Trang:
7/19
chúng ta xóa cờ ngắt của timer 1, bằng cách xóa (clear) bit T1IF của thanh ghi IFS0.
Chế độ làm việc của timer 1 được thiết lập trong thanh ghi T1CON, gồm các thông
tin về nguồn clock, prescaler (bộ chia trước), chế độ timer/counter, bật/tắt timer. Các
thông tin này được đặt vào thanh ghi T1CON bằng các dòng lệnh 29 và 30. Dòng
lệnh 31 cho phép timer 1 tạo ra ngắt khi tràn bằng cách đặt (set) bit T1IE trong thanh
ghi IEC0. Và chương trình con luôn luôn cần một lệnh trở về, return, ở đây được
đặt tại dòng 32. Bạn hãy đọc phần tương ứng trong datasheet của dsPIC được dùng
để có thông tin chi tiết về các chế độ làm việc của timer 1.
Từ dòng 33 đến 36, chúng ta có một chương trình con khởi tạo các cổng xuất/nhập
(vào/ra). Vì chương trình ví dụ này chỉ sử dụng chân RD0 làm ngõ ra, do đó các
chân khác của cổng (port) D được đặt ở trạng thái ngõ vào (Microchip khuyến cáo là
các chân không sử dụng nên đặt là ngõ vào, và nối đến VDD hay GND, tránh để các
ngõ vào trôi trạng thái một cách tự do). Các lệnh trong chương trình con này đã tự
giải thích trong phần chú thích, và khá quen thuộc, do đó tôi không giải thích ở đây
nữa.
Chương trình xử lý (phục vụ) ngắt timer 1 gồm các dòng lệnh từ 37 đến 39. Bạn chú
ý là tên __T1Interrupt đã được định nghĩa trước trong kịch bản liên kết, do đó
bạn phải dùng đúng tên này làm nhãn cho phần chương trình mà bạn muốn dùng
để xử lý ngắt timer 1. Dòng 37 dùng để xóa cờ ngắt, như bạn đã từng thấy ở dòng 28
trong chương trình con khởi tạo timer 1. Việc xóa cờ ngắt được yêu cầu thực hiện
bằng phần mềm, theo như datasheet, và nên được thực thi ngay khi bước vào
chương trình xử lý ngắt. Sau khi xóa cờ ngắt, chúng ta sẽ thực hiện công việc tiếp
theo là lật trạng thái của ngõ ra RD0. Tương tự như dòng PIC cao cấp PIC18, dsPIC
cung cấp một lệnh lật trạng thái bit là btg, như được minh họa ở dòng lệnh 38. Thay
vì dùng lệnh btg PORTD, 0 để lật trạng thái RD0, chúng ta dùng hằng số LED
được khai báo ở phần đầu chương trình, mà hiệu quả của nó thì hẳn bạn đã nắm rõ,
khi bạn quyết định dùng dsPIC và đọc tài liệu này. Các chương trình xử lý ngắt cần
có một lệnh trở về khác so với lệnh trở về từ chương trình con, do đó dòng 39 dùng
lệnh retfie để trở về chương trình bị ngắt, sau khi đã xử lý xong ngắt.
Cuối cùng, tập tin nguồn hợp ngữ cho dsPIC được yêu cầu phải có một dẫn hướng
.end cho biết đã kết thúc khối chương trình (program block), mà chúng ta đặt ở
dòng 40.
Phù! Như vậy là chương trình nháy LED của chúng ta đã viết xong. Bạn hãy thử
xem chương trình biên dịch có thành công hay không, hãy tìm các lỗi được thông
báo để sửa lại. Nếu chương trình đã được biên dịch thành công thì bạn hãy thử bằng
phần cứng với các thông số như trên, nếu bạn muốn mô phỏng bằng MPLAB SIM
thì hãy chỉnh lại bộ chia trước và ngưỡng tràn của timer 1 cho thật nhỏ để tránh mất
thời gian ngồi chờ máy tính mô phỏng các hoạt động lặp lại của chip hàng triệu lần
trước khi chân RD0 đổi trạng thái.