TODOPIC
Microcontroladores PIC => Lenguaje C para microcontroladores PIC => Mensaje iniciado por: Picuino en 23 de Junio de 2012, 06:18:14
-
He buscado en la documentación de microchip para el compilador C18 y en la web y no encuentro la manera de multiplicar dos bytes para conseguir un entero.
Si multiplico dos unsigned char me da como resultado otro unsigned char dentro de una variable entera (no es correcto)
Si hago un cast al primer unsigned char, la multiplicación se convierte en 16bit x 16bit y tarda demasiado.
¿Cómo puede funcionar bien esto?:
unsigned char a, b;
unsigned char mul;
a = 130;
b = 72;
mul = a * b;
Este último código me sale mul = 0x0090 y debería ser mul = 0x2490
Saludos.
-
Solucionado de una forma un poco complicada:
unsigned char a, b;
union {
unsigned int word;
struct {
unsigned char lob;
unsigned char hib;
};
} mul;
a = 130;
b = 72;
mul.lob = a * b;
mul.hib = PRODH;
No hay como publicar una duda para encontrar la solución. Dejo de todas formas mi monólogo por si a alguien más le sirve.
Saludos.
-
La linea PIC18 no tiene multiplicador de 8x8 en hardware ??
-
Y digo yo si esto no seria mejor ?
unsigned char a, b;
unsigned int mul;
a = 130;
b = 72;
mul = (unsigned int)a * b;
Por que por mucha multiplicación de dos bytes que hagas, si el destino también es de un byte, es imposible que te meta 16 bits en 8 bits.
-
es tal como te dice zardos, hay que tener bastante en cuenta ponerle el (unsigned int) delante para decirle que usaras 16bits
-
es tal como te dice zardos, hay que tener bastante en cuenta ponerle el (unsigned int) delante para decirle que usaras 16bits
Si hago un cast al primer unsigned char, la multiplicación se convierte en 16bit x 16bit y tarda demasiado.
:roll:
En C me parece que no queda otra que meter asm para hacerlo por hardware y leer los registros o como lo has realizado
Saludos!
-
asi me sale a mi:
56: lol=(unsigned int)(unsigned char)a*(unsigned char)0x10;
0D66 0100 MOVLB 0x0
0D68 5100 MOVF dat, W, BANKED
0D6A 0D10 MULLW 0x10
0D6C CFF3 MOVFF PRODL, lol
0D6E F0C2 NOP
0D70 CFF4 MOVFF PRODH, 0xC3
0D72 F0C3 NOP
-
Buenísimo! Hace falta uso extensivo del cast :mrgreen:
-
si, no veas si a veces le he dado vueltas a las conversiones, a veces en una linea uso mas casts que variables :D todo por ahorrar algunos tcy. Solo que en dspic es mucho mejor, se acabo lo de trabajar con variables de 8bits, ni preocuparme por las TCY de una multiplicacion = 1TCY, solo me preocupan las divisiones
-
¿Compilas con C18, MerLiNz?
Yo tengo la versión: MPLAB C18 v3.40 (feature limited) y opciones estandar
Cuando intento multiplicar con cast a 16 bit me compila esto:
178: mul.word = (unsigned int)(unsigned char)v_in * (unsigned char)s;
0110 51E4 MOVF 0xe4, W, BANKED
0112 6E16 MOVWF 0x16, ACCESS
0114 6A17 CLRF 0x17, ACCESS
0116 51E6 MOVF 0xe6, W, BANKED
0118 6E14 MOVWF 0x14, ACCESS
011A 6A15 CLRF 0x15, ACCESS
011C C0E6 MOVFF 0xe6, 0x8
011E F008 NOP
0120 C015 MOVFF 0x15, 0x9
0122 F009 NOP
0124 C0E4 MOVFF 0xe4, 0xd
0126 F00D NOP
0128 C017 MOVFF 0x17, 0xe
012A F00E NOP
012C ECFE CALL 0x3fc, 0
012E F001 NOP
0130 C006 MOVFF 0x6, 0xef
0132 F0EF NOP
0134 C007 MOVFF 0x7, 0xf0
0136 F0F0 NOP
:shock: 21 ciclos de instrucción, sin contar con el CALL que no se dónde salta
Creo que hace un cast a 16 bits antes de multiplicar y deja los argumentos en la pila para luego saltar a una rutina de multiplicación de 16 bits.
Utilizando las dos líneas que comentaba antes me sale esto otro:
178: mul.lob = v_in * s;
0110 51E6 MOVF 0xe6, W, BANKED
0112 03E4 MULWF 0xe4, BANKED
0114 CFF3 MOVFF 0xff3, 0xef
0116 F0EF NOP
179: mul.hib = PRODH;
0118 CFF4 MOVFF 0xff4, 0xf0
011A F0F0 NOP
6 ciclos de instrucción.
En ambos casos las variables son estáticas para dar mayor rapidez:
static unsigned char s, v_in;
static long seno_sum, coseno_sum;
static union {
unsigned int word;
struct {
unsigned char lob;
unsigned char hib;
};
} mul;
Saludos.
-
si, con C18 full con la optimizacion al maximo, kizas es por eso, o porque las variables cambien, pero vamos que si te va bien tal y como lo haces pues ya esta, no le des mas vueltas.
-
asi me sale a mi:
56: lol=(unsigned int)(unsigned char)a*(unsigned char)0x10;
0D66 0100 MOVLB 0x0
0D68 5100 MOVF dat, W, BANKED
0D6A 0D10 MULLW 0x10
0D6C CFF3 MOVFF PRODL, lol
0D6E F0C2 NOP
0D70 CFF4 MOVFF PRODH, 0xC3
0D72 F0C3 NOP
A mí también me compila de esta forma poniendo
mul=(unsigned int)(unsigned char)a*(unsigned char)0x10;
mul=(unsigned int)a*0x10;Pero si en vez de poner el segundo parámetro como un literal lo pongo como una variable
mul=(unsigned int)(unsigned char)a*(unsigned char)b;
mul=(unsigned int)a*b;me compila como comenta Picuino, no haciendo uso del multiplicador hardware.
Nunca me había fijado en este detalle, pero me parece un fallo bastante tonto del compilador...
-
Estoy utilizando la multiplicación hardware para hacer un amplificador de tipo Lock-in.
Basicamente es un sintonizador muy preciso que sólo amplifica una frecuencia determinada y no amplifica las demás frecuencias (generalmente ruido)
Si alguien quiere aportar este es el hilo:
http://www.todopic.com.ar/foros/index.php?topic=38565.0
Saludos
-
Tambien hay que tener en cuenta esto que esta en el manual:
Integer Promotions
ISO mandates that all arithmetic be performed at int precision or greater. By default, MPLAB C18 will perform arithmetic at the size of the largest operand, even if both operands are smaller than an int. The ISO mandated behavior can be instated via the -Oi command-line option.
For example:
unsigned char a, b;
unsigned i;
a = b = 0x80;
i = a + b; /* ISO requires that i == 0x100, but in C18 i == 0 */
Note that this divergence also applies to constant literals. The chosen type for constant literals is the first one from the appropriate group that can represent the value of the constant without overflow.
For example:
#define A 0x10 /* A will be considered a char unless -Oi
specified */
#define B 0x10 /* B will be considered a char unless -Oi
specified */
#define C (A) * (B)
unsigned i;
i = C; /* ISO requires that i == 0x100, but in C18 i == 0 */
-
unsigned char a, b;
unsigned i;
a = b = 0x80;
i = a + b; /* ISO requires that i == 0x100, but in C18 i == 0 */
Si lo saben y lo mantienen, ellos sabrán porqué.
En el caso de la multiplicación, está claro que el asm devuelve 16bit cuando se realiza 8bit x 8bit.
Perder a propósito esa precisión es una tontería.
178: mul = a * b;
0110 51E6 MOVF 0xe6, W, BANKED
0112 03E4 MULWF 0xe4, BANKED
0114 CFF3 MOVFF 0xff3, 0xef
0116 F0EF NOP
Y termina con CLRF a la parte alta de la variable mul.
¿Porqué? si casi cuesta lo mismo cargar PRODH en la parte alta de mul.
Saludos.
-
Hola, aporto mi granito de arena. Todo lo que comento Zardoz es correcto. Lo que quieres hacer es un caracteristica llamada en el estandart C "Integral promotions" o promocion a enteros. El estandart dice que toda operacion que involucre a operandos menores a un entero estos deben ser promocionados o convertidos a entero y ahi realizar la operacion. Esto es barbaro en un programa corriendo en una PC en un micro de 8 bits es un suicidio porque no es lo mismo manejar un char sin signo ( tipico registro ) que un entero con signo que pude ocupar 16 o 32 bits dependiendo de la plataforma. Por eso este compilador y casi todos los compiladores de embebidos que conozco la promocion a enteros es deshabilitada por default. Para hacer lo que queres hacer ( char * char = int ) o habilitas la promocion a enteros o colcas el cast que indico Zardoz. La opcion del cast es la mas eficiente ya que tratas el caso en particular y no en general. Este es unos de los principales errores de programacion cuando escribis codigo multiplataforma, yo nunca confio en esta caracteristica y coloco siempre los cast cuando es necesario.
Con respecto a utilizar el multiplicador x hardware para mi es un tema del compilador, muchos compiladores permiten especificar la sub-familia con la que se esta trabajando, de esta manera se habilitan ciertas caracteristicas especificas de la familia en si, ejemplo de esto es un compilador para PC podias especificar si generabas codigo para un 386, 486, 586, etc.
Saludos !