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

AVR język "C" Wyciąganie bitu z bajtu.

kamil007 03 Sty 2011 17:30 11971 36
  • #1 8952255
    kamil007
    Poziom 12  
    Witam!
    Potrzebuję w moim programie kolejno wyciągać bit ze zmiennej i sprawdzać jego stan logiczny.
    Ponieważ sam nie bardzo wiem jak to zrobić proszę o pomoc.
    Miało by to wyglądać jakoś tak:
    
    for (k = 0; k < 8; k++)
    {
     // Teraz sprawdzanie logicznego stanu bitu w zmiennej , jeżeli prawda
           (HIGH) to PORTB.1 = 1;
    else;
    PORTB.1 = 0;
    zmienna <<= 1; // Tu nie wiem czy dobrze kombinuję
    }

    Nie wiem jak sprawdzać stan tego bitu, może coś źle kombinuję od początku??
  • #2 8952333
    dj_west
    Poziom 17  
    Kłania się operator iloczynu bitowego (&).
    if (zmienna & (1 << nr_bitu)) {
       // jakieś działania
    }


    EDIT:

    No i w sumie słusznie w tytule wątku wpisałeś "C" w cudzysłowie... Bo to rzeczywiście takie C z dużym przymrużeniem oka ;)
  • #3 8952961
    kamil007
    Poziom 12  
    He, he dzięki ,
    a poza tym jest to dział "początkujący" i miał być to taki pseudo kod.
    OK, czyli to będzie coś takiego?
    
     for(int i=0; i<8; i++)
      if (zmienna & (1 << i)) 
       { 
         PORTB |= 0x01;
         }
     else
     { 
      PORTB &= ~0x01;
       }
        }
  • #4 8953043
    tmf
    VIP Zasłużony dla elektroda
    Może być tak, przy czym ponieważ operacja << na AVR jest kosztowna, lepiej robić zmienna<<1 i za każdym razem testować najstarszy bit.
  • #5 8955270
    kamil007
    Poziom 12  
    Czyli jak sprawdzać ten bit??
    
    for(int i=0; i<8; i++) 
      if (??) 
       { 
         PORTB |= 0x01; 
         } 
     else 
     { 
      PORTB &= ~0x01; 
       } 
        zmienna<<1;
         }
  • #6 8955498
    acid12
    Poziom 21  
    kamil007: operacja zmienna << 1, bedzie przesuwać bity w bajcie w lewo, czyli bit 0 stanie sie 1; bit 3 stanie sie 4; bit 6 stanie sie 7; a bit 7 zniknie, więc musisz testować bit nr 7 - najstarszy, znajdujący się po lewej stronie.

    zapoznaj się z operatorem logicznym AND ( & ) i testuj najstarszy bit
  • #8 8955562
    dondu
    Moderator na urlopie...
    tmf napisał:
    Może być tak, przy czym ponieważ operacja << na AVR jest kosztowna, lepiej robić zmienna<<1 i za każdym razem testować najstarszy bit.

    Czy mógłbyś wyjaśnić co masz na myśli pisząc: ... jest kosztowna ... ?
  • #9 8955578
    tmf
    VIP Zasłużony dla elektroda
    Rozpisz sobie jak wygląda przesuwanie bajtu o 1, 2, 3, 4, 5, 6 i 7 bajtów to zobaczysz lub przejrzyj plik lss z kompilacji programu, który to robi. Dodatkowo w C jest duża szansa na promocję typów do int i wtedy to się robi prawdziwa masakra.
  • #10 8955585
    mirekk36
    Poziom 42  
    dondu napisał:
    tmf napisał:
    Może być tak, przy czym ponieważ operacja << na AVR jest kosztowna, lepiej robić zmienna<<1 i za każdym razem testować najstarszy bit.

    Czy mógłbyś wyjaśnić co masz na myśli pisząc: ... jest kosztowna ... ?


    Spójrz na wielkość kodu po realizacji obydwu wariantów czy też ilość polecń w asemblerze a zobaczysz naocznie co znaczy, że jest kosztowna. Przypadek opisany przez tmf wygeneruje mniejszy kod a efekt będzie ten sam.
  • #12 8955800
    gdL
    Poziom 27  
    if (zmienna & (1 << i)), ale mniej kosztowne, niż operatory >,<,==,!= ? ASM nie znam, ale czytałem kiedyś opracowania o optymalizacji kodu, tam zasadniczo polecali wszelkiego typu operatory bitowe, zamiast boolowskich.

    Poza tym, w GCC nie ma wartości typu bool (0/1), najmniejsze są chyba char. Dlatego char może trzymać 8 wartości bool. Lepiej używać przesunięć bitowych i 8 booli w 1 char, niż mieć 8 char'ów, żeby trzymać tam po 1 boolu i używać przy nim porównań >,<,==,!=?

    Pytanie też z której strony, ze względu na pamięć tak, ale czy ze względu na czas operacji ? To dobre pytania o optymalizacji, ale pewno niekończąca się dyskusja, co najszybciej, co najoptymalniej.

    "NIE POPADAJMY W PARANOJĘ", zwłaszcza, że często używa się delay(x) :-)
  • #13 8956037
    mirekk36
    Poziom 42  
    gdL napisał:
    if (zmienna & (1 << i)), ale mniej kosztowne, niż operatory >,<,==,!= ? ASM nie znam, ale czytałem kiedyś opracowania o optymalizacji kodu, tam zasadniczo polecali wszelkiego typu operatory bitowe, zamiast boolowskich.


    A kto tu mówi o operatorach boolowskich? Poza tym to nie jest kwestia żeby coś tam kiedyś przeczytać tylko bieżącej praktyki programowania.

    gdL napisał:
    To dobre pytania o optymalizacji, ale pewno niekończąca się dyskusja, co najszybciej, co najoptymalniej.

    "NIE POPADAJMY W PARANOJĘ", zwłaszcza, że często używa się delay(x) :-)


    Kolega coś źle zrozumiał sugestię związaną z tym jak rozwiązać podane zagadnienie żeby był mniejszy koszt jak i kwestię samego kosztu.

    W takim razie wyraźnie podam jak można to sobie porównać i zobaczyć różnicę, pierwsza wersja poniżej (z większym kosztem):

    
    	uint8_t i;
    	for(i=0; i<8; i++) {
    		if (zmienna & (1 << i)) {
    			PORTB |= (1<<PB0);
    		} else {
    			PORTB &= ~(1<<PB0);
    		}
    	}
    


    a tu wersja, która sporo bajtów mniej kosztuje ;)

    
    	uint8_t i;
    	for(i=0; i<8; i++) {
    		if( zmienna & 0x01 ) PORTB |= (1<<PB0);
    		else PORTB &= ~(1<<PB0);
    		zmienna >>= 1;
    	}
    
  • #14 8956260
    dondu
    Moderator na urlopie...
    A możne coś w tym stylu?:

    uint8_t i; 
    for(i=1; i>0; i<<1 ) { 
    	if( zmienna & i ){
    		PORTB |= (1<<PB0);
    	}else{
    		PORTB &= ~(1<<PB0); 
    	}
    } 
    

    Nie sprawdzałem czy to zadziała, ale jeżeli tak to byłoby jeszcze szybsze bo nie byłoby zapisywania zmiennej do ramu (gdy volatile) przy każdym przesunięciu bitu. Po wykonaniu pętli zmienna ma nadal początkową wartość.

    Jak rozumie dla autora tematu nie ma znaczenia czy ZMIENNA na końcu będzie miała pierwotną wartość czy 0 (jak w Waszych przykładach), ponieważ dopuszcza w swoich przykładach oba przypadki.
  • #15 8956268
    gdL
    Poziom 27  
    Cytat:
    Kolega coś źle zrozumiał sugestię związaną z tym jak rozwiązać podane zagadnienie żeby był mniejszy koszt jak i kwestię samego kosztu.


    Dobrze zrozumiał, ale nie chce mi się tłumaczyć. Całość mojego postu abstrahuje od oryginalnego problemu. Tu się zgadzam, że rozwiązanie przedstawione rzeczywiście jest szybsze.

    Często kwestie szybkości i używanej pamięci mocno się rozmijają. Sposoby optymalizacji w obydwu przypadkach są skrajnie różne. Żeby to lepiej rozumieć, wypadałoby poznać działanie kompilatora i język maszynowy, czego niestety ja nie umiem. Jeszcze. Ale może trafisz do mnie do szpitala to Cię wyleczę.

    uint8_t i; 
       for(i=8; i>0; i--) { 
          if( zmienna & 0x01 ) PORTB |= (1<<PB0); 
          else PORTB &= ~(1<<PB0); 
          zmienna >>= 1; 
       } 


    Podobno tak jest szybciej (dekrementacja), ale wersja mojego przedpiscy i tak bardziej mi się podoba.
  • #16 8956380
    mirekk36
    Poziom 42  
    gdL napisał:

    Często kwestie szybkości i używanej pamięci mocno się rozmijają. Sposoby optymalizacji w obydwu przypadkach są skrajnie różne. Żeby to lepiej rozumieć, wypadałoby poznać działanie kompilatora i język maszynowy, czego niestety ja nie umiem. Jeszcze. Ale może trafisz do mnie do szpitala to Cię wyleczę.


    Ale ja do ciebie nic nie mam ;) a co do wyleczenia to zacna umiejętność, że tak powiem.

    Chodziło mi tylko o to, że warto czasem przyglądać się takim ciekawym rozwiązaniom bo później to już wchodzi w krew w trakcie programowania. A na szczęście nie trzeba od razu tutaj znać asemblera (choć wiadomo, że jest on bardzo przydatny)..... Za to porównanie kompilacji na tak prostych 2 przykładach pokazuje stosunkowo sporą oszczędność na bajtach kodu i to jest w tym przypadku bardziej istotne niż kwestia szybkości wykonywania.

    I oczywiście masz rację, że jeśli w procku pozostaje np 30% nie wykorzystanej pamięci FLASH to nie ma żadnej różnicy czy będzie to takie czy inne rozwiązanie. Jeśli jednak rozbudowujemy program i zaczyna być CIASNO to wtedy sam przyznasz, że takie optymalizacje własnego kodu są bardzo przydatne ;)

    Dodano po 1 [minuty]:

    Zresztą myślę, że teraz gdy zobaczyłeś taki sposób, to zwykle zapada on w pamięci i na przyszłość będziesz też i ty starał się od razu z tego korzystać. Bo wbrew pozorom - po jakimś czasie wydaje się on bardziej przyjazny i oczywisty niż ten pierwszy.
  • #18 8956461
    gdL
    Poziom 27  
    @mirekk36 : Tak, z całym powyższym postem mogę się zgodzić. Jeśli chodzi o szybkość wykonywania, to zdaje się jest coś takiego jak profiling. Używałem profilera wbudowanego w DevC++ można zobaczyć która funkcja wykonuje się jak długo statystycznie rzecz ujmując, ile jest wywołań. Jeśli zależy na szybkości, to laik, na ładnie wypisanych numerkach może się dużo nauczyć.

    Google odpowiedziało mi, że profiling pod AVR można zrobić w "VMLAB for AVR". Programu nie mam, nie dotykałem i nie wiem na ile jest przydatny. Drugie "coś", to "Avrora simulator".
  • #19 8956550
    mirekk36
    Poziom 42  
    dondu napisał:
    A przy okazji pytanie związane z tym tematem: Co z flagą CARRY?
    Tzn. jak przy przesuwaniu łapać wypadający bit?


    No ale to jest przecież całkiem inny temat ;) niż ten tutaj poruszany. Bo tu nie interesuje nas co się dzieje z wypadającym bitem, przecież badamy jego stan zanim wypadnie. A całkiem inną kwestią jest zorganizowanie "łapania" takiego wypadającego bitu i użycia go do innych celów. Ale to by trzeba było od nowa jakiś kodzik napisać i chyba nie stanowi to dużego problemu.
  • #20 8956559
    tmf
    VIP Zasłużony dla elektroda
    Korzystać z profilera warto, ale dla złożonych problemów. Jeśli będziesz bawił się w profiler z takimi prostymi operacjami to nigdy nic nie napiszesz:) Po prostu warto wyrabiać sobie dobre nawyki, nawet jeśli doraźnie one nie pomagają, to w końcu trafisz na problem, w którego rozwiązaniu ci pomogą.
    Dondu: Z flagą Carry nic nie zrobisz, bo C nie obsługuje takich niuansów. Jeśli potrzebujesz łapać wylatujące bity to rozszerz typ, wtedy będą one trafiać na dodatkowy bajt. Inną możliwością jest po prostu napisanie wstawki w assemblerze.
  • #21 8956571
    dondu
    Moderator na urlopie...
    mirekk36 napisał:
    No ale to jest przecież całkiem inny temat ;) niż ten tutaj poruszany. Bo tu nie interesuje nas co się dzieje z wypadającym bitem, przecież badamy jego stan zanim wypadnie.

    Ach te morałki :D:D, jakby się tak stricte tytułu tematu trzymać, to właśnie o to autorowi chodziło.
  • #22 8956604
    mirekk36
    Poziom 42  
    dondu napisał:
    mirekk36 napisał:
    No ale to jest przecież całkiem inny temat ;) niż ten tutaj poruszany. Bo tu nie interesuje nas co się dzieje z wypadającym bitem, przecież badamy jego stan zanim wypadnie.

    Ach te morałki :D:D, jakby się tak stricte tytułu tematu trzymać, to właśnie o to autorowi chodziło.


    A tam zaraz jakieś morałki, coś źle mnie kolega odbiera. Zobacz więc przykład odpowiedników poleceń ROL , ROR w języku C:

    #define ROTATE_RIGHT(x) x=(x>>1)|(x<<7)
    #define ROTATE_LEFT(x) x=(x<<1)|(x>>7)

    a w innych przypadkach tak jak pisał tmf - definiujesz sobie "szerszą" zmienną ;) czyli zamiast uint8_t to uint16_t i przesuwając możesz od razu badać stan bitu w kolejnym bajcie - czyli taki ala sztuczny CARRY.
  • #23 8956609
    dondu
    Moderator na urlopie...
    tmf napisał:
    Dondu: Z flagą Carry nic nie zrobisz, bo C nie obsługuje takich niuansów. Jeśli potrzebujesz łapać wylatujące bity to rozszerz typ, wtedy będą one trafiać na dodatkowy bajt. Inną możliwością jest po prostu napisanie wstawki w assemblerze.

    Szukałem tego niedawno w Tutorialu C i nie znalazłem, dzięki teraz już mam pewność i więcej szukać nie będę.

    Dodano po 1 [minuty]:

    mirekk36 napisał:
    A tam zaraz jakieś morałki, coś źle mnie kolega odbiera.

    Ja z uśmieszkami to piszę ... :D i to szczerymi.
  • #24 8957151
    kamil007
    Poziom 12  
    dondu napisał:

    Ach te morałki :D:D, jakby się tak stricte tytułu tematu trzymać, to właśnie o to autorowi chodziło.

    Hehe , no chyba ja wiem lepiej o co mi chodziło :D
    Dziękuję wszystkim a w szczególności kolegom , za konkretne przykłady,
    bo tylko takie są dla mnie bezcennym źródłem edukacji.
  • #25 9997017
    Krzysztof Gustaw
    Poziom 23  
    Jak na mój gust, możesz spróbować coś takiego:

    Zakładam, że "zmienna" została zadeklarowana wcześniej i jako typ int...

    int zmienna;
    .
    .
    .
    .
    int k;
    for (k = 1; k <= 128; k += k)
    PORTB.1 = (zmienna & k) != 0;

    Bez większych kombinacji, zwarty zapis, mało bajtów (mam nadzieję...)

    Wykorzystano tu następujące właściwości:
    dodanie zmiennej do samej siebie jest tym samym, co przesunięcie o 1 w lewo a chyba zajmuje mniej cykli maszynowych, a jeśli nie, to można w trzecim członie pętli for zamiast k += k wpisać k << 1 co daje dokładnie taki sam rezultat.

    Wykorzystano właściwości rezultatu operatora przyrównania: jeśli rezultatem jest FAŁSZ to wartość wynosi 0, a jeśli jest PRAWDA to rezultat jest równy 1.
    Jeśli by tak nie było (mało prawdopodobne) to można się uratować w ten sposób:
    PORTB.1 = ((zmienna & k) != 0) & 1;

    Dodano po 7 [minuty]:

    Odnośnie mojego poprzedniego postu z dnia 5.10.2011 w "zamiast k+=k wpisać k<<1" powinno być: "zamiast k+=k wpisać k <<= 1"
    Sorry za przekłamanie...

    Dodano po 17 [minuty]:

    Można jeszcze pętlę zapisać tak:
    for (k = 1; k & 255; k += 1) PORTB.1 = (zmienna & k) != 0;

    Jeśli k osiągnie wartość 0 to wtedy pętla zakończy swoje działanie, a jeśli kompilator wygeneruje jakąś uwagę w tej materii to żeby uspokoić jego niepokój można napisać:

    for (k = 1; (k & 255) != 0; k += 1) PORTB.1 = zmienna & k != 0;
  • #26 9997212
    piotrva
    VIP Zasłużony dla elektroda
    A od kiedy w c mamy taki dostęp do bitów w zmiennych i rejestrach?
  • #27 10000105
    Krzysztof Gustaw
    Poziom 23  
    piotrva napisał:
    A od kiedy w c mamy taki dostęp do bitów w zmiennych i rejestrach?


    Witam!
    Co do zapisu PORT.1 faktycznie, moja pomyłka, przepraszam, powinienem nazwać PORX czy jakoś tak. A co do dostepu do pojedynczych bitów w języku C taka możliwość istnieje (nie wiem jak w AVR C).
    Jeśli zdefiniujemy strukture np tak:

    struct {
    unsigned a1 : 1; /* pierwsze pole jednobitowe w słowie */
    unsigned a2 : 1; /* drugie pole jednobitowe w słowie */
    unsigned a3 : 2; /* a tu dla odmiany pole dwubitowe */
    } PORTX;

    i odwołujemy się tak jak do klasycznej struktury:
    PORTX.a1, PORTX.a2, PORTX.a3 itp
    Możemy ustawić bit np 1: PORTX.a1 = 1;
    Wyzerować bit np 2: PORTX.a2 = 0;
    sprawdzić; PORTX.a1 == 0; itd.

    Pola są wielkościami całkowitymi bez znaku (unsigned), mogą być umieszczane wyłącznie w słowach (int lub unsigned)

    Jest parę myków: W zależności od maszyny pola mogą być tworzone w kolejności od najmłodszego bitu słowa do najstarszego, albo odwrotnie. Trzeba to dobrze sprawdzić.Jeśli pole wyjdzie poza granice słowa, automatycznie zostanie utworzone w nastepnym słowie. Jeśli nie chcemy miec bitów po kolei, np bit 1 a potem 3, bit 2 nas nie interesuje, to przerwy pomiędzy nimi wypełniamy definiując nienazwane pole. Jesli chcemy wymusic przejście do nastepnego słowa to podajemy rozmiar pola 0 (zero). Pola nie są tablicami, nie mają adresu więc zabronione jest użycie jednoargumentowego operatora & w stosunku do nich
  • #28 10000241
    piotrva
    VIP Zasłużony dla elektroda
    Tak, owszem, ale w C dla AVR (przynajmniej o AVR GCC mówię) takie zabiegi wymagają niezłej gimnastyki i przepisywania rejestr->struktura->rejestr (da się to zrobić za pomocą "paru" myków, ale ich wykonanie to przerost formy nad treścią). Ja osobiście sprawdzam (i inni też) stany bitów tak:
    
    if(REJESTR&(1<<NR_BITU)) bit_ustawiony();
    if(!(REJESTR&(1<<NR_BITU))) bit_skasowany();
    REJESTR|=(1<<NR_BITU); //ustawiamy bit w rejestrze
    REJESTR&=~(1<<NR_BITU); //kasujemy bit w rejestrze
    
  • #30 10001786
    Konto nie istnieje
    Konto nie istnieje  
REKLAMA