Publicidade:

sexta-feira, 25 de setembro de 2015

Arduino - Array de bits

Como sabemos arrays são estruturas de dados utilizados para armazenar dados de um mesmo tipo em sequência. A sintaxe utilizada para a declaração de um array é a seguinte:

type nome_do_array [ tamanho ];

por exemplo,

int valores[10] = {1, 3, 2, 5, 6, 4, 8, 9, 7, 0};

É muito comum termos a necessidade de utilizarmos arrays para representar conjunto de dados como os listados acima.

Os tipos de dados a serem armazenados podem ser qualquer tipo de dado aceito em C.

boolean
char
unsigned char
byte
int
unsigned int
word
long
unsigned long
short
float
double
string - char array
String - object


Cada um dos tipos de dados tem um determinado tamanho que terá quer ser reservado na memória ao ser declarado um array. Um int, por exemplo, ocupa dois bytes de memória, um char ocupa um byte, um long ocupa 4 bytes.

Mas se tratando de programação para micro controladores, algo importante na hora de declarar um array, é escolhermos o tipo de dados mais adequado para armazenarmos os dados pretendidos. Por exemplo, se eu pretendo ter um array de números inteiros, mas os valores armazenados não ultrapassam o valor de 255, poderia utilizar um tipo de dados que não ocupasse mais de um byte, como word, ou byte. Caso contrário, teria que ser utilizado outro tipo, como int, ou ainda long. A escolha de cada um desses tipos de dados resulta em maiores ou menores quantidades de memória utilizadas. Em aplicações pequenas, talvez isso não faça diferença, mas algumas situações acaba fazendo.

Uma situação especial, e bastante comum, é necessitarmos armazenar arrays com informações binárias, ou seja, valor do tipo 1 ou 0. ou true ou false, ou HIGH ou LOW. enfim, apenas dois valores distintos podem ser armazenados em cada posição.

Um exemplo comum, seria por exemplo criar um array pra controlar o estado de um conjunto de lâmpadas, onde elas podem estar ou ligadas ou desligadas.

Vamos supor que fossem 20 lampadas.

Uma maneira fácil seria criar um array assim:

