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.

PWM Generator Triggered Single ADC Sequence Issue

Other Parts Discussed in Thread: TM4C123GH6PGE

Below is the setup of my LM4F232H5QD on custom board.

- 5 ADC channels : AIN3, AIN8, AIN9, AIN20 and AIN21.

- 5 PWM channels: M0PWM0, M0PWM1, M1PWM0, M1PWM1, and M1PWM2.

Therefore:

- 2 PWM Modules: PWM0 and PWM1

- 3 PWM Generators:

Generator 0 (drives channels 0 and 1) on Module 0

Generator 0 (drives channels 0 and 1) on Module 1

Generator 1 (drives channels 2 and 3) on Module 1

Three of the ADC channels 20, 21 and 8  are event driven from PWM channels 0 - 2 on PWM Module 1 respectively. Two sequences are set up for reading.

Sequence A: Reads AIN20 and AIN21; connected to PWM1, PWM Generator 0, Interrupt Enabled

Sequence B: Reads AIN8, AIN3 and AIN9; connected to PWM1, PWM Generator 1, Interrupt Enabled

 

I want to use both ADC Modules to capture the three ADC channel data as close to a point in the PWM pulse as possible.  I have sync'd the two PWM generator clocks for this testing but plan to undo the coupling later since PWM Generator 0 needs to run at a different frequency than Generator 1.  

I set up ADC0 with a single sequence Sequence A (as sequence 0) and ADC1 with single Sequence B (as sequence 0) expecting to capture the data.  However, it doesn't work.  I am only able to capture ADC0-Sequence A and the interrupt never trips for Sequence B. After many different experiments I was able to put the table together below.  If there is only one listed configured sequence, it is sequence 0.  If there are two listed, it is sequence 0 and 1.

 

Configuration

ADC0

ADC1

Interrupt Events Results

1

PWM1Gen0-Sequence A

PWM1Gen1-Sequence B

ADC0 (Seq 0)

2

PWM1Gen0-Sequence A

PWM1Gen0-Sequence A

ADC0 (Seq 0) and ADC1 (Seq 0)

3

PWM1Gen0-Sequence A

PWM1Gen0-Sequence B

ADC0 (Seq 0)  and ADC1 (Seq 0)

4

PWM1Gen0-Sequence A

PWM1Gen1-Sequence B

x

ADC0 (Seq 0 & 1)

5

x

PWM1Gen0-Sequence A

PWM1Gen1-Sequence B

ADC1 (Seq 0 & 1)

6

PWM1Gen1-Sequence A

PWM1Gen1-Sequence B

no (Seq 0)  events

7

PWM1Gen1-Sequence A

x

no (Seq 0)  events

8

x

PWM1Gen1-Sequence B

no (Seq 0)  events

9

PWM1Gen0-Sequence (A or B)

x

ADC0 (Seq 0)

10

x

PWM1Gen0-Sequence (A or B)

ADC1 (Seq 0)

11

PWM1Gen1-Sequence A

PWM1Gen2-Sequence C*

PWM1Gen1-Sequence B

PWM1Gen2-Sequence C*

random behavior (reset related?)

12

PWM1Gen0-Sequence A

PWM1Gen2-Sequence C*

PWM1Gen1-Sequence B

PWM1Gen2-Sequence C*

ADC0 (Seq 0)  and ADC1 (Seq 0)

No PWM Gen 2 is used.

*Sequence C: Reads AIN3 again; connected to PWM1, PWM Generator 2, Interrupt Enabled; Dummy sequence mostly.

 

I conclude from the data gathered that the ADC sequences only work when there is at least one sequence tied to PWM Generator 0 in PWM Module 0 or 1 AND there is at least two sequences in the other ADC Module.  I didn't get crazy with four sequence tests.  I am unable to find anything in the datasheet that mentions Generator 0 must be used in order or use the ADC Sequence functionality.  Only configuration 12 works allowing two “single” sequences to be run on the independent PWM Generator’s with the proper ADC channels connected.  Configuration 1 should have been the solution I believe.  Though not captured in the table above, I did play with different numbers of ADC channels in the sequence, switched the orders, etc. and none of that had any impact on the results.  I also switched the order of the sequences within the same ADC which also had no effect on the results.

