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.

RTOS/TMS320F28075: Allowing interrupts from within TI-RTOS

Part Number: TMS320F28075

Tool/software: TI-RTOS

I am trying to understand why I am getting inconsistent ADC interrupts.  The ADC is triggered by a PWM clock and then it interrupts the CPU when the conversions are complete. The interrupt triggers at a rate of 2500Hz, and I am missing every 4th or 5th interrupt in the sequence (I have a GPIO bit toggling high and low for each, so I can see when it does not trigger).  I have ruled out other known interrupts, but now am wondering if there are times within the operation of TI-RTOS that could be blocking interrupts for a short time.  

I new to TI-RTOS, but understand that when it is running it runs at times in an interrupt context and so would block or at least delay other interrupts.  Is there a way to re-enable interrupts from within TI-RTOS so that longer tasks will be interrupted before they are blocked or switched?  .... or some other way to allow my ADC interrupt to be triggered more consistently?

Thanks,

      Aaron

  • Hi Aaron,

    What speed are you running the device?

    The SYS/BIOS (e.g. TI-RTOS kernel) does disable interrupts for short periods of time. Look in the SYS/BIOS Release Notes for the Benchmark section. You can click through that to find the max interrupt latency. It also shows duration to run a Hwi.

    I expect you are using the default settings on the Clock module, so it uses a Timer that runs at every 1ms. You can set this to be user supplied (or no Clock) to eliminate the Timer. Of course, you'll lose timing mechanisms in the kernel (e.g. no Task_sleep or Semaphore_pend with a timeout), but it would eliminate the timer as an issue.

    There are always zero-latency interrupts in SYS/BIOS also (the kernel adds zero-latency). You can have the ADC interrupt not be managed by the kernel. The downside is that ISR cannot make a kernel call that might impact scheduling (e.g. no Semaphore_post). It can post another Hwi though that can make kernel calls.

    Todd
  • Thanks Todd.

    I am running clock at 120 MHz, and default Clock module settings, I think.

    I tried running the interrupt outside of the dispatcher ( using Hwi_plug() ), but I see the same issue.

    It doesn't sound like the Timer would cause the interrupt not to trigger, but it would instead be delayed. I am seeing the interrupt not trigger about 1/5th of the time, so I think that something else is going on. To drive the timing, I am using PWM to regularly start the ADC and then the ADC triggers the interrupt on the end of conversions. The interrupt fires at least most of the time and ADC values are coming in good. Here's the setup code...

    Does anything look questionable in this setup?


    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // PWM SETUP
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    EALLOW;

    // Setup PWM to run at 2.5kHz

    // Setup TBCLK
    //
    EPwm7Regs.TBPRD = PWM_TIMER_TBPRD; // Set timer period
    EPwm7Regs.TBPHS.bit.TBPHS = 0x0000; // Phase is 0
    EPwm7Regs.TBCTR = 0x0000; // Clear counter

    // Set Compare values
    //
    EPwm7Regs.CMPA.bit.CMPA = PWM_MIN_CMP; // Set compare A value

    // Setup counter mode
    //
    EPwm7Regs.TBCTL.bit.CTRMODE = TB_FREEZE; // Freeze for now, but later set to TB_COUNT_UP
    EPwm7Regs.TBCTL.bit.PHSEN = TB_DISABLE; // Disable phase loading
    // TODO: show clock rate computation
    EPwm7Regs.TBCTL.bit.HSPCLKDIV = TB_DIV4; // Clock ratio to SYSCLKOUT
    EPwm7Regs.TBCTL.bit.CLKDIV = TB_DIV2;

    // Setup shadowing
    //
    EPwm7Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
    EPwm7Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO; // Load on Zero

    // Set actions for motor driver
    //
    EPwm7Regs.AQCTLA.bit.ZRO = AQ_SET;
    EPwm7Regs.AQCTLA.bit.CAU = AQ_CLEAR;

    // Setup ADC triggering on beginning of each period
    //
    EPwm7Regs.ETSEL.bit.SOCAEN = 0; // Disable SOC on A group
    EPwm7Regs.ETSEL.bit.SOCASEL = ET_CTR_ZERO; // Select SOC on re-zeroing of counter (just after PWM goes high)
    EPwm7Regs.ETPS.bit.SOCAPRD = 1; // Generate pulse on 1st event

    EDIS;


    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // ADC SETUP
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    EALLOW;

    // Write configurations
    AdcaRegs.ADCCTL2.bit.PRESCALE = 6; //set ADCCLK divider to /4
    AdcSetMode(ADC_ADCA, ADC_RESOLUTION_12BIT, ADC_SIGNALMODE_SINGLE);

    AdcbRegs.ADCCTL2.bit.PRESCALE = 6; //set ADCCLK divider to /4
    AdcSetMode(ADC_ADCB, ADC_RESOLUTION_12BIT, ADC_SIGNALMODE_SINGLE);

    // Set pulse positions to late
    AdcaRegs.ADCCTL1.bit.INTPULSEPOS = 1;
    AdcbRegs.ADCCTL1.bit.INTPULSEPOS = 1;

    // Power up the ADCs
    AdcaRegs.ADCCTL1.bit.ADCPWDNZ = 1;
    AdcbRegs.ADCCTL1.bit.ADCPWDNZ = 1;

    // At this point, delay for 1ms to allow ADC time to power up
    // before continuing on with other setup. The calling function
    // must insure this. Datasheet calls for minimum of 500us.

    EDIS;

    ...
    Continuing....


    Uint16 acqps;

    // Set minimum acquisition window (in SYSCLKS)
    acqps = 14; // 75ns TBD - need to verify

    // Select the channels to convert and relative timing.
    // Both ADCs will be working in parallel with ADC A driving the
    // interrupt at the end of last conversion.
    //
    EALLOW;
    ///////////////////////////////////////////////////////////////////////////
    // ADC A
    AdcaRegs.ADCBURSTCTL.bit.BURSTEN = 1;
    AdcaRegs.ADCBURSTCTL.bit.BURSTTRIGSEL = BURSTTRIGSEL__EPWM7_ADCSOCA;
    AdcaRegs.ADCBURSTCTL.bit.BURSTSIZE = ADC_A_BURST_LENGTH;

    // SOC0
    AdcaRegs.ADCSOC0CTL.bit.CHSEL = 1; // FSR0
    AdcaRegs.ADCSOC0CTL.bit.ACQPS = acqps;
    // SOC1
    AdcaRegs.ADCSOC1CTL.bit.CHSEL = 2; // FSR1
    AdcaRegs.ADCSOC1CTL.bit.ACQPS = acqps;
    // SOC2
    AdcaRegs.ADCSOC2CTL.bit.CHSEL = 3; // FSR2
    AdcaRegs.ADCSOC2CTL.bit.ACQPS = acqps;
    // SOC3
    AdcaRegs.ADCSOC3CTL.bit.CHSEL = 4; // FSR3
    AdcaRegs.ADCSOC3CTL.bit.ACQPS = acqps;
    // SOC4
    AdcaRegs.ADCSOC4CTL.bit.CHSEL = 5; // AMOTOR
    AdcaRegs.ADCSOC4CTL.bit.ACQPS = acqps;
    // SOC5
    AdcaRegs.ADCSOC5CTL.bit.CHSEL = 15; // FSR6
    AdcaRegs.ADCSOC5CTL.bit.ACQPS = acqps;
    // SOC6
    AdcaRegs.ADCSOC6CTL.bit.CHSEL = 14; // FSR7
    AdcaRegs.ADCSOC6CTL.bit.ACQPS = acqps;

    AdcaRegs.ADCINTSEL1N2.bit.INT1SEL = ADC_A_EOCx_TRIGGER_ADCINT1; // Set: EOCx is trigger for ADCINT1
    AdcaRegs.ADCINTSEL1N2.bit.INT1E = 1; // Enable flag
    AdcaRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; // Clear flag

    ///////////////////////////////////////////////////////////////////////////
    // ADC B
    AdcbRegs.ADCBURSTCTL.bit.BURSTEN = 1;
    AdcbRegs.ADCBURSTCTL.bit.BURSTTRIGSEL = BURSTTRIGSEL__EPWM7_ADCSOCA;
    AdcbRegs.ADCBURSTCTL.bit.BURSTSIZE = ADC_B_BURST_LENGTH;

    // SOC0
    AdcbRegs.ADCSOC0CTL.bit.CHSEL = 0; // ADCINB0, JP1_pin8
    AdcbRegs.ADCSOC0CTL.bit.ACQPS = acqps;
    // SOC1
    AdcbRegs.ADCSOC1CTL.bit.CHSEL = 1; // ADCINB1, JP1_pin9
    AdcbRegs.ADCSOC1CTL.bit.ACQPS = acqps;
    // SOC2
    AdcbRegs.ADCSOC2CTL.bit.CHSEL = 2; // ADCINB2, JP1_pin10
    AdcbRegs.ADCSOC2CTL.bit.ACQPS = acqps;
    // SOC3
    AdcbRegs.ADCSOC3CTL.bit.CHSEL = 3; // FSR5
    AdcbRegs.ADCSOC3CTL.bit.ACQPS = acqps;
    // SOC4
    AdcbRegs.ADCSOC4CTL.bit.CHSEL = 4; // FSR4
    AdcbRegs.ADCSOC4CTL.bit.ACQPS = acqps;
    // SOC5
    AdcbRegs.ADCSOC5CTL.bit.CHSEL = 5; // ADCINB5, U3_VPROPI
    AdcbRegs.ADCSOC5CTL.bit.ACQPS = acqps;

    // No interrupt setup for ADC B, since ADC A is providing this.

    EDIS;


    Thanks,
    Aaron
  • A few #def's that apply to the code above...

    #define PWM_TIMER_TBPRD 3000 // Period register
    #define PWM_MIN_CMP 0

    // From SPRUHM9D table 9-22
    #define BURSTTRIGSEL__EPWM7_ADCSOCA 0x11
    // From SPRUHM9D table 9-28
    #define TRIGSEL__EPWM7_ADCSOCA 0x11
    // Setting for ADC A, one less than the total samples
    #define ADC_A_BURST_LENGTH 6
    #define ADC_A_EOCx_TRIGGER_ADCINT1 6
    // Setting for ADC B, one less than the total samples
    #define ADC_B_BURST_LENGTH 5
  • Hi Aaron,

    I don't see anything strange in your code. Do you have other interrupts? Are you doing any software interrupt prioritization/nesting? Or were you saying in your original post that you had ruled them out as possible causes?

    Whitney
  • Aaron, any progress on this?

    Whitney
  • Thanks for asking Whitney.

    I ruled out other application interrupts as the cause by setting and clearing a GPIO output during each ISR and then watching it on an oscilloscope. None of the other ISRs appear to be interfering during the time periods when the ADC interrupt does not indicate a trigger (as evidenced by a second GPIO output).

    If there is a way to put similar instrumentation (GPIO outputs) in the dispatch ISR, then that would be one approach. But it sounds like TI_RTOS is designed to enable interrupts relatively quickly after this ISR fires, so it doesn't seem like that is a likely issue.

    Another approach would be for me to selectively disable all other code in the system, see if anything makes a difference, and then try to zero-in on what is affecting this. I have had to work on other priorities, so have not had a chance to try this yet.

    Aaron
  • Hi Aaron,

    I took another look at your code, and I suspect that burst mode isn't doing what you're expecting it to do. Burst mode walks through BURSTSIZE+1 consecutive SOCs every time a trigger is received. The SOC that the burst converts first is determined by the round-robin pointer--meaning it doesn't start over at SOC0 every time. Since you were doing bursts of 7 and there are 16 SOCs total, your interrupts were inconsistently spaced.

    I suspect you should just configure all the TRIGSEL fields of the SOCs you're using to the same source. They'll still convert one after another on a single trigger. Let me know if that gets you what you expected.

    Whitney

  • Aaron,

    Have you had a chance to look into my theory above? Any luck?

    Thanks,
    Whitney
  • Whitney,
    I was essentially following the burst mode example in SPRUHM9D but adding the interrupt trigger on end of conversion for SOC6. The round-robin of 16 channels does not appear to be an issue in the example. Apparently, only the channels that are setup should be relevant.
    In case RRPOINTER was an issue, I tried also clearing it at the end of each interrupt so that the next trigger will be forced to start converting with SOC0, but this did not change the behavior.

    However, I took your hint and abandoned burst mode. It doesn't make sense to my why burst mode was not giving a consistent trigger at the end of SOC6 conversion, but I have it working with the code below, which does not use burst mode.


    // From SPRUHM9D table 9-22
    #define BURSTTRIGSEL__EPWM7_ADCSOCA 0x11
    // From SPRUHM9D table 9-28
    #define TRIGSEL__EPWM7_ADCSOCA 0x11
    // Setting for ADC A to trigger on SOC6 completion
    #define ADC_A_EOCx_TRIGGER_ADCINT1 6

    // ADC A
    //
    // SOC0
    AdcaRegs.ADCSOC0CTL.bit.CHSEL = 1; //
    AdcaRegs.ADCSOC0CTL.bit.TRIGSEL = BURSTTRIGSEL__EPWM7_ADCSOCA;
    AdcaRegs.ADCSOC0CTL.bit.ACQPS = acqps;
    // SOC1
    AdcaRegs.ADCSOC1CTL.bit.CHSEL = 2; //
    AdcaRegs.ADCSOC1CTL.bit.TRIGSEL = BURSTTRIGSEL__EPWM7_ADCSOCA;
    AdcaRegs.ADCSOC1CTL.bit.ACQPS = acqps;
    // SOC2
    AdcaRegs.ADCSOC2CTL.bit.CHSEL = 3; //
    AdcaRegs.ADCSOC2CTL.bit.TRIGSEL = BURSTTRIGSEL__EPWM7_ADCSOCA;
    AdcaRegs.ADCSOC2CTL.bit.ACQPS = acqps;
    // SOC3
    AdcaRegs.ADCSOC3CTL.bit.CHSEL = 4; //
    AdcaRegs.ADCSOC3CTL.bit.TRIGSEL = BURSTTRIGSEL__EPWM7_ADCSOCA;
    AdcaRegs.ADCSOC3CTL.bit.ACQPS = acqps;
    // SOC4
    AdcaRegs.ADCSOC4CTL.bit.CHSEL = 5; //
    AdcaRegs.ADCSOC4CTL.bit.TRIGSEL = BURSTTRIGSEL__EPWM7_ADCSOCA;
    AdcaRegs.ADCSOC4CTL.bit.ACQPS = acqps;
    // SOC5
    AdcaRegs.ADCSOC5CTL.bit.CHSEL = 15; //
    AdcaRegs.ADCSOC5CTL.bit.TRIGSEL = BURSTTRIGSEL__EPWM7_ADCSOCA;
    AdcaRegs.ADCSOC5CTL.bit.ACQPS = acqps;
    // SOC6
    AdcaRegs.ADCSOC6CTL.bit.CHSEL = 14; //
    AdcaRegs.ADCSOC6CTL.bit.TRIGSEL = BURSTTRIGSEL__EPWM7_ADCSOCA;
    AdcaRegs.ADCSOC6CTL.bit.ACQPS = acqps;

    AdcaRegs.ADCINTSEL1N2.bit.INT1SEL = ADC_A_EOCx_TRIGGER_ADCINT1; // Set: EOCx is trigger for ADCINT1
    AdcaRegs.ADCINTSEL1N2.bit.INT1E = 1; // Enable flag
    AdcaRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; // Clear flag

    ///////////////////////////////////////////////////////////////////////////
    // ADC B
    //
    // SOC0
    AdcbRegs.ADCSOC0CTL.bit.CHSEL = 0; //
    AdcbRegs.ADCSOC0CTL.bit.TRIGSEL = BURSTTRIGSEL__EPWM7_ADCSOCA;
    AdcbRegs.ADCSOC0CTL.bit.ACQPS = acqps;
    // SOC1
    AdcbRegs.ADCSOC1CTL.bit.CHSEL = 1; //
    AdcbRegs.ADCSOC1CTL.bit.TRIGSEL = BURSTTRIGSEL__EPWM7_ADCSOCA;
    AdcbRegs.ADCSOC1CTL.bit.ACQPS = acqps;
    // SOC2
    AdcbRegs.ADCSOC2CTL.bit.CHSEL = 2; //
    AdcbRegs.ADCSOC2CTL.bit.TRIGSEL = BURSTTRIGSEL__EPWM7_ADCSOCA;
    AdcbRegs.ADCSOC2CTL.bit.ACQPS = acqps;
    // SOC3
    AdcbRegs.ADCSOC3CTL.bit.CHSEL = 3; //
    AdcbRegs.ADCSOC3CTL.bit.TRIGSEL = BURSTTRIGSEL__EPWM7_ADCSOCA;
    AdcbRegs.ADCSOC3CTL.bit.ACQPS = acqps;
    // SOC4
    AdcbRegs.ADCSOC4CTL.bit.CHSEL = 4; //
    AdcbRegs.ADCSOC4CTL.bit.TRIGSEL = BURSTTRIGSEL__EPWM7_ADCSOCA;
    AdcbRegs.ADCSOC4CTL.bit.ACQPS = acqps;
    // SOC5
    AdcbRegs.ADCSOC5CTL.bit.CHSEL = 5; //
    AdcbRegs.ADCSOC5CTL.bit.TRIGSEL = BURSTTRIGSEL__EPWM7_ADCSOCA;
    AdcbRegs.ADCSOC5CTL.bit.ACQPS = acqps;

    // No interrupt setup for ADC B, since ADC A is providing this.