int lampadas[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

Mas será que essa é a melhor forma de declarar esse array?

Como sabemos, int ocupa dois bytes. Multiplicado por 20, totalizaria 40 bytes.

Outra opção seria mudar int, para byte:

byte lampadas[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

O que totalizaria 20 bytes. ou seja a metade.

Mas e se quisermos diminuir ainda mais? Nesse caso teríamos que ter um tipo de dados que ocupasse menos de um byte, mas isso infelizmente a linguagem C não nos oferece, mas ainda assim há um jeito de diminuirmos.

Como sabemos, 1 byte é formado por 8 bits. E cada bit pode assumir apenas dois estados, ou seja, 1 ou 0. Uma saída então, seria utilizar 1 byte, pra controlar 8 lâmpadas. Com isso precisaríamos ter 20 / 8 (2.5 --> 3) bytes para representar os estados das 20 lâmpadas. ou seja, algo assim:

byte lampadas[3];  //24 bits disponíveis

O que totalizaria apenas 3 bytes de memória para gerenciar o estado de 20 lâmpadas.

Sem dúvida essa é uma estratégia bem útil quando temos limitações de memória importante, o que é bem comum quando se trata de micro controladores.

Mas como tudo tem seu preço, teremos agora que criarmos meios de alterarmos bit a bit de cada um dos bytes e para isso precisaremos utilizar operadores de bitwise.

Já fiz um vídeo explicando como funcionam operadores de bitwise, quem não conhece, vale muito a pena aprender.



Pra facilitar o uso desses recursos, criei uma classe que encapsula toda essa parte "complicada", a qual está abaixo:



class BitArray{
  private:
    int _num_bits;   //quantidade de bits a serem gerenciados
    int _num_bytes;  //quantidade de bytes utilizados para armazenar os bits a serem gerenciados
    byte * _bytes;   //array de bytes onde estaram armazenados os bits
  public:
    BitArray(int num_bits){
      _num_bits  = num_bits;
      _num_bytes = _num_bits/8 + (_num_bits%8 ? 1 : 0) + 1;
      _bytes = (byte *)(malloc( _num_bytes * sizeof(byte) ) );
    }
    
    void write(int index, byte value) {
      byte b = _bytes[ index/8 + (index%8 ? 1 : 0) ];
      unsigned int bit = index%8;
      if (value) { b |= (1 << bit); } else { b &= ~(1 << bit);  }
      _bytes[ index/8 + (index%8 ? 1 : 0) ] = b;
    }
    
    void write(byte value) {
      for(int j=0; j<_num_bytes;j++) { _bytes[j] = value ? B11111111 : B00000000;  } 
    }
    
    int read(int index) {
      byte b = _bytes[ index/8 + (index%8 ? 1 : 0) ];
      unsigned int bit = index%8;
      return (b & (1 << bit)) != 0;
    }

   ~BitArray(){ free( _bytes ); }

};


Com isso, agora não iremos ter um array, mas sim um objeto, o qual poderá ser utilizado da seguinte maneira:


BitArray lampadas(20); //instancia do objeto com 20 bits

E para alterarmos uma das lâmpadas, fazemos da seguinte maneira:

lampadas.write(0, HIGH);  //liga a primeira lampada
lampadas.write(1, HIGH);  //liga a segunda lampada

if ( lampadas.read(0) ) { Serial.println("a primeira lampada está ligada."); } //verifica o estado da lampada.



Com isso conseguimos ter o menor consumo de memória, sem termos grandes dificuldades na parte de programação.


versão bidimensional :


class BitArray2D {
  private:
    unsigned int _rows;
    unsigned int _columns;
    unsigned int _cols_array; //pra cada 8 colunas, 1 byte é usado 
    byte**       _bits;
  public:
    BitArray2D(unsigned int rows, unsigned int columns){
      _rows       = rows;
      _columns    = columns;
      _cols_array = columns/8 + (_columns%8 ? 1 : 0) + 1; //divide por 8 o número de colunas
      _bits = (byte **)malloc(_rows * sizeof(byte *));
      for(int i=0;i<_rows;i++){ _bits[i] = (byte *)malloc(  _cols_array  *  sizeof(byte)); } //cria varios arrays
      clear();
    }
    
    unsigned int rows(){ return _rows; }
    unsigned int columns(){ return _columns; }
    
    void clear() { 
      for(int i=0;i<_rows;i++){      
        for(int j=0; j<_cols_array;j++) { _bits[i][j] = B00000000; }       
      }   
    }
  
    void write(unsigned int row, unsigned int column, int value){
      byte b = _bits[row][ column/8 + (column%8 ? 1 : 0) ];
      unsigned int bit = column%8;
      
      if (value) { b |= (1 << bit); } else { b &= ~(1 << bit);  }
      
      _bits[row][ column/8 + (column%8 ? 1 : 0) ] = b;
    }
    
    void write(byte value){
      for(int i=0;i<_rows;i++){      
        for(int j=0; j<_cols_array;j++) {      
          _bits[i][j] = value ? B11111111 : B00000000;     
        }       
      }  
    }
    
    int read(unsigned int row, unsigned int column){
      byte b = _bits[row][ column/8 + (column%8 ? 1 : 0) ];
      unsigned int bit = column%8;
      
      return (b & (1 << bit)) != 0;
    }
    
    void toggle(unsigned int row, unsigned int column){ write(row, column, !read(row, column)); }
    void toggle(){ for(int i=0;i<_rows;i++){      for(int j=0; j<_columns;j++) {      toggle(i,j);   }   }   }
};


Nenhum comentário:

Postar um comentário