Autor Tema: _Q16mac y números negativos  (Leído 4234 veces)

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

Desconectado reiniertl

  • Moderadores
  • PIC24H
  • *****
  • Mensajes: 1187
_Q16mac y números negativos
« en: 28 de Noviembre de 2011, 00:11:43 »
Al utilizar la función  _Q16mac(x,y,prod) de la librería de Microchip, el resultado siempre es positivo, la función trabaja bien porque el manual especifica que el resultado será positivo. Es decir que es posible multiplicar números siempre que ambos tengan el mismo signo, el problema viene cuando los operandos son de signos contrarios.

Alguien que haya lidiado con este problema de forma exitosa?

Tengo que multimplicar número en el formato Q16, pero por las operaciones involucradas habrá números con signos contrarios y a priori no es posible saberlo, así que neceito que el resultado sea correcto.

Saludos
Reinier

Desconectado fabianjsm

  • PIC18
  • ****
  • Mensajes: 255
    • fabianjsm is on twitter
Re: _Q16mac y números negativos
« Respuesta #1 en: 28 de Noviembre de 2011, 01:55:41 »
Tal vez algo asi:
Código: C
  1. int sgn=0;
  2.     if(x<0)
  3.         x *= -1, sgn=1;
  4.     if(y<0)
  5.         y *= -1, sgn = !sgn;
  6.     _Q16mac(x, y, prod);
  7.     return sgn? _Q16neg(prod): prod;
@fabianjsm is on twitter

Desconectado reiniertl

  • Moderadores
  • PIC24H
  • *****
  • Mensajes: 1187
Re: _Q16mac y números negativos
« Respuesta #2 en: 28 de Noviembre de 2011, 02:12:30 »
Supongo que algo así funcionará, pero seguramente destuirá le ventaja de utilizar la aritmética fraccional.

Por ejemplo al utilizar flotantes el algoritmo que estoy implementando consume como promedio unos 200uS, con fraccionales Q16 unos 68uS e imagino que con Q15 será menos.

El caso es que necesito una solución menos costosa que tantos if, etc, etc. Luego utilizaré Q15 para hacer el mismo algoritmo porque es para una clase que debo dar esta semana a mis estudiantes.

La idea es demostrar como se puede ir de una muy mala implementación en C con float que gasta unos 320uS hacia una con Q15 que consumirá sin lugar a dudas menos de 30uS a 40 MIPS y obtener el mismo desempeño del filtro digital IIR, SOLAMENTE POR CAMBIAR LA FORMA DE HACER ARITMETICA.

Prometo subir el código de los ejemplos al foro, me está quedando tan bueno que la tentación ya me provoca subir los ejemplos, pero esperaré a que estén acabados.

Saludos
Reinier

PD: Si alguien puede bajarme el código fuente de_Q16mac de la página de Microchip y enviármelo se lo agradezco. Cuba está bloqueada para acceder a Microchip así que no hay nada de allí a lo que pueda acceder directamente, una putada pero es así.

Desconectado fabianjsm

  • PIC18
  • ****
  • Mensajes: 255
    • fabianjsm is on twitter
Re: _Q16mac y números negativos
« Respuesta #3 en: 28 de Noviembre de 2011, 09:22:59 »
Hola reiniertl. Comprendo lo costoso en tiempo y falta de elegancia de los IF, pero lastimosamente la función _Q16mac no opera con negativos, hay que negarlos con _Q16neg antes de emplearla. Desconozco la existencia de algún parche.

¿Que ocurre si multiplicas los factores con _Q16mac sin considerar el signo y luego solo inviertes el resultado si es necesario? Vi que algunos lo hacen, no se si funciona realmente, yo utilizo la forma anterior, pero seria interesante contar con una optimización para negativos como la que buscas. Si funciona lo anterior, podrías buscar la manera de copiarte solo el byte con el bit significativo de la parte entera de los operandos Q16, si llamamos msb0 y msb1, luego haces

_Q16mac(x, y, prod);
if((msb0 ^ msb1) & 0x80)
    return _Q16neg(prod);
else
    return prod;

Seguimos con el IF, pero las operaciones son ahora a nivel de bit.

No entiendo algo. Supongamos que tu función opera con char y no con Q16, y recibes el binario 1000-0000 ¿cómo sabrás si trata de un 128 sin signo, o de un -1 con signo? ¿Tus parámetros se dan dentro de un rango limitado y controlado?

Espero poder ayudarte.
@fabianjsm is on twitter

Desconectado fram_22

  • PIC18
  • ****
  • Mensajes: 316
Re: _Q16mac y números negativos
« Respuesta #4 en: 28 de Noviembre de 2011, 13:03:01 »
¿Que ocurre si multiplicas los factores con _Q16mac sin considerar el signo y luego solo inviertes el resultado si es necesario?
\

