Máscara de bits

Queria saber como usa a máscara de bits. ESPECIFICAMENTE queria entender essa parte: i & (1 << x))
do código abaixo

/*
Projeto Arduino contador binário com saída em 8 leds que formam 1 byte.
Por Jota
----------------------------------------
--=<| www.ComoFazerAsCoisas.com.br |>=--
----------------------------------------
*/

int const clock = 2; //pino do arduino ligado ao clock do 74HC595
int const latch = 3; //pino do arduino ligado ao latch do 74HC595
int const data  = 4; //pino do arduino ligado ao data do 74HC595

void setup() {
  //definindo os pinos como de saída
  pinMode(clock,OUTPUT);
  pinMode(latch,OUTPUT);
  pinMode(data,OUTPUT);
}

void loop() {
  //Contando de 0 até 255
  for (int i = 0; i <= 255; i++) {
    digitalWrite(latch,LOW); //permite o fluxo dos dados.
       
    for (int x = 0; x <= 7; x++) {
      digitalWrite(clock,LOW); //pronto para receber o bit.
      
      if (i & (1 << x)) {
        digitalWrite(data,HIGH);
      } else {
        digitalWrite(data,LOW);
      }
      
      digitalWrite(clock,HIGH); //grava o bit recebido.
    }    
    
    digitalWrite(latch,HIGH); //finaliza o fluxo dos dados e envia os 8 bits.
    
    delay(100); //tempo de espera entre a exibição de um número e o outro.
  }
}

Há uma operação matemática que se chama "E" (AND em inglês). Esta operação é definida assim:
quando ambos os operadores são 1 o resultado é 1, quando um dos dois operadores é 0 o resultado é 0.
A operação que em C se representa por &, é chamada bitwise AND (ou E bit-a-bit, em português). Para analisar esta operação deve olhar-se para os valores dos operadores em binário. Sendo assim, se tiver-mos 1011 0010 e 1001 0101, o resultado deste E bit-a-bit será 1001 000, isto é, o bit do resultado é 1 quando o bit correspondente de cada um dos operadores é 1 e 0 nos outros casos.

Outra operação que aparece na instrução que questiona é a deslocação. Para analisar esta instrução tem que se olhar também para o valor em binário daquilo que se está a deslocar. Sendo assim, se tiver o valor 53 (em binário 0011 0101) e o deslocar três bits para a esquerda (representa-se: 53 << 3) o resultado será 1010 1000. Isto é, são acrescentados 3 zeros à direita do número e os bits que estavam à esquerda desaparecem. NOTA: estou a considerar que a variável é de 8 bits, mas se o comprimento for diferente, o raciocínio é o mesmo mas com um número diferente de bits. (já agora, o resultado do deslocamento para a esquerda, equivale a uma multiplicação por 2, por cada bit que se desloca , no entanto, no caso do exemplo, não é exactamente esse resultado, pois os bits mais à esquerda desapareceram)

Para além disso, também é importante referir que no if, quando o resultado da condição (a condição é o que está dentro dos parêntesis) é 0, NÃO é executado o que está no bloco de instruções (o que está entre chavetas) que segue o if. Se o resultado da condição for outro valor qualquer, é executado o bloco de instruções.

Chegando aqui é importante "partir" a instrução que levanta dúvidas em duas partes. A primeira a do deslocamento e a segunda a do bitwise AND.
Sendo assim a parte (1 << x), pega no valor 1 e desloca-o x vezes para a esquerda. por exemplo, se x for 3 o resultado será 0000 1000.
Para a outra parte da instrução o resultado do bitwise AND entre o resultado daquilo que foi deslocado e 53 (0011 0101) daria 0000 0000. O resultado do AND com 56 (0011 1000) daria 0000 1000 sendo assim um valor diferente de 0 e desta forma o bloco de instruções do if seria executado.

Sendo assim, a funcionalidade deste if, é pegar na variável i e para cada bit desta variável, verificar se este é 1 ou 0. Se o bit for 1 coloca o pino data a HIGH, se o bit for 0 coloca o pino a LOW.

Peço desculpa pela extensão da resposta, mas como não sei qual é o seu nível de conhecimentos e concretamente em que parte da instrução está a sua dúvida, decidi explicar tudo desde o início. Se a dúvida persistir, diga alguma coisa.

Eu também queria entender!

@Lago: Isso quer dizer que a minha explicação não esclarece? Em que parte exactamente?

Muitíssimo obrigado pela resposta. Sou iniciante em ARDUINO e me perdi em algumas partes(mas consegui absorver grande quantidade de conhecimento com seu post, muito obrigado).

Não tenho nenhuma base em programação.

A função FOR funciona como um loop, certo? O valor de "i" vai variando de 0 até 255.

Quando i for 1(00000001), esses 8 números são enviados ao CI(registrador de deslocamento) para que ele forme um número binário no conjunto de 8 LEDS. (O projeto completo está no link: Controlando 8 leds com o Arduino usando o CI 74HC595, fazendo uma contagem binária.).

O que eu não entendi é como o Ci74.. sabe quais LEDS acenderem de acordo com o número que aparece.

Estou a 1 semana tentando entender isso.

O que eu não entendi é como o Ci74.. sabe quais LEDS acenderem de acordo com o número que aparece.

Simples.
O shift register como deves ter reparado tem 8 pinos de saida(Q0 a Q7) que corresponde a um byte.Basicamente usando a operação de deslocamento (1<<X) estas a manipular o bit dentro do byte que estas a enviar para o shift register.
O resultado é que quando levantares o pino de latch(colocando este em high) o shift register vai colocar os leds de acordo com o que lhe envias-te serialmente.
Tu por exemplo constrões o byte ficando por exemplo 10101011 e envias para o shift register.Nota que o bit mais significativo( o mais a esquerda) esta a 1 logo o led ligado no Q7 vai ficar a 1 também, ja o Q8 vai ficar desligado .... o Q1 ligado e o Q0 ligado também.

