Ayuda para entender el código.

Soy nuevo en la programación en C. Necesito su ayuda para comprender algunas líneas particulares en un segmento de código para el control sensorizado de un motor de CC sin escobillas. Fue escrito para una MCU de 16 bits (dsPIC33FJ32MC710). Esta sección en particular es de la rutina de servicio de interrupción y contiene dos ISR. ¿ Puede explicarme con palabras sencillas las líneas marcadas con //???? ¿Qué se está haciendo allí y por qué? Cualquier otro comentario también es bienvenido.

int DesiredSpeed;
int ActualSpeed;
int SpeedError;
long SpeedIntegral = 0, SpeedIntegral_n_1 = 0, SpeedProportional = 0;
long DutyCycle = 0;
unsigned int Kps = 20000;                   // Proportional gain
unsigned int Kis = 2000;                    // Integral gain

void __attribute__((interrupt, no_auto_psv)) _T1Interrupt (void)
{
#ifdef CLOSEDLOOP
    ActualSpeed = SPEEDMULT/timer3avg;
    SpeedError = DesiredSpeed - ActualSpeed;
    SpeedProportional = (int)(((long)Kps*(long)SpeedError) >> 15);                 // ???
    SpeedIntegral = SpeedIntegral_n_1 + (int)(((long)Kis*(long)SpeedError) >> 15);   //???      
    SpeedIntegral_n_1 = SpeedIntegral;
    DutyCycle = SpeedIntegral + SpeedProportional;
    PDC1 = (int)(((long)(PTPER*2)*(long)DutyCycle) >> 15);  // ???  PWM duty cycle 
    1PDC2 = PDC1;
    PDC3 = PDC1;
#endif                              // in closed loop algorithm

    IFS0bits.T1IF = 0;
}

void __attribute__((interrupt, no_auto_psv)) _IC1Interrupt (void)
{
    int Hall_Index;

    IFS0bits.IC1IF = 0; // Clear interrupt flag
    HallValue = (unsigned int)((PORTB >> 1) & 0x0007);  // Read halls

    if (Flags.Direction)
    {
        OVDCON = StateTableFwd[HallValue];
        Hall_Index = HALL_INDEX_F;
        }
    else
    {
        OVDCON = StateTableRev[HallValue];
        Hall_Index = HALL_INDEX_R;
        }

// The code below is uses TMR3 to calculate the speed of the rotor
    if (HallValue == Hall_Index)  // has the same position been sensed?
        if (polecount++ == POLEPAIRS)  //has one mech rev elasped?         // ???
        {                               // yes then read timer 3
        timer3value = TMR3;
        TMR3 = 0;
        timer3avg = ((timer3avg + timer3value) >> 1);    // ???
        polecount = 1;
        } 
}
Busque 'C typecasting'.

Respuestas (3)

Para hacer matemáticas fraccionarias con enteros en C (y otros lenguajes que no admiten matemáticas fraccionarias), es necesario hacer trucos como este. Lo que realmente le gustaría hacer es multiplicar dos números de 16 bits y mantener los bits altos del producto.

El primer conjunto de expresiones convierte números de 16 bits en números de 32 bits con signo y luego calcula el producto de 32 bits. Luego lo desplazan 15 bits hacia la derecha, descartando los bits menos significativos. Es un poco como lo que sucede con las matemáticas de coma flotante cuando multiplicas dos mantisas (aunque las matemáticas de coma flotante se harían de manera más eficiente ya que no hay necesidad de calcular lo que se descartará).

En la segunda expresión (polecount++ == POLPAIRS) tiene un valor booleano basado en la comparación del valor de la variable polecount con POLPAIRS. El número de polos variable se incrementa posteriormente (se incrementa después de la comparación).

Entonces, ¿el recuento de polos se incrementará solo si la ifdeclaración es verdadera?
No, el número de polos se incrementa cada vez que se alcanza esa línea; es un efecto secundario del cálculo de la comparación. La parte posterior a {stuff in here} se ejecuta solo cuando la declaración 'if' es verdadera.
¿Cuál hubiera sido el inconveniente si las variables se multiplicaran en 16 bits en lugar de 32 bits? Porque eventualmente se vuelven a convertir a 16 bits, a través del encasillamiento.
El inconveniente es que el producto entre dos números de 16 bits no cabe en 16 bits, por lo que se desbordaría y solo obtendrá los bits inútiles menos significativos. De esa manera, la multiplicación está bien ya que 32 bits pueden contener el resultado de 16bx16b, pero luego el resultado se desplaza, descartando el menos sig. bits y manteniendo sólo 16 bits.
Entonces, básicamente, el segmento (long)Kismantiene los bits de la variable Kis originalmente definidos como un int sin firmar como LSB, y rellena con ceros como los 16 MSB superiores.
@AdeelSabir Más o menos. En realidad, esto no es bueno (portátil) C: el resultado de desplazar a la derecha un valor con signo es la implementación definida en C. (ref. 6.5.7 en ISO/IEC 9899:1999 (E)) Por lo general , es un desplazamiento aritmético, lo que significa que el el bit de signo se duplica con cada turno. El programador depende de si esos números pueden ser negativos.

Estas líneas representan la parte proporcional e integral del control del motor.

El SpeedError se multiplica directamente por la ganancia de Kps y la ganancia de Kis multiplica el SpeedError y lo suma a la salida integral de los últimos ciclos SpeedIntegral_n_1.

Debería haber algunas líneas de código más adelante que sumen los términos proporcional e integral para determinar la entrada del motor.

Este método de control intenta garantizar que el control de velocidad del motor sea estable y no oscile. El ajuste de las ganancias proporcionales e integrales afectará la velocidad y la estabilidad del control del motor.

Sí, agregué esas líneas faltantes.
Entendí mal tu pregunta, más C que Control.

El operador >> es un desplazamiento bit a bit hacia la derecha. Desplazar todos los bits hacia la izquierda o hacia la derecha dentro de un número binario multiplicará o dividirá el número por 2 (por cada cambio). También puede haber momentos en los que desee cambiar ciertos bits a ciertas posiciones, posiblemente para alinear bits en un puerto de E/S o los bits dentro de un registro de datos interno especial.

Además, en algunas de estas fórmulas, una o más de las variables se convierten a un determinado tipo de datos (usando un prefijo como (int) o (largo) justo antes del número).

Para el operador de desplazamiento de bits, consulte: http://www.cprogramming.com/tutorial/bitwise_operators.html

Entonces, ¿en la línea timer3avg = ((timer3avg + timer3value) >> 1);se calcula el promedio de acuerdo con la fórmula (x+y)/2 ?
Además, ¿por qué convertir an inta a longpara la multiplicación y luego volver a convertir al original int?
Sí, un desplazamiento a la derecha es una división por 2. Establecer una variable en un largo le brinda más bits utilizables en el mismo número (por ejemplo: no puede desplazar un número de 8 bits 15 veces). El mayor número de bits también puede permitir una mayor precisión si se manipula correctamente.