2010-03-10 13 views
15

Estoy escribiendo un código de sistema para un sistema integrado sin protección de memoria (en un ARM Cortex-M1, compilando con gcc 4.3) y necesito leer/escribir directamente en un registro mapeado en memoria. Hasta ahora, mi código es el siguiente:¿Cuál es el código más corto para escribir directamente en una dirección de memoria en C/C++?

#define UART0  0x4000C000 
#define UART0CTL (UART0 + 0x30) 

volatile unsigned int *p; 
p = UART0CTL; 
*p &= ~1; 

¿Hay alguna forma más corta (más corta en el código, quiero decir) que no utiliza un puntero? Yo buscando una manera de escribir el código de asignación real tan corto como este (que estaría bien si tuviera que usar más #defines):

*(UART0CTL) &= ~1; 

Cualquier cosa que probado hasta ahora terminó con gcc quejándose de que pudiera no asigna algo a la lvalue ...

+4

'#define UART0CT' a' #define X'? –

+0

@The Machine Charmer: +1, gracioso. No creo que el OP significase más corto en una moda de código de golf, pero, bueno. :-) –

+0

El estándar C * requiere * un molde explícito en ordetr para asignar un entero distinto de cero al puntero. Su código, en su forma actual, solo es válido en GCC, pero está roto desde el pedante punto de vista C. Por lo tanto, lo quiera o no, tendrá que alargarlo si realmente desea mantenerlo como válido C. Por supuesto, si desea una solución específica de GCC, es una historia diferente. – AnT

Respuesta

14

Me gustaría ser quisquilloso: ¿estamos hablando de C o C++?

Si C, acepto la respuesta de Chris (y me gustaría que se elimine la etiqueta C++).

Si C++, desaconsejo el uso de esos desagradables C-Casts y #define en total.

El idiomática forma en C++ es utilizar una variable global:

volatile unsigned int& UART0 = *((volatile unsigned int*)0x4000C000); 
volatile unsigned int& UART0CTL = *(&UART0 + 0x0C); 

declaro un mecanografiadas variable global, que obedecen reglas de alcance (a diferencia de las macros).

Se puede usar fácilmente (no es necesario usar *()) y, por lo tanto, ¡aún más corto!

UART0CTL &= ~1; // no need to dereference, it's already a reference 

Si usted quiere que sea puntero, entonces sería:

volatile unsigned int* const UART0 = 0x4000C000; // Note the const to prevent rebinding 

Pero ¿cuál es el punto de utilizar un puntero const que no puede ser nulo? Este es semánticamente por qué se crearon las referencias para.

+0

porque puede poner un puntero const en un archivo de cabecera y #incluirlo tanto de C como de C++. También podría declararlo "estático" en C (esto también es inofensivo en C++) ya que no es necesario que termine en la tabla de símbolos. –

+0

Cierto, solo pensé en el aspecto 'C++' de la pregunta ya que '# define' son tradicionales en los programas' C'. –

+0

Bueno, me parece que es la mejor respuesta para mi proyecto, ya que no tengo motivos para no creer lo que dice la "idiomática forma de C++" ... – orithena

19
#define UART0CTL ((volatile unsigned int *) (UART0 + 0x30)) 

:-P

Editado para añadir: Oh, en respuesta a todos los comentarios acerca de cómo la cuestión se marca C++, así como C, aquí hay un C++ solución. :-P

inline unsigned volatile& uart0ctl() { 
    return *reinterpret_cast<unsigned volatile*>(UART0 + 0x30); 
} 

Esto puede ser pegado directamente en un archivo de cabecera, al igual que la macro-estilo C, pero usted tiene que utilizar la sintaxis de llamada de función para invocarlo.

+0

genial, eso funciona, gracias. Todavía me parece perder los puntos más finos de C/C++ ... – orithena

+0

@maligree: El casting es una de las operaciones fundamentales en C, definitivamente no es un "punto más fino". :-P (Esto no es tanto un golpe para ti, como el hecho de que C parece requerir mucho casting para realizar Real Work. :-P) –

+0

