This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

PWM and Prescaler Problem

Dear all,

I'm developing on a LM4F232 eval board and try to output two PWM signals on pins PF0 and PF2.
For this I use Timer0 and Timer1.
This works fine for frequencies from 245Hz and up (16MHz / 65536, timer clock divided by 16bit counter range).
For frequencies below that a prescaler has to be used.

Here's the problem using the prescaler:
The frequency and duty cycle of the PWM signal get extremely inaccurate (e.g. 150Hz instead of 200Hz).

Here's the initalization code:

    ROM_SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);

    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1);

    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);

    // Unlock PF0 so it can be used for PMW output.
    HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY_DD;
    HWREG(GPIO_PORTF_BASE + GPIO_O_CR) |= GPIO_PIN_0;
    HWREG(GPIO_PORTF_BASE + GPIO_O_AFSEL) &= 0xfe;
    HWREG(GPIO_PORTF_BASE + GPIO_O_DEN) &= 0xfe;
    HWREG(GPIO_PORTF_BASE + GPIO_O_PCTL) = 0x00;

    ROM_GPIOPinConfigure(GPIO_PF0_T0CCP0);
    ROM_GPIOPinConfigure(GPIO_PF2_T1CCP0);
    ROM_GPIOPinTypeTimer(GPIO_PORTF_BASE, GPIO_PIN_0 | GPIO_PIN_2);

    ROM_TimerConfigure(TIMER0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_PWM);
    ROM_TimerConfigure(TIMER1_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_PWM);

    ROM_TimerEnable(TIMER0_BASE, TIMER_A);
    ROM_TimerEnable(TIMER1_BASE, TIMER_A);

This is the code to set the frequency and duty cycle for TIMER0:

    uint16 frequency = 200;
    uint16 dutyCycle = 50;
    uint16 prescaler = 2;

    ROM_TimerPrescaleSet(TIMER0_BASE, TIMER_A, prescaler - 1);

    float32 timerLoad = static_cast<float32>(ROM_SysCtlClockGet()) / (frequency * prescaler);
    float32 timerMatch = timerLoad - (timerLoad / 100) * dutyCycle;

    ROM_TimerLoadSet(TIMER0_BASE, TIMER_A, static_cast<uint32>(timerLoad));
    ROM_TimerMatchSet(TIMER0_BASE, TIMER_A, static_cast<uint32>(timerMatch));

Am I doing something wrong?
The current MCU is a LM4F232HQ5D but the target will be a LM4F111B2QR.

