PWM 1HZ PIC18F14K50

1

Por lo tanto, estoy tratando de lograr una frecuencia PWM de 1HZ con un ciclo de trabajo de aproximadamente el 70%. El problema que encontré fue con la frecuencia PWM mínima que puede alcanzar el PIC18F14K50, que es de aproximadamente 1.892 HZ, usando PR2 = 255 (máx.), FOSC es igual a 31 kHz (cambiado de 16 MHz) y prescaler de TIMER2 = 16:

PWM FREQUENCY = (PR2 + 1) * 4 * TOSC * prescaler

Por lo tanto, mi pregunta reside en una forma de lograr ese 1HZ para la frecuencia PWM con un ciclo de trabajo del 70%.

Notas: No se pueden usar retrasos (función) y no se puede cambiar el hardware (PIC), por lo que la única opción es a través del Software.

Bellow es el código utilizado para alcanzar una frecuencia de 1.892HZ:

//Variables 
int DC; // Duty cycle
//
OSCCON=0; // 31kHZ

// Configure PWM Module 1.892 HZ
    OpenTimer2(T2_PS_1_16); // Prescaler 16 
    OpenPWM1(0xFF); //Configure PWM module and initialize 1 HZ ; PR2 =255
    SetDCPWM1(0); // set duty cyle  
    SetOutputPWM1(SINGLE_OUT, PWM_MODE_1); 

void LED(void) { 

    DC=70; // 70%

    SetDCPWM1((int) DC * 10.23); //set the duty cycle 70% in 10bits//
}

EDITAR: Tratando de seguir los comentarios de todos ustedes, probé esto con TIMER0 y ahora con FOSC = 16MHZ:

void highISR(void) {
    // Check if there was an overflow in TIMER0
    if (INTCONbits.TMR0IF == 1) { 
        LATCbits.LATC5=1; // LED ON 


        INTCONbits.TMR0IF = 0; 
        WriteTimer0(Ktimer); 
    }
}

#pragma code HighInterruptVector=0x0008

void HighInterruptVector(void) {
    _asm
            goto highISR
            _endasm
}
#pragma code
 // CONFIG TIMER0 
    OpenTimer0(TIMER_INT_ON & // Use interruptions
            T0_16BIT & // 16 bit timer
            T0_SOURCE_INT & // Use internal clock as source
            T0_EDGE_FALL & // Update on falling edge
            T0_PS_1_128); // 128 prescaler (lowest period possible) 
    // FOSC = 16 MHz ==> FTY = 16/4 = 4MHz ==> -------TCY = 250 ns
    // Timer 0 prescaler = 128 ==> Count on every--------- 250 ns * 128 = 32 us
    // 1 seg counting = --------1s / 32u = 31250
    //31250*0,7 =21875 70% DC

    Ktimer = 65536 - (21875);
    //WriteTimer0(Ktimer); 


// So the flag occurs 
While(1){

WriteTimer0(Ktimer)
LATCbits.LATC5=0; // LED OFF
}

Pero el led no se enciende. 100% seguro de que la falla está en cómo se construye el código.

    
pregunta Lip

5 respuestas

2

Respuesta final a cualquiera que se pregunte, aquí está el código que funcionó para mí. Es simple, porque realmente no necesito un código o precisión muy específica para el trabajo que estoy haciendo. Quiero agradecerles a todos los que comentaron y me ayudaron a obtener las ideas correctas.

#include <p18f14k50.h>
#include <stdlib.h>
#include <delays.h>
#include <timers.h>


int Ktimer; // variable for overflow 
int time1=10; // Period 1HZ 1*0.1 from Ktimer = 1 second 
int time2=7; // Duty Cycle
//--------------------------- End of Variables ------------------------------------
//********************************************************************************* 

void PWM_LED(void){


    if (time2 > 0)
        LATAbits.LATA5=0; // LED ON 
    else
        LATAbits.LATA5=1; // LED OFF
}
#pragma interruptlow highISR

void highISR(void) {
    // Check if there was overflow by the timer 
    if (INTCONbits.TMR0IF == 1) { 

        time1--;
        time2--;

       INTCONbits.TMR0IF = 0; // Put Flag at zero again 
       WriteTimer0(Ktimer);


    }
}

#pragma code HighInterruptVector=0x0008

void HighInterruptVector(void) {
    _asm
            goto highISR
            _endasm
}
#pragma code


//********************************************************************************* 
//-------------------------------------- Main -------------------------------------
//********************************************************************************* 
void main (void)
{

  //****************************Other congigurations********************************* 
  OSCCON=0x70;        // Select 16 MHz internal clock   

  //************************************Setups*************************************** 

    // Interrupts
    INTCONbits.GIEH = 1; // Enable all high priority interrupts (also required for LOW priority)

    INTCONbits.TMR0IF = 0; //TMR0 Overflow Interrupt Flag bit (must be cleared by software) AO rebentar activa a flag

    INTCONbits.TMR0IE = 1; //Enable TMR0 Overflow Interrupt Enable bit
    INTCON2bits.TMR0IP = 1; //Set TMR0 Overflow Interrupt Priority bit: 1 = High priority

    // Configure Timer0  0,1 seconds  
    OpenTimer0(TIMER_INT_ON & // Use interruptions
            T0_16BIT & // 16 bit timer
            T0_SOURCE_INT & // Use internal clock as source
            T0_EDGE_FALL & // Update on falling edge
            T0_PS_1_128); // 128 prescaler (lowest period possible) 
    // FOSC = 16 MHz ==> FTY = 16/4 = 4MHz ==> -------TCY = 250 ns
    // Timer 0 prescaler = 128 ==> Count on every--------- 250 ns * 128 = 32 us
    // 0.1 seg counting = --------0.1s / 32u = 3125
    // 2^16- 3125 = 62411;
    //Ktimer = 62411;  0.1 seconds
    Ktimer = 65536 - (3125);
    WriteTimer0(Ktimer); // Timer0 will overflow in 100ms


  Delay10TCYx( 5 );             // Delay for 50TCY


  //********************************************************************************* 
  //-------------------------------------- Main cycle -------------------------------
  while (1)
  {

    PWM_LED();

    if(time1 <= 0){ //reset
    time1=10; 
    time2=7; 
    }

    Delay10KTCYx(10);   

  }
  //-------------------------------------- End of Main Cycle ------------------------
  //********************************************************************************* 

  /* Close Peripherals */

  CloseTimer0();
}  
    
