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

[AVR][C] Problem z obsługą klawiatury numerycznej

ADI-mistrzu 03 Sty 2009 16:28 3082 17
  • #1 5943252
    ADI-mistrzu
    Poziom 30  
    Witam!
    Staram się napisać na razie dość prosty program, który ma wyświetlić cyfrę, która została wciśnięta na klawiaturze matrycowej 3x4.

    Program działa prawie poprawnie, jedynie jedną kolumnę dziwnie odczytuje, mianowicie "podnosi ją do góry".
    
     1  2  3
     4  5  6
     7  8  9
     A  0  B
    

    Tzn tam gdzie powinno być 1 jest 4, tam gdzie 4 jest 7 itd.

    Reszta klawiatury działa tak jak trzeba, nie mogę tylko rozwiązać problemu z tą jedną kolumną.
    Skanowanie odbywa się bez przerwań, ponieważ wykorzystuje ATTiny2313 który ma ich nie za wiele i chce je wykorzystać do czegoś innego.
    Program:
    
    #include <avr/io.h>
    
    #define F_CPU 4000000
    #include <util/delay.h>
    
    unsigned int i=0, j, cyfra=0;
    unsigned char znaki[17] = {192,249,164,176,153,146,130,248,128,144,136,131,198,161,134,142,0}; //pokolei 0, 1, 2,...
    
    int skan_klaw(void){
    for(i=0;i<4;++i){
    PORTD = ~_BV(i+3); //funkcja wpisująca 0 do pojedyńczego bitu
    if(bit_is_clear(PIND, PD0)){ //sprawdzanie stanu kolumny 1
    cyfra = (i*3)+1;
    return cyfra;}
    if(bit_is_clear(PIND, PD1)){ //sprawdzanie stanu kolumny 2
    cyfra = (i*3)+2;
    return cyfra;}
    if(bit_is_clear(PIND, PD2)){ //sprawdzanie stanu kolumny 3
    cyfra = (i*3)+3;
    return cyfra;}
    }
    return 0;
    }
    
    int main(void)
    {
    
    DDRA = 0x00;
    PORTA = 0xFF;
    
    DDRB = 0xFF; //Port B jako wyjścia
    PORTB = 0xFF;//Podciągnij
    
    DDRD = 0xF8; // Piny 0, 1 i 2 jako wejścia, reszta wyjście
    PORTD = 0xFF; //podciagnij
    
    while(1){
    
    j = skan_klaw();
    if(j!=0){
    PORTB = znaki[j];
    
    }}
    return 0;
    }
    


    Chciałem jeszcze algorytm do skanowania klawiatury inaczej napisać, wykorzystując przesunięcia bitowe, ale ginę w tym... kompletnie to mi nie wychodzi, jeszcze do końca tego nie pojmuję.

    Układ jest zmontowany następująco:
    Pod port B podpięty jest wyświetlacz 8 segmentowy (8. - ósemka z kropką).
    Portu A jeszcze nie wykorzystuję.
    Do portu D podpiąłem klawiaturę. Bity PD0/1/2 to kolumny, zaś PD3/4/5/6 to wiersze.
    Na początku program ma za zadanie ustawić 0 na jednym z wierszy a następnie sprawdzić kolumny czy nie nastąpiło zwarcie. Jeśli tak to wpisuje numer do zmiennej cyfra która jest zwracana z funkcji. Jeśli nie nastąpiło skanuje następny wiersz i następny...

    -------------------------------------

    Trochę pogrzebałem i stwierdziłem że dziwacznie program się wykonuje.
    tak jak by zamiast linijki:
    
    ...
    if(bit_is_clear(PIND, PD0)){ //sprawdzanie stanu kolumny 1
    cyfra = (i*3)+1;
    ...

    Kompilował coś takiego:
    
    ...
    if(bit_is_clear(PIND, PD0)){ //sprawdzanie stanu kolumny 1
    ++i;
    cyfra = (i*3)+1;
    ...

    I głowie się dlaczego, bo poniższe linijki są identyczne niemal że, a działają prawidłowo, tylko tutaj coś głupieje.
  • #4 5945463
    kasaidolar
    Poziom 19  
    skoro zerujesz bit na poczatku for'a to ustaw go na nowo po sprawdzeniu wszystkich kolumn czyli na koncu for'a

    
    	for(i=0; i<4; i++)
    	{
    		PORTD &= ~_BV(i+3); //funkcja wpisująca 0 do pojedyńczego bitu
    		
    		if(bit_is_clear(PIND, PD0)){//sprawdzanie stanu kolumny 1
    		cyfra = (i*3)+1;
    		return cyfra;
    		}
    	
    		if(bit_is_clear(PIND, PD1)){ //sprawdzanie stanu kolumny 2
    		cyfra = (i*3)+2;
    		return cyfra;
    		}
    	
    		if(bit_is_clear(PIND, PD2)){ //sprawdzanie stanu kolumny 3
    		cyfra = (i*3)+3;
    		return cyfra;
    		}
    		PORTD |= _BV(i+3); //funkcja wpisująca 1 do pojedyńczego bitu
    	}	
    
  • #5 5945760
    anomelif
    Poziom 12  
    Szanowni Przedmówcy, to co piszecie nie jest zgodne z prawdą.

    PORTD = ~_BV(i+3);
    powoduje wpisanie do portu konkretnej wartości, a nie operacje na pojedynczych bitach.

    Np: PORTD = ~_BV(3);
    jest tłumaczony na:

    LDI R24,0xF7
    OUT 0x12,R24

    Ponieważ PORTD.7 nie jest używany, takie podejście nie spowoduje błędnego działania.
    Zatem problem nie tkwi z pewnością w tym miejscu.

    Jeśli chodzi o meritum - program skompilowałem i ... u mnie działa poprawnie - tzn funkcja zwraca wartości zgodne z oczekiwaniami (na emulatorze). Może masz źle zdefiniowane znaki dla wyświetlacza LED?

    Załącz hexa, może faktycznie kompilator robi fikołki? (choć to mało prawdopodobne)
    A najlepiej po prostu odpal ten program na debugerze.
  • #6 5946408
    Dr.Vee
    VIP Zasłużony dla elektroda
    Proponuję:
    1) uprość kod i zastosuj zmienne lokalne typu uint8_t,
    2) wklej listing asemblerowy + wersje kompilatora + linię komend użytą do kompilacji (opcje, optymalizacja)

    Poza tym kompilator zoptymalizuje operację (i*3)+3 do ((++i)*3) - w ten sposób od razu zostanie obliczona kolejna wartość i do wykorzystania w teście w nagłówku pętli.

    No i na koniec - u Ciebie w tablicy znaki[] są kody znaków od 0 do F, ale nijak ma się to do klawiatury - lepiej dać dodatkową tablicę z mapowaniem klawisz -> znak, np:
    const uint8_t znaki[] = {...};
    const uint8_t klawiatura[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 11};
    Oczywiście tablice można umieścić w pamięci flash.

    Pozdrawiam,
    Dr.Vee
  • #7 5956495
    ADI-mistrzu
    Poziom 30  
    Wybaczcie że nie pisałem przez pewien czas. Choroba mnie złapała taka, że nie byłem w stanie usiąść nawet na chwile do komputera.

    Powiem szczerze że jeszcze nie wiem jak zrobić z tym mapowaniem klawisz -> znak, tak naprawdę robiąc ten projekt od razu douczam się języka.
    Ale wydaje mi się że narazie to chyba niema znaczenia nad tym, że teraz program działa nie prawidłowo.

    Kod zmieniłem na następujący:
    
    #include <avr/io.h>
    
    int main(void)
    {
    
    DDRB = 0xFF; //Port B jako wyjścia
    PORTB = 0xFF;//Podciągnij
    
    DDRD = 0xF8; // Piny 0, 1 i 2 jako wejścia, reszta wyjście
    PORTD = 0xFF; //podciagnij
    
    uint8_t j;
    const uint8_t znaki[17] = {192,249,164,176,153,146,130,248,128,144,136,131,198,161,134,142,0}; //pokolei 0, 1, 2,...
    
    int skan_klaw(void){
    uint8_t i = 0;
    for(i=0;i<4;++i){
    PORTD = ~_BV(i+3); //funkcja wpisująca 0 do pojedyńczego bitu
    if(bit_is_clear(PIND, PD0)){ //sprawdzanie stanu kolumny 1
    return ((i*3)+1);}
    if(bit_is_clear(PIND, PD1)){ //sprawdzanie stanu kolumny 2
    return (i*3)+2;}
    if(bit_is_clear(PIND, PD2)){ //sprawdzanie stanu kolumny 3
    return (i*3)+3;}
    }
    PORTD = _BV(i+3);
    return 0;
    }
    
    while(1){
    
    j = skan_klaw();
    if(j!=0){
    PORTB = znaki[j];
    
    }}
    return 0;
    }


    Wersja AVR-GCC 4.2.4 środowisko Linux.
    Programowanie odbywa się za pomocą tych 3 lini:
    
    avr-gcc -mmcu=attiny2313 kod.c -Os
    avr-objcopy -O ihex -R .eeprom a.out program.hex
    avrdude -p attiny2313 -c stk200 -U flash:w:program.hex


    W załączniku listing.
  • #8 5957125
    Dr.Vee
    VIP Zasłużony dla elektroda
    Sprawdź taki program:
    #include <avr/io.h>
    #include <avr/pgmspace.h>
    
    /* znaki 0-F dla wyswietlacza 7-seg */
    static const uint8_t znaki[] PROGMEM = {
        0b11000000, 0b11111001, 0b10100100, 0b10110000, 0b10011001, 0b10010010, 0b10000010, 0b11111000,
        0b10000000, 0b10010000, 0b10001000, 0b10000011, 0b11000110, 0b10100001, 0b10000110, 0b10001110
    };
    
    #define NROWS 4
    #define NCOLS 3
    
    /* indeksy do tablicy znaków w kolejności Row1Col1, Row1Col2... */
    static const uint8_t klawiatura[] PROGMEM =
        {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0, 0xb};
    
    /* Zwróć -1 gdy żaden klawisz nie jest naciśnięty */
    static int8_t skan_klaw(void)
    {
        uint8_t i;
        uint8_t row_mask;
    
        for (i = 0, row_mask = _BV(NCOLS);
             i < NROWS * NCOLS ;
             row_mask <<= 1)
        {
            uint8_t mask;
    
            PORTD = ~row_mask;
    
            for (mask = _BV(0); mask < _BV(NCOLS); mask <<= 1, ++i)
            {
                if (!(PIND & mask))
                    return pgm_read_byte(&klawiatura[i]);
            }
            /* tutaj i = i + NCOLS */
        }
        return -1;
    }
    
    int main(void)
    {
        DDRB = 0xFF;		//Port B jako wyjścia
        PORTB = 0xFF;		//Podciągnij
    
        DDRD = 0xF8;		// Piny 0, 1 i 2 jako wejścia, reszta wyjście
        PORTD = 0xFF;		//podciagnij
    
        while (1) {
            int8_t kod = skan_klaw();
            if (kod >= 0) {
                PORTB = pgm_read_byte(&znaki[kod]);
            }
        }
    }

    Pozdrawiam,
    Dr.Vee
  • #9 5957332
    ADI-mistrzu
    Poziom 30  
    Program działa identycznie, ten sam problem z przesunięciem kolumny.

    Na wszelki wypadek wrzucam obrazek z podpisanymi pinami, które gdzie idą.
    [AVR][C] Problem z obsługą klawiatury numerycznej

    Sprawdziłem także czy przypadkiem nie jest uszkodzony ATTiny, wstawiłem drugi jeszcze nie używany ale efekt ten sam.

    P.S.

    Z ciekawości chciałem sprawdzić jak zadziała program po przepięciu kolumny z portu PD0 na PA0 i jakie było moje zaskoczenie gdy prawidłowo 1 weszła, potem 2, 3 i tu jeszcze bardziej mnie zaskoczyło gdy zamiast 4 weszła 7... :D

    Już nic nie rozumiem, jak ten program może tak działać ? A tym bardziej że procesor był sprawdzony.

    Mógł by ktoś wrzucić gotowy plik HEX do wgrania na kontroler ? Może coś u mnie źle go wykonuje.

    Pozdrawiam
  • #10 5960283
    Dr.Vee
    VIP Zasłużony dla elektroda
    Masz w załączniku HEXa, ale sądze, że masz raczej coś z hardware. Napisz które przyciski dają które kody, np:
    przycisk 1 -> kod ...
    przycisk 2 -> kod ...
    itd.

    Pozdrawiam,
    Dr.Vee
  • #11 5969462
    ADI-mistrzu
    Poziom 30  
    Miałeś rację, problem nie tkwi w programie.

    A kody tzn ? Chodzi jaką wartość zwraca Return w funkcji skan_klaw() ?
    Jeśli tak to kody są identyczne z tymi, które się przyciski wciska, czyli jak wciśniemy jedynkę (PD0 i PD3) to funkcja zwróci 1.

    Czy chodzi Ci o to jakie cyfry wyświetlają się teraz ?
    Jeśli o to to wygląda to następująco:
    
    Przycisk 1 -> 4
    Przycisk 2 -> 2
    Przycisk 3 -> 3
    Przycisk 4 -> 7
    Przycisk 5 -> 5
    Przycisk 6 -> 6
    Przycisk 7 -> A
    Przycisk 8 -> 8
    Przycisk 9 -> 9
    Przycisk 10 -> 1
    Przycisk 11 -> 0
    Przycisk 12 -> B
  • #12 5970095
    Dr.Vee
    VIP Zasłużony dla elektroda
    Skoro nie tkwi w programie, to czy już zlokalizowałeś błąd?

    Z tego, co widzę, to 8 nie dostajesz ani razu, a oprócz tego pierwsza kolumna jest "obrócona". Sprawdź może lepiej tą swoją klawiaturę, co? :)
    Albo podłączenie wyświetlacza/kody znaków 7-seg.

    Pozdrawiam,
    Dr.Vee
  • #13 5970522
    ADI-mistrzu
    Poziom 30  
    Oj, jest ósemka, kopiowałem jedną linijkę i zmieniałem wartości i musiałem tą jedną rubrykę przeoczyć.
    Ogółem gdyby narysować już numeracje na tych klawiszach, to aby się zgadzały musiała by klawiatura wyglądać następująco:
    
    -------------
    | 4 | 2 | 3 |
    -------------
    | 7 | 5 | 6 |
    -------------
    | A | 8 | 9 |
    -------------
    | 1 | 0 | B |
    -------------
    

    Tak chyba łatwiej zobaczyć jak teraz wygląda klawiatura programowo.

    Błędu ciągle nie mogę znaleźć.
    Przyznam się że klawiatura jest prowizoryczna, zrobiona z zestawu wylutowanych przycisków z myszek, przylutowanych do druta 2mm (sztywny, nie lata wszystko) i krótkimi kabelkami przylutowane do podstawki w której jest kontroler.
    Błędu w konstrukcji na pewno niema, jest to tak przejrzyste że widać wszystko od razu.

    Chyba że szumy się wdają, ale to tez mało prawdopodobne, bo tak by wszystkie klawisze szwankowały, a tu tylko jedna kolumna dziwnie pracuje.

    W weekend wykonam drugą, nieco lepiej, może ruszy.

    Po za tym może znacie jakieś schematy klawiatur nawet z jakimś scalakiem, które dobrze chodzą i są godne polecenia ?
  • #14 5970760
    Dr.Vee
    VIP Zasłużony dla elektroda
    No jak nie możesz znaleźć... na skróty można zmienić układ klawiatury w tablicy klaw[] na taki, jaki Ci pasuje ;)

    Albo spróbuj jeszcze zamienić kolejność sprawdzania kolumn, o tak:
    --- klaw.c	2009-01-08 21:20:43.000000000 +0000
    +++ klaw.c.new	2009-01-08 21:23:34.000000000 +0000
    @@ -10,9 +10,9 @@
     #define NROWS 4
     #define NCOLS 3
     
    -/* indeksy do tablicy znaków w kolejności Row1Col1, Row1Col2... */
    +/* indeksy do tablicy znaków w kolejności Row1Col3, Row1Col2... */
     static const uint8_t klawiatura[] PROGMEM =
    -    {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0, 0xb};
    +    {3, 2, 1, 6, 5, 4, 9, 8, 7, 0xb, 0, 0xa};
     
     /* Zwróć -1 gdy żaden klawisz nie jest naciśnięty */
     static int8_t skan_klaw(void)
    @@ -28,7 +28,7 @@
     
             PORTD = ~row_mask;
     
    -        for (mask = _BV(0); mask < _BV(NCOLS); mask <<= 1, ++i)
    +        for (mask = _BV(NCOLS-1); mask > 0; mask /= 2, ++i)
             {
                 if (!(PIND & mask))
                     return pgm_read_byte(&klawiatura[i]);

    Pozdrawiam,
    Dr.Vee
  • #15 5970959
    ADI-mistrzu
    Poziom 30  
    Teraz trzeci rząd jest przestawiony, czyli zamiast 3 mam 6, za 6 jest 9 itd.

    Jeszcze trochę a ta klawiatura i program do niej będzie się nadawał do konkursu na elektrodzie "zjawiska nie wyjaśnione" :D
  • Pomocny post
    #16 5971297
    Dr.Vee
    VIP Zasłużony dla elektroda
    Już chyba wiem co i jak... ;)
    Spróbuj takiej modyfikacji (do mojego pierwszego programu):
    --- klaw.c	2009-01-08 21:24:31.000000000 +0000
    +++ klaw.c.new2	2009-01-08 22:52:53.000000000 +0000
    @@ -1,5 +1,8 @@
    +#define F_CPU 4000000UL /* czy jaką tam miałeś F_CPU */
    +
     #include <avr/io.h>
     #include <avr/pgmspace.h>
    +#include <util/delay.h>
     
     /* znaki 0-F dla wyswietlacza 7-seg */
     static const uint8_t znaki[] PROGMEM = {
    @@ -28,6 +31,8 @@
     
             PORTD = ~row_mask;
     
    +        _delay_us(2);
    +
             for (mask = _BV(0); mask < _BV(NCOLS); mask <<= 1, ++i)
             {
                 if (!(PIND & mask))

    Pozdrawiam,
    Dr.Vee
  • #17 5976367
    ADI-mistrzu
    Poziom 30  
    Chyba ja też już wiem co było nie tak.
    Port niemiał czasu się ustawić i trzeba było dać mu tą chwile ?
  • #18 5976814
    Dr.Vee
    VIP Zasłużony dla elektroda
    Zapewne jakaś minimalna pojemność do przeładowania... Pewnie 2-3 nop-y by też wystarczyły.

    No i teraz musisz poszukać innego "zjawiska niewyjaśnionego" ;)

    Pozdrawiam,
    Dr.Vee
REKLAMA