MSP430 Problema con múltiples temporizadores de interrupción

1

Actualmente estoy investigando el uso de PWM combinada de modulación de densidad de pulso (PDM) (en resumen, PDM-PWM) como sustituto del PWM convencional en la aplicación del seguimiento del punto de máxima potencia (MPPT) para paneles solares. Para lograr esto, estoy usando el microcontrolador MSP430F5529 donde el PDM se implementa en un software que usa un temporizador de interrupción y la salida se envía a un pin de E / S digital para que se pueda observar. Una parte de mi código (con la mayoría de los componentes no críticos eliminados, también el código del algoritmo MPPT se reemplaza con un simple ciclo de trabajo constante de 0.5 para propósitos de prueba) se encuentra al final de esta publicación. Su composición se puede resumir de la siguiente manera:

  1. Lo que está en Vcore es configurar el SMCLK del MSP430 para que se ejecute a 25MHz en lugar del valor predeterminado de 1 o 2 MHz.
  2. El temporizador de interrupción 1A se utiliza para la implementación de PDM y se ejecuta a 200 kHz.
  3. El temporizador de interrupción 2A se usa para el algoritmo MPPT en ejecuciones a 50 Hz.
  4. el temporizador de interrupción B se usa para el bucle de filtro de paso bajo (LPF) que se ejecuta a 500 Hz. Es necesario para filtrar el voltaje y la corriente de ADC ruidosos.

Ahora viene el problema principal. Cuando ejecuto el código, parece que los temporizadores se están interrumpiendo entre sí y bloqueando la operación de cada uno. En particular (y el que más afecta a mi aplicación), el Temporizador B está interrumpiendo el Temporizador 1A de alta frecuencia, que está claro cuando se observa la salida digital del PDM en el osciloscopio, como se muestra en la siguiente imagen. Esos "huecos" largos entre los pulsos cortos se producen cada 2 ms, lo que coincide con los 500 Hz del LPF y, obviamente, no quiero tener estos "huecos". Tampoco aparecen cuando quito el LPF de mi código, aunque desafortunadamente, necesito tener el LPF para mi aplicación.

¿Qué puedo hacer para evitar que esto suceda? No puedo permitirme tener este paro temporal del PDM ya que afecta enormemente la dinámica de mi sistema MPPT.

El Código:

#include <msp430f5529.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define Num_Channels 6
#define Num_Samples 3

#define LED BIT5

volatile float A_Results[Num_Channels][Num_Samples];
// For Low Pass Filter
volatile float A_ResultsR[Num_Channels][Num_Samples];

// For Average
// volatile unsigned long A_Results_T[Num_Channels] ;
int i_ADC = 0;



// Define Electrical Quantity Variables
#define NumInputs 2
#define NumRecords 3
// Output Side
float I_Out;
float V_Out;
// Input Side
float I[NumInputs][NumRecords];
float Vi[NumInputs][NumRecords];
const float D_Start[NumInputs] = {1, 1};
float D[NumInputs] = { 0.01, 0.01 };
// Calibration Formula
float I_V_REF[NumInputs + 1] ; // +1 to include the output measurements.

// Other Constants
const float B2V = (3.3 / 4095);

// Initialize PDM Related Variables
int X1[2] ;
int X2[2] ;
int E1 ;
int E2[2] ;
int U[2] ;
int Scaler = 30000 ;
int D_S ;

int PWM_MaxCounter = 500 ; // Set PWM Frequency Here.

// Load the Functions as defined at the bottom of this code.
void SetVcoreUp(unsigned int level); // SetVcoreUp Function
void ObtainAnalogData();
// sign function, very straight forward.
int sign(float value) {
    return (value > 0) - (value < 0);
}

