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.

RTOS/TM4C123GH6PM: Issues with PWMTimerTiva drivers

Part Number: TM4C123GH6PM
Other Parts Discussed in Thread: SYSBIOS

Tool/software: TI-RTOS

I am using TI-RTOS 2.16.00.08 on the TM4C123GH6PM. I am trying to use some of the Timer module PWMs. The first problem I has was in the setup file when adding the line:

#include <ti/drivers/PWM.h>
#include <ti/drivers/pwm/PWMTimerTiva.h>

PWMTimerTiva_Object pwmTimerTivaObjects[SCU_REV_A_PWMCOUNT];

const PWMTimerTiva_HWAttrs pwmTimerTivaHWAttrs[] = {
    {
        .baseAddr = TIMER1_BASE,
        .timer = TIMER_A
    },
    {
        .baseAddr = TIMER1_BASE,
        .timer = TIMER_B
    },
    {
        .baseAddr = WTIMER2_BASE,
        .timer = TIMER_A
    },
    {
        .baseAddr = WTIMER2_BASE,
        .timer = TIMER_B
    },
    {
        .baseAddr = WTIMER3_BASE,
        .timer = TIMER_A
    },
    {
        .baseAddr = WTIMER3_BASE,
        .timer = TIMER_B
    }
};

Since driverlib/timer.h was not included in any of the files the TIME_A was not defined. I fixed this by adding "#include <driverlib/timer.h>" into the PWMTimerTiva.h file. I just want to check if this is the best solution or if there is a better way of fixing it? Also feel like this is something that should be fixed in the driver files when downloaded.


The next problem I had was that the wide timers do not initiate and return with NULL. The problem appears to be with the following lines in the PWMTimerTiva.c:

    kernelTimerID = 1 << ((hwAttrs->baseAddr >> 12) & 0xF);
    timerPairEnabledBit = (TIMER_BOTH & (~hwAttrs->timer)) &
                          (TIMER_CTL_TAEN | TIMER_CTL_TBEN);

    if (kernelTimerID & Timer_getAvailMask()) {
        /* Timer unused, claim timer from Kernel */
        kernelTimerID = (~kernelTimerID) & 0x0F;
        Timer_setAvailMask(Timer_getAvailMask() & kernelTimerID);
    }
    else if (!(HWREG(hwAttrs->baseAddr + TIMER_O_CTL) & timerPairEnabledBit)) {
        /* Timer used and was not initialized by the other half timer */
        Hwi_restore(key);
        return (NULL);
    }

The Timer_getAvailMask() seems to return 0b111100 and the Timer_getNumTimers() returns 6. This seems to indicate that there are only the first 6 drivers initiated in the bios which would leave the wide-timers inaccessible. Is there a way to change this so that the rest of the timer modules are accessible or are they already being used by the BIOS and therefore can't use them for PWM?

In addition to that kernelTimerID = 1 << ((hwAttrs->baseAddr >> 12) & 0xF); only works for TIMER0, TIMER1, TIMER2, TIMER3, TIMER4, TIMER5 and maybe WTIMER0 and WTIMER1 i havn't tried them all out. This is because there is a break in the base address of the timers and the kernelTimerID is base address dependent. The timer base addresses from hw_memmap.h are below:

#define TIMER0_BASE             0x40030000  // Timer0
#define TIMER1_BASE             0x40031000  // Timer1
#define TIMER2_BASE             0x40032000  // Timer2
#define TIMER3_BASE             0x40033000  // Timer3
#define TIMER4_BASE             0x40034000  // Timer4
#define TIMER5_BASE             0x40035000  // Timer5
#define WTIMER0_BASE            0x40036000  // Wide Timer0
#define WTIMER1_BASE            0x40037000  // Wide Timer1
#define WTIMER2_BASE            0x4004C000  // Wide Timer2
#define WTIMER3_BASE            0x4004D000  // Wide Timer3
#define WTIMER4_BASE            0x4004E000  // Wide Timer4
#define WTIMER5_BASE            0x4004F000  // Wide Timer5
#define TIMER6_BASE             0x400E0000  // General-Purpose Timers
#define TIMER7_BASE             0x400E1000  // General-Purpose Timers

