We deal with the PWM module on tms320

Good afternoon. Some time ago I read that some person wanted to study deeper the question about the ePWM module on the mk tms320f28xxx, so I decided why not write an article on this topic in which I will try to chew this module in detail using the example of tms320f28335 ...



EPWM module capabilities



  • EpwmA epwmB outputs can work as:

    • single-edge operation
    • dual-edge symmetric operation
    • dual edge asymmetric opperation
  • Dead time can be configured
  • The TZ event can be configured and the logical state of both HI and LO output can be set.
  • An interrupt event or SOC event for the ADC can be configured.


Let's get closer to the main blocks: what the ePWM module consists of and what it is connected to.







As you can see from the figure, there are not many blocks in the ePWM module. Therefore, it makes sense to consider what each module is responsible for and start with signals.



  • EPWMxA and EPWMxB signals are probably the most obvious output signal. Normal logic state is either HI or LO, depending on how the output action is configured
  • TZ1 — TZ6 — . , , , . , EPWMxA EPWMxB . , , - , , . . , .
  • EPWMxSYNCI EPWMxSYNCO — , , .
  • EPWMxSOCA and EPWMxSOCB signals - here everything is more than clear from the name. These events can set SOC events for the ADC.
  • EPWMxTZINT and EPWMxINT signals - here the interrupt event on TZ and on events related to the PWM itself, for example, generating an interrupt on the PWM period.


Now let's move on to the



Time base (TB) modules - the module is responsible for the event time of each ePWM module. We will not go into all the settings of this module, I think it is enough to pay attention to the fact that there are 3 modes of operation of the counter:



  • Up-Down-Count Mode
  • Up-Count Mode
  • Down-Count Mode






And also there is a timer synchronization setting by setting the TBCLKSYNC



Counter compare (CC) bit module - through it we just set our duty cycle.

Action-Qualifier (AQ) module - through it you can configure the state for an event. And for outputs, you can configure the following actions:



  • Set to HI state
  • Set to LO state
  • Perform state inversion
  • Nothing to do


Dead-Band Submodule (DB) module - this module can be used to set dead bands for PWM channels. It will not be a secret to anyone that the transistor keys do not switch instantly, and in order to avoid a situation when the upper half-bridge key does not have time to close, and the lower one is already open, they set a delay for switching to the HI state and earlier switching to the LO state.



Trip-Zone Submodule (TZ) module - as mentioned above, this module is associated with handling emergency conditions. Here we can choose 1 of 4 actions.



  • Set to HI state
  • Set to LO state
  • Set High-impedance state
  • Nothing to do


The event that triggers the action of the TZ module can be triggered both by software and hardware. In addition, an interrupt call is provided.



Now let's move from words to practice.



First, you need to configure GPIO for an alternative epwm function



EALLOW;
//  pull-up
GpioCtrlRegs.GPAPUD.bit.GPIO0 = 0x000;  
GpioCtrlRegs.GPAPUD.bit.GPIO1 = 0x000;  
//  GPIO   EPWM1A
GpioCtrlRegs.GPAMUX1.bit.GPIO0 = 0x001;   
GpioCtrlRegs.GPAMUX1.bit.GPIO1 = 0x001;  

EDIS;


Next, when we have already configured the GPIO, we can proceed with the different settings. To configure the PWM operation, you need to decide what we want to get. Let's start with the TBCLK frequency. It is determined by the formula:



TBCLK=SYSCLKOUT/(HSPCLKDIV×CLKDIV)



Here you need to pay attention to the fact that CLKDIV is 1 by default, with HSPCLKDIV everything is different, by default it is 2. This should be kept in mind, since there are times when people forget about it. When loading a program into RAM, often HSPCLKDIV = 1, respectively, this problem is not immediately noticed.



We have decided on the clock frequency of TBCLK. But we would have to choose how the counter will work for us. By recession, by increasing, and maybe this way and that, for this you need to configure the appropriate register, for example:



 EPwm1Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP;
 


In the future, so that no one is intimidated by macros, we will determine where they come from at all. These definitions are defined in a file called DSP2833x_EPwm_defines.h.



Then you need to decide how our GPIOs will react to reaching certain TBCTR values. There are more than enough options. They are shown in the table below:







Then you need to decide what behavior we want from ports A and B, namely, we want them to be connected to each other or to be able to work independently. If we want port A to be the master, then we simply write down actions for it, for example (case for counting up):



    EPwm1Regs.AQCTLA.bit.CAU = AQ_SET; 
    EPwm1Regs.AQCTLA.bit.CAD = AQ_CLEAR;


if we want independence for the second port, then add:



    EPwm1Regs.AQCTLA.bit.ZRO = AQ_SET;
    EPwm1Regs.AQCTLA.bit.CAU = AQ_CLEAR;
    EPwm1Regs.AQCTLB.bit.ZRO = AQ_SET;
    EPwm1Regs.AQCTLB.bit.CBU = AQ_CLEAR;


