Skip to main content
Engineering LibreTexts

29.4: Internal Interrupts — Hand Wrought PWM

  • Page ID
    35918
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)\(\newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    Our next example shows how a timer/counter overflow can be used to create a PWM signal at an arbitrary pin (i.e., not just at the OCnx pins). For this example we shall forego the use of an LED indicator and simply inspect the signal with an oscilloscope. The output frequency is slightly over 60 Hz and the duty cycle is set by a #define although it could just as easily be set by a variable. The duty cycle is (OVF_COUNT_START+1)/256. Thus, a value of 128 yields approximately 50% while a value of 50 yields nearly 20%. The output pin is set to Uno pin 8 or PORTB.0. The code follows:

    /* Timer/Counter Interrupt Example
       Shows how to create hand wrought PWM at an arbitrary pin
    */
    
    #define ARBPINMASK 0x01
    #define OVF_COUNT_START 128
    
    void setup()
    {
        DDRB |= ARBPINMASK;
    
        TCCR2A = 0;    // normal mode, OC2x pin disconnected
        TCCR2B = 0x07; // 1024x prescale
    
        TIMSK2 = (1<<TOIE2);     // enable overflow interrupt
        TCNT2 = OVF_COUNT_START; // init counter, count up from here
    }
    
    ISR(TIMER2_OVF_vect)
    {
        if( PORTB &= ARBPINMASK )
            TCNT2 = OVF_COUNT_START;
        else
            TCNT2 = 255-OVF_COUNT_START;
    
        PORTB ^= ARBPINMASK;
    }
    
    void loop()
    {
    }
    

    The code is very similar to that of the prior example with the addition of the alteration of the TCNT2 count register. Each interrupt causes the pin to toggle, however, unlike the previous example the count register is set to a new starting value which shortens the count time (i.e., the time to overflow). Further, the reset value is dependent on whether the current output level is high or low.


    This page titled 29.4: Internal Interrupts — Hand Wrought PWM is shared under a CC BY-NC-SA 4.0 license and was authored, remixed, and/or curated by James M. Fiore via source content that was edited to the style and standards of the LibreTexts platform; a detailed edit history is available upon request.