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.

CCS/TM4C123GH6PM: MHz switching GPIO

Part Number: TM4C123GH6PM
Other Parts Discussed in Thread: ENERGIA

Tool/software: Code Composer Studio

I am trying to drive a CCD using a TM4C123G Tiva LaunchPad with the goal of developing an open-source spectrometer for science education.  The CCD is supposed to output a series of voltages that vary with light intensity and exposure time.  While the CCD should provide output in response to a signal sequence from the microcontroller, I am getting no change in output with light or time, just a DC voltage as measured using my oscilloscope. I suspect the problem may arise from uncontrolled delays in the timing of my signal sequence. Any recommendations or insight into the problem would be appreciated.  If this results from physical limitations of the microcontroller, that information would be helpful as well.
The data sheet for the CCD says that it requires a clock running at between 0.8 and 2.4 MHz.  Two other pins (ICG and SH) must be synchronized with the clock at the start and finish of an acquisition.  Timing requirements are described here: tcd1304.wordpress.com/.../.  The expected output is shown in a figure here: tcd1304.wordpress.com/.../.  Until recently, my experience coding the LaunchPad has been limited to the Energia IDE.  For this project, I am learning to use Code Composer Studio, mostly by following the Tiva workshop labs.  I initially tried to run the clock using timer interrupts (as described in lab 2), but it appears that the interrupts introduce delays that limit the clock speed.  I have a simplified code with two alternating methods; a StartSequence method and a clock method.  Eventually, I will need to incorporate ADC measurements during the clock method, but right now I am just trying to clean up the LaunchPad output timing. The clock method just toggles the PF1 pin.  It will eventually do this for several ms. To get a symmetric square wave, I set a delay of 2 system clock ticks during the low period of the clock cycle, which gives high and low times of ~450 ns each (measured with oscilloscope). The StartSequence method switches the ICG and SH settings on PF2 and PF3 between high and low states.  ICG goes low as clock goes high, then 200 ns later, SH goes high.  After three clock cycles, SH returns low.  Then after three more clock cycles, ICG returns high.  After that, the StartSequence method ends and the code goes back to the clock method.
It appears that changing methods (ie, leaving clock to enter StartSequence) introduces ~150 ns delay.  I am also unable to sequentially change the state of the ICG and SH pulses without adding a delay of more than ~350 ns.

My system control clock settings (below) should give me 100 MHz, or 100 ns period.
     SysCtlClockSet(SYSCTL_SYSDIV_2|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);

Thanks for your help.

