Autor Tema: Dudas con operacion aritmética  (Leído 5869 veces)

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

Desconectado Miquel_S

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 1251
Re: Dudas con operacion aritmética
« Respuesta #15 en: 23 de Marzo de 2015, 18:43:53 »
Si, esto de modificar la ecuación lo tenia en cuenta deja que lo pruebe y si no lo consigo pregunto.

Saludos!
Todos somos muy ignorantes. Lo que ocurre es que no todos ignoramos las mismas cosas.

Desconectado Miquel_S

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 1251
Re: Dudas con operacion aritmética
« Respuesta #16 en: 24 de Marzo de 2015, 18:48:04 »
Hay una cosa la cual no la tengo clara, en el siguiente código realizado por KILLERJC el cual entiendo lo que hace cada instruccion
Código: ASM
  1. ;----------------------------------------------------------------------------------------
  2. ;   RUTINA PARA OPERACION ARITMETICA
  3. ;----------------------------------------------------------------------------------------
  4. POSICION
  5.             BCF              STATUS,C            ;Limpiamos el bit C del registro STATUS
  6.             RRF              ADRES,W             ;Rotamos a la derecha el valor de ADRES (Dividimos por 2)
  7.             MOVWF        POSIC                   ;ADRES --> POSIC
  8.             RRF              POSIC,W              ;Rotamos a la derecha el valor de POSIC (Dividimos por 2)
  9.             ANDLW        0x30                     ;w AND k --> w
  10.             MOVWF        TEMP                   ;W --> TEMP
  11.             SWAPF        TEMP,W               ;Los cuatro bits de más peso del registro TEMP se intercambian
  12.                                                              ;con los 4 bits de menos peso del mismo registro.
  13.             SUBWF        POSIC,F               ;Resta en complemento a dos el contenido del registro POSIC
  14.                                                              ;menos el contenido del registro W
  15.             MOVLW        0x1F                    ;Cargamos en W el valor 1F, 31 en decimal
  16.             ADDWF        POSIC,F             ;Sumamos el contenido de W al contenido del registro POSIC
  17.             RETURN
¿hay alguna lógica la cual yo no la veo para usar estos valores y operaciones de dividir, sumar, restar o por el contrario se trata de ir probando hasta conseguir el resultado esperado? lo pregunto porque aunque no tenga nada que ver ¿no es mas fácil por ejemplo sumar 20 + 20 que dividir 20 entre dos para después sumarle 30?

Perdón por mi ignorancia.

Gracias.
« Última modificación: 24 de Marzo de 2015, 19:00:06 por Miquel_S »
Todos somos muy ignorantes. Lo que ocurre es que no todos ignoramos las mismas cosas.

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re: Dudas con operacion aritmética
« Respuesta #17 en: 24 de Marzo de 2015, 19:12:29 »
Si, por supuesto que hay una logica detras todos los programas que pase. Todo parte de funcion matematica que pasaste, buscando distintas combinaciones.
Como ves en mis primeros programas podrias dividir el programa en 3 partes, multiplicacion por 31 , division por 64 y luego la suma (en el ultimo tambien pero no tan explicito), luego le buscamos un poco mas la vuelta para generar otra ecuacion, que fue la de reemplazar el 31 por (32 -1) en la formula y que es equivalente. Asi no era necesario multiplicar, sino solamente dividir por numeros potencia de 2. Incluso en el comentario de mi primer programa puse algunas cosas que se me ocurrieron:

Citar
; Podia hacerlo de 4 formas a la multiplicacion:
;  - Sumar 31 veces el resultado del ADC (31 sumas de 16bits )
;  - Sumar 31 por cada unidad del resultado del ADC (x sumas de 16 bits)
;  - Realizar ((ADC * 2) * 3 ) * 5 + ADC = ADC * 31 , separo en simples rotaciones y sumas ( *2 = rotacion , *3 = rotacion y suma , *5 = 2 rotacion y suma )
;  - Sumar las rotaciones de cada bit en 1 del multiplicador - Elegi esta ultima por ser la mas corta en programa aunque lleve un par de ciclos mas (4 sumas de 16bits + rotacion)

