Elektroda.pl
Elektroda.pl
X

Search our partners

Find the latest content on electronic components. Datasheets.com
Elektroda.pl
Please add exception to AdBlock for elektroda.pl.
If you watch the ads, you support portal and users.

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

kamil007 03 Jan 2011 17:30 11338 36
  • #1
    kamil007
    Level 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:
    Code:

    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
    dj_west
    Level 16  
    Kłania się operator iloczynu bitowego (&).
    Code:
    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
    kamil007
    Level 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?
    Code:

     for(int i=0; i<8; i++)
      if (zmienna & (1 << i))
       {
         PORTB |= 0x01;
         }
     else
     {
      PORTB &= ~0x01;
       }
        }
  • #4
    tmf
    Moderator of Microcontroller designs
  • #5
    kamil007
    Level 12  
    Czyli jak sprawdzać ten bit??
    Code:

    for(int i=0; i<8; i++)
      if (??)
       {
         PORTB |= 0x01;
         }
     else
     {
      PORTB &= ~0x01;
       }
        zmienna<<1;
         }
  • #6
    acid12
    Level 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
    dondu
    Moderator on vacation ...
    tmf wrote:
    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
    tmf
    Moderator of Microcontroller designs
    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
    mirekk36
    Level 42  
    dondu wrote:
    tmf wrote:
    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
    gdL
    Level 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
    mirekk36
    Level 42  
    gdL wrote:
    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 wrote:
    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):

    Code:

       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 ;)

    Code:

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

    Code:
    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
    gdL
    Level 27  
    Quote:
    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ę.

    Code:
    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
    mirekk36
    Level 42  
    gdL wrote:

    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
    gdL
    Level 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
    mirekk36
    Level 42  
    dondu wrote:
    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
    tmf
    Moderator of Microcontroller designs
    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
    dondu
    Moderator on vacation ...
    mirekk36 wrote:
    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
    mirekk36
    Level 42  
    dondu wrote:
    mirekk36 wrote:
    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:

    Code:
    #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
    dondu
    Moderator on vacation ...
    tmf wrote:
    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 wrote:
    A tam zaraz jakieś morałki, coś źle mnie kolega odbiera.

    Ja z uśmieszkami to piszę ... :D i to szczerymi.
  • #24
    kamil007
    Level 12  
    dondu wrote:

    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
    Krzysztof Gustaw
    Level 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;
  • #27
    Krzysztof Gustaw
    Level 23  
    piotrva wrote:
    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
    piotrva
    VIP Meritorious for electroda.pl
    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:
    Code:

    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
    User removed account
    User removed account