Thứ Hai, 3 tháng 12, 2018

CHƯƠNG 8 : LÀM VIỆC VỚI NGẮT


I / CƠ CHẾ HOẠT ĐỘNG CỦA NGẮT :
1 / _ Ngắt 1 cấp :
_Trên PIC 14 , 12 ,10 ,tất cả các ngắt chỉ có 1 cấp ưu tiên . Nghĩa là ngắt nào đang được phục vụ thì không thể bị ngắt bởi 1 ngắt khác xảy ra . Cơ chế sinh mã cho ngắt của CCS như sau : nhảy đến địa chỉ ngắt , thường là 004h , sao lưu thanh ghi W, STATUS , PCLATCH , FSR, và nhiều thứ vớ vẫn khác, sau đó nó mới hỏi vòng xem cờ ngắt nào xảy ra thì nhảy đến hàm phục vụ ngắt đó . thực hiện xong thì phục hồi tất cả thanh ghi trên , rồi mới “RETFIE” – thoát ngắt . Số chu kỳ thực thi từ chỗ ngắt đến khi nhảy vào hàm ngắt cỡ 20 chu kỳ lệnh !, nhảy ra cũng cỡ đó .
_Điều gì xảy ra nếu chương trình dùng nhiều ngắt và khicó ngắt thì có 2 ngắt trở lên xảy ra đồng thời ? Nghĩa là : 2 ngắt xảy ra cùng lúc , hay khingắt A kích hoạt và CCS đang lưu các thanh ghi ( chưa tới hỏi vòng cờ ngắt ) thì ngắt B xảy ra , dĩ nhiên ngắt B không thể kích vector ngắt nhảy tới 004h vì bit cho phép ngắt toàn cục ( GIE ) bị khóa tự động khi có ngắt , chỉ có cờ ngắt B bật mà thôi. Sau khi lưu các thanh ghi , chương trình kiểm tra cờ ngắt , rõ ràng là nếu bit nào được kiểm tra trước thì phục vụ trước , dù nó xảy ra sau . Để tránh phục vụ không đúng chỗ , bạn dùng #priority để xác định ưu tiên ngắt ( xem phần chỉ thị tiền xử lý) . Ngắt ưu tiên nhất sẽ luôn được hỏi vòng trước .Sau khi xác định cờ ngắt cần phục vụ , nó sẽ thực thi hàm ngắt tương ứng .Xong thì xoá cờ ngắt đó và thoát ngắt . Phục vụ ngắt nào xong thì chỉ xoá cờ ngắt đó .Nếu A ưu tiên hơn B thì sau khi làm A , chương trình xoá cờ ngắt A , nhưng cờ B không xoá ( vì đâu có phục vụ ) , nên khi thoát ra ngắt A , nó sẽ lại ngắt tiếp ( vì cờ B đã bật ), lại hỏi vòng cờ ngắt từ đầu : nếu cờ A chưa bật thì xét B, lúc này B bật nên phục vụ B , xong thì xoá cờ B và thoát ngắt .
_Môt chương trình dùng nhiều ngắt phải lưu ý điều này , tránh trường hợp : ngắt xảy ra liên tục (tràn ngắt ) , 1 ngắt bị đáp ứng trễ , ngắt không đúng , . . .
2 / _ Ngắt 2 cấp :
_Chỉ có trên PIC 18 ( và dsPIC ) . Có 2 khái niệm : ngắt ưu tiên thấp (low priority) và ngắt ưu tiên cao ( high priority ) . 2 vector thực thi ngắt tương ứng thường là 0008h (high) và 0018h ( low ) . Một ngắt thấp đang được phục vụ sẽ bị ngưng và phục vụ ngắt cao ở 0008h nếu ngắt cao xảy ra . Ngược lại , ngắt cao đang xảy ra thì không bao giờ bị ngắt bởi ngắt thấp .
_Nếu viết hàm ngắt bình thường , không đòi hỏi ưu tiên gì thì CCS sinh mã để tất cả hàm ngắt đều là ngắt ưu tiên cao . Quy trình thựchiện ngắt sẽ như ngắt 1 cấp trên . #priority vẫn được dùng . Số chu kỳ thực thi từ 0008h đến khi nhảy vào thực thi hàm ngắt khoảng 30 chu kỳ , xong hàm ngắt tới khi kết thúc ngắt cũng mất khoảng 30 chu kỳ lệnh .
_Để sử dụng ngắt 2 cấp , khai báo #device phải có high_ints=true . Và hàm ngắt nào muốn ưu tiên cao thì thêm FAST theo sau chỉ thị tiền xử lý hàm đó . Lưu ý : chỉ có duy nhất 1 ngắt được ưu tiên cao ,đây có lẽ là hạn chế của CCS , do cách thức sinh mã .
VD :  #int_timer1 FAST
 Void xu_ly ( )    { . . .
}
_Cơ chế sinh mã như sau : có ngắt thấp thì nhảy tới 0018h , sao lưu W, STATUS , FSR0/1/2 ,. . . rồi mới hỏi vòng cờ ngắt thấp . chạy xong hàm ngắt thì phục hồi tất cả và “RETFIE 0 “ . Riêng ngắt 28 cao không sinh mã sao lưu gì cả mà nhảy thẳng vào hàm ngắt chạy luôn . Vậy thì trật lất rồi ? Mã chạy sai chăng ?
_Thực ra không phải vậy . PIC 18 và dsPIC có cơ chế lưu siêu tốc là FAST STACK REGISTER ( xem datasheet kỹ nhé ) . Khi xảy ra ngắt bất kỳ , W, S , BSR tự động lưu vào thanh ghi trên , PC counter lưu vào stack . xong ngắt thì pop ra . Vấn đề ở chỗ : khi ngắt thấp xảy ra , FAST STACK REGISTER tự động lưu W ,S , BSR , PC -> stack . Trong khi thực hiện hàm phục vụ ngắt thì trường hợp W, S , BSR thay đổi là có thể ( vì vậy mới sao lưu chứ ) . nhưng nếu xảy ra ngắt cao vào thời điểm đó ? FAST STACK REGISTER sẽ bị ghi đè -> mất data . Do đó , cơ chế sinh mã của CCS cần phải luôn đúng , nghĩa là : luôn tự sao lưu riêng W ,S , BSR, và các thanh ghi FSR nữa , khi thực thi ngắt thấp . Còn ngắt cao khi chạy xong sẽ “RETFIE 1 “ – tự động phục hồi W, S , BSR từ FAST STACK REGISTER. Có 2 trường hợp : 1 là chỉ có ngắt cao , thì không có vấn đề gì . 2 là ngắt cao ngắt 1 ngắt thấp đang chạy . Phân tích sẽ thấy rằng cho dù bị ngắt trong khi đang sao lưu ,hay chưa kịp sao lưu , hay đã sao lưu vào các biến riêng rồi , cuối cùng chương trình cũng quay ra đúng địa chỉ
ban đầu với các thanh ghi W, S , BSR như cũ .
_Tuân thủ nguyên tắc ngắt cao thực thi tức thời nên CCS chỉ cho 1 ngắt cao duy nhất bất kỳ hoạt động , nên không sinh mã hỏi vòng , sao lưu thêm gì cả . nếu bạn muốn có nhiều ngắt ưu tiên cao , thì phải tự viết mã riêng thôi ( khi có ngắt cao thì hỏi vòng các cờ ngắt , dùng lệnh ORG chiếm đoạn mã từ 0008h trở đi để viết mã xử lý riêng , trong chương trình không được viết bất kỳ hàm ngắt nào kể cả ngắt thấp mà chỉ viết hàm bình thường , . . . nói chung là tự xử lý hết mọi vấn đề ngắt , phức tạp lắm đấy ) .
II / KHAI BÁO NGẮT :
_Mỗi dòng VDK có số lượng ngắt khác nhau : PIC 14 có 14 ngắt , PIC 18 có 35 ngắt .
_Muốn biết CCS hỗ trợ những ngắt nào cho VDK củabạn , mở file *.h tương ứng , ở cuối file là danh sách các ngắt mà CCS hỗ trợ nó . Cách khác là vào CCS -> View -> Valid interrupts , chọn VDK muốn xem , nó sẽ hiển thị danh sách ngắt có thể có cho VDK đó .
_Sau đây là danh sách 1 số ngắt với chức năng tương ứng : 
#INT_GLOBAL  : ngắt chung , nghĩa là khi có ngắtxảy ra , hàm theo sau chỉ thị này được thực thi , bạn sẽ không được khai báo thêm chỉ thị ngắt nào khác khi sử dụng chỉ thị này . CCS không sinh bất kỳ mã lưu nào , hàm ngắt bắt đầu ngay tại vector ngắt . Nếu bật nhiều cờ cho phép ngắt , có thể bạn sẽ phải hỏi vòng để xác định ngắt nào . Dùng chỉ thị này tương đương viết hàm ngắt 1 cách thủ công mà thôi , như là viết hàm ngắt với ASM vậy . 
  • #INT_AD   : chuyển đổi A /D đã hoàn tất , thường thì không nên dùng
  • #INT_ADOF   : I don’t know
  • #INT_BUSCOL  : xung đột bus
  • #INT_BUTTON  : nút nhấn ( không biết hoạt động thế nào )
  • #INT_CCP1   : có Capture hay compare trên CCP1
  • #INT_CCP2   : có Capture hay compare trên CCP2
  • #INT_COMP   : kiểm tra bằng nhau trên Comparator
  • #INT_EEPROM  : hoàn thành ghi EEPROM
  • #INT_EXT   : ngắt ngoài
  • #INT_EXT1   : ngắt ngoài 1
  • #INT_EXT2   : ngắt ngoài 2
  • #INT_I2C   : có hoạt động I 2C
  • #INT_LCD   : có hoạt động LCD
  • #INT_LOWVOLT  : phát hiện áp thấp
  • #INT_PSP   : có data vào cổng Parallel slave
  • #INT_RB   : bất kỳ thay đổi nào trên chân B4 đến B7
  • #INT_RC   : bất kỳ thay đổi nào trên chân C4 đến C7
  • #INT_RDA   : data nhận từ RS 232 sẵn sàng
  • #INT_RTCC   : tràn Timer 0
  • #INT_SSP   : có hoạt động SPI hay I 2C
  • #INT_TBE   : bộ đệm chuyển RS 232 trống
  • #INT_TIMER0  : một tên khác của #INT_RTCC
  • #INT_TIMER1  : tràn Timer 1
  • #INT_TIMER2  : tràn Timer 2
  • #INT_TIMER3  : tràn Timer 3
  • #INT_TIMER5  : tràn Timer 5
  • #INT_OSCF : lỗi OSC
  • #INT_PWMTB : ngắt cuả PWM time base
  • #INT_IC3DR : ngắt đổi hướng ( direct ) của IC 3
  • #INT_IC2QEI : ngắt của QEI
  • #INT_IC1 : ngắt IC 1