Luego se me ocurrio lo de hacer el (32-1) y ahi cambio y se simplifico mucho mas

La explico como funcionan o como es que llegas a esas operaciones...

Tanto para 8 bits de ADC (este ultimo era para la funcion que es 124/256 o 31/64 ojo) como para 10 bits ( este te la explico para cuando es 124/1024 = 31/256 =

8bits ADC:

Formula utilizada:
31 + (ADC * 124 ) / 256  (8 bits)
Esta formula como ya explique se puede escribir como 31 + ADC/2 - ADC/64
Tomando el 31 como (32 -1) , aplicando la propiedad distributiva y simplificando nuevamente, hasta aqui solo el manejo de simples ecuaciones.

ADC  = xxaa aaan       (Resultado del ADC)
ADC/2 = 0xxa aaaa     (Valor a conseguir)
ADC/64 =0000 00xx    (Valor a conseguir)

Lo primero que hago es rotar a la derecha ( dividir por 2) y guardo es decir ya tengo mi ADC/2 guardado, luego tengo que restarle ADC/64 si observas yo denote a los 2 bits mas significativos como XX que es lo unico que "sobrevive" a esa division
POSIC= ADC/2
Si roto una ves mas ADC/2 (sin importar el carry) este me quedaria: u0xx aaaa  ( u de indefinido segun el carry anterior, podria haberlo puesto a 0, es una instruccion mas y sin sentido ya que luego le hago un AND a ese valor)
Luego una AND con 0011 0000 quedandome  00xx 0000 y guardo en TEMP .
TEMP = ADC/64 * 16
Cuando voy a restar, realizo el cambio de nibble W = 0000 00xx = ADC/64 y resto.
POSIC = ADC/2 - ADC/64
Esto jamas me va a dar negativo por que si tengo cualquiera de los bits significativos en 1, ADC/2 va a ser mucho mayor que ADC/64 por eso quite la parte donde pregunto por el carry en la resta.
Luego sumo 31 o que es igual a 0x1F
POSIC = 31 + ADC/2 - ADC /64
Tampoco pregunto por si hay carry debido a 2 cosas, suponiendo el peor de los casos de ADC = 1111 1111 , al rotarlo queda 0111 1111 , ademas se le resta 3 y le tendrias que sumar casi 128 para que se produzca un overflow. Asi que jamas va a suceder.

10 bits ADC:

Funcion ( sigo manteniendo el maximo de 155 )
El mismo caso de antes, simplifico , tomo 31 como 32 -1 , distribuyo y simplifico nuevamente
31 + ADC * 124 / 1024  =  31 + ADC/8 - ADC/256
= 31 + ADC * 2-3 - ADC *2-8

Usando justificado derecha como veniamos haciendo quedaria:

ADCH = 0000 00xx    (Resultado del ADC)
ADCL = aaaa annn     (Resultado del ADC)

ADC/8 = 0000 0000 0xxa aaaa    (Valor a conseguir)
ADC/256 = 0000 0000 0000 00xx  (Valor a conseguir)

Si observas con el caso anterior la parte baja ADCL es igual que en el caso de 8 bits a pesar que cambian los divisores. Y solo trabajariamos con 8 bits
Cosas interesantes a notar. ADC/256 = ADCH (1 instrucciones) lo cual es super simple de sacar , pero para llegar a ADC/8 tengo que rotar 3 veces (7 instrucciones ya que son de 16bits 2 rotaciones), los bits marcados con nnn no "sobreviven"

Para simplificar se me ocurrio usar el justificado izquierda y observo que solo la parte alta es de mi interes, nuevamente denoto como nnn como los bits que no me interesan o que serian eliminados por la operacion

ADCHji = xxaa aaan
ADCLji = nn00 0000

ADCHji/2 = 0xxa aaaa    ( Lo mismo que ADC/8 del otro justificado, pero ahora con una sola rotacion - 3 instrucciones, consegui el mismo valor pero con menos instrucciones )
ADCHji/4 = u0xx aaaa    ( Igual que antes hago una AND con 0x30 y luego un SWAP antes de restar, equivalente a ADC/256 , este ya no es tan facil como antes que era ADCH sino que volvemos a lo mismo que hice con 8 bits - 3 instrucciones )

Aprovecho el justificado para que los bits esten en otro lugar y tratar de rotar lo menos posible.

Luego hago lo mismo sumo 31 nuevamente, se aplica todo lo demas siempre 0xxa aaaa va a ser mayor que 0000 00xx , por lo tanto no hay carry en la resta. Y tampoco va a excederse de 8 bits por que deberia sumarle en el peor de los casos un numero mayor a 128. Ademas seria imposible ya que el maximo dado por la ecuacion se encuentra en 155 ( 8 bits )

La ultima funcion esta bastante resumida. Si observas uno de los post que puse, cuando llegue con esta idea comenze rotando y necesitando cerca de 27 ciclos, luego con esta explicacion o rebuscado logre reducirlo aun mas

Citar
¿no es mas fácil por ejemplo sumar 20 + 20 que dividir 20 entre dos para después sumarle 30?

No entendi, podrias explicarlo con un ejemplo o una formula ?

EDIT:

Tu ejemplo pierde forma cuando supones esto:

x1 = A + A = 2A
y la otra es:
x2 = A/2 +30

Si A = 40,y uso esa misma logica : x1 = 80 y x2= 50
Para en el unico punto que se intersectan es con A = 20
x1 = x2
2A = A/2 +30
2A - A/2 = 30
3A/2 = 30
A = 30 * 2 / 3
A = 20

En fin no es la misma ecuacion. Y ya no se a donde te dirigias con esa pregunta...

Dio la suerte que esta funcion sea algo simple

Con respecto a los valores yo DEBO sumar 31, en el codigo que estamos haciendo te recuerdo la formula:

Código: C
  1. Posicion_Servo = 31 + (ValorADC * 124) / 256);

