This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Generation of PWM using Extrenal Interrupts and Timers

Hi everybody,

I'm a beginner trying to generate a PWM wave on a C8051F330 micro controller using C programming language. I have many doubts regarding this exercise. It would be really helpful if someone clarifies my doubtswhich will eventually get me going.

Can anybody please explain me roughly the flow of the following program...??

// Include Files //
// Global constants //
// Function Prototypes //
// Main Routine //

void main (void)
{ PCA0MD &= ~0x40; Oscillator_Init (); Port_Init (); External_Interrupt_Init (); Timer2_Init (); PCA_Init (); EA = 1; while (1);
}

// Oscillator Initialisation //
void Oscillator_Init (void)
{ //do something//
}

//Port Initialisation //
void Port_Init (void)
{ //do something//
}

//External Interrupt Initialisation //
void External_Interupt_Init (void)
{ //do something//
EX0 = 1;
}

//Timer2 Initialisation //
void Timer2_Init (void)
{ TMR2CN=0x00;
TMR2RL= RELOAD_VALUE;
TMR2 = 0xFFFF; // Set to reload immediately //
}

//PCA Initialisation //
void PCA_Init (void)
{ //do something//
}

//Interrupt Service Routine (ISR) //
INTERRUPT(INT0_ISR, INTERRUPT_INT0)
{ TR2 = 1;
}

This program is in C. I'm here trying to understand the program flow. I know that the "main()" is the first function that gets executed here. And then the line "PCA0MD &= ~0x40" followed by Osillator_Init function. The program control actually goes into the called Oscillator_Init function (below main()) and executes it,.and so on...

In case of External_Interrupt_Init(),I have enabled /INT0 interrupt using "EX0=1". Where does the program flow control go from here...??

And in ISR where does the program control go after executing TR2 = 1,.