_Hàm đi kèm phục vụ ngắt không cần tham số vì không có tác dụng .
_Sử dụng NOCLEAR sau #int_xxx để CCS không xoá cờ ngắt của hàm đó .
_Để cho phép ngắt đó hoạt động phải dùng lệnh enable_interrupts ( int_xxxx) và enable_interrupts ( global ) .
_Khoá FAST theo sau #int_xxxx để cho ngắt đó là ưu tiên cao, chỉ được 1 ngắt thôi , chỉ có ở PIC 18 và dsPIC .
VD : #int_timer0 FAST NOCLEAR
III / CÁC HÀM THIẾT LẬP HOẠT ĐỘNG NGẮT :
1 / enable_interrupts ( level )
_level là tên các ngắt đã cho ở trên hay là GLOBAL để cho phép ngắt ở cấp toàn cục .
_Mọi ngắt của VDK đều có 1 bit cờ ngắt , 1 bit cho phép ngắt . Khi có ngắt thì bit cờ ngắt bị set =1, nhưng ngắt có họat động được hay không tuỳ thuộc bit cho phép ngắt . enable_interrupts (int_xxx ) sẽ bật bit cho phép ngắt . Nhưng tất cả các ngắt đều không thể thực thi nếu bit cho phép ngắt toàn
cục = 0 , enable_interrupts( global ) sẽ bật bit này .
VD : để cho phép ngắt timer0 và timer1 hoạt động:
enable_interrupts (int_timer0);
enable_interrupts (int_timer1 ) ;
enable_interrupts ( global );   // chỉ cần dùng 1 lần trừ phi muốn có thay đổi đặc biệt
2 / disable_interrupts ( level )
_level giống như trên .
_Hàm này vô hiệu 1 ngắt bằng cách set bit cho phép ngắt = 0 .
_disable_interrupts ( global ) set bit cho phép ngắt toàn cục =0 , cấm tất cả các ngắt .
_Không dùng hàm này trong hàm phục vụ ngắt vì không có tác dụng , cờ ngắt luôn bị xoá tự động .
3 / clear_interupt ( level )
_level không có GLOBAL .
_Hàm này xoá cờ ngắt của ngắt được chỉ định bởi level .
4 / ext_int_edge ( source , edge )
_Hàm này thiết lập nguồn ngắt ngoàiEXTx là cạnh lên hay cạnh xuống .
_source : nguồn ngắt . Trên PIC 18 có 3 nguồn ngắt trên 3 chân EXT0 , EXT1 , EXT2 ứng với
source = 0 ,1 , 2 . Các PIC khác chỉ có 1 nguồn EXT nên source = 0 .
_edge : chọn cạnh kích ngắt , edge = L_TO_H nếu chọn cạnh lên ( từ mức thấp chuyển lên mức
cao ) hay H_TO_L nếu chọn cạnh xuống .
IV / CÁC CHƯƠNG TRÌNH VD VỀ NGẮT :
1 / _ #INT_RB :
_Sau đây là 1 chương trình điển hình về sử dụng ngắt khi có sự thay đổi trên chân B4-B7 .
_Mô tả : mỗi khi nhấn nút bất kỳ trên B4-B7 , sẽ kích ngắt RB , hàm phục vụ ngắt có tên RB_LED được thực thi , hàm này đơn giản là xuất ra LED ở vị trí tương ứng nhưng trên portD từ D4 – D7 .
_VDK là 16F877 .
                                                                                               