Lo unico rebuscado es la forma de multipicar y dividir aprvechandose que se esta en binario y con registros de 8 bits, la suma se hace normalmente.
Y en fin espero demostrar que no es/fue cuestion de prueba y error. O valores al azar.
Y que te sea util xD
« Última modificación: 26 de Marzo de 2015, 10:15:48 por KILLERJC »

Desconectado Miquel_S

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 1251
Re: Dudas con operacion aritmética
« Respuesta #18 en: 26 de Marzo de 2015, 14:51:29 »
Gracias de nuevo KILLERJG, ahora si que entiendo el proceso por completo, pasaba que había datos de la formula final que no sabia de donde salían. Ya puedo ir probando a escribir el código para el control del servo con potenciómetro.
Perdón por no haber dado las gracias antes pero quería tener claro lo de la explicación, lo digo porque en otro foro que muchos conocemos por tardar dos días en dar las gracias ya ni me contestaban si escribía.

Miquel_S
Todos somos muy ignorantes. Lo que ocurre es que no todos ignoramos las mismas cosas.

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re: Dudas con operacion aritmética
« Respuesta #19 en: 26 de Marzo de 2015, 17:27:23 »
Esta bien, por ahi no se posee tiempo, o algunos se dedican en sus tiempos libres a hacerlo.

Yo pienso que es peor que nadie te responda en 2 dias cuando creas un tema, que no que se tarden una semana en dar las gracias.
Igual me paso de gente que le contestas y luego no te responde mas xD

Desconectado elgarbe

  • Moderadores
  • PIC24H
  • *****
  • Mensajes: 2178
Re: Dudas con operacion aritmética
« Respuesta #20 en: 26 de Marzo de 2015, 20:31:29 »
Con semejante explicacion que diste en assembler como para no agradecer!!!!

