Phương pháp quét Keypad ma trận dùng ngắt Timer

Chào các bạn.

Mình không biết phương pháp này các bạn biết chưa, hoặc đã ai post chưa. Nhưng đó là mình suy nghĩ và code ra chứ không hề copy ý tưởng hay code của ai khác :D.

Phương pháp dựa trên dùng ngắt Timer0 dùng cho PIC16F887 do đó sẽ rất là tối ưu, tất nhiên có thể dùng cho tất cả các loại PIC và vi điều khiển khác.

Cấu tạo của Keypad:

Nội dung của phương pháp này như sau, rất đơn giản giống tư tưởng của các phương pháp scan truyền thống:

B1. Cho các cột = 1

B2. Đọc các hàng, nếu hàng nào = 1, thì sau khoảng 10 đến 15ms thì lại đọc tiếp. Nếu vẫn = 1 thì à, đúng là phím bấm rồi, ke ke. Như vậy xác định được hàng nào rồi nha. Cho vào biến row_num.

B3. Cho row_num = 1, đọc các cột xem, cột nào = 1 thì đó là cột bấm đó, gán vào biến col_num. Chờ khi phím nhả thì gọi hàm thực hiện theo từng phím key_function();

Rất đơn giản như thế nhưng mình thực hiện rất tối ưu, các bạn có thể tham khảo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
void scanKeypad(void){
     unsigned char i;        // Define i as register later
    // if state 0 then set column to 1, and see the change of row
    switch(progress){
         case 0:
               // set all column (4 low bit) as output, and row as input, and write '1' to column
               tris_keypad    =  0xF0;
               port_keypad |= 0x0F;
               // if value of row diffirent than 0,then set progress to 1
              // and set Tcycle = 10
              if ((port_keypad & 0xF0) != 0) {
                     // Determine next state
                      progress = 1;
                      cycle_1  = 10;
               }else{
                      progress = 0;
                };
                break;
 
         // Progress 1, check after 10 ms
         case 1:
               cycle_1--;
              // when cycle_1 = 0,then check again
               if(cycle_1 == 0){
                     if ((port_keypad & 0xF0) != 0) {
                     // Determine which row equal 1
                           for(i = 7; i>3; i--){
                               if ( (port_keypad & (1<<i)) != 0){
                                     row_num = i-3;
                                      break;
                                };
                          };
                         //Determine next state
                        progress = 2;
                     }else{
                           progress = 0;        // Start state
                     };
                 };
                 break;
 
           // Progress 2, to determind the column
           case 2:
                 //Set row tris to 0,
                 // row_num corespond to (row_num + 3) bit in port_keypad
                tris_keypad ^= 1 << (row_num + 3);        // clear tris bit of row to 0, as output
                tris_keypad    |= 0x0F;                    // Set all pin of column as input(0 to 3)
                port_keypad != 1 << (row_num + 3);
                for(i = 0;i&lt;4;i++){
                      if ( (port_keypad &  (1<<i)) != 0){
                          col_num = i+1;
                          break;
                           };
                       };
                 // Wait for user release key
               while ((port_keypad &amp; (1&lt;&lt;i)) != 0);
                      // Do all function of keypad after scan sucessfully
                       key_function();
                     //Determind the next state
                      progress = 0;
                     //cycle_2  = 0xFF;
                 break;
           default:
                   break;
           };
};

Bài toán của mình chỉ thực hiện sau khi nhấn phím nên mình mới chờ khi nhả phím. Còn để xác định phím hold hay là nhấn thì dùng thêm process thứ 3, sau thời gian khoảng 250ms nữa hoặc hơn tùy bạn, kiểm tra lại trạng thái của phím bấm đó, nếu thấy vẫn = 1 thì tức là trạng thái hold, còn lại là trạng thái nhấn phím.

Các biến được dùng trong hàm quét phím có thể được khai báo bằng Macro và khai báo biến global.Mình dùng các thanh ghi làm biến, các bạn chú ý dùng để không trùng vào các module đang dùng.

1
2
3
4
5
6
7
8
9
//Define for keypad on PORTD
#define port_keypad PORTD
#define tris_keypad TRISD
 
#define progress    TXREG        //process variable
#define row_num        TMR1L        // Index of row
#define col_num        CCPR2L        // Index of column
 
#define cycle_1        CCPR2H        // 10ms</td>

Vậy chỉ cần thay PORTD bằng bất kì port nào khác mà keypad nối vào là được.

Còn hàm key_function() dùng để thực hiện các lệnh của phím, tùy các bạn.