Hola yo lo he intentado y no funciona correctamente. Ya que el resultado tiene el habito de saturarse y dar valores incorrectos al invertor. Y en lo personal recomiendo invertir antes de la multiplicacion.

saludos!

Desconectado reiniertl

  • Moderadores
  • PIC24H
  • *****
  • Mensajes: 1187
Re: _Q16mac y números negativos
« Respuesta #5 en: 28 de Noviembre de 2011, 23:12:29 »
Esta aplicación es la implementación de un filtro pasa bajos IIR de orden 10 con implementación de Butterworth mediante la transformación bilineal. La frecuencia de corte es en 0.36*Fs. Fs=2kHz.

La aplicación filtra la señal a la entrada y convierte el resultado mediante uno de los PWM del dsPIC, el ejemplo lo he simulado en PROTEUS y funciona muy bien, A la salida del PWM he conectado un filtro pasa bajos analógico de 4to orden con implementación elíptica. La señal de prueba es la suma de un seno con frecuencia inferior a la de la de corte del IIR con un tren de pulsos de 1kHz de amplitud .5V, es muy interesante de ver, cuando tenga todo pulido subiré el proyecto completo para gusto de los que le interesa el tema de filtrar.

Este código funciona bastante bien, en realidad se presenta un fenómeno de saturación como consecuencia de trabajar siempre con valores positivos. Se corrige desplazando el resultado un bit a la derecha. Pero la salida presenta un corrimiento del cero y una reducción de la amplitud. Es algo en lo que estoy trabajando para ver sómo se resuelve.

El resultado final es un incremento de desempeño del 300% respecto de utilizar float. Además la ISR siempre consume el mismo tiempo lo que indica que este código es determinístico.