Finally, the checking to make sure that the timer module is not already open in the BIOS seems insufficient. If the module is being used in the Timer_getAvailMask(), then if the TIMER_A is being initiated it checks to see if the TIMER_B module is in use. If it is in use then it assumes that the module was initiated by the TIMER_B part of the module, but it does not check if the TIMER_A is already initiated and therefore could attempt to over wright a previous initiation. It could also happen that the TIMER_B is being initiated, if the BIOS already initiated the module as a full 32 bit (in 16/32 bit module) then it will check if TIMER_A is initiated, which will be true. It will then over wright the full timer with the half timer. Has anybody else had these problems, I am new to TI-RTOS and am not sure if it is an actual problem? Are there any fixes to these problems available?

  • Hi Carl,

    That is some interesting timer configuration code you added. If not mistaken one RTOS timer is a built in function, recall you can assign GPTM number sacrificed for the BIOS Systick. Best way to see what is happening under the kernels hood is to enter CCS debug and launch ROV then pause the ICDI debug to review all the Kernel registers.
  • I didn't add any code, only used what is already in the PWMTimerTiva.h file (one of the drivers already in TI-RTOS) and modified it to make it work with the timer modules I wanted. I am not trying to use them as timers but rather as PWMs. The problem is that the code that comes with the TI-RTOS doesn't seem to work.
  • You still have to configure the GPTM for PWM mode using Tivaware directives from [/driverlib/timer.c]. I have all six GPTM working in RTOS but sacrifice GPTM5 for BIOS [Clock_tick] in the *.CFG file of BIOS behavior settings. May need to add #include at the top of your main application for [timer.h] to configure the timers with Tivaware.

    Perhaps the GPTM timer module is limited until enable LM4?

    E.G.  The Clock module allows you to define one or more periodic functions that are run in the context of a Swi (software interrupt) thread.

    The Timer module provides LM4-specific timer services that extend those provided by the generic ti.sysbios.hal.Timer module.

    var Timer = xdc.useModule('ti.sysbios.family.arm.lm4.Timer');

    This GPTM configuration works well in RTOS to produce 25kHZ PWM:

    /* EKTM4C129XL-PWM PL4:TM0CCP0 BP1-X9-pin9 / X11-pin 65 */
       ROM_GPIOPinConfigure(GPIO_PL4_T0CCP0);
       ROM_GPIOPinTypeTimer(GPIO_PORTL_AHB_BASE, GPIO_PIN_4); 
    
    
    /**************************************************
     * GPTM-0A: Half width timer
     * Configure GPTM-0A GPTMTbMR 16 bit down count
     * CCP0 Output edge sense for a PWM interrupt handler 
     * used for duty cycle control updates.
     **************************************************/
    /* Disable GPTM-A0 */
    HWREG(TIMER0_BASE + TIMER_O_CTL) &= ~(TIMER_CTL_TAEN);
    
    /* Configure GPTM-0 16Mhz 62.5us clock source.*/
      ROM_TimerClockSourceSet(TIMER0_BASE, TIMER_CLOCK_PIOSC);
    
    /* Set GPTM-0A control register for
     * split half wide 16 bit timers */
    HWREG(TIMER0_BASE + TIMER_O_CFG) = 0x4;
    
    	/* Set GPTM-0A mode register for periodic PWM down count: Edge count mode.
    	 * Set interrupt on CCP0 output pin event positive edge  */
        HWREG(TIMER0_BASE + TIMER_O_TAMR) |=
        		(TIMER_CFG_A_PWM|TIMER_TAMR_TAAMS|TIMER_TAMR_TAPWMIE|
        		                  TIMER_TAMR_TAILD|TIMER_TAMR_TAMRSU);
    
        /* Set the load timer GPTMTaILR value 0x12C0/4800*8.333ns=40us/25kHz */
        ROM_TimerLoadSet(TIMER0_BASE, TIMER_A, 0x280); //640d
        /* Set the inital PWM duty cycle at 50% */
        ROM_TimerMatchSet(TIMER0_BASE, TIMER_A, 0x140);//320d
        /* Configure edge polarity capture events for CCP0. */
        ROM_TimerControlEvent(TIMER0_BASE, TIMER_A, TIMER_EVENT_POS_EDGE);
        /* Register the global interrupt in the controller */
        TimerIntRegister(TIMER0_BASE, TIMER_A, TMCCP0FanPWMInt);
        /* Set the value for the PWM modulator reload interrupt */
        ROM_TimerIntEnable(TIMER0_BASE, TIMER_CAPA_EVENT);
        /* For now disable GPTM-0A PWM CCP0 edge capture interrupt */
        ROM_IntDisable(INT_TIMER0A);
        /* Set TMaEN=1, PWM down count match sets TMaEN=1 */
        ROM_TimerEnable(TIMER0_BASE, TIMER_A);

     

     

  • If you look through the supplied PWMTimerTiva.c file it seems that the file is meant to be setting up the module. I agree that the above method would be a work around but I was trying to use the drivers that TI provided. However since they don't work properly I am will try the method above. Maybe somebody from TI can look at the supplied driver and try make it work.

    In your above code how do you let the kernel know that the GPTM is being used so that it doesn't try and use it for one of its functions or does the kernel only use the GPTMs that are assigned to it in the .cfg file? Also you said you got all six timer modules working, what about the wide-timers?

    Also why does the kernel only have definitions for 6 timers when the TM4C123GH6PM has 12 GPTMs?

  • Carl Engelmann said:
    If you look through the supplied PWMTimerTiva.c file it seems that the file is meant to be setting up the module

    Likely for the BIOS Clock_tick required for semaphore interrupting. You don't have to configure BIOS (Clock_Tick) or sacrifice any GPTM but know logically not to purpose that GPTM# in code if it is already in use by the BIOS.

    Carl Engelmann said:
    Also why does the kernel only have definitions for 6 timers when the TM4C123GH6PM has 12 GPTMs?

    Are you certain there are 12 - 32 bit GPTM or perhaps 6 - 32 bit full wide timers that can be spit in half? 

  • There are 6 32bit (which can be split into 12 16bit) and 6 64bit (which can be split into 12 32bit). I have gotten what I needed working.
    Thanks for the help.
  • Glad to see you produced PWM in RTOS, BTW you are right about being 12 GPTM yet 16/24bit with total of 12 CCP capture pins. The second load values (//) are for 120Mhz GPTM clock source but the error % seems to grow in counting PWM edges.

    I setup M3 NVIC module and enter all peripheral INTs in *.CFG file for Kernel to watch over. Comes in handy to debug with ROV trouble shoot the IRQ handlers . Then we no longer need register each IRQ inside the (startup.ccs.c) file and seem to gain the kernel M3 exception handling added in running tasks. Seemingly select M3 SHI because of semaphore IRQ handling for LWIP1.4.1 and other running SW interrupts but all the HW interrupts must be entered too one by one on the Instance page..