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.

TM4C123GH6PM: Sequencer PWM Trigger still happens even with 0% duty cycle

Part Number: TM4C123GH6PM

I'm reusing a unity test from an NXP microcontroller after we ported our framework to work with Tiva C.

My problem is that after I set up the pwm to be the trigger I can't seem to be able to controll the trigger via duty cycle. I can only reduce the amount of triggers by changing the pwm frequency.

Is the trigger tied to the PWM signal's clock or is it a configuration problem?

I have a fucntion that is called at every trigger:

void UT_ANALOG_SEQUENCE_Hook(ANALOG_SEQUENCE_t* sequence, ANALOG_t* analog, void* data)
{
//	gUT_ANALOG_SEQUENCE_acc += ANALOG_Read( analog );
	int32_t temp;
	ADCSequenceDataGet(ANALOG_SEQUENCE_GetAdcBase(sequence), ANALOG_SEQUENCE_GetSequencerNumber(sequence), &temp);
	gUT_ANALOG_SEQUENCE_acc += temp;
	gUT_ANALOG_SEQUENCE_counter++;
}

My code automatically defines a sequencer based on sequencers already in use and the max amount of channels we want the sequencer to run. As this unity test only needs 1 channel, it defines sequencer 3 as the chosen one.

Enabling the sequencer follows these steps:

ADCSequenceDisable();
ADCSequenceConfigure(); // here I just use the sequencer number as a priority
                        // because we don't actually need proper priorities set up
ADCSequenceStepConfigure(); // as many as we need for the channels we want
                            // being the last added channel the one that will
                            // carry ADC_CTL_IE | ADC_CTL_END | channel
ADCIntRegister(); // this one registers the proper irq handler based
                  // on adc [0..1] and sequencer [0..3]
PWMGenIntTrigEnable() // here I set up the trigger for pwm based on
                      // a previous pwm created, using PWM_TR_CNT_ZERO
                      // (I'm a begginer in embedded engineering, I don't
                      // know how to use all the other flags that are
                      // possible here, nor what do they mean...
                      // I'd love a quick reference to something
                      // as I can't find a detailed description on these hahaha)
ADCIntEnable();
IntEnable();
ADCSequenceEnable();