Claro, pero me refería al arte de poner todo junto en una línea única, pero aún "humanamente legible". Con su solución, IMO terminé con un hermoso código cuya semántica se podía ver en un instante (bueno, está bien, aún puedo #definir UARTDISABLE ~ 1 para agregar más semántica a esa línea ...) – orithena

1
#define UART0 ((volatile unsigned int*)0x4000C000) 
#define UART0CTL (UART0 + 0x0C) 
+0

¿Por qué '+ 10'? ___ – kennytm

+0

@dkrueger: 0x0C solo funciona para punteros de 32 bits. Por supuesto, este es probablemente el caso de ARM, pero aún así, muy poco práctico. :-P –

+0

@Chris: No es importante, para empezar todo es muy poco práctico. (Sin embargo, '0x30/sizeof (int)' sería más claro, en mi humilde opinión.) –

1

Usted puede ir más allá de una respuesta de Chris si desea realizar los registros de hardware se parecen a las variables llanura de edad:

#define UART0  0x4000C000 
#define UART0CTL (*((volatile unsigned int *) (UART0 + 0x30))) 

UART0CTL &= ~1; 

Es una cuestión de gusto que podría ser preferible. Trabajé en situaciones en las que el equipo quería que los registros se vieran como variables, y trabajé en código donde la desreferencia agregada se consideraba "ocultar demasiado", por lo que la macro para un registro se dejaba como un puntero que tenía que ser desreferenciado explícitamente (como en la respuesta de Chris).

1

Me gusta especificar los bits de control reales en una estructura, luego asignar eso a la dirección de control.Algo así como:

typedef struct uart_ctl_t { 
    unsigned other_bits : 31; 
    unsigned disable : 1; 
}; 
uart_ctl_t *uart_ctl = 0x4000C030; 
uart_ctl->disable = 1; 

(Disculpas si la sintaxis no es del todo bien, no he hecho codificado en C durante bastante tiempo ...)

+0

Tengo dudas sobre si el orden de los bits en un campo de bits está especificado por el estándar. Supongo que este código sería un comportamiento definido por la implementación. Pero la idea de usar una 'struct' es buena: defina una' struct uart' que contenga la disposición de la memoria del dispositivo mapeado en memoria, asigne la dirección a un solo puntero 'uart * const uart_regs = 0x4000c000;', y luego acceda a través de ese objeto con 'uart_regs-> ctl &= ~1;'. – cmaster

1

Otra opción que en un poco como para aplicaciones embebidas es utilizar el enlazador para definir secciones para sus dispositivos rígidos y asignar su variable a esas secciones. Esto tiene la ventaja de que si se dirige a varios dispositivos, incluso del mismo proveedor como TI, por lo general tendrá que modificar los archivos del vinculador dispositivo por dispositivo. Es decir, diferentes dispositivos de la misma familia tienen diferentes cantidades de memoria asignada directa interna, y de placa a placa también puede tener diferentes cantidades de ram y hardware en diferentes ubicaciones. Aquí hay un ejemplo de la documentación de GCC:

Normalmente, el compilador coloca los objetos que genera en las secciones como datos y bss. A veces, sin embargo, necesita secciones adicionales, o necesita ciertas variables particulares para aparecer en las secciones especiales , por ejemplo, para asignar a hardware especial. El atributo de sección especifica que una variable (o función) vive en una sección particular de . Por ejemplo, este pequeño programa utiliza varios nombres de las secciones específicas:

 struct duart a __attribute__ ((section ("DUART_A"))) = { 0 }; 
     struct duart b __attribute__ ((section ("DUART_B"))) = { 0 }; 
     char stack[10000] __attribute__ ((section ("STACK"))) = { 0 }; 
     int init_data __attribute__ ((section ("INITDATA"))); 

     main() 
     { 
     /* Initialize stack pointer */ 
     init_sp (stack + sizeof (stack)); 

     /* Initialize initialized data */ 
     memcpy (&init_data, &data, &edata - &data); 

     /* Turn on the serial ports */ 
     init_duart (&a); 
     init_duart (&b); 
     } 

utilizar el atributo de sección con variables globales y variables no locales, como se muestra en el ejemplo.

Usted puede utilizar el atributo sección con variables globales inicializado o no inicializados pero el enlazador requiere que cada objeto ser definido una vez, con la excepción de que las variables no inicializadas tentativamente van en la sección común (o BSS) y pueden ser multiplican “ definido ". El uso del atributo de sección cambiará la sección en la que se encuentra la variable y puede ocasionar que el enlazador emita un error si una variable no inicializada tiene múltiples definiciones. Puede forzar que se inicialice una variable con el distintivo -fno-common o el atributo nocommon.

Cuestiones relacionadas