int main(void) {
WDTCTL = WDTPW + WDTHOLD;                   // Stop watchdog timer

// Code to Set up the 25 MHz Clock for use
P1DIR |= BIT0;                            // ACLK set out to pins
P1SEL |= BIT0;
P2DIR |= BIT2;                            // SMCLK set out to pins
P2SEL |= BIT2;
P7DIR |= BIT7;                            // MCLK set out to pins
P7SEL |= BIT7;

// Increase Vcore setting to level3 to support fsystem=25MHz
// NOTE: Change core voltage one level at a time..
SetVcoreUp(0x01);
SetVcoreUp(0x02);
SetVcoreUp(0x03);

UCSCTL3 = SELREF_2;                       // Set DCO FLL reference = REFO
UCSCTL4 |= SELA_2;                        // Set ACLK = REFO

__bis_SR_register(SCG0);                  // Disable the FLL control loop
UCSCTL0 = 0x0000;                         // Set lowest possible DCOx, MODx
UCSCTL1 = DCORSEL_7;                     // Select DCO range 50MHz operation
UCSCTL2 = FLLD_0 + 762;                   // Set DCO Multiplier for 25MHz
                                          // (N + 1) * FLLRef = Fdco
                                          // (762 + 1) * 32768 = 25MHz
                                          // Set FLL Div = fDCOCLK/2
__bic_SR_register(SCG0);                  // Enable the FLL control loop

// Worst-case settling time for the DCO when the DCO range bits have been
// changed is n x 32 x 32 x f_MCLK / f_FLL_reference. See UCS chapter in 5xx
// UG for optimization.
// 32 x 32 x 25 MHz / 32,768 Hz ~ 780k MCLK cycles for DCO to settle
__delay_cycles(782000);

// Loop until XT1,XT2 & DCO stabilizes - In this case only DCO has to stabilize
do {
    UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG);
    // Clear XT2,XT1,DCO fault flags
    SFRIFG1 &= ~OFIFG;                      // Clear fault flags
} while (SFRIFG1 & OFIFG);                   // Test oscillator fault flag

// Test LED Setup
P2DIR |= LED;
P2OUT &= ~LED; // Turn it Off First
// Timer Setup
// Timer 1A0 Specifications. This one is for the MPPT sampling loop
TA2CTL = TASSEL_1 + MC_1 + TACLR + ID_0; // Set the timer A to ACLK, Continuous
TA2CCR0 = 640; // Makes it 50 Hz :)
TA2CCTL0 |= CCIE;
// Timer 2A0 Specifications. This one is for the low pass filtering loop.
  TB0CCTL0 = CCIE;                          // CCR0 interrupt enabled
  TB0CCR0 = 64; // Make is 500 Hz
  TB0CTL = TBSSEL_1 + MC_1 + TBCLR;         // SMCLK, contmode, clear TAR
// ADC Setup Code
P6SEL |= BIT0 + BIT1 + BIT2 + BIT3 + BIT4 + BIT5; // Enable A/D channel inputs, P6.0 to P6.5
// ADC12_A ref control registers
ADC12CTL0 = ADC12ON + ADC12MSC + ADC12SHT0_10;//+ ADC12REFON + ADC12REF2_5V ; // Turn on ADC12, extend sampling time
                                              // to avoid overflow of results
ADC12CTL1 = ADC12SHP + ADC12CONSEQ_3 + ADC12SSEL_3 + ADC12DIV_0 ; // Use sampling timer, repeated sequence

ADC12MCTL0 = ADC12INCH_0 + ADC12SREF_0;           // ref+=AVcc, channel = A2
ADC12MCTL1 = ADC12INCH_1 + ADC12SREF_0;           // ref+=AVcc, channel = A3
ADC12MCTL2 = ADC12INCH_2 + ADC12SREF_0;           // ref+=AVcc, channel = A4
ADC12MCTL3 = ADC12INCH_3 + ADC12SREF_0;
ADC12MCTL4 = ADC12INCH_4 + ADC12SREF_0;
ADC12MCTL5 = ADC12INCH_5 + ADC12SREF_0 + ADC12EOS; // ref+=AVcc, channel = A12, end seq.
ADC12IE = 0x08;                           // Enable ADC12IFG.3
ADC12CTL0 |= ADC12ENC;                    // Enable conversions
ADC12CTL0 |= ADC12SC;                     // Start convn - software trigger

// PWM Setup Code
// Use P1.2 and P1.3 for PWM Output
P1DIR |= BIT3 + BIT4;
P1SEL |= BIT3 + BIT4;
TA0CCR0 = PWM_MaxCounter ;             // PWM Period, TACCR0 = 500 makes 50KHz =)
TA0CCTL2 |= OUTMOD_7;      // CCR2 reset/set
TA0CCR2 = 0 ;               // CCR1 PWM duty cycle
TA0CCTL3 |= OUTMOD_7;      // CCR3 reset/set
TA0CCR3 = D[1] * TA0CCR0 ;
TA0CTL = TASSEL_2 + MC_1 + TACLR;   // SMCLK, up mode
// PDM Setup Code, Use T1A Timer
P1DIR |= BIT5 ;
P2OUT &= BIT5 ;
TA1CTL = TASSEL_2 + MC_1 + TACLR + ID_0 ;
TA1CCR0 = 125 ; // Makes it 200kHz :)
TA1CCTL0 |= CCIE;