Is there a missing step I skipped? Or is it just triggering based on the pwm's clock?

  • Hello Vinicius,

    Without seeing the parameters you are using for the configuration of the sequence, I can't really offer any support here. Please post the full configuration with all parameters.

    Regarding the flags for the PWMGenIntTrigEnable API, the pwm.h file has the following information:

    #define PWM_INT_CNT_ZERO        0x00000001  // Int if COUNT = 0
    #define PWM_INT_CNT_LOAD        0x00000002  // Int if COUNT = LOAD
    #define PWM_INT_CNT_AU          0x00000004  // Int if COUNT = CMPA U
    #define PWM_INT_CNT_AD          0x00000008  // Int if COUNT = CMPA D
    #define PWM_INT_CNT_BU          0x00000010  // Int if COUNT = CMPA U
    #define PWM_INT_CNT_BD          0x00000020  // Int if COUNT = CMPA D
    #define PWM_TR_CNT_ZERO         0x00000100  // Trig if COUNT = 0
    #define PWM_TR_CNT_LOAD         0x00000200  // Trig if COUNT = LOAD
    #define PWM_TR_CNT_AU           0x00000400  // Trig if COUNT = CMPA U
    #define PWM_TR_CNT_AD           0x00000800  // Trig if COUNT = CMPA D
    #define PWM_TR_CNT_BU           0x00001000  // Trig if COUNT = CMPA U
    #define PWM_TR_CNT_BD           0x00002000  // Trig if COUNT = CMPA D

    Best Regards,

    Ralph Jacobi

  • Sorry for the delayed answer, I didn't have access to the code on the weekend.

    The sequence is configured as follows:

    ADCSequenceDisable( ADC0_BASE, 3 );
    ADCSequenceConfigure( ADC0_BASE, 3,  ADC_TRIGGER_PWM0 | ADC_TRIGGER_PWM_MOD0, 3 ); // here I just use the sequencer number as a priority
                                                                       // because we don't actually need proper priorities set up
    ADCSequenceStepConfigure( ADC0_BASE, 3, 0, ADC_CTL_CH0 | ADC_CTL_IE | ADC_CTL_END );
    ADCIntRegister( ADC0_BASE, 3, ADC0_S3_IRQHandler ); // this one registers the proper irq handler based
                                                        // on adc [0..1] and sequencer [0..3]
                                                        // ADC0_S3_IRQHandler calls UT_ANALOG_SEQUENCE_Hook internally
    PWMGenIntTrigEnable( PWM0_BASE, PWM_GEN_0, PWM_TR_CNT_ZERO ) // here I set up the trigger for pwm based on
                                                                 // a previous pwm created, using PWM_TR_CNT_ZERO
                                                                 // (I'm a begginer in embedded engineering, I don't
                                                                 // know how to use all the other flags that are
                                                                 // possible here, nor what do they mean...
                                                                 // I'd love a quick reference to something
                                                                 // as I can't find a detailed description on these hahaha)
    ADCIntEnable( ADC0_BASE, 3 );
    IntEnable( INT_ADC0SS0_TM4C123 );
    ADCSequenceEnable( ADC0_BASE, 3 );

  • Hello Vinicius,

    Thanks. So your issue to how I read it is that you are not getting the ADC trigger to change as you adjust duty cycle. The ADC trigger is based on the frequency.

    Per your configuration, that tracks actually.

    You are using PWM_TR_CNT_ZERO to trigger the ADC interrupt. This triggers when your count reaches zero at the end of count cycle which is based on the PWM frequency.

    The duty cycle is controlled by CmpA/CmpB. So if you want to trigger according to duty cycle, you need to use to use one of these depending on your PWM configuration:

    #define PWM_TR_CNT_AU           0x00000400  // Trig if COUNT = CMPA U
    #define PWM_TR_CNT_AD           0x00000800  // Trig if COUNT = CMPA D
    #define PWM_TR_CNT_BU           0x00001000  // Trig if COUNT = CMPB U
    #define PWM_TR_CNT_BD           0x00002000  // Trig if COUNT = CMPB D

    Regarding those trigger comments, the shorthand translates as follows: CMPA U = Compare A, Up, CMPB D = Compare B, Down etc.

    Once you use the correct flag for that, you should see the trigger occurring per your duty cycle.

    One other advise I'd give, I don't think it will notably impact performance but it's a best practice:

    With continuous triggers like this, the recommended approach is to use PWMEnable as the trigger point and not ADCSequenceEnable.

    The general process has been to configure the ADC first and then configure the triggering peripheral and start that peripheral once everything is configured to trigger samples starting.

    Best Regards,

    Ralph Jacobi

  • Thanks for the answer! I was able to understand best what the registers were and my code works better now! But... I can't seem to see a way to use a duty cycle of 100% to have only a single trigger as it triggers based on clock, not on signal edges. Is there any configuration or am I just stuck with clock triggering?

  • Hi Vinicius,

    Can you elaborate on the single trigger? I would expect that per period, there is only one match if you only configure a single Compare trigger?

    Best Regards,

    Ralph Jacobi

  • If you used the generaed pwm signal as a trigger for the ADC, had it's duty set to 0% and switched it to 100%, you'd have only one rising edge.

    This single rising edge would be the trigger.

    I was trying to come up with a solution to have this work on tiva, but I'm having a hard time because the triggers are all tied to the clock cycle, so it will ALWAYS trigger once inside the time period we set for the PWM signal.

    Now I have a way to turn on and off the triggers based on the duty cycle, being 0% = off and  >0% = on.

  • Hi Vinicius,

    I see, I think your solution is probably the best one. I can't say we've had such a use case before but reading the D/S I believe your description is accurate.

    I feel like maybe using a timer might make more sense for your use case but I can't really say for sure and you may have ruled it out already for other reasons. You'd be able to customer the load periods and enable/disable it pretty easily plus you have both periodic and one-shot modes. Just a thought in case you hadn't evaluated that avenue yet.

    Best Regards,

    Ralph Jacobi

  • Hi, Ralph!

    I've defined CMPA and CMPB as the right configurations for our setup. We already have timers set up and working properly also. I was just trying to follow a previously done unity test for an LPC microcontroller. As the triggers work differently in each uC, I couldn't make it work like this.

    Thanks for your help! I now understand pwm's setup and triggers way better!