2010-10-19 10 views
6

Tengo que programar registros periféricos en un microcontrolador basado en ARM9.#define vs. enums para direccionar periféricos

Por ejemplo, para el USART, almaceno las direcciones de memoria correspondientes en un enum:

enum USART 
{ 
    US_BASE = (int) 0xFFFC4000, 
    US_BRGR = US_BASE + 0x16, 
    //... 
}; 

Entonces, utilizar punteros en una función para inicializar los registros:

void init_usart (void) 
{ 
    vuint* pBRGR = (vuint*) US_BRGR; 
    *pBRGR = 0x030C; 
    //... 
} 

Pero mi el maestro dice que es mejor que use #define s, como:

#define US_BASE (0xFFFC4000) 
#define US_BRGR (US_BASE + 0x16) 
#define pBRGR ((vuint*) US_BRGR) 

void init_usart (void) 
{ 
    *pBRGR = 0x030C; 
} 

Como tal, dice, usted no tiene la sobrecarga de asignar punteros a la pila.

Personalmente, no me gusta #define s, ni otras directivas de preprocesador. Entonces, la pregunta es, en este caso particular, ¿vale la pena usar #define s en lugar de enum sy los punteros asignados a la pila?


pregunta relacionada: Want to configure a particular peripheral register in ARM9 based chip

+4

Me sorprendería que en una compilación que no es de depuración, el código generado por el compilador sea diferente. –

+1

Odio a los profesores que solo dicen: "Usa esto", por lo que los estudiantes más tarde tienen que cavar por razones. Por lo general, esos maestros no conocen las respuestas por sí mismos. – Andrey

+0

Si define los punteros como estáticos, no se asignarán a la pila. Si también los declaras const, entonces ni siquiera pueden causar que se asigne memoria en el área de datos (pero esto dependerá del compilador específico que estés usando) –

Respuesta

12

El enfoque siempre he preferido es definir primero una estructura que refleja los periféricos registran diseño

typedef volatile unsigned int reg32; // or other appropriate 32-bit integer type 
typedef struct USART 
{ 
    reg32 pad1; 
    reg32 pad2; 
    reg32 pad3; 
    reg32 pad4; 
    reg32 brgr; 
    // any other registers 
} USART; 

USART *p_usart0 = (USART * const) 0xFFFC4000; 

Luego, en el código que sólo puede utilizar

p_usart0->brgr = 0x030C; 

Este enfoque es mucho más limpio cuando se tener varias instancias del mismo tipo de periférico:

USART *p_usart1 = (USART * const) 0xFFFC5000; 
USART *p_usart2 = (USART * const) 0xFFFC6000; 

Usuario sbass provi Ded de un enlace a an excellent column por Dan Saks que da muchos más detalles sobre esta técnica, y señala sus ventajas sobre otros enfoques.

Si tiene la suerte de estar utilizando C++, puede agregar métodos para todas las operaciones comunes en el periférico y encapsular las peculiaridades de los dispositivos.

+0

Esto es mucho mejor.TI usa este método para sus procesadores (al menos, para los DSP 28xx y MSP430). El método para ubicar registros es un poco diferente; los pega en secciones particulares y usa el archivo del enlazador para ubicarlos en una dirección específica. –

+0

También debe tener toda la estructura sea volátil, no solo los componentes individuales. –

+0

@ Jason, si todos los registros son declarados volátiles, declarar la estructura volátil no hará ninguna diferencia. En cualquier caso, el compilador no puede optimizar ninguna lectura explícita o escritura en los registros. –

0

que necesariamente no diría que de cualquier manera es mejor. Es solo preferencia personal. En cuanto al argumento de su profesor, es realmente un punto discutible. La asignación de variables en la pila es una instrucción, no importa cuántas, generalmente en el formulario sub esp, 10h. Entonces, si tiene un local o 20, sigue siendo una instrucción asignar el espacio para todos.

Yo diría que la única ventaja del #include es que si por alguna razón, en el futuro, usted quiere cambiar la forma en que se accede a ese puntero, solo tiene que cambiarlo en una ubicación.

0

Me inclinaría por el uso de una enumeración, para una posible compatibilidad futura con el código C++. Digo esto porque en mi trabajo, tenemos muchos archivos de encabezado C compartidos entre proyectos, algunos de los cuales usan código C y algunos usan C++. Para aquellos que usan C++, a menudo nos gustaría envolver las definiciones en un espacio de nombres, para evitar el enmascaramiento de símbolos, pero no se puede asignar un #define a un espacio de nombres.

5

Me temo que enum son un callejón sin salida para tal tarea. El estándar define las constantes enum para que sean del tipo int, por lo que en general son no compatibles con punteros.

Un día en una arquitectura con 32bit int y punteros de 64 bits es posible que tenga una constante que no se ajusta a un int. No está bien definido lo que sucederá.

Por otro lado, el argumento de que enum asignaría algo en la pila no es válido. Son constantes de tiempo de compilación y no tienen nada que ver con la pila de funciones ni más que las constantes que especifique mediante macros.

+0

Las versiones '# define' de' US_BASE' y 'US_BRGR' también son ents ... –

+0

@Michael: pero eso se puede arreglar agregando' UL' al final. – Potatoswatter

1

En mi experiencia, una gran razón para usar #define para este tipo de cosas es que es más de la expresión estándar utilizada en la comunidad integrada.

Usar enums en lugar de #define generará preguntas/comentarios de los instructores (y en el futuro, colegas), incluso cuando el uso de otras técnicas tenga otras ventajas (como no pisar el espacio de nombres del identificador global).

Personalmente me gusta usar las enumeraciones para las constantes numéricas, pero a veces debe hacer lo que es habitual para qué y dónde está trabajando.

Sin embargo, el rendimiento no debería ser un problema.

1

La respuesta siempre es hacer lo que quiera el maestro y pasar la clase luego en su propia pregunta de todo y averiguar si sus razones fueron válidas y formar sus propias opiniones. No puedes ganar contra la escuela, no vale la pena.

En este caso, es fácil compilar para ensamblar o desmontar para ver la diferencia, si hay alguna, entre la enumeración y la definición.

Recomendaría la definición sobre enum, he tenido molestias en el compilador con enumeraciones. Desanimo el uso de punteros de la misma forma que los usa, he visto que cada compilador no genera las instrucciones deseadas con precisión, es raro, pero cuando sucede, se preguntará cómo funcionaron sus últimas décadas de codificación. Señalar estructuras o cualquier otra cosa es considerablemente peor. A menudo me entusiasman por esto, y espero esta vez. Demasiadas millas alrededor del bloque, se corrigió demasiado código roto con estos problemas para ignorar la causa raíz.

3

Dan Saks ha escrito una serie de columnas sobre esto para la Programación de Sistemas Embebidos. Aquí está uno de sus latest ones. Habla sobre C, C++, enumeraciones, estructuras, clases, etc. y por qué uno puede ser uno sobre otro. Definitivamente vale la pena leerlo y siempre es un buen consejo.

+0

El enlace está ahora muerto – Eric

+0

Posiblemente movido a [aquí] (http://www.embedded.com/electronics-blogs/programming-pointers/4208573/Accessing-memory-mapped-classes) o [aquí] (http://www.embedded.com/design/programming-languages-and-tools/4027659/Alternative-Models-for-Memory-Mapped-Devices) – Eric