Discussion:
Sobre calculos.
(demasiado antiguo para responder)
j***@gmail.com
2007-04-06 08:09:43 UTC
Permalink
Hola:

Estoy programando un microcontrolador atmel atmega32 y atraves del
"puerto" i2c tengo conectado un termometro DS1631 que me da la
temperatura en 2 palabrase de 8 bits. Lo que hago para pasar eso a
grados centigrados es:

unsigned char MSB;
unsigned char LSB;
float temp_c;

temp_c = (float)((MSB<<8 + LSB) - 65536) * 0.0625;

Es correcto, ya que no me da lectura ninguna. ¿?

S2.
Bartomeu
2007-04-06 10:35:04 UTC
Permalink
Primero comprueba que en MSB y LSB tienes valores. Suponiendo que tengan
valores, ya que no se cómo ni cuando cambia los unsigned char a int, lo
haría yo explicitamente así:

temp_c = (float)((((unsigned int)MSB)<<8u + (unsigned int)LSB) - 65536u) *
0.0625;

O si tienes int de 16 bits, te podrías ahorrar el desplazamiento y la suma
con un union de un int con dos unsigned char.

<***@gmail.com> escribi� en el mensaje news:***@o5g2000hsb.googlegroups.com...
Hola:

Estoy programando un microcontrolador atmel atmega32 y atraves del
"puerto" i2c tengo conectado un termometro DS1631 que me da la
temperatura en 2 palabrase de 8 bits. Lo que hago para pasar eso a
grados centigrados es:

unsigned char MSB;
unsigned char LSB;
float temp_c;

temp_c = (float)((MSB<<8 + LSB) - 65536) * 0.0625;

Es correcto, ya que no me da lectura ninguna. ¿?

S2.
Pedro Maicas
2007-04-06 16:42:24 UTC
Permalink
Post by j***@gmail.com
temp_c = (float)((MSB<<8 + LSB) - 65536) * 0.0625;
Es correcto, ya que no me da lectura ninguna. ¿?
Eso es matar moscas a cañonazos, en un micro
sin unidad de coma flotante, sin apenas memoria
de programa ni ram, y sobre todo sin ninguna necesidad,
no deberías usar float, aunque el compilador lo soporte
seguro que te mete ahí un motón de código.

Por otro lado el msb<<8 es sopechoso de que te va a perder
su contenido, como al compilador le de por no convertirlo
a entero (y supuesto un entero de 16 bits, que habría que verlo,
igual el entero es de 8 bits en este caso). Aún con 16 bits,
la operacion -65536 no parece tener mucho sentido, puesto
que con dos bytes nunca vas a tener una lectura 65536,
¿ entonces el 'cero' cual seria ?

Y para colmo, multiplicar por 0.0625 es lo mimso que
dividir por 16, es decir lo mismo que hacer un desplazamiento
de cuatro bits, ... todo bastante absurdo.


Saludos :-) -Pedro-

http://www.maicas.net/

e-mail en www.maicas.net
j***@gmail.com
2007-04-08 15:26:06 UTC
Permalink
Hola de nuevo:

Me explicare de otra manera. El termometro del que debo leer la
informacion me envia esta en dos palabras de 8 bits, las cuales forman
una de palabra de 16 bits en complemento a dos. Lo que necesito es
saber la forma de pasar las palabras de 8 bits al otro formato. Y
despues devolver un resultado que me indique el valor en grados
celsius. Perdonad, entiendo que es muy basico, pero mis conocimientos
tambien lo son. Yo intento esto:

Con esto capturo la palabra de 8 bits del registro del
microcontrolador

int8_t I2C_DS1631_Get_Data( void)
{
int8_t twst;

while ((TWCR & _BV(TWINT)) == 0) ; /* wait for transmission */
twst= TWDR;
return twst;
}

Y con esto paso dicha palabra de 8 bits a una de 16

para la primera palabra, la de mayor rango:
temperatura=(int16_t)(I2C_DS1631_Get_Data()<< 8 ); /*Inserto la
primera palabra de 8 bits en la de 16 y la pongo a izqdas.*/
para la segunda palabra, la de menor rango:
temperatura |= (int16_t)(I2C_DS1631_Get_Data()>>8 ); /*Inserto la
segunda palabra de 8 bits en la de 16 y la pongo a drchas*/

¿Con esto, no deberia tener ya la palabra de 16 bits?

en fin, ruego me ayudeis, gracias.

S2.
Pedro Maicas
2007-04-08 17:22:24 UTC
Permalink
Post by j***@gmail.com
temperatura=(int16_t)(I2C_DS1631_Get_Data()<< 8 ); /*Inserto la
primera palabra de 8 bits en la de 16 y la pongo a izqdas.*/
Yo quitaría el parentesis, primero lo conviertes
a entero de 16 bits y luego lo desplazas:

