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.

AM3356: PWM library usage

Part Number: AM3356
Other Parts Discussed in Thread: SYSBIOS

Hi,

I am working on  Processor SDK RTOS 06_03_00_106. 

I am using beaglebone black as my device and I have successfully used the LLD for I2C and UART in my project. However when I try to find PWM LLD , it is not there and after consulting with the TI team I found out that I need to use the Starterware library for this, however I didnt get detailed answers on how to build this example in my CCS and use it in an RTOS project. Please help me with the following questions:

1. Can I open the starterware example epwm on my CCS inside of an RTOS project?

2. The epwm has APIs like EpwmAppPwmCfg(), EpwmAppTimebaseModuleCfg(), EpwmAppCounterComparatorCfg(), EPWMAppInit() etc. Can I use them as it is for my PWM implementation on BeagleBone Black?

3. Please provide an example usage of pwm CSL in an RTOS project as I am having a difficult time searching for resources online and have been working with the same issue since a few days now.

  • Hi Arishiya,

    Arshiya Tabassum said:
    1. Can I open the starterware example epwm on my CCS inside of an RTOS project?

    No. I already covered this here: https://e2e.ti.com/support/microcontrollers/other/f/908/p/924519/3421459#3421459

    Arshiya Tabassum said:
    2. The epwm has APIs like EpwmAppPwmCfg(), EpwmAppTimebaseModuleCfg(), EpwmAppCounterComparatorCfg(), EPWMAppInit() etc. Can I use them as it is for my PWM implementation on BeagleBone Black

    Yes, it should be possible. Maybe a good first step would be to port the Starterware ePWM example to BBB before attempting to integrate ePWM into a TI-RTOS app. This would familiarize you with the APIs and allow you to root out any platform specific code.

    Arshiya Tabassum said:
    3. Please provide an example usage of pwm CSL in an RTOS project as I am having a difficult time searching for resources online and have been working with the same issue since a few days now.

    That won't be possible. If you encounter specific issue I can try to help, but I can't develop a new example for you.

    Starterware is CSL for AM335x devices. This is described here: https://software-dl.ti.com/processor-sdk-rtos/esd/docs/06_03_00_106/AM335X/rtos/index_device_drv.html#csl

    AM335x/AM437x

    The CSL component of AM335x/AM437x Processor SDK is referred as StarterWare in the legacy baseline releases.To maintain backward compatibility for existing applications on AM335x/AM437x SOCs, StarterWare low level package is retained. Customers are recommended to use driver interfaces for ease of migration of application software across SOCs.

    Regards,
    Frank

  • Hi, 

    I am trying to utilize the epwm.h for my PWM implementation. I came across this comment in the epwm_app.c file , can you please elaborate what it means? 

    /* ========================================================================== */
    /*                                Macros                                      */
    /* ========================================================================== */
    
    /* TODO: This macro is not needed. This value should be provided by PRCM info. */
    /** \brief Functional clock to the PWMSS. */
    #define SOC_EHRPWM_2_MODULE_FREQ      (100U * FREQ_MHZ)
    

    This is in reference to this line of code in the function EPWMAppInit() in the same source file:

    /* Get the functional clock value of PWMSS */
                /* TODO: This value has to be provided by the PRCM data base */
                pPwm->funcClk = SOC_EHRPWM_2_MODULE_FREQ;

    Does that mean I need to use this API  shown below: 

    /**
     * \brief This API gets the input functional clock rate of a module. This can
     *        be used by the device driver API's to get the input clock and configure
     *        the internal multipliers/dividers accordingly. Clock manipulations
     *        inside the module are not modelled here, and left to the individual
     *        module drivers to handle.
     *
     * \param moduleId       Unique ID of the module
     *                           #chipdbModuleID_t
     * \param instNum        Instance number of the module
     * \param clockId        Clock ID of the clock edge to be traversed. For 
     *                       modules like CPSW, DSS etc, where more than one clock 
     *                       feeds the module, one of the clock need to be selected.
     *                           #prcmClockId_t
     * \param *pClockRate    Pointer to get the clock rate in KHz 
     *
     * \retval S_PASS              Clock rate returned in 'clockRate' is valid
     * \retval E_INVALID_PARAM     Invalid module id
     * \retval E_INVALID_OPERATION API called when the module is disabled
     **/
    int32_t PRCMGetClockRate(chipdbModuleID_t moduleId, 
                             uint32_t instNum,
                             prcmClockId_t clockId, 
                             uint32_t *pClockRate);

    If so, is this the correct parameters I am passing for PWM instance 1 and getting the clock value in "read_var"

    What would be the third parameter here for PWM instance 1? I couldn't find the appropriate one for pwm in this structure(in file hw_prcm.h):

    /** 
     * \brief Clocks
     */
    typedef enum 
    {
        PRCM_CLK_L4_CEFUSE_GCLK,
        PRCM_CLK_MIN = PRCM_CLK_L4_CEFUSE_GCLK,
        /*!< Lower bound (inclusive) */
        PRCM_CLK_CUST_EFUSE_SYSCLK,
        PRCM_CLK_DEBUG_CLKA_GCLK,
        PRCM_CLK_DEBUG_CLKB_GCLK,
        PRCM_CLK_DEBUG_CLKC_GCLK,
        PRCM_CLK_L3_AON_GCLK,
        PRCM_CLK_DBGSYSCLK,
        PRCM_CLK_DSS_GCLK,
        PRCM_CLK_DSS_L3_GCLK,
        PRCM_CLK_DSS_L4_GCLK_EN,
        PRCM_CLK_DSS_SYSCLK,
        PRCM_CLK_L4LS_GCLK,
        PRCM_CLK_EMIF_PHY_GCLK,
        PRCM_CLK_DDR_DLL_GCLK,
        PRCM_CLK_DLL_AGING_GCLK,
        PRCM_CLK_EMIF_L3_GICLK,
        PRCM_CLK_L3S_GCLK,
        PRCM_CLK_MMC_FCLK,
        PRCM_CLK_L3_GCLK,
        PRCM_CLK_GFX_GFCLK,
        PRCM_CLK_GFX_L3_GCLK,
        PRCM_CLK_GPIO_1_GDBCLK,
        PRCM_CLK_GPIO_2_GDBCLK,
        PRCM_CLK_GPIO_3_GDBCLK,
        PRCM_CLK_GPIO_4_GDBCLK,
        PRCM_CLK_GPIO_5_GDBCLK,
        PRCM_CLK_MGC_FGCLK,
        PRCM_CLK_L3D2_GCLK,
        PRCM_CLK_ICSS_IEP_GCLK,
        PRCM_CLK_ICSS_OCP_GCLK,
        PRCM_CLK_ICSS_UART_GCLK,
        PRCM_CLK_LCD_L3_GCLK,
        PRCM_CLK_LCD_L4S_GCLK,
        PRCM_CLK_LCD_GCLK,
        PRCM_CLK_CAN_CLK,
        PRCM_CLK_HDQ1W_GCLK,
        PRCM_CLK_I2C_FCLK,
        PRCM_CLK_MCASP_FCLK,
        PRCM_CLK_SPI_GCLK,
        PRCM_CLK_TIMER10_GCLK,
        PRCM_CLK_TIMER11_GCLK,
        PRCM_CLK_TIMER2_GCLK,
        PRCM_CLK_TIMER3_GCLK,
        PRCM_CLK_TIMER4_GCLK,
        PRCM_CLK_TIMER5_GCLK,
        PRCM_CLK_TIMER6_GCLK,
        PRCM_CLK_TIMER7_GCLK,
        PRCM_CLK_TIMER8_GCLK,
        PRCM_CLK_TIMER9_GCLK,
        PRCM_CLK_UART_GFCLK,
        PRCM_CLK_CPSW_CPTS_RFT_CLK,
        PRCM_CLK_CPSW_250MHZ_CLK,
        PRCM_CLK_CPSW_5MHZ_CLK,
        PRCM_CLK_CPSW_50MHZ_CLK,
        PRCM_CLK_CPSW_125MHZ_GCLK,
        PRCM_CLK_MPU_CLK,
        PRCM_CLK_OCPWP_L3_GCLK,
        PRCM_CLK_OCPWP_L4_GCLK_EN,
        PRCM_CLK_RTC_32KCLK,
        PRCM_CLK_L4_RTC_GCLK,
        PRCM_CLK_L4_WKUP_GCLK,
        PRCM_CLK_SR_SYSCLK,
        PRCM_CLK_USBPHY_32KHZ_GCLK,
        PRCM_CLK_WDT0_GCLK,
        PRCM_CLK_WDT1_GCLK,
        PRCM_CLK_TAMPER_L4_GCLK,
        PRCM_CLK_USB_OTG_SS_REFCLK,
        PRCM_CLK_USB_PLL_CLK,
        PRCM_CLK_USIM0_FCLK,
        PRCM_CLK_USIM0_FCLK32,
        PRCM_CLK_USIM1_FCLK,
        PRCM_CLK_USIM1_FCLK32,
        PRCM_CLK_L3S_TSC_GCLK,
        PRCM_CLK_ADC_FCLK,
        PRCM_CLK_24MHZ,
        PRCM_CLK_GPIO0_GDBCLK,
        PRCM_CLK_I2C0_GFCLK,
        PRCM_CLK_L4_WKUP_AON_GCLK,
        PRCM_CLK_SYNCTIMER32K_GFCLK,
        PRCM_CLK_TIMER0_GCLK,
        PRCM_CLK_TIMER1_GCLK,
        PRCM_CLK_UART0_GFCLK,
        PRCM_CLK_STD_EFUSE_SYSCLK,
        PRCM_CLK_MAX = PRCM_CLK_STD_EFUSE_SYSCLK,
        /*!< Enum Upper bound */
        PRCM_CLK_UNDEF = (UINT32_MAX),
        /*!< Undefined value */
    
    } prcmClockId_t;

    PRCMGetClockRate(CHIPDB_MOD_ID_PWMSS, 1, ?, read_var);

    I look forward to hearing from you.

    Thank you,

    Arshiya Tabassum

  • Hi Arshiya,

    Arshiya Tabassum said:
    can you please elaborate what it means? 

    Arshiya Tabassum said:
    Does that mean I need to use this API  shown below

    Your understanding is correct. You would use the PRCMGetClockRate() instead of the compile-time macro to obtain the PWMSS input clock frequency at run-time.

    Arshiya Tabassum said:
    is this the correct parameters I am passing for PWM instance 1 and getting the clock value in "read_var"

    Yes, but read_var should be passed as call-by-reference (address).

    Arshiya Tabassum said:
    What would be the third parameter here for PWM instance 1?

    Please see the AM335x TRM (spruh73q.pdf), Table 15-2. PWMSS Connectivity Attributes. In this table the input clock to PWMSS (OCP & functional) is listed as PD_PER_L4LS_GCLK, so I would use PRCM_CLK_L4LS_GCLK as the third parameter.

    Regards,
    Frank

  • Hi Frank,

    Thank you for your detailed explanation.  I appreciate it!

    1. I have a follow up question regarding the macro "PRCM_CLK_L4LS_GCLK " . This is defined in for am43xx , I couldn't find anything with the same name under starterware/includes/am335x path. 

    Am not sure if I need to use this for am335x, can you please verify? 

    2. Even if I assume this is right, and include the path of this hw_prcm.h (shown in the screenshot above) file, I am losing my prcm.h files and getting an error that PRCMGetClockRate() is undefined. Please let me know how to go about this.

    3. Lastly, can I use the compile time macro " #define SOC_EHRPWM_2_MODULE_FREQ      (100U * FREQ_MHZ)" to test my pwm in case I won't be able to use the function "PRCMGetClockRate()"? 

    Awaiting your response!

    Thank you!

  • Hi Arshiya,

    I searched AM335x & AM437x PRSDK 6.3, and it appears PRCMGetClockRate() has only been implemented for AM437x. So in principle this approach could be used for AM335x, but the software hasn't been fully implemented. Perhaps this is the reason for the "To Do" comment in the source code.

    Arshiya Tabassum said:
    Lastly, can I use the compile time macro " #define SOC_EHRPWM_2_MODULE_FREQ      (100U * FREQ_MHZ)" to test my pwm in case I won't be able to use the function "PRCMGetClockRate()"? 

    Yes, it should be possible. The value of the macro should be set for CORE_CLKOUTM4/2.

    Please see the following documentation in the AM335x TRM for details on CORE_CLKOUTM4

    • Figure 8-13: The Core PLL provides the source for a majority of the device infrastructure and peripheral clocks. The Core PLL comprises an ADPLLS with HSDIVIDER and additional dividers and muxes located in the PRCM
    • Table 8-22: Table 8-22 gives the typical PLL and clock frequencies. The HSDIVIDER is used to generate three divided clocks M4, M5 & M6. M4 & M5 are nominally 200 & 250 MHz, respectively.
    • 8.1.6.3 ADPLLS
    • 8.1.6.3.1 Clock Functions
    • 8.1.12.2.27 CM_CLKSEL_DPLL_CORE Register
    • 8.1.12.2.33 CM_DIV_M4_DPLL_CORE Register

    The GEL file included in CCS 9.3 for BBB sets CORE_CLKOUTM4 to 200 MHz.

    This clock can be observed on CLKOUT2, please see the following documentation in the AM335x TRM:

    • Figure 8-18. CLKOUT Signals
    • Figure 8-13. Core PLL (it appears L3F_CLK is CORE_CLKOUTM4)
    • For the ZCZ package, CLKOUT2 is output to ball D14 (be careful to take care of the PAD configuration).

    Regards,
    Frank

  • Hi  Frank,

    Thank you for the update. I was able to make the PWM work and move the motor from the PWM pulses.

    I have a question for you related to generating interrupts on PWM. I couln't find an example of epwm interrupt in the rtos package.

    I want to achieve the following: 

    Count the number of steps my motor has moved by counting the number of pulses of PWM. 

    How I am planning to acheive this uisng the epwm library (epwm.h file) is as follows(Q represent questions):

     Enable the Event trigger submodule by selecting the event source and period.

    Q.1. What would be my event and period if I want to count the number of pulses ?

    When the interrupt is generated, i want to go to the callback function and in that callback I want to keep a count of pulses.

    Q.2. How do I link the interrupt generation with my callback function?

    Q.3. Can I use the function EPWMEtIntrTrigger() to generate the interrupt on the event source and period that I have configured using the API EPWMEtIntrCfg(baseAddr, pPwm->etCfg.intrEvtSource, pPwm->etCfg.intrPrd);?

    Q.4. What are the steps to stop PWM pulses on EHRPWM1A or EHRPWM2A outputs? Just calling EPWMClockDisable() is enough?

    Thank you,

    Arshiya Tabassum

  • Hi Frank,

    I closed the DMTimer thread and asking the ePWM question here as suggested. 

    Thank you for your response and providing the detailed breakdown of timer API usage. I appreciate it!

    The only reason I was following the starterware example of DMTimer was because I wanted to see how to use the Interrupt Controller (AINTC) for generating interrupts for ePWM.

    Can you please if possible explain, how to generate interrupts on ePWM ?

    So far, I have followed the steps as below as directed in AM335x TRM section 15.2.2.2 

    1. Disable global interrupts - INTCDisableIntr()

    2. Disable ePWM interrupts - EPWMEtIntrDisable()

    3. Initialize peripheral registers - Followed ePWM example and initialized all necessary registers

    4. Clear any spurious ePWM flags - EPWMEtIntrClear()

    5. Enable ePWM interrupts - EPWMEtIntrEnable()

    6. Enable global interrupts - INTCEnableIntr()

    And, last but not the least, I have enabled the Event Trigger module of ePWM and configured the source as TBCNT=TBPRD event.

    I am using the API EPWMEtIntrStatus() to check the status and see that no interrupt is generated. 

    Please guide me to see what else I am missing to successfully generate interrupt on my ePWM. 

    Looking forward to your response.

    Thank you,

    Arshiya Tabassum

  • Hi Arshiya,

    For an RTOS application, you shouldn't use the Starterware INTC functions for configuring the Interrupt Controller.

    Please see <PDK>\packages\ti\starterware\examples\dcan\readme.txt:

    Configuring Interrupts
    -----------------------
    Major difference for running the StarterWare examples in the baremetal and RTOS environment is the way
    interrupts are configured and dispatched. StarterWare baremetal examples configure the interrupts
    using INTC and GIC modules. This need to be updated to use the RTOS APIs to add support for TI RTOS.
    TI OSAL library should be used to configure the interrupts. All the functions doing the interrupt
    configuration shall be updated to use OSAL library APIs.

    I suggest trying either of the following:

    #1: Use OSAL HWI API in <PDK>\packages\ti\osal\HwiP.h.
    Use API functions HwiP_Params_init(), HwiP_create() & HwiP_enableInterrupt().

    #2: Use SYS/BIOS HWI, see <BIOS>/docs/cdoc/ti/sysbios/hal/Hwi.html
    Use API functions Hwi_Params_init() & HWI_create (or Hwi_construct()) and Hwi_enableInterrupt().

    For either approach, the interrupt number should be set to the interrupt number for the ePWM instance listed in TRM, Table 6-1. ARM Cortex-A8 Interrupts.

    Otherwise your approach looks good. I would confirm:

    • the ePWM counter is running by inspecting TBCNT
    • the ePWM interrupt is enabled by inspecting ETSEL

    Let me know how it goes..

    Regards,
    Frank

  • Hi Frank,

    Thank you for your response. 

    I followed the first option, in the sequence of HwiP_Params_init(), HwiP_create() & HwiP_enableInterrupt(). 

    I am now able to generate PWM interrupt and I can count the motor steps.

    Thank you for your help!

    Arshiya Tabassum

  • Hi Arishiya,

    No problem, I'm glad you got it working!

    Regards,
    Frank