¿Por qué el código AVR utiliza el desplazamiento de bits [cerrado]

8

En la programación de AVR, los bits de registro se establecen invariablemente desplazando a la izquierda a 1 a la posición apropiada del bit, y se borran con el complemento de uno de los mismos.

Ejemplo: para un ATtiny85, podría establecer PORTB, b 4 de esta manera:

PORTB |= (1<<PB4);

o bórralo así:

PORTB &= ~(1<<PB4);

Mi pregunta es: ¿Por qué se hace de esta manera? El código más simple termina siendo un caos de cambios de bits. ¿Por qué los bits se definen como posiciones de bits en lugar de máscaras?

Por ejemplo, el encabezado IO para el ATtiny85 incluye esto:

#define PORTB   _SFR_IO8(0x18)
#define PB5     5
#define PB4     4
#define PB3     3
#define PB2     2
#define PB1     1
#define PB0     0

Para mí, sería mucho más lógico definir los bits como máscaras (como esto):

#define PORTB   _SFR_IO8(0x18)
#define PB5     0x20
#define PB4     0x10
#define PB3     0x08
#define PB2     0x04
#define PB1     0x02
#define PB0     0x01

Entonces podríamos hacer algo como esto:

// as bitmasks
PORTB |=  PB5 |  PB3 |  PB0;
PORTB &= ~PB5 & ~PB3 & ~PB0;

para activar y desactivar los bits b 5 , b 3 y b 0 , respectivamente. A diferencia de:

// as bit-fields
PORTB |=  (1<<PB5) |  (1<<PB3) |  (1<<PB0);
PORTB &= ~(1<<PB5) & ~(1<<PB3) & ~(1<<PB0);

El código de máscara de bits se lee mucho más claramente: establece los bits PB5 , PB3 y PB0 . Además, parece que guarda operaciones ya que los bits ya no necesitan ser desplazados.

Pensé que tal vez se hizo de esta manera para preservar la generalidad a fin de permitir el código de portabilidad de un n -bit AVR a un m -bit (ejemplo de 8 bits a 32 bits). Pero este no parece ser el caso, ya que #include <avr/io.h> se resuelve en archivos de definición específicos para el microcontrolador de destino. Incluso el cambio de objetivos de un ATtiny de 8 bits a un Atmega de 8 bits (donde las definiciones de bits cambian sintácticamente de PBx a PORTBx , por ejemplo), requiere cambios de código.

    
pregunta Blair Fonville

2 respuestas

7
  

El código más simple termina siendo un caos de cambios de bits. ¿Por qué los bits se definen como posiciones de bits en lugar de máscaras?

No. De ningún modo. Los turnos solo están en el código fuente de C, no en el código de máquina compilado. Todos los ejemplos que mostró pueden ser y serán resueltos por el compilador en tiempo de compilación porque son simples expresiones constantes.

(1<<PB4) es solo una forma de decir "bit PB4".

  • Así que no solo funciona, no crea más tamaño de código.
  • También tiene sentido para el programador humano nombrar los bits por su índice (por ejemplo, 5) y no por su máscara de bits (por ejemplo, 32) porque de esta manera se pueden usar números consecutivos 0..7 para identificar bits en lugar de la incómoda potencia de dos (1, 2, 4, 8, .. 128).

  • Y hay otra razón (quizás la razón principal):
    Los archivos del encabezado C no solo se pueden usar para el código C, sino también para el código fuente del ensamblador (o el código del ensamblador incluido en el código fuente C). En el código de ensamblador AVR, definitivamente no solo desea usar máscaras de bits (que se pueden crear a partir de índices mediante desplazamiento de bits). Para algunas instrucciones del ensamblador de manipulación de bits AVR (por ejemplo, SBI, CBI, BST, BLD), tiene que usar índices de bits como operador inmediato en su código de operación.
    Solo si identifica bits de SFR por índices (no por máscara de bits) puede usar dichos identificadores directamente como operando inmediato de las instrucciones del ensamblador. De lo contrario, tendría que tener dos definiciones para cada bit SFR: una que define su índice de bits (que se puede usar, por ejemplo, como operando en las instrucciones de ensamblaje de manipulación de bits mencionadas anteriormente) y una que define su máscara de bits (que solo se puede usar para instrucciones donde todo el byte es manipulado).

respondido por el Curd
4

Quizás el desplazamiento de bits no sea el único caso de uso para las definiciones PB* . Quizás haya otros casos de uso donde las definiciones de PB* se usan directamente en lugar de como cantidades de turnos. Si es así, creo que el principio DRY lo llevaría a implementar un conjunto de definiciones que se pueden usar para ambos usos casos (como estos PB* define) en lugar de dos conjuntos diferentes de definiciones que tienen información repetitiva.

Por ejemplo, escribí una aplicación que puede tomar medidas de hasta 8 canales ADC. Tiene una interfaz para iniciar una nueva medición en la que puede especificar múltiples canales a través de un campo de 8 bits, un bit para cada canal. (Las mediciones se realizan en paralelo cuando se especifican múltiples canales).  Luego tiene otra interfaz que devuelve los resultados de medición para un canal individual. Así que una interfaz usa el número de canal como un cambio en un campo de bits y la otra interfaz usa el número de canal directamente. Definí una sola enumeración para cubrir ambos casos de uso.

typedef enum
{
    CHANNEL_XL_X = 0,
    CHANNEL_XL_Y = 1,
    CHANNEL_XL_Z = 2,
    CHANNEL_G_X = 3,
    CHANNEL_G_Y = 4,
    CHANNEL_G_Z = 5,
    CHANNEL_AUX1 = 6,
    CHANNEL_AUX2 = 7
} ChannelNum;

struct MeasurementResult;

void StartMeasurement(uint8_t channel_mask);
MeasurementResult ReadMeasurementResult(ChannelNum channel_num);

main
{
    ...

    StartMeasurement( (1 << CHANNEL_XL_X) | (1 << CHANNEL_XL_Y) | (1 << CHANNEL_XL_Z) );

    meas_result_x = ReadMeasurementResult(CHANNEL_XL_X);
    meas_result_y = ReadMeasurementResult(CHANNEL_XL_Y);
    meas_result_z = ReadMeasurementResult(CHANNEL_XL_Z);
}
    
respondido por el kkrambo

Lea otras preguntas en las etiquetas

Comentarios Recientes

en lugar de GPWM? GPWM puede crear paralelismo artificial al cambiar la ventana de contexto del sombreador en píxeles cuando se lee un valor delta y volver a leerlo una vez que se lee un valor de color. Como el código tiene más de 2 operaciones y para mejorar la velocidad, se necesita un cambio en cualquier dirección de lo que el sombreador del sombreador ya está haciendo en cada iteración. Ambas cosas promueven cualquier escenario de sombreado de la siguiente manera. Si hacemos un montón de dibujos: dibuje una... Lees verder