temperatura=(int16_t)I2C_DS1631_Get_Data() << 8;
Post by j***@gmail.com
temperatura |= (int16_t)(I2C_DS1631_Get_Data()>>8 ); /*Inserto la
segunda palabra de 8 bits en la de 16 y la pongo a drchas*/
Esto te pierde el contenido del byte leído al
desplazarlo, mejor:

temperatura |= (int16_t) I2C_DS1631_Get_Data();

Supuesto que int8_t sea unsigned, porque si es
con signo tampoco funcionará, podrías probar:

temperatura |= (int16_t) I2C_DS1631_Get_Data() & 0x00FF;

Y cruzas los dedos (no estoy seguro de que funcione,
puede depender del tamaño del entero de este compilador)

Lo más facil sin embargo seria escribir directamente
cada byte en su sitio,

unsigned char *pp = (unsigned char*) &temperatura;

pp[0] = I2C_DS1631_Get_Data();
pp[1] = I2C_DS1631_Get_Data();

O al revés (depende del compilador):

pp[1] = I2C_DS1631_Get_Data();
pp[0] = I2C_DS1631_Get_Data();

Lo que hagas luego ya depende de lo que necesites,
por ejemplo si eso son décimas de grado y quieres
mostrarlo en un lcd o similar, tandrás que descomponer
el numero en sus digitos en formato decimal o bien
comprobar si printf lo hace:

printf("%d", temperatura);
si esto te da la temperatura en decimas de grado
y quieres añadir el punto decimal en el medio
podrías hacer algo así:

if(temperatura < 0){
putchar('-');
temperatura = - temperatura;
}
printf("%d", temperatura / 10);
putchar('.');
printf("%d", temperatura % 10);

Aún así la division por diez es un lujo en un micro de estos,
muchas veces es mejor recurir a tablas o a algoritmos
raros para para evitar multiplicaciones y divisiones.

Saludos :-) -Pedro-

http://www.maicas.net/

e-mail en www.maicas.net
j***@gmail.com
2007-04-08 17:49:12 UTC
Permalink
Muchas gracias:

Parece que ahora ya funciona.

Lo primero de todo, por lo visto el dichoso chip primero entrega el
LSB y luego el MSB, aunque en la datasheets parece indicar lo
contrario ¿?.
Lo segundo cambie el tipo para LSB y MSB de int8_t a unsigned char y
va bien.
Lo tercero puse esta asignacion para transformar los LSB y MSB a un
int16_t para la temperatura:

temperatura=(int16_t)(((unsigned int)MSB)<<8u | ((unsigned
int)LSB>>8u));

Despues, aunque me digas que es un consumo de recursos excesivo, para
pasar a grados centigrados, lo tengo asi:

temp_c=(float)(((temperatura)*125)/32000);

siempre que la temperatura sea positiva claro.

En fin gracias por ayudarme.

S2.

PD:Ya te dare la lata con otra cosa :)
Pedro Maicas
2007-04-08 19:42:39 UTC
Permalink
Post by j***@gmail.com
Despues, aunque me digas que es un consumo de recursos excesivo, para
temp_c=(float)(((temperatura)*125)/32000);
Bueno, si te funciona y no te falta memoria ni velocidad
no es que valga la pena cambiar nada, peeeero si quieres
darle un poco a la neurona (cosa que nunca vienen mal,
algo se aprende) piensa que:


uno: temperatura es un entero de 16 bits, lo multiplicas por
125 y lo divides por 32000, en la multiplicacion puedes tener
un overflow y te fallará (haz la prueba para el valor mas
alto posible)

dos: de todos modos esa operacion es lo mismo que dividir por 256,
y eso es lo mismo que despreciar el byte bajo y quedarte solo con
el alto. O estás perdiendo precision y no te has enterado o bien
toda la comedura de tarro anterior no ha servido para nada, si solo
necesitabas el byte alto.

prueba ((float)temperatura * 125) / 32000;
que igual te da mejor resultado :-)

... y no se parece ne nada al anterior en el que dividías por 0.065

Saludos :-) -Pedro-

http://www.maicas.net/

e-mail en www.maicas.net
j***@gmail.com
2007-04-08 21:40:26 UTC
Permalink
Hola de nuevo:

Tienes razon, el tema de la resolucion es el siguiente:

Table 4. 12-BIT RESOLUTION TEMPERATURE/DATA RELATIONSHIP
TEMPERATURE(ºC) DIGITAL OUTPUT(BINARY) DIGITAL OUTPUT(HEX)
+125 0111 1101 0000 0000 7D00h
+25.0625 0001 1001 0001 0000 1910h
+10.125 0000 1010 0010 0000 0A20h
+0.5 0000 0000 1000 0000 0080h
0 0000 0000 0000 0000 0000h
-0.5 1111 1111 1000 0000 FF80h
-10.125 1111 0101 1110 0000 F5E0h
-25.0625 1110 0110 1111 0000 E6F0h
-55 1100 1001 0000 0000 C900h

