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 sampling triggered by timer, significant, reproducible frequency offset

Other Parts Discussed in Thread: EK-TM4C123GXL, TM4C1233H6PM

Hello!

As this is a bit complicated, I try to make this as concise and systematical as possible; please excuse my lack of full sentences.

EDIT: This is the Tiva C TM4C123G, on the Tiva C launchpad

Concept:

Using the Tiva C as software-programmable USB ADC on four channels. 



Method:

  • Set CPU clock rate to 100MHz (SysCtlClockGet returns 100000000)
  • initialize Timer0 A to exhibit a trigger rate of 50kHz/25kHz/20kHz (note: these are dividers of 500kHz, the physical ADC rate, so they should be attainable!)
  • initialize ADC sequencer to provide a four-input ADC sequence, triggered on timer
  • Handle ADC results in ADC ISR, push to USB buffer

Problem:

  • Works, but:
  • sampling rate at what should be a sampling rate of 50kHz is only 0.667*50kHz
  • at should-be 25kHz is actually 1.2*25kHz
  • at should-be 20kHz is yet different and so on, even for low (sub-10kHz) rates (== big load values for timer load register)
  • I verify that neither over- nor underruns in the ADC buffer occur

Code excerpt:

int main()

{

[...]

		// Set the clocking to run from the PLL at 100MHz

SysCtlClockSet(SYSCTL_SYSDIV_2 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN |
SYSCTL_XTAL_16MHZ);
// enable the 0. ADC peripheral
SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
// enable the GPIO port that hosts the ADC
SysCtlPeripheralEnable(ADC_GPIO_SYSCTL_1);
SysCtlPeripheralEnable(ADC_GPIO_SYSCTL_2);
[...]
//disable the interrupts prior to set up - just a precaution
IntDisable(INT_ADC0SS0);
ADCIntDisable(ADC0_BASE, ADC_SEQUENCER);

// Enable the Timer peripheral
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
// Timer should run periodically
TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);
// set the value that is loaded into the timer everytime it finishes
// it's the number of clock cycles it takes till the timer triggers the ADC
TimerLoadSet(TIMER0_BASE, TIMER_A, SysCtlClockGet()/F_SAMPLE);
// enable triggering
TimerControlTrigger(TIMER0_BASE, TIMER_A, true);
// enable the timer
TimerEnable(TIMER0_BASE, TIMER_A);
[...]
// The ADC will follow sampling sequence ADC_SEQUENCER
ADCSequenceConfigure(ADC0_BASE, ADC_SEQUENCER, ADC_TRIGGER_TIMER, 0);
[...]
}

extern void IntADC()
{
ADCIntClear(ADC0_BASE, ADC_SEQUENCER);
[...]
for(int counter = 0; counter < 4; counter++)
{
g_value = HWREG(ADC_REGISTERS + ADC_SSFIFO);
uint8_t *byteptr = (uint8_t *) &g_value;
g_pui8USBTxBuffer[g_write_index++ % BULK_BUFFER_SIZE] = *byteptr;
g_pui8USBTxBuffer[g_write_index++ % BULK_BUFFER_SIZE] = (*(byteptr+1))&0x0F;
}


//underflow?
if(HWREG(ADC0_BASE + ADC_O_USTAT) & 1 << ADC_SEQUENCER)
{
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_PIN_1);
HWREG(ADC0_BASE + ADC_O_USTAT) = 1 << ADC_SEQUENCER;
}

//overflow?
if(HWREG(ADC0_BASE + ADC_O_OSTAT) & 1 << ADC_SEQUENCER)
{
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, GPIO_PIN_2);
HWREG(ADC0_BASE + ADC_O_OSTAT) = 1 << ADC_SEQUENCER;
}

