Elektroda.pl
Elektroda.pl
X
Proszę, dodaj wyjątek www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

[ATmega32][C] Komunikacja z klawiaturą PC/AT

cezik 08 Gru 2008 15:49 3356 21
  • #1 08 Gru 2008 15:49
    cezik
    Poziom 10  

    Witam!

    Piszę mały programik, który ma za zadanie komunikować się z klawiaturą poprzez PS/2, czyli wyświetlać znaki przez nią wysyłane na LCD. Wszystko jest pięknie dopóki pisze się w miarę wolno. Generalnie chodzi o to, że w momencie szybkiego naciśnięcia klawiszy po sobie, nie jest wyświetlany ten pierwszy, tylko ten drugi, ale za to dwa razy. Przykład:

    -naciskam bardzo szybko po kolei "K" i "O". Na LCD pojawia się "KK".

    Sprawia to że sprawne pisanie jest praktycznie niemożliwe. Z czym może być związany taki problem? Czy taktowanie mikroprocka ma znaczenie?

    Sczytywanie znaków z klawiatury odbywa się w przerwaniu INT0 na prostym switch(case).

    Dzięki za pomoc!

    0 21
  • #2 09 Gru 2008 09:36
    GienekS
    Poziom 32  

    Nie wiem jak wygląda program ale musisz wiedzieć że klawiatura zwraca kod klawisza także w sytuacji jak zostanie dany klawisz puszczony. Czy to też uwzględniłeś ?

    0
  • #3 09 Gru 2008 20:04
    cezik
    Poziom 10  

    Tak wygląda obsługa przerwania i funkcja oczekująca na klawisz (zresztą nie napisania przeze mnie, tylko wykorzystana). Mam świadomość jak wygląda transmisja bitów od klawiatury (czyli kod klawisza-kod puszczenia-kod klawisza).

    Code:


    ISR(SIG_INTERRUPT0)
    {

       switch(i)
       {
           case 0 : // bit startu
           PS2_Dane = 0;
           i++;
           break;

           case 1 : // bity danych
           if(PS2_TEST) PS2_Dane |= 0x01;
           i++;
           break;

           case 2:
           if(PS2_TEST) PS2_Dane |= 0x02;
           i++;
           break;

           case 3:
           if(PS2_TEST) PS2_Dane |= 0x04;
           i++;
           break;

           case 4:
           if(PS2_TEST) PS2_Dane |= 0x08;
           i++;
           break;

           case 5 :
           if(PS2_TEST) PS2_Dane |= 0x10;
           i++;
           break;

           case 6:
           if(PS2_TEST) PS2_Dane |= 0x20;
           i++;
           break;

           case 7 :
           if(PS2_TEST) PS2_Dane |= 0x40;
           i++;
           break;

           case 8:
           if(PS2_TEST) PS2_Dane |= 0x80;
           i++;
           break;

           case 9 :
           PS2_Dane |= 0x00;// bit parzystości
           i++;
           break;

           case 10 :
           PS2_Dane |= 0x00; // bit stopu

           PS2_Czekaj = 1;
           i = 0;
           break;
        }

    }

    void PS2_CzekajNaKlawisz()
    {
       while(PS2_Czekaj!=1);
       PS2_Czekaj = 0;
    }

    0
  • #4 09 Gru 2008 20:33
    Dr.Vee
    VIP Zasłużony dla elektroda

    Pokaż lepiej co dalej robisz z danymi z klawiatury. Możliwe (choć mało prawdopodobne), że PS2_Dane zostaną nadpisane w przerwaniu przed odczytaniem ich w reszcie programu...

    Spróbuj wypisać scan kody, które przesyła klawiatura, może zauważysz gdzie jest błąd.

    Pozdrawiam,
    Dr.Vee

    0
  • #5 09 Gru 2008 22:21
    asembler
    Poziom 32  

    Ja to robię w ten sposób ze tworze bufor klawiatury.
    W przerwaniu bufor jest ladowany a gdy czytam w celu jakiejs reakcji czy tez wyswietlania bufor jest oprózniany. Wtedy na klawiaturze mogę pisac jak szybko tylko chcę.
    Pozdrawiam

    0
  • #6 09 Gru 2008 22:28
    cezik
    Poziom 10  

    No właśnie czytałem że klawiatura ma swój bufor, dlatego się nie zabierałem za to, ale skoro jest wymagany... możesz opisać dokładniej jak tworzysz taki bufor? Wpisujesz dane do tablicy, a później je odczytujesz?

    0
  • #7 09 Gru 2008 22:49
    asembler
    Poziom 32  

    Klawiatura jako taka nie ma swojego buforu.
    Jak tworze bufor?
    rezerwuje np 32 bajty pamieci (tablica 32 bakty) i tworze zmieną "licznik"
    W procedurze przerwania odebranie nacinietego klawisz i jego zdekodowanie wpisuje do kolejnych komorek kod klawisza. jednoczesnie zwiekszam zmienna licznik.

    Przy "wyciąganiu" danych z bufora odczytuje pierwszy bajt bufora a kolejne bajty przesuwam o jedną pozycje tak aby przy kolejnym odczycie na pierwszej pozycji bufora znalazł sie drugi bajt. Na koncu wpisuje #0 do ostatniej komorki bufora. Robię to dlatego aby przy wyciaganiu danych z bufora mozna było stwierdzic czy na pierwszej pozycji jest kod klawisz rózny od #0 (czyli nacisniety klawisz).

    0
  • #8 09 Gru 2008 23:13
    Dr.Vee
    VIP Zasłużony dla elektroda

    Nie lepiej 2 liczniki/wskaźniki? Jeden do odczytu, drugi do zapisu? Taką strukturę nazywa się buforem cyklicznym.

    Pozdrawiam,
    Dr.Vee

    0
  • #9 09 Gru 2008 23:23
    asembler
    Poziom 32  

    Oczywiscie ze mozna i tak istnieje jeszcze wiele rozwiazan tego problemu np mozna w ogole licznikow/wskazników. To co zapronowałem wydaje mi sie optymalne ezerwuje tylko 1 bajt na licznik/wskaznik, a program jest prosty.

    0
  • #10 09 Gru 2008 23:48
    Dr.Vee
    VIP Zasłużony dla elektroda

    Przy użyciu Twojego rozwiązania problemem jest zabezpieczenie struktury przed wielokrotnym dostępem.

    Jeśli po odczycie znaku najpierw zaktualizujesz licznik, a później będziesz kopiował dane, to możesz zgubić znak (przerwanie nadpisze ostatni z kopiowanych znaków).

    Jeśli najpierw będziesz kopiował, a później aktualizował licznik, to możesz:
    1) stracić znak (przerwanie zapisze nowy znak i zaktualizuje licznik, Ty go "na nowo" zaktualizujesz)
    2) zamazać znak (wpisujesz zero tam, gdzie przerwanie właśnie wpisało nowy znak)
    3) pewnie jeszcze kilka innych możliwości ;)

    Oczywiście można wyłączyć przerwania na czas odczytu - problem w tym, żeby wyłączać je na krótko.

    W metodzie z 2 licznikami przerwanie aktualizuje tylko pierwszy licznik, a procedura odczytu tylko drugi - tutaj nawet nie trzeba wyłączać przerwań. Wadą jest marnowanie 1 bajtu bufora (liczniki nie mogą być równe, bo to oznacza pusty bufor, więc można przechowywać w buforze max. n-1 znaków). Żeby tego uniknąć można zastąpić drugi licznik liczbą bajtów w buforze, ale wtedy znów trzeba uważać z aktualizacją liczników.

    Pozdrawiam,
    Dr.Vee

    0
  • #11 10 Gru 2008 00:11
    asembler
    Poziom 32  

    najprostrzą metodą zabezpieczenia (zresztą napisałes) sie jest wyłaczenie przerwania na czas przesuwania bufora i to wszystko. Co do przepelnienia sie zapisu w procedurze odczytu z klawiatury nalezy tylko sprawdzac czy nie wychodzimy z licznikiem poz zakres n bajtów bufora
    Na przesuniecie 32 bajtów mamy czas okolo 10mkrS czyli oko 160 cylki przy 16MHz oscylatora. U mnie to dział bez zarzutu z tym ze ja stosuje bufor max 16 bajtowy.

    0
  • #12 10 Gru 2008 20:14
    cezik
    Poziom 10  

    Czytam tak tą dyskusję i dochodzę do wniosku że chyba źle implementuje ten bufor...

    W obsludze przerwania klawiatury wykonuję zapis do bufora, ale tylko dwóch bajtów które lecą w trakcie naciśnięcia i puszczenia (klawisz puszczenia 0xF0 olewam). Następnie

    Code:


    ISR(SIG_INTERRUPT0)
    {

       switch(i)
       {
           case 0 : // bit startu
           PS2_Dane = 0;
           i++;
           break;

           case 1 : // bity danych
           if(PS2_TEST) PS2_Dane |= 0x01;
           i++;
           break;

           case 2:
           if(PS2_TEST) PS2_Dane |= 0x02;
           i++;
           break;

           case 3:
           if(PS2_TEST) PS2_Dane |= 0x04;
           i++;
           break;

           case 4:
           if(PS2_TEST) PS2_Dane |= 0x08;
           i++;
           break;

           case 5 :
           if(PS2_TEST) PS2_Dane |= 0x10;
           i++;
           break;

           case 6:
           if(PS2_TEST) PS2_Dane |= 0x20;
           i++;
           break;

           case 7 :
           if(PS2_TEST) PS2_Dane |= 0x40;
           i++;
           break;

           case 8:
           if(PS2_TEST) PS2_Dane |= 0x80;
           i++;
           break;

           case 9 :
           PS2_Dane |= 0x00;// bit parzystości
           i++;
           break;

           case 10 :
           PS2_Dane |= 0x00; // bit stopu
          
                   //TUTAJ NASTĘPUJE ZAPIS ZA KAŻDYM RAZEM GDY BAJT
                  // JEST RÓŻNY OD 0xF0

          if(PS2_Dane != 0xF0)




          {
             PS2_ZapiszDoBufora();
             PS2_Czekaj = 1;         
          }

           i = 0;
           break;
        }
    }


    Odczyt danych i wysłanie ich na LCD nastepuje w funkcji PS2_To_LCD. Funkcja ta jest wywoływana za każdym razem gdy przyleci jakiś bajt z klawiatury, czyli PS2_Czekaj ustawi się na 1.

    Code:


    void PS2_To_LCD()
    {
       
       
       PS2_Bufor[u+1] = 0x00; // zapisz na ostatnia pozycje tablicy 0x00

       for (k = 0; k<=sizeof(PS2_Scancodes); k++)
       {   // przeszukiwanie tablicy scankodow klawiatury
          
          do
                    // przeszukiwanie bufora klawiatury
          {
          
             if ((pgm_read_byte(&PS2_Scancodes[k]) == PS2_Bufor[u]) && l == !TRUE)
             {// jesli scankod zgodzi sie ze znakiem w buforze to idz dalej
                
                l = TRUE;
                LCD_WyslijDane(wskaznik[k]); // wyslij zgodną daną na LCD

                k=sizeof(PS2_Scancodes);
                k++;
             }
             
             else if((pgm_read_byte(&PS2_Scancodes[k]) == PS2_Bufor[u]) && l == TRUE)
             {jesli znowu sie zgodzilo to tylko ustaw flage 'l'
                l = !TRUE;
                
             }

             u++;
          }
          while (PS2_Bufor[u] != 0x00);//przeszukuj dopoki trafisz na 0x00
          
          
          u = 0; // nastepnym razem przeszukuj od nowa
       }
    }



    No i funkcja zapisu do bufora:

    Code:

    void PS2_ZapiszDoBufora()
    {
       PS2_Bufor[u] = PS2_Dane;
       u++;
    }


    Nie wiem gdzie miałbym blokować przerwania. I w ogóle nie wiem czemu program działa tak naprawdę jak poprzednio bez bufora, czyli nie nadąża gdy szybko pisze... jakieś porady?

    0
  • #13 11 Gru 2008 19:32
    Proutto
    Poziom 2  

    Nie nadąża gdy szybko piszesz? A w jaki sposób? Dalej masz podwójne znaki? Z tego co widzę, to ignorujesz tylko PS2_Dane równe 0xF0, a następujący po nim ponowny kod klawisza to już do bufora wpisujesz.

    Druga sprawa: PS2_ZapiszDoBufora() - tu przydałoby się sprawdzać czy u nie przekracza rozmiaru bufora. Tak na wszelki wypadek.

    Trzecia sprawa: PS2_To_LCD() - co jeśli podczas wykonywania tej funkcji naciśniesz klawisz? Wpiszesz kod klawisza do bufora, ale nie wiadomo gdzie. Przy wejściu do tej funkcji wyłącz przerwanie zewnętrzne od zegara PS2, a przy wyjściu z niej wyczyść flagę tego przerwania i włącz je. Swoją drogą coś nie widzę w tej funkcji zerowania u na początku...

    A jak chcesz sobie spokojnie protestować PS2 to może zacznij od wyświetlania kodów klawiszy bezpośrednio, bez dekodowania (ale jako np. hex)

    0
  • #14 15 Gru 2008 21:12
    cezik
    Poziom 10  

    Niestety ale włączenie i wyłączenie przerwań we wskazanych miejscach praktycznie przestało odbierać jakiekolwiek znaki.

    0
  • #15 16 Gru 2008 00:28
    Dr.Vee
    VIP Zasłużony dla elektroda

    Ja się tam nie dziwię, że Ci nie działa... przy takiej strukturze przetwarzania skankodów na kody znaków możesz mieć bardzo duże opóźnienia.

    Radziłem Ci już - skup się na razie na 2 rzeczach:
    1) odczytaniu bajtu z klawiatury (przerwanie),
    2) wypisaniu go na LCD/UART w postaci hex (pętla głowna).

    Przerwania musisz blokować na jak najkrótszy czas, czyli przy pobieraniu bajtów z bufora:
    1) blokuj przerwania
    2) pobierz jeden bajt, aktualizuj strukturę bufora
    3) odblokuj przerwania
    4) przetwórz bajt

    Co do przetwarzania skankod -> ascii... W tej chwili przeglądasz tablicę skankodów liniowo, więc musisz średnio przeszukać połowę tablicy, żeby znaleźć skankod - czyli O(n). Możesz albo użyć wyszukiwania binarnego dla posortowanych rosnąco wartości skankodów O(n log n), albo zrobić tablicę 256 elementówą, po jednym elemencie na skankod, którą odczytasz w czasie O(1).

    Pozdrawiam,
    Dr.Vee

    0
  • #16 27 Gru 2008 22:24
    cezik
    Poziom 10  

    Zaimplementowałem wyszukiwanie binarne tak jak radziłeś. Niestety bez efektu. Aktualnie wygląda to tak:

    Przy naciśnięciu klawiszy "Z" i "S" szybko po sobie wyświetla mi "ZSZS" więc łapie oba klawisze, ale wyświetla podwójnie. Efekt jest zamierzony ponieważ nigdzie nie mam warunku żeby sczytywać tylko jeden bajt z klawiatury. Aktualnie czyta 3 bajty, a wyświetla dwa ponieważ bajt puszczenia 0x0F nie istnieje w tablicy więc go nie odnajduję i tym samym nie wyświetlam. Niestety w momencie gdy dodaję warunek IF ELSE na pobieranie tylko jednego bajtu z klawiatury to wyświetla "ZZ" czyli źle. Natomiast pojedyncze litery są wyświetlane poprawnie. Dopiero szybkie pisanie nie wyłapuje liter pojedynczych. Podejrzewam, że ten IF ELSE psuje wszystko. Wyłączanie przerwań całkowicie sypie program.

    0
  • #17 29 Gru 2009 12:49
    jaros85
    Poziom 20  

    Witam uczę się aktualnie programowania mikrokotrolerów AVR w C i szukam jakiegoś prostego programu do obsługi klawiatury PC na ATmega8.
    Znalazłem coś takiego:
    http://radzio.dxp.pl/pckeyboard/
    Mniej więcej wiem o co chodzi ale poza tym ktoś w ogóle usuną funkcję main i program nie chce się kompilować.
    Czy ktoś mógłby podesłać jakiś prosty programik który wystarczy że kod klawisza zapisze do jakiejś zmiennej a ja już dalej sobie z tym poradzę.

    Dzięki za pomoc

    0
  • #18 29 Gru 2009 14:42
    MODI
    Poziom 16  

    kod 0xf0 jest bardzo istotny i to przez to że twój program go pomija pojawiają się te błędy.
    przykładowo wciskasz klawisz S
    otrzymujesz taką sekwencję

    0x32
    0xf0
    0x32
    a teraz jeśli wciśniesz S i zaraz potem Z

    0x32
    0xf0
    0x32
    0x46
    0xf0
    0x46
    to twój program po pominięciu 0xf0 domyślam się że co drugi odrzuca. Wszystko było by ok gdyby nie to że często zdaża (przy szybkim pisaniu)się że kolejnośc otrzymania sie zamienia
    0x32
    0x46
    0xf0
    0x32
    0xf0
    0x46
    wtedy twój program nie pominie podwójnych znaków bo nie następują po sobie
    a jeśli pomijasz co drugi to powtarza sie się ten który byl pierwszy 2 razy raz gdy został wcisniety (tu opuszczony zostaje drugi klaiwsz ktory został wcisniety) i 2 raz klawisz pierwszy kiedy został puszczony.
    Rozwiązanie jest całkiem proste
    wystarczy zrobic jakas flage ktora bedzie blokowała dodawanie do bufora kodu jeśli wystąpił zaraz po 0xf0
    pozdrawiam
    modi

    0
  • #19 30 Gru 2009 21:44
    jaros85
    Poziom 20  

    MODI napisał:
    kod 0xf0 jest bardzo istotny i to przez to że twój program go pomija pojawiają się te błędy.
    ...


    Ale czy mógłbyś dokładnie powiedzieć co jest nie tak lub po prostu podać gotowy działający kod bo dla mnie to jest zbyt rozległy materiał i nie jestem na raz obciąć całego kodu zwłaszcza że nazwy funkcji są akurat takie że jedna jest podobna do drugiej i prawie takie same nazwy mają.

    Nie chcę iść na łatwiznę ale pojąłem już jak działa uart i i2c i jestem w stanie to oprogramować ale ten kod jest strasznie pogmatwany.

    0
  • #21 31 Gru 2009 11:43
    jaros85
    Poziom 20  

    Niestety w asemblerze nie za bardzo bo nie znam dokładnie tego języka.

    0
  • #22 31 Gru 2009 12:12
    asembler
    Poziom 32  

    No to czas sie podszkolic nei taki diabel straszny.

    0