Elektroda.pl
Elektroda.pl
X
Please add exception to AdBlock for elektroda.pl.
If you watch the ads, you support portal and users.

[AT Mega] [Bascom] Wielofunkcyjna klawiatura

Freddy 18 Dec 2008 15:45 3247 13
  • #1
    Freddy
    Level 43  
    Witam,
    szukałem na forum, oraz na znanych mi zagranicznych i polskich stronach, ale nic nie znalazłem.
    Potrzebuję obsługi tzw. wielofunkcyjnej klawiatury, 4 klawisze.
    Wielofunkcyjna dlatego, że krótkie naciśnięcie ma wywołać np funkcje A1, A2 a długie powyżej 1 sekundy funkcję B1, B2. Potrzebuję jeszcze, aby to pracowało w przerwaniach.
    Kto z wami spotkał się z czyms takim ?
  • Helpful post
    #2
    dawid512
    Level 32  
    Ciekawe jest to widać w twoim profilu (Pomógł: 79927) :P ale do rzeczy.
    Według mnie coś takiego można zrealizować w następujący sposób:
    - konfigurujesz przerwanie zewnętrzne żeby było wyzwalane przerwanie zboczem opadającym, rosnącym etc.
    - w tym przerwaniu uruchamiasz timer który zlicza sobie impulsy
    - jeżeli zliczy określoną ilość impulsów to wykonujesz jakąś tam instrukcję oraz go zerujesz a następnie znowu uruchamiasz i znów nalicza określoną ilość impulsów i znowu jakaś isntrukcja
    - w momencie gdy wykona drugą instrukcję zerujesz go
  • #3
    Freddy
    Level 43  
    dawid512 wrote:
    Ciekawe jest to widać w twoim profilu (Pomógł: 79927) :P ale do rzeczy.
    Według mnie coś takiego można zrealizować w następujący sposób:
    - konfigurujesz przerwanie zewnętrzne żeby było wyzwalane przerwanie zboczem opadającym, rosnącym etc.
    - w tym przerwaniu uruchamiasz timer który zlicza sobie impulsy
    - jeżeli zliczy określoną ilość impulsów to wykonujesz jakąś tam instrukcję oraz go zerujesz a następnie znowu uruchamiasz i znów nalicza określoną ilość impulsów i znowu jakaś isntrukcja
    - w momencie gdy wykona drugą instrukcję zerujesz go

    DZięki, masz jakiś przykład, nie za bardzo mi właśnie przychodzi do głowy jak to zrobić.
    Od razu mówię, że nie chcę gotowca.
    Hmmm, rzeczywiście ciekawe to 79927 razy, tak na oko ze 2 cyfry za dużo :D
  • #4
    K_o_n_r_a_d
    Level 23  
    Napisałem się, napisałem... a tu wylogowało i przepadło a wstecz w przeglądarce nie przywróciło :( ale postaram się odtworzyć.

    W wyżej zaproponowanym sposobie musisz mieć przycisk podpięty pod zewnętrzne przerwanie.
    Ja zrobiłbym to inaczej. Obsługę przycisku zrobiłbym w przerwaniu timera. Przerwanie wywoływał co kilka milisekund. W przerwaniu najpierw redukował drgania styków (o ile potrzebne, można tez sprzętowo wyeliminować i pominąć to) np. jeśli dany stan utrzymuje się przez 3-5 przerwań tzn., że możemy ten stan uznać za stabilny (moim zdaniem z obserwacji różnych metod redukcji drgań ta jest najlepsza).

    Dalej gdy już mamy stan stabilny i jest to "stan wciśnięcia", liczymy kolejne przerwania.
    A jeśli jest to "stan puszczenia" i jednocześnie licznik przerwań z poprzedniego punktu jest >0 znaczy, że przycisk był naciśnięty, więc sprawdzamy jak długo (wartość licznika) i odpowiednio działamy (ustawiamy odpowiednia flagę itp.) i zerujemy licznik przerwań.

    Proste, łatwe do zrealizowania, brak ograniczeń ilości przycisków ani rodzaju klawiatury (czy matrycowa, czy też zwykła bezpośrednio podpinana - każda będzie działała).

    Dodano po 2 [minuty]:

    Przykładu nie mam bo nie robiłem tego jeszcze, ale myślę, że moje przemyślenia są dobre, jeśli się mylę dajcie znać co byście poprawili, może się przyda na przyszłość :)
  • #5
    Freddy
    Level 43  
    K_o_n_r_a_d :arrow: nie ma czasu na wywoływanie przerwań co kilka milisekund.
    Podpięcie pod zewnętrzne przerwanie, mogę zrealizować stosują prostą bramke diodową, coś ala ta na obrazku poniżej, oczywiście zaadaptowaną tylko do czterech przycisków.
    [AT Mega] [Bascom] Wielofunkcyjna klawiatura
    Myślałem też o wykorzystaniu expandera i2c jak poniżej, tylko jak tam zrealizować "długie naciśnięcie"
    [AT Mega] [Bascom] Wielofunkcyjna klawiatura
  • #6
    K_o_n_r_a_d
    Level 23  
    To w takim razie może uruchamiać mój pomysł przerwaniem? Wtedy te przerwania timera, co kilka ms, byłyby wykonywane tylko podczas naciśnięcia przycisku?
    1. Przerwanie zewnętrzne uruchamia timera (co kilka ms), samo blokuje siebie.
    2. W przerwaniu timera robimy to co wyżej, dodatkowo jeśli wykryjemy puszczenie przycisku (tylko dopiero jak już mamy stan stabilny) wyłączamy przerwanie timera a włączamy przerwanie zewnętrzne - jednocześnie redukcja drgań, co wykorzystując przerwanie zewnętrzne jest bardzo ważne.

    PCF8574 ma możliwość generowania przerwania podczas zmiany stanu pinu. Można to wykorzystać, ale jeśli masz wystarczającą ilość pinów w uK to nie ma potrzeby.
  • #7
    Freddy
    Level 43  
    K_o_n_r_a_d wrote:
    To w takim razie może uruchamiać mój pomysł przerwaniem? Wtedy te przerwania timera, co kilka ms, byłyby wykonywane tylko podczas naciśnięcia przycisku?
    1. Przerwanie zewnętrzne uruchamia timera (co kilka ms), samo blokuje siebie.
    2. W przerwaniu timera robimy to co wyżej, dodatkowo jeśli wykryjemy puszczenie przycisku (tylko dopiero jak już mamy stan stabilny) wyłączamy przerwanie timera a włączamy przerwanie zewnętrzne - jednocześnie redukcja drgań, co wykorzystując przerwanie zewnętrzne jest bardzo ważne.

    PCF8574 ma możliwość generowania przerwania podczas zmiany stanu pinu. Można to wykorzystać, ale jeśli masz wystarczającą ilość pinów w uK to nie ma potrzeby.

    Ad.1 Nie moge wywoływać przerwania co kilka ms, najwyżej co 50 - 100 ms
    Pinów mam dość, bo mam Mega32
  • Helpful post
    #8
    K_o_n_r_a_d
    Level 23  
    Hehe... no to następny pomysł - a jak szybko obraca się pętla główna programu? Nie wiem jak masz skonstruowany program, czy w ogóle jest? Dużej precyzji czasów chyba nie potrzeba, nie ma różnicy, moim zdaniem, czy przytrzyma się przycisk 1s czy 1,1s, więc może ten pomysł wsadzić do pętli głównej i bez przerwań.
    Wtedy w zależności od szybkości pętli sprawdzany stan klawiatury mógłby być nawet i tysiące razy na sekundę, wystarczyłoby odpowiednie wartości liczników po przeliczać (lub sprawdzić doświadczalnie).

    A co takiego ten program robi (jeśli nie tajemnica), że przerwania mogą być nie częściej jak 100ms?:)
    Możesz też i wywoływać przerwania timera co 50-100ms (uruchamiane przerwaniem zewnętrznym) i już bez programowej redukcji drgań lub też zastosować prostą sprzętową, np. mały kondensator - też będzie dobrze.
  • #9
    Freddy
    Level 43  
    Z czasem 1 sekundy podałem przykładowo, może być np. 0.5 sek, albo 1,5 sek. Rzeczywiście tu nie gra roli czas. W głównej pętli muszę jezcze obsłużyć wyświetlacz i parę czujników.
    W przerwaniach chodzi jeszcze coś. Klawiatura musi byc w przerwaniu.
  • #10
    dawid512
    Level 32  
    Skoro w przerwaniu to będziesz musiał zastosować dodatkowy kondensatorek żeby wyeliminować drgania styków. W przerwaniu albo robisz prosty licznik albo zaprzęgasz do tego timer.
  • #11
    Freddy
    Level 43  
    Z tym kondensatorkiem, to wiem, masz rację.
    Obawiam się, że będę miał małe problemy. Zrobić klawiaturkę w przerwaniu, to nie jest problem. według pierwszego, czy też drugiego schematu. Gorzej jednak będzie z tym "dłuższym trzymaniem".
    Wpadł mi do głowy jeszcze inny pomysł, zamiast "długie trzymanie" naciśnięcie dwóch klawiszy równocześnie. To też jest rozwiązanie.

    Znalazłem w sieci coś takiego, niestety nic mi to nie mówi, bo kompletnie nie znam C :cry:

    
    /************************************************************************/
    /*                                                                      */
    /*                      Debouncing 8 Keys				*/
    /*			Sampling 4 Times				*/
    /*			With Repeat Function				*/
    /*                                                                      */
    /*              Author: Peter Dannegger                                 */
    /*                      danni@specs.de                                  */
    /*                                                                      */
    /************************************************************************/
    
    #include <io.h>
    #include <interrupt.h>
    #include <signal.h>
    
    #define KEY_INPUT	PIND
    #define LED_OUTPUT	PORTB
    
    
    #define REPEAT_MASK	(1<<PD1^1<<PD2)	// repeat: key 1, 2
    #define REPEAT_START	125		// after 500ms
    #define REPEAT_NEXT	25		// every 100ms
    
    
    char key_state;				// debounced and inverted key state:
    					// bit = 1: key pressed
    char key_press;				// key press detect
    
    char key_rpt;				// key long press and repeat
    
    
    SIGNAL (SIG_OVERFLOW0)			// every 4ms at 16MHz
    {
      static char ct0, ct1, rpt;
      char i;
    
      i = key_state ^ ~KEY_INPUT;		// key changed ?
      ct0 = ~(ct0 & i);			// reset or count ct0
      ct1 = ct0 ^ (ct1 & i);		// reset or count ct1
      i &= ct0 & ct1;			// count until roll over ?
      key_state ^= i;			// then toggle debounced state
      key_press |= key_state & i;		// 0->1: key press detect
    
      if( (key_state & REPEAT_MASK) == 0 )	// check repeat function
         rpt = REPEAT_START;		// start delay
      if( --rpt == 0 ){
        rpt = REPEAT_NEXT;			// repeat delay
        key_rpt |= key_state & REPEAT_MASK;
      }
    }
    
    
    char get_key_press( char key_mask )
    {
      cli();
      key_mask &= key_press;                        // read key(s)
      key_press ^= key_mask;                        // clear key(s)
      sei();
      return key_mask;
    }
    
    
    char get_key_rpt( char key_mask )
    {
      cli();
      key_mask &= key_rpt;                        	// read key(s)
      key_rpt ^= key_mask;                        	// clear key(s)
      sei();
      return key_mask;
    }
    
    
    int main( void )
    {
      TCCR0 = 1<<CS02;				// divide by 256 * 256
      TIMSK = 1<<TOIE0;				// enable timer interrupt
    
      DDRB = 0xFF;
      sei();
    
      for(;;){					// main loop
    
    		// single press:
    
        if( get_key_press( 1<<PD0 ))		// Key 0:
          LED_OUTPUT ^= 1<<PB0;			// toggle LED 0
    
    
    		// single long press:
    
        if( get_key_rpt( 1<<PD1 )			// long press key 1
          && get_key_press( 1<<PD1 ))		// after short press:
          LED_OUTPUT ^= 1<<PB1;			// toggle LED 1
    
    
    		// repeat on long press:
    
        if( get_key_press( 1<<PD2 )			// Key 2 or
          || get_key_rpt( 1<<PD2 ))			// long press Key 2:
          LED_OUTPUT++;				// LEDs count up
      }
    }
    
  • #12
    Freddy
    Level 43  
    Czy ktos z Was pomoże mi przetłumaczyć powyższy kod w C ?
  • #13
    K_o_n_r_a_d
    Level 23  
    A jesteś świadomy tego, że jest tu przerwanie timera wywoływane co 4ms? A jest to coś podobnego do tego o czym pisałem wyżej, ale nie używa przerwań zewnętrznych a ciągle sprawdza w przerwaniach timera.
  • #14
    Freddy
    Level 43  
    Nie jestem, bo kompletnie nie znam C. To dla mnie czarna magia.
    Widzę, że muszę zrekonstruować program i coś pozmieniać.
    Znalazłem coś równie ciekawego. Procedura w Bascom opracowana na podstawie plików w ASM
    
    '-----------------------------------------------------------------------------------------
    'name                      :  TB_peda_debouncer_BP_V1.bas
    'date                      :  20.09.2008
    'copyright                 :  public domain
    'micro                     :  Mega8
    'description               :  Bulletproof Tasten-Entprell-Routine in BASCOM
    '                             Original in ASM von Peter Dannegger
    '                             an PortD.2 bis D.7 ist ein 16*2 LCD angschlossen
    '                             an PortB.0 bis B.5 sind 6 Taster angeschlossen (gegen GND)
    '                             an PortC.0 bis C.5 sind 6 LEDs angeschlossen (gegen GND)
    '                             Key_state ist aktueller Taster-Zustand gedrückt oder nicht
    '                             Key_press zeigt an, welche Taste gedrückt wurde
    '-----------------------------------------------------------------------------------------
    
    $regfile = "m8def.dat"                                      ' specify the used micro
    $crystal = 3686400                                          ' used crystal frequency
    $baud = 9600                                                ' use baud rate
    $hwstack = 32                                               ' default use 32 for the hardware stack
    $swstack = 20                                               ' default use 10 for the SW stack
    $framesize = 40                                             ' default use 40 for the frame space
    
    
    '$sim
    'REMOVE the above command for the real program !!
    '$sim is used for faster simulation
    
    
    'LCD-Display 16*2 HD44780 konfigurieren
    Config Lcd = 16 * 2                                         '16 Zeichen und 2 Zeilen
    Config Lcdpin = Pin , Db4 = Portd.4 , Db5 = Portd.5 , Db6 = Portd.6 , Db7 = Portd.7 , E = Portd.3 , Rs = Portd.2
    Cls
    '----------------------------------
    
    
    'Variablen für die Entprell-Routine
    Dim Iwr0 As Byte
    Dim Key_ct0 As Byte
    Dim Key_ct1 As Byte
    Dim Key_state As Byte
    Dim Key_press As Byte
    Dim Leds As Byte
    
    Key_ct0 = 0
    Key_ct1 = 0
    Decr Key_ct0
    Key_ct1 = Key_ct0
    Key_state = 0
    Key_press = 0
    Leds = 255                                                  'alle LEDs an
    '----------------------------------
    
    
    'Ports konfigurieren
    Ddrb = &B00000000                                           'PortB = Eingang
    Portb = &B00111111                                          'Pullup aktiv von PortB.0 bis 5
    Key_port Alias Pinb                                         '"Key_port" = PinB
    Ddrc = &B00111111                                           'PortC.0 bis C.5 = Ausgang
    Led_port Alias Portc                                        '"Led_port" = PortC
    '----------------------------------
    
    
    'Timer0-Interrupt konfigurieren (Timer0 = 8bit)
    Config Timer0 = Timer , Prescale = 256
    Enable Timer0
    On Timer0 Timer0_isr                                        'Interrupt Service Routine anspringen
    Enable Interrupts
    Timer0 = 111                                                '3,6864MHz+Prescale256 = ca.10ms
    '----------------------------------
    
    
    'Start des Hauptprogramms
    Do
       Disable Interrupts                                       'Interrupts deaktivieren
       Leds = Leds Xor Key_press                                'Job: Leds mit Tastendruck toggeln
       Key_press = 0                                            'Key_press nach dem Job löschen
       Enable Interrupts                                        'Interrupts wieder aktivieren
       Led_port = Leds                                          'LEDs ein/ausschalten
       Upperline                                                'LCD 1.Zeile Pos 1
       Lcd "K_state:" ; Bin(key_state)                          'Key_state (Taste gedrückt?) auf LCD
       Lowerline                                                'LCD 2.Zeile Pos 1
       Lcd "K_press:" ; Bin(key_press)                          'Key_press auf LCD (0,da Auge zu träge)
    Loop
    
    End
    '==============================================================================
    
    
    'Interrupt Service Routine für Timer0
    Timer0_isr:
    Timer0 = 111
    Iwr0 = Key_port
    Iwr0 = Not Iwr0
    Iwr0 = Iwr0 Xor Key_state
    Key_ct0 = Key_ct0 And Iwr0
    Key_ct1 = Key_ct1 And Iwr0
    Key_ct0 = Not Key_ct0
    Key_ct1 = Key_ct1 Xor Key_ct0
    Iwr0 = Iwr0 And Key_ct0
    Iwr0 = Iwr0 And Key_ct1
    Key_state = Key_state Xor Iwr0
    Iwr0 = Iwr0 And Key_state
    Key_press = Key_press Or Iwr0
    '
    ' insert other timer functions here
    '
    Return
    '----------------------------------