Please folks help me find answers to my doubts.

  • But again the question is, Can I have two interrupts in the same code...??

  • Do you reckon the following...??

    //-----------------------------------------------------------------------------
    // Include Files
    //-----------------------------------------------------------------------------

    #include <compiler_defs.h> // Macro definitions for 8051 Compiler //
    #include <C8051F336_defs.h> // SFR declarations //

    //-----------------------------------------------------------------------------
    // Global Constants
    //-----------------------------------------------------------------------------

    #define SYSCLK 24500000/12 // Clock speed in Hz //

    #define TIMER_TICKS_PER_MILLI_SECOND SYSCLK/100/ 1000

    #define RELOAD_VALUE -TIMER_TICKS_PER_MILLI_SECOND

    SBIT (SW2, SFR_P0, 7); // SW2==0 means switch depressed

    //-----------------------------------------------------------------------------
    // Function Prototypes
    //-----------------------------------------------------------------------------

    void Oscillator_Init (void); // Configure the system clock //
    void Port_Init (void); // Configure the Crossbar and GPIO //
    void Ext_Interrupt_Init (void); // Configure External Interrupts (INT0) //
    void Timer2_Init (int counts // Configure Timer2 //
    void PCA_Init (void); // Configure PCA for PWM mode //

    INTERRUPT_PROTO (INT0_ISR, INTERRUPT_INT0);
    INTERRUPT_PROTO (TIMER2_ISR, INTERRUPT_TIMER2);

    //-----------------------------------------------------------------------------
    // MAIN Routine
    //-----------------------------------------------------------------------------

    void main (void)
    {
       PCA0MD &= ~0x40;                                 // Disable Watchdog timer //
    
       Oscillator_Init();                           // Initialize the system clock //
       Port_Init ();                                        // Initialize crossbar and GPIO //
       Ext_Interrupt_Init();                                // Initialize External Interrupts //
       Timer2_Init(SYSCLK/100000)// Initialize Timer2 to generate interrupts at a 20 Hz rate//
       PCA_Init ();                                 // Initialize PCA for PWM //
    
       EA = 1;
    
       while(1);                            // Infinite while loop waiting for an interrupt //
    }
    
    


    //-----------------------------------------------------------------------------
    // Oscillator_Init
    //-----------------------------------------------------------------------------
    // This routine initializes the system clock to use the precision internal
    // oscillator as its clock source.
    //-----------------------------------------------------------------------------

    void Oscillator_Init (void)
    {
            CLKSEL = 0x00;          // Clock Select. SYSCLK is derived from the Internal     High Frequency Oscillator and scaled per IFCN bits in register OSCICN //
            OSCICN = 0x83;            // Internal H-F Oscillator enabled and SYSCLK derived from Internal H-F Oscillator divided by 1 //
    }
    

    //-----------------------------------------------------------------------------
    // Port_Init
    //-----------------------------------------------------------------------------

    void Port_Init (void)
    {
    
       XBR1      = 0x40;                            // Enable crossbar and weak pull ups //
       P0SKIP    = 0x7F;                    // Skip unused pins on P0 and P1 //
       P1SKIP    = 0xFF;
    
    }
    

    //-----------------------------------------------------------------------------
    // Ext_Interrupt_Init
    //-----------------------------------------------------------------------------

    void Ext_Interrupt_Init (void)
    {
    
       TCON   = 0x01;                      // INT 0 is edge triggered //
       IT01CF = 0x07;                      // INT0 active low; INT0 on P0.7 //
       EX0 = 1;                            // Enable INT0 interrupts //
    
    }
    
    


    //-----------------------------------------------------------------------------
    // Timer2_Init
    //-----------------------------------------------------------------------------

    void Timer2_Init (int counts)
    {
    
       TMR2CN  = 0x00;                     // Stop Timer2. Clear T2SPLIT.
                                    // Use SYSCLK/12 as timebase, 16-bit auto-reload
      // CKCON  &= 0x30;                   // Timer2 High and Low byte uses the SYSCLK //
       TMR2RL  = -counts;              // Initialize reload value
       TMR2    = 0xFFFF;                  // Set to reload immediately
    
    }
    
    

    //-----------------------------------------------------------------------------
    // PCA_Init
    //-----------------------------------------------------------------------------

    void PCA_Init(void)
    {
    
            PCA0MD    = 0x00;
            PCA0CPM0  = 0xC7;               // PWM 16, ECOM, TOG, PWM, ECCF bits set //
    
    }
    


    //-----------------------------------------------------------------------------
    // Interrupt Service Routines
    //-----------------------------------------------------------------------------

    INTERRUPT(INT0_ISR, INTERRUPT_INT0)
    {
    
             TR2  = 1;            // Start Timer2 //
             ET2 = 1;
    
    }
    

    INTERRUPT(Timer2_ISR, INTERRUPT_TIMER2)
    {
    
            while (TF2H ==1)
            {
             for (RELOAD_VALUE = -TIMER_TICKS_PER_MILLI_SECOND ; RELOAD_VALUE <65535; RELOAD_VALUE++)
                            {
                                    PCA0CPH0  = RELOAD_VALUE;
                      }
             continue;
            }
    
    }
    

  • Why did you break up your code into many blocks? Why not just a single pre-block for everything?

    Of course you have have multiple interrupt service routines in your code.

    But you shouldn't have such a busy loop - don't you want your main loop to run?

    You can have the timer generate an interrupt every time it needs servicing. So the processor will jump to the timer ISR multiple times. And in-between, you can do something else.

    An ISR should always be optimized to quickly end. So careful with loops. And try to avoid calling functions from inside the ISR - at least as long as you program a 8051 chip.

    Let main loop fill a ring buffer with reload values to use.
    Let the timer ISR pick up next reload value.

  • Thanks for your reply.

    "But you shouldn't have such a busy loop - don't you want your main loop to run?" - If I let the main () loop to run then it would mean another switch/button hit isn't it...?? I want a continuous PWM signal generated until I hit the reset on the MCU or stop debug session.

    "And try to avoid calling functions from inside the ISR - at least as long as you program a 8051 chip." - Have I implemented this in my code..?? If so please copy and paste in the next post please...

  • the basics (which includes most questions I have seen in this thread) about the '51 are best described in "the bible" In my opinion nobody should set out working with the '51 without studying "the bible" first. www.8052.com/.../120112

    Erik

  • You do realize that your timer can generate an interrupt and then continue to tick?
    So your timer ISR figures out a new reload value for the timer.
    And the timer generates the next interrupt.
    And your timer ISR figures out a new reload value.
    ...

    So the timer hardware + timer ISR can function with zero involvement of your main loop.

    It's just a question _if_ you want your main loop to decide what curve shape to emit or not. But you don't need to involve the main loop if you don't want to.

    No, you haven't called functions from your timer ISR - I just informed you that when you add some intelligence in that ISR you should avoid making function calls. Just as you should not have any wild loops inside the ISR. The ISR should end, while letting the timer continue to do what the timer is designed to do. Until the timer cries for service the next time.

  • Thanks for your reply. Is there any other way of implementing this without having to need a Interrupt service routine for Timer servicing. Can I do the same within the main (),.. as the following...??

    //-----------------------------------------------------------------------------
    // Include Files
    //-----------------------------------------------------------------------------
    
    #include <compiler_defs.h> // Macro definitions for 8051 Compiler //
    #include <C8051F336_defs.h> // SFR declarations //
    
    //-----------------------------------------------------------------------------
    // Global Constants
    //-----------------------------------------------------------------------------
    
    #define SYSCLK 24500000/12 // Clock speed in Hz //
    
    #define TIMER_TICKS_PER_MILLI_SECOND SYSCLK/100/ 1000
    
    #define RELOAD_VALUE -TIMER_TICKS_PER_MILLI_SECOND
    
    SBIT (SW2, SFR_P0, 7); // SW2==0 means switch depressed
    
    //-----------------------------------------------------------------------------
    // Function Prototypes
    //-----------------------------------------------------------------------------
    
    void Oscillator_Init (void); // Configure the system clock //
    void Port_Init (void); // Configure the Crossbar and GPIO //
    void Ext_Interrupt_Init (void); // Configure External Interrupts (INT0) //
    void Timer2_Init (int counts // Configure Timer2 //
    void PCA_Init (void); // Configure PCA for PWM mode //
    
    INTERRUPT_PROTO (INT0_ISR, INTERRUPT_INT0);
    INTERRUPT_PROTO (TIMER2_ISR, INTERRUPT_TIMER2);
    
    //-----------------------------------------------------------------------------
    // MAIN Routine
    //-----------------------------------------------------------------------------
    void main (void)
    {
       PCA0MD &= ~0x40;                                 // Disable Watchdog timer //
    
       Oscillator_Init();                           // Initialize the system clock //
       Port_Init ();                                        // Initialize crossbar and GPIO //
       Ext_Interrupt_Init();                                // Initialize External Interrupts //
       Timer2_Init(SYSCLK/100000)// Initialize Timer2 to generate interrupts at a 20 Hz rate//
       PCA_Init ();                                 // Initialize PCA for PWM //
    
       EA = 1;
    
       while(1)                            // Infinite while loop waiting for an interrupt //
    {
       if (TF2H ==1)
         {
          RELOAD_VALUE = 0;
          while(RELOAD_VALUE < 65535)
             {
               PCA0CPH0  = RELOAD_VALUE;
               TF2H = 0;
               RELOAD_VALUE++;
             }
          }
    
    }
    
    }
    
    
    
     //-----------------------------------------------------------------------------
    // Oscillator_Init
    //-----------------------------------------------------------------------------
    // This routine initializes the system clock to use the precision internal
    // oscillator as its clock source.
    //-----------------------------------------------------------------------------
    void Oscillator_Init (void)
    {
            CLKSEL = 0x00;          // Clock Select. SYSCLK is derived from the Internal     High Frequency Oscillator and scaled per IFCN bits in register OSCICN //
            OSCICN = 0x83;            // Internal H-F Oscillator enabled and SYSCLK derived from Internal H-F Oscillator divided by 1 //
    }
    
     //-----------------------------------------------------------------------------
    // Port_Init
    //-----------------------------------------------------------------------------
    void Port_Init (void)
    {
    
       XBR1      = 0x40;                            // Enable crossbar and weak pull ups //
       P0SKIP    = 0x7F;                    // Skip unused pins on P0 and P1 //
       P1SKIP    = 0xFF;
    
    }
    
     //-----------------------------------------------------------------------------
    // Ext_Interrupt_Init
    //-----------------------------------------------------------------------------
    void Ext_Interrupt_Init (void)
    {
    
       TCON   = 0x01;                      // INT 0 is edge triggered //
       IT01CF = 0x07;                      // INT0 active low; INT0 on P0.7 //
       EX0 = 1;                            // Enable INT0 interrupts //
    
    }
    
    
    
     //-----------------------------------------------------------------------------
    // Timer2_Init
    //-----------------------------------------------------------------------------
    void Timer2_Init (int counts)
    {
    
       TMR2CN  = 0x00;                     // Stop Timer2. Clear T2SPLIT.
                                    // Use SYSCLK/12 as timebase, 16-bit auto-reload
      // CKCON  &= 0x30;                   // Timer2 High and Low byte uses the SYSCLK //
       TMR2RL  = -counts;              // Initialize reload value
       TMR2    = 0xFFFF;                  // Set to reload immediately
    
    }
    
    
     //-----------------------------------------------------------------------------
    // PCA_Init
    //-----------------------------------------------------------------------------
    void PCA_Init(void)
    {
    
            PCA0MD    = 0x00;
            PCA0CPM0  = 0xC7;               // PWM 16, ECOM, TOG, PWM, ECCF bits set //
    
    }
    
    
     //-----------------------------------------------------------------------------
    // Interrupt Service Routines
    //-----------------------------------------------------------------------------
    INTERRUPT(INT0_ISR, INTERRUPT_INT0)
    {
    
             TR2  = 1;            // Start Timer2 //
    
    }
    

  • Is there any other way of implementing this without having to need a Interrupt service routine for Timer servicing
    why? you get a much more precise operation with interrupts than with polling. A timer ISR ia just about the simplest thing there is.

    Erik

  • Again, that's a really basic question - nothing specifically to do with Keil or SiLabs.

    You really should think about getting some training in these foundational areas...

  • Think about it.

    With auto-reload, the timer can manage on its own without need for a main loop or an ISR to do something. But it will then only use the same reload value every time.

    If you want the timer to change the reload value - such as ramping the reload value like you showed in some previous code - then you must somewhere have the code that regularly changes the reload value.

    You can do it in the main loop by polling. But then your main loop will have a hard time doing something else because of the quick response times needed when the reload value is short.

    Or you can use an interrupt handler, so you get an interrupt every time the timer have reloaded - then you put in your next reload value and exists. So every time the timer reloads, your ISR gets activated to feed a new reload value. And your main program can do whatever it likes without being disturbed.

    Think about using printf() to send out data on the serial port. During the time printf() formats the string, your main loop can't service the timer with new reload values. But an interrupt service routine (ISR) will manage.

    So unless the processor is so advanced that it can make use of DMA to distribute new reload values, the best possible solution really is to use a timer ISR.

    By the way - your modified code don't seem to contain any timer ISR. Did you get it to work?

  • I agree with what you're saying. It makes sense to use a Timer ISR to process the Timer overflow. But I didn't get it working. So, it made me to think of alternatives. Many thanks for your reply and patience.

  • So why didn't you post the non-working code?

    Note that you want to set a new reload value after the timer have taken the current reload value. Then you decide if you should change reload value once/repetition or maybe slower.

    But see your code:

       if (TF2H ==1)
         {
          RELOAD_VALUE = 0;
          while(RELOAD_VALUE < 65535)
             {
               PCA0CPH0  = RELOAD_VALUE;
               TF2H = 0;
               RELOAD_VALUE++;
             }
          }
    
    


    Exactly what do you think you are doing?

    If you give the timer a reload value, you should then also let the timer tick this value before the timer picks up the new reload value and you can update with the next reload value to use.

    You just have a wild loop that steps through reload values as fast as possible, without caring about what the timer is doing.

  • I agree with what you're saying. It makes sense to use a Timer ISR to process the Timer overflow. But I didn't get it working. So, it made me to think of alternatives

    just about the worst you can do is to "think of alternatives" when you "didn't get it working.". You will never learn anything that way.

    Erik

  • Hello,

    Thanks very much for your reply. I understand what you mean exactly by a wild loop now. I have made the following changes in the main() and tried to execute the code. But I still don't see any waveform generated. Please help find bugs if there are any...
    This is what I'm trying to do exactly. The external interrupt should trigger/start a timer (16-bit timer2 with auto reload functionality). Every time the timer overflows from all FFFF's to 0000's a new value (starting from 0 to 65534) should be put in the PCA register.

    
    //-----------------------------------------------------------------------------
    // Include Files
    //-----------------------------------------------------------------------------
    
    #include <compiler_defs.h>                           // Macro definitions for 8051 Compiler //
    #include <C8051F336_defs.h>                  // SFR declarations //
    
    //-----------------------------------------------------------------------------
    // Global Constants
    //-----------------------------------------------------------------------------
    
    #define SYSCLK             24500000/12             // Clock speed in Hz //
    
    
    SBIT (SW2, SFR_P0, 7);                         // SW2==0 means switch depressed //
    
    
    //-----------------------------------------------------------------------------
    // Function Prototypes
    //-----------------------------------------------------------------------------
    
    void Oscillator_Init (void);             // Configure the system clock //
    void Port_Init (void);                       // Configure the Crossbar and GPIO //
    void Timer2_Init (int counts);           // Configure Timer2 //
    void PCA_Init (void);                    // Configure PCA for PWM mode //
    void Ext_Interrupt_Init (void);             // Configure External Interrupts (INT0) //
    
    
    INTERRUPT_PROTO (INT0_ISR, INTERRUPT_INT0);
    
    
    //-----------------------------------------------------------------------------
    // MAIN Routine
    //-----------------------------------------------------------------------------
    void main (void)
    {
       PCA0MD &= ~0x40;                       // Disable Watchdog timer //
       int RELOAD_VALUE = 0;
    
       Oscillator_Init();                  // Initialize the system clock //
       Port_Init ();                           // Initialize crossbar and GPIO //
       Timer2_Init(SYSCLK/100);// Initialize Timer2 to generate interrupts at a 20kHz rate//
       PCA_Init ();                    // Initialize PCA for PWM //
       Ext_Interrupt_Init();              // Initialize External Interrupts //
    
       EA = 1;
    
       while(1)                           // Infinite while loop //
    {
                     if(TF2H == 1)
                     {
                             if (RELOAD_VALUE<65535)
                                            {
                                                    TF2H = 0;
                                                    PCA0CP0 = RELOAD_VALUE;
                                                    RELOAD_VALUE ++;
                                            }
                             else
                                            {
                                                    TF2H = 0;
                                                    RELOAD_VALUE =0;
                                            }
                            }
    
    }
    
    }
    
    
    //-----------------------------------------------------------------------------
    // Oscillator_Init
    //-----------------------------------------------------------------------------
    // This routine initializes the system clock to use the precision internal
    // oscillator as its clock source.
    //-----------------------------------------------------------------------------
    void Oscillator_Init (void)
    {
            CLKSEL = 0x00;    // Clock Select. SYSCLK is derived from the Internal High Frequency Oscillator and scaled per IFCN bits in register OSCICN //
            OSCICN = 0x83;            // Internal H-F Oscillator enabled and SYSCLK derived from Internal H-F Oscillator divided by 1 //
    }
    
    
    //-----------------------------------------------------------------------------
    // Port_Init
    //-----------------------------------------------------------------------------
    
    void Port_Init (void)
    {
    
       XBR1      = 0x40;            // Enable crossbar and weak pull ups //                    P0SKIP    = 0x7F;                    // Skip unused pins on P0 and P1 //
       P1SKIP    = 0xFF;
    
    }
    
    
    //-----------------------------------------------------------------------------
    // Timer2_Init
    //-----------------------------------------------------------------------------
    
    void Timer2_Init (int counts)
    {
    
       TMR2CN  = 0x00;                 // Stop Timer2. Clear T2SPLIT.
                                   // Use SYSCLK/12 as timebase, 16-bit auto-reload
       CKCON  |= 0x30;                // Timer2 High and Low byte uses the SYSCLK //
       TMR2RL  = -counts;           // Initialize reload value
       TMR2    = 0xFFFF;               // Set to reload immediately
    
    }
    
    
    //-----------------------------------------------------------------------------
    // PCA_Init
    //-----------------------------------------------------------------------------
    
    void PCA_Init(void)
    {
    
            PCA0MD    = 0x00;             // PCA Counter/Timer Idle Control enabled, Watchdog timer disabled, Watchdog timer enable unlocked, Unused, System clock divided by 12, Disable CF Interrupt //
            PCA0CPM0  = 0x82;             // PWM 16, ECOM, TOG, PWM, ECCF bits set //
             PCA0PWM        &= ~0x80;
    }
    
    
    //-----------------------------------------------------------------------------
    // Ext_Interrupt_Init
    //-----------------------------------------------------------------------------
    
    void Ext_Interrupt_Init (void)
    {
    
       TCON   = 0x01;                   // INT 0 is edge triggered //
       IT01CF = 0x07;                   // INT0 active low; INT0 on P0.7 //
       EX0 = 1;                         // Enable INT0 interrupts //
    
    }
    
    
    //-----------------------------------------------------------------------------
    // Interrupt Service Routines
    //-----------------------------------------------------------------------------
    
    INTERRUPT(INT0_ISR, INTERRUPT_INT0)
    {
       TR2  = 1;            // Start Timer2 //
    }