AVR Atmega16 Ngắt Ngoài | Interrupt - CodeVision

1. Interrupt ( Ngắt trên Atmega16)

Ngắt là một tín hiệu khẩn cấp được gửi đến vi xử lý, yêu cầu tạm ngừng các hoạt động hiện tại để nhảy đến một hàm ngắt thực hiện ngay một nhiệm vụ nào đó, hàm này còn gọi là trình phục vụ ngắt – ISR (Interrupt Service Routine). Khi thực hiện xong nhiệm vụ trong ISR, vi xử lý sẽ quay về thực hiện tiếp các nhiệm vụ đang làm trước đó.

Các tín hiệu dẫn đến ngắt có thể xuất phát từ bên trong vi điều khiển như ngắt Timer/Counter, UART hoặc ngắt do các tác nhân bên ngoài như ngắt báo nhận tín hiệu từ các button, cảm biến…

Vậy tại sao phải dùng ngắt?

2. External interrupt (Ngắt ngoài)

Trong bài này mình sẽ giới thiệu về cách cấu hình và sử dụng chức năng ngắt ngoài (tín hiệu ngắt đến từ bên ngoài vi điều khiển): Ngắt ngoài được sử dụng khi có sự thay đổi tín hiệu đối với các chân ngắt, một cờ ngắt sẽ báo cho biết có ngắt và nhảy đến chương trình con ngắt ngoài.

Atmega16 có 3 ngắt ngoài là INT0 (PORTD.2), INT1 (PORTD.3) và INT2 (PORTB.2).

 - Các thanh ghi liên quan đến ngắt ngoài:

  • MCUCR (MCU Control Register): thanh ghi điều khiển các chế độ ngắt cho ngắt ngoài INT0INT1.
  • 2 bit đầu (ISC00,ISC01) cho INT0 và 2 bit sau (ISC10,ISC11) cho INT1
  • Khai báo điều điện xảy ra interrupt cho INT0 MCUCR |= (1<<0) hoặc MCUCR |= (1<<ISC00)
  • Ta sẽ sử dụng 2 bit ISC11 và ISC10 để cấu hình chế độ ngắt ngoài cho INT1:

Chế độ ngắt ngoài cho INT1

  • Tương tự với các chế độ ngắt ngoài của INT0 (dùng 2 bit ISC01-ISC00):

Chế độ ngắt ngoài cho INT0

  • MCUCSR (MCU Control and Status Register): thanh ghi điều khiển chế độ ngắt cho INT2

  • Khác với INT0 và INT1, ngắt ngoài INT2 chỉ có 1 bit ISC2 để điều khiển chế độ ngắt, cũng vì thế mà nó chỉ có 2 chế độ: Nếu bit ISC2 được đặt bằng 1, INT2 sẽ kích hoạt ngắt theo sườn lên, ngược lại nếu bit ISC2 được đặt bằng 0 thì INT2 sẽ kích hoạt ngắt theo sườn xuống.
  • Xung trên INT2 có độ rộng lớn hơn độ rộng xung được cho trong bảng dưới đây, các xung có độ rộng nhỏ hơn không đảm bảo là có thể tạo ra ngắt.

  • GICR (General Interrupt Control Register): thanh ghi điều khiển ngắt chung. Khi bit thứ 6 của thanh ghi INT1 = 1 thì ngắt ngoài 1 được bật, tương tự với 2 ngắt còn lại.
  • Các bạn để ý 3 bit cuối (INT1 ,INT2 ,INT3) và đây là thanh ghi chọn INT
  • Để bật INT0 các bạn khai báo GICR |= (1<<6) hoặc GICR |= (1<<INT0)

  • GIFR (General Interrupt Flag Register): thanh ghi cờ ngắt chung có 3 bit INTF0, INTF1, INTF2 là các cờ ngắt của INT0, INT1 và INT2. Khi có ngắt trên chân INT1, bit INTF1 sẽ tự động set lên 1, tương tự với INTF0, INTF2.
  • Ta có thể sử dụng các bit INTF1, INTF2, INTF3 để phát hiện ngắt, tuy nhiên điều này là không cần thiết nếu chúng ta cho phép ngắt tự động, vì vậy thanh ghi này thường không được quan tâm khi lập trình ngắt ngoài.

  • Cuối cùng, để tín hiệu ngắt thực sự được kích hoạt, chúng ta cần bật 1 chiếc cầu dao tổng, nó được gọi là thanh ghi điều khiển ngắt toàn cục. Nếu ngắt toàn cục không được bật thì không một tín hiệu ngắt nào được tạo ra cả. Để bật ngắt toàn cục trong IDE CodeVisionAVR, chúng ta dùng lệnh :
    #asm("sei");
  • Ngược lại, để tắt ngắt toàn cục, ta dùng lệnh :

    #asm("cli");

3. Hàm interrupt (Ngắt ngoài)

CodeVison:

interrupt [EXT_INT0] void ext_int0_isr(void) Hàm sử dụng cho ngắt 0
interrupt [EXT_INT1] void ext_int1_isr(void) Hàm sử dụng cho ngắt 1

interrupt [EXT_INT0] void ext_int0_isr(void)
{
// Viết code tại đây
}

Atmel studio:

ISR(INTO_Vect) Hàm sử dụng cho ngắt 0
ISR(INT1_Vect) Hàm sử dụng cho ngắt 1

ISR(INTO_Vect)
{
// Viết code tại đây
} 

4. Các bước khai báo

  • Bước 1: Khai báo PORT
  • Bước 2: Khai báo các interrupt // Thanh ghi GICR: INT0, INT1, INT2
  • Bước 3: Khai báo mode interrupt // Thanh ghi MCUCR
  • Bước 4: Gọi hàm sei() để cho phép ngắt toàn cục
  • Bước 5: Tạo hàm Vector(Handler)

5. Ví dụ sử dụng ngắt ngoài với Atmega16

Sơ đồ:

Code:

#include <mega16.h>
#include <delay.h>
unsigned char i,j;
interrupt [EXT_INT0] void ext_int0_isr(void)
{ 
    if((PIND & (1<<PIND2))== 0)
    {     
        for(j=0;j<4;j++)
       {
        PORTD.4 =~ PORTD.4;
        delay_ms(500); 
        }
    }
}

void main(void)
{
    DDRA = 0xff; // all là output
    PORTA = 0x00; // OV
    DDRD = (1<<PIND4)|(0<PIND2);
    PORTD = (0<<PORTD4)|(1<<PORTD2); //D2: input, D4 output
    GICR |= (1<<INT0); // bat INT0
    MCUCR = (1<ISC01)|(0<<ISC00); //falling edge
    #asm("sei")

    while (1)
      {
         PORTA = 0x01; 
         delay_ms(250);
         for(i=0;i<7;i++)
         {
           PORTA = PORTA<<1;  
           delay_ms(250);
         }
      }
}

Demo:

Nhận xét

Mới hơn Cũ hơn