//#############################################################################
//
// FILE: erad_ex8_pwm_protection.c
//
// TITLE: ERAD PWM PROTECTION.
//
//! \addtogroup driver_example_list
//! <h1>ERAD PWM PROTECTION</h1>
//!
//! This example uses a BUS COMPARATOR and the CLB to detect the event when
//! the delay between the interrupt and the ISR execution is longer than
//! expected. The PWM output is also tripped in this case.
//!
//! \b Watch \b Variables \n
//! - adcAResults stores the results of the conversions from the ADC
//!
//! \b External \b Connections \n
//! - Monitor the PWM output (GPIO0)
//
//#############################################################################
//
// Included files
//
#include "driverlib.h"
#include "device.h"
#include "clb_config.h"
#include "board.h"
//
// Macros for configuring the CLB
//
#define RESULTS_BUFFER_SIZE 256
//
// Function prototypes
//
void config_XBar();
void config_CLB();
__interrupt void clb1ISR();
void initADC(void);
void initEPWM(void);
void initADCSOC(void);
__interrupt void adcA1ISR(void);
__interrupt void epwm1TZISR(void);
//
// global variables
//
uint16_t adcAResults[RESULTS_BUFFER_SIZE]; // Buffer for results
uint16_t index; // Index into result buffer
volatile uint16_t bufferFull; // Flag to indicate buffer is full
uint16_t epwm1TZIntCount = 0; // Variable to indicate that trip interrupt executed
void *addr_adcA1ISR = (void *)&adcA1ISR;
//
// Main
//
void main(void)
{
//
// Initialize device clock and peripherals
//
Device_init();
//
// Disable pin locks and enable internal pullups.
//
Device_initGPIO();
//
// Initialize PIE and clear PIE registers. Disables CPU interrupts.
//
Interrupt_initModule();
//
// Initialize the PIE vector table with pointers to the shell Interrupt
// Service Routines (ISR).
//
Interrupt_initVectorTable();
//
// Interrupts that are used in this example are re-mapped to ISR functions
// found within this file.
//
Interrupt_register(INT_ADCA1, &adcA1ISR);
Interrupt_register(INT_EPWM1_TZ, &epwm1TZISR);
//
// Board initialization
//
Board_init();
//
// Set up the ADC and the ePWM and initialize the SOC
//
initADC();
initEPWM();
initADCSOC();
//
// Configure the modules
//
config_XBar();
config_CLB();
//
// Initialize results buffer
//
for(index = 0; index < RESULTS_BUFFER_SIZE; index++)
{
adcAResults[index] = 0;
}
index = 0;
bufferFull = 0;
//
// Enable ADC interrupt
//
Interrupt_enable(INT_ADCA1);
Interrupt_enable(INT_EPWM1_TZ);
CLB_clearInterruptTag(CLB1_BASE);
//
// Enable Global Interrupt (INTM) and realtime interrupt (DBGM)
//
EINT;
ERTM;
ESTOP0;
int i = 0;
//
// Loop indefinitely
//
for(;i<3;i++)
{
//
// Start ePWM1, enabling SOCA and putting the counter in up-count mode
//
EPWM_enableADCTrigger(EPWM1_BASE, EPWM_SOC_A);
EPWM_setTimeBaseCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_UP);
//
// Wait while ePWM1 causes ADC conversions which then cause interrupts.
// When the results buffer is filled, the bufferFull flag will be set.
//
while(bufferFull == 0)
{
}
bufferFull = 0; // Clear the buffer full flag
//
// Stop ePWM1, disabling SOCA and freezing the counter
//
EPWM_disableADCTrigger(EPWM1_BASE, EPWM_SOC_A);
EPWM_setTimeBaseCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_STOP_FREEZE);
//
// Software breakpoint. At this point, conversion results are stored in
// adcAResults.
//
// Hit run again to get updated conversions.
//
ESTOP0;
}
//
// Disable the interrupt from the ADC to simulate a longer time between
// interrupt generation and the ISR execution
//
Interrupt_disable(INT_ADCA1);
// EPWM_enableADCTrigger(EPWM1_BASE, EPWM_SOC_A); // To test EPWM1A AQ in long term ordinary run.
EPWM_setTimeBaseCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_UP);
while(1)
{
}
}
//
// initADC - Function to configure and power up ADCA.
//
void initADC(void)
{
//
// Set ADCCLK divider to /4
//
ADC_setPrescaler(ADCA_BASE, ADC_CLK_DIV_4_0);
//
// Set resolution and signal mode
//
ADC_setMode(ADCA_BASE, ADC_RESOLUTION_12BIT, ADC_MODE_SINGLE_ENDED);
//
// Set pulse positions to late
//
ADC_setInterruptPulseMode(ADCA_BASE, ADC_PULSE_END_OF_CONV);
//
// Power up the ADC and then delay for 1 ms
//
ADC_enableConverter(ADCA_BASE);
DEVICE_DELAY_US(1000);
}
//
// initEPWM - Function to configure ePWM1 to generate the SOC.
//
void initEPWM(void)
{
//
// Disable SOCA
//
EPWM_disableADCTrigger(EPWM1_BASE, EPWM_SOC_A);
//
// Configure the SOC to occur on the first up-count event
//
EPWM_setADCTriggerSource(EPWM1_BASE, EPWM_SOC_A, EPWM_SOC_TBCTR_U_CMPA);
EPWM_setADCTriggerEventPrescale(EPWM1_BASE, EPWM_SOC_A, 1);
//
// Set the compare A value to 2048 and the period to 4096
//
EPWM_setCounterCompareValue(EPWM1_BASE, EPWM_COUNTER_COMPARE_A, 0x0800);
EPWM_setTimeBasePeriod(EPWM1_BASE, 0x1000);
//*********************************************************
//** Add AQ settings to expose EPWM1A output waveform. *
//** 2025.06.16 *
//*********************************************************
EPWM_setClockPrescaler(EPWM1_BASE, EPWM_CLOCK_DIVIDER_1, EPWM_HSCLOCK_DIVIDER_1);
EPWM_setPeriodLoadMode(EPWM1_BASE, EPWM_PERIOD_SHADOW_LOAD);
EPWM_setCounterCompareShadowLoadMode(EPWM1_BASE, EPWM_COUNTER_COMPARE_A,
EPWM_COMP_LOAD_ON_CNTR_ZERO_PERIOD);
EPWM_setActionQualifierAction(EPWM1_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_LOW,
EPWM_AQ_OUTPUT_ON_TIMEBASE_PERIOD);
EPWM_setActionQualifierAction(EPWM1_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_HIGH,
EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPA);
//
// Freeze the counter
//
EPWM_setTimeBaseCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_STOP_FREEZE);
//
// Enable and select trip input4 to the DCAL output
//
EPWM_enableDigitalCompareTripCombinationInput(EPWM1_BASE, EPWM_DC_COMBINATIONAL_TRIPIN4, EPWM_DC_TYPE_DCAL);
EPWM_selectDigitalCompareTripInput(EPWM1_BASE, EPWM_DC_TRIP_COMBINATION, EPWM_DC_TYPE_DCAL);
//
// Set event condition as DCAL going high
//
EPWM_setTripZoneDigitalCompareEventCondition(EPWM1_BASE, EPWM_TZ_DC_OUTPUT_A1, EPWM_TZ_EVENT_DCXL_HIGH);
//
// No filter required
//
EPWM_setDigitalCompareEventSource(EPWM1_BASE, EPWM_DC_MODULE_A, EPWM_DC_EVENT_1, EPWM_DC_EVENT_SOURCE_ORIG_SIGNAL);
//
// Make the PWM go low on trip
//
EPWM_setTripZoneAction(EPWM1_BASE, EPWM_TZ_ACTION_EVENT_DCAEVT1, EPWM_TZ_ACTION_LOW);
//
// Enable interrupt as well on trip
//
EPWM_enableTripZoneInterrupt(EPWM1_BASE, EPWM_TZ_INTERRUPT_DCAEVT1);
//
// Enable the trip zone signal as DCAEVT1
//
// EPWM_enableTripZoneSignals(EPWM1_BASE, EPWM_TZ_SIGNAL_OSHT6); // Original
EPWM_enableTripZoneSignals(EPWM1_BASE, EPWM_TZ_SIGNAL_DCAEVT1); // Revised 2025.06.16
//
// Monitor GPIO0 to see the PWM
//
GPIO_setPadConfig(0, GPIO_PIN_TYPE_STD);
GPIO_setPinConfig(GPIO_0_EPWM1A);
}
//
// initADCSOC - Function to configure ADCA's SOC0 to be triggered by ePWM1.
//
void initADCSOC(void)
{
//
// Configure SOC0 of ADCA to convert pin A0 with a sample window of 10
// SYSCLK cycles. The EPWM1SOCA signal will be the trigger.
//
ADC_setupSOC(ADCA_BASE, ADC_SOC_NUMBER0, ADC_TRIGGER_EPWM1_SOCA,
ADC_CH_ADCIN0, 10);
//
// Set SOC0 to set the interrupt 1 flag. Enable the interrupt and make
// sure its flag is cleared.
//
ADC_setInterruptSource(ADCA_BASE, ADC_INT_NUMBER1, ADC_SOC_NUMBER0);
ADC_enableInterrupt(ADCA_BASE, ADC_INT_NUMBER1);
ADC_clearInterruptStatus(ADCA_BASE, ADC_INT_NUMBER1);
}
//
// Configures the CLB module
//
void
config_CLB()
{
SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_CLB1);
CLB_enableCLB(CLB1_BASE);
initTILE1(CLB1_BASE);
//
// Select Global input instead of local input for all CLB IN
//
CLB_configLocalInputMux(CLB1_BASE, CLB_IN0, CLB_LOCAL_IN_MUX_GLOBAL_IN);
CLB_configLocalInputMux(CLB1_BASE, CLB_IN1, CLB_LOCAL_IN_MUX_GLOBAL_IN);
CLB_configLocalInputMux(CLB1_BASE, CLB_IN2, CLB_LOCAL_IN_MUX_GLOBAL_IN);
CLB_configLocalInputMux(CLB1_BASE, CLB_IN3, CLB_LOCAL_IN_MUX_GLOBAL_IN);
CLB_configLocalInputMux(CLB1_BASE, CLB_IN4, CLB_LOCAL_IN_MUX_GLOBAL_IN);
CLB_configLocalInputMux(CLB1_BASE, CLB_IN5, CLB_LOCAL_IN_MUX_GLOBAL_IN);
CLB_configLocalInputMux(CLB1_BASE, CLB_IN6, CLB_LOCAL_IN_MUX_GLOBAL_IN);
CLB_configLocalInputMux(CLB1_BASE, CLB_IN7, CLB_LOCAL_IN_MUX_GLOBAL_IN);
//
// Select the Comparator A in the EPWM 1 module for CLB1, IN0
// Select ERAD Bus comparator 2 for CLB1, IN1
//
CLB_configGlobalInputMux(CLB1_BASE, CLB_IN0, CLB_GLOBAL_IN_MUX_EPWM1_CTR_CMPA);
CLB_configGlobalInputMux(CLB1_BASE, CLB_IN1, CLB_GLOBAL_IN_MUX_CPU1_ERAD_EVENT0);
//
// Unused Inputs below
//
CLB_configGlobalInputMux(CLB1_BASE, CLB_IN2, CLB_GLOBAL_IN_MUX_EPWM1A);
CLB_configGlobalInputMux(CLB1_BASE, CLB_IN3, CLB_GLOBAL_IN_MUX_EPWM1A);
CLB_configGlobalInputMux(CLB1_BASE, CLB_IN4, CLB_GLOBAL_IN_MUX_EPWM1A);
CLB_configGlobalInputMux(CLB1_BASE, CLB_IN5, CLB_GLOBAL_IN_MUX_EPWM1A);
CLB_configGlobalInputMux(CLB1_BASE, CLB_IN6, CLB_GLOBAL_IN_MUX_EPWM1A);
CLB_configGlobalInputMux(CLB1_BASE, CLB_IN7, CLB_GLOBAL_IN_MUX_EPWM1A);
//
// Select External for CLB1, IN0
// Select External for CLB1, IN1
//
CLB_configGPInputMux(CLB1_BASE, CLB_IN0, CLB_GP_IN_MUX_EXTERNAL);
CLB_configGPInputMux(CLB1_BASE, CLB_IN1, CLB_GP_IN_MUX_EXTERNAL);
CLB_selectInputFilter(CLB1_BASE, CLB_IN0, CLB_FILTER_RISING_EDGE);
CLB_selectInputFilter(CLB1_BASE, CLB_IN1, CLB_FILTER_RISING_EDGE);
//
// Unused inputs to GP register
//
CLB_configGPInputMux(CLB1_BASE, CLB_IN2, CLB_GP_IN_MUX_GP_REG);
CLB_configGPInputMux(CLB1_BASE, CLB_IN3, CLB_GP_IN_MUX_GP_REG);
CLB_configGPInputMux(CLB1_BASE, CLB_IN4, CLB_GP_IN_MUX_GP_REG);
CLB_configGPInputMux(CLB1_BASE, CLB_IN5, CLB_GP_IN_MUX_GP_REG);
CLB_configGPInputMux(CLB1_BASE, CLB_IN6, CLB_GP_IN_MUX_GP_REG);
CLB_configGPInputMux(CLB1_BASE, CLB_IN7, CLB_GP_IN_MUX_GP_REG);
//
// Configure OUTPUT-XBAR OUTPUT1 as CLB1_OUT5
//
XBAR_setOutputMuxConfig(OUTPUTXBAR_BASE, XBAR_OUTPUT1, XBAR_OUT_MUX03_CLB1_OUT5);
XBAR_enableOutputMux(OUTPUTXBAR_BASE, XBAR_OUTPUT1, XBAR_MUX03);
//
// Routes the output of the CLB to the EPWM module
// through the EPWM X-Bar
//
XBAR_setEPWMMuxConfig(XBAR_TRIP4, XBAR_EPWM_MUX01_CLB1_OUT4);
XBAR_enableEPWMMux(XBAR_TRIP4, XBAR_MUX01);
CLB_setGPREG(CLB1_BASE, 0);
// CLB_setOutputMask(CLB1_BASE, CLB_OUTPUT_04|CLB_OUTPUT_05, true); // Original
CLB_setOutputMask(CLB1_BASE, CLB_OUTPUT_05, true); // Revised 2025.06.16
}
//
// Configures the Output X-bar and GPIOs to show output on LEDs
//
void
config_XBar()
{
GPIO_setPadConfig(DEVICE_GPIO_PIN_LED1, GPIO_PIN_TYPE_STD);
GPIO_setDirectionMode(DEVICE_GPIO_PIN_LED1, GPIO_DIR_MODE_OUT);
GPIO_writePin(DEVICE_GPIO_PIN_LED1, 1);
//
// Routes the output from the Output X-Bar to LED2
// LED2 will turn on
//
GPIO_setPadConfig(DEVICE_GPIO_PIN_LED2, GPIO_PIN_TYPE_STD);
GPIO_setDirectionMode(DEVICE_GPIO_PIN_LED2, GPIO_DIR_MODE_OUT);
GPIO_setPinConfig(GPIO_34_OUTPUTXBAR1);
}
//
// epwm1TZISR - ePWM1 TZ ISR
//
__interrupt void epwm1TZISR(void)
{
epwm1TZIntCount++;
//
// Makes LED1 glow
//
GPIO_writePin(DEVICE_GPIO_PIN_LED1, 0);
//
// To re-enable the OST Interrupt, uncomment the below code:
//
// EPWM_clearTripZoneFlag(EPWM1_BASE,
// (EPWM_TZ_INTERRUPT | EPWM_TZ_FLAG_OST));
//
// Acknowledge this interrupt to receive more interrupts from group 2
//
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP2);
ESTOP0;
}
//
// adcA1ISR - ADC A Interrupt 1 ISR
//
__interrupt void adcA1ISR(void)
{
//
// Add the latest result to the buffer
//
adcAResults[index++] = ADC_readResult(ADCARESULT_BASE, ADC_SOC_NUMBER0);
//
// Set the bufferFull flag if the buffer is full
//
if(RESULTS_BUFFER_SIZE <= index)
{
index = 0;
bufferFull = 1;
}
//
// Clear the interrupt flag
//
ADC_clearInterruptStatus(ADCA_BASE, ADC_INT_NUMBER1);
//
// Check if overflow has occurred
//
if(true == ADC_getInterruptOverflowStatus(ADCA_BASE, ADC_INT_NUMBER1))
{
ADC_clearInterruptOverflowStatus(ADCA_BASE, ADC_INT_NUMBER1);
ADC_clearInterruptStatus(ADCA_BASE, ADC_INT_NUMBER1);
}
//
// Acknowledge the interrupt
//
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1);
}
修改前:EPWM1A在GPIO0始终无输出;
主要改动:1. initEPWM中添加AQ配置;
2. initEPWM末段的EPWM_enableTripZoneSignals输入参数修正;
3. config_CLB末段的CLB_setOutputMask输入参数修正;
效果:1. 主函数的for循环前3轮中,EPWM Clock启停之间能看到EPWM1A输出波形(若GPIO0外接LED效果更明显);
2. 跳出循环后TripZone触发关断动作,能看到EPWM1A输出降为0(外接LED熄灭);
声明:个人观点,友好交流,抱头勿喷,谢谢,抱拳