logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

[Atmega128][C] Klawiatura matrycowa 4x4

wdogli 11 Lis 2008 13:26 9473 8
  • #1 5725649
    wdogli
    Poziom 18  
    Witam.
    Zbudowałem sobie klawiaturę matrycową 4x4 do mikrokontrolera Atmega 128. Klawiatura jest podłączona do 8 wyjść portu D. Obsługę klawiatury chce realizować poprzez przerwania wykorzystując przerwania zewnętrzne INT0...INT3
    Poniżej przedstawiam kody:

    DEFINICJE:

    
    #define KLAD  DDRD                                     // PORT LINII STERUJACYCH
    #define KLAP  PORTD
    //#define WE1  0                                         // LINIA WEJSCIE
    //#define WE2  1                                         // LINIA WEJSCIE
    //#define WE3  2                                         // LINIA WEJSCIE
    //#define WE4  3                                         // LINIA WEJSCIE
    
    #define WY1  4                                         // LINIA WYJSCIE
    #define WY2  5                                         // LINIA WYJSCIE
    #define WY3  6                                         // LINIA WYJSCIE
    #define WY4  7                                         // LINIA WYJSCIE
    // 
    #define SET_WY1  KLAP |= _BV(WY1) 
    #define CLR_WY1  KLAP &= ~_BV(WY1) 
    // 
    #define SET_WY2  KLAP |= _BV(WY2) 
    #define CLR_WY2  KLAP &= ~_BV(WY2)
    // 
    #define SET_WY3  KLAP |= _BV(WY3) 
    #define CLR_WY3  KLAP &= ~_BV(WY3) 
     // 
    #define SET_WY4  KLAP |= _BV(WY4) 
    #define CLR_WY4  KLAP &= ~_BV(WY4)
    
    



    KONFIGURACJA ORAZ ODBLOKOWANIE PRZERWAN NA INT0...INT3

    
    
    void on_int0_3()
    {
    EICRA = 0xAA;                                             // wejscie reaguje na opadajace zbocze
    EIMSK = 0x0F;                                             // odblokowuje przerwania na wejsciach INT0...INT3
    SREG |= _BV(7);                                           // globalne odblokowanie przerwan
    }
    
    


    ODNOSNIKI PRZERWAN

    
    SIGNAL(SIG_INTERRUPT0)
    {
    skan_klaw();
    }
    SIGNAL(SIG_INTERRUPT1)
    {
    skan_klaw();
    }
    SIGNAL(SIG_INTERRUPT2)
    {
    skan_klaw();
    }
    SIGNAL(SIG_INTERRUPT3)
    {
    skan_klaw();
    }
    
    


    FUNKCJA skan_klaw();

    
    
    void skan_klaw(void)                                      // procedura odczytu całej klawiatury
    {
    //KLAD = 0xF0;                                        // przygotowanie portów WY=> jako wyjścia; WE=> jako wejscia
    KLAP = 0xFF;                                        // przygotowanie pinów  WY=> stan wysoki;  WE=> reaguje na GND
    dlms(2);
    CLR_WY1; 
    if(bit_is_clear(PIND,PD0))                            // WE1 => S1
    {
    dlms(20);                                                 
    if(bit_is_clear(PIND,PD0))
    {
    lcd_goto(1,2);
    lcd_write_string("S1 ");
    }
    }
    if(bit_is_clear(PIND,PD1))                            // WE2 => S2
    {                    
    dlms(20);                                                 
    if(bit_is_clear(PIND,PD1))
    {
    lcd_goto(1,1);
    lcd_write_string("S2 ");
    }
    }
    if(bit_is_clear(PIND,PD2))                            // WE3 => S3
    {                    
    dlms(20);                                                 
    if(bit_is_clear(PIND,PD2))
    {
    lcd_goto(1,1);
    lcd_write_string("S3 ");
    }
    }
    if(bit_is_clear(PIND,PD3))                            // WE4 => S4
    {
    dlms(20);                                                
    if(bit_is_clear(PIND,PD3))
    {
    lcd_goto(1,1);
    lcd_write_string("S4 ");
    }
    }
    SET_WY1;
    CLR_WY2;
    if(bit_is_clear(PIND,PD0))                            // WE1 => S5
    {
    dlms(20);                                                 
    if(bit_is_clear(PIND,PD0))
    {
    lcd_goto(1,1);
    lcd_write_string("S5 ");
    }
    }
    if(bit_is_clear(PIND,PD1))                            // WE2 => S6
    {
    dlms(20);                                                 
    if(bit_is_clear(PIND,PD1))
    {
    lcd_goto(1,1);
    lcd_write_string("S6 ");
    }
    }
    if(bit_is_clear(PIND,PD2))                            // WE3 => S7
    {
    dlms(20);                                                 
    if(bit_is_clear(PIND,PD2))
    {
    lcd_goto(1,1);
    lcd_write_string("S7 ");
    }
    }
    if(bit_is_clear(PIND,PD3))                            // WE4 => S8
    {
    dlms(20);                                                 
    if(bit_is_clear(PIND,PD3))
    {
    lcd_goto(1,1);
    lcd_write_string("S8 ");
    }
    }
    SET_WY2;
    CLR_WY3;
    if(bit_is_clear(PIND,PD0))                            // WE1 => S12
    {
    dlms(20);                                                 
    if(bit_is_clear(PIND,PD0))
    {
    lcd_goto(1,1);
    lcd_write_string("S12");
    }
    }
    if(bit_is_clear(PIND,PD1))                            // WE2 => S11
    {
    dlms(20);                                                
    if(bit_is_clear(PIND,PD1))
    {
    lcd_goto(1,1);
    lcd_write_string("S11");
    }
    }
    if(bit_is_clear(PIND,PD2))                            // WE3 => S10
    {
    dlms(20);                                                 
    if(bit_is_clear(PIND,PD2))
    {
    lcd_goto(1,1);
    lcd_write_string("S10");
    }
    }
    if(bit_is_clear(PIND,PD3))                            // WE4 => S9
    {
    dlms(20);                                                
    if(bit_is_clear(PIND,PD3))
    {
    lcd_goto(1,1);
    lcd_write_string("S9");
    }
    }
    SET_WY3;
    CLR_WY4;
    if(bit_is_clear(PIND,PD0))                            // WE1 => S13
    {
    dlms(20);                                                
    if(bit_is_clear(PIND,PD0))
    {
    lcd_goto(1,1);
    lcd_write_string("S13");
    }
    }
    if(bit_is_clear(PIND,PD1))                            // WE2 => S14
    {
    dlms(20);                                                 
    if(bit_is_clear(PIND,PD1))
    {
    lcd_goto(1,1);
    lcd_write_string("S14");
    }
    }
    if(bit_is_clear(PIND,PD2))                            // WE3 => S15
    {
    dlms(20);                                                 
    if(bit_is_clear(PIND,PD2))
    {
    lcd_goto(1,1);
    lcd_write_string("S15");
    }
    }
    if(bit_is_clear(PIND,PD3))                            // WE4 => S16
    {
    dlms(20);                                                 
    if(bit_is_clear(PIND,PD3))
    {
    lcd_goto(1,1);
    lcd_write_string("S16");
    }
    }
    SET_WY4;
    KLAP = 0x0F;
    }
    
    


    FUNKCJA MAIN

    
    
    int main (void) 
    { 
    KLAD = 0xF0;                                        // przygotowanie portów WY=> jako wyjscia; WE=> jako wejscia
    KLAP = 0x0F;
    on_int0_3();
    lcdg_init1();
    lcd_wyswietlaj();
       while(1) 
       { 
        
       } 
    }    
    
    


    PROBLEM!!!

    Zasada działania takiego układu jest następująca:
    1) Odpowiednia konfiguracja pinów PORTU D
    (PIND0-3 jako wejścia reagujące na GND)
    (PIND4-7 jako wyjścia w stanie niskim)
    2)w związku z powyższym zwarcie styku powoduje powstanie zbocza opadającego na wejściu i wyzwolenie przerwania zewnętrznego.

    I teraz mój problem.

    Działają mi wszystkie przyciski oprócz jednej kolumny. Kolumny podporządkowanej Przerwaniu INT0
    I teraz gdy na krótko wejście INT0 podpinam pod GND przerwanie zostaje wywołane. Natomiast gdy próbuję je wyzwolić za pomocą przycisku lub na krótko podpinając do pinu wyjścia przerwanie nie zostaje wywołane.

    Dodam jeszcze iż sprawdziłem klawiaturę zamieniając przewody w skutek czego doszedłem do wniosku że sama klawiatura jest wykonana poprawnie.

    PYTANIE:

    Czy wykorzystywanie przerwania INT0 w atmedze128 wymaga poza standardowymi jakiegoś dodatkowego konfigurowania go w rejestrach atmegi?? a może popełniam jakieś błędy w kodzie choć w przypadku kiedy na tym samym kodzie działają inne przyciski wydaje mi się wątpliwa.

    Proszę o pomoc.

    Pozdrawiam
  • #2 5726571
    skynet_2
    Poziom 26  
    powiem że strasznie trudno mi się w tym programie połapać.
    Ale czy nie możesz dużo prościej sprawdzać który przycisk jest wciśnięty ?

    w obsłudze przerwań dajesz skan_klaw(0-3);//0-3 to numer przerwania które zadziałało
    
    void skan_klaw(uint8_t i)//i to numer przerwania od 0 do 3
    {
    uint8_t j = (i*4 + (PIND>>4));//sprawdzenie numeru przycisku
    char bufor[2];//zmienna pomocnicza do wyświetlacza
    
    _delay_ms(20);
    
    if(j == (i*4 + (PIND>>4))){//sprawdzenie czy po 20ms przycisk nadal jest wciścięty
    
    lcd_goto(1,1); 
    lcd_write_string("S");
    
    utoa(j,bufor,10);
    lcd_write_string(bufor);
    
    lcd_write_string(" ");
    
    }else{break;}
    
    }
    


    zrobiłeś matrycę jakoś tak ?
    [Atmega128][C] Klawiatura matrycowa 4x4
  • #3 5726963
    wdogli
    Poziom 18  
    Witam.
    Faktycznie ja to może robię trochę na piechotę ale inaczej nie potrafię.
    Jeśli chodzi o klawiaturę to mam ją wykonaną wg tego schematu:

    [Atmega128][C] Klawiatura matrycowa 4x4


    A teraz postaram się trochę lepiej opisać w/w kod a więc:

    Na początku definiuje nazwy zamienne czyli zamiast
    DDRD będe używał KLAD
    natomiast zamiast
    PORTD będe używał KLAP
    potem definiuje nazwy linii oraz numer pinu do którego jest ta linia podłączona czyli
    WY1 linia wyjście 1 jest podłączona do linii nr 4 i tak dalej
    następnie definiuje nazwy skrótowe
    #define SET_WY1 KLAP |= _BV(WY1)
    czyli jeśli w kodzie napisze SET_WY1 to mikrokontroler ustawi mi stan wysoki na linii nr 4 portu D
    No i to wszystko jeśli chodzi o definicje.

    Teraz kody:
    a więc

    void on_int0_3() - funkcja odblokowująca przerwania
    czyli
    EICRA = 0xAA; - wpisuje do rejestru EICRA liczbę która będzie powodowała że przerwanie będzie wyzwalane gdy zaistnieje zbocze opadające,

    EIMSK = 0x0F; - wpisuje do rejestru EIMSK liczbę która będzie powodowała odblokowanie przerwań na wyjściach PD0 do PD3 czyli INT0 do INT3

    SREG |= _BV(7); - globalne odblokowanie przerwań

    Teraz kawałek kodu który odpowiada za to co ma zrobic mikrokontroler gdy zaistnieje przerwanie czyli:

    SIGNAL(SIG_INTERRUPT0) gdy wystąpi przerwanie na INT0 wykonaj....
    {
    skan_klaw(); - funkcja do wykonania.....
    }

    .
    .
    .i tak dalej

    No i prawie na sam koniec funkcja do wykonania czyli:

    void skan_klaw(void) czyli skanuj klawiaturę a więc:

    KLAP = 0xFF;
    - ustawiam na wszystkich liniach wyjściowych stan wysoki natomiast na liniach wejściowych reakcje na stan niski.
    dlms(2); - czekaj 2 mili sekundy
    CLR_WY1; - ustaw stan niski na linii wejściowej czyli na linii PD4 czyli będe skanował pierwszy wiersz przycisków
    if(bit_is_clear(PIND,PD0)) teraz sprawdzam na linii wejściowej PD0 czy linia jest w stanie niskim
    { -jeśli jest
    dlms(20); - czekam 20mili sekund
    if(bit_is_clear(PIND,PD0)) - i sprawdzam ponownie
    { - jeśli nadal jest
    lcd_goto(1,2); - skacze do odpowiedniego miejsca na wyswietlaczu
    lcd_write_string("S1 "); - i pisze S1 czyli przyciśnięto przycisk nr1
    }

    następnie skanuje kolejne przyciski w tym wierszu po czym:

    SET_WY1; ustawiam stan wysoki na lini wyjście 1
    CLR_WY2; - i ustawiam stan niski na linii wyjście 2 aby skanować kolejny wiersz przycisków
    No i tak do samego końca
    Na końcu jeszcze ustawiam na wszystkich liniach wyjściowych stan niski KLAP = 0x0F; aby było możliwe zaistnienie przerwania opadającym zboczem.
    No i na sam koniec funkcja main

    int main (void)
    {
    KLAD = 0xF0; Port D PINY 0-3 ustawiam jako wejścia PINY 4-7 ustawiam jako wyjścia
    KLAP = 0x0F; Ustawiam stan niski na liniach wyjściowych PINY 4-7 aby mogło powstać przerwanie w skutek zwarcia mikrostyku oraz ustawiam reakcje PINY 0-3 na stan niski
    on_int0_3(); - inicjuje przerwania
    lcdg_init1(); - inicjuje wyświetlacz
    lcd_wyswietlaj(); procedury związane z zapisem parametrów startowych do sterownika wyświetlacza.
    while(1)
    {

    }
    }

    Dzięki za pomoc ale na razie nie znalazłem rozwiązania mojego problemu
    Dlaczego działają mi wszystkie przyciski oprócz przycisków S1, S5, S9, S13 czyli przyciski które są podpięte do wejścia WE1 czyli do pinu odpowiadającego INT0
    Pozdrawiam
  • #4 5727252
    skynet_2
    Poziom 26  
    ponieważ nie znalazłem przyczyny dlaczego nie działa, trzeba będzie sprawdzać po kolei:
    //zastosowałem nowsze nazwy przerwań Link
    najpierw przerwania
    SIGNAL(INT0_vect) 
    { 
    lcd_goto(1,1); 
    lcd_write_string("INT0");
    } 
    SIGNAL(INT1_vect) 
    { 
    lcd_goto(1,1); 
    lcd_write_string("INT1");
    } 
    SIGNAL(INT2_vect) 
    { 
    lcd_goto(1,1); 
    lcd_write_string("INT2");
    } 
    SIGNAL(INT3_vect) 
    { 
    lcd_goto(1,1); 
    lcd_write_string("INT3");
    }


    jak to zadziała znaczy że przerwania działają, na wyświetlaczu dostaniesz info która kolumna czy wiersz[zależy jak podłączyłeś] jest aktywowana.
  • #5 5727486
    wdogli
    Poziom 18  
    No to dzięki tobie jestem o kroczek dalej :)
    Wstawiłem przez ciebie napisany fragment programu i na wyświetlaczu wyświetliły mi się wszystkie napisy. czyli INT0, INT1, INT2, INT3 dzięki temu wiem już że przerwania działają.
    Następnie zastosowałem podane przez ciebie nazwy przerwań w pierwotnym programie i niestety działa tak samo jak poprzednio czyli błędnie wnioskuję z tego iż mam źle napisany kod którym skanuję klawiaturę. W związku z tym postaram się zastosować zaproponowany przez ciebie kod do skanowania klawiatury.
    Bardzo dziękuję za pomoc.
    Jeśli masz jeszcze jakieś sugestie chętnie z nich skorzystam. :)
    O wynikach moich zmagań jeszcze napiszę.
    Dzięki Wielkie
    Pozdrawiam
  • #6 5727854
    skynet_2
    Poziom 26  
    ten kod napisałem pod troszkę inny układ więc nie zadziała, zorientowałem się jak dałeś schemat.

    edit: tu masz poprawiony

    void skan_klaw(uint8_t i)//i to numer przerwania od 0 do 3
    {
    
    DDRD = 0xf0;
    PORTD= 0x0f;
    
    uint8_t j = ( i*4 ) + ( PIND>>4 );//zwraca numer przycisku w matrycy
    
    _delay_ms(20);
    
    if(j == (( i*4 )+( PIND>>4 )) ){
    
    lcd_goto(1,1); 
    lcd_write_string("S"); 
    
    char bufor[2];//zmienna pomocnicza do wyświetlacza 
    utoa(j,bufor,10);//konwersja z int do char
    
    lcd_write_string(bufor); 
    
    lcd_write_string(" ");
    }
    }


    SIGNAL(INT0_vect) 
    { 
    skan_klaw(0);//musisz podać parametr
    }


    funkcja wykrywa[przynajmniej powinna :D] aktualnie wciśnięty przycisk, jeśli chcesz detekcje wielu przycisków jednocześnie trzeba to wrzucić w pętle for[tylko 4 cykle] i zmieniać PORTD przy każdym cyklu.
  • #7 5729643
    wdogli
    Poziom 18  
    Hej jeszcze tego nie wypróbowałem bo zabiorę się za to dopiero wieczorkiem ale tak na moje oko to nie będzie działać a jeśli będzie to raczej wadliwie.

    uint8_t j = ( i*4 ) + ( PIND>>4 );//zwraca numer przycisku w matrycy 


    w/w kawałek kodu ma za zadanie obliczyć numer wciśniętego przycisku.
    jak na moje oko wyrazenie
    i*4 w zależności od numeru przerwania będzie przyjmować wielkość 0; 4; 8; 12 natomiast wyrazenie
    PIND>>4 będzie przyjmować wartości 1; 2; 4; 8 i teraz przy pierwszych dwóch
    czyli gdy wcisnę przyciski S1 dostane wartość 0+1 i tu wszystko gra;
    Natomiast gdy wcisnę przycisk S3 to otrzymam wartość 0+4 gdyż
    PIND=01000000
    po przesunięciu o cztery miejsca czyli
    PIND>>4
    przyjmie wartość
    0100 a to daje w systemie dziesiętnym liczbę 4 i tutaj podejrzewam będą błędy.
    Co ty o tym sądzisz??
    Pozdrawiam
  • Pomocny post
    #8 5729971
    skynet_2
    Poziom 26  
    Oops pomyliłem system binarnym z dziesiętni wagami bitów :D
    zaraz poprawie

    edit:najprościej
    uint8_t k;//deklaruje wcześniej ponieważ będe używał poza pętlą
    
    for(k=0; k<4; k++){
    if( (PIND>>(4+k))&0x1 ){k++;break;}//k++ ponieważ otrzymam 0-3 a potrzebuke 1-4
    uint8_t j = ( i*4 ) + k;


    jeszcze
    if(j == (( i*4 )+( PIND>>4 )) ){

    zamień na
    for(k=0; k<4; k++){
    if( (PIND>>(4+k))&0x1 ){k++;break;}
    
    if(j == (( i*4 )+ k) ){
  • #9 5733320
    wdogli
    Poziom 18  
    Witam ponownie.
    Przeprowadziłem wczoraj kilka prób i doszedłem do wniosku że problem z działaniem klawiatury nie wynika z błednego oprogramowania.
    Co zauważyłem:
    w czasie gdy żaden przycisk nie jest wciśnięty napięcie pomiędzy wejściami a wyjściami jest około +5V
    Gdy wgram program który ma za zadanie jedynie wykryć przerwanie i wyświetlić na wyświetlaczu INT0 do INT3 w zależności które przerwanie zadziałało mierząc napięcia pomiędzy wejściami a wyjściami to w momencie naciśnięcia przycisku napięcie pomiędzy nimi spada do 0 i tak dzieje się pomiędzy wszystkimi wejściami i wyjściami. A więc wszystko jest poprawnie.

    Natomiast gdy wgram program który ma wykryć przerwanie a następnie odczytać który przycisk został wciśnięty to w momencie wciśnięcia przycisków które przynależą wo wejść od INT1 do INT3 podobnie jak powyrzej napiecie spada do zera.
    Nie dzieje sie tak w przypadku przycisków przynależących do pinu INT0 wygląda to tak jakgdyby pin PD0 po przerwaniu nie pelnił roli pinu typu I/O. I to jest wydaje mi się przyczyna tego że nie działa mi pierwsza kolumna klawiatury natomiast wszystkie pozostałe działają.

    Dodam jeszcze ze gdy zamienię miejscami wejścia to znaczy na przykład wejście 1 podłącze na miejsce wejścia 2 a wejście 2 na wejście 1 w złączu klawiatury to nie działa ta kolumna do której jest podłączona wejście 1 czyli INT0. A wiec stad wniosek ze klawiatura działa poprawnie.

    Teraz konfiguracja wyjść:

    PD0-PD3/INT0-INT3 => wejścia
    PD4-PD7 => WYJŚCIA

    stan pinow w trakcie na oczekiwanie na przerwanie:
    DDRD=0xF0;
    PORTD=0x0F;
    czyli wejścia na swoich pinach mają stan niski natomiast wejścia reagują na stan niski

    teraz po wykryciu przerwania:

    DDRD=0xF0;
    PORTD=0xFF;

    i teraz skanowanie:
    skan pierwszego wiersza:
    PORTD=0x7F; czyli 01111111
    skan drugiero wiersza:
    PORTD=0xBF; czyli 10111111
    skan trzeciego wiersza:
    PORTD=0xDF; czyli 11011111
    skan czwartego wiersza:
    PORTD=0xEF; czyli 11101111

    Wydaje mi sie ze wszystko jest poprawnie.
    Czy ktoś wie może czy wyjście PD0 ma jakieś inne przeznaczenie albo czy w jakichś okolicznościach przestaje być pinem typu I/O
    Pozdrawiam


    EDIT: 16.11.2008r.

    No i problem rozwiązany. :)
    To znaczy rozwiązany w pewnym sensie a mianowicie nie udało mi się tej klawiatury uruchomić na przerwaniach jednak przy standardowym skanowaniu wejść i wyjść klawiatury klawiatura hula jak ta lala.
    Obecne buduję nową klawiaturę tym razem 3x4 wg tego schematu Link z zastosowaniem kostki KS74HCTLS21N gdyż bardzo mi zależy na klawiaturze działającej na przerwaniach nawet kosztem ilości przycisków no i oczywiście z założeniem iż nie może ona zajmować więcej niż jeden port mikrokontrolera.
    Wielkie dzięki za pomoc dla skynet_2
    Serdecznie pozdrawiam :)
REKLAMA