#include < 16F877.h >
#device PIC16F877 *=16
#use delay (clock = 20000000 )  //thêm khai báo này nếu ctrình có dùng hàm delay,OSC=20 Mhz
#byte portb = 0x06      //tạo tên danh định portb thay thế địa chỉ portB là 06h
#byte portd = 0x08      //tạo tên danh định portd thay thế địa chỉ portD là 08h 
#INT_RB
Void RB_LED ( )      // hàm phục vụ ngắt
{
portd=portb;
}
void main ( )
{  set_tris_b ( 0xF0 ) ;    // portB = 11110000 , B4-B7 là ngõ vào , B0-B3 là ngõ ra
set_tris_d ( 0x00 ) ;    // portD = 00000000 , D0-D7 đều là ngõ ra
enable_interrupts ( INT_RB ) ;  // cho phép ngắt RB
enable_interrupts ( GLOBAL ) ;  // cho phép ngắt toàn cục
// do chương trình không làm gì khác ngoài việc chờ ngắt nên vòng while này trống không
while( true )
{ //có thể thêm mã xử lý ở đây . . .
}
} //main 

Không có nhận xét nào:

Đăng nhận xét

Bài đăng mới nhất

Valdes Fernando - Microcontrollers Applications With Pic

Bài đăng phổ biến