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.

F28M35x fails to boot up due to arbitrary changes in code

Other Parts Discussed in Thread: CONTROLSUITE

I've been dealing with an extremely frustrating problem with the bootup of the microcontroller. I'm working with the F28M35H52C1 Rev B.

Upon making seemingly innocuous and arbitrary changes to my code, the microcontroller fails to boot up properly, always ending up stuck in the same place in TI's ADC calibration code, specifically at this line:

while (Adc1Regs.ADCINTFLG.bit.ADCINT1 == 0) {}


This is in the funciton Uint16 Adc1Conversion(void) within the file F28M35x_Adc.c.

The changes that trigger this bootup bug have nothing to do with the ADCs, have nothing to do with bootup, and indeed sometimes are never called from anywhere in the code. The mere presence of this code triggers the bootup bug.

Here is an example of code that triggers the bug:

float ABO::GetMagnitude()
{
    return InvSqrt(A*A + B*B);
}


This is just returning the magnitude of a vector. Here is the seemingly trivial change that fixes the bug, and allows the microcontroller to boot up properly:

float ABO::GetMagnitude()
{
    float mag = InvSqrt(A*A + B*B);
    return mag;
}

The only change was storing the return value in a local variable first. This is utterly incomprehensible to me.
Note that sometimes, I need to change code the other way to get it to work - meaning I need to remove a local variable and return the value directly. Also, after some time has passed and multiple revisions have been made in the overall code, I can come back to the example shown and revert the "fix" without triggering the bootup bug. But I can check out the faulty commit from history again and faithfully reproduce the bug.

Note that this is just ONE example of code that breaks the bootup. Other seemingly innocuous changes also trigger the bug. Examples include commenting out an empty function that is not called from anywhere; commenting out a global variable that is not used anywhere; and other similar arbitrary examples that do not make sense to me.

This bug seems completely arbitrary based on my code, but it is not random! It is deterministic. The same code will always exhibit the bug or not.

If I had to try to use an analogy for my experience with this bug, the best thing that comes to mind is that it's as if the bug happens when a hash function of my code is divisible by 3, or something silly like that. Completely arbitrary, seemingly chaotic, but deterministic.

I've tried multiple controlCards, (all RevB), and it is consistent across control cards. I've tried it from multiple computers, with different versions of CCS and the compiler, and it does not matter. I've tried increasing the stack size, which does not fix the bug. I've tried increasing the size of various memory blocks in the linker command file, and nothing fixes the bug.


This bug sometimes doesn't show up for days, but when it does, I then have to go back and revert changes to my code until I find the source, then figure out which arbitrary line is causing it and then figure out which meaningless code change (like storing the return value in a local variable first) fixes the issue. It's frustrating.

