2011-09-16 5 views
5

Estoy programando C en un sistema integrado. La arquitectura del procesador es 32 bits (sizeof(int) es 32 bits, sizeof(short) es 16 bits). Hay una variable de 32 bits que es un registro de control mapeado en la memoria (CTRL_REG) que se especifica solo como los 16 bits inferiores que se usan, y contienen un valor entero de 16 bits con signo (escribir en los bits más altos no tiene ningún efecto). Los accesos a la memoria deben estar alineados a 32 bits, por lo que no puedo simplemente colocar el puntero sobre un par de bytes, y tampoco puedo suponer una endiancia. Me preocupa que la promoción automática de tipos vaya a interferir con lo que estoy almacenando al extender el bit de signo al bit 31 en lugar de dejarlo en el bit 15 donde lo desee. ¿Cuál es la mejor manera de almacenar algo en esta ubicación?almacenamiento corto firmado en los 16 bits inferiores de un unsigned int

Aquí fue mi código original, que estoy casi seguro que está mal:

#define CTRL_REG *((volatile unsigned int *)0x4000D008u) 
short calibrationValue; 

CTRL_REG = -2 * calibrationValue; 

Luego he intentado esto, pero creo que aún podría estar sujeto a un entero promoción en el punto de la misión:

CTRL_REG = (short)(-2 * calibrationValue); 

Entonces, finalmente pensé en esto:

CTRL_REG = (unsigned short)(short)(-2 * calibrationValue); 

no puede evaluar estas opciones muy bien porque todos funcionan en mis pruebas porque calibrationValue resulta ser negativo (es un parámetro de calibración específico para cada dispositivo, por lo que podría ser positivo en algunos dispositivos), así que después de multiplicar por -2, termino almacenando un valor positivo y por lo tanto, en realidad no me encuentro con el problema que estoy esperando en mis pruebas.

Su ayuda es muy apreciada, incluso si es solo para decir "usted está pensando demasiado".

+0

Si quiere estar seguro de que su variable es de 32 bits, puede usar 'uint32_t' en su lugar, desde stdint.h. Tenga en cuenta que un 'int' [no tiene que ser de 32 bits] (http://codinghorror.typepad.com/.a/6a0120a85dcdae970b0128776ff992970c-pi), ni siquiera en sistemas de 32 bits. –

+1

@WTP: gracias por su preocupación. tenemos un encabezado específico del compilador que contiene definiciones de tipo que garantizan el tamaño. No son la uint32_t estándar ni la nomenclatura, así que en lugar de confundir mi publicación con tipos no estándar, opté por convertir todo en tipos que todos hayan visto antes. – rmeador

+1

Sugeriría al menos un par adicional de paréntesis alrededor de la macro: "(* ((volatile unsigned int *) 0x4000d008u))" – wildplasser

Respuesta

4

pensar acerca de lo que es un -16 (por ejemplo) se ve como en 16 bits: '0xFFF0', y en 33 bits: '0xfffffff0'. La extensión de la señal es exactamente lo que desea para asegurarse de tener un bit de signo en el lugar correcto. Como los 16 superiores no son cuidadosos, está bien llenarlos con 1s.Por lo tanto crear un valor con signo de 32 bits, y luego la echó a un sin signo de 32 para poner en su registro:

Int32 calreg= -2L * calibrationValue; 
    CTRl_REG = (Uint32)calreg; 

Si prefiere escribir 0s a los bits altos, máscara con 0xFFFF antes de la fundición.

+0

Creo que has descubierto el error en mi razonamiento ... Estaba pensando que el bit de signo se movió, en lugar de que todo el número intermedio de bits se llenara con 1s. Si estoy siguiendo correctamente, incluso la variable intermedia y el molde en su ejemplo son innecesarios, y mi código original (el más simple) funcionará, independientemente de cómo el compilador pueda extenderlo/promoverlo. ¿Es esto correcto? – rmeador

+0

Sí, eso es correcto; el código simple original funcionará bien. –

+0

El código original debería funcionar. Todavía agregaría el molde, tanto para evitar las advertencias del compilador como para dejar en claro que el cambio de tipo es intencional. No estoy seguro de que la 'L' después de la 2 sea estrictamente necesaria, pero prefiero incluirla en lugar de descubrir que no pude predecir las reglas de promoción entera correctamente: 'CTRL_REG = (Uint32) (- 2L * calibrationValue); ' – AShelly

0

Si se ignoran los valores escritos en los 16 bits más significativos, creo que puede confiar en la conversión de tipo. En general, no te importan los bits más altos.

#define CTRL_REG *((volatile signed int *)0x4000D008u) 
short calibrationValue; 

CTRL_REG = -2 * calibrationValue; 

en cuenta que la CTRL_REG está declarada como una firmaron número entero ahora.

+0

Me temo que no tengo la capacidad de redefinir esa macro como signed. Sin embargo, no creo que importe, como otros han señalado. – rmeador

0
#define SS2L(sh,sl) (((sh) <<16) | ((sl) & 0xffff)) 

Para combinar dos cortos de 16 bits en uno de 32 bits de largo.

Lo mejor es que los bitop estén hechos usando tipos sin firmar; promoción a int puede atraparte. Tal vez agregue algunos moldes adicionales a unsigned a la macro anterior, también.

3

En lugar de unsigned int, defina una unión de un int sin firmar y 2 ints cortos firmados.

Así es como manejo los "graciosos registros de control de hardware" en mis sistemas ARM donde, con frecuencia, los bits impares aquí y allá son "no me importa" o "no deben tener 1 escrito para ellos".

Rgds, Martin