PDA

View Full Version : Lúng túng với Interrupts - nhờ trợ giúp


Mr.Bi
03-11-2007, 04:30 PM
#include <16F877A.h>
#include <DEFINE_16F877A.h>
#include <DEFINE_16F87x.h>
#fuses NOWDT,XT,NOPROTECT,NOLVP,PUT
#use delay(clock=4000000)

#define RS RC0
#define RW RC1
#define E RC2
#define LCD PORTD

#define mo_khoa RA0
#define kichSCR RA1
#define OK RA2
#define CANCEL RA3




const unsigned char num[] = {' ','0','1','2','3','4','5','6','7','8','9'} ;
const unsigned char pass[] = {' ','3','9','5','2','6','1','0'} ; // password
unsigned char key[] ;
unsigned char a = 0 ; // bien tang cac so nhap vao key [a++]
unsigned char i ; // bien hien thi cac so da nhan , sau khi nhan OK

//----------------------------
void delay_ms_MAIN(unsigned int j)
{ unsigned int k , l;
for( k=0 ; k<=j;k++)
{ l = j ; while(l!=0) l-- ; }
}
//----------------------------
void delay_ms_INT(unsigned int j)
{ unsigned int k , l;
for( k=0 ; k<=j;k++)
{ l = j ; while(l!=0) l-- ; }
}


void ham_DK_MAIN()
{ RS = 0 ; RW = 0 ; E =1 ; E=0 ; delay_ms_MAIN(2);}
void ham_HienThi_MAIN()
{ RS = 1 ; RW = 0 ; E =1 ; E=0 ; delay_ms_MAIN(1);}

//----------------
void ham_DK_INT()
{ RS = 0 ; RW = 0 ; E =1 ; E=0 ; delay_ms_INT(2);}
void ham_HienThi_INT()
{ RS = 1 ; RW = 0 ; E =1 ; E=0 ; delay_ms_INT(1);}

//----------------------
void quetphim_hienthi()
{ PORTB = 0b11101111 ;
if(RB0==0) { delay_ms_INT(20) ; while (RB0==0) continue ; delay_ms_INT(20);
LCD = num[1] ; ham_HienThi_INT();
key[a++] = num[1]; // key[a++] = '0'
} ;
if(RB1==0) { delay_ms_INT(20) ; while (RB1==0) continue ; delay_ms_INT(20);
LCD = num[2] ; ham_HienThi_INT();
key[a++] = num[2]; // key[a++] = '1'
} ;
if(RB2==0) { delay_ms_INT(20) ; while (RB2==0) continue ; delay_ms_INT(20);
LCD = num[3] ; ham_HienThi_INT();
key[a++] = num[3]; // key[a++] = '2'
} ;
PORTB = 0b11011111 ;
if(RB0==0) { delay_ms_INT(20) ; while (RB0==0) continue ; delay_ms_INT(20);
LCD = num[4] ; ham_HienThi_INT();
key[a++] = num[4]; // key[a++] = '3'
} ;
if(RB1==0) { delay_ms_INT(20) ; while (RB1==0) continue ; delay_ms_INT(20);
LCD = num[5] ; ham_HienThi_INT();
key[a++] = num[5]; // key[a++] = '4'
} ;
if(RB2==0) { delay_ms_INT(20) ; while (RB2==0) continue ; delay_ms_INT(20);
LCD = num[6] ; ham_HienThi_INT();
key[a++] = num[6]; // key[a++] = '5'
} ;
PORTB = 0b10111111 ;
if(RB0==0) { delay_ms_INT(20) ; while (RB0==0) continue ; delay_ms_INT(20);
LCD = num[7] ; ham_HienThi_INT();
key[a++] = num[7]; // key[a++] = '6'
} ;
if(RB1==0) { delay_ms_INT(20) ; while (RB1==0) continue ; delay_ms_INT(20);
LCD = num[8] ; ham_HienThi_INT();
key[a++] = num[8]; // key[a++] = '7'
} ;
if(RB2==0) { delay_ms_INT(20) ; while (RB2==0) continue ; delay_ms_INT(20);
LCD = num[9] ; ham_HienThi_INT();
key[a++] = num[9]; // key[a++] = '8'
} ;
PORTB = 0b01111111 ;
if(RB0==0) { delay_ms_INT(20) ; while (RB0==0) continue ; delay_ms_INT(20);
LCD = num[10] ; ham_HienThi_INT();
key[a++] = num[10]; // key[a++] = '9'
} ;
}