1
2
3
4
5
6
7
8
9
10
11
void key_function(void){
      switch(row_num){
           case 1:
                  switch(col_num){
           case 1:        //Key 1
           case 2:        // Key 2
           case 3:        // Key 3
 
           ………………….
       }
}

Chú ý: các bạn gọi hàm này trong hàm ngắt của Timer, bao nhiêu ms hay ns là tùy bạn.

Update 30/01/2009

Sửa lại code với tag pre cho đẹp, vì cái lúc trước quên không cho tag vào mà chỉ cho vào 1 table nên không đọc hiểu được code.

Chúc các bạn thành công.

{ 13 comments… add one }
  • Phạm Thành Nam August 3, 2008, 12:57 am

    có ai không giúp mình với mình sắp thi rui ma không biết thế nào về C cả……………………cảm ơn trước

    Reply
  • hoanguit October 30, 2008, 11:59 pm

    chào anh hải, em đang có một số thắc mắc về cách gán giá trị cho các biến trong hàm ngắt,xin a chỉ rõ cho e ,
    vd: đây là dùng ngắt timer 2
    2CON=0X04; /// chỗ này e không biết tại sao lại gán giá trị nay
    T2MOD=0X00; // và thêm chỗ này nưa
    TH2=0XFC;
    RCAPCH=0XFC;
    TL2=0X18;
    RCAPCL=0X18;
    ET2=1;
    TR2=1;
    e không hiểu tại sao lại có địa chỉ này ,trong khi đó địa chỉ bit thanh ghi cua TCON
    sbit IT0 = 0x88;
    sbit IE0 = 0x89;
    sbit IT1 = 0x8A;
    sbit IE1 = 0x8B;
    sbit TR0 = 0x8C;
    sbit TF0 = 0x8D;
    sbit TR1 = 0x8E;
    sbit TF1 = 0x8F;
    và địa chỉ của TMOD là:
    #define T0_M0_ 0x01
    #define T0_M1_ 0x02
    #define T0_CT_ 0x04
    #define T0_GATE_ 0x08
    #define T1_M0_ 0x10
    #define T1_M1_ 0x20
    #define T1_CT_ 0x40
    #define T1_GATE_ 0x80

    #define T1_MASK_ 0xF0
    #define T0_MASK_ 0x0F

    Reply
  • anh04 May 8, 2009, 5:17 pm

    Cho em hỏi ý nghĩa của lệnh này với:

    if ((port_keypad & 0xF0) != 0) {
    // Determine next state
    progress = 1;
    cycle_1 = 10;
    }else{
    progress = 0;
    };

    Reply
    • ngohaibac May 8, 2009, 5:37 pm

      Đó là nhiều lệnh mà em, em không hiểu cụ thể lệnh nào hả em ?

      Reply
      • anh04 May 8, 2009, 9:48 pm

        Xin lỗi, em copy mà sao bị thiếu mất chữ, cụ thể là &amp trong câu lệnh sau:

        if ((port_keypad & 0xF0) != 0)

        Em không hiểu mệnh đề if này có ý nghĩa gì?

        Reply
        • anh04 May 8, 2009, 10:32 pm

          Ủa, sao kỳ vậy, rõ ràng em copy nguyên câu lệnh của anh mà sao khi post lên thì chữ &amp lại biến đâu mất. Để em gõ lại vậy:
          Đây là câu lệnh em thấy: if((post_keypad &amp ; 0xF0) !=0).

          Reply
          • ngohaibac May 11, 2009, 8:39 am

            Chào em.

            Đó là dấu and đó: & . Do anh post lên cái wordpress nó hiểu nhầm thành thế thôi em.

            Chúc em thành công.

            Reply
  • tiendao87 October 29, 2009, 7:57 am

    anh bắc ơi sao chương trình lại thiếu vậy.anh có thể post hết lên được không ạ.?

    Reply
  • vanthanh October 15, 2011, 10:17 am

    cho mình các bạn chỉ dùm tý nhen
    mình dùng p3 đưa dự liệu vào
    còn p0,p1,p2 là quét cột
    mình chỉ biết quét 8 cột còn nhiều cột hơn mình chưa làm có gì mong các bạn chỉ dùm
    sơ đồ ngyên lys như sau

    Reply
  • vanthanh October 15, 2011, 10:23 am

    chương trình này viết 8×8 (cootx hàng) còn viết 16 cột hay 24 cột thì điều chỉnh như thế nào mong các bạn chỉ dùm do mới bắt đầu học nên chưa biết
    mình muốn p0,p1,p2 quét cột còn dự liệu đưa vào hàng p3

    #include

    sfr p0 = 0x80; // quet cot
    sfr p1 = 0x90;// quet cot
    sfr p2 = 0xa0; // quet cot
    sfr p3 = 0xb0;// du lieu vao hang
    // ham tre
    void delay(long timer)
    {
    while(timer–);
    }
    // khai bao bien
    unsigned char kytu1[9];// mang 9 phan tu chua cac gia tri cot ra p2
    char k=0,k1=0; // bien xac dinh cac gia tri
    // ma hoa
    void mahoa(unsigned char x)
    {
    switch(x)
    {
    case 0: // dau trang
    {
    kytu1[0]=0xff;kytu1[1]=0xff;kytu1[2]=0xff;kytu1[3]=0xff;kytu1[4]=0xff;kytu1[5]=0xff;kytu1[6]=0xff;kytu1[7]=0xff;kytu1[8]=0xff;

    break;
    }
    case 1: //chu t
    {
    kytu1[0]=0xfe;kytu1[1]=0xfe;kytu1[2]=0xfe;kytu1[3]=0x00;kytu1[4]=0x00;kytu1[5]=0xfe;kytu1[6]=0xfe;kytu1[7]=0xfe;kytu1[8]=0xff;

    break;
    case 2:// chu h
    {
    kytu1[0]=0xff;kytu1[1]=0x00;kytu1[2]=0xe7;kytu1[3]=0xe7;kytu1[4]=0xe7;kytu1[5]=0xe7;kytu1[6]=0xe7;kytu1[7]=0x00;kytu1[8]=0xff;
    break;
    }
    case 3: // chu A
    {
    kytu1[0]=0xff;kytu1[1]=0x03;kytu1[2]=0xed;kytu1[3]=0xee;kytu1[4]=0xee;kytu1[5]=0xed;kytu1[6]=0x03;kytu1[7]=0xff;kytu1[8]=0xff;

    break;
    }
    case 4: // chu N
    {
    kytu1[0]=0x00;kytu1[1]=0xfe;kytu1[2]=0xfb;kytu1[3]=0xf7;kytu1[4]=0xef;kytu1[5]=0xdf;kytu1[6]=0x3f;kytu1[7]=0x00;kytu1[8]=0xff;

    break;
    }
    case 5: // chu H
    {
    kytu1[0]=0xff;kytu1[1]=0x00;kytu1[2]=0xe7;kytu1[3]=0xe7;kytu1[4]=0xe7;kytu1[5]=0xe7;kytu1[6]=0xe7;kytu1[7]=0x00;kytu1[8]=0xff;

    break;
    }
    case 6: // chu L
    {
    kytu1[0]=0xff;kytu1[1]=0x00;kytu1[2]=0x7f;kytu1[3]=0x7f;kytu1[4]=0x7f;kytu1[5]=0x7f;kytu1[6]=0xff;kytu1[7]=0xff;kytu1[8]=0xff;

    break;
    }
    case 7: // chu IE
    {
    kytu1[0]=0x00;kytu1[1]=0xff;kytu1[2]=0x00;kytu1[3]=0x66;kytu1[4]=0x66;kytu1[5]=0x66;kytu1[6]=0x66;kytu1[7]=0xff;kytu1[8]=0xff;

    break;
    }
    case 8: // chu U
    {
    kytu1[0]=0xff;kytu1[1]=0x80;kytu1[2]=0x7f;kytu1[3]=0x7f;kytu1[4]=0x7f;kytu1[5]=0x7f;kytu1[6]=0x80;kytu1[7]=0xff;kytu1[8]=0xff;

    break;
    }
    case 9: //dautrang
    {
    kytu1[0]=0xff;kytu1[1]=0xff;kytu1[2]=0xff;kytu1[3]=0xff;kytu1[4]=0xff;kytu1[5]=0xff;kytu1[6]=0xff;kytu1[7]=0xff;kytu1[8]=0xff;

    break;
    }
    }
    }
    }

    // ham quet led ma tran_ hien thi dich
    void hienthido(void)
    {
    unsigned char n,m,lap;
    unsigned char quet[8]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};

    // cac phan tu quet hang
    for (m=0;m<8;m++) // dich hien thi
    {
    for(lap=0;lap<20;lap++)// lap hien thi ,cho chay nhanh hay cham
    {
    for (n=0;n<8;n++) // quet led cot p1
    {

    if((n+m)7) // hien thi kytu thu 2
    {
    mahoa(k+1);
    p0 = quet[n];
    p3 = kytu1[n+m-8];
    delay(30);
    }
    }

    p1 =0x00;
    p2 =0x00;
    p0 =0x00;
    p3 =0xff;
    }
    }
    }

    // ham chinh
    void main(void)
    {

    while(1)
    {

    hienthido();
    k=k+1;
    if(k==9)k=0;

    }
    }

    Reply
  • thientu2020 April 30, 2012, 12:18 pm

    anh hải ơi,em hỏi code trên (code đầu tiên)đc viết bằng phần mềm gì vậy?
    em đang dùng phần mềm ccs ,em thử doạn code trên báo lỗi nhiều lắm.
    em muốn quét phím sử dụng ngắt ngoài thì làm thế nào ?
    mong anh gợi ý!
    cảm ơn anh!

    Reply
    • ngohaibac May 31, 2012, 3:50 pm

      Cái này dùng trình dịch HTPIC mà, dùng CCS dịch thì tất nhiên là bị lỗi rồi.

      Reply

Leave a Comment