g_samples_in_packet_count += 4;
g_request_size -= 1;
}
  • Hi,

    Missing important info here:

    a) what is your micro (TM4C123 or TM4C129)?

    b) what is your board?

    Petrei

  • Sorry, my fault for omitting this:

    a) TM4C123G

    b) TIVA C series launchpad (EK-TM4C123GXL)

  • Hi,

    Then your setting at 100 MHz seems wrong, since TM4C123 works only at maximum 80 MHz. Also count what functions did you used for setting the system clock. The latest Tiva version has separate functions for 123 and 129 micros. Take care what you use.

    Petrei

  • Hi,
    I'm using the TM4C123G -- how comes the processor reports an 100MHz clock when it's only running at 80MHz? Is this a known bug?

    Also, this wouldn't explain varying relative errors, but a constant factor of 0.8. However, at 50kHz sampling, I see my 2kHz test tone at 3kHz, meaning that sampling occurs at 2/3*50kHz.

    Thanks, and greetings,

    Marcus
  • Hi,

    Check also the value of the clock - insert this line after you set the system clock:

    ui32tclock = SysCtlClockGet();

    Set a breakpoint after this line and check the value of ui32tclock.

    Tell me the version of your Tiva, to check one more thing.

    Petrei

  • Hi Marcus,

    As Petrei mentioned, to use the SysCtlClockGet function to get the clock value, it would be important to note that in TIVAWare 2.1.0 release there is a SW bug in SysCtlClockGet function which will return 66.67MHz even if the System Clock is 80Mhz. The ratio of 66.67/80 lines up with your observations of ~0.8

    Regards

    Amit

  • There are known issues w/"SysCtlClockGet(); under Tiva & newest MCUs.

    Is not direct measurement of System Clock most always preferable?  (you escape/or reduce any SW introduced errors)

    Any Timer - ordered into PWM mode - then set to, "divide by 10" will output, "System Clock / 10" @ that timer's pin.  Scope or frequency counter quickly/easily confirms.  And - you now have a controllable (& variable) frequency source - often adding value/benefit to your project...

  • Hello,

    as written in my initial post, I've checked and SysCtlClockGet() returns 100000000 -- I even check that remotely (using UART):


            UARTprintf("SysCtlClockGet: %d, effective sampling clock interval %d\n", SysCtlClockGet(), 
                TimerLoadGet(TIMER0_BASE, TIMER_A));

    prints "SysCtlClockGet: 100000000, effective sampling clock interval 2000" for my 50kHz Sampling Rate setup -- as would be expected.

    My Tiva Ware version seems to be 1.0, judging from MANIFEST.txt

  • Marcus M��ller said:
    as written in my initial post, I've checked and SysCtlClockGet() returns 100000000

    And - as written my post - that, "SysCtlClockGet()" most likely is returning garbage!

    Petrei correctly stated that your MCU is rated to 80MHz, maximum.  You can (perhaps) goose that up a bit - but not to 100MHz.   (points to a too quick, not detailed read - of your MCU manual)

    Timer method I outlined is precise - will reveal the truth... (i.e. hardware takes over...)

  • I'm in the process of upgrading to the newest TivaWare release now; I hope this won't break anything.

    Again, I want to point out that SysCtlClockGet does *not* report 66.7MHz but 100Mhz.

  • Hello Marcus,

    The new TIVAWare 2.1.0 will report 66.67MHz. Can you attach the sysctl.c file from your existing TIVAWare so that we can figure out where the problem is?

    Regards

    Amit

  • Hi,

    OK then, try first to lower the system clock - instead  SYSCTL_SYSDIV_2 use first SYSCTL_SYSDIV_4 to get something "normal" like 50 MHz - test first and see.

    Petrei

  • Ouch - my poor head - (frequency) reality resides @ Timer pin under PWM mode.

    Function which worked well - for years - has been rendered suspect.  Do you really seek to "hitch your wagon" to such a standard?   (especially when safety/reality (hardware only) is so near...)

  • UPDATE:

    after Upgrading to 2.1.0, SysCtlClockGet does, in fact, return 66.67MHz instead of the 100MHz that I specify:

            SysCtlClockSet(SYSCTL_SYSDIV_2 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN |
                            SYSCTL_XTAL_16MHZ);

    With SYSCTL_SYSDIV_2_5 however, the result is the same (66.67 MHz). This is on TivaWare 2.1.

    Is there any knowledge if the 80MHz/66MHz confusion persists, or am I setting the Clock wrong:
            // Set the clocking to run from the PLL at 80MHz
            SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN |
                            SYSCTL_XTAL_16MHZ);

  • Hello Marcus,

    The clock setting is correct. The method by which the clock frequency is returned has a problem, causing it to return 66.67MHz. This is to be corrected in the next TIVAWare release.

    For the moment you can override the return value of the SysCtlClockGet with 80MHz.

    Regards

    Amit

  • SYSDIV_2 *should* be 100MHz, but is reported as 66.7 MHz (tivaware 2.1), SYSDIV_4 should be 50MHz and is reported as such. However, this hurts the usefulness of my product.

  • Hello Marcus

    Please look at the following table in the data sheet for the part

    Table 5-6. Examples of Possible System Clock Frequencies with DIV400=1

    This is what the SysCtlClockGet and SysCtlClockSet work as per. However as mentioned, there is a bug in the SW file. If it is OK, I can send you the patch but you would have to recompile the drvierlib

    Regards

    Amit

  • Hello Amit,

    this is where I got SYSDIV_2_5 from in the first place :-)

    Yes, I'd be very interested in the patch.

    Regards,

    Marcus

  • Hello Marcus,

    In the SysCtlClockGet API in sysctl.c file replace the following

                case SYSCTL_DC1_MINSYSDIV_66:
                {
                    ui32Max = 66666666;
                    break;
                }

    with

                case SYSCTL_DC1_MINSYSDIV_66:
                {
                    ui32Max = 80000000;
                    break;
                }
    Please do compile the driverlib

    Regards

    Amit

  • This patch makes the SysCtlClockGet() call actually return 80000000 for the 2_5 divisor.

    I've attached the file change as an automatically applicable patch file, If anyone stumbles across this thread again.

    --- sysctl.c.orig-2.1.0	2014-02-07 17:13:56.000000000 +0100
    +++ sysctl.c	2014-06-16 13:08:26.023937639 +0200
    @@ -2867,7 +2867,7 @@
                 }
                 case SYSCTL_DC1_MINSYSDIV_66:
                 {
    -                ui32Max = 66666666;
    +                ui32Max = 80000000;
                     break;
                 }
                 case SYSCTL_DC1_MINSYSDIV_50:
    

  • Hello Marcus,

    Just for the record (so that unsuspecting users are aware): This is a temp-patch for 2.1.0 but may not be the final implementation in the next TIVAWare release.

    Regards

    Amit

  • Hello All,

    I've catch this bug too. Function SysCtlClockGet() returns incorrect value for 80MHz CPU clock
    I think it is more correct if header files ( all of the processor specific headers and hw_sysctl.h ) will be fixed.

    There is definition of SYSCTL_DC1_MINSYSDIV_66 in processor header files ( tm4c1233h6pm.h and so on) and hw_sysctl.h file.

    //*****************************************************************************
    //
    // The following are defines for the bit fields in the SYSCTL_DC1 register.
    //
    //*****************************************************************************
    #define SYSCTL_DC1_MINSYSDIV_M  0x0000F000  // System Clock Divider
    #define SYSCTL_DC1_MINSYSDIV_80 0x00001000  // Specifies an 80-MHz CPU clock
                                                // with a PLL divider of 2.5
    #define SYSCTL_DC1_MINSYSDIV_66 0x00002000  // Specifies a 66-MHz CPU clock
                                                // with a PLL divider of 3

    There is no definition for 66-MHz CPU in TM4C 123 System Control Register ( Device Capabilities 1 "DC1 register" ).
    So, SYSCTL_DC1_MINSYSDIV_80 is referenced to Reserved condition and SYSCTL_DC1_MINSYSDIV_66 is referenced to 80-MHz CPU clock in header file.





  • Hello Timur

    Yes, the bug is being fixed for the next TivaWare release. In the meantime you can use the direct value of 80000000 as the System Clock Frequency.

    Regards,

    Amit