#int_RB
void ngat_RB()
{ while(OK!=0) { quetphim_hienthi();}
delay_ms_INT(20);

RBIF =0;
}

main()
{ set_tris_A(0b00001100); output_A(0b001100);
set_tris_B(0x0F); output_B(0x0F); // port B = keypad
set_tris_C(0);
set_tris_D(0);
delay_ms_MAIN(100); // thoi gian LCD khoi dong

LCD = 0x38 ; ham_DK_MAIN(); // modify LCD 2 dong matrix 5x7
LCD = 0x0C ; ham_DK_MAIN(); // tat con tro bat hien thi

enable_interrupts(global);
enable_interrupts(int_RB);
ext_int_edge(H_to_L);

while(1)
{
LCD = 0xC4 ; ham_DK_MAIN(); // hien chu dong 2 cot 5
LCD = 'R' ; ham_HienThi_MAIN();
LCD = 'e' ; ham_HienThi_MAIN();
LCD = 'a' ; ham_HienThi_MAIN();
LCD = 'd' ; ham_HienThi_MAIN();
LCD = 'y' ; ham_HienThi_MAIN();
LCD = '!' ; ham_HienThi_MAIN();

} ;
}
http://i189.photobucket.com/albums/z78/NgCongMinhDN/matranphim.jpg

hiện tượng xảy ra thế này
- khi cho dòng lệnh enable_interrupts(global) - cho phép ngắt - thì LCD ko hiện dòng chữ gì cả ? ngay cả khi ta reset để cố tình cho ct chỉ chạy trong MAIN (tức là phải có dòng "ready!'
- khi disable_interrupts(global) thì dòng "ready!" hiện ra ( có thể hiểu là ct chạy đúng theo ý đồ )
tại sao vậy ?
- chả lẽ set_tris các port bị sai so với phần cứng ?
- hay khởi tạo ngắt chưa đúng , nên ct nhảy ngắt lung tung ?

bien_van_khat
03-11-2007, 06:04 PM
Nếu bạn debug = ICD2 thì INT_RB sẽ hoạt động ko đúng, vi lúc này 2 chân RB6 RB7 do ICD2 điều khiển.

Trong hàm phục vụ ngắt bạn không cần xóa cờ ngắt, trình dịch tự sinh mã làm điều này cho bạn.

Bạn có thể sử dụng thư viện giao tiếp LCD của CCS C(trong thư mục /drivers)

Mr.Bi
04-11-2007, 01:29 PM
- thư viện LCD trong PICC/drivers là giao tiếp 4 bit , mình đang giao tiếp 8 bit
- mới nhập môn nên ko hiểu ICD2 là cái gì , đc biết nó là mạch debug ngoài , ko biết có đúng ko ? nhưng mình đang test ct trên mạch thật .
Do đó , điều thắc mắc bây giờ là : cần chỉnh lại ct chỗ nào để chạy đúng ý đồ( ý đồ : khi có ngắt thì quét phím & hiển thị số đã bấm ; khi ko có ngắt thì hiện dòng READY! ) hay phải thay đổi lại phần cứng ?

electronicltv
04-11-2007, 02:07 PM
Bạn coi lại chân RA2==OK, bạn dùng CCS thì khi chạy, sau khi enable interrupts thì chuơng trình sẽ nhảy vào ISR liền. Khi đó nếu RA2=1 thì chuơng trình chỉ chạy trong phần ISR ( int_RB) mà thôi, chưa kịp tới vòng while(1) để hiển thị chữ ready.

bien_van_khat
04-11-2007, 07:08 PM
Thực tế nếu ko xài ICD2, theo mình với chương trình như trên ngắt RB không bao giờ xảy ra vì bạn cấu hình PORTB<4:7> là output. Do 4 chân này không được pull-up hoặc pull-down nên cho dù cấu hình là input bạn cũng không thể dùng ngắt INT_RB theo đúng ý đồ được.

Chương trình của bạn viết khá rối rắm nên mình mới khuyên dùng thư viện của CCS, ví dụ

ext_int_edge(H_to_L);

Là cấu hình cho ngắt ngoài, ko phải cho ngắt thay đổi mức trên PORTB.

Để kiểm tra nguyên nhân bạn thử disable INT_RB xem.

Mr.Bi
04-11-2007, 10:58 PM
Thực tế nếu ko xài ICD2, theo mình với chương trình như trên ngắt RB không bao giờ xảy ra vì bạn cấu hình PORTB<4:7> là output. Do 4 chân này không được pull-up hoặc pull-down nên cho dù cấu hình là input bạn cũng không thể dùng ngắt INT_RB theo đúng ý đồ được.

Chương trình của bạn viết khá rối rắm nên mình mới khuyên dùng thư viện của CCS, ví dụ

ext_int_edge(H_to_L);

Là cấu hình cho ngắt ngoài, ko phải cho ngắt thay đổi mức trên PORTB.

Để kiểm tra nguyên nhân bạn thử disable INT_RB xem.


uhm ! đúng như bạn nói , khi mình disable int_RB thì mọi chuyện hoạt động bình thường
Bây giờ :
- cho 4 con trở 10k kéo xuống ở chân RB4 - RB7 .
- còn cấu hình cho ngắt khi thay đổi mức trên RB , mình phải chữa lại ntn ?
ĐÚng là mình chưa nghĩ thấu đáo vấn đề !

Mr.Bi
04-11-2007, 11:04 PM
Bạn coi lại chân RA2==OK, bạn dùng CCS thì khi chạy, sau khi enable interrupts thì chuơng trình sẽ nhảy vào ISR liền. Khi đó nếu RA2=1 thì chuơng trình chỉ chạy trong phần ISR ( int_RB) mà thôi, chưa kịp tới vòng while(1) để hiển thị chữ ready.

mình nghĩ : ct chỉ nhảy vào ngắt khi xuất hiện thay đổi mức trên RB , vì mình cấu hình như vậy . RA2 có thay đổi trạng thái thì cũng ko ngắt đc .
cách giải thích của bien_van_khat có vẻ đúng . Mình nghĩ hơi nông cạn ! hic....

Mr.Bi
05-11-2007, 03:50 PM
#use delay(clock=4000000)
#use fast_io(b)
#use fast_io(c)
int a;
const unsigned char dig[]={0b00111111,0b00000110, 0b01011011,0b01001111,\
0b01100110,0b01101101,0b01111101,0b00000111,0b0111 1111,0b01101111,0b01110111,\
0b01111100,0b00111001,0b01011110,0b11111001,0b1111 0001};
// ma hoa digital duoi dang mang
// Chuong trinh ngat
#int_RB
void ngat_RB()
{
if((RBIF)&&(RBIE))
{
{
if(RB4&&RB0)
a=dig[0];
}
{
if(RB4&&RB1)
a=dig[4];
}
{
if(RB4&&RB2)
a=dig[8];
}
{
if(RB4&&RB3)
a=dig[12];
}
//.......
{
if(RB5&&RB0)
a=dig[1];
}
{
if(RB5&&RB1)
a=dig[5];
}
{
if(RB5&&RB2)
a=dig[9];
}
{
if(RB5&&RB3)
a=dig[13];
}
//........
{
if(RB6&&RB0)
a=dig[2];
}
{
if(RB6&&RB1)
a=dig[6];
}
{
if(RB6&&RB2)
a=dig[10];
}
{
if(RB6&&RB3)
a=dig[14];
}
//........
{
if(RB7&&RB0)
a=dig[3];
}
{
if(RB7&&RB1)
a=dig[7];
}
{
if(RB7&&RB2)
a=dig[11];
}
{
if(RB7&&RB3)
a=dig[15];
}
RBIF=0; //Xoa co ngat RB
}
}
// Chuong trinh chinh
main()
{
set_tris_b(0b11110000);
set_tris_c(0);
enable_interrupts(global);
enable_interrupts(int_RB);
ext_int_edge(H_to_L);
portb=0;
portc=0;
while(true)
{
portb=1;
portb=2;
portb=4;
portb=8;
portc=a;
}
}
http://i189.photobucket.com/albums/z78/NgCongMinhDN/Giaimabanphim.gif

đây là mạch & code của anh nnh
- có phải mắc mạch quét phím kiểu này thì mới dùng đc INT_RB ?
ah có vẻ bây giờ mình đã phân biệt đc : ngắt có thay đổi mức trên RB & ngắt ngoài trên RB . Ko biết mình hiểu như thế đã trọn vẹn chưa ?

bien_van_khat
05-11-2007, 04:21 PM
Sơ đồ cũng như code bạn vừa đưa ra ở đây cũng có vấn đề, mình nghĩ rằng nó chỉ hoạt động đúng khi mô phỏng còn khi chạy thực tế mạch sẽ không ko hoạt động như ý đồ.

Thứ 1 về pull-up hoặc pull-down. Khi hoạt động thực tế, bạn không bao giờ được để Input pin lơ lửng như thế. Khi ở mode input, trở kháng vào của chân IO là trở kháng vào của cực Gate của MOSFET. Do trở kháng vào này cực kỳ lớn nên chỉ cần một sự biến thiên điện trường nhỏ cũng đủ gây nên một xung điện áp trên mạch vào, điện áp này đủ khả năng để đầu ra của Smith trigger thay đổi trạng thái, hoặc khiến mạch vào TTL chuyển mức. Nói tóm lại là tín hiệu đọc trên PORT sẽ nhảy tùm lum dù ko có phím bấm.

Vậy nên dùng pull-up hay pull-down? Thiết kế input ko nên sử dụng pull-down, vì nếu pull-down, tức là khi idle, mức logic=0, vậy để chuyển mức logic thành 1, bạn cần phải đưa lên nguồn Vdd qua một điện trở hạn dòng (ko hạn dòng sẽ làm hư IO pin). Như vậy rõ ràng một mạch vào cần 2 điện trở là ko hiệu quả về linh kiện, chưa nói đến hiệu quả về thiết kế nếu IO pin cần hạn dòng nhiều, bạn phải tính toán giá trị điện trở cho phù hợp.

Sử dụng pull-up vừa tiết kiệm vừa đơn giản.

Thứ 2, ngắt thay đổi mức trên PORTB bạn có thể coi trong datasheet của 16F877A, đây là ngắt xảy ra khi mức logic trên PORTB<4:7> thay đổi (từ 0->1 hoặc từ 1->0). Bạn nên để ý là với 877A chỉ có 4 bit cao của PORTB là có chức năng này thôi nhé, và chỉ xảy ra khi chân đó là input.

Với mạch của bạn rõ ràng vấn đề nằm ở ngắt INT_RB, bạn có thể mắc thêm 4 con 10K lên Vdd, hoặc gọi hàm

port_b_pullups(TRUE);

để sử dụng weak pull-up bên trong chip.

Mr.Bi
05-11-2007, 10:29 PM
http://i189.photobucket.com/albums/z78/NgCongMinhDN/matranphim2.jpg
http://i189.photobucket.com/albums/z78/NgCongMinhDN/LCDkeypad.png


#int_RB
void ngat_RB()
{ while(OK!=0) { quetphim_hienthi();}

delay_ms(20);
}

main()
{
set_tris_A(0b00001100); output_A(0b001100);
set_tris_B(0xF0); // port B = keypad
set_tris_C(0);
set_tris_D(0);
delay_ms(100); // thoi gian LCD khoi dong

enable_interrupts(global);
enable_interrupts(int_RB);
ext_int_edge(H_to_L);
lcd_int() ;
while(1)
{ // cho hiển thị dòng "READY!" } ;

}

đây là mạch mình đã chỉnh lại hoàn toàn :
- LCD giao tiếp 4 bit ( để lợi dụng thư viện CCS)
- ma trận phím để sd INT_RB
- cách viết code như vậy đã đúng : khi có ngắt thay đổi mức trên RB4-RB7 thì tạo ngắt & ct nhảy vào quetphim_hienthi() ; ko thì hiện dòng READY !

Mr.Bi
05-11-2007, 10:34 PM
các bạn cho ý kiến về mạch + ct để mình hoàn thiện trước khi đi in . Chắc cái mạch mình đang test ... vứt đi rùi ! Làm lại mạch mới thôi ... cũng tốn kha khá đấy chứ nhẩy ? hic !