// UART Setup
P4SEL |= BIT4 + BIT5;                        // P4.4,5 = UART1 TXD/RXD
// configure USCI_A1 UART
UCA1CTL1 = UCSSEL_2;                      // MCLK
UCA1BR0 = 217;                         // 25MHz / 217 ~= 115200 Baud Rate =)
UCA1BR1 = 0x0;
UCA1MCTL = UCBRS_3 + UCBRF_0;               // Modulation UCBRSx = 3
UCA1CTL1 &= ~UCSWRST;                   // **Initialize USCI state machine**

__bis_SR_register(LPM4_bits + GIE);       // Enter LPM0, Enable interrupts

// Other Setup Code
// Set Current Sensor's Reference Voltage.
int k;
for (k = 0; k < NumInputs + 1; k++) {
    I_V_REF[k] = 1 ;
}
//CalibrateSensors() ;
// Program Loop
while (1) {
    // Transmission and Receiving from UART is conducted here. Not shown since it is irrelevant.
  }
 }

// Timer 1 A0 interrupt service routine
#pragma vector=TIMER2_A0_VECTOR
__interrupt void TIMER2_A0_ISR(void) {
// B2V, I_0Ref
// Calculate Current and Voltage for each input channel.
I[0][0] = (float) (A_Results[0][0] * B2V - I_V_REF[0]) * 0.2936 ;
Vi[0][0] = (float) A_Results[1][0] * B2V * 3.0263 ; // Voltage Range divided by resolution
I[1][0] = (float) (A_Results[2][0] * B2V - I_V_REF[1])  * 0.2936 ;
Vi[1][0] = (float) A_Results[3][0] * B2V * 3.0344 ;

// Obtain Current and Voltage on the Output Side
I_Out = (float) (A_Results[4][0] * B2V - I_V_REF[2]) * 0.285 ;
V_Out = (float) A_Results[5][0] * B2V * 3.0344 ;
// The MPPT Algorithm Fits in here.
// It is not shown for the sake of brevity and also since it is not important.
D[0] = 0.5 ;
// Update the Registers to generate the new PWM signal
D_S = D[0] * Scaler ;
TA0CCR3 = D[1] * TA0CCR0;
P2OUT ^= LED; // Blink LED to test Frequency on Oscilloscope, it shows 50Hz which is what I wanted, btw.
}


// Timer 0 B0 Interrupt Service Routine
// Timer0 interrupt service routine
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=TIMER0_B0_VECTOR
__interrupt void TIMER0_B0_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(TIMER0_B0_VECTOR))) TIMER0_B0_ISR (void)
#else
#error Compiler not supported!
#endif
{
   FilterAnalogData(); // Obtain Filtered Analog Data.
}

void FilterAnalogData() {
int x;
int y;
// Implement Low Pass Filter
// Update Historic Values of the Analog Data
for (x = 0; x < Num_Channels; x++) {
    for (y = 1; y < Num_Samples; y++) {
        A_ResultsR[x][y] = A_ResultsR[x][y - 1];
        A_Results[x][y] = A_Results[x][y - 1];
    }
}
// Apply the LPR Discretized Z-Domain Formula
for (x = 0; x < Num_Channels; x++) {
        A_Results[x][0] = 0.5 * A_Results[x][1] + 0.25 * (A_ResultsR[x][0] + A_ResultsR[x][1]); // 1st Order Designed
}
}

    // Timer 1A0 Interrupt Service Routine
// Timer 1 interrupt service routine
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=TIMER1_A0_VECTOR
__interrupt void TIMER1_A0_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(TIMER1_A0_VECTOR))) TIMER1_A0_ISR (void)
#else
#error Compiler not supported!
#endif
{
 // Integration Loop
  E1 = D_S - U[1] ;
  X1[0] = X1[1] + (E1 >> 3) ; // >> Stands for Bit Shift to Right (ie. Make Number Smaller)
  E2[0] = X1[0] - U[1] ;
  X2[0] = X2[1] + (E2[1] >> 1) ;
  // Quantizer
  if (X2[0] >= 0) {
      U[0] = Scaler ;
      P1OUT |= BIT5 ; // Turn on P1.5
      TA0CCR2 = PWM_MaxCounter ;
  } else {
      U[0] = 0 ;
      P1OUT &= ~BIT5; // Turn off P1.5
      TA0CCR2 = 0 ;
  }
  // Update History Variables
  X1[1] = X1[0] ;
  X2[1] = X2[0] ;
  E2[1] = E2[0] ;
  U[1] = U[0] ;
}