sds.
-
Leonardo Garberoglio

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re: Dudas con operacion aritmética
« Respuesta #21 en: 28 de Marzo de 2015, 00:22:05 »
Como todo esto comenzo en C y quisiera ver que tan bueno puede ser.
Ademas para expandir el conocimiento, y sacarme la duda xD
Decidi hacer algunos calculos y mirarlos con el stopwatch y descubri algunas cosas interesantes:

El codigo que hice fue este:

Código: C
  1. unsigned char ADC,RESULTADO;
  2.     unsigned int  ADCD,RESULTADOD;
  3.  
  4.     ADC = 0xFF;
  5.     ADCD = 0x3FF;
  6.  
  7.     while(1)
  8.     {
  9.     RESULTADO = 31 + ((int)ADC * 124)/256;
  10.     RESULTADO = 31 + ((int)ADC * 31)/64;
  11.     RESULTADO = 31 + (((int)ADC * 31) >> 6);
  12.     RESULTADO = 31 + ADC/2 - ADC/64;
  13.     RESULTADO = 31 + (ADC >> 1) - (ADC >> 6);
  14.     RESULTADO = 31 + (ADC - ADC/32)/2;
  15.     RESULTADO = 31 + ((ADC - (ADC >> 5) )>> 1);
  16.  
  17.     RESULTADOD = 31 + ((unsigned short long)ADCD * 124)/1024;
  18.     RESULTADOD = 31 + (ADCD * 31)/256;
  19.     RESULTADOD = 31 + ((ADCD * 31) >> 8);
  20.     RESULTADO = 31 + (ADCD >> 3) - (ADCD >> 8);
  21.     RESULTADOD = 31 + ADCD/8 - ADCD/256;
  22.     RESULTADOD = 31 + (ADCD >> 3) - (ADCD >> 8);
  23.     RESULTADOD = 31 + (ADCD - ADCD/32)/8;
  24.     RESULTADOD = 31 + ((ADCD - ADCD >> 5) >> 3);
  25.     }

Muchos parentesis sino las rotaciones las toma mal , todas las funciones dan el mismo resultado, aunque el maximo de algunas esta entre 154 y 155
Probe mas combinaciones tambien, pero con pobres resultados.

Los resultados fueron los siguientes:

Citar
Target halted. Stopwatch cycle count = 654 (654 µs)
Target halted. Stopwatch cycle count = 740 (740 µs)
Target halted. Stopwatch cycle count = 290 (290 µs)
Target halted. Stopwatch cycle count = 1195 (1,195 ms)
Target halted. Stopwatch cycle count = 54 (54 µs)
Target halted. Stopwatch cycle count = 1238 (1,238 ms)
Target halted. Stopwatch cycle count = 55 (55 µs)

Target halted. Stopwatch cycle count = 506 (506 µs)
Target halted. Stopwatch cycle count = 312 (312 µs)
Target halted. Stopwatch cycle count = 312 (312 µs)
Target halted. Stopwatch cycle count = 21 (21 µs)
Target halted. Stopwatch cycle count = 41 (41 µs)
Target halted. Stopwatch cycle count = 41 (41 µs)
Target halted. Stopwatch cycle count = 69 (69 µs)
Target halted. Stopwatch cycle count = 67 (67 µs)

Primero que nada es notar la diferencia entre ciclos al realizar un desplazamiento o una division, por ejemplo entre estos 4 equivalentes:

    RESULTADO = 31 + ADC/2 - ADC/64; ---------------------------------- 1195 ciclos (casi el doble que nuestra ecuacion original y uno pensaria que deberia ser mucho mas rapido por como es la formula y como se aplica al microcontrolador)
    RESULTADO = 31 + (ADC >> 1) - (ADC >> 6);------------------------ 54 ciclos     (lo mismo que arriba pero con dezplazamientos, uan decima de lo que lleva la ecuacion original)
    RESULTADO = 31 + (ADC - ADC/32)/2;--------------------------------- 1238 ciclos
    RESULTADO = 31 + ((ADC - (ADC >> 5) )>> 1);---------------------- 55 ciclos