Segun lo que me comentas y para una resolucion de 0.0625, solo es
necesario hacer la cuenta de la manera siguiente, o eso creo:

Para valores positivos, bit 15 a 0:

temp_c=((float)temperatura/256);

El cual me da un valor fraccionario y preservo la precision, mientras
que si desplazo, dicha parte decimal desaparece y pierdo toda
precision, o eso creo.

Para valores negativos, bit 15 a uno:

temp_c=((float)(temperatura-65536)/256);

La verdad, no estoy muy puesto en calculo binario, disculta los
errores.

S2.
Pedro Maicas
2007-04-10 17:28:07 UTC
Permalink
Post by j***@gmail.com
La verdad, no estoy muy puesto en calculo binario, disculta los
errores.
No he tenido tiempo de mirarlo, pero comprobare los
valores binarios y te diré algun método de calculo
más sencillo (para el micro).

Una cosa: para cambiarle el signo a un numero
en complemento a dos se suele hacer n = (~n) + 1;
Es decir que se invierten todos los bits y se suma
uno. Lo digo porque restar 65536 te obliga de nuevo
a sobrepasar el tamaño del entero de 16 bits


Saludos :-) -Pedro-

http://www.maicas.net/

e-mail en www.maicas.net
j***@gmail.com
2007-04-10 18:10:54 UTC
Permalink
Te pego la contestación que me ha dado un colega sobre las
características concretas del DS1631 "el termómetro".
From the datasheet, in the 12bit mode, resolution is in 0.0625degC. As
a binary fraction 0.0625 * 2^16 (65536) = 4096. Since the lower 4 bits
of the result are 0, we divide by 2^4 (16). 4096/16 = 256 (2^8. so, If
you multiply the 16 bit result by 256, you'll get the temperature with
16bits of fraction.

Or, if you want to truncate the result to integer degrees, just divide
the result by 256.

¿En este cálculo que pasa con el complemento a dos.?Creo que lo está
metiendo como un valor más de la conversión de temperatura.¿No se
deberia primero quitar el complemento (~n+1) para despues multiplicar
por 256.?

S2.
Pedro Maicas
2007-04-11 17:36:11 UTC
Permalink
Post by j***@gmail.com
Te pego la contestación que me ha dado un colega sobre las
características concretas del DS1631 "el termómetro".
La opcion mas sencilla, desde el punto de vista de un
programador trabajando en C, es la que te dice Antoine,
temp_c = temperatura / 256.0F;
que es lo mismo que:
temp_c = (float)temperatura / 256;

Pero si programases en ensamblador un micro de estos, te darías
cuenta hasta que punto conviene prescindir de las operaciones
en coma flotante e incluso de multiplicaciones y divisiones,
todo lo que se pueda hacer sumando y restando enteros (y mejor
en 8 bits) es siempre mucho mejor.

El termometro ese, tiene una precision de 0.5ºC, el byte
alto te proporciona la parte entera, y el bit 7 del byte bajo
te proporciona la precision suficiente (medio grado).


Saludos :-) -Pedro-

http://www.maicas.net/

e-mail en www.maicas.net
j***@gmail.com
2007-04-11 17:59:53 UTC
Permalink
Gracias a todos.

La verdad es un gusto contar con vuestra ayuda.

Antoine Leca
2007-04-11 08:04:59 UTC
Permalink
Post by j***@gmail.com
Table 4. 12-BIT RESOLUTION TEMPERATURE/DATA RELATIONSHIP
TEMPERATURE(ºC) DIGITAL OUTPUT(BINARY) DIGITAL OUTPUT(HEX)
+125 0111 1101 0000 0000 7D00h
+25.0625 0001 1001 0001 0000 1910h
O sea, que el primer octeto te da directamente la lectura en grados Celsius
(signado), y el segundo te da los decimales (no signado y multiplicado por
256).
Post by j***@gmail.com
temp_c=((float)temperatura/256);
Correcto (y para negativo también). También se puede escribir
temp_c = temperatura / 256.0F;

El hecho que 256.0 es un float forzará a usar la división de los float,
aunque temperatura siga un entero.


Si después no necesitas manejar float (por ejemplo, si solo vas a leer los
grados), lo más senzillo es guardar el primer octeto directamente como
temperatura leída, despues leer el segundo octeto en un temp, y si es
superior o ígual a 0x80 (si lees como unsigned) o si es negativo (si lees
como signed, de hecho el valor suyacente es el mismo, es de la forma 1xxx
0000) añadir 1 a la temperatura por redondeo.


Antoine
Loading...