// ADC Interrupt Service Routine
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=ADC12_VECTOR
__interrupt void ADC12ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(ADC12_VECTOR))) ADC12ISR (void)
#else
#error Compiler not supported!
#endif
{
switch (__even_in_range(ADC12IV, 34)) {
case 0:
    break;                           // Vector  0:  No interrupt
case 2:
    break;                           // Vector  2:  ADC overflow
case 4:
    break;                           // Vector  4:  ADC timing overflow
case 6:
    break;                           // Vector  6:  ADC12IFG0
case 8:
    break;                           // Vector  8:  ADC12IFG1
case 10:
    break;                           // Vector 10:  ADC12IFG2
case 12:                                  // Vector 12:  ADC12IFG3
    // Record A_ResultsX from the ADC Memory
    A_ResultsR[0][0] = ADC12MEM0; // I1
    A_ResultsR[1][0] = ADC12MEM1; // V1
    A_ResultsR[2][0] = ADC12MEM2; //I2
    A_ResultsR[3][0] = ADC12MEM3; // V2
    A_ResultsR[4][0] = ADC12MEM4; //I_OUT
    A_ResultsR[5][0] = ADC12MEM5; // V_Out
    __bic_SR_register_on_exit(LPM0_bits);
    break;
case 14:
    break;                           // Vector 14:  ADC12IFG4
case 16:
    break;                           // Vector 16:  ADC12IFG5
case 18:
    break;                           // Vector 18:  ADC12IFG6
case 20:
    break;                           // Vector 20:  ADC12IFG7
case 22:
    break;                           // Vector 22:  ADC12IFG8
case 24:
    break;                           // Vector 24:  ADC12IFG9
case 26:
    break;                           // Vector 26:  ADC12IFG10
case 28:
    break;                           // Vector 28:  ADC12IFG11
case 30:
    break;                           // Vector 30:  ADC12IFG12
case 32:
    break;                           // Vector 32:  ADC12IFG13
case 34:
    break;                           // Vector 34:  ADC12IFG14
default:
    break;
}
}

// VCore Function

void SetVcoreUp(unsigned int level) {
// Open PMM registers for write
PMMCTL0_H = PMMPW_H;
// Set SVS/SVM high side new level
SVSMHCTL = SVSHE + SVSHRVL0 * level + SVMHE + SVSMHRRL0 * level;
// Set SVM low side to new level
SVSMLCTL = SVSLE + SVMLE + SVSMLRRL0 * level;
// Wait till SVM is settled
while ((PMMIFG & SVSMLDLYIFG) == 0)
    ;
// Clear already set flags
PMMIFG &= ~(SVMLVLRIFG + SVMLIFG);
// Set VCore to new level
PMMCTL0_L = PMMCOREV0 * level;
// Wait till new level reached
if ((PMMIFG & SVMLIFG))
    while ((PMMIFG & SVMLVLRIFG) == 0)
        ;
// Set SVS/SVM low side to new level
SVSMLCTL = SVSLE + SVSLRVL0 * level + SVMLE + SVSMLRRL0 * level;
// Lock PMM registers for write access
PMMCTL0_H = 0x00;
}
    
pregunta Trobby

1 respuesta

1

Los comentaristas son correctos, debes hacer algunas cosas:

  1. Implemente matemática de punto fijo . Hay varias formas de hacerlo, pero realizar operaciones de flotación en un MSP430 dentro de una interrupción es uno de los pecados más altos de la codificación del microcontrolador.

  2. Use algún tipo de administrador de tareas para su bajo frecuencia. De esta manera, el temporizador de baja frecuencia es interrumpible.

  3. Use algún tipo de baja memoria RAM y técnica de promedio rápido para su baja filtro de frecuencia.

Coloqué tres enlaces allí b / c Ya resolví la mayoría de esto, la mayoría de los cuales tengo ejemplos en github (y en el MSP430 en algunos casos).

No dude en contactarme por otros medios si tiene alguna pregunta. El administrador de tareas en particular te ayudará, ya que significa que solo necesitarás una interrupción. Es muy confiable y bastante fácil de usar, pero tiene que recortar la grasa de su código (eliminar las operaciones de flotación) o las tareas aún pueden bloquearse entre sí.

    
respondido por el slightlynybbled

Lea otras preguntas en las etiquetas