Manipulación de bits en C y aplicaciones

La manipulación de bits u operaciones bit a bit (bitwise) son algo común en los programas con microcontroladores, ya que nos permiten configurar los registros para usar el hardware incluido, acceder a los puertos de entrada y salida, hacer «cálculos rápidos», verificar la autenticidad de los datos enviados/recibidos, etc.

En lenguaje C, este tipo de «manipulaciones» se realizan con operadores que están incluidos en el lenguaje. Estos operadores son iguales a las básicas en álgebra booleana:

  • & – operación AND
  • | – operación OR
  • ~ – operación NOT
  • ^ – operación XOR

Además de operaciones de corrimiento:

  • >> – corrimiento a la derecha
  • << – corrimiento a la izquierda

Explicaremos cada una de estas operaciones.

Operación AND (&)

Esta operación es de multiplicación. Por ejemplo, para la operación: a = b & c; donde (en binario) b=10101010 y c=11001100

10101010 &
11001100
----------
10001000

Como se ve, si ambos bits (que están en la misma posición) son ‘1’ el resultado será también ‘1’

Operación OR (|)

Esta operación es de suma. Donde cualquiera de los bits que se encuentren en la misma posición sea ‘1’, el resultado será ‘1’. Por ejemplo, para la operación: a = b | c; donde (en binario) b=10101010 y c=11001100

10101010 |
11001100
----------
11101110

Operación XOR (^)

Esta operación es de una o-exclusiva, donde la diferencia de estados en los bits de igual posición dará como resultado ‘1’ y la igualdad de estados dará como resultado ‘0’. Se puede entender como «una cosa u otra pero no ambas», por decirlo de alguna manera. Por ejemplo, para la operación: a = b ^ c; donde (en binario) b=10101010 y c=11001100

10101010 ^
11001100
----------
01100110

Por cierto, no esta demás aclarar que estas operaciones son conmutativas (‘a & b’ es lo mismo que ‘b & a’) 😉

Operación NOT (~)

Esta operación sólo se aplica solo a un elemento, pero invierte los valores de los bits del elemento involucrado. Por ejemplo, para la operación: a = ~b; donde (en binario) b=10101010

~10101010 
----------
 01010101

Corrimiento a la derecha (>>)

Este corrimiento, desplaza los bits de izquierda a derecha (del bit más significativo al menos significativo), un numero definido de veces, el bit menos significativo se pierde a cada corrimiento, mientras que que el bit mas significativo se va rellenando con un cero. Por ejemplo, para la operación a = b>>3; con b= 10101010, se hacen tres desplazamientos a la derecha, con lo que ‘a’ queda con el valor 00010101. También esta operación se puede ver como que a cada desplazamiento se hace una división entre 2, por lo que en nuestro ejemplo, se divide tres veces entre 2, y en total se hace la división entre 8.

10101010 >> 3
-------------
00010101

Corrimiento a la izquierda (<<)

Es un proceso similar al anterior. Aquí, a cada corrimiento, el bit mas significativo se va perdiendo y el menos significativo se va llenando con cero. Se puede entender también como una multiplicación por 2 por cada desplazamiento. Por ejemplo, con el resultado de a (operacion anterior), ahora hacemos b= a<<3; b queda al final con el valor 10101000.

00010101 << 3
-------------
10101000

Aplicaciones

Los «movimientos» basicos.

Cuando empezamos a programar microcontroladores, los registros son la principal fuente de dolores de cabeza. Ya sea si queremos saber la configuracion o establecer la nuestra, la manipulacion de bits ayuda mucho con este tema.

Las siguientes expresiones simplifican mucho las manupulaciones de bits. Las pondré como «funciones» macro de C, por el hecho que de no rquieren el uso de pila cuando se les llama, ya que en precompilacion se sustituyen en el codigo.

// BIT(x) regresa el bit x puesto a uno y los demas bits en cero, ej. BIT(3) regresa 00001000
#define BIT(x)         (1<<(x))
// BIT_GET(x,b) regresa el bit b-esimo de x ej. BIT_GET(PINC,3)
#define BIT_GET(x,b)   ((x) & BIT(b))
// BIT_SET(x,b) establece en '1' el bit b de x ej. BIT_SET(PORTD,4)
#define BIT_SET(x,b)   ((x) |= BIT(b))
// BIT_CLEAR(x,b) establece a '0' el bit b de x
#define BIT_CLEAR(x,b) ((x) &= ~BIT(b))
// BIT_TOGGLE(x,b) invierte el valor del bit b de x a su complemento,
#define BIT_TOGGLE(x,b)  ((x) ^= BIT(b))
// BIT_WRITE(x,b,v) establece el valor 'v' de
#define BIT_WRITE(x,b,v) ((v)? BIT_SET(x,b) : BIT_CLEAR(x,b))

