|
|
# ePWM Generation, Interrupts, and Trip-Zone Protection on the TMS320F28379D
|
|
|
|
|
|
This project demonstrates how to configure the enhanced Pulse Width Modulator (**ePWM**) peripheral of the Texas Instruments **TMS320F28379D** using two register-access methods. The implementation uses **CPU1 with C2000 bit-field structures** and **CPU2 with `HWREG()` / `HWREGH()` register access from driverlib**.
|
|
|
|
|
|
The guide follows an incremental approach: begin with an almost-empty program, find the relevant peripheral registers in C2000Ware, configure one submodule at a time, and verify each result with an oscilloscope. The supplied source files serve as the initial examples; any identified concerns are documented at the end rather than being changed without notice.
|
|
|
|
|
|
# 1. Hardware and software requirements
|
|
|
|
|
|
- TMS320F28379D microcontroller or compatible board (confirm that the listed GPIOs are exposed); we are using the `LAUNCHXL-F28379D` board development kit.
|
|
|
- Code Composer Studio (CCS), version 20.5.0 or above.
|
|
|
- C2000Ware and its device-support headers and/or driverlib, or previously installed the `C2000Ware_26_00_00_00`.
|
|
|
- Oscilloscope or logic analyzer to observe PWM and diagnostic GPIOs.
|
|
|
- A safe logic-level input or suitable test switch for trip-zone experiments.
|
|
|
- Initially **do not connect the PWM signals to power semiconductor gates**; verify the outputs and fault response at logic level.
|
|
|
|
|
|
**Timing assumption:** the examples configure an ePWM module input clock of **100 MHz** and the time-base clock (`TBCLK`) of **50 MHz**. The device system clock, `EPWMCLKDIV`, `HSPCLKDIV`, and `CLKDIV` must agree with these assumptions.
|
|
|
|
|
|
# 2. Project objectives and expected behavior
|
|
|
|
|
|
The two source files are `pwm_cpu1_bitfield.c` and `pwm_cpu2_driverlib.c`.
|
|
|
|
|
|
| CPU | ePWM | Outputs | Counter mode | Intended PWM frequency | Initial comparison |
|
|
|
|---|---|---|---|---|---|
|
|
|
| CPU1 | ePWM1 | GPIO0 / GPIO1 | Up-count (sawtooth counter) | 10 kHz | `CMPA = 1999`, approximately 40% |
|
|
|
| CPU1 | ePWM2 | GPIO2 / GPIO3 | Up-down (triangular counter) | 5 kHz | `CMPA = 1750`, parameter `m = -0.3` |
|
|
|
| CPU2 | ePWM4 | GPIO6 / GPIO7 | Up-count (sawtooth counter) | approximately 15 kHz | `CMPA = 1999`, approximately 60% |
|
|
|
| CPU2 | ePWM5 | GPIO8 / GPIO9 | Up-down (triangular counter) | 4 kHz | code uses `m = +0.7`, subject to review |
|
|
|
|
|
|
Additional tasks:
|
|
|
|
|
|
- **ePWM1 interrupt:** intended at `TBCTR = 0`, once per PWM cycle; toggle **GPIO22**.
|
|
|
- **ePWM5 interrupt:** intended at `TBCTR = 0` **and** `TBCTR = TBPRD`, twice per PWM cycle; toggle **GPIO52**.
|
|
|
- **Trip-zone input:** GPIO97 is routed through Input X-BAR 1 as `TZ1` to request a one-shot shutdown of ePWM1, ePWM2, ePWM4, and ePWM5 outputs.
|
|
|
- **Trip-zone ISRs:** CPU1 receives ePWM1/ePWM2 trip interrupts; CPU2 receives ePWM4/ePWM5 trip interrupts.
|
|
|
|
|
|
The output pairs use the dead-band submodule. The two signals must be checked on the oscilloscope: *do not assume complementary outputs are safe for a power stage without measuring the dead time and fault behavior*.
|
|
|
|
|
|
# 3. General program flow
|
|
|
|
|
|
## CPU1
|
|
|
|
|
|
1. Initialize system clocks and the interrupt controller.
|
|
|
2. Select ePWM clock division and assign peripheral ownership across CPU1/CPU2.
|
|
|
3. Enable the clock gates for ePWM1/ePWM2.
|
|
|
4. Disable `TBCLKSYNC` while preparing the time-base counters.
|
|
|
5. Configure time-base (`TB`), counter-compare (`CC`), action qualifier (`AQ`), and dead-band (`DB`).
|
|
|
6. Configure event trigger (`ET`) for ePWM1 and trip zone (`TZ`) for ePWM1/ePWM2.
|
|
|
7. Register the ePWM and trip-zone interrupt service routines.
|
|
|
8. Configure GPIO0–GPIO3, diagnostic GPIO22, the GPIO52 ownership, and Input X-BAR from GPIO97.
|
|
|
9. Clear initial interrupt/fault flags as appropriate **only after confirming the fault is inactive**.
|
|
|
10. Enable synchronization after **both CPUs are ready**, then enter the main loop.
|
|
|
|
|
|
## CPU2
|
|
|
|
|
|
1. Initialize the interrupt controller and vector table.
|
|
|
2. Enable the clock gates for ePWM4/ePWM5 on CPU2.
|
|
|
3. Configure `TB`, `CC`, `AQ`, and `DB` with direct register operations.
|
|
|
4. Configure the ePWM5 event-trigger interrupt and the ePWM4/ePWM5 trip zones.
|
|
|
5. Register and enable the CPU2 interrupt paths.
|
|
|
6. Signal configuration readiness to CPU1 using an explicit interprocessor synchronization mechanism.
|
|
|
7. Enter the main loop.
|
|
|
|
|
|
**Dual-core note:** CPU1 controls cross-CPU ownership selection and shared startup coordination. Independent writes to `TBCLKSYNC` from both programs do not constitute a reliable synchronization protocol.
|
|
|
|
|
|
# 4.0 Create a dual CPU project ready for bitfield and driverlib
|
|
|
1. Open a new folder (5-epwm-example); not a workspace
|
|
|
2. Import project from `C2000Ware_26_01_00_00`: `driverlib/f2837xd/examples/dual/empty_projects/css`
|
|
|
3. Change folder/project names
|
|
|
- `pwm_cpu1_bitfield.`
|
|
|
- `pwm_cpu2_driverlib`
|
|
|
4. Change the `.c` files also; follow the same approach
|
|
|
5. Bitfield project needs some additional compilation variables
|
|
|
- Path type var named `C2000_Common` points to `/Users/gmarx/ti/C2000Ware_26_01_00_00/device_support/f2837xd/common/include`
|
|
|
- Path type var named `C2000_Headers` pointing to `/Users/gmarx/ti/C2000Ware_26_01_00_00/device_support/f2837xd/headers/include`
|
|
|
6. Also, in `C2000 Compiler` and `Include Options`, we should add the previously created vars as:
|
|
|
- `${C2000_Common}`
|
|
|
- `${C2000_Headers}`
|
|
|
7. Additionally, at `C2000 Compiler` and `Predefined Symbols`, add the entry:
|
|
|
- `_DUAL_HEADERS` and `_LAUNCHXL_F28379D`.
|
|
|
8. Additional source files must be added from `/Users/gmarx/ti/C2000Ware_26_00_00_00/device_support/f2837xd/common/source`:
|
|
|
- `F2837xD_SysCtrl.c`
|
|
|
- `F2837xD_usDelay.asm`
|
|
|
- `F2837xD_EPwm.c`
|
|
|
9. Add the file `F2837xD_GlobalVariableDefs.c`-data structure to map all peripherals- that is at `/Users/gmarx/ti/C2000Ware_26_00_00_00/device_support/f2837xd/headers/source/F2837xD_GlobalVariableDefs.c`
|
|
|
10. One last file must be included: `F2837xD_Headers_nonBIOS_cpu1.cmd` from the path `/Users/gmarx/ti/C2000Ware_26_00_00_00/device_support/f2837xd/headers/cmd/F2837xD_Headers_nonBIOS_cpu1.cmd` **we are using the nonBIOS option because the code will run in debug mode**
|
|
|
11. However, the CPU2 has an issue at the `Predefined symbols`, we must change the name `CPU1` to `CPU2`
|
|
|
|
|
|
|
|
|
- From the 12 PWMs, only 6 are available
|
|
|
- PWM1 (PWM1A and PWM1B) and PWM2 will be used for CPU1
|
|
|
- PWM4 and PWM5 will be used for CPU2
|
|
|

