Tutorial n

Chia sẻ: Thuy Linh | Ngày: | Loại File: PDF | Số trang:19

0
110
lượt xem
26
download

Tutorial n

Mô tả tài liệu
  Download Vui lòng tải xuống để xem tài liệu đầy đủ

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.

Chủ đề:
Lưu

Nội dung Text: Tutorial n

  1. 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 no 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: dsPIC30F/33F In nghiêng Tài liệu tham khảo Programmer’s Reference Manual Viết hoa chữ đầu Chọn một menu chọn Project Wizard Một tên trường trong cửa “Save project before Đặt trong dấu nháy kép sổ hay hộp thoại 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 Một phím trên bàn phím Nhấn , ngoặc nhọn < > Font Courier: Courier thường Mã nguồn mov #0x8010, W0
  2. 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
  3. 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 ;---------------------------------------------------------------
  4. 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)
  5. 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).
  6. 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,
  7. 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.
  8. Người báo cáo: Nguyễn Quang Nam Tài liệu: TUT02.01 Ngày: 5/3/2006 Trang: 8/19 Trước khi chuyển sang ví dụ kế tiếp, bạn có thể luyện tập thêm bằng cách thử điều chỉnh chương trình theo các hướng: điều chỉnh ngưỡng tràn để LED sẽ nhấp nháy đúng 1 Hz (với mã hiện tại thì LED sẽ nhấp nháy ở 1,6 Hz, vì tần số thực thi lệnh của chip sẽ là 8 MHz với bộ dao động nội 8 MHz và PLLx4 được kích hoạt), dùng thêm một LED khác nối vào RD1 (theo cùng cách như bạn đã nối LED vào RD0) và làm cho 2 LED nhấp nháy ngược nhau. 3.2. Ví dụ 2-2 Để sử dụng tập tin nguồn “Vidu2-2.s” (được đính kèm), bạn có thể tạo ra một project mới, hay có thể dùng lại project “Vidu2” bằng cách làm theo hướng dẫn dưới đây (tôi thường làm theo cách này để không phải tạo ra quá nhiều project). Trước hết, trong cửa sổ quản lý tập tin của project, bạn nhấp nút chuột phải vào tên tập tin nguồn “Vidu2-1.s” để làm xuất hiện một mênu gọi lên (pop-up) như hình 3.2 đã minh họa. Sau đó bạn chọn lệnh Remove để xóa tập tin nguồn đó ra khỏi danh sách các tập tin nguồn của project (bạn yên tâm là tập tin nguồn của bạn vẫn không bị xóa). Hình 3.2: Thao tác loại bỏ tập tin nguồn ra khỏi danh sách Tiếp đến bạn sẽ thêm tập tin nguồn mới vào project, bằng cách nhấp nút chuột phải vào đầu mục “Source Files” trong cửa sổ quản lý tập tin đó, và chọn lệnh Add Files ..., như được minh họa trong hình 3.3. Đến đây thì bạn sẽ gặp một hộp thoại cho phép bạn thêm các tập tin nguồn .s vào project, bạn hãy chọn tập tin nguồn “Vidu2-2.s”. Đến đây thì bạn có thể sẽ thắc mắc: Vậy tập tin .hex được tạo ra sẽ có tên gì, nếu tôi cứ thay đổi tên tập tin nguồn, hay project của tôi có nhiều tập tin nguồn? Tập tin đầu ra của project sẽ mang tên của project (ở đây project là “Vidu2”, do đó tên tập tin .hex là “Vidu2.hex”), không phụ thuộc vào tên của các tập tin nguồn. Điều này cần xác định rõ ràng, vì bạn sẽ cần tập tin .hex này để thử chương trình của mình trên phần cứng.
  9. Người báo cáo: Nguyễn Quang Nam Tài liệu: TUT02.01 Ngày: 5/3/2006 Trang: 9/19 Hình 3.3: Thao tác thêm tập tin nguồn vào danh sách Trong ví dụ 2-2, chúng ta sẽ thực hiện bài toán hiệp sĩ đi tuần (KnightRider), di chuyển điểm sáng qua lại một số LED với tốc độ là 1 giây mỗi lần dịch chuyển. Cổng E của dsPIC được sử dụng để điều khiển LED, cụ thể là các chân RE0 đến RE5 (dsPIC30F4012 chỉ đưa ra các chân RE0 đến RE5, và RE8, mà chúng ta thường dùng các chân liên tiếp nhau trong loại bài toán này để đơn giản hóa phần viết chương trình). Tương tự như trong ví dụ trước, mã nguồn chương trình được liệt kê dưới đây để tiện theo dõi. Sơ đồ mạch phần cứng để thử nghiệm được thể hiện trong hình 3.4. Phần nằm trong khung nét đứt chỉ cần thiết khi bạn dùng thạch anh ngoài, các chân nối nguồn Vdd và Vss không được thể hiện ở đây, nhưng bạn phải đảm bảo đã nối hết các chân đó vào các mức điện áp thích hợp. Vdd R1 33k U1 9 CLKIN/OSC1 10 26 X1 1 RC15/CLKO/OSC2 RE0/PWM1L 25 MCLR RE1/PWM1H 24 RE2/PWM2L 2 23 RB0/CN2/VREF+/AN0/EMUD3 RE3/PWM2H 3 22 8MHz RB1/CN3/VREF-/AN1/EMUC3 RE4/PWM3L 4 21 C2 5 RB2/CN4/SS1/AN2 RE5/PWM3H 16 C1 6 RB3/CN5/INDX/AN3 FLTA/INT0/RE8 33pF 33pF RB4/CN6/IC7/QEA/AN4 7 RB5/CN7/IC8/QEB/AN5 RP1 18 8 RF2/SDA/SDI1/U1RX/EMUC/PGC 17 7 RF3/SCL/SDO1/U1TX/EMUD/PGD 11 6 RC13/CN1/U1ATX/T2CK/SOSCI/EMUD Vdd 12 5 RC14/CN0/U1ARX/T1CK/SOSCO/EMUC 4 15 28 3 EMUC2/OC1/IC1/INT1/RD0 AVDD 14 27 2 EMUD2/OC2/IC2/INT2/RD1 AVSS DSPIC30F4012DIP 1 1k Hình 3.4: Mạch thử nghiệm cho bài toán hiệp sĩ đi tuần Liệt kê mã nguồn “Vidu2-2.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 6 config __FGS, CODE_PROT_OFF
  10. Người báo cáo: Nguyễn Quang Nam Tài liệu: TUT02.01 Ngày: 5/3/2006 Trang: 10/19 ;---------------------------------------------------------------------- ;Cac hang so cua chuong trinh (gia tri tuc thoi dung trong chuong trinh) 7 .equiv LED_PORT, PORTE ;LED noi vao cong E 8 .equiv LED_TRIS, TRISE ;Thanh ghi trang thai cua port xuat LED 9 .equiv LED_LAT, LATE ;Thanh ghi chot cua port xuat LED ;---------------------------------------------------------------------- ;Cac khai bao toan cuc: 10 .global __reset ;Khai bao nhan bat dau chuong trinh (bat buoc) 11 .global __T1Interrupt ;Khai bao toan cuc chuong trinh xu ly ngat Timer 1 ;---------------------------------------------------------------------- ;Doan ma trong vung nho chuong trinh 12 .text ;Bat dau doan ma chuong trinh __reset: 13 mov #__SP_init, W15 ;Khoi tao con tro ngan xep (stack) 14 mov #__SPLIM_init, W0 15 mov W0, SPLIM ;Khoi tao thanh ghi gioi han con tro ngan xep (stack) 16 nop ;Can mot lenh NOP sau khi ghi vao SPLIM 17 clr W0 ;Xoa thanh ghi lam viec W0 18 mov W0, W14 ;Xoa cac thanh ghi W1 den W14 19 repeat #12 20 mov W0,[++W14] ;Dia chi cua cac thanh ghi: 0x0002 - 0x001C 21 clr W14 ;---------------------------------------------------------------------- 22 rcall Init_TMR1 ;Khoi tao cho TMR1 (tran moi 1 giay) 23 rcall Init_PORTS ;Khoi tao cac cong I/O 24 mov #0x0001, W1 ;W1 la co chi huong, '1' la dich trai, '0' la ;dich phai, khoi dong la dich trai 25 mov #0x0001, W2 ;bat dau tu bit 0, dich trai 26 mov W2, LED_LAT ;hien thi ra cac LED main_loop: 27 nop 28 nop 29 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 1 giay tai muc xung 8 Mips (fcy = 8 MHz) ;---------------------------------------------------------------------- Init_TMR1: 30 clr TMR1 31 mov #0x7A12, W0 ;TMR1 tran moi 1 giay 32 mov W0, PR1 ;Dat nguong tran vao PR1 33 bclr IFS0, #T1IF ;Xoa co ngat cua TMR1 34 mov #0x8030, W0 ;TMR dung fcy lam clock, prescale la 1:256, 35 mov W0, T1CON ;va khoi dong TMR1 36 bset IEC0, #T1IE ;cho phep ngat khi TMR1 tran 37 return ;---------------------------------------------------------------------- ;Chuong trinh con khoi tao cac cong I/O, de noi voi LED ;---------------------------------------------------------------------- Init_PORTS: 38 clr LED_LAT ;Xoa thanh ghi chot cong xuat LED
  11. Người báo cáo: Nguyễn Quang Nam Tài liệu: TUT02.01 Ngày: 5/3/2006 Trang: 11/19 39 mov #0xFFC0, W0 ;LED noi vao cong E (RE0 .. RE5) 40 mov W0, LED_TRIS 41 return ;---------------------------------------------------------------------- ;Trinh phuc vu ngat cho TMR1 ;Moi 1 giay ngat nay duoc thuc thi. No se di chuyen cac LED noi vao port E ;---------------------------------------------------------------------- __T1Interrupt: ;Ten nay da duoc dinh truoc trong tap tin lien ket 42 bclr IFS0, #T1IF ;Truoc tien, can xoa co ngat 43 btss W1, #0 ;Co phai dang dich trai? 44 goto shift_right ;Khong, vay la dich phai 45 btsc W2, #5 ;Da den bit 5 roi? 46 goto shift_right ;Dung, vay la dich phai shift_left: 47 bset W1, #0 ;Dat co dich trai 48 sl W2, W2 ;Thuc hien dich trai 49 mov W2, LED_LAT 50 retfie ;Xong viec, tro ve tu ngat shift_right: 51 bclr W1, #0 ;Dat co dich phai 52 btsc W2, #0 ;Da den bit 0 roi? 53 goto shift_left ;Dung, vay la dich trai 54 lsr W2, W2 ;Khong dung, vay van dich phai 55 mov W2, LED_LAT ;Thuc hien dich phai 56 retfie ;Tro ve tu ngat ;---------------------------------------------------------------------- 57 .end ;Ket thuc phan ma trong tap tin nay Những phần mã nguồn giống như trong ví dụ 2-1 tôi sẽ không giải thích lại nữa. Các dòng từ 7 đến 9 khai báo các hằng số được dùng để biểu diễn cổng, thanh ghi trạng thái cổng, và thanh ghi chốt mà các LED sẽ nối vào. Như vậy khi bạn muốn nối LED vào cổng khác thì chỉ cần thay đổi các giá trị cần thiết tại các dòng này, mà không phải tìm trong toàn bộ chương trình để thay thế. Ở đây, chương trình chính của chúng ta vẫn gọi các chương trình con để khởi tạo timer 1 và các cổng vào/ra, và sau một số thao tác khác sẽ đi vào vòng lặp chính, không thực hiện việc gì khác ngoài việc chờ ngắt timer 1. Tuy nhiên, các dòng 24 đến 26 có sự khác biệt so với ví dụ 2-1. Giải pháp để di chuyển vị trí LED sáng qua lại một cách trực quan là dùng các phép toán dịch bit, khi đó chúng ta có thể dùng một thanh ghi để chứa trạng thái các ngõ ra, và một cờ thể hiện hướng dịch. Tôi đã chọn thanh ghi W1 làm cờ chỉ hướng dịch (giá trị 1 là dịch trái và 0 là dịch phải), và thanh ghi W2 làm biến chứa trạng thái của ngõ ra. Như vậy, các dòng 24 đến 26 đặt hướng dịch khởi đầu là dịch trái, và bit có LED được bật sáng là bit 0. Từ dòng 30 đến dòng 37 là chương trình con khởi tạo timer 1, với các lệnh hoàn toàn giống như trong ví dụ 2-1, chỉ có các giá trị ngưỡng tràn và prescale được đặt khác đi để tạo ra một ngắt timer 1 sau mỗi 1 giây ở tần số thực thi lệnh là 8 MHz (tần số xung clock là 32 MHz).
  12. Người báo cáo: Nguyễn Quang Nam Tài liệu: TUT02.01 Ngày: 5/3/2006 Trang: 12/19 Từ dòng 38 đến dòng 41 là chương trình con khởi tạo các cổng vào/ra. Trong ví dụ này chúng ta dùng cổng E và dùng các chân RE0 đến RE5, do đó các giá trị thiết lập trạng thái cổng và địa chỉ cổng đã khác đi. Phần khác biệt chính là chương trình xử lý ngắt, vì chủ yếu các chức năng của chương trình được đặt ở đây, mà chức năng của chương trình trong ví dụ này là khác với ví dụ trước. Thuật toán ở đây là chúng ta sẽ kiểm tra cờ chỉ hướng dịch, sau đó kiểm tra các điểm biên để đổi hướng dịch nếu cần thiết. Nếu thỏa mãn các điều kiện để dịch theo một hướng nào đó thì sẽ thực hiện dịch bit và xuất kết quả ra các ngõ ra. Bây giờ chúng ta phân tích từng dòng lệnh. Dòng 43 sử dụng lệnh btss (Bit Test Skip if Set) để kiểm tra bit 0 của thanh ghi W1, nếu bit bằng ‘1’ (tức là đang dịch trái) thì sẽ bỏ qua lệnh kế tiếp ở dòng 44, chính là lệnh nhảy đến đoạn chương trình thực hiện dịch phải. Còn nếu lệnh ở dòng 44 được thực thi thì bit 0 của thanh ghi W1 bằng ‘0’, rõ ràng là đang chỉ hướng dịch phải. Tại dòng 45, khi chúng ta đã khẳng định là đang dịch trái, chúng ta sẽ kiểm tra xem LED đã được dịch đến vị trí của RE5 hay chưa, bằng một lệnh btsc (Bit Test Skip if Clear). Nếu bit 5 bằng ‘0’ thì chúng ta vẫn giữ hướng dịch trái, và sẽ thực hiện thao tác dịch trái và sau đó trở về chương trình chính bằng đoạn lệnh tiếp theo, từ dòng 47 đến dòng 50. Ngược lại (bit 5 bằng ‘1’), chúng ta sẽ phải đổi hướng thành dịch phải, do đó sẽ thực thi lệnh nhảy đến đoạn chương trình dịch phải. Ở đoạn lệnh vừa rồi, chúng ta gặp một lệnh nhảy không điều kiện khác là lệnh goto. Lệnh goto có thể nhảy đến bất kỳ địa chỉ nào trong không gian địa chỉ 24-bit của dsPIC, là một lệnh được dùng thừa ở đây (vì chiếm 2 từ lệnh), nhưng được tôi cố ý đưa vào nhằm mục đích giới thiệu. Bạn có thể tiết kiệm một word bộ nhớ chương trình với mỗi lần thay thế goto bằng bra. Bạn thực hiện dịch trái bằng lệnh sl (Shift Left). Lệnh sl có thể thao tác trên các thanh ghi Wn và các thanh ghi chức năng, và có thể dịch tối đa 15 bit trong một lần dịch (chỉ thực hiện dịch nhiều bit trên các thanh ghi Wn). Từ dòng 51 đến dòng 56 là đoạn lệnh thực hiện kiểm tra và dịch phải. Khi bộ xử lý thực thi đến đoạn lệnh này thì chúng ta đã biết chắc chắn là sẽ dịch phải, do đó sẽ đặt cờ chỉ hướng là dịch phải. Nhưng chúng ta sẽ tiếp tục kiểm tra xem đã dịch đến vị trí RE0 hay chưa, nếu đã đến vị trí đó thì chúng ta phải đổi hướng dịch, và nhảy ngược về đoạn lệnh thực hiện dịch trái ở phía trên. Trong trường hợp này, việc đặt lại cờ chỉ hướng dịch là cần thiết, và đó là lý do cho việc xuất hiện lệnh đặt cờ chỉ hướng là dịch trái ở dòng 47. Như đa số các bộ vi xử lý khác, bộ xử lý của dsPIC cũng phân biệt dịch phải số học và dịch phải logic. Ở đây chúng ta thực hiện dịch phải logic, do đó sử dụng lệnh lsr (Logic Shift Right). Tương tự như lệnh sl, lệnh lsr cũng cho phép thao tác trên các thanh ghi Wn và các thanh ghi chức năng, và có thể dịch tối đa 15 bit trong một lần dịch (một lần nữa, chỉ thực hiện dịch nhiều bit trên các thanh ghi Wn). Như bạn có thể thấy, trong ví dụ này, việc thực hiện nhiệm vụ của chương trình được giải quyết một cách trực quan, bám sát hiện tượng. Tuy nhiên, cách giải quyết này
  13. Người báo cáo: Nguyễn Quang Nam Tài liệu: TUT02.01 Ngày: 5/3/2006 Trang: 13/19 chỉ thích hợp cho một số trường hợp đơn giản, và sẽ gặp khó khăn trong những bài toán đòi hỏi các trạng thái liên tiếp phức tạp hơn hay thậm chí là bất kỳ, hay ngẫu nhiên. Kỹ thuật bảng là một trong những cách giải quyết vấn đề này, và sẽ được nói đến trong ví dụ 2-4. Ví dụ tiếp theo giới thiệu một cách giải quyết khác cho bài toán được nêu ra trong ví dụ 2-2. 3.3. Ví dụ 2-3 Bạn hãy thực hiện các bước tương tự như trong ví dụ 2-2 để loại bỏ tập tin nguồn “Vidu2-2.s” ra khỏi project, và thêm vào project tập tin nguồn “Vidu2-3.s”. Trong ví dụ 2-3, chúng ta sử dụng một biến đếm để theo dõi quá trình di chuyển vị trí LED sáng, thay cho việc kiểm tra các điểm biên trong quá trình di chuyển. Mã nguồn của ví dụ 2-3 được liệt kê dưới đây để tiện theo dõi, nhưng chỉ những phần khác với ví dụ 2-2 là được giải thích. Liệt kê mã nguồn “Vidu2-3.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 6 config __FGS, CODE_PROT_OFF ;------------------------------------------------------------------------- ;Cac hang so cua chuong trinh (gia tri tuc thoi dung trong chuong trinh) 7 .equiv LED_PORT, PORTE ;LED noi vao cong E 8 .equiv LED_TRIS, TRISE ;Thanh ghi trang thai cua port xuat LED 9 .equiv LED_LAT, LATE ;Thanh ghi chot cua port xuat LED ;------------------------------------------------------------------------- ;Cac khai bao toan cuc: 10 .global __reset ;Khai bao nhan bat dau chuong trinh (bat buoc) 11 .global __T1Interrupt ;Khai bao toan cuc chuong trinh xu ly ngat Timer 1 ;------------------------------------------------------------------------- ;Khai bao cac bien trong RAM 12 .section .bss 13 .align 2 ;chinh dia chi theo boi so cua 2 count: 14 .space 2 ;khai bao bien 'count' kieu word (2 byte) ;------------------------------------------------------------------------- ;Doan ma trong vung nho chuong trinh 15 .text ;Bat dau doan ma chuong trinh __reset: 16 mov #__SP_init, W15 ;Khoi tao con tro ngan xep (stack) 17 mov #__SPLIM_init, W0 18 mov W0, SPLIM ;Khoi tao thanh ghi gioi han con tro ngan xep (stack) 19 nop ;Can mot lenh NOP sau khi ghi vao SPLIM 20 clr W0 ;Xoa thanh ghi lam viec W0
  14. Người báo cáo: Nguyễn Quang Nam Tài liệu: TUT02.01 Ngày: 5/3/2006 Trang: 14/19 21 mov W0, W14 ;Xoa cac thanh ghi W1 den W14 22 repeat #12 23 mov W0,[++W14] ;Dia chi cua cac thanh ghi: 0x0002 - 0x001C 24 clr W14 ;------------------------------------------------------------------------- 25 rcall Init_TMR1 ;Khoi tao cho TMR1 (tran moi 1 giay) 26 rcall Init_PORTS ;Khoi tao cac cong I/O, W0 dung lam bien trang ;thai cua cac LED 27 mov #0x0001, W1 ;W1 la co chi huong, '1' la dich trai, '0' la ;dich phai, khoi dong la dich trai 28 mov #0x0005, W2 29 mov W2, count ;Bien dem so buoc dich main_loop: 30 nop 31 nop 32 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 1 giay tai muc xung 8 Mips (fcy = 8 MHz) ;------------------------------------------------------------------------- Init_TMR1: 33 clr TMR1 34 mov #0x7A12, W0 ;TMR1 tran moi 1 giay 35 mov W0, PR1 ;Dat nguong tran vao PR1 36 bclr IFS0, #T1IF ;Xoa co ngat cua TMR1 37 mov #0x8030, W0 ;TMR dung fcy lam clock, prescale la 1:256, 38 mov W0, T1CON ;va khoi dong TMR1 39 bset IEC0, #T1IE ;Cho phep ngat khi TMR1 tran 40 return ;------------------------------------------------------------------------- ;Chuong trinh con khoi tao cac cong I/O, de noi voi LED ;------------------------------------------------------------------------- Init_PORTS: 41 clr LED_LAT ;Xoa thanh ghi chot cong xuat LED 42 mov #0xFFC0, W0 ;LED noi vao cong E (RE0 .. RE5) 43 mov W0, LED_TRIS 44 mov #0x0001, W0 ;Dinh trang thai ban dau cho cac LED (RE0 sang) 45 mov W0, LED_LAT ;bit = 1 nghia la LED tuong ung sang 46 return ;------------------------------------------------------------------------- ;Trinh phuc vu ngat cho TMR1 ;Moi 1 giay ngat nay duoc thuc thi. No se di chuyen cac LED noi vao port E ;------------------------------------------------------------------------- __T1Interrupt: ;Ten nay da duoc dinh truoc trong tap tin lien ket 47 bclr IFS0, #T1IF ;Truoc tien, can xoa co ngat 48 btss W1, #0 ;Co phai dich trai khong? 49 goto shift_right ;Khong, vay la dich phai 50 dec count ;Giam bien dem 51 bra NZ, left_s ;Het dich trai chua? 52 clr W1 ;Dung, nhip ke tiep se dich phai 53 mov W2, count ;Dat lai bien dem left_s: 54 sl W0, W0
  15. Người báo cáo: Nguyễn Quang Nam Tài liệu: TUT02.01 Ngày: 5/3/2006 Trang: 15/19 55 mov W0, LED_LAT ;Thuc hien dich trai 56 retfie ;Xong viec, tro ve tu ngat shift_right: 57 dec count ;Giam bien dem 58 bra NZ, right_s ;Het dich phai chua? 59 mov #0x0001, W1 ;Dung, nhip ke tiep se dich trai 60 mov W2, count ;Dat lai bien dem right_s: 61 lsr W0, W0 62 mov W0, LED_LAT ;Thuc hien dich phai 63 retfie ;Tro ve tu ngat ;------------------------------------------------------------------------- 64 .end ;Ket thuc phan ma trong tap tin nay Với các dòng từ 12 đến 14, chúng ta khai báo một vùng nhớ RAM, trong đó chúng ta có một biến count có kiểu word (2-byte), với các biến được cấp phát theo các phần có địa chỉ bắt đầu cách nhau bằng các bội số của 2 byte (hay nói cách khác, vùng nhớ được canh lề theo từng 2 byte một). Tại các dòng 28 và 29, biến đếm được khởi tạo giá trị là 5 (vì chúng ta dịch chuyển LED sáng từ RE0 cho đến RE5, qua 5 khoảng). Phần khác biệt chính là chương trình xử lý ngắt, vì chúng ta dùng giải pháp và kiểu dữ liệu khác, do đó chương trình xử lý cũng phải khác. Trước tiên, tôi sẽ giải thích đôi chút về thuật toán. Dựa vào cờ chỉ hướng dịch, chúng ta biết được sẽ thực hiện dịch trái hay dịch phải, chúng ta sẽ giảm biến đếm số lần dịch đi 1 sau mỗi lần dịch, và dựa vào biến đếm này để biết đã đến lúc đổi hướng dịch hay chưa. Nếu cần thực hiện đổi hướng dịch thì chúng ta cũng cần đặt lại giá trị khởi động cho biến đếm ở hướng dịch mới. Cụ thể trong chương trình xử lý ngắt của ví dụ 2-3, dòng 47 thực hiện xóa cờ ngắt, là việc cần làm ngay khi vào chương trình xử lý ngắt. Chúng ta kiểm tra xem có phải đang dịch trái hay không ở dòng 48, và sẽ chuyển sang đoạn chương trình dịch phải nếu cờ chỉ hướng là dịch phải bằng lệnh rẽ nhánh ở dòng 49. Tại dòng 50, chúng ta gặp lệnh số học dec dùng để giảm giá trị của biến đếm đi 1 đơn vị, và dựa vào giá trị của biến đếm này để kiểm tra xem có cần đổi hướng dịch hay không ở dòng 51. dsPIC cung cấp các lệnh tăng (inc) hoặc giảm (dec) giá trị của các thanh ghi nói chung hay các thanh ghi Wn. Ngoài ra, còn có các lệnh tăng/giảm từng 2 đơn vị là inc2 và dec2, chấp nhận các toán hạng giống như lệnh inc hoặc dec. Ở dòng 51 (cũng như ở dòng 58), chúng ta thấy có lệnh rẽ nhánh có điều kiện bra NZ, left_s (hay bra NZ, right_s), đây là một lệnh thuộc nhóm lệnh rẽ nhánh có điều kiện. dsPIC cung cấp khá nhiều các lệnh rẽ nhánh có điều kiện (20 lệnh), giúp người lập trình dễ dàng hơn trong việc thể hiện các thuật toán. Vì chúng ta thực hiện giảm biến đếm trước, nên dù đã đếm đến 0, chúng ta vẫn thực hiện dịch trái sau khi đã đặt lại cờ chỉ hướng dịch và biến đếm (bằng các lệnh ở dòng 52 và 53). Sau khi thực hiện dịch trái bằng các dòng 54 và 55, chương trình xử lý ngắt sẽ trở về chương trình chính.
  16. Người báo cáo: Nguyễn Quang Nam Tài liệu: TUT02.01 Ngày: 5/3/2006 Trang: 16/19 Tương tự như vậy, bạn có thể dễ dàng hiểu được cách hoạt động của đoạn chương trình thực hiện dịch phải từ dòng 57 đến 63. Và dẫn hướng .end tại dòng 64 luôn luôn cần thiết đối với một tập tin nguồn hợp lệ. Tất nhiên bạn có thể dùng ngay một thanh ghi Wn nào đó để dùng làm biến đếm, nhưng ở đây tôi muốn minh hoạ cách dùng biến trong RAM của dsPIC, do đó chương trình đã viết có vẻ dài dòng một cách không cần thiết. 3.4. Ví dụ 2-4 Bạn hãy thực hiện các bước tương tự như trong ví dụ 2-2 để loại bỏ tập tin nguồn “Vidu2-3.s” ra khỏi project, và thêm vào project tập tin nguồn “Vidu2-4.s”. Tương tự như trong phần 3.3, mã nguồn của ví dụ 2-4 sẽ được liệt kê, nhưng chỉ những phần khác với ví dụ 2-2 là được giải thích. Liệt kê mã nguồn “Vidu2-4.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 6 config __FGS, CODE_PROT_OFF ;------------------------------------------------------------------------- ;Cac hang so cua chuong trinh (gia tri tuc thoi dung trong chuong trinh) 7 .equiv LED_PORT, PORTE ;LED noi vao cong E 8 .equiv LED_TRIS, TRISE ;Thanh ghi trang thai cua port xuat LED 9 .equiv LED_LAT, LATE ;Thanh ghi chot cua port xuat LED ;------------------------------------------------------------------------- ;Cac khai bao toan cuc: 10 .global __reset ;Khai bao nhan bat dau chuong trinh (bat buoc) 11 .global __T1Interrupt ;Khai bao toan cuc chuong trinh xu ly ngat Timer 1 ;------------------------------------------------------------------------- ;Khai bao bang hang so trong ROM 12 .section Table, psv, address(0x0200) ;bang ma hoa cac trang thai ;Bang nay dung cho LED cathode chung ; .hword 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020 ; .hword 0x0010, 0x0008, 0x0004, 0x0002 ;Bang nay dung cho LED anode chung 13 .hword 0xFFFE, 0xFFFD, 0xFFFB, 0xFFF7, 0xFFEF, 0xFFDF 14 .hword 0xFFEF, 0xFFF7, 0xFFFB, 0xFFFD ;------------------------------------------------------------------------- ;Doan ma trong vung nho chuong trinh 15 .text ;Bat dau doan ma chuong trinh
  17. Người báo cáo: Nguyễn Quang Nam Tài liệu: TUT02.01 Ngày: 5/3/2006 Trang: 17/19 __reset: 16 mov #__SP_init, W15 ;Khoi tao con tro ngan xep (stack) 17 mov #__SPLIM_init, W0 18 mov W0, SPLIM ;Khoi tao thanh ghi gioi han con tro ngan xep (stack) 19 nop ;Can mot lenh NOP sau khi ghi vao SPLIM 20 clr W0 ;Xoa thanh ghi lam viec W0 21 mov W0, W14 ;Xoa cac thanh ghi W1 den W14 22 repeat #12 23 mov W0,[++W14] ;Dia chi cua cac thanh ghi: 0x0002 - 0x001C 24 clr W14 ;------------------------------------------------------------------------- 25 rcall Init_TMR1 ;Khoi tao cho TMR1 (tran moi 1 giay) 26 rcall Init_PORTS ;Khoi tao cac cong I/O, W0 dung lam bien trang ;thai cua cac LED 27 bset CORCON, #PSV ;Cho phep dung Program Space Visibility (PSV) 28 mov #psvpage(Table), W0 ;Lay dia chi trang cua bang du lieu 29 mov W0, PSVPAG ;va ghi vao thanh ghi trang PSV 30 mov #psvoffset(Table), W1 ;W1 chua dia chi offset cua bang du lieu 31 mov W1, W2 ;W2 la dau bang du lieu 32 add W1, #(10*2), W3 ;W3 la vi tri ngay sau bang du lieu main_loop: 33 nop 34 nop 35 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 1 giay tai muc xung 8 Mips (fcy = 8 MHz) ;------------------------------------------------------------------------- Init_TMR1: 36 clr TMR1 37 mov #0x7A12, W0 ;TMR1 tran moi 1 giay 38 mov W0, PR1 ;Dat nguong tran vao PR1 39 bclr IFS0, #T1IF ;Xoa co ngat cua TMR1 40 mov #0x8030, W0 ;TMR dung fcy lam clock, prescale la 1:256, 41 mov W0, T1CON ;va khoi dong TMR1 42 bset IEC0, #T1IE ;Cho phep ngat khi TMR1 tran 43 return ;------------------------------------------------------------------------- ;Chuong trinh con khoi tao cac cong I/O, de noi voi LED ;------------------------------------------------------------------------- Init_PORTS: 44 clr LED_LAT ;Xoa thanh ghi chot cong xuat LED 45 mov #0xFFC0, W0 ;LED noi vao cong E (RE0 .. RE5) 46 mov W0, LED_TRIS 47 return ;------------------------------------------------------------------------- ;Trinh phuc vu ngat cho TMR1 ;Moi 1 giay ngat nay duoc thuc thi. No se di chuyen cac LED noi vao port E ;------------------------------------------------------------------------- __T1Interrupt: ;Ten nay da duoc dinh truoc trong tap tin lien ket 48 bclr IFS0, #T1IF ;Truoc tien, can xoa co ngat 49 mov [W1++],W0 ;Lay trang thai can hien thi+dieu chinh con tro 50 mov W0, LED_LAT ;va hien thi 51 cpsgt W3, W1 ;Da het bang chua? 52 mov W2, W1 ;Dung, tro ve dau bang
  18. Người báo cáo: Nguyễn Quang Nam Tài liệu: TUT02.01 Ngày: 5/3/2006 Trang: 18/19 53 retfie ;Tro ve tu ngat ;------------------------------------------------------------------------- 54 .end ;Ket thuc phan ma trong tap tin nay Với các dòng 12 đến 14, chúng ta khai báo một bảng hằng số trong ROM tên là Table, được truy xuất thông qua một cơ chế đặc biệt của dsPIC, được gọi là PSV (Program Space Visibility). Ở đây chúng ta tạm hiểu là một cửa sổ nhìn vào không gian bộ nhớ chương trình được mở ra, cho phép chúng ta dùng một trong những thanh ghi Wn làm con trỏ đến bảng dữ liệu và truy xuất bằng kiểu định vị gián tiếp thanh ghi như chúng ta vẫn làm với các dữ liệu 16-bit trong RAM. Các bước chuẩn bị cho việc sử dụng PSV được minh họa bằng các dòng 27 đến 30. Trước hết, chúng ta cần đặt (set) bit PSV trong thanh ghi CORCON, như dòng 27. Sau đó, chúng ta dùng toán tử psvpage() để lấy địa chỉ trang của bảng dữ liệu đặt và vào trong thanh ghi trang PSV (dòng 28 và 29). Sau cùng, chúng ta dùng toán tử psvoffset() để lấy địa chỉ offset của bảng dữ liệu, đặt vào một trong các thanh ghi Wn để dùng làm con trỏ truy xuất bảng dữ liệu sau này, minh hoạ bằng dòng 30. Ở các dòng 31 và 32, chúng ta xác định hai địa chỉ đầu và cuối của bảng dữ liệu. Bạn chú ý là ở dòng 32 chúng ta có một lệnh số học hơi đặc biệt, có 3 toán hạng. Lệnh này sẽ cộng toán hạng thứ nhất với toán hạng thứ hai (là một hằng số lưu trong 5-bit, nghĩa là phải nhỏ hơn 32), sau đó lưu kết quả vào toán hạng thứ ba. Bây giờ chúng ta hãy xem chương trình xử lý ngắt của chúng ta ra sao với cách dùng kỹ thuật bảng này. Dòng 48 hẳn là không cần phải giải thích nữa. Vì dữ liệu xuất đã được chuẩn bị sẵn trong bảng, chúng ta chỉ cần truy xuất bằng một lệnh sao chép dữ liệu dùng kiểu định vị gián tiếp thanh ghi (dòng 49), sau đó xuất ra ngõ ra (dòng 50). Con trỏ đã được cập nhật ngay trong lệnh truy xuất dữ liệu ở dòng 49. Chúng ta chỉ còn cần kiểm tra xem đã xuất hết bảng dữ liệu hay chưa bằng cách kiểm tra con trỏ so với vị trí cuối bảng. Vì con trỏ được cập nhật sau khi truy xuất dữ liệu, nên chúng ta phải kiểm tra địa chỉ ngay sau bảng dữ liệu, thay vì là địa chỉ cuối bảng dữ liệu, tại dòng 51. Nếu con trỏ nhỏ hơn địa chỉ này, chúng ta không cần đưa con trỏ về đầu bảng, ngược lại, chúng ta đặt lại giá trị cho con trỏ để nó chỉ đến đầu bảng dữ liệu, như dòng 52 đã minh họa. Sau khi hoàn tất các nhiệm vụ, chương trình xử lý ngắt sẽ trở về chương trình bị ngắt (ở đây là chương trình chính) với dòng 53. Dòng 54 đóng lại mã nguồn của chương trình. Để đánh giá tầm quan trọng của kỹ thuật bảng này, bạn hãy nghĩ xem nếu không dùng kỹ thuật này thì bạn sẽ thực hiện việc hiển thị các mẫu phức tạp, ví dụ cho các LED sáng từ ngoài vào trong hay từ trong ra ngoài, hay các mẫu ngẫu nhiên ra sao. Một điều cần chú ý là các chương trình ví dụ ở đây hoàn toàn không phải là tối ưu. Do đó, sau khi làm quen, sửa lỗi (nếu có) và biên dịch thành công các ví dụ này, bạn hãy suy nghĩ thêm về các phương án sửa đổi các ví dụ, thử thực hiện các sửa đổi và quan sát các thay đổi có thể có, đánh giá các ảnh hưởng của các sửa đổi đó. Cách thực
  19. Người báo cáo: Nguyễn Quang Nam Tài liệu: TUT02.01 Ngày: 5/3/2006 Trang: 19/19 hành như vậy sẽ giúp bạn hiểu rõ hơn về các khía cạnh khác nhau của việc lập trình hợp ngữ cho dsPIC. 4. Tóm tắt Trong tutorial này, thông qua các ví dụ về hiển thị LED, một khung cơ bản của một chương trình hợp ngữ viết cho dsPIC đã được trình bày. Các kỹ thuật thiết lập ngăn xếp, đặt cấu hình cho chip, khai báo các vùng nhớ, khai báo chương trình chính, chương trình con, chương trình xử lý ngắt đã được giới thiệu. Bạn đã được giới thiệu về một số lệnh thuộc các nhóm lệnh: 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. Cách thiết lập và sử dụng cổng vào/ra, cũng như bộ định thời đã được nói đến. Các kiểu định vị thanh ghi, định vị tức thời, cũng như định vị gián tiếp thanh ghi cũng được đề cập. Tôi cũng xin đề nghị là khi bạn lập trình cho vi điều khiển, bạn nên có sẵn các tài liệu tham khảo trong tay (với dsPIC và dùng hợp ngữ thì bạn nên có sẵn trong tay các tài liệu MPLAB ASM30, MPLAB LINK30, and Utilities User’s Guide (DS51317) và dsPIC30F/33F Programmer’s Reference Manual (DS70157), cùng với datasheet của chip đang được sử dụng). Các tài liệu này ngoài những thông tin không thể thiếu mà bạn cần tham khảo, còn có nhiều ví dụ rất trực quan giúp bạn rút ngắn đáng kể thời gian viết chương trình. Chúc bạn thành công!
Đồng bộ tài khoản