// ES_PAR(x) regresa 0 cuando x no es par y algo diferente de 0 cuando es par
#define ES_PAR(x) (!BIT_GET(x,0))

// Y gracias a la magia del XOR, podemos intercambiar el valor de 2 variables
// tipo int o char sin la necesidad de una variable temporal

#define SWAP1(x,y) (x = x ^ y ,  y = x ^ y ,  x = x ^ y )
#define SWAP2(x,y) (x^=y^=x^=y)
#define SWAP3(x,y) (x^=y^=x^=y^=x^=y^=x^=y^=x^=y)

Para esta última parte (swap), si no me creen jajaja, les dejo un codigo

#include <stdio.h>

#define SWAP1(x,y) (x = x ^ y ,  y = x ^ y ,  x = x ^ y )
#define SWAP2(x,y) (x^=y^=x^=y)
#define SWAP3(x,y) (x^=y^=x^=y^=x^=y^=x^=y^=x^=y)

int main(void)
{    
    int a = 5, b=-30;
    printf("Original a=%d, b=%d\n",a,b);
    SWAP1(a,b);
    printf("SWAP1 -> a=%d, b=%d\n",a,b);
    SWAP2(a,b);
    printf("SWAP2 -> a=%d, b=%d\n",a,b);
    SWAP3(a,b);
    printf("SWAP3 -> a=%d, b=%d\n",a,b);
    return 0;
}

Y pueden ver como corre aqui. Lo he probado con exito en los AVR, PSoC, PIC, MSP430 y TIVA 😛

Un poco mas allá de lo básico.

La siguiente aplicacion es una encriptacion basica. La idea es la de usar la siguiente propiedad de la XOR

// Cuando hacemos la operacion de XOR 2 veces con la misma variable, el resultado se restaura
x=x^y;  // se altera a 'x' con los valores de 'y'
x=x^y;  // se vuelve a alterar, pero x ha sido restaurada al valor original

El código lo pueden encontrar aqui. Y como ven esto presenta la siguiente utilidad en una ecriptacion básica: ‘y’ (codigo anterior) puede ser utilizado como la clave de encriptacion/desencriptacion. Veamos el siguiente codigo:

#include <stdio.h>

char texto[] = "hola";
char clave_m = 45;

void encripta(char *mensaje, char clave, int tam)
{
 int i;
 for(i=0; i<tam; i++)
 {
     mensaje[i]^=clave;
 }
}

void desencripta(char *mensaje, char clave, int tam)
{
    encripta(mensaje, clave, tam);
}

void muestra_mensaje(char *mensaje, int tam)
{
    int i;
    for(i=0; i<tam; i++)
    {
        printf("0x%X ", mensaje[i]);
    }
    printf("\n");
}

int main(void)
{
    printf("Mensaje original: %s\n", texto);
    printf("En bytes: ");
    muestra_mensaje(texto, 4);
    
    encripta(texto,clave_m,4);
    printf("Encriptado: %s\n", texto);
    printf("Encriptado en bytes: ");
    muestra_mensaje(texto, 4);
    
    desencripta(texto,clave_m,4);
    printf("Desencriptado: %s\n", texto);
    printf("Desencriptado en bytes: ");
    muestra_mensaje(texto, 4);
    
    return 0;
}

Pueden ver el código ejecutandose aqui.

 Codigo de verificacion de integridad

Cuando usamos la uart/usart de algun microcontrolador para comunicarnos con algun dispositivo, las señales transmitidas son susceptibles a ser alteradas por ruido electromagnético. Es necesario asegurarnos que nuestro mensaje llegue sin alteraciones, para hacer eso se pueden usar varias tecnicas, pero por ahora me enfocare en la manipulacion de bits.

// esta funcion genera un byte de verificacion
unsigned char genera_BVerif(unsigned char *mensaje, int tam)
{
int i;
unsigned char BVerif = 0;
for(i=0; i<tam; i++)
   BVerif ^= mensaje[i];
return BVerif;
}

Esta funcion va haciendo un «acumulado» de XOR por todo el arreglo. Si algun bit en algun byte es alterado, el byte de verificacion ya no es el mismo al final. Asi que, una manera de enviar y recibir mensajes es: enviar el mensaje, generar el byte de verificacion y enviarlo; el receptor por su parte: recibir el mensaje, recibir el byte de verificacion, generar el byte de verificacion del mensaje recibido y comparar con el byte de verificacion que se nos envió, si los dos coinciden, el mensaje ha sido recibido sin alteraciones en el contenido.

Como hemos visto, las manipulaciones de bits u operaciones bit a bit son una herramienta indispensable para todos lo que trabajamos con microcontroladores.

Espero que estas aplicaciones les sean de utilidad.

Sin mas por el momento…

Argos.

Anuncio publicitario

2 comentarios en “Manipulación de bits en C y aplicaciones”

Deja una respuesta

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Salir /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Salir /  Cambiar )

Conectando a %s