Đây là phần mềm rất hữu ích trong việc lập trình vi điều khiển PIC. Việc phát triển các trình ứng dụng lớn và chuyên nghiệp thì các trình biên dịch như CSS, Keil C chưa phù hợp. Vì thế Phần mềm MPLAB C18 sẽ giúp giải quyết các vấn đề đó một cách tốt nhất. 70 trang tài liệu sẽ giúp bạn học cách lập trình với phần mềm này một cách khá nhuần nhuyễn.
Trang 1Mở đầu
Sau nhiều năm có mặt tại Việt Nam, hiện nay dòng vi điều khiểnPIC vẫn thể hiện ưu thế của nó Từ các ứng dụng nhỏ tới các ứng dụnglớn ta đều có thể chọn loại PIC phù hợp với ứng dụng của mình mà giáthành vẫn phù hợp
Ở Việt Nam hiện nay phát triển các ứng dụng thường sử dụng các
trình biên dịch ngôn ngữ C cho vi điều khiển như CSS, Keil C
compiler… Bởi vì lí do đơn giản là các hàm, các thư vi ện mà các trình
dịch này cung cấp tương đối đầy đủ và khá dễ sử dụng Tuy nhiên việcphát triển các trình ứng dụng lớn và mang tính chuyên nghi ệp hơn thìcác trình dịch này hầu như vẫn chưa mang lại 1 đặc tính nữa là trìnhdịch này không phải do chính hãng sản xuất phần cứng Microchipcung cấp do vậy tính mới và tính chuyên nghiệp sẽ không được cao
như chính do nhà sản xuất cung cấp
MPLAB là môi trường lập trình tích hợp do chính Microchip cung
cấp, đi kèm với nó ngôn ngữ lập trình phù hợp với từng dòng vi điềukhiển Pic16 có ngôn ngữ C16, Pic 18 có ngôn ngữ C18, Pic 30 có
C30…Theo tôi thấy các thư viện hàm, các ví dụ ứng dụng do
Microchip cung cấp khá đầy đủ và chuyên nghiệp Hiện nay việc pháttriển các ứng dụng trên nền MPLAB ở nước ta vẫn chưa phổ biến Vớimong muốn góp 1 phần nhỏ và rút ngắn thời gian học tập của các bạnmới làm quen với môi trường này Trong tài liệu này tôi xin giới thiệunhững nét chính về ngôn ngữ C18 và các ứng dụng trên dòng Pic18 sửdụng ngôn ngữ này
Trang 2M c l c
P hần I : Ngô n Ng ữ C1 8 3
1 K i ể u dữ l i ệ u v à các g i ới hạn 3
2 Sự m ở r ộn g c ủa C1 8 9
3. #pragm a s ect i ont ype 11
4 #P ragm a i nt errupt l o w và #P ragm a i nt errupt 17
5 #P ragm a t m pdat a [ s e ct i on - nam e] 26
6 #pragm a varl oc at e ba nk vari a bl e nam e và #pragm a v arl ocat e " s ect i on -nam e" vari abl e - n am e Error! Bookmark not defined 7 #pragm a conf i g 31
8 P roces s or - Speci f i c Header F i l es 33
9 Processor-Specific Register Definitions Files 37
P hần I I : Lập t rì nh P I C18 b ằng M P L AB C1 8 38
1 Sử d ụng M P L AB 38
1.1 Q uản l í P roj ect 38
1.2 Các b ư ớ c t ạo ra f i l e.he x 40
1.3 Cửa s ổ t i ệ n í ch s ử d ụ ng t ro ng M P LAB 54
2 Ví dụ l ậ p t rì nh P i c18 b ằng C18 55
2.1 I / O cơ b ả n và del ay 56
2.2 RES ET 63
2.3 Sl eep 70
Trang 3Phần I: Ngôn Ngữ C18
Ngôn ngữ này được xây dựng trên nền ngôn ngữ C cơ sở Chính vìvậy đối với những ai đã quen câu lệnh lập trình C rồi thì việc nắm bắt
và sử dụng nó sẽ trở nên đơn giản hơn rất nhiều
1 Kiểu dữ liệu và các giới hạn
Giống như ngôn ngữ lập trình C cơ sở và các ngôn ngữ lập trìnhkhác kiểu dữ liệu là cơ sở trong lập trình C18 có các kiểu dữ liệu sau:
• Kiểu số nguyên: bảng sau trình bày các kiểu số nguyên ( tên, độ
rộng, giới hạn max-min )sử dụng trong C18
Kiểu char là kiểu mặc định trong C18 nếu ta ko khai báo kiểu dữ liệu
phía trước
Hình 1.1
Trang 4• Kiểu dấu phảy động
C18 hỗ trợ khai báo 2 kiểu dấu phảy động là float và double với chitiết về độ động, giới hạn min-max ( hình 1.2)
Ví dụ: ta khai báo
C18 lưu dữ liệu kiểu endianness
Endianness là khái niệm cơ bản trong điện tử mà ta đã làm quentrong cấu trúc máy tính: nó có nghĩa là lưu những byte ít quan trọngnhất ở vùng địa chỉ thấp nhất, và ngược lại những byte quan trọng nhấtthì ở vùng cao nhất
Ví dụ: trong trình dịch C18 ta có đoạn mã sau
Hình 1.2
Trang 5Ta có thể thấy rằng biến l có byte DD thấp nhất thì được lưu vàotrong vùng nhớ thấp nhất, byte AA cao nhất thì được lưu vào vùng nhớcao nhất
Các lớp của vùng lưu trữ
MPLAB C18 hỗ trợ các lớp lưu trữ chuẩn ANSI như auto, extern,
register, static và typedef Chúng ra nhắc lại 1 chút về các chuẩn lưu
trữ này:
khi 1 hàm thực thi xong nó sẽ mất
là biến được khai báo kiểu extern trong file1.* có thể sử dụng
trong file2.* khi trong file2.* có l ệnh #include<file1.*>
Trang 6x=1234; // sử dụ n g x m à ko c ần kh ai báo l ại
dụng nhiều và vì thế biến đó được lưu trong thanh ghi v ới mục
đích làm truy cập dữ liệu nhanh Khai báo này chỉ được sử dụng
trong biến formal của hàm và biến auto, nếu khai báo biến extern
dạng này thì trình dịch sẽ bỏ qua và coi như không có khai báo
register
register int x; // kh ai báo n à y t rình dịch
register char c; // sẽ coi nh ư ko có k hai b áo regist er
tron g b iến fo rmal
{
}
chỗ là lifetime (thời gian tồn tại) trong toàn bộ thời gian chạy
của chương trình Nhưng lại khác so với biến extern ở chỗ là nó
được cấp phát tĩnh (allocated statically) còn biến extern được
Trang 7trình, tức là biến đó được cấp phát vùng nhớ trước khi chương trìnhcủa ta được chạy Còn ngược lại, cấp phát động thì biến được cấp phát
bộ nhớ trong khi thực hiện chương trình Để trực quan ta xem ví dụ:
Ngoài các định nghĩa vùng lưu trữ chuẩn ở trên MPLAB còn cung
cấp thêm 1 khai báo lưu tr ữ nữa là overlay Kiểu này chỉ áp dụng khi trình dịch hoạt động trong chế độ Non-extended và chỉ áp dụng cho biến địa phương (biến auto) Như ở trên tôi trình bày phần biến static,
vấn đề gặp phải của biến x là khi gọi lại hàm biến x giữ lại giá trị
Trang 8được tính toán (tức là ko khởi tạo lại giá trị =0 khi gọi hàm) nhưng đôi
khi ta không muốn điều đó, vì thế biến kiểu overlay chính là giải quyết vấn đề này Tóm lại biến kiểu overlay là biến cấp phát tĩnh nhưng khi
gọi hàm thì nó sẽ được khởi tạo lại Ta xem ví dụ dưới sẽ hiểu rõ:
Ta cũng có section ( đo ạn dữ liệu lưu ở trong bộ nhớ) có thể khai
báo đặc tính kiểu overlay Tác dụng của khai báo này như th ế nào tôi
sẽ nhắc lại phía dưới
MPLAB C18 giới thiệu các qualifier far, near, rom, ram Phạm vi và
vùng nhớ dựa trên Qualifiers theo bảng sau:
Trang 9Ví dụ:
Nếu ta khai báo:
near ram char var_mta // có nghĩa là biến var-mta lưu trong bộ nhớ ram có thể truy cập được.
Tới đây lại xuất hiện khái niệm mới về như thế nào là bộ nhớ có thểcập được và không thể Để hiểu rõ khái niệm này các bạn có thể đọc
trong cuốn PICmicro™ Mid-Range MCU Family Reference Manual Có
thể download tại đây
thường
Tóm lại: ta có thể hiểu điều này giống như lập trình C thông
thường ta có struct lồng vào trong union nhưng sự khác biệt là
struct ở đây có thể nặc danh ( tức là ko tên) Việc truy cập tới các biến trong union như cấu trúc C thông thường Thể hiện ở ví
dụ sau:
Trang 10Nhúng code assembly vào trong chươ ng trình
MPLAB C18 cung cấp trình dịch assembler sử dụng cú pháp giống vớitrình dịch assembler MPASM Để sử dụng đoạn mã assem ta phải khai
báo như sau:
_asm [lab el :] [<instr uct io n> [ar g1[ , a rg2[ , a r g3] ] ] ]
• Ko có mặc định toán hạng mà chúng phải ghi rõ trong lệnh, nếu
ko khi dịch chương trình sẽ báo lỗi
• Hệ số mặc định là cơ số 10
• Sử dụng các ký hiệu của C ví dụ: số hexa phải được viết là
0x1234 chứ ko phải là H ‘1234’ như assem thông thường
• Sau label có dấu ‘:’
Trang 11• Cú pháp định địa chỉ theo chỉ số không được hỗ trợ ở đây (nghĩa
là không được dùng []) mà phải chỉ rõ bit truy cập Ví dụ như assem thường ta viết CLRF[2] nhưng giờ phải viết CLRF 2,0
Cú pháp và tất cả các luật nhúng code assem trong code C v ừa trìnhbày ở trên được thể hiện ở ví dụ sau:
[ a t t r i b u t e - l i s t ] : là thuộc tính của section tôi sẽ nói r õ ở phần dưới
s e c t i o n - n a m e : tên của section ( do ng ười lập trình đặt)
[ = a d d r e s s ] : địa chỉ đầu tiên của section
Tất cả thuộc tính, tên, địa chỉ của section có thể có hoặc ko (đặttrong dấu ngoặc vuông), nếu khi lập tr ình không chỉ rõ (specified) thì
Trang 12trình dịch sẽ tự động làm thay Tôi sẽ trình bày chi tiết từng phần ở
section cho mỗi kiểu bộ nhớ:
• Bộ nhớ chương trình
qualifier rom)
• Bộ nhớ dữ liệu
1 section absolute là section có địa chỉ rõ ràng thông qua khai báo
=address.
1 section assigned được gán là section đặc trưng với khai báoSECTION
1 section unassigned không phải là section absolute và assigned
Nghe có vẻ khó hiểu Ví dụ sau thể hiện các đối tượng được đặttrong bộ nhớ như thế nào để bạn có thể hiểu được sự khác nhau củacác đoạn dữ liệu:
Trang 14ii idata bar_ic idata
Tới đây ta có thể hiểu được các kiểu bộ nhớ của Pic18 mà ta có thểchủ động lưu trữ dữ liệu vào trong đó qua các khai báo như trên
Khá là mệt khi ta phải nắm chắc được những điều này, với những
chương trình nhỏ đôi khi điều đó là không cần thiết nhưng thực
hiện những project lớn thì điều đó là cần thiết và nhất là nắm
được những điều này mới hiểu được các đoạn mã gốc mà
Microchip cung cấp cho chúng ta vì thế mới sử dụng được tốt
OK, let’s go…
• Các section mặc định
ở trên phần khai báo #pragma tôi có nói tên section có th ể ko được
khai báo bởi người lập trình Nếu điều đó xảy ra trình dịch sẽ tự động
đặt tên cho section phụ thuộc vào đó là kiểu section nào theo bảng
filename: là tên file của ta
Ví dụ: nếu tên file chương trình của ta là first_pro và kiểu section làcode thì đoạn mặc đinh sẽ có tên là code_first_pro
Trang 15• Thuộc tính (attribute) của section
Thuộc tính của section gồm có access và overlay
access: báo cho trình dịch biết rằng section được đặt trong vùng có thể
truy cập của bộ nhớ dữ liệu ( xem chi tiết về như thế nào là bộ nhớ cóthể truy cập trong PIC 18C MCU Family Reference Manual) Biến đặt
trong access section phải được khai báo với từ khóa near Ví dụ:
# p r a g m a u d a t a a c c e s s m y _ a c c e s s
/ * a l l a c c e s s e s t o t h e s e w i l l b e u n b a n k e d * /
n e a r u n s i g n e d c h a r a v 1 , a v 2 ;
overlay: là thuộc tính cho phép section khác đư ợc đặt tại cùng địa chỉ
vật lí Thuộc tính này có thể sử dụng cùng với thuộc tính access.
Có 4 điều kiện phải thỏa mãn để có thể overlay:
1 Mỗi section phải được đặt trong file nguồn khác nhau
2 Cả 2 section phải có tên giống nhau
3 Nếu đoạn 1 được khai báo có thuộc tính access thì đoạn
2 cũng phải khai báo thuộc tính này
4 Nếu gán địa chỉ tuyệt đối cho 1 section, thì section cònlại cũng phải gán địa chỉ tuyệt đối đó
Trang 16địa chỉ được gán sẽ là nơi lưu tr ữ đoạn mã.
Trang 174 #Pragma interruptlow và #Pragma interrupt
Ngắt là 1 phần quan trọng trong vi điều khiển (VĐK) giúp cho
chương trình của chúng ta rẽ nhánh và thực hiện các công việc khác
nhau, khi xảy ra ngắt VĐK con trỏ chương trình sẽ trỏ tới địa chỉ của
chương trình ngắt tương ứng ( địa chỉ của chương trình ngắt tùy thuộc
vào loại ngắt và loại VĐK, khi thực hiện lập trình với VĐK nào thì taxem datasheet của nó là biết) và thực hiện chương trình ngắt được lưu
ở đó, khi thực hiện chương trình ngắt xong nó sẽ trở lại thực hiệnchương trình main() tại địa chỉ lúc nó dừng lại ( địa chỉ này được lưu
trong stack) Vậy ta sẽ xem xét cách khai báo, khai báo mức ưu vàthực hiện chương trình ngắt trong C18 như th ế nào, go on…
#Pragma interrupt fname: khai báo hàm phục vụ ngắt mức ưu tiên
cao (high-priority ISR)
Trang 18#Pragma interruptlow fname: khai báo chương trình phục vụ ngắt
mức ưu tiên thấp(low-priority ISR)
temporary của ISR
[s av e=s a v e - l i s t ] : Sử dụng để lưu các biến, các section Trong đó
s a v e - l i s t là danh sách các biến, các section được lưu
[n os av e=n o s a v e - l i s t ] : sử dụng để chỉ rằng compiler ko cần lưu cácbiến, các section n o s a v e - l i s t là danh sách các biến, các section ko cần
lưu
Ta sẽ tìm hiểu dần dần chi tiết từng đối tượng ở phía dưới
Với khai báo này trình dịch sẽ tạo ra biến kiểu temporary (thế nào
là biến, section kiểu temporaty tôi sẽ chỉ rõ phía dưới ) trong udata
section có tên là fname_tmp Ví dụ:
Trang 19Với chương trình này các biến giành cho chương trình ngắt sẽ được
đặt ở trong udata section có tên là foo_tmp.
Trang 20Như tôi đã nói ở trên biến, section kiểu temporary (nhất thời) tức là
nó chỉ xuất hiện nhất thời trong quá trình thực hiện chương trình Ví
dụ với chương trình sau:
Section kiểu temporary là dùng đ ể lưu lại các biến trung gian trongquá trình tính toán, thực hiên chương trình và các biến tmp_1, tmp_2
trong chương trình là biến kiểu temporary Và section kiểu temporary
là section có thể dùng chung giữa các hàm với nhau ( theo thuật ngữcủa C18 đó chính là section ki ểu overlay mà tôi trình bày ở trên)
Trang 21• ISR Context Saving (lưu trạng thái chương trình ngắt) sử dụng
[save=s a v e - l i s t ]
Cú pháp:
#pragma interrupt isr save=myint, section("mydata") // lệ nh này sẽ lư u biế n ‘ myint’ và section có tên là ’mydata’.
Ok, câu hỏi đặt ra cho chúng ta là t ại sao lại phải lưu trạng thái
chương trình ngắt? Và câu hỏi đầu tiên sẽ là trạng thái chương trình
ngắt là gì?
Trong khoa học máy tính, context đồng nghĩa với trạng thái (state) của
chương trình Nhiều người còn cho rằng nó là tài nguyên của CPU màchương trình đang sử dụng tại 1 thời điểm Tài nguyên này có thể là
các thanh ghi, bộ nhớ và ngoại vi
Tại sao phải lưu trạng thái chương trình ngắt???
Trong thực hiện chương trình có các tài nguyên có đặc tínhvulnerable (tính dễ bị tổn thương, hay nói cách khác là nó d ễ bị thay
đổi giá trị Ở đây tôi xin được giữ nguyên thuật ngữ vulnerable mà ko
dịch ra tiếng việt) Tài nguyên này là loại tài nguyên mà thường dùngchung giữa ISR và code chương tr ình chính Bởi vậy nếu ta ko tiến
hành lưu lại các tài nguyên này trư ớc khi thực hiện code của ISR thì
sau khi thực hiện chương trình nó sẽ thay đổi và những dữ liệu bị thay
đổi này lại được code của chương trình chính thực hiện và như vậychương trình chúng ta sau khi thực hiện ngắt sẽ chạy sai
Ví dụ: tài nguyên có tên là WREG có chức năng lưu những tính toántrung gian Giả sử khi đang thực hiện chương trình chính mà xảy rangắt mà ta ko tiến hành lưu lại WREG thì chương trình ngắt sẽ thay đổigiá trị trong tài nguyên này Vậy khi thực hiện xong chương trình ngắt
Trang 22mà trở lại thực hiện chương trình main thì khi chương trình main đọccác dữ liệu trong này ra để thực hiện tính toán tiếp sẽ là những dữ liệusai Do vậy kết quả cuối cùng là chương trình của chúng ta thực hiệnsai.
Vậy cách giải quyết cho vấn đề này là như thế nào?
Ta có thể ngay rằng muốn chương trình ngắt ISR không làm thay
đổi giá trị các section tmpdata gốc của chương trình chính thì ta phải
có section tmpdata riêng cho nó Mà như tôi đ ã trình bày ở trên khai
báo # p r a g m a i n t e r r u p t sẽ tự động tạo ra section tmpdata cho riêngnó
Bởi vậy nên ta phải tiến hành lưu trạng thái chương trình ngắt
Tài nguyên có tính vulnerability trong thu ật ngữ của C18 chính làtài nguyên quản lí bởi trình dịch (compiler managed resource) Cónghĩa rằng là các tài nguyên này d ễ bị thay đổi trong khi thực hiện
chương trình ngắt làm sai chương trình chính vì thế cần phải quản lí
(với ý nghĩa như vậy theo tôi các loại tài nguyên này có th ể gọi là tài
nguyên được che bởi trình dịch – compiler masked sources) Bảngdưới là danh sách các tài nguyên qu ản lí bởi trình dịch:
Trang 237 thanh ghi đầu tiên được gọi là ‘vital’ có ngh ĩa rằng nó mặc địnhđược lưu lại trước khi thực hiện ngắt, nay nói cách khác chương tr ình
thực hiện ngắt chắc chắn sẽ thay đổi giá trị 7 thanh ghi này
Tiếp theo là các thanh ghi TBLPTR và TABLAT ( các thanh ghi
được sử dụng để truy cập bộ nhớ chương trình) Nếu bạn biết rằng ISR
ko truy cập dữ liệu rom thì bạn có thể bảo vệ trình dịch ko lưu cácthanh ghi này (phía dưới tôi sẽ trình bày cách làm thế nào để ko lưucác dữ liệu nếu ko cần thiết phải lưu)
Giống như vậy, nếu bạn biết ISR ko sử dụng con trỏ hàm, bạn cóthể ko lưu các thanh ghi PCLATH và PCLATU
MATHDATA là 1 section đ ặc biệt được sử dụng bởi thư việc math,ngoài ra nó được sử dụng trong chế độ truyền thống để thực thi những
giao diện hàm Section này nhỏ và thường bạn ko cần lo lắng về nó
Trang 24Cuối cùng là section tmpdata ch ứa những giá trị trung gian chonhững tính toán phức tạp mà tôi đã trình bày như thế nào là sectiontemporary (.tmpdata) ở phần trên Section loại này lớn lên trong quátrình thực hiện chương trình mà chúng ta không kiểm soát được sự lớnlên của nó trong từng thời điểm.
Ta đã thấy rằng tại sao phải lưu trạng thái chương trình ngắt.Nhưng vấn đề đi kèm với lưu trạng thái ngắt sẽ là thời gian phục vụ
ngắt bị tăng lên Bởi vì trước khi thực hiện code ngắt thì phải lưu lạitrạng thái và sau khi thực hiện ngắt lại phải tiến hành khôi phục trạng
thái đã lưu lại đó Công việc này tốn khá nhiều thời gian nếu dữ liệu
cần lưu lớn Đặc biệt là trình dịch v3.0 và phiên bản cao hơn, trìnhdịch nhận dạng tài nguyên vulnerable 1 cách dè dặt(conservative), cónghĩa là nếu nó ko chứng mình được tài nguyên đó là tài nguyênvulnerable hay ko thì nó s ẽ lưu tài nguyên đó Ta xem ví d ụ sau:
Trang 25ISR đầu tiên ko gọi hàm khác Bởi vậy trình dịch có thể phân tích
hàm isr và nhận ra các tài nguyên được sửa là các thanh ghi WREG,BSR, STATUS, TBLPTR và TABLAT Vì th ế trình dịch sẽ tự động lưucác thanh ghi này và cũng chỉ lưu các thanh ghi này
Trong chương trình thứ 2, có gọi hàm khác vì thế trình dịch không
biết rằng nó đang và sẽ sửa cái gì trong hàm foo Trong thực tế hàmfoo có thể là module riêng rẽ Bởi vậy trình dịch lưu tất cả tài nguyênquản lí bởi trình dịch (compiler managed resources) Với cơ chế hoạt
động này thì nếu section kiểu tmpdata có dữ liệu lớn thì sẽ tốn nhiều
thời gian lưu và khôi phục vì như tôi đã chỉ ra ở trên rằng section kiểu
này tăng lên trong quá tr ình thực hiện chương trình và ta ko kiểm soátđược điều này
Trang 26Vậy nhiệm vụ tiếp theo của ta chính là phải tối ưu quá trình lưu dữliệu, tức là lưu những dữ liệu cần thiết còn những dữ liệu ko cần thiếtthì ko lưu Nó được thực hiện bằng [nos av e=n o s a v e - l i s t ]
• Chỉ rõ biến và dữ liệu ko cần lưu trong trạng thái ngắt bằng
[nosave=n o s a v e - l i s t ]
Như tôi đã trình bày ở trên khai báo này sẽ chỉ ra rằng các biến và
section trong n o s a v e - l i s t sẽ ko được lưu
#pragma interrupt foo nosave=TBLPTR, TABLAT // khai báo này sẽ ko lư u
2 thanh ghi TBLPTR, TABLAT, bở i vì 2 thanh ghi này ko đư ợ c sử dụ ng trong chư ơ ng trình main() và nó chỉ sử dụ ng trong chư ơ ng trình ngắ t vì thế ko cầ n lư u lạ i (ko
cầ n bả o vệ )
Với ngắt mức ưu tiên cao (khai báo b ằng #pragma interrupt) các vị trítài nguyên có thể không cần lưu bảo vệ là: FSR0 , TBLPTR , TBLPTRU , TABLAT ,
PCLATH , PCLATU , PROD , section(".tmpdata") , or section("MATH_DATA")
Với ngắt mức ưu tiên thấp (khai báo bằng #pragma interruptlow) các
vị trí tài nguyên có thể là không cần lưu bảo vệ là những tài nguyênvừa kể trên và thêm WREG , BSR , or STATUS
Vấn đề tiếp theo là ta xác đ ịnh section hoặc thanh ghi nào bị thay
đổi, hoặc ko bị thay đổi để có thể save hoặc nosave Chẳng nhẽ ta lại
phải đọc mã máy mà trình dịch tạo ra để xác định xem section nào bị
thay đổi Nếu bắt buộc phải làm công việc đó thì chắc chắn rằng ta
muốn tối ưu tài nguyên th ì phải mất nhiều thời gian, công sức, và nhưvậy có lẽ chẳng ai sử dụng C18 để lập trình Vậy cách giải quyết sẽ là
như thế nào Ta tìm hiểu vấn đề này tiếp sau:
5 #Pragma tmpdata [section-name]
Trang 27Câu lệnh này sẽ thay đổi section thành section lo ại dữ liệu tmpdata
Cú pháp:
#pragma tmpdata [section-name]
[section-name] tên của section dữ liệu mà trình dịch tạo các biếntemporary
Ví dụ:
#pragma tmpdata user_tmp
// câu lệnh bên trong sẽ tạo ra các biến dạng tmpdata lưu trong section có tên làuser_tmp
#pragma tmpdata
Ta xem xét xem vấn đề tôi vừa gợi ý là làm thế nào để xác định
section nào ko thay đ ổi để không cần phải lưu Ta xét ví d ụ sau:
Trang 28Lệnh #pragma tmpdata setporttmp….#pragma tmpdata : sẽ tạothêm 1 section kiểu tmpdata để lưu trữ dữ liệu trung gian khi thựchiện hàm SetPort().
Ở lệnh tiếp theo #pragma interruptlow low_isr nonsave(“.tmpdata”)
tạo ra section tmpdata cho chương tr ình ngắt và bảo vệ section có tên
là ‘setPorttmp’ b ởi chính vì thế mà hàm setPort() có thể được dùng
cho cả chương trình chính mà ko bị lỗi khi thực hiện Hay nói cách
khác section ‘setPorttmp’ đư ợc dùng chung cho cả chương trình ngắt
và chương trình chính
Việc tận dụng tài nguyên ở mọi nơi trong chương trình là vấn đềkhá quan trọng giúp chúng thực hiện được các Project lớn mà khôngtốn nhiều tiền chi cho tài nguyên ( Tài nguyên tôi mu ốn nói tới chính
là bộ nhớ và tốc độ xử lí) Dưới đây ta xem xét cách t ối ưu sử dụng tài
nguyên như thế nào
Tiết kiệm tài nguyên khi xử lí nhiều ngắt mức ưu tiên cao
Trong chương trình có nhiều ngắt ưu tiên cao (#pragma interrupt
), vì các
chương trình ngắt mức ưu tiên cao thì chỉ có thể kích hoạt ở 1 thờiđiểm nên có thể dùng chung các section kiểu tmpdata
Ví dụ:
void increment (int counter);
void isr1 (void);
void isr2 (void);
#pragma interrupt isr1 isr_tmp nosave=section(".tmpdata")
void isr1 (void)
Trang 29void isr2 (void)
#pragma tmpdata isr_tmp
void increment (int counter)
{
}
#pragma tmpdata
Trong chương trình ví dụ trên ta thấy 2 chương trình ngắt mức ưu
tiên cao dùng chung section tpmdata tên là isr_tmp B ởi vì các
chương trình ngắt mức ưu tiên cao ở 1 thời điểm chỉ có thể kích hoạt 1chương trình Ta tưởng tượng rằng nếu ko có đặc tính này khi ngắt
isr1 xảy ra và chương trình phục vụ ngắt của nó đang thực hiện,
chương trình isr1() chạy vẫn chưa xong thì ngắt 2 sẽ được kích hoạtthì section ‘isr_tmp’ b ị thay đổi Như thế sau khi thực hiện xong isr2()chương trình quay lại thực hiện isr1() và sử dụng các giá trị trongsection ‘isr_tmp’ đ ã bị thay đổi chương trình isr1() thực hiện không
chính xác
Vậy với ngắt có mức ưu tiên khác nhau ở trong cùng 1 chương trình
mà chương trình ngắt ở mức ưu tiên thấp hơn thì chúng ta phải xử línhư thế nào để đảm bảo chương trình vẫn tận dụng được tài nguyên và
chức năng chương trình vẫn được đảm bảo chính xác
Ngắt lồng nhau (Nested Interrupts)
Ta xét và phân tích ví d ụ sau:
void increment (int counter);
void isr1 (void);
void isr2 (void);
Trang 30#pragma interrupt isr1 isr_tmp save=section("isr_tmp")
#pragma tmpdata isr_tmp
void increment (int counter)
{
}
#pragma tmpdata
Ta thấy rằng section ‘isr_tmp’ ph ải được lưu lại trước khi thực hiện
chương trình ngắt vì ngắt isr2 là loại ngắt có mức ưu tiên thấp nên
trong khi thực hiện chương trình ngắt isr2() nó có thể bị ngắt đoạn
Đến đây chắc các bạn có thể tự phân tích được tại sao lại phải khaibáo chương trình như thế, tôi ko phân tích rõ chương trình này, các
bạn tự phân tích như ví d ụ trên tôi vừa phân tích sẽ thấy và hiểu rõ
Tip: tới đây bạn tự đặt câu hỏi tại sao lại phải lằng nhằng như
vậy Việc quản lí tài nguyên sao ko đ ể trình dịch tự làm Như tôi
đã phân tích ở trên rằng hiểu rõ được những điều tôi vừa trình
bày sẽ tận dụng được tối đa tài nguyên Gi ả sử ta thực hiện 1Project thì phải làm sao tận dụng tối đa tài nguyên ta có, t ức làlàm sao mà tài nguyên nh ỏ nhất ta dùng để giải quyết được bài
toán đó Như tôi đã nói ở trên tài nguyên ở đây là dung lượng bộ
Trang 31nhớ và tốc độ xử lí chương trình Nếu ta ko hiểu rõ và ko thựchiện được những khai báo để tận dụng tối đa thì có thể ta sẽ phải
dùng VĐK có tài nguyên l ớn hơn, và như thế tốn tiền, và đôi khi
thậm chí là nhiều khi có tiền cũng ko mua được
6.#pragma config
Khai báo này cài đ ặt cấu hình để sử dụng trong các ứng dụng khác
nhau Và có thể sử dụng nhiều khai báo để cấu hình cho thiết bị
setti ng -na m e = valu e-na m e
settin g-n am e = va l ue-na m e: ứng với từng thiết bị, để tra chúng ta sử
dụng tài liệu PIC18 Configuration Settings online help Bạn có thể
dowload tài liệu tại đây
Ví dụ với P IC 18 F2 220 tra trong tài liệu ta có:
Trang 32Dựa vào đó ta cấu hình cho P IC 18 F2 220 như sau:
Trang 337 Processor-Specific Header Files
Là loại file chứa những khai báo cho những thanh ghi chức năng
Trang 34Khai báo đầu tiên nghĩa rằng PORTA là 1 byte ( kiểu unsigned
char) Modiffer extern cần thiết để khi gọi file header trong file khác thì biến mới có hiệu lực, near có nghĩa là biến PORTA được đặt trong
bộ nhớ RAM có thể truy cập Dùng khai báo này đ ể thiết lập giá trịPortA
Ví dụ:
P O R T A = 0 x 3 4 ; / * g á n g i á t r ị 0 x 3 4 t ớ i c ổ n g A * /
Tiếp theo là khai báo thứ 2 là cấu trúc nặc danh mà tôi đã trình bày
ở trên, vậy ta hiểu khai báo này như th ế nào Bạn có thể thấy các nhãnRA0, RA1….; AN0, AN1…giống với nhãn mà trong datasheet ghi Tức
là dùng khai báo này chúng ta có th ể truy cập từng bit trong portA
Ví dụ:
Trang 35Ngoài việc sử dụng tên trực tiếp khai báo đó ta có th ể sử dụng lệnh
header của VĐK tương ứng
Trong các file header còn định nghĩa sẵn cho chúng ta các Macroassembly và ta sẵn sử dụng chúng trong lập trình Danh sách cácMacro và chức năng của chúng tôi liệt kê dưới bảng sau:
Nop()
Không thực hiện gì, chỉ là delay 1 chu kì dao
động thạch anh, thường sử dụng trong viết hàm
delayClrWdt() Clear watchdog timer