Autor Tema: problemas con operaciones aritméticas en CCS  (Leído 1953 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado remi04

  • PIC24F
  • *****
  • Mensajes: 657
problemas con operaciones aritméticas en CCS
« en: 17 de Mayo de 2017, 08:01:15 »
Hola a todos, el problema es que si escribo esta expresion:

 signed int32 X1 = (UT-AC6) * AC5 / 32768

 Pues esa expresión siempre me devuelve CERO (0) .

Las variables UT y AC6 son globales, declaradas en mayúscula por razones de desarrollo. UT es signado y AC6 no.

 

  Sin embargo, si lo escribo así:

   
   signed int32 X1 =  UT - AC6;
    X1 = X1 * AC5;
   X1 = X1 / 32768;

    Me funciona perfectamente.

  Mi version de CCS es 5.015 y lo uso como plugin en Mplab X IDE.

  ¿Alguna sugerencia?
     
« Última modificación: 17 de Mayo de 2017, 08:04:16 por remi04 »

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:problemas con operaciones aritméticas en CCS
« Respuesta #1 en: 17 de Mayo de 2017, 08:26:32 »
Que tal unos parentesis mas?

Código: C
  1. signed int32 X1 = ((UT-AC6) * AC5) / 32768

o con un cast por las dudas

Código: C
  1. signed int32 X1 = (signed int32)((UT-AC6) * AC5) / 32768

Desconectado remi04

  • PIC24F
  • *****
  • Mensajes: 657
Re:problemas con operaciones aritméticas en CCS
« Respuesta #2 en: 17 de Mayo de 2017, 08:43:46 »
Hola KILLERJC.  Gracias por la respuesta.

  He probado las dos opciones que planteas y ambas me siguen dando cero.
 
Te paso los valores de las variables:

  UT= 24180
AC6 = 15336
AC5 = 24741



  Para ver el resultado estoy usando :

printf("X1: %Ld  ",X1);

Por si pudiese estar mal redactado el print, aunque el mismo si que me funciona con las expresiones una a una.




Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:problemas con operaciones aritméticas en CCS
« Respuesta #3 en: 17 de Mayo de 2017, 09:04:10 »
Me confundi al poner el cast, asi si funciona.

Código: C
  1. signed int32 X1 = ((signed int32)(UT-AC6) * AC5) / 32768

Lo que hacemos es forzar que el resultado de UT-AC6 se ponga temporalmente en un entero de 16 bits, asi al multiplicarlo por AC5 sigue almacenado en ese temporal de 32 bits.Y da un resultado. Seguramente al no tener eso como solo necesitaba un entero de 16 bits para la operacion de la resta se mantenia ese tamaño.

Desconectado remi04

  • PIC24F
  • *****
  • Mensajes: 657
Re:problemas con operaciones aritméticas en CCS
« Respuesta #4 en: 17 de Mayo de 2017, 09:26:08 »
Ahora si..   Muchisimas gracias.

Una ultima pregunta,  ¿por qué hay que ir "casteando" en las expresiones,  ¿fallo del compilador?

 Yo es que en C llevo muy poco tiempo y vengo de Pbp

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:problemas con operaciones aritméticas en CCS
« Respuesta #5 en: 17 de Mayo de 2017, 15:36:54 »
No, es un caso "raro" que ocurra esto, pero puede ocurrir, voy a mostrar un ejemplo y vamos a ver en ensamblador que es lo que hace el compilador, uso XC8 en ves de CCS pero es casi igual.. Esto suele ocurrir cuando uno maneja varios tipos de datos en una misma sentencia, ya que los numeros serian enteros sin signos de 16bits, y que luego no solo los pones en 32bits sino que ademas es con signo. Como casi siempre uno se maneja con numeros SIN signos ocurre poco. Vayamos al ejemplo, tal ves no tengas idea de ASM pero no importa, esta explicado que hace el codigo luego de darlo.

El codigo es este:

Código: C
  1. #include <xc.h>
  2. #include <stdint.h>
  3.  
  4. #define UT (24180)
  5. #define AC6  (15336)
  6. #define AC5  (24741)
  7.  
  8. int32_t X1;
  9.    
  10. void main (void)
  11. {
  12.  
  13.    
  14.         while(1)
  15.         {
  16.             // Codigo 1
  17.             X1 = ((int32_t)(UT-AC6) * AC5) >> 15;
  18.             // Codigo 2
  19.             X1 = ((UT-AC6) * AC5);
  20.             X1 >>= 15;
  21.         }
  22. }

Codigo 1 en ASM:

Código: ASM
  1. 0x1FF66: MOVLW 0x15
  2. 0x1FF68: MOVWF X1, ACCESS
  3. 0x1FF6A: MOVLW 0x1A
  4. 0x1FF6C: MOVWF 0x12, ACCESS
  5. 0x1FF6E: MOVLW 0x0
  6. 0x1FF70: MOVWF 0x13, ACCESS
  7. 0x1FF72: MOVLW 0x0
  8. 0x1FF74: MOVWF 0x14, ACCESS

Parece complicado, pero tenes que pensar que X1 es de 4 bytes, entonces como el micro utiliza registros de 1 byte, debe asignar 4 valores. Comienza asignado el valor 0x15 al byte de menor peso, luego 0x1A, luego 0x0 y finalmente 0x0. Es decir el primer "Codigo" al ser todos constantes el compilador precalcula  y directamente reemplaza el valor por esa constante calculada ( No requiere instrucciones del micro para calcularlo).
Resumen asigna el valor 0x00001A15 (6677) a X1.

El codigo 2 consta de 2 partes, la primera es la asignacion a X1 y luego el shift

Código: ASM
  1. !      Aca poniendo el valor
  2. 0x1FF76: MOVLW 0x3C
  3. 0x1FF78: MOVWF X1, ACCESS
  4. 0x1FF7A: MOVLW 0xC4
  5. 0x1FF7C: MOVWF 0x12, ACCESS
  6. 0x1FF7E: MOVLW 0xFF
  7. 0x1FF80: MOVWF 0x13, ACCESS
  8. 0x1FF82: MOVLW 0xFF
  9. 0x1FF84: MOVWF 0x14, ACCESS
  10. !            X1 >>= 15;
  11. 0x1FF86: MOVLW 0xF
  12. 0x1FF88: MOVWF 0xD, ACCESS
  13. 0x1FF8A: RLCF 0x14, W, ACCESS
  14. 0x1FF8C: RRCF 0x14, F, ACCESS
  15. 0x1FF8E: RRCF 0x13, F, ACCESS
  16. 0x1FF90: RRCF 0x12, F, ACCESS
  17. 0x1FF92: RRCF X1, F, ACCESS
  18. 0x1FF94: DECFSZ 0xD, F, ACCESS
  19. 0x1FF96: BRA 0xFF8A

Este codigo es interesante, en si la primer parte es una asignacion simple como ocurrio antes. Y aca es donde ocurre la "magia".
Supuestamente deberiamos tener el valor: (24180-15336) * 24741 = 218809404 = 0x0D0A.C43C (Separo cada 16 bits asi es mas entendible)

Pero mirando el codigo vemos que lo que se asigna en realidad es: 0xFFFF.C43C  Lo cual es distinto A lo que deberia haber sido, seguramente hay algo importante que no estamos viendo.
Y lo que no estamos viendo es que C ejecuta las cosas "temporalmente" y luego asigna al lugar.

Aca viene la explicacion de porque ocurre esto.

Segundo caso ( el erroneo ) :
Tengo numeros de 16 bits como lo son UT, AC6  y AC5. Entonces espero que las operaciones que se realizen con estos numeros enten dentro de los valores de esos 16 bits. En este caso lo precalcula el compilador, pero si hubieran sido registros de no hacerlo asi, deberia trabajar con todos numeros de 32bits para lo cual un microcontrolador de 8 bits le llevaria el doble de trabajo o mas en algunas operaciones. Asi que si tenes 2 enteros sin signo de 16bits y los restas, se van a generar instrucciones para que solamente sobrevivan 16bits. Me explico un poco mas:
Temporalmente trabaja con todos los bits. primero hace la resta, luego la multiplicacion y despues?. Al multiplicar le queda esto:

0x0D0A.C43C

Pero estoy trabajando con solo 16 bits entonces RECORTA esos 32 en solo 16bits quedando solamente: 0xC43C ( los ultimos 4 nibbles )
De alli ahora tiene eso y el shift que va a tener que ser un entero con signo.. Ese entero de 16bits se promociona a uno mas grande que es el entero con signo. Quedando 0xFFFF.C43C, por que queda asi? si recordas 0xC en binario es 1100 , el bit de mayor peso define el signo por lo tanto al pasarlo a un numero de mas bits se debe trasladar el signo por eso son todas las F (1111) al comienzo. Si rotamos van a ir agregandose unos a la izquierda para mantener el signo , por lo tanto llegariamos al punto de tener 0xFFFF.FFFF ( -1 )

Que es lo que me da el segundo codigo.

Ahora el primer codigo que es el que funciona:
La diferencia radica en que le especificamos al compilador que el resultado de la resta lo guarde en un entero con signo de 32 bits. De esa forma internamente va a llevar los numeros ( UT y AC6) promocionarlos de 16bits a 32bits con signo y de alli operar, pero no va a recortarlo para la otra operacion ya que nosotros le dijimos que lo mantenga en 32bits. De alli simplemente es continuar y seguir haciendo lo normal.

Resumen, le cambiamos el comportamiento del compilador al obligarlo a que el resultado de la resta (que lo hizo con enteros sin signo o con signo) lo guarde y mantenga temporalmente en un entero con signo de 32bits. Y no proceda a recortarlo a esos 16 bits que eran el tipo de numero que tenia.

Desconectado remi04

  • PIC24F
  • *****
  • Mensajes: 657
Re:problemas con operaciones aritméticas en CCS
« Respuesta #6 en: 18 de Mayo de 2017, 12:29:22 »
Gracias de nuevo. Da para estudiarlo bien de modo que este mensaje pasa a mis apuntes.

  Como son muchas operaciones, lo que hice fué declarar todas las variables implicadas en las formulas como enteros con signo de 32 bits salvo las que me indican que deben ser sin signo. Esto para no tener que castear demasiado.

  Todo cogiéndolo con pinzas, por que por ejemplo, la variable AC1 debe ser entero signado de 16 bits. Al declararla como 32 bits para evitar castear me daba error alguna operación que usaba "<<" por que aunque los dos bytes bajos conservan su valor, lo cierto es que cuando se obtenía una cifra negativa, el operador << me tumbaba el MSbit "el primero de los 32 bits" que es el que indica el signo dando un resultado erróneo.

  Al final lo he conseguido. Ya están todas las operaciones ejecutándose perfectamente.

  También he tenido que lidiar con un "Segment out of rom" que me ha obligado a reestrucuturar el codigo para acortar las funciones que contienen estas operaciones.

  Gracias por todo, me ha servido enormemente la ayuda.

  Saludos.


 

anything