Lập trình ngắt cho AVR trên CodeVisionAVR
1. Khái niệm
Ngắt là một quá trình can thiệp vào chương trình chính đang chạy, sau khi thực hiện xong quá trình đó nó sẽ quay trở lại tiếp tục thực hiện chương trình đang chạy.
VD: Bạn đang làm một việc nào đó theo trình tự từ đầu đến cuối rồi lại quay lại, cứ như vậy có vẻ như không có chuyện gì xảy ra, đột nhiên xếp (Trưởng phòng) gọi bạn đem cho một cốc cafe chẳng hạn, thì bạn sẽ dừng công việc đó lại để đi pha cafe đem cho xếp, xong việc pha cafe thì bạn lại cắm đầu vào công việc đang dang dở vữa nãy. Nếu hôm nào xếp không yêu cầu pha cafe thì "ngắt" sẽ không thể xảy ra.
Thứ tự ưu tiên giảm dần theo địa chỉ của chương trình, tức ngằt RESET và INT0, INT1 có mức ưu tiên cao nhất. Trong quá trình chạy chương trình nếu có 2 ngắt cùng xảy ra thì ngắt nào có thứ tự ưu tiên cao hơn sẽ được thực thi trước.
2. Danh sách các ngắt trên AVR
STT | Tên ngắt | Địa chỉ chương trình | Miêu tả chức năng |
1 | RESET | 0X000 | Ngắt phần cứng Reset |
2 | INT0 | 0x001 | Ngắt ngoài tại chân INT0 |
3 | INT1 | 0x002 | Ngắt ngoài tại chân INT1 |
4 | TIMER2 COMP | 0x003 | Ngắt so sánh timer 2 |
5 | TIMER2 OVF | 0x004 | Ngắt tràn timer 2 |
6 | TIMER1 CAPT | 0x005 | Ngắt chụp sự kiện timer 1 |
7 | TIMER1 COMPA | 0x006 | Ngắt so sánh A timer 1 |
8 | TIMER1 COMPB | 0x007 | Ngắt so sánh B timer 1 |
9 | TIMER1 OVF | 0x008 | Ngăt tràn timer 1 |
10 | TIMER0 OVF | 0x009 | Ngắt tràn timer 0 |
11 | SPI, STC | 0x00A | Ngắt giao tiếp SPI |
12 | USART, RXC | 0x00B | Ngắt nhận dữ liệu cổng nối tiếp |
13 | USART, UDRE | 0x00C | Ngắt thanh ghi dữ liệu cổng nối tiếp trống |
14 | USART, TXC | 0x00D | Ngắt truyền dữ liệu cổng nối tiếp |
15 | ADC | 0x00E | Ngắt chuyển đổi ADC |
16 | EEP_RDY | 0x00F | Ngắt bộ nhớ EEPROM sẵn sàng |
17 | ANA_COMP | 0x010 | Ngắt bộ so sánh analog |
18 | TWI | 0x011 | Ngắt giao tiếp 2 dây |
19 | SPM_RDY | 0x012 | Ngắt bộ nhớ chương trình sẵn sàng |
3. Bảng vector ngắt cho lập trình C trên CodeVisionAVR
STT | Tên ngắt | Vector ngắt |
1 | RESET | |
2 | INT0 | EXT_INT0 |
3 | INT1 | EXT_INT1 |
4 | TIMER2 COMP | TIM2_COMP |
5 | TIMER2 OVF | TIM2_OVF |
6 | TIMER1 CAPT | TIM2_CAPT |
7 | TIMER1 COMPA | TIM1_COMPA |
8 | TIMER1 COMPB | TIM1_COMPB |
9 | TIMER1 OVF | TIM1_OVF |
10 | TIMER0 OVF | TIM0_OVF |
11 | SPI, STC | SPI_STC |
12 | USART, RXC | USART_RXC |
13 | USART, UDRE | USART_UDRE |
14 | USART, TXC | USART_TXC |
15 | ADC | ADC_INT |
16 | EEP_RDY | EEP_RDY |
17 | ANA_COMP | ANA_COMP |
18 | TWI | TWI |
19 | SPM_RDY | SPM_RDY |
4. Khai báo chương trình phục vụ ngắt trên CodeVisionAVR
interrupt [vector_ngắt] void tên_ctr(void)
{
//Nội dung code chương trình phục vụ ngắt
}
VD cho ngắt ADC:
interrupt [ADC_INT] void adc_int(void)
{
adc_data=ADCH; //Lấy dữ liệu ADC 8bit vừa chuyển đổi
}
5. VD về ngắt ngoài và ngắt timer
a. Ngắt ngoài
Ngắt ngoài là quá trình tác động từ bên ngoài vào bên trong VDK làm cho VDK thực hiện một ngoại lệ nào đó.
Trong AVR cung cấp cho chúng ta 2 ngắt ngoài là INT0 và INT1 cơ chế hoạt động giống hệt nhau, có 4 kiểu là:
- Ngắt mức thấp Low level - Chỉ xảy ra ngắt khi chân INT0 hoặc INT1 ở mức thấp
- Ngắt thay đổi logic Any change - Bất kỳ thay đổi logic nào trên chân INT0 hoặc INT1
- Ngắt sườn lên Rising Edge (logic 0 lên logic 1)
- Ngắt sườn xuống Falling Edge (logic 1 xuống logic 0)
Do cơ chế giống nhau ta chỉ xét 1 ngắt cho cả 2 ngắt luôn, các thanh ghi tham gia điều khiển ngắt ngoài:
MCUCR (MCU Control Register): Là thanh ghi điều khiển ngắt ngoài, ta chỉ quan tâm tới 4bit thấp của thanh ghi này, 4bit cao không phụ trách điều khiển ngắt ngoài:
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
SE | SM2 | SM1 | SM0 | ISC11 | ISC10 | ISC01 | ISC00 |
Trong đó:
- ISC01 và ISC00 là 2bit chọn kiểu ngắt ngoài INT0.
- ISC11 và ISC10 là 2bit chọn kiểu ngắt ngoài INT1.
Ta có bảng sau:
ISC01/ISC11 | ISC00/ISC10 | Kiểu ngắt |
0 | 0 | Ngắt mức thấp |
0 | 1 | Ngắt thay đổi logic |
1 | 0 | Ngắt sườn xuống |
1 | 1 | Ngăt sườn lên |
VD ta muốn sử dụng cả 2 ngắt ở kiểu ngắt sườn xuống thì ta set như sau: MCUCR=0x0A; tương đương MCUCR=00001010
GICR (General Interrupt Control Register): Là thanh ghi mặt nạ ngắt cho ngắt ngoài có 2 bit cho phép ngắt là:
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
INT1 | INT0 | - | - | - | - | IVSEL | IVCE |
Ta chỉ việc set 1 trong 2 bit trên lên logic 1 là sử dụng được ngắt ngoài tương ứng. Lưu ý các chip AVR như AT90 thì thanh ghi mặt nạ ngắt là GIMSK.
GIFR (General Interrupt Flag Register): Là thanh ghi lưu trạng thái cờ ngắt ta không quan tâm thanh ghi này trong lập trình mà chỉ biết là nó có 2bit cờ là INTF1, INTF0.
Như vậy ta sẽ có code VD ngắt ngoài INT0 như sau:
Code này sẽ bật tắt LED khi nhấn nút, nút nhấn được nối với chân INT0, minh họa bằng chip ATmega8.
#include <mega8.h>
#define LED PORTB.0 //Chan PB0 gan LED
interrupt [EXT_INT0] void ngat_ngoai_0(void)
{
LED = ~LED; //Dao muc logic
}
void main()
{
DDRB.0 = 1; //PB0 lam Output
MCUCR |= (1<<ISC01); //ISC01=1, ISC00=0 Ngat suon xuong INT0
GICR |= (1<<INT0); //Ngat ngoai INT0
LED = 0; //Led tat
#asm("sei") //Cho phep ngat cuc bo
while(1); //Khong lam gi ca
}
Mô phỏng trên proteus 7.10:
b. Ngắt timer
Để hiểu rõ về timer vui lòng xem bài 2 để hiểu rõ về cơ chế của timer, ở bài 2 đã nói rất rõ các thanh ghi phụ trách điều khiển timer trong đó ngắt, phần code bên dưới sẽ là code mẫu cho tất cả các timer trong AVR, các bạn dựa vào code mẫu để xây dựng chương trình của riêng các bạn:
Link: http://sangtaoclub.net/bai-viet/176-bai-2-tim-hieu-bo-timer-counter-tren-avr.html
Code mẫu: Nhấp nháy LED sau mỗi lần ngắt timer 0
#include <mega8.h>
#define LED PORTD.0 //Chan PD0 gan LED
interrupt [TIM0_OVF] void ngat_timer_0(void)
{
LED = ~LED; //Dao muc logic
TCNT0 = 5; //Set lai gia tri
}
void main()
{
DDRD.0 = 1; //PD0 lam Output
TCCR0 = (1<<CS02) | (1<<CS00); //CS02=1, CS01=0, CS00=1: xung clock bang Fosc/1024
TCNT0 = 5; //Khoi tao gia tri
TIMSK = (1<<TOIE0); //Ngat tran timer 0
LED = 0; //Led tat
#asm("sei") //Cho phep ngat cuc bo
while(1); //Khong lam gi ca
}
Mạch mô phỏng:
Các bạn tải code mẫu và mô phỏng về trong File đính kèm...
Tải về đính kèm: