Ngắt là một hoạt động khác chen vào một hoạt động đang thực thi mà không biết trước. Đối với chương trình của vi điều khiển, chương trình đang thực thi là chương trình chính. Hoạt động ngắt đã được cài đặt cho phép cho trước, nếu có dấu hiệu ngắt (cờ ngắt bật lên) chương trình chính sẽ tạm dừng và con trỏ chương trình sẽ chỉ đến địa chỉ ngắt để thực hiện chương trình ngắt. Đối với hệ vi xử lý, tại mỗi thời điểm nhất định chỉ có một chương trình được thực thi, các hoạt động giám sát vào ra của các thiết bị, giao tiếp với các thiết bị… được CPU thực hiện bằng cách hỏi vòng tuần tự theo các công việc hành động đã được sắp đặt trước. Như vậy trong một vài trường hợp sẽ ảnh hưởng đến chất lượng của hệ thống do hoạt động hỏi vòng tốn mất nhiều thời gian nếu chương trình lớn, dẫn đến sự trể xử lý các tác vụ cần xử lý. Ví dụ như để phát hiện và cảnh báo các trạng thái cảm biến nhiệt độ và chập mạch điện, CPU sẽ hỏi vòng lần lượt để đưa ra các cảnh báo nếu có sự cố xảy ra, nếu khi CPU đang xử lý tác vụ kiểm tra nhiệt độ hiện đang cao và đang điều khiển quạt để giảm nhiệt độ mà trong lúc đó xảy ra bị chập điện dẫn đến phát nhiệt hỏa hoạn do CPU đang thực thi chương trình giảm nhiệt độ, còn chương trình kiểm tra ngắn mạch chưa đến. Do đó rất cần thiết có một hoạt động cưỡng bức chen vào các hoạt động khác, đó gọi là ngắt. Như ở ví dụ trên, nếu chương trình thực hiện kiểm tra và điều khiển nhiệt độ đặt trong chương trình chính (hỏi vòng), còn chương trình cần xử lý nhanh và ưu tiên thì để trong chương trình ngắt. Nếu đang thực thi điều khiển quạt do nhiệt độ còn cao mà có sự cố ngắn mạch điện xảy ra thì chương trình sẽ tạm dừng chương trình chính điều khiển nhiệt độ và nhảy đến chương trình bảo vệ ngắn mạch điện vì hoạt động bảo vệ ngắn mạch quan trọng hơn. Trong vi xử lý có rất nhiều nguồn ngắt khác nhau, tùy theo yêu cầu cần ngắt nào mà ta chỉ cài đặt và sử dụng nó nếu thấy cần thiết 3.2. Các nguồn ngắt và sơ đồ ngắt Một số ngắt cơ bản trong PIC16F887: - Ngắt ngoài trên RB0 sẽ xảy ra khi có tác động cạnh lên hoặc cạnh xuống trên chân RB0 - Ngắt tràn bộ định thời Timer 0 xảy ra khi thanh ghi TMR0 thay đổi từ 255 về 0 - Ngắt trên sự thay đổi PORTB xảy ra khi có sự thay đổi 1 trong các chân của PORTD - Ngắt chuyển đổi ADC xảy ra khi quá trình chuyển đổi ADC thực hiện xong - Ngắt tràn Timer 1 xảy ra khi thanh ghi giá trị của TMR1L + TMR1H thay đổi từ 65535 về 0 - Ngắt tràn Timer 2 tương tự Timer0 - Ngắt truyền nhận EUSART xảy ra khi truyền hoặc nhận xong 1 byte dữ liệu qua EUSART Từ sơ đồ ngắt trên cho thấy để xảy ra ngắt trên CPU (interrupt CPU) cần phải qua một hệ thống các cổng logic Ngõ ra cuối cùng bằng 1 thì sẽ xảy ra ngắt, trong chương trình ngắt muốn biết được ngắt nào đang xảy ra thì phải kiểm tra xem trước đó đã cho phép ngắt nào và hiện tại cờ nào đang được bật 3.3. Các bit cho phép ngắt và các bit cờ ngắt Như trên đã trình bày, muốn có ngắt xảy ra phải qua hệ thống cổng logic, có các bit đứng trước tại ngõ vào các cổng and có các chức năng khác nhau - GIE_bit (Global Interrupt Enable): Bit cho phép ngắt toàn cục - PEIE_bit (Peripheral Interrupt Enable): Bit cho phép ngắt ngoại vi. Các ngắt qua cổng and với PEIE được gọi là các ngoại vi. Các ngoại vi hoạt động độc lập không phù thuộc vào CPU cho đến khi nó xảy ra sự kiện ngắt - T0IF_bit (Timer0 Interrupt Flag), T0IE_bit (Timer0 Interrupt Enable): bit cờ ngắt và bit cho phép ngắt Timer0, khi có ngắt xảy ra, cờ ngắt sẽ bật lên 1 - INTF_bit (External Interrupt Flag), INTE_bit (External Interrupt Enable): bit cờ ngắt và bit cho phép ngắt ngoài - RBIF_bit (PORTB Change Interrupt Flag), RBIE_bit (PORTB Change Interrupt Enable): bit cờ ngắt và bit cho phép ngắt thay đổi trên PORTB - TXIF_bit (Transmit Interrupt Flag), TXIE_bit (Transmit Interrupt Enable): bit cờ ngắt và bit cho phép ngắt truyền xong 1 byte dữ liệu EUSART. - RXIF_bit (Receive Interrupt Flag), RXIE_bit (Receive Interrupt Enable): bit cờ ngắt và bit cho phép ngắt nhận xong 1 byte dữ liệu EUSART - TMR1IF_bit (Timer1 Interrupt Flag), TMR1IE_bit (Timer1 Interrupt Enable): bit cờ ngắt và bit cho phép ngắt tràn Timer1 - TMR2IF_bit (Timer2 Interrupt Flag), TMR2IE_bit (Timer2 Interrupt Enable): bit cờ ngắt và bit cho phép ngắt tràn Timer2 - ADIF_bit (ADC Interrupt Flag), ADIE_bit (ADC Interrupt Enable): bit cờ ngắt và bit cho phép ngắt chuyển đổi xong kết quả ADC 3.4. Vec tơ ngắt và chương trình ngắt Khi ngắt CPU xảy ra, chương trình chính đang thực thi sẽ tạm dừng, chương trình ngắt sẽ thực thi, chương trình chính sẽ tiếp tục ở đoạn lúc nảy đã bị ngắt khi CPU thực hiện xong chương trình ngắt. Mỗi chương trình được định nghĩa ở một vị trí địa chỉ nhất định gọi là vec tơ. Chương trình chính được bắt đầu ở 0x00, chương trình ngắt bắt đầu ở 0x04 Chương trình của vi điều khiển được chạy như sau: khi bật nguồn hoặc khi bị reset, chương trình sẽ chạy qua các khai báo (thư viện, định nghĩa, biến) sau đó đến chương trình chính (void main()) có địa chỉ 0x00 và sẽ thực hiện hỏi vòng ở vòng lặp vô tận. Nếu có ngắt xảy ra, chương trình chính sẽ tạm dừng và nhảy đến địa chỉ 0x04 và thực hiện chương trình ngắt, thực hiện xong chương trình ngắt sẽ trở về đoạn chương trình chính bị ngắt. Như vậy vec tơ là địa chỉ vùng nhớ chương trình mà CPU sẽ trỏ đến để thực hiện các lệnh bắt đầu ở đó, 0x00 là vec tơ bắt đầu chương trình chính, 0x04 là vec tơ bắt đầu chương trình ngắt. Chương trình ngắt xử lý các nhiệm vụ ngắt với cơ ngắt tương ứng, chương trình ngắt cần viết sao cho việc xử lý diễn ra nhanh chóng để còn trở về lại chương trình chính thực hiện các nhiệm vụ khác 3.5. Ngắt ngoài RB0 Ngắt ngoài RB0 được thiết kế dạng ngắt cạnh, tức là khi có tín hiệu cạnh lên hoặc xuống (tùy theo cài đặt trước) sẽ xảy ra ngắt Để cấu hình cho ngắt này, ngoài cờ ngắt và cho phép ngắt còn phải bật bit cho phép ngắt toàn cục GIE_bit, xác định cạnh ngắt INTEDG_bit bằng 0 nếu ngắt cạnh xuống hoặc bằng 1 nếu ngắt cạnh lên, cấu hình RB0_bit là ngõ vào R/W-0 R/W-0 R/W-0 R/W-0 R/W-0 R/W-0 R/W-0 R/W-x GIE PEIE T0IE INTE RBIE T0IF INTF RBIF Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
R/W-0 R/W-1 R/W-1 R/W-1 R/W-1 R/W-1 R/W-1 R/W-1
!(RBPU) INTEDG T0CS T0SE PSA PS2 PS1 PS0 Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
Cấu hình ngắt RBx:
- Cấu hình ngõ vào cho chân RB0 - Cho phép ngắt toàn cục - Xác định cạnh ngắt INTEDG_bit - Xóa cờ ngắt INTF_bit và cho phép ngắt RB0 bằng cách set INTE_bit Chương trình ngắt: - Kiểm tra cờ ngắt để xác định loại ngắt - Xóa cờ ngắt - Thực hiện nội dung ngắt
Ví dụ của 1 đoạn chương trình ngắt ngoài
void interrupt() { if (INTF_BIT==1)&& (INTE_BIT==1{ // kiểm tra có phải là ngắt ngoài RB0 INTF_BIT=0;// Xóa cờ ngắt ngoài led=1;// nội dung ngắt: bật 1 đèn led khi có ngắt ngoài } } void main() { // cấu hình ngắt TRISB0_bit=1;// RB0 là ngõ vào GIE_BIT=1; // cho phép ngắt toàn cục INTEDG_BIT=1;// ngắt cạnh lên INTF_BIT=0;// Xóa cờ ngắt INTE_BIT=1;// cho phép ngắt ngoài // chương trình lặp vô tận while(1) { } } 3.6. Ngắt thay đổi trên RB (RB On Change Interrupt) Khi có 1 sự thay đổi mức logic trên chân RBx sẽ xảy ra ngắt, CPU sẽ biết được sự thay đổi bằng cách so sánh giá trị cũ của RBx với giá trị hiện tại để phát hiện. Do đó, mỗi lần xảy ra ngắt, yêu cầu người dùng phải đọc/ghi lại giá trị PORTB để lần sau còn biết để so sánh tiếp. Để cấu hình ngắt này, ngoài các cấu hình RBIF và RBIE còn phải cấu hình cho phép ngắt thay đổi trên từng chân cụ thể ở IOCBx. Còn IOC-Bx là bit phát hiện có sự thay đổi mức logic so với trước đó R/W-0 R/W-0 R/W-0 R/W-0 R/W-0 R/W-0 R/W-0 R/W-0 IOCB7 IOCB6 IOCB5 IOCB4 IOCB3 IOCB2 IOCB1 IOCB0 Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 R/W-0 R/W-0 R/W-0 R/W-0 R/W-0 R/W-0 R/W-0 R/W-x GIE PEIE T0IE INTE RBIE T0IF INTF RBIF Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
Cấu hình ngắt RBx:
- Cấu hình ngõ vào cho chân cần ngắt hay PORTB - Cho phép ngắt toàn cục - Cho phép ngắt thay đổi trên các chân bằng cách set IOCBx_bit tương ứng với chân chọn ngắt - Đọc PORTB, xóa cờ ngắt RBIF_bit (lưu ý phải đọc PORTB trước khi xóa cờ ngắt nếu không sẽ xảy ra lỗi) và cho phép ngắt RBx bằng cách set RBIE_bit Chương trình ngắt: - Kiểm tra cờ ngắt để xác định loại ngắt - Đọc PORTB, xóa cờ ngắt (đọc PORTB trước khi xóa cờ ngắt nếu không sẽ xảy ra lỗi) - Thực hiện nội dung ngắt
Ví dụ của 1 đoạn chương trình ngắt RBx
char i; void interrupt() { if((RBIF_BIT==1)&&(RBIE_BIT==1)){ i=portb;// Đọc PORTB trước khi xóa cờ ngắt RBIF_BIT=0;// Xóa cờ ngắt RBx } } void main() { anselh=0; ansel=0;// vô hiệu hóa chức năng analog trên PORTA và PORTB trisb=255; GIE_BIT=1; IOCB=0b11111101;// Cho phép ngắt thay đổi RB0, RB2, RB3, RB4,…., RB7 i=portb; RBIF_bit=0;// Xóa cờ ngắt RBx RBIE_bit=1;// Cho phép cờ ngắt RBx i=0; while(1){ } } 3.7. Bài tập ứng dụng 3.7.1. Bài tập 1 - Kết nối cảm biến với chân RB0, cảm biến phát hiện có vật sẽ lên mức 1 - Lập trình đếm số vật mỗi khi vật đi qua, hiển thị số vật đếm được trên PORTD 3.7.2. Bài tập 2 - Kết nối cảm biến với chân RB0, cảm biến phát hiện có vật sẽ lên mức 1 - Lập trình đếm số vật mỗi khi vật đi qua, hiển thị số vật đếm được trên 1 led 7 đoạn 3.7.3. Bài tập 3 - Kết nối cảm biến với chân RB0, cảm biến phát hiện có vật sẽ lên mức 1 - Lập trình đếm số vật mỗi khi vật đi qua, hiển thị số vật đếm được trên LCD 16x2 3.7.3. Bài tập 3 - Kết nối cảm biến với chân RB2, cảm biến phát hiện có vật sẽ lên mức 1 - Lập trình ngắt thay đổi RB2 để đếm số vật mỗi khi vật đi qua, hiển thị số vật đếm được trên PORTD 3.7.4. Bài tập 4 - Kết nối cảm biến với chân RB3, cảm biến phát hiện có vật sẽ lên mức 0 - Lập trình ngắt thay đổi RB3 để đếm số vật mỗi khi vật đi qua, hiển thị số vật đếm được trên 1 led 7 đoạn 3.7.5. Bài tập 5 - Kết nối cảm biến 1 với chân RB3, cảm biến 2 với chân RB4, cảm biến 3 với chân RB6, các cảm biến phát hiện có vật sẽ lên mức 1 - Lập trình ngắt thay đổi RB3, RB4, RB6 để đếm số vật đi qua trên mỗi cảm biến, hiển thị số vật đếm được trên PORTA, PORTC, PORTD 3.7.6. Bài tập 6 - Kết nối cảm biến 1 với chân RB2, cảm biến 2 với chân RB4, cảm biến 3 với chân RB6, cảm biến 1 phát hiện có vật sẽ lên mức 1, cảm biến 2 và cảm biến 3 phát hiện có vật sẽ lên mức 0 - Lập trình ngắt thay đổi RB2, RB4, RB6 để đếm số vật đi qua trên mỗi cảm biến, hiển thị số vật đếm được trên LCD 3.7.7. Bài tập 7 - Kết nối cảm biến 1 với chân RB0, cảm biến 2 với chân RB4, cảm biến 3 với chân RB6, cảm biến 2 phát hiện có vật sẽ lên mức 1, cảm biến 1 và cảm biến 3 phát hiện có vật sẽ lên mức 0 - Lập trình ngắt ngoài RB0 và ngắt thay đổi RB4, RB6 để đếm số vật đi qua trên mỗi cảm biến, hiển thị số vật đếm được trên LCD 3.7.8. Bài tập 8 - Kết nối 8 cảm PORTB, 4 cảm biến đầu tiên phát hiện có vật sẽ lên mức 1, 4 cảm biến sau phát hiện có vật sẽ lên mức 0 - Lập trình ngắt thay đổi PORTB để đếm số vật đi qua trên mỗi cảm biến, hiển thị số vật đếm được trên LCD 3.7.9. Bài tập 9 Động cơ DC được trang bị endocer có 200 xung/vòng dùng cảm biến quang và khe hở để nhận biết vị trí. Dùng ngắt ngoài để đếm số vòng của động cơ khi chạy 3.7.10. Bài tập 10 Thực hiện như bài 9 nhưng yêu cầu đếm 400 lần trên 1 vòng đối với encoder 200 xung/vòng (gợi ý: dùng vừa ngắt cạnh lên vừa ngắt cạnh xuống)