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.
Tool/software: Code Composer Studio
Hello everyone,
i'm just programming a simple EPWM which triggers the ADC when CMPA is reached.
The code bases on the driverlib ADC example.
The sample and hold time is only one zyklus for testing.
When the interrupt occures the register TBPRD has the number 5050 instead of 5000 or 300 instead of 250. The SYSCLK is 100MHz.
I hope you will understand my problem. Thanks.
#include "driverlib.h" #include "device.h" #include "main.h" // // Defines // #define EPWM1_TIMER_TBPRD 10000U #define EPWM1_CMPA 5000U #define EPWM_CMP_UP 1U #define EPWM_CMP_DOWN 0U //Define Outputs #define DEVICE_GPIO_PIN_DRIVER1 10U #define DEVICE_GPIO_PIN_DRIVER2 11U #define DEVICE_GPIO_PIN_ANALOGCONT 12U #define DEVICE_GPIO_PIN_COMPROGDIS 13U #define DEVICE_GPIO_PIN_COMPSHUNTDIS 17U //Define Inputs #define DEVICE_GPIO_PIN_PWMCONTSHUNT 16U #define DEVICE_GPIO_PIN_PWMCONTROG 6U // // Defines // #define RESULTS_BUFFER_SIZE 256 // // Globals // 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 void initEPWM1(void); void initADC(void); void initADCSOC(void); __interrupt void adcA1ISR(void); void main(void) { // Initialize device clock and peripherals Device_init(); // Initialize GPIO and configure the GPIO pin as a push-pull output Device_initGPIO(); GPIO_setPadConfig(24U, GPIO_PIN_TYPE_STD); GPIO_setDirectionMode(24U, GPIO_DIR_MODE_OUT); //Initialize PWM GPIO_setPadConfig(0U, GPIO_PIN_TYPE_STD); GPIO_setPinConfig(GPIO_0_EPWM1A); // // 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(); // Enable Global Interrupt (INTM) and realtime interrupt (DBGM) // Interrupt_register(INT_ADCA1, &adcA1ISR); //init ADC and power it up; init EPWM initADC(); // // Disable sync(Freeze clock to PWM as well) // SysCtl_disablePeripheral(SYSCTL_PERIPH_CLK_TBCLKSYNC); initEPWM1(); // // Enable sync and clock to PWM // SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_TBCLKSYNC); initADCSOC(); for(index = 0; index < RESULTS_BUFFER_SIZE; index++) { adcAResults[index] = 0; } index = 0; bufferFull = 0; // // Enable ADC interrupt // Interrupt_enable(INT_ADCA1); EINT; ERTM; // Loop Forever for(;;) { EPWM_enableADCTrigger(EPWM1_BASE, EPWM_SOC_A); //EPWM_enableADCTrigger(EPWM1_BASE, EPWM_SOC_B); EPWM_setTimeBaseCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_UP); } } void initEPWM1() { // // 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-up TBCLK // EPWM_setTimeBasePeriod(EPWM1_BASE, EPWM1_TIMER_TBPRD); EPWM_setTimeBaseCounter(EPWM1_BASE, 0U); // // Set Compare values // EPWM_setCounterCompareValue(EPWM1_BASE, EPWM_COUNTER_COMPARE_A, EPWM1_CMPA); // // Set up counter mode // EPWM_setClockPrescaler(EPWM1_BASE, EPWM_CLOCK_DIVIDER_1, EPWM_HSCLOCK_DIVIDER_1); // // Set up shadowing // EPWM_setCounterCompareShadowLoadMode(EPWM1_BASE, EPWM_COUNTER_COMPARE_A, EPWM_COMP_LOAD_ON_CNTR_ZERO); // // Set actions // EPWM_setActionQualifierAction(EPWM1_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_HIGH, EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPA); EPWM_setActionQualifierAction(EPWM1_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_LOW, EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO); EPWM_setTimeBaseCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_STOP_FREEZE); } void initADC() { // // Setup VREF as internal // ADC_setVREF(ADCA_BASE, ADC_REFERENCE_EXTERNAL, ADC_REFERENCE_3_3V); // // Set ADCCLK divider to /4 // ADC_setPrescaler(ADCA_BASE, ADC_CLK_DIV_2_0); //50Mhz Clock (100Mhz/2) // // 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); } void initADCSOC(void) { // // Configure SOC0 of ADCA to convert pin A0 with a sample window of 11 // SYSCLK cycles. The EPWM1SOCA signal will be the trigger. // ADC_setupSOC(ADCA_BASE, ADC_SOC_NUMBER0, ADC_TRIGGER_EPWM1_SOCA, ADC_CH_ADCIN0, 1); // // 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); } __interrupt void adcA1ISR(void) { // // Add the latest result to the buffer // GPIO_togglePin(24U); // 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); }
Green PWM
BLUE TOGGLE PIN inside of ISR
You're not going to see exactly a TBCTR that is exactly equal to CMPA in your ADC ISR. You have to take into account the interrupt latency, the ADC sample and hold time, and the time it takes from the end of the S+H window to when the ADCINT flag is actually set. There are some timing diagrams in the ADC chapter that show this. You can do the calculations to see if this makes sense for the size of the delay you're seeing.
Some of that can't be avoided, but one adjustment you could try is to set your interrupt pulse generation to occur at the end of the acquisition window instead of at the end of the conversion. See the description of the INTPULSEPOS bit in the TRM. The code above that would change is the pulseMode parameter in ADC_setInterruptPulseMode().
Whitney
Thanks for your answer. I tried to set the pulse generation to "ADC_PULSE_END_OF_ACQ_WIN" and it is little bit faster.
I cannot believe that this is the maximum speed of the ADC (running with 50Mhz). If yes, I cannot measure the current exactly.The PWM is only running with 100kHz.
I rechecked the timings in the datasheet and my adc would need in the Early Interrupt Mode 1(tint=0 tsh=1) and in Late Interrupt Mode 23 (tint=22 tsh=1) SYSCLK Cycles . If i compare this with my oscilloscope pictures, my delay is much larger.
I did some additonal test. I summarize everything in the following table:
TBPRD = 1000U
CMPA = 500U
Trigger when TBCTR == zero
Sample and Hold Cycle |
End of acquisition (Number in TBCTR Register when calling adcA1ISR()) |
End of conversion (Number in TBCTR Register when calling adcA1ISR()) |
1 | 13 (HEX) -> 19 (DEC) | 27 (HEX) -> 39 (DEC) |
11 | 1D (HEX) -> 29 (DEC) | 31 (HEX) -> 49 (DEC) |
Trigger when TBCTR == CMPA
Sample and Hold Cycle |
End of acquisition (Number in TBCTR Register when calling adcA1ISR()) |
End of conversion (Number in TBCTR Register when calling adcA1ISR()) |
1 | 208 (HEX) -> 520 (DEC) | 21C (HEX) -> 540 (DEC) |
11 | 212 (HEX) -> 530 (DEC) | 226 (HEX) -> 550 (DEC) |
The values in the table shows, that the ADC timings are okay. Only the interrupt is ~ 18-19 SYSCLK cycles to late. Why is this happening?
Are you counting the interrupt latency? There's a minimum of 14 cycles.
http://processors.wiki.ti.com/index.php/Interrupt_FAQ_for_C2000#ISR_Latency
You can look at the disassembly to see how many instructions are being executed between the start of the ISR and the toggling of the pin (or the point at which you're pointing a breakpoint to look at TBCTR).
Also, if you don't have optimization turned on, GPIO_togglePin() is going to do a function call and some calculations before the actual write to the toggle register, so that could be contributing. Turning on optimization should inline it and make it faster.
Whitney
Thanks Whitney,
after I turned on the optimizer, the speed of the internal ISR is as described in the datasheet.
Patrick