|
|
|
|
|
|
# 4. Start from an almost-empty CPU1 program (bit-field approach)
|
|
|
|
|
|
```c
|
|
|
#include "F28x_Project.h"
|
|
|
#include "driverlib.h"
|
|
|
#include "device.h"
|
|
|
|
|
|
__interrupt void epwm1_isr(void) { /* TODO */ }
|
|
|
__interrupt void epwm1_trip_isr(void) { /* TODO */ }
|
|
|
__interrupt void epwm2_trip_isr(void) { /* TODO */ }
|
|
|
|
|
|
void main(void)
|
|
|
{
|
|
|
// 1. Initialize the system and interrupts.
|
|
|
// 2. Select ePWM clocks and assign CPU ownership.
|
|
|
// 3. Enable ePWM1 and ePWM2 clocks.
|
|
|
// 4. Stop time-base synchronization.
|
|
|
// 5. Configure TB and CC.
|
|
|
// 6. Configure AQ and dead band.
|
|
|
// 7. Configure event triggers and trip zones.
|
|
|
// 8. Register and enable interrupts.
|
|
|
// 9. Configure PWM GPIOs, diagnostic pins, and X-BAR.
|
|
|
// 10. Confirm fault conditions and synchronize startup.
|
|
|
for (;;) {}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
The following sections progressively fill in these steps. Register names are those used in the **provided source**.
|
|
|
|
|
|
**Note: we are in video ePWM part 2**
|
|
|
|
|
|
# 5 CPU1 code
|
|
|
|
|
|
## initial steps
|
|
|
- `CLK_CFG_REGS` Registers (section 3.17.11)-> PERCLKDIVSEL -> F2837xD_sysctrl.h
|
|
|
- `DEV_CFG_REGS` (section 3.17.10), same file
|
|
|
- `CPU_SYS_REGS` (section 3.17.12) -> PCLKCR2 register has the pwm bits -> F2837xD_sysctrl.h -> search for the struct `CPU_SYS_REGS `
|
|
|
- `TBCLKSYNC` bit
|
|
|
- `EPWM_REGS` Registers (section 15.15.2)
|
|
|
|
|
|
```c
|
|
|
#include "F28x_Project.h"
|
|
|
#include "driverlib.h"
|
|
|
#include "device.h"
|
|
|
|
|
|
__interrupt void epwm1_isr(void) { /* TODO */ }
|
|
|
__interrupt void epwm1_trip_isr(void) { /* TODO */ }
|
|
|
__interrupt void epwm2_trip_isr(void) { /* TODO */ }
|
|
|
|
|
|
void main(void)
|
|
|
{
|
|
|
// 1. Initialize the system and interrupts.
|
|
|
InitSysCtrl();
|
|
|
EALLOW;
|
|
|
ClkCfgRegs.PERCLKDIVSEL.bit.EPWMCLKDIV = 1;
|
|
|
EDIS;
|
|
|
|
|
|
// 2. Select ePWM clocks and assign CPU ownership.
|
|
|
EALLOW;
|
|
|
DevCfgRegs.CPUSEL0.bit.EPWM1 = 0; //cpu0
|
|
|
DevCfgRegs.CPUSEL0.bit.EPWM2 = 0;
|
|
|
DevCfgRegs.CPUSEL0.bit.EPWM4 = 1; //cpu1
|
|
|
DevCfgRegs.CPUSEL0.bit.EPWM5 = 1;
|
|
|
EDIS;
|
|
|
|
|
|
// 3. Enable ePWM1 and ePWM2 clocks.
|
|
|
EALLOW;
|
|
|
CpuSysRegs.PCLKCR2.bit.EPWM1 = 1;
|
|
|
CpuSysRegs.PCLKCR2.bit.EPWM2 = 1;
|
|
|
EDIS;
|
|
|
|
|
|
// 4. Stop time-base synchronization.
|
|
|
EALLOW;
|
|
|
CpuSysRegs.PCLKCR0.bit.TBCLKSYNC = 1; //stop the timer
|
|
|
EDIS;
|
|
|
|
|
|
// 5. Configure TB and CC.
|
|
|
// Configure the eWM modules 1 and 2
|
|
|
// Time base sub-module
|
|
|
// 100 MHz. TBCLK = 100 / (2 * 1) = 50 Mhz
|
|
|
EPwm1Regs.TBCTL.bit.FREE_SOFT = 2; // 2 or 3
|
|
|
EPwm1Regs.TBCTL.bit.CLKDIV = 0; I
|
|
|
EPwm1Regs.TBCTL.bit.HSPCLKDIV = 1;
|
|
|
EPwm1Regs.TBCTL.bit.PRDLD = 0;
|
|
|
EPwm1Regs.TBCTL.bit.CTRMODE = 0;
|
|
|
|
|
|
EPwm2Regs.TBCTL.bit.FREE_SOFT = 2; // 2 or 3
|
|
|
EPwm2Regs.TBCTL.bit.CLKDIV = 0; I
|
|
|
EPwm2Regs.TBCTL.bit.HSPCLKDIV = 1;
|
|
|
EPwm2Regs.TBCTL.bit.PRDLD = 0;
|
|
|
EPwm2Regs.TBCTL.bit.CTRMODE = 2; // up-down count mode
|
|
|
|
|
|
//Period registers
|
|
|
EPwm1Regs. TBPRD = 5000 - 1; // 10 kHz sawtooth
|
|
|
EPwm2Regs. TBPRD = 5000; // 5 kHz triangular
|
|
|
|
|
|
// Counter-compare sub-module
|
|
|
EPwm1Regs.CMPCTL.bit.SHDWAMODE = 0;
|
|
|
EPwm1Regs.CMPCTL.bit.LOADAMODE = 0;
|
|
|
|
|
|
EPwm1Regs.CMPA.bit.CMPA = 1999;
|
|
|
|
|
|
EPwm2Regs.CMPCTL.bit.SHDWAMODE = 0;
|
|
|
EPwm2Regs.CMPCTL.bit.LOADAMODE = 0;
|
|
|
|
|
|
EPwm2Regs. CMPA.bit. CMPA = (uint16_t) ( ((-0.3+1)/2.0) * 5000 );
|
|
|
|
|
|
// 6. Configure AQ and dead band.
|
|
|
|
|
|
// Action qualifier sub-module
|
|
|
// ePWM1 - A output to be fed to the upper device of a leg
|
|
|
// B output to be fed to the lower device of a leg
|
|
|
// TBCTR = 0, A will be high, B will be low (ZRO - AQCTLA/B)
|
|
|
// TBCTR = CMPA. A will be low and B will be high (CAU - AQCTLA/B)
|
|
|
EPwm1Regs. AQCTLA.bit.ZRO = 2;
|
|
|
EPwm1Regs .AQCTLB.bit.ZRO = 1;
|
|
|
EPwm1Regs .AQCTLA.bit.CAU = 1;
|
|
|
EPwm1Regs.AQCTLB.bit.CAU = 2;
|
|
|
|
|
|
// ePWM2 - A output to be fed to the upper device of a leg
|
|
|
// B output to be fed to the lower device of a leg
|
|
|
// TBCTR = CMPA first half, A will be high, B will be low (CAU - AQCTLA/B)
|
|
|
// TBCTR = CMPA second half. A will be low and B will be high (CAD - AQCTLA/B)
|
|
|
|
|
|
EPwm2Regs. AQCTLA.bit.ZROI = 2;
|
|
|
EPwm2Regs .AQCTLB.bit.ZRO = 1;
|
|
|
EPwm2Regs. AQCTLA.bit.CAU = 1;
|
|
|
EPwm2Regs. AQCTLB.bit.CAU = 2;
|
|
|
// 7. Configure event triggers and trip zones.
|
|
|
// 8. Register and enable interrupts.
|
|
|
// 9. Configure PWM GPIOs, diagnostic pins, and X-BAR.
|
|
|
// 10. Confirm fault conditions and synchronize startup.
|
|
|
for (;;) {}
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
## Configuring actions
|
|
|
|
|
|
```c
|
|
|
#include "F28x_Project.h"
|
|
|
#include "driverlib.h"
|
|
|
#include "device.h"
|
|
|
|
|
|
__interrupt void epwm1_isr(void) { /* TODO */ }
|
|
|
__interrupt void epwm1_trip_isr(void) { /* TODO */ }
|
|
|
__interrupt void epwm2_trip_isr(void) { /* TODO */ }
|
|
|
|
|
|
void main(void)
|
|
|
{
|
|
|
// 1. Initialize the system and interrupts.
|
|
|
InitSysCtrl();
|
|
|
EALLOW;
|
|
|
ClkCfgRegs.PERCLKDIVSEL.bit.EPWMCLKDIV = 1;
|
|
|
EDIS;
|
|
|
|
|
|
// 2. Select ePWM clocks and assign CPU ownership.
|
|
|
EALLOW;
|
|
|
DevCfgRegs.CPUSEL0.bit.EPWM1 = 0; //cpu0
|
|
|
DevCfgRegs.CPUSEL0.bit.EPWM2 = 0;
|
|
|
DevCfgRegs.CPUSEL0.bit.EPWM4 = 1; //cpu1
|
|
|
DevCfgRegs.CPUSEL0.bit.EPWM5 = 1;
|
|
|
EDIS;
|
|
|
|
|
|
// 3. Enable ePWM1 and ePWM2 clocks.
|
|
|
EALLOW;
|
|
|
CpuSysRegs.PCLKCR2.bit.EPWM1 = 1;
|
|
|
CpuSysRegs.PCLKCR2.bit.EPWM2 = 1;
|
|
|
EDIS;
|
|
|
|
|
|
// 4. Stop time-base synchronization.
|
|
|
EALLOW;
|
|
|
CpuSysRegs.PCLKCR0.bit.TBCLKSYNC = 1; //stop the timer
|
|
|
EDIS;
|
|
|
|
|
|
// 5. Configure TB and CC.
|
|
|
// Configure the eWM modules 1 and 2
|
|
|
// Time base sub-module
|
|
|
// 100 MHz. TBCLK = 100 / (2 * 1) = 50 Mhz
|
|
|
EPwm1Regs.TBCTL.bit.FREE_SOFT = 2; // 2 or 3
|
|
|
EPwm1Regs.TBCTL.bit.CLKDIV = 0; I
|
|
|
EPwm1Regs.TBCTL.bit.HSPCLKDIV = 1;
|
|
|
EPwm1Regs.TBCTL.bit.PRDLD = 0;
|
|
|
EPwm1Regs.TBCTL.bit.CTRMODE = 0;
|
|
|
|
|
|
EPwm2Regs.TBCTL.bit.FREE_SOFT = 2; // 2 or 3
|
|
|
EPwm2Regs.TBCTL.bit.CLKDIV = 0; I
|
|
|
EPwm2Regs.TBCTL.bit.HSPCLKDIV = 1;
|
|
|
EPwm2Regs.TBCTL.bit.PRDLD = 0;
|
|
|
EPwm2Regs.TBCTL.bit.CTRMODE = 2; // up-down count mode
|
|
|
|
|
|
//Period registers
|
|
|
EPwm1Regs. TBPRD = 5000 - 1; // 10 kHz sawtooth
|
|
|
EPwm2Regs. TBPRD = 5000; // 5 kHz triangular
|
|
|
|
|
|
// Counter-compare sub-module
|
|
|
EPwm1Regs.CMPCTL.bit.SHDWAMODE = 0;
|
|
|
EPwm1Regs.CMPCTL.bit.LOADAMODE = 0;
|
|
|
|
|
|
EPwm1Regs.CMPA.bit.CMPA = 1999;
|
|
|
|
|
|
EPwm2Regs.CMPCTL.bit.SHDWAMODE = 0;
|
|
|
EPwm2Regs.CMPCTL.bit.LOADAMODE = 0;
|
|
|
|
|
|
EPwm2Regs. CMPA.bit. CMPA = (uint16_t) ( ((-0.3+1)/2.0) * 5000 );
|
|
|
|
|
|
// 6. Configure AQ and dead band.
|
|
|
|
|
|
// Action qualifier sub-module
|
|
|
// ePWM1 - A output to be fed to the upper device of a leg
|
|
|
// B output to be fed to the lower device of a leg
|
|
|
// TBCTR = 0, A will be high, B will be low (ZRO - AQCTLA/B)
|
|
|
// TBCTR = CMPA. A will be low and B will be high (CAU - AQCTLA/B)
|
|
|
EPwm1Regs. AQCTLA.bit.ZRO = 2;
|
|
|
EPwm1Regs .AQCTLB.bit.ZRO = 1;
|
|
|
EPwm1Regs .AQCTLA.bit.CAU = 1;
|
|
|
EPwm1Regs.AQCTLB.bit.CAU = 2;
|
|
|
|
|
|
// ePWM2 - A output to be fed to the upper device of a leg
|
|
|
// B output to be fed to the lower device of a leg
|
|
|
// TBCTR = CMPA first half, A will be high, B will be low (CAU - AQCTLA/B)
|
|
|
// TBCTR = CMPA second half. A will be low and B will be high (CAD - AQCTLA/B)
|
|
|
|
|
|
EPwm2Regs. AQCTLA.bit.ZROI = 2;
|
|
|
EPwm2Regs .AQCTLB.bit.ZRO = 1;
|
|
|
EPwm2Regs. AQCTLA.bit.CAU = 1;
|
|
|
EPwm2Regs. AQCTLB.bit.CAU = 2;
|
|
|
// 7. Configure event triggers and trip zones.
|
|
|
// GPIO Muxed Pins table
|
|
|
// Configure GPIO0 to GPIO3 as ePWM functionality
|
|
|
EALLOW;
|
|
|
GpioCtr1Regs.GPAGMUX1.bit.GPI00 = 0;
|
|
|
GpioCtr1Regs.GPAGMUX1.bit.GPI01 = 0;
|
|
|
GpioCtr1Regs.GPAGMUX1.bit.GPI02 = 0;
|
|
|
GpioCtr1Regs.GPAGMUX1.bit.GPI03 = 0;
|
|
|
GpioCtrIRegs.GPAMUX1.bit.GPI00 = 1;
|
|
|
GpioCtr1Regs.GPAMUX1.bit.GPI01 = 1;
|
|
|
GpioCtr1Regs.GPAMUX1.bit.GPI02 = 1;
|
|
|
GpioCtriRegs. GPAMUX1.bit.GPI03 = 1;
|
|
|
EDIS;
|
|
|
|
|
|
// 8. Register and enable interrupts.
|
|
|
// 9. Configure PWM GPIOs, diagnostic pins, and X-BAR.
|
|
|
// 10. Confirm fault conditions and synchronize startup.
|
|
|
// Start the timer counters for ePWM 1 and 2
|
|
|
EALLOW;
|
|
|
EDIS;
|
|
|
CpuSysRegs.PCLKCRO.bit.
|
|
|
|
|
|
for (;;) {}
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
## 5. CPU1: system initialization and peripheral ownership
|
|
|
|
|
|
The provided source initializes the system and interrupt tables:
|
|
|
|
|
|
```c
|
|
|
InitSysCtrl(); // <---
|
|
|
InitPieCtrl();
|
|
|
IER = 0x0000;
|
|
|
IFR = 0x0000;
|
|
|
InitPieVectTable();
|
|
|
```
|
|
|
|
|
|
The CPU1 program sets the ePWM module input clock divisor and allocates the peripherals —CLK_CFG_REGS Registers -> PERCLKDIVSEL -> F2837xD_sysctrl.h—:
|
|
|
|
|
|
```c
|
|
|
EALLOW;
|
|
|
ClkCfgRegs.PERCLKDIVSEL.bit.EPWMCLKDIV = 1; // <---
|
|
|
DevCfgRegs.CPUSEL0.bit.EPWM1 = 0;
|
|
|
DevCfgRegs.CPUSEL0.bit.EPWM2 = 0;
|
|
|
DevCfgRegs.CPUSEL0.bit.EPWM4 = 1;
|
|
|
DevCfgRegs.CPUSEL0.bit.EPWM5 = 1;
|
|
|
EDIS;
|
|
|
```
|
|
|
|
|
|
- `0` indicates CPU1 ownership and `1` indicates CPU2 ownership in these fields.
|
|
|
- Establish peripheral ownership **before CPU2 configures its modules**.
|
|
|
- Check the resulting `EPWMCLK` from the actual system clock and clock-divider settings.
|
|
|
|
|
|
Enable the CPU1 modules and stop time-base synchronization during setup:
|
|
|
|
|
|
```c
|
|
|
EALLOW;
|
|
|
CpuSysRegs.PCLKCR2.bit.EPWM1 = 1;
|
|
|
CpuSysRegs.PCLKCR2.bit.EPWM2 = 1;
|
|
|
CpuSysRegs.PCLKCR0.bit.TBCLKSYNC = 0;
|
|
|
EDIS;
|
|
|
```
|
|
|
|
|
|
**Register research:** locate the definitions of `CPUSEL0`, `PCLKCR2`, `PCLKCR0`, and `PERCLKDIVSEL` in the F2837xD device headers. Record their bit fields and the clock/ownership dependencies.
|
|
|
|
|
|
## 6. CPU1: time-base submodule (TB)
|
|
|
|
|
|
The time-base submodule selects counter mode and defines the switching period.
|
|
|
|
|
|
```c
|
|
|
// ePWM1: up-count
|
|
|
EPwm1Regs.TBCTL.bit.FREE_SOFT = 2;
|
|
|
EPwm1Regs.TBCTL.bit.CLKDIV = 0;
|
|
|
EPwm1Regs.TBCTL.bit.HSPCLKDIV = 1;
|
|
|
EPwm1Regs.TBCTL.bit.PRDLD = 0;
|
|
|
EPwm1Regs.TBCTL.bit.CTRMODE = 0;
|
|
|
EPwm1Regs.TBPRD = 5000 - 1;
|
|
|
|
|
|
// ePWM2: up-down count
|
|
|
EPwm2Regs.TBCTL.bit.FREE_SOFT = 2;
|
|
|
EPwm2Regs.TBCTL.bit.CLKDIV = 0;
|
|
|
EPwm2Regs.TBCTL.bit.HSPCLKDIV = 1;
|
|
|
EPwm2Regs.TBCTL.bit.PRDLD = 0;
|
|
|
EPwm2Regs.TBCTL.bit.CTRMODE = 2;
|
|
|
EPwm2Regs.TBPRD = 5000;
|
|
|
```
|
|
|
|
|
|
With the assumed clock:
|
|
|
|
|
|
\[
|
|
|
f_{TBCLK}=\frac{100\ \mathrm{MHz}}{2\cdot1}=50\ \mathrm{MHz}.
|
|
|
\]
|
|
|
|
|
|
For up-count mode, using a zero-based period register:
|
|
|
|
|
|
\[
|
|
|
f_{PWM}\approx\frac{f_{TBCLK}}{TBPRD+1}.
|
|
|
\]
|
|
|
|
|
|
For up-down mode:
|
|
|
|
|
|
\[
|
|
|
f_{PWM}=\frac{f_{TBCLK}}{2TBPRD}.
|
|
|
\]
|
|
|
|
|
|
Therefore:
|
|
|
|
|
|
- ePWM1: `TBPRD = 4999` → **10 kHz**.
|
|
|
- ePWM2: `TBPRD = 5000` → **5 kHz**.
|
|
|
|
|
|
**Exercise:** use the oscilloscope to compare the PWM periods of the up-count and up-down examples. Explain why an up-down counter requires twice as many `TBCLK` ticks for a complete PWM period.
|
|
|
|
|
|
## 7. CPU1: counter-compare submodule (CC)
|
|
|
|
|
|
The compare register determines the positions of edges generated by the action qualifier.
|
|
|
|
|
|
```c
|
|
|
// ePWM1
|
|
|
EPwm1Regs.CMPCTL.bit.SHDWAMODE = 0;
|
|
|
EPwm1Regs.CMPCTL.bit.LOADAMODE = 0;
|
|
|
EPwm1Regs.CMPA.bit.CMPA = 1999;
|
|
|
|
|
|
// ePWM2
|
|
|
EPwm2Regs.CMPCTL.bit.SHDWAMODE = 0;
|
|
|
EPwm2Regs.CMPCTL.bit.LOADAMODE = 0;
|
|
|
EPwm2Regs.CMPA.bit.CMPA =
|
|
|
(uint16_t)(((-0.3 + 1.0) / 2.0) * 5000);
|
|
|
```
|
|
|
|
|
|
For the *pre-dead-band ePWM1 A waveform* with high at zero and low at `CAU`, a useful nominal approximation is:
|
|
|
|
|
|
\[
|
|
|
D\approx\frac{CMPA}{TBPRD+1}=\frac{1999}{5000}\approx 0.40.
|
|
|
\]
|
|
|
|
|
|
The ePWM2 source defines an **initial modulation parameter**, not a continuously varying modulation signal:
|
|
|
|
|
|
\[
|
|
|
CMPA=\frac{m+1}{2}TBPRD,
|
|
|
\qquad m=-0.3\Rightarrow CMPA=1750.
|
|
|
\]
|
|
|
|
|
|
The requested modulation range `-0.97` to `+0.97` appears in the source-file comments, but **the supplied example does not implement a time-varying modulation algorithm**. Students can later update `CMPA` periodically to implement one.
|
|
|
|
|
|
**Exercise:** derive `CMPA` for `m = 0`, `m = +0.5`, and `m = -0.5`. Explain the waveform using the action-qualifier edge definitions; do not assume the modulation parameter is numerically equal to the final PWM duty cycle.
|
|
|
|
|
|
## 8. CPU1: action-qualifier submodule (AQ)
|
|
|
|
|
|
The action qualifier converts timer events into logic-level transitions.
|
|
|
|
|
|
| Event | Description | Relevant fields |
|
|
|
|---|---|---|
|
|
|
| `ZRO` | Counter reaches zero | `AQCTLA.ZRO`, `AQCTLB.ZRO` |
|
|
|
| `CAU` | Up-count reaches compare A | `AQCTLA.CAU`, `AQCTLB.CAU` |
|
|
|
| `CAD` | Down-count reaches compare A | `AQCTLA.CAD`, `AQCTLB.CAD` |
|
|
|
|
|
|
```c
|
|
|
// ePWM1: initial high at zero, low at up-count CMPA
|
|
|
EPwm1Regs.AQCTLA.bit.ZRO = 2;
|
|
|
EPwm1Regs.AQCTLB.bit.ZRO = 2;
|
|
|
EPwm1Regs.AQCTLA.bit.CAU = 1;
|
|
|
EPwm1Regs.AQCTLB.bit.CAU = 1;
|
|
|
|
|
|
// ePWM2: high on up CMPA, low on down CMPA
|
|
|
EPwm2Regs.AQCTLA.bit.CAU = 2;
|
|
|
EPwm2Regs.AQCTLB.bit.CAU = 2;
|
|
|
EPwm2Regs.AQCTLA.bit.CAD = 1;
|
|
|
EPwm2Regs.AQCTLB.bit.CAD = 1;
|
|
|
```
|
|
|
|
|
|
The source configures both **pre-dead-band A and B** paths with matching actions; the desired output polarity is subsequently generated using the dead-band polarity selection. Distinguish between *signals before dead band* and *physical ePWMxA/ePWMxB pins*.
|
|
|
|
|
|
## 9. CPU1: dead-band submodule (DB)
|
|
|
|
|
|
Dead band introduces timing separation between switching transitions. The supplied code selects two delay generators and a polarity arrangement:
|
|
|
|
|
|
```c
|
|
|
EPwm1Regs.DBCTL.bit.IN_MODE = 1;
|
|
|
EPwm1Regs.DBCTL.bit.POLSEL = 2;
|
|
|
EPwm1Regs.DBCTL.bit.OUT_MODE = 3;
|
|
|
EPwm1Regs.DBRED.all = 100;
|
|
|
EPwm1Regs.DBFED.all = 100;
|
|
|
|
|
|
EPwm2Regs.DBCTL.bit.IN_MODE = 1;
|
|
|
EPwm2Regs.DBCTL.bit.POLSEL = 2;
|
|
|
EPwm2Regs.DBCTL.bit.OUT_MODE = 3;
|
|
|
EPwm2Regs.DBRED.all = 250;
|
|
|
EPwm2Regs.DBFED.all = 250;
|
|
|
```
|
|
|
|
|
|
For `TBCLK = 50 MHz`, assuming the dead-band counter uses the same clock and no additional division:
|
|
|
|
|
|
\[
|
|
|
t_{DB}=\frac{DBRED\text{ or }DBFED}{50\times10^6}.
|
|
|
\]
|
|
|
|
|
|
| Module | DBRED | DBFED | Nominal rising/falling delay |
|
|
|
|---|---:|---:|---:|
|
|
|
| ePWM1 | 100 | 100 | 2 µs |
|
|
|
| ePWM2 | 250 | 250 | 5 µs |
|
|
|
|
|
|
**Exercise:** measure the time between relevant complementary output transitions. Confirm that no overlapping high pulses occur; also calculate whether the desired minimum pulse width can accommodate the chosen dead time.
|
|
|
|
|
|
## 10. CPU1: ePWM1 event-trigger interrupt
|
|
|
|
|
|
The supplied code generates an interrupt when the counter reaches zero, nominally once each 10 kHz PWM period:
|
|
|
|
|
|
```c
|
|
|
EPwm1Regs.ETSEL.bit.INTEN = 1;
|
|
|
EPwm1Regs.ETSEL.bit.INTSEL = 1;
|
|
|
EPwm1Regs.ETPS.bit.INTPRD = 1;
|
|
|
```
|
|
|
|
|
|
Register the handler and enable its PIE/CPU paths:
|
|
|
|
|
|
```c
|
|
|
EALLOW;
|
|
|
PieVectTable.EPWM1_INT = &epwm1_isr;
|
|
|
EDIS;
|
|
|
PieCtrlRegs.PIEIER3.bit.INTx1 = 1;
|
|
|
IER |= 0x4U; // CPU interrupt group 3
|
|
|
```
|
|
|
|
|
|
The ISR from the source toggles GPIO22, clears the ePWM flag, and acknowledges PIE group 3:
|
|
|
|
|
|
```c
|
|
|
__interrupt void epwm1_isr(void)
|
|
|
{
|
|
|
GpioDataRegs.GPATOGGLE.bit.GPIO22 = 1;
|
|
|
EPwm1Regs.ETCLR.bit.INT = 1;
|
|
|
PieCtrlRegs.PIEACK.bit.ACK3 = 1;
|
|
|
}
|
|
|
```
|
|
|
|
|
|
Since each ISR **toggles** rather than emits a complete pulse, GPIO22 should have an idealized waveform frequency of **5 kHz**, even though the interrupt rate is **10 kHz**. This assumes the CPU can reliably service all interrupts.
|
|
|
|
|
|
**Exercise:** measure GPIO22 independently from GPIO0 and identify the difference between PWM frequency, interrupt frequency, and toggle-output frequency.
|
|
|
|
|
|
## 11. CPU1: one-shot trip-zone protection
|
|
|
|
|
|
The supplied project routes GPIO97 through `INPUT1SELECT` and enables the `TZ1` one-shot source in both ePWM modules:
|
|
|
|
|
|
```c
|
|
|
EALLOW;
|
|
|
InputXbarRegs.INPUT1SELECT = 97U;
|
|
|
|
|
|
EPwm1Regs.TZSEL.bit.OSHT1 = 1;
|
|
|
EPwm1Regs.TZCTL.bit.TZA = 2;
|
|
|
EPwm1Regs.TZCTL.bit.TZB = 2;
|
|
|
EPwm1Regs.TZEINT.bit.OST = 1;
|
|
|
|
|
|
EPwm2Regs.TZSEL.bit.OSHT1 = 1;
|
|
|
EPwm2Regs.TZCTL.bit.TZA = 2;
|
|
|
EPwm2Regs.TZCTL.bit.TZB = 2;
|
|
|
EPwm2Regs.TZEINT.bit.OST = 1;
|
|
|
EDIS;
|
|
|
```
|
|
|
|
|
|
The `TZA = 2` and `TZB = 2` actions select **force-low** behavior for the corresponding ePWM outputs. The one-shot fault is designed to **latch** until software explicitly clears it.
|
|
|
|
|
|
Enable both trip-zone interrupts:
|
|
|
|
|
|
```c
|
|
|
EALLOW;
|
|
|
PieVectTable.EPWM1_TZ_INT = &epwm1_trip_isr;
|
|
|
PieVectTable.EPWM2_TZ_INT = &epwm2_trip_isr;
|
|
|
EDIS;
|
|
|
PieCtrlRegs.PIEIER2.bit.INTx1 = 1;
|
|
|
PieCtrlRegs.PIEIER2.bit.INTx2 = 1;
|
|
|
IER |= 0x2U; // CPU interrupt group 2
|
|
|
```
|
|
|
|
|
|
**Important protection rule:** the original CPU1 ISRs clear `TZCLR.INT` and `TZCLR.OST` immediately. This is not an adequate fault-latching strategy for an actual power converter. For a safe experiment, **record the fault and keep PWM inhibited** until the input is no longer active and a deliberate software restart has been performed. Do not automatically clear the one-shot latch within the trip ISR merely to resume operation.
|
|
|
|
|
|
Also confirm GPIO97's input pad, multiplexing, and qualification are correctly configured on the selected board, and test the active polarity. Setting `INPUT1SELECT` alone does not fully document the external electrical behavior of that pin.
|
|
|
|
|
|
## 12. CPU1: GPIO setup and startup
|
|
|
|
|
|
The original program sets GPIO0–GPIO3 for ePWM1A/B and ePWM2A/B, GPIO22 for interrupt diagnostics, and GPIO52 for CPU2 diagnostics. For example:
|
|
|
|
|
|
```c
|
|
|
EALLOW;
|
|
|
GpioCtrlRegs.GPAGMUX1.bit.GPIO0 = 0;
|
|
|
GpioCtrlRegs.GPAMUX1.bit.GPIO0 = 1;
|
|
|
GpioCtrlRegs.GPAGMUX1.bit.GPIO1 = 0;
|
|
|
GpioCtrlRegs.GPAMUX1.bit.GPIO1 = 1;
|
|
|
GpioCtrlRegs.GPAGMUX1.bit.GPIO2 = 0;
|
|
|
GpioCtrlRegs.GPAMUX1.bit.GPIO2 = 1;
|
|
|
GpioCtrlRegs.GPAGMUX1.bit.GPIO3 = 0;
|
|
|
GpioCtrlRegs.GPAMUX1.bit.GPIO3 = 1;
|
|
|
|
|
|
GpioCtrlRegs.GPAGMUX2.bit.GPIO22 = 0;
|
|
|
GpioCtrlRegs.GPAMUX2.bit.GPIO22 = 0;
|
|
|
GpioCtrlRegs.GPADIR.bit.GPIO22 = 1;
|
|
|
|
|
|
GpioCtrlRegs.GPBGMUX2.bit.GPIO52 = 0;
|
|
|
GpioCtrlRegs.GPBMUX2.bit.GPIO52 = 0;
|
|
|
GpioCtrlRegs.GPBDIR.bit.GPIO52 = 1;
|
|
|
GpioCtrlRegs.GPBCSEL3.bit.GPIO52 = 2; // CPU2 ownership
|
|
|
EDIS;
|
|
|
```
|
|
|
|
|
|
The supplied source also configures GPIO6–GPIO9 on CPU1 for ePWM4/ePWM5 output routing. Keep this separate from the CPU2 ePWM module configuration; **CPU ownership of an ePWM peripheral is not identical to ownership of an individual GPIO pin**.
|
|
|
|
|
|
Before enabling `TBCLKSYNC`, ensure CPU2 configuration is complete, protective inputs are in an appropriate state, and initial flags are handled. The source currently ends with:
|
|
|
|
|
|
```c
|
|
|
EALLOW;
|
|
|
CpuSysRegs.PCLKCR0.bit.TBCLKSYNC = 1;
|
|
|
EDIS;
|
|
|
for (;;) {}
|
|
|
```
|
|
|
|
|
|
A complete dual-core laboratory example should introduce a startup handshake so this action occurs only after both cores finish configuration.
|
|
|
|
|
|
## 13. CPU2: start from an almost-empty program (HWREG approach)
|
|
|
|
|
|
```c
|
|
|
#include "driverlib.h"
|
|
|
#include "device.h"
|
|
|
|
|
|
__interrupt void epwm5_isr(void) { /* TODO */ }
|
|
|
__interrupt void epwm4_trip_isr(void) { /* TODO */ }
|
|
|
__interrupt void epwm5_trip_isr(void) { /* TODO */ }
|
|
|
|
|
|
void main(void)
|
|
|
{
|
|
|
// 1. Initialize CPU2 interrupt module and vector table.
|
|
|
// 2. Enable ePWM4 and ePWM5 peripheral clocks.
|
|
|
// 3. Configure time-base and counter-compare registers.
|
|
|
// 4. Configure action qualifiers and dead band.
|
|
|
// 5. Configure event triggers and trip zones.
|
|
|
// 6. Register and enable interrupts.
|
|
|
// 7. Notify CPU1 that CPU2 configuration is complete.
|
|
|
for (;;) {}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
For this section, the student should locate:
|
|
|
|
|
|
- `hw_memmap.h`: peripheral base addresses such as `EPWM4_BASE`, `EPWM5_BASE`, `CPUSYS_BASE`.
|
|
|
- `hw_epwm.h`: offsets such as `EPWM_O_TBCTL`, `EPWM_O_CMPA`, `EPWM_O_AQCTLA`, and `EPWM_O_TZCLR`.
|
|
|
- `hw_sysctl.h`: clock-gate register offsets.
|
|
|
- `hw_pie.h` and `hw_ints.h`: PIE register offsets and interrupt-vector identifiers.
|
|
|
|
|
|
`HWREGH(address)` accesses a 16-bit register location, while `HWREG(address)` accesses a 32-bit register location. For `CMPA`, students should determine whether a 32-bit access covers both `CMPA` and `CMPAHR`; never assume an arbitrary bit mask or `|=` operation overwrites all intended compare bits.
|
|
|
|
|
|
## 14. CPU2: initialize modules and configure the time base
|
|
|
|
|
|
```c
|
|
|
Interrupt_initModule();
|
|
|
Interrupt_initVectorTable();
|
|
|
|
|
|
EALLOW;
|
|
|
HWREG(CPUSYS_BASE + SYSCTL_O_PCLKCR2) |= (uint32_t)0x18U;
|
|
|
EDIS;
|
|
|
|
|
|
// Time-base: values from the supplied CPU2 program
|
|
|
HWREGH(EPWM4_BASE + EPWM_O_TBCTL) = 0xC080;
|
|
|
HWREGH(EPWM5_BASE + EPWM_O_TBCTL) = 0xC082;
|
|
|
HWREGH(EPWM4_BASE + EPWM_O_TBPRD) = 3333 - 1;
|
|
|
HWREGH(EPWM5_BASE + EPWM_O_TBPRD) = 6250;
|
|
|
```
|
|
|
|
|
|
The `TBCTL` register words encode free-run debug behavior, clock prescalers, and up/up-down counter selection. Students should **decode every bit field** using the TI technical reference manual before using these values.
|
|
|
|
|
|
With `TBCLK = 50 MHz`:
|
|
|
|
|
|
- ePWM4: `50 MHz / 3333 ≈ 15.0015 kHz`.
|
|
|
- ePWM5: `50 MHz / (2 × 6250) = 4 kHz`.
|
|
|
|
|
|
The CPU2 source also clears the `TBCLKSYNC` bit during configuration and sets it again at the end. In the final dual-core application, replace this unsynchronized startup behavior with a **CPU1-coordinated release**.
|
|
|
|
|
|
## 15. CPU2: counter comparison and action qualifier
|
|
|
|
|
|
The supplied code uses:
|
|
|
|
|
|
```c
|
|
|
// Configure CMPCTL shadow/load bits.
|
|
|
HWREGH(EPWM4_BASE + EPWM_O_CMPCTL) &= 0xffefU;
|
|
|
HWREGH(EPWM4_BASE + EPWM_O_CMPCTL) &= 0xfffcU;
|
|
|
HWREGH(EPWM4_BASE + EPWM_O_CMPA) |= (uint32_t)0x07CF0000U;
|
|
|
|
|
|
HWREGH(EPWM5_BASE + EPWM_O_CMPCTL) &= 0xffefU;
|
|
|
HWREGH(EPWM5_BASE + EPWM_O_CMPCTL) &= 0xfffcU;
|
|
|
HWREGH(EPWM5_BASE + EPWM_O_CMPA + 1) =
|
|
|
(uint16_t)(((0.7 + 1.0) / 2.0) * 5000);
|
|
|
```
|
|
|
|
|
|
The ePWM4 compare constant encodes `0x07CF = 1999` as the upper 16-bit comparison value. **Review** the `|=` access before using it repeatedly because it does not clear previous compare bits. The CPU2 ePWM5 line computes `4250` from a hard-coded `5000`, whereas its actual `TBPRD` is `6250`. If the intended formula is `(m + 1) × TBPRD / 2`, the result for `m = 0.7` should be **5312** after C conversion to an integer (the unrounded mathematical result is 5312.5). This is a source-code discrepancy to correct as a student exercise.
|
|
|
|
|
|
The source configures the AQ registers with these writes:
|
|
|
|
|
|
```c
|
|
|
// ePWM4: high at ZRO, low at CAU
|
|
|
HWREGH(EPWM4_BASE + EPWM_O_AQCTLA) = 0;
|
|
|
HWREGH(EPWM4_BASE + EPWM_O_AQCTLB) = 0;
|
|
|
HWREGH(EPWM4_BASE + EPWM_O_AQCTLA) |= 0x0002U;
|
|
|
HWREGH(EPWM4_BASE + EPWM_O_AQCTLB) |= 0x0002U;
|
|
|
HWREGH(EPWM4_BASE + EPWM_O_AQCTLA) |= 0x0010U;
|
|
|
HWREGH(EPWM4_BASE + EPWM_O_AQCTLB) |= 0x0010U;
|
|
|
|
|
|
// ePWM5: high at CAU, low at CAD
|
|
|
HWREGH(EPWM5_BASE + EPWM_O_AQCTLA) = 0;
|
|
|
HWREGH(EPWM5_BASE + EPWM_O_AQCTLB) = 0;
|
|
|
HWREGH(EPWM5_BASE + EPWM_O_AQCTLA) |= 0x0020U;
|
|
|
HWREGH(EPWM5_BASE + EPWM_O_AQCTLB) |= 0x0020U;
|
|
|
HWREGH(EPWM5_BASE + EPWM_O_AQCTLA) |= 0x0040U;
|
|
|
HWREGH(EPWM5_BASE + EPWM_O_AQCTLB) |= 0x0040U;
|
|
|
```
|
|
|
|
|
|
**Exercise:** identify which bits correspond to the ZRO, CAU, and CAD fields, and reproduce the expected output-edge sequence before inserting dead band.
|
|
|
|
|
|
## 16. CPU2: dead-band setup
|
|
|
|
|
|
```c
|
|
|
HWREGH(EPWM4_BASE + EPWM_O_DBCTL) = (uint16_t)0x1BU;
|
|
|
HWREGH(EPWM4_BASE + EPWM_O_DBRED) = 100;
|
|
|
HWREGH(EPWM4_BASE + EPWM_O_DBFED) = 100;
|
|
|
|
|
|
HWREGH(EPWM5_BASE + EPWM_O_DBCTL) = (uint16_t)0x1BU;
|
|
|
HWREGH(EPWM5_BASE + EPWM_O_DBRED) = 250;
|
|
|
HWREGH(EPWM5_BASE + EPWM_O_DBFED) = 250;
|
|
|
```
|
|
|
|
|
|
These are the CPU2 register-word equivalents of the CPU1 dead-band setup. The intended delays are **2 µs** for ePWM4 and **5 µs** for ePWM5 under the assumed 50 MHz clock.
|
|
|
|
|
|
## 17. CPU2: ePWM5 interrupt and GPIO52 diagnostic
|
|
|
|
|
|
The provided CPU2 program writes:
|
|
|
|
|
|
```c
|
|
|
HWREGH(EPWM5_BASE + EPWM_O_ETSEL) |= (uint16_t)0xBU;
|
|
|
HWREGH(EPWM5_BASE + EPWM_O_ETPS) |= (uint16_t)0x1U;
|
|
|
```
|
|
|
|
|
|
`ETSEL = 0x000B` enables interrupt generation with selection `INTSEL = 3` (**counter zero OR counter period**). In up-down mode, this creates two selected events in one PWM cycle. `ETPS.INTPRD = 1` selects every event.
|
|
|
|
|
|
```c
|
|
|
__interrupt void epwm5_isr(void)
|
|
|
{
|
|
|
HWREG(GPIODATA_BASE + GPIO_O_GPBTOGGLE) = (uint32_t)0x100000U;
|
|
|
HWREGH(EPWM5_BASE + EPWM_O_ETCLR) |= (uint16_t)0x1U;
|
|
|
HWREGH(PIECTRL_BASE + PIE_O_ACK) |= (uint16_t)0x4U;
|
|
|
}
|
|
|
```
|
|
|
|
|
|
If all interrupts are serviced, two events per 4 kHz PWM cycle imply an **8 kHz interrupt rate** and a **4 kHz GPIO52 toggle waveform**. Real timing depends on CPU interrupt latency and system load.
|
|
|
|
|
|
The PIE configuration in the supplied program is:
|
|
|
|
|
|
```c
|
|
|
Interrupt_register(INT_EPWM5, &epwm5_isr);
|
|
|
HWREGH(PIECTRL_BASE + PIE_O_IER3) |= (uint16_t)0x10U;
|
|
|
IER |= (uint16_t)0x4U;
|
|
|
```
|
|
|
|
|
|
The corresponding ePWM5 trip-zone interrupt belongs to PIE **group 2**, channel 5; ePWM4 trip-zone belongs to group 2, channel 4.
|
|
|
|
|
|
## 18. CPU2: one-shot trip-zone configuration
|
|
|
|
|
|
The CPU2 source selects `TZ1` for ePWM4/ePWM5, forces both PWM outputs low, and enables the OST interrupt:
|
|
|
|
|
|
```c
|
|
|
EALLOW;
|
|
|
HWREGH(EPWM4_BASE + EPWM_O_TZSEL) |= (uint16_t)0x100U;
|
|
|
HWREGH(EPWM4_BASE + EPWM_O_TZCTL) = (uint16_t)0xAU;
|
|
|
HWREGH(EPWM4_BASE + EPWM_O_TZEINT) = (uint16_t)0x4U;
|
|
|
|
|
|
HWREGH(EPWM5_BASE + EPWM_O_TZSEL) |= (uint16_t)0x100U;
|
|
|
HWREGH(EPWM5_BASE + EPWM_O_TZCTL) = (uint16_t)0xAU;
|
|
|
HWREGH(EPWM5_BASE + EPWM_O_TZEINT) = (uint16_t)0x4U;
|
|
|
EDIS;
|
|
|
```
|
|
|
|
|
|
The handlers are registered with:
|
|
|
|
|
|
```c
|
|
|
Interrupt_register(INT_EPWM4_TZ, &epwm4_trip_isr);
|
|
|
Interrupt_register(INT_EPWM5_TZ, &epwm5_trip_isr);
|
|
|
HWREGH(PIECTRL_BASE + PIE_O_IER2) |= (uint16_t)0x18U;
|
|
|
IER |= (uint16_t)0x2U;
|
|
|
```
|
|
|
|
|
|
**Do not carry over the source's unconditional clearing of both `TZCLR.INT` and `TZCLR.OST` as an automatic restart strategy.** For a hardware-protection demonstration, first inspect the latched fault state, require the unsafe condition to be removed, then implement an explicit authorized reset procedure.
|
|
|
|
|
|
## 19. Controlled test procedure
|
|
|
|
|
|
1. **Clock verification:** confirm system clock, module clock, and time-base clock before relying on calculated frequencies.
|
|
|
2. **CPU1, one module at a time:** observe GPIO0/1 at 10 kHz, then GPIO2/3 at 5 kHz. Measure duty cycle and dead time.
|
|
|
3. **CPU2, one module at a time:** observe GPIO6/7 at about 15 kHz and GPIO8/9 at 4 kHz, after CPU ownership and startup coordination are established.
|
|
|
4. **Interrupt diagnostics:** measure GPIO22 and GPIO52 and compare with the computed event rates.
|
|
|
5. **Trip-zone test:** with no power switching stage connected, activate the fault input and verify all eight ePWM outputs are forced low and remain disabled while the one-shot latch is set.
|
|
|
6. **Fault recovery test:** remove the fault, verify that recovery requires deliberate software action, and confirm outputs return only when commanded.
|
|
|
|
|
|
Keep a table of *calculated values, measured values, percent error, and observations*. Use the same oscilloscope time base for corresponding before/after captures where feasible.
|
|
|
|
|
|
## 20. Technical checks before final submission
|
|
|
|
|
|
The following items are not silently corrected in this guide, because students should understand their cause and consequences.
|
|
|
|
|
|
- **CPU2 `CMPA` access:** ePWM4 uses a bitwise `|=` for an existing compare register. A fresh assignment with an appropriate mask or field selection is preferable when a known numeric value is required.
|
|
|
- **ePWM5 modulation inconsistency:** its compare formula uses `5000` while `TBPRD = 6250`. Derive and implement a period-consistent formula.
|
|
|
- **Static rather than variable modulation:** both `m = -0.3` and `m = +0.7` are compile-time constants. A real variable modulation implementation needs an update algorithm and bounded comparisons.
|
|
|
- **Fault protection:** one-shot latches must not be routinely cleared inside trip interrupts, especially with a connected power stage.
|
|
|
- **GPIO97:** verify its actual input mapping and active signal polarity; include proper input configuration and conditioning for the selected hardware.
|
|
|
- **Dual-core coordination:** CPU2 readiness and CPU1-controlled synchronization require a defined interprocessor handshake.
|
|
|
- **CPU startup:** determine where CPU2 is booted and initialized in the system; the CPU1 source does not explicitly document a CPU2 boot handshake.
|
|
|
- **Shared and peripheral clocks:** check per-CPU ePWM clock gates, ownership timing, and the state of `TBCLKSYNC` before using the output.
|
|
|
- **Debug configuration:** test the intended behavior when execution is halted, considering `FREE_SOFT` and external-power safety requirements.
|
|
|
- **Output-safe states:** force-low actions must be verified at *physical pins*, not inferred from register writes alone.
|
|
|
|
|
|
## 21. Questions for students
|
|
|
|
|
|
1. Which registers determine the ePWM counter frequency, count mode, and PWM output frequency?
|
|
|
2. Why does up-down counting require twice the `TBPRD` count interval of up-count mode for the same output frequency?
|
|
|
3. What is the distinction between counter comparison (`CMPA`) and action qualifier (`AQ`)?
|
|
|
4. How do `DBRED`, `DBFED`, `IN_MODE`, and `POLSEL` affect complementary outputs?
|
|
|
5. What are the expected interrupt rates and GPIO toggle-waveform frequencies for ePWM1 and ePWM5?
|
|
|
6. Why is `TBCLKSYNC` disabled during configuration and why does its restoration need coordination between CPUs?
|
|
|
7. What does a one-shot trip-zone latch do? What checks should occur before it is cleared?
|
|
|
8. Why is `|=` risky when programming a register that should receive an entirely new comparison value?
|
|
|
9. How would you replace a fixed modulation parameter with a slowly varying reference while preserving safe limits?
|
|
|
10. How can an oscilloscope demonstrate that complementary PWM signals do not overlap?
|
|
|
|
|
|
## 22. Relevant Texas Instruments references
|
|
|
|
|
|
- [TMS320F28379D product and datasheet](https://www.ti.com/product/TMS320F28379D)
|
|
|
- [F2837xD ePWM Driverlib API Guide](https://software-dl.ti.com/C2000/docs/C2000_driverlib_api_guide/f2837xd/build/html/modules/epwm.html)
|
|
|
- [TMS320F2837xD Microcontroller Workshop](https://software-dl.ti.com/trainingTTO/trainingTTO_public_sw/c28x28379/F2837xD_Microcontroller_MDW_2-0.pdf)
|
|
|
|
|
|
Refer also to the **TMS320F2837xD Technical Reference Manual**, particularly the ePWM, system control, interrupt controller, GPIO, and Input X-BAR chapters. In the installed C2000Ware, consult `F2837xD_epwm.h`, `F2837xD_sysctrl.h`, `F2837xD_gpio.h`, `hw_epwm.h`, `hw_memmap.h`, `hw_pie.h`, and `hw_ints.h` as applicable to the selected code style.
|
|
|
|
|
|
---
|
|
|
|
|
|
**Status:** Teaching draft based on the supplied code. Hardware behavior, trip-zone polarity, register-level details, and dual-core startup must be validated on the selected TMS320F28379D board before the implementation is used to control a power stage.
|