Código: C
  1. /*
  2.     This is free software; you can redistribute it and/or modify it under
  3.     the terms of the GNU General Public License (version 2) as published by the
  4.     Free Software Foundation.
  5.     This software is distributed in the hope that it will be useful, but WITHOUT
  6.     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  7.     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  8.     more details. You should have received a copy of the GNU General Public
  9.     License
  10.  
  11.     1 tab == 4 spaces!
  12.  
  13. */
  14.  
  15. /* Standard includes. */
  16. #define USE_AND_OR /* To enable AND_OR mask setting */
  17. #include <adc.h>
  18. #include <libq.h>
  19. #include <outcompare.h>
  20. #include <timer.h>
  21. #include <dsp.h>
  22.  
  23.  
  24.  
  25. /************************ Enable AND_OR mask setting *************************/
  26. #define USE_AND_OR
  27.  
  28. /******************** Select the filter to be implemented.  *******************
  29. **********  NOTE:  Only one filter must be active at the same time  **********/
  30. #define FILTER 4
  31.  
  32. /*****************************************************************************/
  33.  
  34. /******************************************************************************  
  35. Setting the configuration bits for this project. The next lines are the
  36. fuses definitions Reefer to Section: "Macros for setting device configuration
  37. registers" in p33FJ16MC304.h dsPIC33FJ16MC304 device datasheet for more details
  38. ******************************************************************************/
  39. //Boot Segment Program Memory
  40. _FBS( BSS_NO_BOOT_CODE &  BWRP_WRPROTECT_OFF )
  41.  
  42. //Code protection
  43. _FGS( GSS_OFF & GCP_OFF & GWRP_OFF )
  44.  
  45. //Oscillator selection
  46. _FOSCSEL( FNOSC_PRIPLL & IESO_OFF  )
  47.  
  48. //Oscillator configuration
  49. _FOSC( FCKSM_CSDCMD & IOL1WAY_ON & OSCIOFNC_OFF & POSCMD_HS )
  50.  
  51. //Watch Dog configuration
  52. _FWDT( FWDTEN_OFF & WINDIS_OFF & WDTPRE_PR128 & WDTPOST_PS32768)
  53.  
  54. //PWM motor control module, I2c Alternate pins and Power on reset
  55. _FPOR( PWMPIN_ON & HPOL_OFF & LPOL_OFF  & ALTI2C_OFF & FPWRT_PWR128)
  56.  
  57. //Background Debugging and JTAG
  58. _FICD( BKBUG_OFF & COE_OFF & JTAGEN_OFF & ICS_PGD1 )
  59.  
  60.  
  61. /******************************************************************************
  62. ***********************  Global declaration for FILTERS  **********************
  63. ******************************************************************************/
  64. /*---------------------------------- FILTER 3 -------------------------------*/
  65.  
  66. #if (FILTER==4)
  67. /*
  68. const float a_n[] = { 2.79000,  4.63740,  5.019600,  3.9199000,  2.23010000,
  69.                                          0.93236, 0.27913,  0.057080,  0.0071498,  0.00041555};
  70. const float b_n[] = {0.00021598, 0.0021598, 0.00971930, 0.0259180, 0.04535700, 0.054428,
  71.                                          0.04535700, 0.0259180, 0.00971930, 0.0021598, 0.00021598};
  72. #define ELEMENTS_IN_a_n sizeof(a_n)/sizeof(float)
  73. #define ELEMENTS_IN_b_n sizeof(b_n)/sizeof(float)
  74.  
  75. _Q16 f_a_n[ELEMENTS_IN_a_n];
  76. _Q16 f_b_n[ELEMENTS_IN_b_n];
  77.  
  78. static _Q16  x[11];
  79. static _Q16  y[11];
  80. #endif  
  81. /*---------------------------------------------------------------------------*/
  82.  
  83.  
  84. /******************************************************************************
  85.  ************************ Setting up the hardware.  ***************************
  86.  *****************************************************************************/
  87. static void prvSetupHardware( void );
  88.  
  89.  
  90. /******************************************************************************
  91.  -Main function:
  92.         *Initialize code for your application
  93.         *Execute an endless loop
  94. ******************************************************************************/
  95.  
  96. int main( void )
  97. {
  98.        
  99.         /* Configure any hardware required for this project. */
  100.         prvSetupHardware();
  101.  
  102.        
  103.         for(i=0; i<ELEMENTS_IN_a_n;i++)
  104.          f_a_n[i]=_Q16ftoi(a_n[i]);
  105.        
  106.         for(i=0; i<ELEMENTS_IN_b_n;i++)
  107.          f_b_n[i]=_Q16ftoi(b_n[i]);
  108.  
  109.  
  110.         while(1)
  111.         {
  112.         }      
  113.  
  114.    
  115.         /* Will only reach here if there is insufficient heap available during.
  116.            setup of hardware modules */
  117.         return 0;
  118. }
  119. /*---------------------------------------------------------------------------*/
  120.  
  121. /*******************  prvSetup Hardware  *************************************/
  122. static void prvSetupHardware( void )
  123. {
  124. /*--------------------  Remappable PIN0 tied to OC1  ------------------------*/
  125.         RPOR0 = 0x12;
  126. /*----------------------- Config PWM1 an its TIMER --------------------------*/
  127. ConfigIntOC1(OC_INT_OFF );  //PWM without interrupt
  128. ConfigIntTimer2(T2_INT_OFF);  //TIMER2 without interrupt
  129. //TIMER2 is the timer source for PWM1
  130. OpenTimer2(T2_ON & T2_SOURCE_INT & T2_GATE_OFF & T2_32BIT_MODE_OFF & T2_PS_1_1,1025);
  131. //PWM1 without fault check
  132. OpenOC1(OC_IDLE_CON & OC_TIMER2_SRC & OC_PWM_FAULT_PIN_DISABLE, 1, 0);
  133.  
  134. /*----------------- Configuring the ADC channel 0 with ADC No.1 -------------*/
  135. CloseADC1();
  136. OpenADC1(ADC_MODULE_OFF & ADC_IDLE_STOP & ADC_AD12B_10BIT & ADC_FORMAT_INTG &
  137.                  ADC_CLK_TMR,
  138.                  ADC_VREF_AVDD_AVSS & ADC_SCAN_OFF & ADC_SELECT_CHAN_0 & ADC_SAMPLES_PER_INT_1,
  139.                  ADC_SAMPLE_TIME_1 & ADC_CONV_CLK_1Tcy,
  140.                  ADC_DMA_BUF_LOC_1,
  141.                  ENABLE_ALL_DIG_16_31,
  142.                  ENABLE_AN4_ANA,
  143.                  SCAN_NONE_0_15,
  144.                  SCAN_NONE_16_31);
  145.        
  146. SetChanADC1( ADC_CH123_POS_SAMPLEA_0_1_2, ADC_CH0_POS_SAMPLEA_AN4 & ADC_CH0_NEG_SAMPLEA_VREFN);
  147.                                
  148. ConfigIntADC1(ADC_INT_ENABLE & ADC_INT_PRI_1); 
  149.  
  150. ConfigIntTimer3(T3_INT_OFF);
  151. T2CONbits.T32 = 0;
  152. OpenTimer3(T3_ON & T3_SOURCE_INT & T3_GATE_OFF & T3_PS_1_1,12500);
  153. }
  154. /*---------------------------------------------------------------------------*/
  155.  
  156. /*******************************  ADC INTERRUPT ******************************/
  157. void __attribute__((__interrupt__, auto_psv)) _ADC1Interrupt( void )
  158. {
  159. static int DutyCycle=0;
  160.  
  161. /******************************************************************************
  162. **********************  Local Filter data definitions  ************************
  163. ******************************************************************************/
  164.  
  165. /******************************************************************************
  166. ********************************  Filter implementations  *********************
  167. ******************************************************************************/
  168. /*----------------------------------  FILTER 4  -----------------------------*/
  169.  
  170.         y[10] = y[9];
  171.         y[9] = y[8];
  172.         y[8] = y[7];
  173.         y[7] = y[6];
  174.         y[6] = y[5];
  175.         y[5] = y[4];
  176.         y[4] = y[3];
  177.         y[3] = y[2];
  178.         y[2] = y[1];
  179.         y[1] = y[0];
  180.  
  181.         x[10] = x[9];
  182.         x[9] = x[8];
  183.         x[8] = x[7];
  184.         x[7] = x[6];
  185.         x[6] = x[5];
  186.         x[5] = x[4];
  187.         x[4] = x[3];
  188.         x[3] = x[2];
  189.         x[2] = x[1];
  190.         x[1] = x[0];
  191.         x[0] = ReadADC1(0);
  192.  
  193.        
  194.     y[0] = _Q16mac(f_b_n[0], x[0], y[0]);  
  195.     y[0] = _Q16mac(f_b_n[1], x[1], y[0]);    
  196.     y[0] = _Q16mac(f_b_n[2], x[2], y[0]);    
  197.     y[0] = _Q16mac(f_b_n[3], x[3], y[0]);  
  198.     y[0] = _Q16mac(f_b_n[4], x[4], y[0]);
  199.     y[0] = _Q16mac(f_b_n[5], x[5], y[0]);    
  200.     y[0] = _Q16mac(f_b_n[6], x[6], y[0]);    
  201.     y[0] = _Q16mac(f_b_n[7], x[7], y[0]);    
  202.     y[0] = _Q16mac(f_b_n[8], x[8], y[0]);    
  203.     y[0] = _Q16mac(f_b_n[9], x[9], y[0]);    
  204.     y[0] = _Q16mac(f_b_n[10], x[10], y[0]);  
  205.     y[0] = _Q16mac(f_a_n[0], y[1],  y[0]);  
  206.     y[0] = _Q16mac(f_a_n[2], y[3],  y[0]);  
  207.     y[0] = _Q16mac(f_a_n[4], y[5],  y[0]);  
  208.     y[0] = _Q16mac(f_a_n[6], y[7],  y[0]);  
  209.     y[0] = _Q16mac(f_a_n[8], y[9],  y[0]);  
  210.     y[0] = y[0] - _Q16mac(f_a_n[1], y[2],  0);  
  211.     y[0] = y[0] - _Q16mac(f_a_n[3], y[4],  0);      
  212.     y[0] = y[0] - _Q16mac(f_a_n[5], y[6],  0);      
  213.     y[0] = y[0] - _Q16mac(f_a_n[7], y[8],  0);  
  214.     y[0] = y[0] - _Q16mac(f_a_n[9], y[10], 0);
  215.  
  216.    if(y[0]<0)
  217.       y[0] = _Q16neg(y[0]);
  218.  
  219.  
  220.     y[0]= _Q16shrNoSat(y[0], 1);
  221.      DutyCycle = y[0];
  222.  
  223. /*---------------------------------------------------------------------------*/
  224.    if (DutyCycle <= 0 ) DutyCycle = 1;
  225.    if (DutyCycle >= 1023) DutyCycle = 1023;
  226.    IFS0bits.AD1IF = 0;
  227.    SetDCOC1PWM(DutyCycle);
  228. }
  229. /*****************************************************************************/


