Trước hết trước khi bắt đầu các bạn nếu chưa có datasheet của dùng MSP430 thì có thể download tại đây : http://www.ti.com/general/docs/lit/getliterature.tsp?baseLiteratureNumber=slaa559&fileType=pdf.
Lưu ý Datasheet vẫn luôn là tài liệu quan trọng nhất khi bạn tiếp cận bất cứ dòng vi điều khiển nào , nên việc thành thạo tiếng Anh và khả năng đọc Datasheet vẫn là quan trọng nhất. Tài liệu được viết cho MSP430G2553,các chip khác có module khác mã sẽ không sử dụng được thư viện đi kèm.
Bài trước chúng ta đã được làm quen với UART và GPIO , như vậy từ bài này các bạn có thể tận dụng gần hết tất cả các chức năng mà kit Launchpad có thể làm dc . Chỉ còn phần cuối cùng liên quan đến thạch anh 32Khz nối ngoài sẽ được mình đề cập đến trong bài Clock , sẽ viết sau này . Các bạn chú ý nên giữ lại thạch anh này vì MSP430G2553 chỉ hỗ trợ thạch anh ngoài 32Khz là cao nhất , nó rất hữu ích khi các bạn cần căn chỉnh chính xác xung nội .
Tiếp tục với bài tập hôm nay liên quan
đến ngắt ngoài . Hai kỹ thuật quan trọng nhất trong lập trình nhúng là ngắt và hỏi vòng , vì
vậy các bạn cần nắm vững bài tập này . Kỹ thuật hỏi vòng chính là kiểu lập trình
while(…) { if …. } mà chúng ta đã làm trong bài GPIO .
1.Lý thuyết
Ngắt (interrupt) là quá trình dừng
chương trình chính đang chạy để ưu tiên thực hiện
một chương trình khác , chương trình này được gọi là chương trình phục vụ ngắt (ISR –
Interrupt Service Routine) .
một chương trình khác , chương trình này được gọi là chương trình phục vụ ngắt (ISR –
Interrupt Service Routine) .
MSP430 có 2 loại ngắt là ngắt khả ngụy
và ngắt không khả ngụy .
-
Ngắt không khả ngụy liên quan đến các hoạt động
Reset,phát hiện lỗi xong clock , sự cố về bộ nhớ Flash.Nói chung ngắt này có mức
độ ưu tiên cao nhất , ở chế độ bình thường không ảnh hưởng đến hoạt động của
chương trình . Một số chế độ đặc biệt mình sẽ hướng dẫn trong loạt bài MSP430_Advance , sẽ
viết sau,trình bày cách điều khiển hoạt động của các ngắt không khả ngụy này
như tắt chức năng reset ở chân Reset,khởi động lại bằng phần mềm.Ngắt không khả ngụy không điều khiển bằng bit GIE trong thanh
ghi điều khiển .
-
Ngắt khả ngụy là loạt ngắt liên quan trực tiếp
đến hoạt động của hầu hết các chương trình cho phép thực hiện các chương trình
gọi từ ngắt của các cổng Inout,ngắt từ Timer , ADC , Các giao thức truyền thông . Có
hai loại ngắt khả ngụy là ngắt trong và ngắt ngoài . Ngắt
khả ngụy điều khiển bằng bit GIE trong thanh ghi điều khiển.Tức là muốn hoạt động
thì BIT GIE phải được set=1 . (đây là một bit trong thanh ghi trạng thái SR(đọc thêm trong sách
Cơ sở lý thuyết MSP430))
o
Ngắt ngoài là các ngắt do tác động của bên
ngoài lên chip VD như ngắt GPIO,ADC,Các giao thực truyền thông
o
Ngắt trong là các loại ngắt sinh ra từ bên
trong chip như ngắt Timer/Counter,Watchdog.
-
Lấy ví dụ thế này,chương trình chính của bạn
thực hiện nháy led P1.0
§
VD : while (1){ P1OUT^=BIT0;
_delay_cycles(1000000);}
o
Bạn muốn nhấn nút P1.3 thì đèn P1.6 sáng.Bạn
có thế thực hiện bằng cách hỏi vòng,
§
while (1){
P1OUT^=BIT0;
_delay_cycles(1000000);
If((P1IN&BIT3)==0)P1OUT|=BIT6;//Hỏi
vòng
}
o
Hoặc có thể dùng ngắt,tức là khi nhấn nút thì
Module GPIO xác nhận có sự thay đổi điện áp và tự động chạy chương trình ngắt
GPIO,bật led và quay lại chương trình chính.
Như vậy có thế thấy nếu hỏi vòng thì
thao tác bật đèn P1.6 bị trễ một khoảng thời gian ở _delay_cycles(),còn khi
dùng ngắt thì P1.6 sáng tức thì.Đây là một trong những điểm khác biệt giữa kỹ
thuật hỏi vòng và ngắt,cũng gần giống việc VĐK của bạn chạy 2 chương trình cùng
1 lúc vậy(Về bản chất thì không phải)
Sau khi đã hiểu bản chất của ngắt và ứng
dụng của nó,chúng ta sẽ tìm hiểu cách hoạt động trên MSP430.Bài này mình chỉ
minh họa ngắt ngoài ,các loại ngắt khác sẽ được đề cập đến trong các bài về từng
Module riêng .
2.Ngắt ngoài
MSP430 được thiết kế có rất nhiều ngắt
ngoài(đây là 1 trong những điểm vượt trội so với các dòng cũ như AVR,PIC..).Các
cổng P1 P2 của hầu hết MSP430 đều có ngắt.Việc điều khiển ngắt đươc thông qua
các thanh ghi PxIE , PxIES , PxIFG .
Mỗi chân của P1 và P2 có thể làm một
ngắt yêu cầu . Các chân này được thiết lập với các
thanh ghi phụ như:
-
Cho phép ngắt ( PxIE) :
Thanh ghi
này cho phép ngắt trên những chân riêng rẽ.
o Bit = 0:
không cho phép ngắt.
o Bit = 1:
Cho phép ngắt.
Mỗi Bit cho phép ngắt PxIE này được
liên kết với cờ ngắt PxIFG tương ứng.
Việc ghi vào thanh ghi PxOUT và PxDIR
có thể ảnh hưởng đến việc điều chỉnh PxIE.
-
Thanh ghi lựa chọn ngắt cạnh ( PxIES) :
Lựa chọn
loại ngắt khi xuất hiện sự biến đổi tín hiệu ( Nếu PxIE và GIE được Set)
o Bit = 0:
Ngắt cạnh lên
o Bit = 1:
Ngắt cạnh xuống
o
-
Thanh ghi cờ ngắt ( PxIFG)
Cờ ngắt này được Set tự động theo
chương trình đã được lập trình trước khi xuất hiện
một sự chuyển đổi tín hiệu. Cờ PxIFG
phải sử dụng chương trình để Reset.
o Bit = 0:
Không có ngắt.
o Bit = 1:
Có ngắt.
Để thực hiện ngắt cần 2 bước:
o
Thiết lập cấu hình Module để cho phép ngắt xảy
ra và điều kiện xảy ra ngắt
o
Gọi hàm thực hiện ngắt tương ứng
Lấy ví dụ : Đèn P1.0 nháy chu kỳ
0.5Hz,nếu nhấn nút P1.3 thì đèn P1.6 đổi giá trị ngay lập tức( sáng <-> tối)
o
Thiết lập cổng vào ra,phải cài đặt P1.3 là cổng
Input,có điện trở nội kéo lên để chống nhiễu,cài đặt cho phép ngắt cạnh xuống.Cài
đặt cổng Output cho các đèn P1.0 và P1.6.Cuối cùng là set BIT GIE=1 bằng hàm __bis_SR_register(GIE);(đây là một trong số các hàm set thanh ghi trạng
thái SR(đọc thêm trong sách Cơ sở lý thuyết MSP430))
o
Gọi hàm thực hiện ngắt,có bố cục giống nhau giữa
các hàm ngắt khác nhau
#pragma vector = (Tên hàm ngắt mặc định) //xem cuối bài
__interrupt void (Tên hàm ngắt do người dùng đặt)(void)
{
//Thực hiện chương trình ngắt
//Hàm xóa cờ ngắt
}
Chương trình tham khảo:
#include "msp430g2553.h"
/* Khai báo loại chip đang dùng
* Thư
viện này chứa các khai báo tên #define cho địa
* chỉ
các thanh ghi
*/
void main(void)
{
WDTCTL = WDTPW | WDTHOLD; //Stop
Watchdog
BCSCTL1 = CALBC1_1MHZ; //2 thanh
ghi cài đặt tần số hoạt động
DCOCTL = CALDCO_1MHZ; //Ở đây
là 1 Mhz
//Cài đặt cấu hình ngắt ngoài cho P1.3
//Như trong bài GPIO,chúng ta phải cài đặt chống nhiễu cho nút bấm
P1.3
//Vì sự thay đổi của điện áp P1.3 có thể vô tình tạo ra ngắt
P1DIR&=~(BIT3); //Chọn
nhập
P1REN|=BIT3; //Cho phép
trở treo
P1OUT|=BIT3; //Trở treo
lên nguồn
//Cài đặt ngắt
P1IE |=BIT3; //Cho phép
ngắt ở P1.3
P1IES |=BIT3; //Ngắt
cạnh xuống,
//tức là khi điện áp P1.3 từ 1 ->0 thì xảy ra ngắt
P1IFG &=~BIT3; //Xóa cờ
ngắt
__bis_SR_register(GIE);
//Cài đặt đèn
P1DIR|=BIT0+BIT6;
P1OUT&=~(BIT0+BIT6);
while(1)
{
P1OUT^=BIT0;
_delay_cycles(1000000);
}
}
#pragma vector
= PORT1_VECTOR
__interrupt void test(void)
{
//Kiểm tra ngắt có phải là ở P1.3 không
if(P1IFG&BIT3)
{
//Đảo dấu đèn P1.6
P1OUT^=BIT6;
//xoa co ngat tai chan P1.1
P1IFG &=~BIT3;
}
} |
Bảng vector ngắt,các bạn có thể xem trong file “msp430g2553.h”
/************************************************************
*
Interrupt Vectors (offset from 0xFFE0)
************************************************************/
#define VECTOR_NAME(name) name##_ptr
#define EMIT_PRAGMA(x) _Pragma(#x)
#define CREATE_VECTOR(name) void (* const VECTOR_NAME(name))(void) = &name
#define
PLACE_VECTOR(vector,section) EMIT_PRAGMA(DATA_SECTION(vector,section))
#define ISR_VECTOR(func,offset)
CREATE_VECTOR(func); \
PLACE_VECTOR(VECTOR_NAME(func), offset)
#ifdef __ASM_HEADER__ /* Begin #defines for
assembler */
#define PORT1_VECTOR ".int02" /* 0xFFE4 Port 1 */
#else
#define PORT1_VECTOR (2 * 1u) /* 0xFFE4 Port 1 */
/*#define
PORT1_ISR(func) ISR_VECTOR(func,
".int02") */ /* 0xFFE4 Port 1 */ /* CCE V2 Style */
#endif
#ifdef __ASM_HEADER__ /* Begin #defines for
assembler */
#define PORT2_VECTOR ".int03" /* 0xFFE6 Port 2 */
#else
#define PORT2_VECTOR (3 * 1u) /* 0xFFE6 Port 2 */
/*#define
PORT2_ISR(func) ISR_VECTOR(func,
".int03") */ /* 0xFFE6 Port 2 */ /* CCE V2 Style */
#endif
#ifdef __ASM_HEADER__ /* Begin #defines for
assembler */
#define ADC10_VECTOR ".int05" /* 0xFFEA ADC10 */
#else
#define ADC10_VECTOR (5 * 1u) /* 0xFFEA ADC10 */
/*#define
ADC10_ISR(func) ISR_VECTOR(func,
".int05") */ /* 0xFFEA ADC10 */ /* CCE V2 Style */
#endif
#ifdef __ASM_HEADER__ /* Begin #defines for
assembler */
#define USCIAB0TX_VECTOR ".int06" /* 0xFFEC USCI A0/B0
Transmit */
#else
#define USCIAB0TX_VECTOR (6 * 1u) /* 0xFFEC USCI A0/B0 Transmit */
/*#define
USCIAB0TX_ISR(func) ISR_VECTOR(func,
".int06") */ /* 0xFFEC USCI A0/B0 Transmit */ /* CCE V2 Style */
#endif
#ifdef __ASM_HEADER__ /* Begin #defines for
assembler */
#define USCIAB0RX_VECTOR ".int07" /* 0xFFEE USCI A0/B0
Receive */
#else
#define USCIAB0RX_VECTOR (7 * 1u) /* 0xFFEE USCI A0/B0 Receive */
/*#define
USCIAB0RX_ISR(func) ISR_VECTOR(func,
".int07") */ /* 0xFFEE USCI A0/B0 Receive */ /* CCE V2 Style */
#endif
#ifdef __ASM_HEADER__ /* Begin #defines for
assembler */
#define TIMER0_A1_VECTOR ".int08" /* 0xFFF0 Timer0)A CC1, TA0
*/
#else
#define TIMER0_A1_VECTOR (8 * 1u) /* 0xFFF0 Timer0)A CC1, TA0 */
/*#define
TIMER0_A1_ISR(func) ISR_VECTOR(func,
".int08") */ /* 0xFFF0 Timer0)A CC1, TA0 */ /* CCE V2 Style */
#endif
#ifdef __ASM_HEADER__ /* Begin #defines for
assembler */
#define TIMER0_A0_VECTOR ".int09" /* 0xFFF2 Timer0_A CC0 */
#else
#define TIMER0_A0_VECTOR (9 * 1u) /* 0xFFF2 Timer0_A CC0 */
/*#define
TIMER0_A0_ISR(func) ISR_VECTOR(func,
".int09") */ /* 0xFFF2 Timer0_A CC0 */ /* CCE V2 Style */
#endif
#ifdef __ASM_HEADER__ /* Begin #defines for
assembler */
#define WDT_VECTOR ".int10" /* 0xFFF4 Watchdog Timer */
#else
#define WDT_VECTOR (10 * 1u) /* 0xFFF4 Watchdog Timer */
/*#define
WDT_ISR(func) ISR_VECTOR(func,
".int10") */ /* 0xFFF4 Watchdog Timer */ /* CCE V2 Style */
#endif
#ifdef __ASM_HEADER__ /* Begin #defines for
assembler */
#define COMPARATORA_VECTOR ".int11" /* 0xFFF6 Comparator A */
#else
#define COMPARATORA_VECTOR (11 * 1u) /* 0xFFF6 Comparator A */
/*#define
COMPARATORA_ISR(func) ISR_VECTOR(func,
".int11") */ /* 0xFFF6 Comparator A */ /* CCE V2 Style */
#endif
#ifdef __ASM_HEADER__ /* Begin #defines for
assembler */
#define TIMER1_A1_VECTOR ".int12" /* 0xFFF8 Timer1_A CC1-4,
TA1 */
#else
#define TIMER1_A1_VECTOR (12 * 1u) /*
0xFFF8 Timer1_A CC1-4, TA1 */
/*#define
TIMER1_A1_ISR(func) ISR_VECTOR(func,
".int12") */ /* 0xFFF8 Timer1_A CC1-4, TA1 */ /* CCE V2 Style */
#endif
#ifdef __ASM_HEADER__ /* Begin #defines for
assembler */
#define TIMER1_A0_VECTOR ".int13" /* 0xFFFA Timer1_A CC0 */
#else
#define TIMER1_A0_VECTOR (13 * 1u) /* 0xFFFA Timer1_A CC0 */
/*#define
TIMER1_A0_ISR(func) ISR_VECTOR(func,
".int13") */ /* 0xFFFA Timer1_A CC0 */ /* CCE V2 Style */
#endif
#ifdef __ASM_HEADER__ /* Begin #defines for
assembler */
#define NMI_VECTOR ".int14" /* 0xFFFC Non-maskable */
#else
#define NMI_VECTOR (14 * 1u) /* 0xFFFC Non-maskable */
/*#define
NMI_ISR(func) ISR_VECTOR(func,
".int14") */ /* 0xFFFC Non-maskable */ /* CCE V2 Style */
#endif
#ifdef __ASM_HEADER__ /* Begin #defines for
assembler */
#define RESET_VECTOR ".reset" /* 0xFFFE Reset [Highest
Priority] */
#else
#define RESET_VECTOR (15 * 1u) /* 0xFFFE Reset [Highest Priority] */
/*#define
RESET_ISR(func) ISR_VECTOR(func,
".int15") */ /* 0xFFFE Reset [Highest
Priority] */ /*
CCE V2 Style */
#endif
cho em hỏi, mình sử dụng ngắt đầu vào nút bắm ở 2 port khác nhau trong 1 chương trình được hong ạ?
ReplyDelete(em thử làm nhưng không được)