Thanks for your help,
Stefan

  • Have a different approach to the, "liberation" of PF0 and this raises concern.  We use a different version of M4F Stellaris than the 2 you reference - but believe the care/handling of PF0 is the same.

    Now our MCU data sheet shows that PF0 indeed must first be, "unlocked" - and we employ the first two HWREG instructions just as you do.  But then our interpretations differ - rather drastically.  Our read of our M4F datasheet reveals that Registers: GPIO_O_AFSEL, GPIO_O_DEN, and GPIO_O_PCTL - each default to 0x0000.0000 upon power-up/reset.   Now you seek to repurpose PF0 as TMR_PWM - we seek to repurpose it as Analog Comparator Output.

    MCU Table 9-3. "GPIO Pad Configuration Examples" shows that for your use, "Digital Output (Timer-PWM) both AFSEL and DEN must be 1.  And the exact same conditions hold for our set-up, "Digital Output Comparator"  (there is no selection for Digital Output, Analog Comparator). 

    The code you posted ands a 0 into all 3 key registers:  GPIO_O_AFSEL, GPIO_O_DEN, and GPIO_O_PCTL - believe this disagrees with the set-up codings presented herein.  In our case - we force a 1 into AFSEL and DEN via,       |= 0x01 - not the &= 0xFE you provide.  (recall - registers are 0 at reset...)

    Moving then to Table 22-5 GPIO Pins and Alternate Functions "GPIOPCTL PMCx Bit Field Encoding" we find the requirement for 0x07 to secure your T0CCP0 and for 0x09 for our C0O.  Your code places 0 into this register.

    May we ask how you arrived at your encodings?  Had you noted the datasheet guidelines - and chose a different tack for some other reason?  (not being unkind - in our case we made a critical discovery...) 

    Our MCU appears to configure the Analog Comparator properly - but we have noted a very strange anomaly - which may effect your use as well.  Prefer to hold that revelation till you've had the chance to respond...

    Am aware that this writing avoids your low frequency PWM issue (for now)  - will experiment with a non-PF0 Timer and report.  Please indicate your lowest frequency of interest and your accuracy requirements...

    *** Update: Can now confirm your findings re: "Use of Prescaler to achieve Low Frequency PWM."  My Prescale results are so strange - would be valuable if you'd independently confirm.  Find that the entries into both of the "main counters" via ROM_TimerLoadSet() and ROM_TimerMatchSet() have very minimal effect !  Instead - the major PWM control mechanism springs from Prescalers: ROM_TimerPrescaleSet() and ROM_TimerMatchSet() ! 

    Astounding - we changed the order of code too - to no avail.  Conclusion must be that when in Timer-PWM Mode - the Prescaler contains the most significant bits of the count - and this is described under certain other Timer modes w/in the datasheet. (and likely holds true for Timer-PWM Mode) 

    We found that a value of 10 placed into, "ROM_TimerPrescaleSet()" and value of 5 placed into, "ROM_TimerPrescaleMatchSet" yielded a perfectly stable, 50% duty cycle PWM @ 75Hz.  Replacing those (10,5) values with (30,15) yielded a stable 50% duty @ 25Hz.  However - as you identified - the resolution achievable is orders of magnitude below that obtained using just the main counter.  We were able to generate PWM to less than 10Hz with this Prescale-only approach. (did not matter what values we placed w/in the main counter) 

    It appears that we've confirmed your results - and have no good news at this stage...  Although - using the PLL - you may be able to further divide down your System Clock - and achieve lower frequencies by employing the main Timer counters without any Prescale, "assistance."

  • Combining our findings - and your post - it appears that the, "Unlocking Procedure" for PF_0 is (at least for now) unnecessary!   Weeks ago we discovered that out M4F, MCU's Analog Comparator function performed identically - either with or without - our execution of the PF_0 unlock. 

    Reviewing your post - my bet is that all you need to "configure" PF_0 for your desired Timer-PWM mode is:  (in blue)

        ROM_GPIOPinConfigure(GPIO_PF0_T0CCP0);
        ROM_GPIOPinTypeTimer(GPIO_PORTF_BASE, GPIO_PIN_0 | GPIO_PIN_2);

    In our case - all we needed was:  (again in blue)

       ROM_GPIOPinConfigure(GPIO_PF0_C0O);

    Reviewing our IDE and GPIOPCTL - we note that the above function (alone) places 0x09 into that register - irrespective of any PF_0 Unlock! 

    Perhaps even more amazingly - the Analog Comparator performs just fine with, "NO Set-Up code" at all - not even the ROM_GPIOPinConfigure() shown.  (I will have to reexamine "GPIOPCTL" to try and determine just how (and even if) it is loaded with the requisite 0x09 for our Analog Comparator set-up...)

    I can report that our "ORing In" of AFSEL and DEN does properly "unlock" the CR Register for Port_F - but that exacting, Unlocking procedure, appears to offer no real benefit at this point in time...



     

  • Hi cb1_mobile,

    I tried your proposed changes and only used ROM_GPIOPinConfigure(GPIO_PF0_T0CCP0). This doesn't work for the microcontroller I use. I need to unlock PF0 (see this post) and then configure the pin type. I used the code from the before mentioned post. It worked so I didn't consult the datasheet really.

    Apart from that the PWM signal doesn't behave as expected even on pin PF2, which is a regular GPIO pin. It looks fine as long as I don't use the prescaler. I will have a look at your findings with TimerPrescaleSet and TimerPrescaleMatchSet. Thanks for the hints! I'll get back to you soon.

  • Believe that - as stated earlier - that when Timer-PWM Mode is selected and the Prescaler is enabled - the prescaler provides the most significant bits.  And - as the prescaler is only 8 bits - the wide variance you reported (and which we confirmed) is the natural result.  Not too useful if "normal" PWM resolution is required.

    Here's an extract from the most current SW-DRL-UG:

    27.2.2.26 TimerPrescaleSet
    Prototype:
    void
    TimerPrescaleSet(unsigned long ulBase,
    unsigned long ulTimer,
    unsigned long ulValue)

    Description:
    This function configures the value of the input clock prescaler. The prescaler is only operational
    when in half-width mode and is used to extend the range of the half-width timer modes.
    The prescaler provides the least significant bits when counting down in periodic and one-shot
    modes; in all other modes, (i.e. "your" Timer-PWM) the prescaler provides the most significant bits.

    The resulting PWM signal is clean - and can achieve very low frequencies (we reached below 10Hz) but the ability to "dial in" a specific duty cycle is limited due to the narrow 8 bit width of the prescaler.  Again - our findings showed that various load values into the 16 bit counters had little impact when the Prescaler was enabled!

    Remain mystified how the past code treatment of AFSEL and DEN could have successfully, "unlocked" your M4F...   You should be able to use your IDE and single step thru the unlock code - the or'ing of "1" into bit 0 of both Port_F registers is the only means we've found to properly, "unlock."

    Lost in this detail was the suggestion to employ the PLL - and then divide by the largest legal value.  In this way you can avoid use of the prescaler - and achieve the lowest frequency PWM with great resolution...  SW-DRL-UG shows that, "SYSCTL_SYSDIV_64" is legal for an M4 MCU.  This should yield 200/64 or 3.125MHz as your System Clock - thus you should achieve better than a  5x improvement in low-frequency, Timer-PWM Mode performance - with this simple change.  And - believe that you can alter this MCU System Clock "live" - based upon your program requirements.   

    *** Update: Successfully generated stable, high resolution, PWM outputs down to 47.7Hz - using just the Timer without Prescaler.  Used the above SYSDIV_64 (highest divide supported) to accomplish this.  Here's my code:

    SysCtlClockSet(SYSCTL_SYSDIV_64 | SYSCTL_USE_PLL | SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN);   //  yields 3.125MHz System Clock

    Note:
    a)  I'm using 25MHz xtal - your board uses 16MHz - don't just copy/paste.
    b)  Our M4 is version A-1 - and not the same part no. as yours.  I could not get the ROM_ version of this System Clock function to work correctly when I selected either SYSDIV_32 or SYSDIV_64.  (SYSDIV_16 did work)  Thus I used the Flash version of the function rather than the ROM version.  MCU ran with the ROM version - but frequency was off...   (suspect that the ROM did not contain the newest version of StellarisWare)

    Might the detail/methodology here warrant a, "Verified Answer" tick?