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.

ADC Code Taking longer to execute - sometimes



I have a project that uses PWM1 to trigger an ADC conversion on a 28335 DSP.  The conversion complete generates an interrupt.  This is setup as an HWI using DSP Bios.  Most of the time the execution of the read takes less than 1 us.  Sometimes the execution is taking 13 us to execute.  The section of code is below that I am referencing.  I am using a GPIO pin that gets set at the beginning of execution and cleared at the end.  I have checked that there are no other places where this pin is being set.  It seems to me that the HWI may be getting interrupted somehow.  There is only one other HWI defined and this is for UART communication coming in periodically.  Both HWI are set to use the dispatcher with the interrupt mask set to self.  Any way that I can prevent this from being interrupted?

void F2833X_adcb_seq_drv_read(ADCVALSB *p)
{
       int16 DatQ15;
       int32 Tmp;

        ////ch 0
        p->Ch1RAW = AdcRegs.ADCRESULT0 >> 4;
        DatQ15 = AdcRegs.ADCRESULT0^0x8000;      // Convert raw result to Q15 (bipolar signal)
        Tmp = (int32)p->Ch1Gain*(int32)DatQ15;  // Tmp = gain*dat => Q28 = Q13*Q15
        p->Ch1Out = (int16)(Tmp>>13);           // Convert Q28 to Q15
        p->Ch1Out += p->Ch1Offset;              // Add offset */

        ////ch 1
        p->Ch2RAW = AdcRegs.ADCRESULT1 >> 4;
        DatQ15 = AdcRegs.ADCRESULT1^0x8000;      // Convert raw result to Q15 (bipolar signal)
        Tmp = (int32)p->Ch2Gain*(int32)DatQ15;  // Tmp = gain*dat => Q28 = Q13*Q15
        p->Ch2Out = (int16)(Tmp>>13);           // Convert Q28 to Q15
        p->Ch2Out += p->Ch2Offset;              // Add offset */

        ////ch 2
        p->Ch3RAW = AdcRegs.ADCRESULT2 >> 4;
        DatQ15 = AdcRegs.ADCRESULT2^0x8000;      // Convert raw result to Q15 (bipolar signal)
        Tmp = (int32)p->Ch3Gain*(int32)DatQ15;  // Tmp = gain*dat => Q28 = Q13*Q15
        p->Ch3Out = (int16)(Tmp>>13);           // Convert Q28 to Q15
        p->Ch3Out += p->Ch3Offset;              // Add offset */

        ////ch 3
        p->Ch4RAW = AdcRegs.ADCRESULT3 >> 4;
        DatQ15 = AdcRegs.ADCRESULT3^0x8000;      // Convert raw result to Q15 (bipolar signal)
        Tmp = (int32)p->Ch4Gain*(int32)DatQ15;  // Tmp = gain*dat => Q28 = Q13*Q15
        p->Ch4Out = (int16)(Tmp>>13);           // Convert Q28 to Q15
        p->Ch4Out += p->Ch4Offset;              // Add offset

        p->Ch5RAW = AdcRegs.ADCRESULT4 >> 4;
        DatQ15 = AdcRegs.ADCRESULT4^0x8000;      // Convert raw result to Q15 (bkipolar signal)
        Tmp = (int32)p->Ch5Gain*(int32)DatQ15;  // Tmp = gain*dat => Q28 = Q13*Q15
        p->Ch5Out = (int16)(Tmp>>13);           // Convert Q28 to Q15
        p->Ch5Out += p->Ch5Offset;              // Add offset

        p->Ch6RAW = AdcRegs.ADCRESULT5 >> 4;
        p->Ch7RAW = AdcRegs.ADCRESULT6 >> 4;
        p->Ch8RAW = AdcRegs.ADCRESULT7 >> 4;

        AdcRegs.ADCTRL2.all |= 0x4000;          // Reset the sequence
        AdcRegs.ADCTRL2.bit.RST_SEQ1 = 1;         // Reset SEQ1
        AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1;    
}            

  • Two questions and one solution/suggestion for you:

    Questions: Where exactly are you turning on the GPIO lines?  Do you have the associated code for them and the function call for the above function?

    and

    Suggestion:  One way to ensure the code is uninterrupted is running it with interrupts disabled such as:

    DINT;

    GPIO ON;

    Call function;

    GPIO OFF;

    EINT;

    This will at least give you clarity that it is an interrupt causing the delay or not.  However this comes at a cost to the rest of your program as it increases the interrupt latency significantly (other interrupts are going to get 1us+ delays).   This may not be an issue for you but it is good to avoid.  You can reduce the interrupt latency by condensing only the critical parts of that code into the critical section (time with interrupts off) as below:

    DINT;

    p->Ch1RAW  = AdcRegs.ADCRESULT0;

    p->Ch2RAW  = AdcRegs.ADCRESULT1;

    ...

    p->Ch8RAW  = AdcRegs.ADCRESULT7;

            AdcRegs.ADCTRL2.all |= 0x4000;          // Reset the sequence
            AdcRegs.ADCTRL2.bit.RST_SEQ1 = 1;         // Reset SEQ1
           AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1;    

    EINT;

    Then you do the rest of the code such as the shifts, calculations etc.  You shouldn't worry if these parts are interrupted, unless there is an interrupt which uses this data.  If there is then you can just disable that one.  This will reduce the interrupt latency to maybe 10-20 cycles at most which should not have any excess negative impact on other parts of your program.

     

    Tim

  • Thanks Tim.  The DINT;/EINT; worked and showed that the ISR was being interrupted.  This is a motor control ISR and cannot be interrupted.  The GPIO is being set prior to alling the adc read and cleared on the line after the call.  The other interrupt defined in the system is for a low priority UART.  A 1 us latency will not be a problem.  I would like to determine, at some point, how my ISR could get preempted.  From the documentation and training I have received it seems that this should be disabled by default.

     

    The code for the GPIOS is:

    GpioDataRegs.GPASET.bit.GPIO9 = 1;

    GpioDataRegs.GPACLEAR.bit.GPIO9 = 1;

  • Chris,

    This is not interrupt related, but could save you some cycles.

    On this device there are 2 locations to find the ADC Results.  The one that the code is using is in PF2, with the other ADC control register, and is left justified as you have noted with your right shifts for the "Raw" value.  The other thing to note is that everything on PF2 is wait stated by 2 cycles for reads.  This will add to your delay.

    There is another Result space we added that is in PF0, which is 0 WS.  The base address of these is 0x0B00, and is referenced in the header files as AdcMirrorRegs.  This will save you some cycles for the Raw reads.  Additionally, for the bipolar signal(which you keep in left justified for that reason) I believe it may be more efficient to use the Mirror Regs for this and do a left shift of 4.  I suppose it depends on how the compiler reads this info, but I believe an in line shift has 0 cycle penalty as it is read into the accumulator.  Even if this shift takes 1 cycle it is still more efficient than the 2WS reads.

    On another note, and this is interrupt related, is the ADC procession routine defined as an interrupt rather than a function call?  If this is just a function call then interrupts will not be disabled the function is invoked.  Interrupts do perform this automatic disabling of interrupts for the very reason you have stated.  I'm a bit out of my element on this aspect, not sure how BIOS treats a HWI vs a function call.

    Best,

    Matthew

  • Thanks Matt.  The motor control ISR is fired on an ADC conversion complete.  This ISR calls many functions, one of which is this ADC read function.  This is all within the scope of the ISR/HWI.

     

    I will look into the mirror registers.  Anything that can save cycles is good.

  • The interrupt premption may be part of BIOS.  As you noted from your training/documentation calling an interrupt disables the int flag (im pretty sure, but i haven't double checked) so there is no interrupt premption.  However RTOSs such as BIOS use interrupts for efficient task switching so usually reenable the interrupt enable flag in the RTOS code (again dont quote me on this for BIOS, i haven't used it yet, only other RTOSes).  This would explain why you were getting nested interrupt calls in your interrupt.  The way around it is above (as I showed before - which seems to work for you which is good) or there may be a flag somewhere in setting up your interrupt in BIOS which disables interrupt nesting.

    Tim