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

[AVR][C] Jak zapisać tablice int bit po bicie

mrrudzin 24 Mar 2009 21:35 3939 6
REKLAMA
  • #1 6328140
    mrrudzin
    Poziom 39  
    Mam 16 elementową tablicę int TAB[16];
    Z zewnątrz dostaje dane do tablicy, ale bit po bicie. Jak najprościej wypełnić całą tablicę? Jak możnaby zbudować jakiś mechanizm który zaczłąby wypełniać tablicę od zerowego bitu elementu TAB[0]?

    Możnaby użyć wskaźnika?
  • REKLAMA
  • #2 6328323
    BoskiDialer
    Poziom 34  
    Rozwiązanie pierwsze: zbierać po 16 bitów i zapisywać całe komórki; rozwiązanie drugie: napisać funkcję, która będzie aktualizować konkretny bit; rozwiązanie trzecie: podobnie jak rozwiązanie 2, ale w celu zaoszczędzenia cykli wprowadzone pewne modyfikacje.

    W kodzie wyglądało by to jakoś tak:
    
    uint8_t get_bit(void); // funkcja zwracająca odebrany bit
    
    // rozwiązanie pierwsze
    uint8_t i;
    for(i=0; i<16; i++)
    {
      int v = 0;
      int mask = 1;
      for(v=0, mask=1; mask; mask<<=1)
        if(get_bit())
          v |= mask;
      TAB[i] = v;
    }
    
    // rozwiązanie drugie
    void update_bit(int T[], uint8_t bit, uint8_t bitval)
    {
      int mask = 1 << (bit & 15);
      int tmp = T[bit >> 4];
      if(bitval)
        tmp |= mask;
      else
        tmp &= ~mask;
      T[bit >> 4] = tmp;
    }
    
    uint16_t i;
    for(i=0; i<256; i++)
      update_bit(TAB, i, get_bit());
    

    Rozwiązania trzeciego nie chce mi się pisać, chodzi o to aby zamienić przesunięcia w lewo o zmienną wartość w przesunięcia o stałą wartość, dzięki czemu zaoszczędzi się sporo cykli.

    Co do wskaźników - same nie wystarczą, nie można nimi adresować poszczególnych bitów.
  • REKLAMA
  • #3 6331140
    mrrudzin
    Poziom 39  
    Bajty idą ciurkiem (0 czy 1 odczytuje w przerwaniu). Chodziło mi o to, żeby stworzyć dłuugą zmieną
    (unsigned long long BITY)

    i korzystając z inkrementacji zmiennej i wypełniać zmienną
    if(odebral_1) BITY|=1<<i;


    Teraz dalej żeby zadeklarować wskaźnik pokazujący na początek tej zmiennej, który będzie miał długość 8 bitów
    uint8_t *wsk=&BITY; 

    i następnie skacząc wskaźnikiem po zmiennej BITY odczytywać sobie je po kolei
    DANE[i]=*(wsk+i) 
  • REKLAMA
  • #5 6331307
    mrrudzin
    Poziom 39  
    Może źle sprecyzowałem w czym problem i nie zdążyłęm edytować.

    Jeszcze raz. Nie chodzi mi o zapisywanie bitów za pomocą wskaźnika (nie znalazłem typu bitowego i to pewnie byłby pierwszy problem) - to rozwiązałem jak w poście wyżej , a o odczytywanie obszaru pamięci za pomocą wskaźnika typu bajtowego.

    Odczyt bajtów zrobiłem tak:
    Bajty idą ciurkiem (0 czy 1 odczytuje w przerwaniu). Zatem tworze długą zmienną
    (unsigned long long BITY)

    i korzystając z inkrementacji zmiennej i wypełniam ją
    if(odebral_1) BITY|=1<<i;

    Czyli wykonuje jakąś wariację rozwiązania kolegi wyżej.

    Teraz dalej chciałbym odczytać to co otrzymałem. Pomysł jest taki, żeby stworzyć wskaźnik pokazujący na początek tej zmiennej, który będzie miał długość 8 bitów
    uint8_t *wsk=&BITY; 

    i następnie skacząc wskaźnikiem po zmiennej BITY odczytywać sobie je po kolei
    DANE[i]=*(wsk+i) 


    Czy to rozwiązanie jest poprawne?
  • #6 6331364
    Dr.Vee
    VIP Zasłużony dla elektroda
    Cały czas wyrażasz się nieprecyzyjnie. Może napisz kawałek kodu i skomentuj jak wg. Ciebie miałby on działać. I nie pomyl bitów z bajtami...

    Tak czy inaczej najmniejszą adresowalną jednostką pamięci jest bajt. A użycie wskaźnika to dokładnie to samo co użycie tablicy (oprócz tego, że wskaźnik to 16 bitów a indeks tablicy może mieć np. 8).

    Pozdrawiam,
    Dr.Vee
  • REKLAMA
  • Pomocny post
    #7 6331645
    BoskiDialer
    Poziom 34  
    Rozwiązanie drugie które napisałem, praktycznie w postaci aktualnej nadaje się do przerwań - dodać tylko jedną zmienną która będzie informować ile aktualnie odczytano bajtów i samą funkcję update_bit można wywoływać po razie w przerwaniu, jako trzeci argument podając odczytany bit. Równie dobrze można przekształcić rozwiązanie pierwsze wyciągając z ciała funkcji zmienną indeksującą tablicę (ewentualnie zamieniając we wskaźnik) oraz maskę (wyciągnięcie to da w efekcie rozwiązanie trzecie).
    
    // rozwiązanie drugie na przerwaniach
    volatile uint8_t rx_bitpos;
    ISR(jakies_przerwanie_vect)
    {
      uint8_t bitval = get_bit();
      uint8_t bp = rx_bitpos;
      if(bp < 128)
      {
        update_bit(TAB, bp, bitval);
        rx_bitpos = bp+1;
      }
    }
    /* inicjalizacja: rx_bitpos=0; zakończenie informowane przez to, że rx_bitpos == 128 */
    
    
    // rozwiązanie trzecie na przerwaniach
    volatile uint8_t rx_bitpos;
    volatile int rx_cache;
    volatile int rx_mask;
    volatile int* volatile rx_ptr;
    ISR(jakies_przerwanie_vect)
    {
      uint8_t bitval = get_bit();
      uint8_t bp = rx_bitpos;
      if(bp < 128)
      {
        int cp_mask = rx_mask;
        int cp_cache = rx_cache;
    
        if(get_bit()) 
          cp_cache |= cp_mask;
        cp_mask <<= 1;
        if(!cp_mask)
        {
          cp_mask = 1;
          *rx_ptr++ = cp_cache;
          rx_cache = 0;
        } else
          rx_cache = cp_cache;
        rx_mask = cp_mask;
        rx_bitpos = bp+1;
      }
    }
    /* inicjalizacja: rx_bitpos=0, rx_mask=1, rx_cache=0, rx_ptr = TAB; zakończenie informowane przez to, że rx_bitpos == 128 */
    

    To rozwiązanie trzecie jest tym, co ja rozumiem chcesz uzyskać: zbierać bity do zmiennej, po czym hurtowo je zapisać do do komórki tablicy. Możesz zwiększyć rozmiar zmiennej buforującej, ale z góry powiem jest to skazane na niepowodzenie (będzie wymagane dużo rejestrów pod tymczasowe zmienne, a więc wystąpi dużo odwołań do stosu, kod będzie trochę wolniejszy oraz trochę obszerniejszy). Lepiej będzie nawet zmniejszyć rozmiar komórek przy wskaźniku nawet do jednego bajtu. Nawet jeśli tablica składa się z komórek po 16 bitów, po 32 bity, to i tak można uzyskać wskaźnik na bajty, ponieważ avry są w porządku little-endian, nie zmieni to kolejności zapisywania bitów w pamięci.

    (zmienne cp_* są w celu optymalizacji - pominięcie nadmiarowych odczytów z pamięci spowodowanych przez volatile)
REKLAMA