Is this a bug in the micro hardware?

TI FYI: It would nice to have a TivaWare function for setting which PWM module is used for ADC trigger(access to the HWREG(ui32Base + ADC_O_TSSEL)).

  • Tim49805 said:
    It would nice to have a TivaWare function for setting which PWM module is used for ADC trigger

    Does not this past StellarisWare function, "meet your requirement?"

        ROM_PWMGenIntTrigEnable(PWM0_BASE, PWM_GEN_0,
                            PWM_INT_CNT_ZERO | PWM_TR_CNT_LOAD);   //   PWM_TR_CNT_LOAD triggers the ADC...

    (I'll have to go back - review that function in more detail - as/if you require...)

  • That function causes the PWM to generate a trigger for the ADC capture but it does not do anything with the ADCTSSEL register for the ADC sequence to trigger off the correct PWM module.  Related but different functionality.

  • Tim49805 said:
    Does not do anything to - ADCTSSEL register - so the ADC sequence triggers correct PWM module

    May - may not - be true.  (haven't yet looked)

    That said - my understanding is that the PWM Generator "triggers" the ADC - thus starting PWM controlled conversions.  Are you suggesting that the "reverse" (i.e. ADC is to instead trigger the PWM Generator?)  I have no knowledge of this...

    Update: (until now - although the subject/title of your post, "PWM Gen.Triggered - Single ADC Sequence Issue" seems to conform to my earlier, "reverse question..."  Below is extract from our M4 MCU data manaual - ADC Chapter:

    Triggers
    The digital comparator trigger function is enabled by setting the CTE bit in the ADCDCCTLn register.
    This bit enables the trigger function state machine to start monitoring the incoming ADC conversions.
    When the appropriate set of conditions is met, the corresponding digital comparator trigger to the
    PWM module is asserted

    Is this closer to your issue - I remain confused...  (to be fair - haven't had time to "dig" deeply into this issue...)

  • This is getting the purpose of this thread off topic but I will respond to the last post to clear it up for other readers.  If you don't change the ADCTSSEL register from default and the PWM0 and PWM1 modules have the generator ADC trigger enabled (through the PWMGenIntTrigEnable function), the ADC module will use only the PWM0 trigger.  It must be told that there is [also] a trigger for it on PWM1 module through the ADCTSSEL. It seems it is only mentioned in the ADCEMUX register description of the datasheet and there is currently no access in the TivaWare.  It took me a while to figure out how to trigger off PWM1.  

    Now that I have it configured to trigger off PWM1, I would like to understand what is going on with the sequences (original post).

  • Off topic never good - sorry about that.  In fairness - have designed, built, shipped thousands of ARM MCU boards (this/other ARM vendors) and have never encountered your exact requirement.

    Can now confirm your report of use/partial results under control of Register ADC0TSSEL and Register ADC0EMUX.  (Our M4 MCU has both ADC0 & ADC1 modules)  We ran a search in files w/in Rev 9453 StellarisWare - and just as you state - ADC0TSSEL is nowhere found w/in StellarisWare function log!  (appears only w/in adc.h and MCU.h)

    Our 64 pin M4 Stellaris presently manages 10 of 12 ADC channels - with 8 of these under PWMGen0 trigger.  By luck - we've thus far escaped the need to really dig beneath the surface (as you clearly have) - and extend beyond this PWMGen0 (default) trigger.

    I will try to carve time tomorrow - and replicate some of your findings.  If you can list top 3 or 4 - I'll start with those. 

  • Tim,

    I'll try to investigate your setup tomorrow. I can also pass your ADC0TSSEL Stellaris/TivaWare feature request to the software team.

  • Hi Tim,

    Please refer to this thread: http://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/t/179988.aspx

    My findings were never acknowledged or refuted, so I'll bet the bit field mapping of the ADCTSSEL register are still jumbled.  Please let us know if you have any interesting discoveries.  Good luck, and thank you!

    Regards,

    Dave

  • Dave,

    I didn't use the bit-fields from the TivaWare file but I see that they are correct.  It looks like the Datasheet has not been corrected yet to note that the ADCTSSEL register sets the PWM Module.  It still says PWM generator 0/1.  I assumed they made a mistake with that and your other post provides confirmation of it.  My code is/was setting the ADCTSSEL register with the proper bits.  I have a custom function that takes an array of sequences and loops through them for setup.

    HWREG(ADC1_BASE + ADC_O_TSSEL) |= (pwm_module) << ((8 * sequence) + 4);

    I believe the issue I am posting about centers around the ADCEMUX register.  

    - If the EM0 parameter in the register is set to PWM[generator]0 (0x6) the ADC event is triggered.  If EM1 - EM3 are also set to PWM triggers, those events work too.

    - If the EM0 parameter in the register is set to PWM[generator]1 (0x7) and it is the only parameter set (others left at default), the ADC event does not trigger.

    - If the EM0 parameter in the register is set to PWM[generator]1 (0x7) and EM1 parameter is set to PWM[generator]2 (0x8), then the ADC event is triggered for PWM Generator 1.

    I didn't confirm whether PWM Generator 2 event in the last case is also triggered as I don't have that PWM enabled. Therefore it shouldn't be triggered.  It was added as an experiment and found it made the first sequence trigger so seemed worth noting to help TI troubleshoot this issue.  

  • Tim,

    The latest Tiva equivalent datasheet for your part (TM4C123GH6PGE) has the ADCTSSEL register setting the PWM modules. I will request that future datasheets have specific references to ADCTSSEL in the ADC configuration directions.

    Can you post your ADC configuration code for cases 2,3, and 6 in your first post?

  • My actual code is wrapped in a couple nested loops to set up each ADC channel and ADC sequence.  I expanded out my loops and put into a single statement and just ran this code verifying it performs the same as reported earlier.

    #include <stdint.h>
    #include <stdbool.h>
    #include "inc/tm4c123gh6pge.h"
    #include "inc/hw_types.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_adc.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/adc.h"
    #include "driverlib/interrupt.h"
    
    #include "inc/hw_gpio.h"
    #include "driverlib/gpio.h"
    
    
    
    #define CASE_6
    
    
    static void ADC0_IntAdcSeq0Handler(void);
    static void ADC0_IntAdcSeq1Handler(void);
    static void ADC1_IntAdcSeq0Handler(void);
    static void ADC1_IntAdcSeq1Handler(void);
    
    void Initialize_ADCs(void)
    {
      uint32_t sequence = 0;
      uint32_t pwm_module = 1;
      uint32_t temp = 0;
    
      /************************  ADC Module 0 ********************************/
      // Enable ADC0 peripheral with dummy read to insert a few cycles.
      SysCtlPeripheralEnable( SYSCTL_PERIPH_ADC0 );
      temp = SYSCTL_RCGC2_R;
      temp = SYSCTL_RCGC2_R;
      temp = SYSCTL_RCGC2_R;
    
    
      // Set the ADC speed.
      // SysCtlADCSpeedSet( SYSCTL_ADCSPEED_125KSPS );  LM3 Legacy Function breaks PWM's
      // ADCSpeedSet( ADC0_BASE, ADC_PC_SR_125K );  // Missing new TivaWare function.
      ADC0_PC_R = 0x01;   // Set the register directly
    
      // Set reference source
      ADCReferenceSet( ADC0_BASE, ADC_REF_INT );
    
      // Disable the hardware over-sampling
      ADCHardwareOversampleConfigure( ADC0_BASE, sequence );
    
      /********************** Configure Sequence 0 in ADC0    *****************/
      sequence = 0;
      ADCSequenceDisable( ADC0_BASE, sequence );
    
      // Set the PWM Module the sequences will be triggered from
      HWREG(ADC0_BASE + ADC_O_TSSEL) |= (pwm_module) << ((8 * sequence) + 4);
    
      // Assign the Sequencer to the PWM and set it's priority to the Seq #
    #if defined(CASE_2) || defined(CASE_1) || defined(CASE_3) || defined(CASE_4)
      ADCSequenceConfigure( ADC0_BASE, sequence, ADC_TRIGGER_PWM0, 0 );
      ADCSequenceStepConfigure( ADC0_BASE, sequence, 0, ADC_CTL_CH20 );
      ADCSequenceStepConfigure( ADC0_BASE, sequence, 1, ADC_CTL_CH21 | (ADC_CTL_IE | ADC_CTL_END) );
    #elif defined(CASE_6)
      ADCSequenceConfigure( ADC0_BASE, sequence, ADC_TRIGGER_PWM1, 0 );
      ADCSequenceStepConfigure( ADC0_BASE, sequence, 0, ADC_CTL_CH20 );
      ADCSequenceStepConfigure( ADC0_BASE, sequence, 1, ADC_CTL_CH21 | (ADC_CTL_IE | ADC_CTL_END) );
    #endif
      ADCSequenceEnable( ADC0_BASE, sequence );
      ADCIntClear( ADC0_BASE, sequence );
      ADCIntRegister( ADC0_BASE, sequence, ADC0_IntAdcSeq0Handler );
      ADCIntEnable( ADC0_BASE, sequence );
      IntEnable( INT_ADC0SS0 );
    
    
      /********************** Configure Sequence 1 in ADC0    *****************/
    #if defined(CASE_4)
      sequence = 1;
      ADCSequenceDisable( ADC0_BASE, sequence );
    
      // Set the PWM Module the sequences will be triggered from
      HWREG(ADC0_BASE + ADC_O_TSSEL) |= (pwm_module) << ((8 * sequence) + 4);
    
      // Assign the Sequencer to the PWM and set it's priority to the Seq #
      ADCSequenceConfigure( ADC0_BASE, sequence, ADC_TRIGGER_PWM1, 0 );
      ADCSequenceStepConfigure( ADC0_BASE, sequence, 0, ADC_CTL_CH8 );
      ADCSequenceStepConfigure( ADC0_BASE, sequence, 1, ADC_CTL_CH9 );
      ADCSequenceStepConfigure( ADC0_BASE, sequence, 2, ADC_CTL_CH3 | (ADC_CTL_IE | ADC_CTL_END) );
      ADCSequenceEnable( ADC0_BASE, sequence );
      ADCIntClear( ADC0_BASE, sequence );
      ADCIntRegister( ADC0_BASE, sequence, ADC0_IntAdcSeq1Handler );
      ADCIntEnable( ADC0_BASE, sequence );
      IntEnable( INT_ADC0SS1 );
    #endif
    
    
      /************************  ADC Module 1 ********************************/
      // Enable ADC0 peripheral with dummy read to insert a few cycles.
      SysCtlPeripheralEnable( SYSCTL_PERIPH_ADC1 );
      temp = SYSCTL_RCGC2_R;
      temp = SYSCTL_RCGC2_R;
      temp = SYSCTL_RCGC2_R;
    
    
      // Set the ADC speed.
      // SysCtlADCSpeedSet( SYSCTL_ADCSPEED_125KSPS );  LM3 Legacy Function breaks PWM's
      // ADCSpeedSet( ADC0_BASE, ADC_PC_SR_125K );  // Missing new TivaWare function.
      ADC1_PC_R = 0x01;   // Set the register directly
    
      // Set reference source
      ADCReferenceSet( ADC1_BASE, ADC_REF_INT );
    
      // Disable the hardware over-sampling
      ADCHardwareOversampleConfigure( ADC1_BASE, sequence );
    
      /********************** Configure Sequence 0 in ADC1    *****************/
      sequence = 0;
      ADCSequenceDisable( ADC1_BASE, sequence );
    
      // Set the PWM Module the sequences will be triggered from
      HWREG(ADC1_BASE + ADC_O_TSSEL) |= (pwm_module) << ((8 * sequence) + 4);
    
      // Assign the Sequencer to the PWM and set it's priority to the Seq #
    #if defined(CASE_1)
      ADCSequenceConfigure( ADC1_BASE, sequence, ADC_TRIGGER_PWM1, 0 );
      ADCSequenceStepConfigure( ADC1_BASE, sequence, 0, ADC_CTL_CH8 );
      ADCSequenceStepConfigure( ADC1_BASE, sequence, 1, ADC_CTL_CH9 );
      ADCSequenceStepConfigure( ADC1_BASE, sequence, 2, ADC_CTL_CH3 | (ADC_CTL_IE | ADC_CTL_END) );
    #elif defined(CASE_2)
      ADCSequenceConfigure( ADC1_BASE, sequence, ADC_TRIGGER_PWM0, 0 );
      ADCSequenceStepConfigure( ADC1_BASE, sequence, 0, ADC_CTL_CH20 );
      ADCSequenceStepConfigure( ADC1_BASE, sequence, 1, ADC_CTL_CH21 | (ADC_CTL_IE | ADC_CTL_END) );
    #elif defined(CASE_3)
      ADCSequenceConfigure( ADC1_BASE, sequence, ADC_TRIGGER_PWM0, 0 );
      ADCSequenceStepConfigure( ADC1_BASE, sequence, 0, ADC_CTL_CH8 );
      ADCSequenceStepConfigure( ADC1_BASE, sequence, 1, ADC_CTL_CH9 );
      ADCSequenceStepConfigure( ADC1_BASE, sequence, 2, ADC_CTL_CH3 | (ADC_CTL_IE | ADC_CTL_END) );
    #elif defined(CASE_6)
      ADCSequenceConfigure( ADC1_BASE, sequence, ADC_TRIGGER_PWM1, 0 );
      ADCSequenceStepConfigure( ADC1_BASE, sequence, 0, ADC_CTL_CH8 );
      ADCSequenceStepConfigure( ADC1_BASE, sequence, 1, ADC_CTL_CH9 );
      ADCSequenceStepConfigure( ADC1_BASE, sequence, 2, ADC_CTL_CH3 | (ADC_CTL_IE | ADC_CTL_END) );
    #endif
      ADCSequenceEnable( ADC1_BASE, sequence );
      ADCIntClear( ADC1_BASE, sequence );
      ADCIntRegister( ADC1_BASE, sequence, ADC1_IntAdcSeq0Handler );
      ADCIntEnable( ADC1_BASE, sequence );
      IntEnable( INT_ADC1SS0 );
    }
    
    
    static void ADC0_IntAdcSeq0Handler(void)
    {
      ADCIntClear( ADC0_BASE, 0 );
    }
    
    static void ADC0_IntAdcSeq1Handler(void)
    {
      ADCIntClear( ADC0_BASE, 1 );
    }
    
    static void ADC1_IntAdcSeq0Handler(void)
    {
      ADCIntClear( ADC1_BASE, 0 );
    }
    
    static void ADC1_IntAdcSeq1Handler(void)
    {
      ADCIntClear( ADC1_BASE, 1 );
    }
    
    

    I did find that you should do a System Reset in CCS after a build and program as it does not always clear out the previous data in the PWM and ADC modules and can make you think it is working. I set up 4 breakpoints in the interrupt handlers and had a port pin toggling to scope the lines.

  • Tim,

    I took a look at the code and nothing major jumped out at me. When you set the ADCTSSEL register, if you use the |= operator, then you can't switch from PWM module 1 to 0 without resetting the part. Mask the bits you are writing to 0 first like ADCTSSEL=(ADCTSSEL(&~mask))|value.

    Also, when you configure two sequences per ADC, they need to have different priorities in ADCSequenceConfigure(). Currently, case 4 produces this, but there is a comment that suggests it was different before you expanded out your loops.

    I can confirm that the ADCTSSEL configuration instructions will be added to future datasheets to avoid future problems.

    I will do what I can to start a more thorough investigation into the interrupts not triggering in certain configurations.

  • Tim,

    What silicon revision is the part on your board? A1,A3,B0 or B1?

  • I have a B1 revision part.  Register 0x400F.E000 reports 0x18050101.

    I'll comment briefly on the items in your previous post too.  I only call the configuration at power-up so I expect the registers to be the default value.  I can change my configuration of the ADCTSSEL to be safer though.   The priorities are set correctly in my loop too.  I have created an object per ADC module that gets passed into the ADC configure function.  It makes it easy for me to change all the values, reorder, etc.  I included my structure below.  This code has been used on many projects so I don't expect there is a bug in it but after putting together the test code I included above, the behavior is the same.  

    typedef struct
    {
      UInt8 Pwm_Module;
      UInt8 Pwm_Generator;
      UInt8 Number_Channels;
      UInt8 Priority;
      UInt8 Channel_List[8];      // Seq 0: Max 8, Seq 1 and 2: Max 4, Seq 3: Max 1
    } tADC_Sequence;

    static tADC_Sequence ADC0_Sequence_Configuration[] = {
        {1, 0, 2, 0, { 20, 21, 0, 0, 0, 0, 0, 0 }}, // Sequencer 0, Tied to PWM 0/1
        //{1, 1, 3, 1, { 8, 9, 3, 0, 0, 0, 0, 0 }}, // Sequencer 1, Tied to PWM 2/3  Sequence moved to ADC1
    };

    static tADC_Sequence ADC1_Sequence_Configuration[] = {
        //{1, 0, 3, 0, { 8, 9, 3, 0, 0, 0, 0, 0 }}, // Sequencer 0, Tied to PWM 0/1
        {1, 1, 3, 0, { 8, 9, 3, 0, 0, 0, 0, 0 }},   // Sequencer 0, Tied to PWM 2/3
        {1, 2, 1, 2, { 20, 0, 0, 0, 0, 0, 0, 0 }},  // Sequencer 1, Tied to PWM 4/5 Dummy sequence to make Sequence 0 trigger.

  • Tim,

    Your issue has been raised with the IC design team, and they have requested your system clock configuration and PWM configuration code.

  • See attached.

    #include <stdint.h>
    #include <stdbool.h>
    #include "../../typedefs.h"
    #include "inc/tm4c123gh6pge.h"
    #include "inc/hw_types.h"
    #include "inc/hw_memmap.h"      /* peripheral addresses       */
    #include "driverlib/sysctl.h"   /* system control API */
    #include "driverlib/gpio.h"     /* GPIO API             */
    #include "driverlib/pwm.h"
    #include "../pwm.h"
    #include <stdio.h>
    #include <stdarg.h>
    
    /***********************************************************************************************//**
     * @details   Initialize a PWM generator at a specific frequency and polarity.  Pass in the channel
     *            (n 0-7), frequency in hz (100Hz to 100khz), center flag, and the invert flag (Inverted
     *            0/1). If the Centered flag is 0 the PWM signal will be left justified.  If it 1 the
     *            PWM signal will be centered.  This function does not enable the PWM's after setup.
     *            This allows for the syncing in time of multiple PWM's after they are configured.  
     *
     * @param     n    Channel number of the PWM (0-7)
     * @param     hz   Operation Frequency in hertz (100Hz to 100khz)
     * @param     centered Indicates how the counter works for interrupt triggers
     * @param     inverted Indicates whether active high or low (0 or 1)
     * @param     adc_trigger True if the channel/generator should generate an ADC sequence 
     **************************************************************************************************/
    void hw_PWM1_Initialize(Int32 n, Int32 hz, UInt32 centered, UInt32 inverted, Boolean adc_trigger)
    {
      volatile unsigned long temp;
      int PwmGen;
      int PwmN;
      int PwmBit;
    
      unsigned long Config = (
        PWM_GEN_MODE_NO_SYNC         |      // If sync'd, then a manual force sync must be called
                                            //   when making changes to the period or pulse width.
                                            //   Can be sync'd across multiple PWM's.
        PWM_GEN_MODE_DBG_STOP        |
        PWM_GEN_MODE_GEN_SYNC_LOCAL  |      // Changes to the PWM are made when the Load counter
                                            //   reaches 0 at the end of the PWM cycle. Prevents
                                            //   pulses from triggering A to D sequence in the middle
                                            //   of an A to D sequence.  Errata also applies. The Global
                                            //   setting would also require a sync function to be called
                                            //   in order to update the settings.
        PWM_GEN_MODE_DB_NO_SYNC      |      // No Deadband usage in this configuration.
        PWM_GEN_MODE_FAULT_UNLATCHED |
        PWM_GEN_MODE_FAULT_NO_MINPER |
        PWM_GEN_MODE_FAULT_LEGACY);
    
    
      switch(n)
      {
        case 0: PwmGen = PWM_GEN_0; PwmN = PWM_OUT_0; PwmBit = PWM_OUT_0_BIT; break;
        case 1: PwmGen = PWM_GEN_0; PwmN = PWM_OUT_1; PwmBit = PWM_OUT_1_BIT; break;
        case 2: PwmGen = PWM_GEN_1; PwmN = PWM_OUT_2; PwmBit = PWM_OUT_2_BIT; break;
        case 3: PwmGen = PWM_GEN_1; PwmN = PWM_OUT_3; PwmBit = PWM_OUT_3_BIT; break;
        case 4: PwmGen = PWM_GEN_2; PwmN = PWM_OUT_4; PwmBit = PWM_OUT_4_BIT; break;
        case 5: PwmGen = PWM_GEN_2; PwmN = PWM_OUT_5; PwmBit = PWM_OUT_5_BIT; break;
        case 6: PwmGen = PWM_GEN_3; PwmN = PWM_OUT_6; PwmBit = PWM_OUT_6_BIT; break;
        case 7: PwmGen = PWM_GEN_3; PwmN = PWM_OUT_7; PwmBit = PWM_OUT_7_BIT; break;
        default: return;
      }
    
      // At the current clock rate the PWMs are limited to 100Hz to 100kHz.
      if (hz < 100)   hz = 100;
      if (hz > 100000) hz = 10000;
    
      // Enable the peripheral.
      SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1);
    
      // Do a dummy read to insert a few cycles after enabling the peripheral.
      temp = SYSCTL_RCGC2_R;
      temp = SYSCTL_RCGC2_R;
    
      // Set the PWM clock to the system clock / 4.
    //  PWM1_PC_R = 0x00000101;                   // Direct hardware set
    //  PWMClockSet(PWM1_BASE, PWM_PC_PWMDIV_4);  // Missing new TivaWare function
      SysCtlPWMClockSet(SYSCTL_PWMDIV_4);         // LM3 Legacy Function
    
      // The PWM can be centered or left justified.
      if (centered)
      {
        Config |= PWM_GEN_MODE_UP_DOWN;
      }
      else
      {
        Config |= PWM_GEN_MODE_DOWN;
      }
    
      // Configure the PWM generator with the above set options.
      PWMGenConfigure(PWM1_BASE, PwmGen, Config);
    
      // Set the period for the selected PWM generator.
      PWMGenPeriodSet(PWM1_BASE, PwmGen, (SysCtlClockGet()/4) / hz);
    
      // Set up a small dummy pulse width to keep the tied A to D sequencer from colliding.
      PWMPulseWidthSet(PWM1_BASE, PwmN, 200);
    
      // Invert the PWM signal.
      PWMOutputInvert(PWM1_BASE, PwmBit, (inverted != 0));
    
      // Set up a trigger that can be captured by the A to D to start a capture sequence.
      if (adc_trigger)
      {
        PWMGenIntTrigEnable(PWM1_BASE, PwmGen, PWM_TR_CNT_LOAD);
      }
      else
      {
        PWMGenIntTrigDisable(PWM1_BASE, PwmGen, PWM_TR_CNT_LOAD);
      }
    }
    
    /*************************************************************************/
       
    SysCtlClockSet( SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ );
    
    
    SysCtlPeripheralEnable( SYSCTL_PERIPH_GPIOG );
    
    GPIOPinConfigure(GPIO_PG2_M1PWM0);
    GPIOPinTypePWM(GPIO_PORTG_BASE, GPIO_PIN_2);
    hw_PWM1_Initialize( 0, 3000, 1, 0, True );
    
    GPIOPinConfigure(GPIO_PG3_M1PWM1);
    GPIOPinTypePWM(GPIO_PORTG_BASE, GPIO_PIN_3);
    hw_PWM1_Initialize( 1, 3000, 1, 0, True );
    
    GPIOPinConfigure(GPIO_PG4_M1PWM2);
    GPIOPinTypePWM(GPIO_PORTG_BASE, GPIO_PIN_4);
    hw_PWM1_Initialize( 2, 2000, 1, 0, True );
    
    PWMGenEnable(PWM1_BASE, PWM_GEN_0);
    PWMGenEnable(PWM1_BASE, PWM_GEN_1);
    
    

  • Tim,

    The IC design team is continuing to investigate the issue. To get the best picture of your configuration, they have requested some memory dumps. They asked for a dump of the entire memory from your ADC and PWM modules during operation when set to a failing configuration. Look in the "Register Map" sections in the datasheet for where the base of the memory dump should begin and the offset where the dump should end.

    They also would like  the values of the RCC and RCC2 registers in the System Control module.

  • TIm,

    I think we've found the problem. There is a mistake in the datasheet. The fields in the ADCTSSEL register should connect the PWM generators to modules like this:

    PS0

    Generator 0 triggers from PWM0 vs PWM1

    PS1

    Generator 1 triggers from PWM0 vs PWM1

    PS2

    Generator 2 triggers from PWM0 vs PWM1

    PS3

    Generator 3 triggers from PWM0 vs PWM1

    The current (and incorrect) datasheet has the fields connecting the sample sequencers to PWM modules.

    If you look at the cases in your initial post, this works out because generator 1 triggers when sample sequencer 1 has been configured (case 4,5 and 12). Generator 1 fails to trigger when only sample sequencer 0 has been configured (cases 1,6,7,8) . Also, generator 0 fails to trigger when sample sequencer 0 has not been configured (9,10).

    To fix this, replace this: HWREG(ADC0_BASE + ADC_O_TSSEL) |= (pwm_module) << ((8 * sequence) + 4);

    with: HWREG(ADC1_BASE + ADC_O_TSSEL) = (HWREG(ADC1_BASE + ADC_O_TSSEL) & ~(0x3 << ((8 * generator) + 4))) | ((pwm_module) << ((8 * generator) + 4));

    The ADCEMUX register is where the mapping of sample sequences to PWM generators is done. The description of that register actually has a correct description of how the ADCTSSEL register operates.

  • Thanks John for figuring this out.  It is going to take some time for me to understand the change and then retrain my brain on this ADC operation.  The statement "The fields in the ADCTSSEL register should connect the PWM generators to [PWM] modules." seems odd; like a PWM configuration rather than an ADC configuration.  

    I will make the suggested change and test it out.  Thanks again.

  • I was able to test out the fix (with a little extra debugging) and it is working fine now.  Thanks.

  • Good to hear you got it working. Thanks for bringing this to our attention. We will have a fix for the ADCTSSEL description in the next revision of the datasheet.