Pero es de notarse unicamente en 8 bits, pero cuando hacemos lo mismo con variables de 16 bits obtenemos:

    RESULTADOD = 31 + ADCD/8 - ADCD/256;----------------------------- 41 ciclos
    RESULTADOD = 31 + (ADCD >> 3) - (ADCD >> 8 );-------------------- 41 ciclos
    RESULTADOD = 31 + (ADCD - ADCD/32)/8;----------------------------- 69 ciclos
    RESULTADOD = 31 + ((ADCD - ADCD >> 5) >> 3); ------------------- 67 ciclos

Otra cosa interesante.. 16 bits menos ciclos que 8 bits ... incluso hasta con desplazamientos

Y la diferencia notable de 8 vs 16 bits:

    RESULTADO = 31 + (ADC >> 1) - (ADC >> 6);------------------------ 54 ciclos (ADC variable 8 bits)
    RESULTADO = 31 + (ADCD >> 3) - (ADCD >> 8 ); -------------------- 21 ciclos (ADC variable 16 bits)

El claro ganador es:

    RESULTADO = 31 + (ADCD >> 3) - (ADCD >> 8 ); -------------------- 21 ciclos
    RESULTADO = 31 + ADCD/8 - ADCD/256; ------------------------------ 21 ciclos ( este lo simule despues pero usa la misma cantidad de ciclos )

Que le quita 20 ciclos por solo cambiar a donde se guarda por una variable de 8 bits. Y no esta para nada lejos del ASM, y no le deja nada que envidiar.

Conclusiones:
- Al momento de dividir aca es mejor rotar si es potencia de 2 o tal ves no xD
- Y una diferencia inexplicable entre 8 y 16bits ( cuando 16 bits deberia ser aquel que lleva mas ciclos ), hasta la ecuacion original el cual es casteada a una variable de 24bits lleva menos tiempo que la de 8 bits casteada a 16
 
Probe hacerlo con un justificado izquierda como hicimos en ASM, pero el simple hecho de rotarlo mas veces para conseguir el mismo resultado, hacia que tuviera muchos mas ciclos

Por ultimo estudiamos el ASM generado:

Código: Microchip Assembler
  1. MOVF ADCDH, W         ; Mueve los registros ADCDH:ADCDL a unos temporales que estan ubicados en 0x20 (__pcstackBANK0) y 0x21
  2. MOVWF 0x21
  3. MOVF ADCD, W
  4. MOVWF __pcstackBANK0                  ; Rotacion de 16 bits , 3 rotaciones , es decir dividirlo por 8 , ya mis registros temporales tienen el valor ADCD/8
  5. BCF STATUS, 0x0
  6. RRF 0x21, F
  7. RRF __pcstackBANK0, F
  8. BCF STATUS, 0x0
  9. RRF 0x21, F
  10. RRF __pcstackBANK0, F
  11. BCF STATUS, 0x0
  12. RRF 0x21, F
  13. RRF __pcstackBANK0, F
  14. MOVF ADCDH, W               ; Toma la parte superior 0x03
  15. XORLW 0xFF                     ; Y restamos! como ? usando el complemento a 2 de 0x03 y sumando
  16. ADDLW 0x1
  17. ADDWF __pcstackBANK0, W         ;Por ser sin signo no se pregunta por el carry. Y el compilador decide no ponerlo, W = ADCD/8 - ADCD/256
  18. ADDLW 0x1F                       ; Suma 31, interesante ver que no sale de W el resultado y las operaciones, , W = 31 + ADCD/8 - ADCD/256 ,  al haberlo ubicado como destino una varaible de 8 bits imagino que no le importa si en la suma existe un carry o no
  19. MOVWF 0x22                     ; Guarda el resultado en otro registro temporal que usa C
  20. MOVF 0x22, W                   ; Y procede al guardado en la variable en cuestion
  21. MOVWF RESULTADO

Tal ves se podrian haber ahorrado hasta 7 intrucciones, pero es bueno saber como lo hace

Resultado: Si te la rebuscas funciona como queres :D
« Última modificación: 28 de Marzo de 2015, 01:11:15 por KILLERJC »