I hope I've provided enough information to help track down the issue. Any help is greatly appreciated. This issue has been extremely frustrating to my team.

  • Michael,

    when is this code getting executed and where is this code getting executed from? is this code executed only once or multiple times during the application execution?

    usually the locals are allocated from stack, so check if the SP is properly initialized before this code gets executed, how much stack is allocated and how much of it is used, are there any arrays before this that are overflowing else where?

    also since you can get a deterministic error behavior it is good, can you step through the disassembly and see what exactly is going wrong here, the retruns fails or the functions call fails and what exactly is different in relevant memory locations between when it works and when it doesn;t work?

    Hope this helps.

    Best Regards

    Santosh Athuru

  • Hi Santosh,


    Don't latch on too strongly to the example provided, that is only one case out of many. I have explored the stack and I don't think that's the issue. To reiterate some points from my post:

    1. Increasing stack size does not fix the issue.

    2. Sometimes the code has to be adjusted the OTHER way - remove the local variable and return directly.

    3. Sometimes the fix has nothing to do with the stack - removing a global variable or function, for example.

    4. The code that needs to be changed is sometimes never called from anywhere else in the code.

  • Also, to add to this:

    I have tried stepping through the bootup before, and it seems to progress just fine until that while loop I posted. Then it just remains stuck in that while loop indefinitely.


    I have not stepped through the assembly though, as I am not familiar enough with assembly to know what to look for.

    Hope that helps. Any ideas of how I should debug next?


    Thanks

  • Hi Santosh,


    Any further steps I could try to debug this?

    If there is some insight that can be gained by stepping through in disassembly mode rather than the normal mode, please let me know and I am happy to try that.


    Mike

  • Michael,

    I have been thinking about it, since you have that function in hand which works or doesn't work based on something you change in the code that would be a better point to start debug. As you have hinted there could be a bigger under lying issue here and since yours is a C++ application this might have to do something with the memory and when and where classes are initiated. If there is problem with the initialization code or the related memory the failure could happen somewhere else. Inspect your memory map file to understand the different sections. If it is not stack it could be that the RAM is being over written by some code.

    Try to start looking at the assembly and the specific instruction that is executed when it fails and compare the same instruction execution when it works just fine, there will be a memory write or read or some other operation that is failing and see why it is failing.

    try to initialize all the RAM memory to a pattern (non-zero) and see if you can get the application to fail else where and again try to find exactly why it is failing.

    Also try to move the NVIC_BASE to application RAM and install exception handlers to catch any exceptions, take a look at different faults that could happen on ARM and see which one is happening.

    are you executing from flash or RAM or both?

    Hope this helps

    Best Regards

    Santosh

  • The disassembly at the line where the bootup hangs looks about the same whether it is buggy or not:

    Buggy:

    100c7d:   761F01C4    MOVW         DP, #0x1c4
    100c7f:   4004        TBIT         @0x4, #0x0
    100c80:   EFFD        SBF          C$L1, NTC

    Working:

    100c7e:   761F01C4    MOVW         DP, #0x1c4
    100c80:   4004        TBIT         @0x4, #0x0
    100c81:   EFFD        SBF          C$L1, NTC

    Memory around the line:

    Buggy:

    0x100c7d   761F    01C4    4004    EFFD    1A05    0001    761F    002C    0E00    0746    1E46    0E01    0746    1E46    0E02    0746    1E46    0E03    0746    1E46    0E04    0746    1E46    0E05    0746

    Working:


    0x100c7e    C$L1
    0x100c7e    761F    01C4    4004    EFFD    1A05    0001    761F    002C    0E00    0746    1E46    0E01    0746    1E46    0E02    0746    1E46    0E03    0746    1E46    0E04    0746    1E46    0E05    0746


    I have tried increasing the allocated memory for each of the sections that seemed relevant in the linker file, such as .text, .ebss, .cinit, etc. Nothing I tried fixed the bug.


    I've attached the memory map file (in the buggy case), in case you want to look at it. I don't know what to look for exactly but if you can advise me I can try looking for something.

    5873.MG C28.txt

    > try to initialize all the RAM memory to a pattern (non-zero) and see if you can get the application to fail else where and again try to find exactly why it is failing.

    > Also try to move the NVIC_BASE to application RAM and install exception handlers to catch any exceptions, take a look at different faults that could happen on ARM and see which one is happening.

    Unfortunately, i don't know how to do that. Happy to do it though if you can instruct me.

    I believe most of the code is from flash, this line loads code into RAM:

    memcpy(&RamfuncsRunStart, &RamfuncsLoadStart, (size_t) &RamfuncsLoadSize);

    And the only ramfuncs code that I know of is:

    #pragma CODE_SECTION(InitFlash, "ramfuncs");
    #pragma CODE_SECTION(SetupFlash, "ramfuncs");
    #pragma CODE_SECTION(FlashGainPump,"ramfuncs");
    #pragma CODE_SECTION(FlashLeavePump,"ramfuncs");

  • Maybe the problem is not the line itself where the code hangs, but rather what that line is doing.

    It is an indefinite while loop waiting for Adc1Regs.ADCINTFLG.bit.ADCINT1 to be flagged. Why would that register fail in one case and not the other.

  • can you add a delay between the reads to the ADCINTFLG in the ADC regs and try? I'm not sure if back to back reads to ADC from M3 have any restrictions, I will ask someone from analog team to look at it as well.

    Best Regards

    Santosh Athuru

  • I changed the line to:

    while (Adc1Regs.ADCINTFLG.bit.ADCINT1 == 0) {
                DELAY_US(ADC_usDELAY);
            }

    The bootup is still stuck there.

  • > I'm not sure if back to back reads to ADC from M3 have any restrictions

    Just to clarify, this function is on the C28. It's in the file F28M35x_Adc.c


    Out of curiosity, I just checked what the status on the M3 is during this. The M3 seems to boot up just fine and is hanging out in the main loop.

  • are watchdogs disabled on M3? is there any reason you suspect that while loop reading ADC flag on C28x?

    Best Regards

    Santosh

  • Watchdogs are disabled.

    That while loop is where the bootup process stops. It never returns from the while loop when the bug happens.

  • can you attach the bootup/init code for someone from analog team to review it?

    Best Regards

    Santosh

  • This is not my code where it breaks, it is TI's own code. This is in the function Uint16 Adc1Conversion(void) within the file F28M35x_Adc.c. At this point, it hasn't reached any of my own code.

    In fact, the first line of my code is exactly:

    void main(void)
    {
        InitAdcClocks();


    The function InitAdcClocks()

    void InitAdcClocks()
    {
    	// *IMPORTANT*
    	// The Device_cal function MUST be called
    	// for the ADC and oscillators to function according
    	// to specification. The clocks to the ADC MUST be enabled before calling
    	// this function. See the device data manual and/or the ADC Reference
    	// Manual for more information.
    	
    	// Workaround for RevB Advisory in the Silicon Errata
    	*(unsigned int*) 0x4E58 = 7;
    	
    	// Setup ADC Clock as 1/4 System Clock, which gives max of 37.5MHz
    	(**InitAnalogSystemClock )(ACLKDIV4);
    	
    	EALLOW;
    	// Initialize the Analog Sub-System clocks
    	while ((**AnalogClockEnable )(AnalogConfig1, ADC1_ENABLE))
    		;    // Enable ADC 1
    	while ((**AnalogClockEnable )(AnalogConfig2, ADC2_ENABLE))
    		;    // Enable ADC 2
    		
    	// Workaround for RevB Advisory in the Silicon Errata
    	(**ReadAnalogClockStatus )(AnalogConfig2);
    	
    	// Reset both ADC in case the last reset was a debugger reset (which doesn't 
    	// reset the ADCs
    	ADC1.ADCCTL1.bit.RESET = 1;
    	ADC2.ADCCTL1.bit.RESET = 1;
    	
    	// Wait to ensure ADCs are out of reset before device cal is called
    	__asm(" nop");
    	__asm(" nop");
    	
    	// Calibrate the device for temperature
    	(**Device_Cal )();
    	EDIS;
    	
    	// Workaround for RevB Advisory in the Silicon Errata
    	Adc1OffsetSelfCal();
    	Adc2OffsetSelfCal();
    	
    	// Clear assignments made from the Self Calibration function
        Adc1Regs.ADCINTSOCSEL1.bit.SOC0  = 0;
        Adc1Regs.ADCINTSOCSEL1.bit.SOC1  = 0;
        Adc1Regs.ADCINTSOCSEL1.bit.SOC2  = 0;
        Adc1Regs.ADCINTSOCSEL1.bit.SOC3  = 0;
        Adc1Regs.ADCINTSOCSEL1.bit.SOC4  = 0;
        Adc1Regs.ADCINTSOCSEL1.bit.SOC5  = 0;
        Adc1Regs.ADCINTSOCSEL1.bit.SOC6  = 0;
        Adc1Regs.ADCINTSOCSEL1.bit.SOC7  = 0;
        Adc1Regs.ADCINTSOCSEL2.bit.SOC8  = 0;
        Adc1Regs.ADCINTSOCSEL2.bit.SOC9  = 0;
        Adc1Regs.ADCINTSOCSEL2.bit.SOC10 = 0;
        Adc1Regs.ADCINTSOCSEL2.bit.SOC11 = 0;
        Adc1Regs.ADCINTSOCSEL2.bit.SOC12 = 0;
        Adc1Regs.ADCINTSOCSEL2.bit.SOC13 = 0;
        Adc1Regs.ADCINTSOCSEL2.bit.SOC14 = 0;
        Adc1Regs.ADCINTSOCSEL2.bit.SOC15 = 0;
    }

    The call to Adc1OffsetSelfCal then calls Adc1Conversion within it, which is where the while loop resides. Both are TI provided functions. Note that Adc1OffsetSelfCal and Adc2OffsetSelfCal are added according to the notes in the Silicon Errata, page 16. Otherwise it is identical to TI provided code.

  • if you comment out the problematic function call for ex: the while loop then would you application run properly?

    Best Regards

    Santosh

  • Well, yes, commenting it out does skip the line, but then there is a problem with the next similar line, for INT2:

    while (Adc1Regs.ADCINTFLG.bit.ADCINT2 == 0) {}

    I could also comment that out, but then Adc2Conversion has the same problem, and so on.

    But at this point the ADC calibration is entirely useless and in fact counterproductive, as the following lines are attempting to use the ADC results to calibrate, but skipping those lines means the ADC hasn't even finished, so we are calibrating with garbage.

  • hmm...thanks. Looks like something is wrong with the adc1regs. Let me find if someone from analog team would be able to help.

    Best Regards

    Santosh Athuru

  • Hi Santosh,


    Just wondering if anyone from the analog team has had a chance to look at this issue?

    I am happy to help debug this further if you have any suggestions.

    Mike

  • Mike,

    Getting stuck in the ADCINTFLG polling loop suggests that the ADC module is failing in some manner.

    Can you read back the values the ADC registers when the boot process gets stuck here?  The most important registers would be ADCCTL1, ADCCTL2, ADCINTFLG, ADCINTOVF, ADCSOCFLG1, and ADCSOCFRC1 based on your symptoms.

    It might be easiest to save off the memory map range of 0x00 - 0x2F for each ADC and post the data here.  That would be 0x7100-0x712F and 0x7180-0x71AF.

    -Tommy

  • Hi Tommy,

    I paused the core when the bug occurs, and looked at the memory browser. Attached is a file from the Save Memory button on the Memory Browser, starting 0x7100 of length 0x0200.

    2210.Mem.dat


    I've also copied the registers below from the Registers window, maybe easier to view:

    ADC1:

    ADCCTL1	0x4706	ADC Control 1 [Memory Mapped]	
    ADCCTL2	0x0000	ADC Control 2 [Memory Mapped]	
    ADCINTFLG	0x0000	ADC Interrupt Flag [Memory Mapped]	
    ADCINTFLGCLR	0x0000	ADC Interrupt Flag Clear [Memory Mapped]	
    ADCINTOVF	0x0001	ADC Interrupt Overflow [Memory Mapped]	
    ADCINTOVFCLR	0x0000	ADC Interrupt Overflow Clear [Memory Mapped]	
    INTSEL1N2	0x2E26	ADC Interrupt 1 and 2 Selection [Memory Mapped]	
    INTSEL3N4	0x0000	ADC Interrupt 3 and 4 Selection [Memory Mapped]	
    INTSEL5N6	0x0000	ADC Interrupt 5 and 6 Selection [Memory Mapped]	
    INTSEL7N8	0x0000	ADC Interrupt 7 and 8 Selection [Memory Mapped]	
    INTSEL9N10	0x0000	ADC Interrupt 9 and 10 Selection [Memory Mapped]	
    SOCPRICTL	0x00E0	ADC SOC Priority Control [Memory Mapped]	
    ADCSAMPLEMODE	0x0000	ADC Sampling Mode [Memory Mapped]	
    ADCINTSOCSEL1	0xAAAA	ADC Interrupt SOC Selection 1 [Memory Mapped]	
    ADCINTSOCSEL2	0x5555	ADC Interrupt SOC Selection 2 [Memory Mapped]	
    ADCSOCFLG1	0x0000	ADC SOC Flag 1 [Memory Mapped]	
    ADCSOCFRC1	0x0000	ADC SOC Flag Force 1 [Memory Mapped]	
    

    ADC2:

    ADCCTL1	0x4000	ADC Control 1 [Memory Mapped]	
    ADCCTL2	0x0000	ADC Control 2 [Memory Mapped]	
    ADCINTFLG	0x0000	ADC Interrupt Flag [Memory Mapped]	
    ADCINTFLGCLR	0x0000	ADC Interrupt Flag Clear [Memory Mapped]	
    ADCINTOVF	0x0000	ADC Interrupt Overflow [Memory Mapped]	
    ADCINTOVFCLR	0x0000	ADC Interrupt Overflow Clear [Memory Mapped]	
    INTSEL1N2	0x0000	ADC Interrupt 1 and 2 Selection [Memory Mapped]	
    INTSEL3N4	0x0000	ADC Interrupt 3 and 4 Selection [Memory Mapped]	
    INTSEL5N6	0x0000	ADC Interrupt 5 and 6 Selection [Memory Mapped]	
    INTSEL7N8	0x0000	ADC Interrupt 7 and 8 Selection [Memory Mapped]	
    INTSEL9N10	0x0000	ADC Interrupt 9 and 10 Selection [Memory Mapped]	
    SOCPRICTL	0x0400	ADC SOC Priority Control [Memory Mapped]	
    ADCSAMPLEMODE	0x0000	ADC Sampling Mode [Memory Mapped]	
    ADCINTSOCSEL1	0x0000	ADC Interrupt SOC Selection 1 [Memory Mapped]	
    ADCINTSOCSEL2	0x0000	ADC Interrupt SOC Selection 2 [Memory Mapped]	
    ADCSOCFLG1	0x0000	ADC SOC Flag 1 [Memory Mapped]	
    ADCSOCFRC1	0x0000	ADC SOC Flag Force 1 [Memory Mapped]	
    

  • For reference, if I edit my code slightly to "fix" the bug, where these "fixes" have nothing to do with the ADCs, I get the following:

    ADC1:

    ADCCTL1    0x4706    ADC Control 1 [Memory Mapped]    
    ADCCTL2    0x0000    ADC Control 2 [Memory Mapped]    
    ADCINTFLG    0x0003    ADC Interrupt Flag [Memory Mapped]    
    ADCINTFLGCLR    0x0000    ADC Interrupt Flag Clear [Memory Mapped]    
    ADCINTOVF    0x0001    ADC Interrupt Overflow [Memory Mapped]    
    ADCINTOVFCLR    0x0000    ADC Interrupt Overflow Clear [Memory Mapped]    
    INTSEL1N2    0x2E26    ADC Interrupt 1 and 2 Selection [Memory Mapped]    
    INTSEL3N4    0x0000    ADC Interrupt 3 and 4 Selection [Memory Mapped]    
    INTSEL5N6    0x0000    ADC Interrupt 5 and 6 Selection [Memory Mapped]    
    INTSEL7N8    0x0000    ADC Interrupt 7 and 8 Selection [Memory Mapped]    
    INTSEL9N10    0x0000    ADC Interrupt 9 and 10 Selection [Memory Mapped]    
    SOCPRICTL    0x00E0    ADC SOC Priority Control [Memory Mapped]    
    ADCSAMPLEMODE    0x0000    ADC Sampling Mode [Memory Mapped]    
    ADCINTSOCSEL1    0xAAAA    ADC Interrupt SOC Selection 1 [Memory Mapped]    
    ADCINTSOCSEL2    0x5555    ADC Interrupt SOC Selection 2 [Memory Mapped]    
    ADCSOCFLG1    0x0000    ADC SOC Flag 1 [Memory Mapped]    
    ADCSOCFRC1    0x0000    ADC SOC Flag Force 1 [Memory Mapped]    



    ADC2:

    ADCCTL1    0x4000    ADC Control 1 [Memory Mapped]    
    ADCCTL2    0x0000    ADC Control 2 [Memory Mapped]    
    ADCINTFLG    0x0000    ADC Interrupt Flag [Memory Mapped]    
    ADCINTFLGCLR    0x0000    ADC Interrupt Flag Clear [Memory Mapped]    
    ADCINTOVF    0x0000    ADC Interrupt Overflow [Memory Mapped]    
    ADCINTOVFCLR    0x0000    ADC Interrupt Overflow Clear [Memory Mapped]    
    INTSEL1N2    0x0000    ADC Interrupt 1 and 2 Selection [Memory Mapped]    
    INTSEL3N4    0x0000    ADC Interrupt 3 and 4 Selection [Memory Mapped]    
    INTSEL5N6    0x0000    ADC Interrupt 5 and 6 Selection [Memory Mapped]    
    INTSEL7N8    0x0000    ADC Interrupt 7 and 8 Selection [Memory Mapped]    
    INTSEL9N10    0x0000    ADC Interrupt 9 and 10 Selection [Memory Mapped]    
    SOCPRICTL    0x0400    ADC SOC Priority Control [Memory Mapped]    
    ADCSAMPLEMODE    0x0000    ADC Sampling Mode [Memory Mapped]    
    ADCINTSOCSEL1    0x0000    ADC Interrupt SOC Selection 1 [Memory Mapped]    
    ADCINTSOCSEL2    0x0000    ADC Interrupt SOC Selection 2 [Memory Mapped]    
    ADCSOCFLG1    0x0000    ADC SOC Flag 1 [Memory Mapped]    
    ADCSOCFRC1    0x0000    ADC SOC Flag Force 1 [Memory Mapped] 

       EDIT: Nevermind, they are not identical. The first buggy case has 0x0 for ADCINTFLG, while the 2nd case has 0x3. That makes a lot of sense given that the loop is waiting for that flag.

  • For further reference, here is the full code:

    void InitADCClocks()
    {
    	// *IMPORTANT*
    	// The Device_cal function MUST be called
    	// for the ADC and oscillators to function according
    	// to specification. The clocks to the ADC MUST be enabled before calling
    	// this function. See the device data manual and/or the ADC Reference
    	// Manual for more information.
    	
    	// Workaround for RevB Advisory in the Silicon Errata
    	*(unsigned int*) 0x4E58 = 7;
    	
    	// Setup ADC Clock as 1/4 System Clock, which gives max of 37.5MHz
    	(**InitAnalogSystemClock )(ACLKDIV4);
    	
    	EALLOW;
    	// Initialize the Analog Sub-System clocks
    	while ((**AnalogClockEnable )(AnalogConfig1, ADC1_ENABLE))
    		;    // Enable ADC 1
    	while ((**AnalogClockEnable )(AnalogConfig2, ADC2_ENABLE))
    		;    // Enable ADC 2
    		
    	// Workaround for RevB Advisory in the Silicon Errata
    	(**ReadAnalogClockStatus )(AnalogConfig2);
    	
    	// Reset both ADC in case the last reset was a debugger reset (which doesn't 
    	// reset the ADCs
    	ADC1.ADCCTL1.bit.RESET = 1;
    	ADC2.ADCCTL1.bit.RESET = 1;
    	
    	// Wait to ensure ADCs are out of reset before device cal is called
    	__asm(" nop");
    	__asm(" nop");
    	
    	// Calibrate the device for temperature
    	(**Device_Cal )();
    	EDIS;
    	
    	// Workaround for RevB Advisory in the Silicon Errata
    	Adc1OffsetSelfCal();
    	Adc2OffsetSelfCal();
    	
    	// Clear assignments made from the Self Calibration function
        Adc1Regs.ADCINTSOCSEL1.bit.SOC0  = 0;
        Adc1Regs.ADCINTSOCSEL1.bit.SOC1  = 0;
        Adc1Regs.ADCINTSOCSEL1.bit.SOC2  = 0;
        Adc1Regs.ADCINTSOCSEL1.bit.SOC3  = 0;
        Adc1Regs.ADCINTSOCSEL1.bit.SOC4  = 0;
        Adc1Regs.ADCINTSOCSEL1.bit.SOC5  = 0;
        Adc1Regs.ADCINTSOCSEL1.bit.SOC6  = 0;
        Adc1Regs.ADCINTSOCSEL1.bit.SOC7  = 0;
        Adc1Regs.ADCINTSOCSEL2.bit.SOC8  = 0;
        Adc1Regs.ADCINTSOCSEL2.bit.SOC9  = 0;
        Adc1Regs.ADCINTSOCSEL2.bit.SOC10 = 0;
        Adc1Regs.ADCINTSOCSEL2.bit.SOC11 = 0;
        Adc1Regs.ADCINTSOCSEL2.bit.SOC12 = 0;
        Adc1Regs.ADCINTSOCSEL2.bit.SOC13 = 0;
        Adc1Regs.ADCINTSOCSEL2.bit.SOC14 = 0;
        Adc1Regs.ADCINTSOCSEL2.bit.SOC15 = 0;
    }

    void Adc1OffsetSelfCal()
    {
        Uint16 AdcConvMean;
        EALLOW;
        Adc1Regs.ADCCTL1.bit.ADCREFSEL = 0;                     //Select internal
                                                                // reference mode
        Adc1Regs.ADCCTL1.bit.VREFLOCONV = 1;                    //Select VREFLO
                                                                // internal
                                                                // connection on B5
        Adc1ChanSelect(13);                                     //Select channel B5
                                                                // for all SOC
        Adc1Regs.ADCOFFTRIM.bit.OFFTRIM = 80;                   //Apply artificial
                                                                // offset (+80) to
                                                                // account for a
                                                                // negative offset
                                                                // that may reside
                                                                // in the ADC1 core
        AdcConvMean = Adc1Conversion();                         //Capture ADC1
                                                                // conversion on
                                                                // VREFLO
        Adc1Regs.ADCOFFTRIM.bit.OFFTRIM = 80 - AdcConvMean;     //Set offtrim
                                                                // register with new
                                                                // value (i.e remove
                                                                // artical offset
                                                                // (+80) and create
                                                                // a two's
                                                                // compliment of the
                                                                // offset error)
        Adc1Regs.ADCCTL1.bit.VREFLOCONV = 0;                    //Select external
                                                                // ADCIN5 input pin
                                                                // on B5
        EDIS;
    }

    void Adc1ChanSelect(Uint16 ch_no)
    {
        Adc1Regs.ADCSOC0CTL.bit.CHSEL= ch_no;
        Adc1Regs.ADCSOC1CTL.bit.CHSEL= ch_no;
        Adc1Regs.ADCSOC2CTL.bit.CHSEL= ch_no;
        Adc1Regs.ADCSOC3CTL.bit.CHSEL= ch_no;
        Adc1Regs.ADCSOC4CTL.bit.CHSEL= ch_no;
        Adc1Regs.ADCSOC5CTL.bit.CHSEL= ch_no;
        Adc1Regs.ADCSOC6CTL.bit.CHSEL= ch_no;
        Adc1Regs.ADCSOC7CTL.bit.CHSEL= ch_no;
        Adc1Regs.ADCSOC8CTL.bit.CHSEL= ch_no;
        Adc1Regs.ADCSOC9CTL.bit.CHSEL= ch_no;
        Adc1Regs.ADCSOC10CTL.bit.CHSEL= ch_no;
        Adc1Regs.ADCSOC11CTL.bit.CHSEL= ch_no;
        Adc1Regs.ADCSOC12CTL.bit.CHSEL= ch_no;
        Adc1Regs.ADCSOC13CTL.bit.CHSEL= ch_no;
        Adc1Regs.ADCSOC14CTL.bit.CHSEL= ch_no;
        Adc1Regs.ADCSOC15CTL.bit.CHSEL= ch_no;
    }

    Uint16 Adc1Conversion(void)
    {
        Uint16 index, SampleSize, Mean, ACQPS_Value;
        Uint32 Sum;
    
        index       = 0;            //initialize index to 0
        SampleSize  = 256;          //set sample size to 256 (**NOTE: Sample size
                                    // must be multiples of 2^x where is an integer
                                    // >= 4)
        Sum         = 0;            //set sum to 0
        Mean        = 999;          //initialize mean to known value
    
        //Set the ADC1 sample window to the desired value (Sample window = ACQPS +
        // 1)
        ACQPS_Value = 6;
        Adc1Regs.ADCSOC0CTL.bit.ACQPS  = ACQPS_Value;
        Adc1Regs.ADCSOC1CTL.bit.ACQPS  = ACQPS_Value;
        Adc1Regs.ADCSOC2CTL.bit.ACQPS  = ACQPS_Value;
        Adc1Regs.ADCSOC3CTL.bit.ACQPS  = ACQPS_Value;
        Adc1Regs.ADCSOC4CTL.bit.ACQPS  = ACQPS_Value;
        Adc1Regs.ADCSOC5CTL.bit.ACQPS  = ACQPS_Value;
        Adc1Regs.ADCSOC6CTL.bit.ACQPS  = ACQPS_Value;
        Adc1Regs.ADCSOC7CTL.bit.ACQPS  = ACQPS_Value;
        Adc1Regs.ADCSOC8CTL.bit.ACQPS  = ACQPS_Value;
        Adc1Regs.ADCSOC9CTL.bit.ACQPS  = ACQPS_Value;
        Adc1Regs.ADCSOC10CTL.bit.ACQPS = ACQPS_Value;
        Adc1Regs.ADCSOC11CTL.bit.ACQPS = ACQPS_Value;
        Adc1Regs.ADCSOC12CTL.bit.ACQPS = ACQPS_Value;
        Adc1Regs.ADCSOC13CTL.bit.ACQPS = ACQPS_Value;
        Adc1Regs.ADCSOC14CTL.bit.ACQPS = ACQPS_Value;
        Adc1Regs.ADCSOC15CTL.bit.ACQPS = ACQPS_Value;
    
        //Enable ping-pong sampling
    
        // Enabled ADCINT1 and ADCINT2
        Adc1Regs.INTSEL1N2.bit.INT1E = 1;
        Adc1Regs.INTSEL1N2.bit.INT2E = 1;
    
        // Disable continuous sampling for ADCINT1 and ADCINT2
        Adc1Regs.INTSEL1N2.bit.INT1CONT = 0;
        Adc1Regs.INTSEL1N2.bit.INT2CONT = 0;
    
        Adc1Regs.ADCCTL1.bit.INTPULSEPOS = 1;    //ADCINTs trigger at end of
                                                 // conversion
    
        // Setup ADCINT1 and ADCINT2 trigger source
        Adc1Regs.INTSEL1N2.bit.INT1SEL = 6;      //EOC6 triggers ADCINT1
        Adc1Regs.INTSEL1N2.bit.INT2SEL = 14;     //EOC14 triggers ADCINT2
    
        // Setup each SOC's ADCINT trigger source
        Adc1Regs.ADCINTSOCSEL1.bit.SOC0  = 2;    //ADCINT2 starts SOC0-7
        Adc1Regs.ADCINTSOCSEL1.bit.SOC1  = 2;
        Adc1Regs.ADCINTSOCSEL1.bit.SOC2  = 2;
        Adc1Regs.ADCINTSOCSEL1.bit.SOC3  = 2;
        Adc1Regs.ADCINTSOCSEL1.bit.SOC4  = 2;
        Adc1Regs.ADCINTSOCSEL1.bit.SOC5  = 2;
        Adc1Regs.ADCINTSOCSEL1.bit.SOC6  = 2;
        Adc1Regs.ADCINTSOCSEL1.bit.SOC7  = 2;
        Adc1Regs.ADCINTSOCSEL2.bit.SOC8  = 1;    //ADCINT1 starts SOC8-15
        Adc1Regs.ADCINTSOCSEL2.bit.SOC9  = 1;
        Adc1Regs.ADCINTSOCSEL2.bit.SOC10 = 1;
        Adc1Regs.ADCINTSOCSEL2.bit.SOC11 = 1;
        Adc1Regs.ADCINTSOCSEL2.bit.SOC12 = 1;
        Adc1Regs.ADCINTSOCSEL2.bit.SOC13 = 1;
        Adc1Regs.ADCINTSOCSEL2.bit.SOC14 = 1;
        Adc1Regs.ADCINTSOCSEL2.bit.SOC15 = 1;
    
    //    DELAY_US(ADC_usDELAY);                  // Delay before converting ADC1
                                                // channels
    
        //ADC1 Conversion
    
        Adc1Regs.ADCSOCFRC1.all = 0x00FF;  // Force Start SOC0-7 to begin ping-pong
                                           // sampling
    
        while( index < SampleSize ) {
    
            //Wait for ADCINT1 to trigger, then add ADCRESULT0-7 registers to sum
            while (Adc1Regs.ADCINTFLG.bit.ADCINT1 == 0) {}
            Adc1Regs.ADCINTFLGCLR.bit.ADCINT1 = 1;   //Must clear ADCINT1 flag since
                                                     // INT1CONT = 0
            Sum += Adc1Result.ADCRESULT0;
            Sum += Adc1Result.ADCRESULT1;
            Sum += Adc1Result.ADCRESULT2;
            Sum += Adc1Result.ADCRESULT3;
            Sum += Adc1Result.ADCRESULT4;
            Sum += Adc1Result.ADCRESULT5;
            Sum += Adc1Result.ADCRESULT6;
            Sum += Adc1Result.ADCRESULT7;
    
            //Wait for ADCINT2 to trigger, then add ADCRESULT8-15 registers to sum
            while (Adc1Regs.ADCINTFLG.bit.ADCINT2 == 0) {}
            Adc1Regs.ADCINTFLGCLR.bit.ADCINT2 = 1;   //Must clear ADCINT2 flag since
                                                     // INT2CONT = 0
            Sum += Adc1Result.ADCRESULT8;
            Sum += Adc1Result.ADCRESULT9;
            Sum += Adc1Result.ADCRESULT10;
            Sum += Adc1Result.ADCRESULT11;
            Sum += Adc1Result.ADCRESULT12;
            Sum += Adc1Result.ADCRESULT13;
            Sum += Adc1Result.ADCRESULT14;
            Sum += Adc1Result.ADCRESULT15;
    
            index+=16;
    
        } // end data collection
    
        //Disable ADCINT1 and ADCINT2 to STOP the ping-pong sampling
        Adc1Regs.INTSEL1N2.bit.INT1E = 0;
        Adc1Regs.INTSEL1N2.bit.INT2E = 0;
    
        Mean = Sum / SampleSize;    //Calculate average ADC1 sample value
    
        return Mean;                //return the average
    
    }

  • Mike,

    Thanks for the register information.

    I think what might be happening is that at the end of the Adc1Conversion() function, there is a potential race condition between disabling the interrupt ping-pong sampling mechanism (by disabling the interrupts in INTSEL1N2) and the continuous SOC hardware sampling taking place in the background.

    If the background SOC sampling completes SOC6 before INT1E is disabled, the function can exit with an unserviced interrupt.  That would explain why you also get an ADCINTOVF bit set.  Any subsequent ADC sampling using code similar to Adc1Conversion() would stall because there is an unserviced interrupt pending.

    Honestly, I would have expected the ADCINTFLG register to continue to update, but it looks like the behavior gets quirky once an overflow condition is asserted.

    If this is the case, any one of the following modifications to Adc1Conversion (and Adc2Conversion) can help fix the problem.  I would recommend making all of the modifications just to be extra safe.

    Modification 1 - Make the interrupt generation continuous by changing the INTSEL1N2 settings:

    Adc1Regs.INTSEL1N2.bit.INT1CONT = 1;
    Adc1Regs.INTSEL1N2.bit.INT2CONT = 1;

    Modification 2 - Clear any pending interrupt flags just prior to the "Adc1Regs.ADCSOCFRC1.all = 0x00FF" command:

    Adc1Regs.ADCINTFLGCLR.bit.ADCINT1 = 1;
    Adc1Regs.ADCINTFLGCLR.bit.ADCINT2 = 1;

    Adc1Regs.ADCINTOVFCLR.bit.ADCINT1 = 1;
    Adc1Regs.ADCINTOVFCLR.bit.ADCINT2 = 1;

    Modification 3 - Clear any pending interrupt flags just prior to the return command at the end of the function:

    Adc1Regs.ADCINTFLGCLR.bit.ADCINT1 = 1;
    Adc1Regs.ADCINTFLGCLR.bit.ADCINT2 = 1;

    Adc1Regs.ADCINTOVFCLR.bit.ADCINT1 = 1;
    Adc1Regs.ADCINTOVFCLR.bit.ADCINT2 = 1;

    Let me know if this helps.

    -Tommy

  • Thanks so much Tommy! The combination of modifications fixes the problem. This will make life so much easier!

    Since this is TI provided code, can this be fixed for the next code version so others don't have the same problem? Just to confirm, running this code as part of the startup process is recommended, right?


    Just out of curiosity, do you have an idea of why arbitrary code changes that have nothing to do with the ADCs would trigger the bug? It's quite mysterious to me.

    While we are looking at this code, perhaps you would be interesting in getting to the bottom of a possibly unrelated bug within the same function? I'll point out that the code I posted was modified slightly from the TI version: I commented out the line: DELAY_US(ADC_usDELAY);

    Leaving that line in also stalls the code, but in an seemingly different way, independent of the arbitrary changes that cause the original problem. I had posted about this problem before, but did not get a resolution:
    http://e2e.ti.com/support/microcontrollers/c2000/f/171/p/340125/1189058.aspx#1189058

    This code stalls somewhere not mapped to the uncompiled code, so in disassembly, it's at 0x3FEFA6. Copied below are some lines before and after that line in the disassembly, in case that helps.

    3fefa1:   0200        MOVB         ACC, #0
    3fefa2:   1E44        MOVL         *-SP[4], ACC
    3fefa3:   1902        SUBB         ACC, #2
    3fefa4:   767FF33C    LCR          0x3ff33c
    3fefa6:   6F00        SB           0, UNC
    3fefa7:   761B        ASP          
    3fefa8:   0005        PUSH         AR1H:AR0H
    3fefa9:   A8BD        MOVL         *SP++, XAR4
    3fefaa:   A0BD        MOVL         *SP++, XAR5
    3fefab:   C2BD        MOVL         *SP++, XAR6

  • Mike,

    I'll ask to have ControlSUITE updated.

    Performing your own ADC offset trim is recommended to improve the ADC accuracy.

    I would have to assume that the race condition was so sensitive that a handful of cycles could make a difference between a good and bad outcome.  A wild guess would be that when you made changes to other pieces of software, the compiler rearranged the memory locations of the code so perhaps the underlying opcode or execution were slightly different.

    I'm not very familiar with the technical details behind the DELAY_US issues, but my understanding is that the function should only be executed out of RAM.  Would you happen to know if it is executing out of RAM in your application?  You can also replace the function with your own dummy for-loop to introduce the functional delay.

    -Tommy

  • By "Performing your own ADC offset trim", you mean calling the AdcxOffsetCal functions provided by TI right, or is there something additional we should do? It's the "your own" that is confusing me.

    The DELAY_US function should be residing in RAM. I have the "F28M35x_usDelay.asm" file in my project, with code copied below. I believe that is sufficient, I don't need to link to that file or reference it in my code otherwise, right?

           .def _DSP28x_usDelay
           .sect "ramfuncs"
    
            .global  __DSP28x_usDelay
    _DSP28x_usDelay:
            SUB    ACC,#1
            BF     _DSP28x_usDelay,GEQ    ;; Loop if ACC >= 0
            LRETR

    EDIT: I just realized I have memcpy(&RamfuncsRunStart, &RamfuncsLoadStart, (size_t) &RamfuncsLoadSize) after the ADC Offset calibration, which has the call to DELAY_US in it.

    Should the memcpy be before I call anything else in main()?

  • What you are doing is correct.  By "your own" I mean a trim sequence that is initiated through your application after the device has been installed on your PCB, as opposed to a factory trim performed by TI using a manufacturing PCB that probably has very different electrical characteristics.

    The memcpy function should definitely be called prior to executing DELAY_US.  I think it would be safe to call the memcpy function before anything else in main().

  • Hey Tommy,

    Thanks a bunch for your help. Moving memcpy to the front of main() fixed the problem.

    Mike

  • Mike,

    I'm glad that things are working for you now.  Thanks for reporting back your results so that others can benefit as well.

    -Tommy

  • Hi everyone,

    I'm facing the same problem, Is there any workarround to fix it?

    Thanks,

    Alex

  • Hi Alex,


    Did you make the changes described in the post I marked as the solution? That fixed it for me.

    TI came out with a new version of the code in controlSuite which, according to the documentation, says it fixed the problem. However, in my testing, the problem still remained and I still needed to put in the changes in that post.

    Mike