En los próximos días voy a subir un ejemplo completo de varias implementaciones de este filtro.
« Última modificación: 28 de Noviembre de 2011, 23:16:16 por reiniertl »

Desconectado fabianjsm

  • PIC18
  • ****
  • Mensajes: 255
    • fabianjsm is on twitter
Re: _Q16mac y números negativos
« Respuesta #6 en: 29 de Noviembre de 2011, 03:10:18 »
Gracias por compartirlo reiniertl!
Te adjunto el código donde hace la negación después del producto, no es mío. Tener en cuenta el comentario de fram_22, si desborda falla.
@fabianjsm is on twitter

Desconectado seba85

  • PIC10
  • *
  • Mensajes: 28
Re: _Q16mac y números negativos
« Respuesta #7 en: 15 de Agosto de 2013, 19:18:18 »
Hola!
También estoy implementando una aplicación con dsPIC y los tiempos de procesamiento no me dejan mucho margen...
Por que no hay una función _Q15mac?
en caso de que quiera multiplicar en formato Q15...
« Última modificación: 15 de Agosto de 2013, 20:46:10 por seba85 »

Desconectado fram_22

  • PIC18
  • ****
  • Mensajes: 316
Re: _Q16mac y números negativos
« Respuesta #8 en: 16 de Agosto de 2013, 12:55:32 »
esa es una buena pregunta. solamente los de microchip saben porque, quizás si les preguntas usando un support ticken te aclaren un poco lo que pasa.

saludos!


 

anything