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.

Error in ADCSequenceConfigure

There appears to be an error in ADCSequenceConfigure() in the v2.1.2.111 with regard to the setting of ADCTSSEL which encodes the PWM Generator and Module with which to trigger the ADC.  You can demonstrate the error by setting a PWM trigger for PWM 1, Gen 2--the function will erroneously set PWM 1 and Gen 1.  The existing code uses the PWM module and ADC Sequence rather than the PWM Generator:

ui32SequenceNum *= 4;

...

//
// Set the source PWM module for this sequence's PWM triggers.
//
ui32SequenceNum *= 2;
HWREG(ui32Base + ADC_O_TSSEL) = ((HWREG(ui32Base + ADC_O_TSSEL) &
~(0x30 << ui32SequenceNum)) |
((ui32Trigger & 0x30) <<
ui32SequenceNum));

The code below works correctly:

uint32_t ui32Gen;

...

//
// Set the source PWM module for this sequence's PWM triggers.
//
ui32Gen = ui32Trigger & 0x0f;
if(ui32Gen >= ADC_TRIGGER_PWM0 && ui32Gen < ADC_TRIGGER_PWM3)
{
// Set the shift for the module and generator
ui32Gen = (ui32Gen - ADC_TRIGGER_PWM0) * 8;
HWREG(ADC0_BASE + ADC_O_TSSEL) = ((HWREG(ADC0_BASE + ADC_O_TSSEL) &
~(0x30 << ui32Gen)) |
((ui32Trigger & 0x30) << ui32Gen));
}

  • Here's the complete corrected function:

    void
    ADCSequenceConfigureFixed(uint32_t ui32Base, uint32_t ui32SequenceNum,
                         	 uint32_t ui32Trigger, uint32_t ui32Priority)
    {
        uint32_t ui32Gen;
    
        //
        // Check the arugments.
        //
        ASSERT((ui32Base == ADC0_BASE) || (ui32Base == ADC1_BASE));
        ASSERT(ui32SequenceNum < 4);
        ASSERT(((ui32Trigger & 0xF) == ADC_TRIGGER_PROCESSOR) ||
               ((ui32Trigger & 0xF) == ADC_TRIGGER_COMP0) ||
               ((ui32Trigger & 0xF) == ADC_TRIGGER_COMP1) ||
               ((ui32Trigger & 0xF) == ADC_TRIGGER_COMP2) ||
               ((ui32Trigger & 0xF) == ADC_TRIGGER_EXTERNAL) ||
               ((ui32Trigger & 0xF) == ADC_TRIGGER_TIMER) ||
               ((ui32Trigger & 0xF) == ADC_TRIGGER_PWM0) ||
               ((ui32Trigger & 0xF) == ADC_TRIGGER_PWM1) ||
               ((ui32Trigger & 0xF) == ADC_TRIGGER_PWM2) ||
               ((ui32Trigger & 0xF) == ADC_TRIGGER_PWM3) ||
               ((ui32Trigger & 0xF) == ADC_TRIGGER_ALWAYS) ||
               ((ui32Trigger & 0x30) == ADC_TRIGGER_PWM_MOD0) ||
               ((ui32Trigger & 0x30) == ADC_TRIGGER_PWM_MOD1));
        ASSERT(ui32Priority < 4);
    
        //
        // Compute the shift for the bits that control this sample sequence.
        //
        ui32SequenceNum *= 4;
    
        //
        // Set the trigger event for this sample sequence.
        //
        HWREG(ui32Base + ADC_O_EMUX) = ((HWREG(ui32Base + ADC_O_EMUX) &
                                         ~(0xf << ui32SequenceNum)) |
                                        ((ui32Trigger & 0xf) << ui32SequenceNum));
    
        //
        // Set the priority for this sample sequence.
        //
        HWREG(ui32Base + ADC_O_SSPRI) = ((HWREG(ui32Base + ADC_O_SSPRI) &
                                          ~(0xf << ui32SequenceNum)) |
                                         ((ui32Priority & 0x3) <<
                                          ui32SequenceNum));
    
        //
        // Set the source PWM module for this sequence's PWM triggers.
        //
        ui32Gen = ui32Trigger & 0x0f;
        if(ui32Gen >= ADC_TRIGGER_PWM0 && ui32Gen < ADC_TRIGGER_PWM3)
        {
        	// Set the shift for the module and generator
        	ui32Gen = (ui32Gen - ADC_TRIGGER_PWM0) * 8;
        	HWREG(ADC0_BASE + ADC_O_TSSEL) = ((HWREG(ADC0_BASE + ADC_O_TSSEL) &
    										  ~(0x30 << ui32Gen)) |
    										 ((ui32Trigger & 0x30) << ui32Gen));
        }
    }

  • You can demonstrate the problem with the following code:

    ADCSequenceConfigure(ADC0_BASE, 1, ADC_TRIGGER_PWM_MOD1 | ADC_TRIGGER_PWM2, 0);

    This sets ADC_TSSEL to 0x00001000 which is in the reserved part of the register.

    The corrected code sets ADC_TSSEL to 0x00100000

    Regards,

    --Mike

  • Lets try that again (syntax highlighter just doesn't get me...)

    You can demonstrate the problem with the following code:

    ADCSequenceConfigure(ADC0_BASE, 1, ADC_TRIGGER_PWM_MOD1 | ADC_TRIGGER_PWM2, 0);
    This sets ADC_TSSEL to 0x00001000 which is in the reserved part of the register.

    The corrected code sets ADC_TSSEL to 0x00100000

    Regards,

    --Mike

  • OK, syntax highlighter, that was just spiteful!

    One more time (moderator please feel free to delete the above fails!).

    You can demonstrate the problem with the following code:

    ADCSequenceConfigure(ADC0_BASE, 1, ADC_TRIGGER_PWM_MOD1 | ADC_TRIGGER_PWM2, 0);

    This sets ADC_TSSEL to 0x00001000 which is in the reserved part of the register.

    The corrected code sets ADC_TSSEL to 0x00100000

    Regards,

    --Mike

  • Hello Michael,

    Which TM4C device is being used?

    Regards
    Amit
  • TM4CGH6PM

    On Sunday, March 6, 2016, Amit Ashara <bounce-310847@mail.e2e.ti.com> wrote:

     

    A Message from the TI E2E™ Community
    Texas Instruments

     

    Amit Ashara replied to Error in ADCSequenceConfigure.

    Hello Michael,

    Which TM4C device is being used?

    Regards
    Amit

     

     

    You received this notification because you subscribed to the forum.  To unsubscribe from only this thread, go here.

    Flag this post as spam/abuse.



    --

  • Hello Michael,

    I checked the TivaWare 2.1.2.111 and in the function post above you mentioned

    //
    // Set the source PWM module for this sequence's PWM triggers.
    //
    ui32Gen = ui32Trigger & 0x0f;
    if(ui32Gen >= ADC_TRIGGER_PWM0 && ui32Gen < ADC_TRIGGER_PWM3)
    {
    // Set the shift for the module and generator
    ui32Gen = (ui32Gen - ADC_TRIGGER_PWM0) * 8;
    HWREG(ADC0_BASE + ADC_O_TSSEL) = ((HWREG(ADC0_BASE + ADC_O_TSSEL) &
    ~(0x30 << ui32Gen)) |
    ((ui32Trigger & 0x30) << ui32Gen));
    }

    I see in the repository...

    //
    // Set the source PWM module for this sequence's PWM triggers.
    //
    ui32SequenceNum *= 2;
    HWREG(ui32Base + ADC_O_TSSEL) = ((HWREG(ui32Base + ADC_O_TSSEL) &
    ~(0x30 << ui32SequenceNum)) |
    ((ui32Trigger & 0x30) <<
    ui32SequenceNum));

    It seems that you have a locally modified copy which may be the cause.

    Regards
    Amit
  • Amit, The first block of code is the repo code; that is the code that doesn't work. The locally modified code (ADCSequenceConfigureFixed()) is the code that works. But don't take my word for it, just run this code: ADCSequenceConfigure(ADC0_BASE, 1, ADC_TRIGGER_PWM_MOD1 | ADC_TRIGGER_PWM2, 0); Then check the ADCTSSEL register and see if it has PWM 1 and Gen2 selected. Regards, Mike
  • Clarification: by "first block of code", I meant the first block of code in the original post, which matches your second block of code (the repo code).
  • Hello Michael,

    Alright, I will check it on Monday and reply on the post.

    Regards
    Amit
  • Hello Michael.

    EDIT

    I checked the code and I can confirm the observation you have. It is indeed an API Bug.

    The location specified in the ADCTSSEL is corresponding to the Sequencer and not the Generator. This is a documentation issue. In other words, when selecting PWM1 Gen-2 for ADC Sequencer-1 the location is correctly programmed. In the data sheet description of the bit field for the ADC_TSSEL if the word "Generator" is replaced by "Sequencer", it will clarify the description.

    Regards
    Amit

  • Amit,

    If what you say is true (ADCTSSEL is based on Sequencer, not Generator), then my modified version of ADCSequenceConfigure should fail.

    However, it fails to fail.

    Have a look:

    #include <stdbool.h>
    #include <stdint.h>
    //#include "inc/tm4c123gh6pm.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "inc/hw_adc.h"
    #include "inc/hw_ints.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/rom.h"
    #include "driverlib/pwm.h"
    #include "driverlib/adc.h"
    #include "driverlib/gpio.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/uart.h"
    #include "driverlib/debug.h"
    #include "utils/uartstdio.h"
    
    void adc_seq_1_isr(void);
    
    //*****************************************************************************
    //
    // This function sets up UART0 to be used for a console to display information
    // as the example is running.
    //
    //*****************************************************************************
    void
    InitConsole(void)
    {
        //
        // Enable GPIO port A which is used for UART0 pins.
        // TODO: change this to whichever GPIO port you are using.
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    
        //
        // Configure the pin muxing for UART0 functions on port A0 and A1.
        // This step is not necessary if your part does not support pin muxing.
        // TODO: change this to select the port/pin you are using.
        //
        GPIOPinConfigure(GPIO_PA0_U0RX);
        GPIOPinConfigure(GPIO_PA1_U0TX);
    
        //
        // Enable UART0 so that we can configure the clock.
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    
        //
        // Use the internal 16MHz oscillator as the UART clock source.
        //
        UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
    
        //
        // Select the alternate (UART) function for these pins.
        // TODO: change this to select the port/pin you are using.
        //
        GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    
        //
        // Initialize the UART for console I/O.
        //
        UARTStdioConfig(0, 9600, 16000000);
    }
    
    //*****************************************************************************
    //
    // Configure ADC0 for a single-ended input and a single sample.  Once the
    // sample is ready, an interrupt flag will be set.  Using a polling method,
    // the data will be read then displayed on the console via UART0.
    //
    //*****************************************************************************
    
    #define adc_seq_num			1
    
    void Setup_ADC(void)
    {
    	// The ADC0 peripheral must be enabled for use.
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
    
    	// Set clock source for ADC to PIOSC (16 MHz) divided by 1
    	ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PIOSC | ADC_CLOCK_RATE_FULL, 1);
    
    	// Enable Ports
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
    
    	// Select the analog ADC function for these pins.
    	// Consult the data sheet to see which functions are allocated per pin.
    	GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_0);
    	GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_1);
    
    	// Enable sample sequence 1 with a processor signal trigger.
    	// Set to lowest priority (3)
    	ADCSequenceConfigure(ADC0_BASE, adc_seq_num, ADC_TRIGGER_PROCESSOR, 0);
    
    	// Configure step 0-1 on sequence 1.
    	ADCSequenceStepConfigure(ADC0_BASE, adc_seq_num, 0, ADC_CTL_CH3);								// PE1
    	ADCSequenceStepConfigure(ADC0_BASE, adc_seq_num, 1, ADC_CTL_CH2 | ADC_CTL_IE | ADC_CTL_END);	// PE0
    
    	// Since sample sequence 0 is now configured, it must be enabled.
    	ADCSequenceEnable(ADC0_BASE, adc_seq_num);
    }
    
    void
    ADCSequenceConfigureFixed(uint32_t ui32Base, uint32_t ui32SequenceNum,
                         	 uint32_t ui32Trigger, uint32_t ui32Priority)
    {
        uint32_t ui32Gen;
    
        //
        // Check the arugments.
        //
        ASSERT((ui32Base == ADC0_BASE) || (ui32Base == ADC1_BASE));
        ASSERT(ui32SequenceNum < 4);
        ASSERT(((ui32Trigger & 0xF) == ADC_TRIGGER_PROCESSOR) ||
               ((ui32Trigger & 0xF) == ADC_TRIGGER_COMP0) ||
               ((ui32Trigger & 0xF) == ADC_TRIGGER_COMP1) ||
               ((ui32Trigger & 0xF) == ADC_TRIGGER_COMP2) ||
               ((ui32Trigger & 0xF) == ADC_TRIGGER_EXTERNAL) ||
               ((ui32Trigger & 0xF) == ADC_TRIGGER_TIMER) ||
               ((ui32Trigger & 0xF) == ADC_TRIGGER_PWM0) ||
               ((ui32Trigger & 0xF) == ADC_TRIGGER_PWM1) ||
               ((ui32Trigger & 0xF) == ADC_TRIGGER_PWM2) ||
               ((ui32Trigger & 0xF) == ADC_TRIGGER_PWM3) ||
               ((ui32Trigger & 0xF) == ADC_TRIGGER_ALWAYS) ||
               ((ui32Trigger & 0x30) == ADC_TRIGGER_PWM_MOD0) ||
               ((ui32Trigger & 0x30) == ADC_TRIGGER_PWM_MOD1));
        ASSERT(ui32Priority < 4);
    
        //
        // Compute the shift for the bits that control this sample sequence.
        //
        ui32SequenceNum *= 4;
    
        //
        // Set the trigger event for this sample sequence.
        //
        HWREG(ui32Base + ADC_O_EMUX) = ((HWREG(ui32Base + ADC_O_EMUX) &
                                         ~(0xf << ui32SequenceNum)) |
                                        ((ui32Trigger & 0xf) << ui32SequenceNum));
    
        //
        // Set the priority for this sample sequence.
        //
        HWREG(ui32Base + ADC_O_SSPRI) = ((HWREG(ui32Base + ADC_O_SSPRI) &
                                          ~(0xf << ui32SequenceNum)) |
                                         ((ui32Priority & 0x3) <<
                                          ui32SequenceNum));
    
        //
        // Set the source PWM module for this sequence's PWM triggers.
        //
        ui32Gen = ui32Trigger & 0x0f;
        if(ui32Gen >= ADC_TRIGGER_PWM0 && ui32Gen < ADC_TRIGGER_PWM3)
        {
        	// Set the shift for the module and generator
        	ui32Gen = (ui32Gen - ADC_TRIGGER_PWM0) * 8;
        	HWREG(ADC0_BASE + ADC_O_TSSEL) = ((HWREG(ADC0_BASE + ADC_O_TSSEL) &
    										  ~(0x30 << ui32Gen)) |
    										 ((ui32Trigger & 0x30) << ui32Gen));
        }
    }
    
    void Setup_PWM(void)
    {
    	// Enable Ports
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    
    	GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_1);
    
    	// PWM Trigger Setup
    	// Enable PWM 1
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1);
    
    	// Configure GPIO Pins for PWM mode.
    	GPIOPinConfigure(GPIO_PF1_M1PWM5);
    
    	// Configure the PWM generators for count up/down mode with immediate updates
    	// to the parameters.
    	PWMGenConfigure(PWM1_BASE, PWM_GEN_2, PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_NO_SYNC);
    
    	// Set the PWM clock to 80 MHz / 64 = 1.25 MHz
    	SysCtlPWMClockSet(SYSCTL_PWMDIV_64);
    
    	// Set the PWM period to be as long as possible. For 80 MHz clock with a divider = 64, the
    	// PWM clock is 80 / 64 = 1.25 MHz.  In up/down mode we must count 2xload so 1.25 MHz / (2 * 65535) = 9.5 Hz.
    	PWMGenPeriodSet(PWM1_BASE, PWM_GEN_2, 65535);
    
    	// Set the pulse width of PWM 5 for a 25%duty cycle.
    	PWMPulseWidthSet(PWM1_BASE, PWM_OUT_5, 120);
    
    	// Start the timers in generator 2.
    	PWMGenEnable(PWM1_BASE, PWM_GEN_2);
    
    	// Turn on the Output pins
    	PWMOutputState(PWM1_BASE, PWM_OUT_5_BIT, true);
    
    	// Trigger the ADC on when PWM matches the load register.
    	PWMGenIntTrigEnable(PWM1_BASE, PWM_GEN_2, PWM_TR_CNT_LOAD);
    
    #if 0
    	ADCSequenceConfigure(ADC0_BASE, adc_seq_num, ADC_TRIGGER_PWM_MOD1 | ADC_TRIGGER_PWM2, 0);
    #else
    	ADCSequenceConfigureFixed(ADC0_BASE, adc_seq_num, ADC_TRIGGER_PWM_MOD1 | ADC_TRIGGER_PWM2, 0);
    #endif
    
    	ADCIntEnable(ADC0_BASE, adc_seq_num);
    
    	// Clear the interrupt status flag.
    	ADCIntClear(ADC0_BASE, adc_seq_num);
    }
    
    // TODO:  Get it working at 80 MHz (are we setting ADC clock right?)
    int main(void)
    {
    	uint32_t data[2];
    
        // Enable lazy stacking for interrupt handlers.  This allows floating-point
        // instructions to be used within interrupt handlers, but at the expense of
        // extra stack usage.
        ROM_FPULazyStackingEnable();
    
        // Set the clocking to run from the PLL (400 MHz / (2 * 2.5) = 80 MHz.
        // Set the clocking to run from the PLL (400 MHz / (2 * 10) = 20 MHz.
        // Set the clocking to run from the PLL (400 MHz / (2 * 12.5) = 16 MHz.
        SysCtlClockSet(SYSCTL_SYSDIV_12_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
    
        //
        // Set up the serial console to use for displaying messages.  This is
        // just for this example program and is not needed for ADC operation.
        //
        InitConsole();
    
        //
       	// Clear Screen
       	//
       	UARTprintf("\033[2J\033[;H");
    
       	//
       	// Home & set text color to blue
       	//
       	UARTprintf("\033[H\033[34m");
    
       	UARTprintf("**************************************************\n");
       	UARTprintf("**              PWM Triggered ADC               **\n");
       	UARTprintf("**************************************************\n");
    
       	// Set text color to black
       	UARTprintf("\n\033[30m");
    
       	// Setup ADC
       	Setup_ADC();
    
    	// Setup PWM Trigger for ADC
       	Setup_PWM();
    
    	while(1)
    	{
    		// Wait for conversion to be completed.
    		while(!ADCIntStatus(ADC0_BASE, adc_seq_num, false));
    
    		// Clear the ADC interrupt flag.
    		ADCIntClear(ADC0_BASE, adc_seq_num);
    
    		// Read ADC Values.
    		ADCSequenceDataGet(ADC0_BASE, adc_seq_num, data);
    
    		UARTprintf("\r\nSliders:  %4d %4d", data[0], data[1]);
    	}
    }
    
    

     

  • Hello Michael

    Did you check the value of the ADCTSSEL?

    Regards
    Amit
  • Yes, the corrected code sets ADCTSSEL to 0x00100000, while the original code sets it to 0x00001000, which is reserved based on the current datasheet.

    This corresponds to PWM 0 on Generator 2.

  • Correction: The original code does not set ADCTSSEL to a reserved part of the register when called with sequence 1, PWM Mod 1 & Generator 2, but neither does it set the Generator 2 field (as currently documented), and it fails to trigger the ADC.
  • Hello Michael,

    It would be easier if you can zip your CCS project and attach it to the Forum Post.

    Regards
    Amit
  • Hello Michael

    Thanks. As a side note: I did notice that you have set the define as TARGET_IS_TM4C123_RA1. Please note that TM4C123 devices are from RB0 onwards and the launchpad has RB1 devices. So the define would be TARGET_IS_TM4C123_RB1.

    Regards
    Amit
  • Thanks for the heads up--I must have started with an old example project.

  • Amit,

    Have you had a chance to try running the project I posted?

    --Mike

  • Hello Michael,

    Yes, I can reproduce the issue you are seeing and the workaround helps. I am checking the design before I can confirm the same

    Regards
    Amit
  • Hello Mike,

    One more correction to the updated function

    if(ui32Gen >= ADC_TRIGGER_PWM0 && ui32Gen < ADC_TRIGGER_PWM3)

    must be

    if(ui32Gen >= ADC_TRIGGER_PWM0 && ui32Gen <= ADC_TRIGGER_PWM3)

    Do you concur?

    Regards
    Amit
  • Yes, you are correct.
    --Mike
  • Hello Mike,

    I confirm that the function has an issue. The description of the register is correct in the data sheet. I have filed a bug for the next TivaWare release. In the meantime you can use your updated function

    Thanks for the bug report.

    Regards
    Amit
  • No problem, thanks for confirming.