Jack

  • Your TM4C123 'tops out' @ 80MHz - thus your line of code must replace 'SYSDIV_2' - with 'SYSDIV_2.5'.

    Top GPIO Toggle Rates occur when employing a Timer w/in its "PWM Mode." It is possible to synchronize several timers - which appears worthy of investigation - on your part...

    There are Cortex M4 MCUs which run at 180MHz & beyond (helpful to your efforts) - yet I believe that a small, inexpensive 'CPLD' (or small FPGA) to be 'optimally suited' - for your task.

    As always - your review of, "How the commercially successful devices perform their magic" - is sure to provide good insight...    (unless they've adopted an, 'All in one, custom IC.')

  • Thanks cb1_mobile. I will look into the PWM question.
    I tried replacing SYSDIV_2 with SYSDIV_2.5. The code would not compile with this. Perhaps SYSDIV_2 actually divides by 2.5. I guess I could do some experimenting to find out.
    Jack
  • This proved (most) surprising (and upsetting) as unless I'm hallucinating (again) - 'SYSDIV_2.5' has 'LONG' been a known, legal parameter value.      And the only means to achieve 80MHz!

    My investigation's 'findings/facts'  follow:

    Well that's NO GOOD - 80MHz escapes this listing due to, 'NO FRACTIONAL SYSDIV!'      Let the 'search'  thru the 'fine MCU Manual' continue...

    And 'Eureka' - yet could this have been made - at all - more convoluted and/or difficult?     (as long-time here (but recently escaped) I've never had to (past) engage in such (demanding) indirectness!)

    You'll be best served by your own read/review of the MCU Manual - it may be easier for you to, 'Start w/the FAR EASIER ATTAINED 66.67MHz value.'    (from the top chart)

    The CPLD suggestion has, 'Gained in Strength' ... has it not?

  • If I might step in... I think the issue is just a case of using a . vs a _

    SYSCTL_SYSDIV_2.5 is not defined in TivaWare, but SYSCTL_SYSDIV_2_5 is.

    Please try that Jack, and you will see cb1 is accurate in what he has presented.

  • That would be, 'Dit - Dah' from the past WA2/DL4.

    You may note - that I (seriously) checked - prior to composing.    

    I checked the sysctl.c file as well - AWOL there - as well...    That's NOT GOOD - is it?

    It is noted that all (expected) INTEGER Values for SYSDIV ...  ARE RECOGNIZED!      Something HAS CHANGED HERE - that's my belief...

    <edit>  SYSCTL_SYSDIV_2_5  Does Appear w/in sysctl.h  -   yet ALL of its 'integer brethren' reveal in the more LIKELY,  Reader Accessible/Friendly venues - which (again) CANNOT BE GOOD!

  • That makes a lot more sense (and it compiles too). Thanks Ralph and cb1.
    Jack
  • Hi cb1,

    That is due to how C code works in general. You can't use a period in a standalone variable name because it is a member access operator for structures. Therefore the common coding practice is to replace any dots that would be in a variable with an underscore just like a space.
  • I realize that Ralph - but absent (eased) mention of 'SYSCTL_SYSDIV_2_5' - in the normal/customary User Manuals - how is (any) user to find that parameter. You'll note that I did (eventually find it) - yet 'exhausted from the search' - missed the '_5.'

    As stated (and presented here) - under T-Ware - BOTH the 'DRL User Guide'  AND  'sysctl.c' (completely) AVOID any/all mention of 2_5 -  which seems a 'significant' oversight - does it not?      I hadn't intended to return - but learning of this poster's struggle - and the LACK of REAL (proper) GUIDANCE - from the (usual, published sources) felt compelled to assist...

    It should be noted - as well - that (the LONG PAST) 'sysctl.c' w/in StellarisWare 9107 - made 'crystal-clear mention' of the 'Half-Dividers' - as this copy/paste reveals:

    //! Sets the clocking of the device.
    //!
    //! \param ulConfig is the required configuration of the device clocking.
    //!
    //! This function configures the clocking of the device. The input crystal
    //! frequency, oscillator to be used, use of the PLL, and the system clock
    //! divider are all configured with this function.
    //!
    //! The \e ulConfig parameter is the logical OR of several different values,
    //! many of which are grouped into sets where only one can be chosen.
    //!
    //! The system clock divider is chosen with one of the following values:
    //! \b SYSCTL_SYSDIV_1, \b SYSCTL_SYSDIV_2, \b SYSCTL_SYSDIV_3, ...
    //! \b SYSCTL_SYSDIV_64. Only \b SYSCTL_SYSDIV_1 through \b SYSCTL_SYSDIV_16
    //! are valid on Sandstorm-class devices.   Half-dividers, such as
    //! \b SYSCTL_SYSDIV_2_5 and \b SYSCTL_SYSDIV_3_5. are available on Tempest-,
    //! Firestorm-, and Blizzard-class devices.

    And - the always helpful/rescuing Peripheral Driver Library (StellarisWare 9453)  past presented 'Half-Dividers' as well:

    24.2.2.6 SysCtlClockSet
    Sets the clocking of the device.
    Prototype:
    void
    SysCtlClockSet(unsigned long ulConfig)
    Parameters:
    ulConfig is the required configuration of the device clocking.
    Description:
    This function configures the clocking of the device. The input crystal frequency, oscillator to be
    used, use of the PLL, and the system clock divider are all configured with this function.
    The ulConfig parameter is the logical OR of several different values, many of which are grouped
    into sets where only one can be chosen.
    The system clock divider is chosen with one of the following values: SYSCTL_SYSDIV_1,
    SYSCTL_SYSDIV_2, SYSCTL_SYSDIV_3, ... SYSCTL_SYSDIV_64. Only
    SYSCTL_SYSDIV_1 through SYSCTL_SYSDIV_16 are valid on Sandstorm-class devices.
    Half-dividers, such as SYSCTL_SYSDIV_2_5 and SYSCTL_SYSDIV_3_5. are
    available on Tempest-, Firestorm-, and Blizzard-class devices.

    Somehow - during the move to T-Ware - mention of those 'Half-Dividers' - from (BOTH of those key sources) has ... 'Left the Building!'      And that clearly - is NOT Good!   (likely CAUSED the poster's plight!)

  • The pwm suggestion was a good one and I have some code that looks like it is going to work.  Since others might find this useful (and since I generally don't know what I am doing), I am providing my code below and will leave the post open for a couple of days to allow comments. After that I will mark the issue as answered.
    COMMENTS:  I established the 1 MHz clock for the CCD on PD0 by setting up the system clock at 80 MHz, and the pwm clock at 40 MHz.  To get it to align with the ICG signal required it run in up_down mode so it could be phase shifted using the DeadBand command.  I chose to use the same pwm module for the three signals to keep things in sync, but used different generators so that the signals could be offset.  While I incorporated the PWMSyncTimeBase and PWMSyncUpdate commands, it is not clear that these did anything from their effects on the oscilloscope traces.

        uint16_t clockTime = 40;  // gives 1 MHz square wave output with 40 MHz system clock
        uint16_t integrationTime = 20;  // clock tics per integration
          integrationTime *= clockTime;
        uint16_t ICGTime = 5;           // clock tics per ICG pulse
          ICGTime *= clockTime;
        uint16_t SHTime = 3;            // clock tics per SH pulse
          SHTime *= clockTime;

        SysCtlClockSet(SYSCTL_SYSDIV_2_5|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);  // for 80 MHz clock
        SysCtlPWMClockSet(SYSCTL_PWMDIV_2);           // pwm clock at 40 MHz

        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);  // PWM for Clock on PD0
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);  // PWM for ICG on PE4
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);  // PWM for SH on PF1
        SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1);   // PWM TEST, ICG

        GPIOPinTypePWM(GPIO_PORTD_BASE, GPIO_PIN_0);  // pwm pin for clock
        GPIOPinTypePWM(GPIO_PORTE_BASE, GPIO_PIN_4);  // pwm pin for ICG
        GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_1);  // pwm pin for SH

        GPIOPinConfigure(GPIO_PD0_M1PWM0);   // clock on PD0
        GPIOPinConfigure(GPIO_PE4_M1PWM2);   // ICG on PE4
        GPIOPinConfigure(GPIO_PF1_M1PWM5);   // SH on PF1

    ////////////////  Clock on PD0  /////////////
        PWMGenConfigure(PWM1_BASE, PWM_GEN_0, PWM_GEN_MODE_UP_DOWN); // needed for adjusting phase
        PWMGenPeriodSet(PWM1_BASE, PWM_GEN_0, clockTime);     // clock at 1 MHz
        PWMPulseWidthSet(PWM1_BASE, PWM_OUT_0, clockTime/2);    // square wave, pulse width = 1/2 period

    //////////////   ICG  on PE4  ////////////////
        PWMGenConfigure(PWM1_BASE, PWM_GEN_1, PWM_GEN_MODE_DOWN);
        PWMGenPeriodSet(PWM1_BASE, PWM_GEN_1, integrationTime);  
        PWMPulseWidthSet(PWM1_BASE, PWM_OUT_2, 50);   
        PWMOutputInvert(PWM1_BASE, PWM_OUT_2_BIT, true);  // ICG inverted

    ////////////////  SH on PF1   ////////////////
        PWMGenConfigure(PWM1_BASE, PWM_GEN_2, PWM_GEN_MODE_DOWN); // | PWM_GEN_MODE_SYNC); // M0PWM6
        PWMGenPeriodSet(PWM1_BASE, PWM_GEN_2, integrationTime);  // five times longer than clock
        PWMPulseWidthSet(PWM1_BASE, PWM_OUT_5, 30);   

        PWMGenEnable(PWM1_BASE, PWM_GEN_0);  
        PWMGenEnable(PWM1_BASE, PWM_GEN_1);
        PWMGenEnable(PWM1_BASE, PWM_GEN_2);
       
        PWMDeadBandEnable(PWM1_BASE, PWM_GEN_0, 0, 10);    // 40 clock cycles = 1 us
        PWMSyncTimeBase(PWM1_BASE, PWM_GEN_0| PWM_GEN_1 | PWM_GEN_2);
        PWMSyncUpdate(PWM1_BASE, PWM_GEN_0| PWM_GEN_1 | PWM_GEN_2);

        PWMOutputState(PWM1_BASE, PWM_OUT_0_BIT|PWM_OUT_2_BIT, true);  
        PWMOutputState(PWM1_BASE, PWM_OUT_5_BIT, true);

     while(1){
    }