Quando cliquei em "reply", o HugoPT já tinha respondido, no entanto, não disse exactamente aquilo que eu queria dizer.
O ciclo for que se encontra em primeiro lugar no programa, percorre todos os números de 0 a 255, isto é, o programa envia todos estes números para o shifter register.
O outro ciclo for é que concretamente envia os valores para o shifter register. Como é que ele faz isso? Através do if. Para um determinado valor a enviar, percorre os 8 bits um-a-um e verifica se o bit é 1 ou 0 e, conforme o caso, coloca o pino data a ALTO ou BAIXO.

As instruções:

digitalWrite(clock,LOW); //pronto para receber o bit.
(...)
digitalWrite(clock,HIGH); //grava o bit recebido.

permitem ao shifter register saber que existe um novo dado para ele processar (chama-se sinal de relógio ou clock).

A instrução:

    digitalWrite(latch,HIGH); //finaliza o fluxo dos dados e envia os 8 bits.

faz com que os dados fiquem disponíveis no exterior do "registo de deslocamento" (antes desse momento apenas se encontram armazenados no seu interior).

Eu também tinha dúvida de como funcionava especificamente o comando (1 << x). A resposta na verdade é mais simples do que imaginava: o número 1, só tem 1 bit "válido" (0000 0001), então deslocando este bit 'x' vezes, eu posso manipular facilmente o bit a ser escrito ou comparado.

"Traduzindo para o portugues" a expressão if (i & (1 << x)) seria algo como:
Se o bit atual sendo comparado (for (int x = 0; x <= 7; x++)) do byte (i) possui o bit (1<<x) válido, coloca no registrador (digitalWrite(data,HIGH);). Se não, coloca 0 (digitalWrite(data,LOW);).
O latch grava o bit.

Eu me confundo muito por causa da minha escassa base em programação.

Mas eu acho que sei um jeito de consolidar todo o conhecimento que vocês me passaram.

Tipo, o número 0 em binários é 0;
O número 1 é 1;
o número 2 é 10;

Até aqui eu entendi o deslocamento à esquerda. Primeiro era 0, depois deslocou 1 e ficou 1, e depois deslocou o 1 para a esquerda e ficou 10.

Mas o número 3 é 11
E agora? Como esse segundo número "1" aparece?

Sério ... Eu sou bem iniciante e provavelmente posso estar fazendo uma pergunta besta. Mas isso não está entrando na minha mente

Se calhar, a melhor forma de ver isto é fazer um número inteiro passo-a-passo. Podemos pegar, por exemplo, no número do primeiro exemplo, 53, que em binário era 0011 0101. Vamos imaginar que este é o número que está na variável i:
Agora vamos ver como ficaria o segundo ciclo for:

    for (int x = 0; x <= 7; x++) {
      digitalWrite(clock,LOW); //pronto para receber o bit.
      
      if (i & (1 << x)) {
        digitalWrite(data,HIGH);
      } else {
        digitalWrite(data,LOW);
      }
      
      digitalWrite(clock,HIGH); //grava o bit recebido.
    }

x começa sendo 0, então a instrução (1 << x) desloca o 1 zero casas para a esquerda, isto é, o 1 fica onde está, ou seja, 0000 0001. Seguidamente é feito o AND bit-a-bit com o 53 (0011 0101 & 0000 0001) e o resultado é 0000 0001. Como este resultado não é zero, o if é executado e então o pino data é escrito a ALTO.
Na seguinte iteração do ciclo for, x é 1. Sendo assim a instrução (1 << x) desloca o 1, 1 casa para a esquerda, ficando 0000 0010. Fazendo o AND com o número 53, fica 0011 0101 & 0000 0010 = 0000 0000. Como o resultado é zero, o if não é executado. Como este if tem um else, se o primeiro não é executado é executado o segundo. Sendo assim o pino data é escrito a BAIXO.
Na seguinte execução do ciclo for, o x será 2. Sendo assim o 1 é deslocado duas casa para a esquerda, ficando 0000 0100. Fazendo o AND disto com o número 53, fica 0011 0101 & 0000 0100 = 0000 0100. Este valor não é zero, portanto o pino data é escrito a ALTO.
Na próxima execução, o valor de x é 3, o deslocamento é de 3 casas e o resultado será 0000 1000. O resultado do AND é, neste caso, 0011 0101 & 0000 1000 = 0000 0000. Sendo assim, o pino é escrito a BAIXO.
Na seguinte, o valor de x é 4. É feito o deslocamento que dá 0001 0000. O AND dá 0011 0101 & 0001 0000 = 0001 0000 e então o pino é escrito a ALTO.
Na próxima, x é 5 e o resultado do deslocamento é 0010 0000. O resultado do AND é 0011 0101 & 0010 0000 = 0010 0000 e o pino é escrito a ALTO.
Na seguinte, x é 6 e o resultado do deslocamento é 0100 0000. O resultado do AND é 0011 0101 & 0100 0000 = 0000 0000 e o pino é escrito a BAIXO.
Por último, o x é 7, assim sendo, o resultado do deslocamento é 1000 0000. O AND com 53 é 0011 0101 & 1000 0000 = 0000 0000. Sendo assim, o pino data é escrito a BAIXO.

Li novamente com calma todos os posts, inclusive esse último e, FINALMENTE, entendi.

Valeu mesmo. Muito obrigado pela paciência.

Ok. De nada. Se precisar de mais alguma coisa diga.

Bom trabalho.