For more details on the settings, you need to refer to the image above for just one addition, there are a little more AQCTLA registers than shown in the table, this does not change the picture much, but only specifics are introduced regarding in which case the counter reached the desired value, for example, when counting in top or count down. More briefly about the bits can be found in the cut from the system .h file



struct AQCTL_BITS {            // bits   description
    Uint16 ZRO:2;              // 1:0    Action Counter = Zero
    Uint16 PRD:2;              // 3:2    Action Counter = Period
    Uint16 CAU:2;              // 5:4    Action Counter = Compare A up
    Uint16 CAD:2;              // 7:6    Action Counter = Compare A down
    Uint16 CBU:2;              // 9:8    Action Counter = Compare B up
    Uint16 CBD:2;              // 11:10  Action Counter = Compare B down
    Uint16 rsvd:4;             // 15:12  reserved
};


If we have 2 ePWM ports that work independently and we want to set the dead time, then we need to set the register to the desired state, for example:



EPwm1Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;


Now that we have decided on the description of the periphery, we can proceed to specific examples.



Configuring ePWM in Counting Mode



Here is an example without dead time and port A and port B work independently. When A is active, B is inactive.



	EPwm1Regs.TBPRD = 150000 / 5; 	// .  150 / 5000 
    //   50%
    EPwm1Regs.CMPA.half.CMPA = EPwm1Regs.TBPRD / 2;	
    EPwm1Regs.TBPHS.half.TBPHS = 0; 
    EPwm1Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP; 
    EPwm1Regs.TBCTL.bit.PHSEN = TB_DISABLE; 
    EPwm1Regs.TBCTL.bit.PRDLD = TB_SHADOW;
    EPwm1Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_DISABLE;
    EPwm1Regs.TBCTL.bit.HSPCLKDIV = 0;
    EPwm1Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
    EPwm1Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
    EPwm1Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO; 
    EPwm1Regs.CMPCTL.bit.LOADBMODE = CC_CTR_ZERO; 
    EPwm1Regs.AQCTLA.bit.PRD = AQ_CLEAR;
    EPwm1Regs.AQCTLA.bit.CAU = AQ_SET;
    EPwm1Regs.AQCTLB.bit.PRD = AQ_SET;
    EPwm1Regs.AQCTLB.bit.CAU = AQ_CLEAR;


On the oscillogram you can see the result obtained:







Now you can try adding dead time, for this we add:



    EPwm1Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;
    EPwm1Regs.DBCTL.all = BP_ENABLE + POLSEL_ACTIVE_HI_CMP;	//  db 
    EPwm1Regs.DBFED = 300;	//   = 150 * 2 = 300
    EPwm1Regs.DBRED = 300; 


The dead time is counted in the same way as the frequency, according to the formula:

DB=TBCLKdeadTime;



And now we got dead time just the way we wanted it.







What if we need to untie port A and port B? This also takes place. Everything is simple here. We return to the first example and delete the last 4 lines, and write each duty cycle in the following registers.



    EPwm1Regs.AQCTLA.bit.ZRO = AQ_SET;
    EPwm1Regs.AQCTLA.bit.CAU = AQ_CLEAR;
    EPwm1Regs.AQCTLB.bit.ZRO = AQ_SET;
    EPwm1Regs.AQCTLB.bit.CBU = AQ_CLEAR;
    EPwm1Regs.CMPA.half.CMPA 	= EPwm1Regs.TBPRD / 2;	//   50%  
    EPwm1Regs.CMPB	= EPwm1Regs.TBPRD / 3;	//   33%  


Now we have such a picture. You can set the duty cycle for each channel separately.







For the decay mode, everything is approximately the same. There is a difference with the countdown in the up-down mode. Here the shim frequency is calculated using the formula:

TBPRD=TBCLK/(2Fpwm)



The same is true for dead Time.



Probably the only important thing that was not considered is the TZ setting, well, now let's dwell on this module in a little more detail.



To programmatically trigger an alarm event, it is enough to configure the following registers:



    EPwm1Regs.TZCTL.bit.TZA = TZ_FORCE_LO;
    EPwm1Regs.TZCTL.bit.TZB = TZ_FORCE_LO;


The PWM alarm can be called up and reset using the following commands:



    // 
    EALLOW;
        EPwm1Regs.TZFRC.bit.OST = 0x001;
    EDIS;
	
    //  
    EALLOW;
	EPwm1Regs.TZCLR.bit.OST = 0x0001;
    EDIS;


If we want to call the TZ signal in hardware, then everything is even easier, through the TZSEL register we set the TZ we need, but in addition to this, it is necessary to configure the GPIO to TZ.



Conclusion



If someone finds this article interesting, then I can write a couple more articles in a more or less accelerated manner. I plan to consider the can module, I would like to dma, and maybe I will also write a small article on IQMath from ti with their libraries.



All Articles