respondido por el Lip
1

Puede obtener PWM preciso en bajas frecuencias utilizando el módulo CCP para alternar el pin de salida (quizás después de un número determinado de interrupciones generadoras de impactos de comparación).

Cada vez que la comparación de CCP es una coincidencia, la configura para la siguiente comparación y la configura para que gire el pin de salida (o no).

De esta manera, obtienes una precisión de nanosegundos en tiempos largos y arbitrarios (limitado solo por la precisión del reloj y la vibración).

Si no le importa mucho el jitter en la salida, puede hacer todo con una interrupción fija, por ejemplo, establezca una interrupción de 250usec y realice el PWM en la ISR (rutina de servicio de interrupción). Eso le daría resolución de 0.025% a 1Hz. Eso es algo más simple, y tiene la ventaja de que puede usar cualquier pin que desee para la salida.

    
respondido por el Spehro Pefhany
1

Para algo tan lento como 1 Hz, puede hacer el PWM con suficiente facilidad en el firmware. Regrese al reloj de velocidad completa y configure una interrupción periódica de 1 ms (frecuencia 1 kHz). Con su oscilador original de 16 MHz, la velocidad de instrucción sería de 4 MHz, por lo que habría 4000 ciclos de instrucción por interrupción. Eso es mucho en comparación con lo que debe hacer la interrupción.

Ahora tiene un código que se ejecuta cada 1/1000 de su período de PWM. La lógica para hacer PWM es bastante fácil desde aquí. Usted mantiene un contador para el período de PWM. Lo empiezas en 999 y lo disminuyes cada 1 ms de tiempo. Cuando se vuelve negativo, lo restableces a 999 en su lugar.

Cuando eso sucede, también restablece otro contador al ciclo de trabajo deseado. En su caso, es 700. Establezca la salida alta cuando este contador sea mayor que 0. Luego disminuya el contador, excepto que lo deja en 0 si ya es 0.

Lo que describí anteriormente solo debería tomar unos 10s de ciclos como máximo. Incluso si tomó 50 ciclos (mucho más de lo debido), eso es solo el 1¼% de la CPU. Fácilmente podría ejecutar la interrupción más rápido, como a 4 kHz para una resolución más alta, pero parece que ni siquiera necesita los 10 bits de resolución que obtiene 1 kHz.

    
respondido por el Olin Lathrop
0

¡Su lógica no es tan clara que intente algo como esto en el uso!

Borre la bandera si no lo hace el hardware. Si (DC toggle ++ & 0x01)   TxCNT = - DC   Llevado en Más   TxCNT = -_DC   Llevar afuera Fin

DC es el período en el que el led está encendido, y _dc es el período en el que el led está apagado.

TXCNT es el contador, se supone que es un contador hacia arriba aquí. Un DC y _DC precalculados para que no tenga que hacer los cumplidos allí.

    
respondido por el dannyf
0

para darle una idea de cómo se puede hacer esto, aquí hay un ejemplo de cómo se ejecuta TIMER0 alternativamente entre DC y PR-DC:

    //isr
void interrupt isr(void) {
    static uint8_t toggle=0;                //toggle. 1=DC, 0=PR-DC

    //tmr0 isr
    TMR0IF = 0;                             //clear the flag
    if (++spwm_cnt==0) {                    //counter underflow
        if (toggle++ & 0x01) {              //01->DC
            sPWM_ON();                      //turn on the spwm
            spwm_cnt = -spwm_dc;            //load the offset
        } else {
            sPWM_OFF();                     //DC
            spwm_cnt = -spwm_pr;            //load the offset for the off period
        }
    }
}   

Aquí está la salida que produce en un 12f675, utilizando el bucle principal a continuación:

int main(void) {

    mcu_init();                             //initialize the mcu
    spwm_init();                            //reset the module
    //spwm_setdc(sPWM_PR * 1 / 4);          //set dc to 1/4
    //spwm_setdc(sPWM_PR * 2 / 4);          //set dc to 2/4
    spwm_setdc(sPWM_PR *3 / 4);             //set dc to 3/4
    ei();
    while (1) {
    }
}

Lo estaba ejecutando a 75% dc, 1Hz pwm.

lo que hace es aislar la ejecución en el isr y liberar al usuario de tener que prestar atención a la generación pwm en el bucle principal. Si necesita cambiar el ciclo de trabajo, todo lo que necesita hacer es llamar a spwm_setdc () según sea necesario.

    
respondido por el dannyf

Lea otras preguntas en las etiquetas