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.

TM4C1294 signal acquisition

Other Parts Discussed in Thread: STRIKE

Hi
I am new to programming the TM4C micro controller. I am trying to use the TM4C1294 micro controller to read in a 100khz sine wave from a function generator. What should the sampling rate be? Is it twice the the frequency of the 100khz signal? I am currently using a timer interrupt to tell the ADC when to get a reading.

I have attached the code that I currently have but I do not know if I am on the right path. Any help would be appreciated.

#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/debug.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#include "driverlib/adc.h"
#include "driverlib/timer.h" //TimerConfigure ad TimerLoadSet
#include "driverlib/interrupt.h" //defines and macros for nvic controller interrupt API
#include "inc/tm4c1294ncpdt.h" //definitions fo interrupts and register assignments


int main(void)
{
	//uint32_t ui32ACCValues[4];
	//volatile uint32_t ui32AccX;
	//volatile uint32_t ui32AccY;
	//volatile uint32_t ui32AccZ;
	uint32_t ui32SysClkFreq;
	uint32_t ui32Period;

ui32SysClkFreq = SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480), 120000000);

	//getting desired frequency for sampling rate
	ui32Period = ui32SysClkFreq/100000/2;
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPION);
		SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);


		GPIOPinTypeGPIOOutput(GPIO_PORTN_BASE, GPIO_PIN_0|GPIO_PIN_1);
			TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);

	TimerLoadSet(TIMER0_BASE, TIMER_A, 40000);
	IntEnable(INT_TIMER0A);
	TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
	IntMasterEnable();
	TimerEnable(TIMER0_BASE, TIMER_A);

	SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);

	GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 );

	ADCSequenceConfigure(ADC0_BASE, 1, ADC_TRIGGER_PROCESSOR, 0);
	ADCSequenceStepConfigure(ADC0_BASE, 1, 0, ADC_CTL_CH3);
	ADCSequenceStepConfigure(ADC0_BASE, 1, 1, ADC_CTL_CH2);
	ADCSequenceStepConfigure(ADC0_BASE, 1, 2, ADC_CTL_CH1|ADC_CTL_IE|ADC_CTL_END);

	ADCSequenceEnable(ADC0_BASE, 1);

	while(1)
	{
		/*ADCIntClear(ADC0_BASE, 1);
		ADCProcessorTrigger(ADC0_BASE, 1);
		while(!ADCIntStatus(ADC0_BASE, 1, false))
		{
		}
		ADCSequenceDataGet(ADC0_BASE, 1, ui32ACCValues);
		ui32AccX = ui32ACCValues[0];
		ui32AccY = ui32ACCValues[1];

		//ui32AccZ = ui32ACCValues[2];*/
	}
}

void Timer0IntHandler(void){
	/*TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
	if(GPIOPinRead(GPIO_PORTN_BASE, GPIO_PIN_1))
	{
	GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_1, 0);
	}
	else
	{
	GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_1, 2);
	}*/

	//clear the timer interrupt
	uint32_t ui32InputValues[1];
	volatile uint32_t ui32Impedance;
	//	volatile uint32_t ui32AccY;
		//volatile uint32_t ui32AccZ;
	TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
	ADCIntClear(ADC0_BASE, 1);
	ADCProcessorTrigger(ADC0_BASE, 1);
			while(!ADCIntStatus(ADC0_BASE, 1, false))
			{
			}
			ADCSequenceDataGet(ADC0_BASE, 1, ui32InputValues);
			ui32Impedance = ui32InputValues[0];
			//ui32AccY = ui32ACCValues[1];
			//ui32AccZ = ui32ACCValues[2];*/

}



  • Hello Theron,

    Nyquist requires 2x of the highest frequency in the sample window. TM4C129 can sample upto 4MSPS or 40x times the 100KHz input frequency.

    The more efficient method here would be configure the timer as the trigger source for the ADC using the ADCEMUX register. This way the timer interrupt is not required for processing and neither is the blocking statement that has been created.

    What needs to be made sure is that the timer load value is set to the frequency of sampling and ADC trigger in the Timer register enabled

    Regards
    Amit
  • So 2x the highest frequency would mean a sampling rate 2x times 100khz? What is the ADCEMUX register? How would I configure the trigger source using the ADCEMUX register?


    Sorry I'm completely new to this and everything I have is based off of the TM4C1294 workshop tutorial guide from Ti.
  • Hello Theron,

    I would suggest referring to the Peripheral Driver Lib User Guide Document in TivaWare docs folder. That will show how to use the API's of ADC for different purposes. To help you start to configure the EMUX you would need to use the API call ADCSequenceConfigure. The parameters I would ask you to look into the documentation.

    Regards
    Amit
  • Strictly speaking I believe it is > 2x the frequency. However, if Theron actually wants to see a sine wave result then I would suggest much greater than the incoming frequency.

    Robert
  • Theron Yutiza said:
    I'm completely new to this and everything I have is based off of the TM4C1294 workshop tutorial guide

    Two comments based upon your writing:

    • the project you've chosen is not particulary apt for one, "completely new."
    • it is unwise to limit, "everything you have" to that of a single, limited source - is it not?

    As others have suggested - there is a wealth of necessary tech data here - awaiting your search & find.   Developing your skills as a, "Technical Investigator" will pay off immensely - both for this project and those future...

    It's extremely rare that forum users describe the, "Why & How" a particular project was chosen.    The nature of your opening & follow posts suggests that you've much reading & learning ahead - and that "KISS" is not serving as your initial, "Building Block."

    Universities most often offer courses in a strict progression - which insures the orderly uncovering & mastery of "basics" - prior to moving to more advanced areas.   This is time tested - risks abound for those who, "Rush w/out reasonable understanding..."

  • Hello Robert

    Yes, 2X is the minimum. Getting the wave sampled better is proportional to the sampling rate. Higher the better, but then processing the larger set of data in the same time taxes a uC.

    Regards
    Amit
  • Thanks for your comment.

    Generally I would want to KISS unfortunately I'm under time constraints and my senior design course decided to assign us a project that was a bit out of our scope. Our courses provided only basic programming instructions and not enough instruction on the higher level stuff such as working with an API so I'm in bit of a pickle here.

  • Feel your pain.   I wonder if Medical Schools operate similarly - send first year residents into the ER - to perform immediate & complex brain surgery?   (that's also, "bit out of scope" is it not?)

    Why do the schools do this?   And why do students accept such?    Really - what will you learn - what will stick?   (I've past co-founded, took tech firm public - we would never/ever operate in such manner!)

    If what you say is true the school is, "Out of touch" and grossly violates the concept of, "orderly progression."

    Not the best way to, "Run a Railroad..."

  • Alright so this is what I have so far. Am I on the right path?

    #include <stdint.h>
    #include <stdbool.h>
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "driverlib/debug.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/gpio.h"
    #include "driverlib/adc.h"
    #include "driverlib/timer.h" //TimerConfigure ad TimerLoadSet
    #include "driverlib/interrupt.h" //defines and macros for nvic controller interrupt API
    #include "inc/tm4c1294ncpdt.h" //definitions fo interrupts and register assignments
    
    int main(void) {
    	uint32_t ui32ImpedanceValues[1];
    	uint32_t ui32Impedance;
    	uint32_t ui32SysClkFreq;
    	uint32_t ui32Period;
    
    	ui32SysClkFreq = SysCtlClockFreqSet(
    			(SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL
    					| SYSCTL_CFG_VCO_480), 120000000);
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
    	//getting desired frequency for sampling rate
    	///ui32Period = ui32SysClkFreq/100000/2;
    
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPION);
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
    
    	GPIOPinTypeGPIOOutput(GPIO_PORTE_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    	TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);
    
    	TimerLoadSet(TIMER0_BASE, TIMER_A, 400000);
    	/*load value =40000 means sampling freq=500hz;
    	load value =30000 means sampling fre=667hz;
    	load value=20000 means sampling freq=1000hz
    	load value=10000 means sampling freq=2000hz
    	load value=5000 means sampling freq=4000hz;*/
    
    	IntEnable(INT_TIMER0A);
    	TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
    	IntMasterEnable();
    	TimerEnable(TIMER0_BASE, TIMER_A);
    
    
    
    	GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2);
    
    	ADCSequenceConfigure(ADC0_BASE, 3, ADC_TRIGGER_PROCESSOR , 0);
    
    	ADCSequenceStepConfigure(ADC0_BASE, 3, 0,
    			ADC_CTL_CH3| ADC_CTL_IE | ADC_CTL_END);
    
    	ADCSequenceEnable(ADC0_BASE, 3);
    	ADCIntClear(ADC0_BASE, 3);
    	while (1) {
    
    		ADCProcessorTrigger(ADC0_BASE, 3);
    		while (!ADCIntStatus(ADC0_BASE, 3, false)) {
    		}
    		ADCIntClear(ADC0_BASE, 3);
    		ADCSequenceDataGet(ADC0_BASE, 3, ui32ImpedanceValues);
    		ui32Impedance = ui32ImpedanceValues[0];
    
    	}
    }
    
    /*void Timer0IntHandler(void){
     TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
     if(GPIOPinRead(GPIO_PORTN_BASE, GPIO_PIN_1))
     {
     GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_1, 0);
     }
     else
     {
     GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_1, 2);
     }
    
     //clear the timer interrupt
     uint32_t ui32InputValues[1];
     volatile uint32_t ui32Impedance;
     //	volatile uint32_t ui32AccY;
     //volatile uint32_t ui32AccZ;
     TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
     ADCIntClear(ADC0_BASE, 1);
     ADCProcessorTrigger(ADC0_BASE, 1);
     while(!ADCIntStatus(ADC0_BASE, 1, false))
     {
     }
     ADCSequenceDataGet(ADC0_BASE, 1, ui32InputValues);
     ui32Impedance = ui32InputValues[0];
     //ui32AccY = ui32ACCValues[1];
     //ui32AccZ = ui32ACCValues[2];
    
     }*/
    
    

  • Hello Theron

    What is the role of the timer here? Also channel PE3 I believe is AIN0. In the ADCSequenceStepConfigure API call the 3rd parameter must be 3 if you wish to sample channel PE0.

    Regards
    Amit
  • I removed the timer. It was old code. When I set the 3rd parameter in ADCsequenceStepConfigure it doesn't give me a reading but when its set as 0 I can get a reading from AI0. So now I have seem to have everything set up and I'm using the graph tool in code composer to graph the data coming in from the function generator. Right now the function generator is set at 1hz but the graph is not really smooth. What other configurations do I need to play with? In the end I want to be able to send data from the microcontroller across the USB and into Matlab for further processing. Thanks for the help!

    #include <stdint.h>
    #include <stdbool.h>
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "driverlib/debug.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/gpio.h"
    #include "driverlib/adc.h"
    #include "driverlib/timer.h" //TimerConfigure ad TimerLoadSet
    #include "driverlib/interrupt.h" //defines and macros for nvic controller interrupt API
    #include "inc/tm4c1294ncpdt.h" //definitions fo interrupts and register assignments
    
    int main(void) {
        uint32_t ui32ImpedanceValues[2];
       volatile uint32_t ui32Impedance;
        uint32_t ui32SysClkFreq;
        uint32_t ui32Period;
    
        ui32SysClkFreq = SysCtlClockFreqSet(
                (SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL
                        | SYSCTL_CFG_VCO_480), 120000000);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
        //getting desired frequency for sampling rate
        ///ui32Period = ui32SysClkFreq/100000/2;
    
    
        GPIOPinTypeGPIOOutput(GPIO_PORTE_BASE, GPIO_PIN_0 | GPIO_PIN_1);
       //PE0 AI3 ADc_CTL_CH3
       //PE1 AI2 adc_ctl_ch2
       //PE2 AI1 Adc_CTL_CH1
       // TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);
    
        /*load value =40000 means sampling freq=500hz;
        load value =30000 means sampling fre=667hz;
        load value=20000 means sampling freq=1000hz
        load value=10000 means sampling freq=2000hz
        load value=5000 means sampling freq=4000hz;*/
    
        
        ADCSequenceDisable(ADC0_BASE,3); //good to disable ADC
    
        GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2);
    
        ADCSequenceConfigure(ADC0_BASE, 3, ADC_TRIGGER_PROCESSOR , 0); //using 3rd sample sequencer
        /*	Channel sequence 	SS3 samples 1 Depth FIFO   #1
        						ss2         4               4
        						ss1         4 				4
    							ss0			8				8*/
        ADCSequenceStepConfigure(ADC0_BASE, 3, 0,
                ADC_CTL_CH3| ADC_CTL_IE | ADC_CTL_END);
    
        ADCSequenceEnable(ADC0_BASE, 3);
        ADCIntClear(ADC0_BASE, 3);
        while (1) {
    
            ADCProcessorTrigger(ADC0_BASE, 3);
            while (!ADCIntStatus(ADC0_BASE, 3, false)) {
            }
            ADCIntClear(ADC0_BASE, 3);
            ADCSequenceDataGet(ADC0_BASE, 3, ui32ImpedanceValues);
            ui32Impedance = ui32ImpedanceValues[0];
    
        }
    }
    

  • Actually Amit, I was suggesting twice the highest frequency was below the minimum. It's a subtle difference that really only matters if you're actually trying to work at the limit. I'd be hesitant to work that close to the edge.

    Robert
  • Hello Robert

    Yes, I know. That is why higher sampling frequency are mandated. A 100KHz wave can at most be sampled at 2MSPS by a TM4C and a change of spec would be tougher to manage on TM4C. Rather having High Speed ADC connected to TM4C would be more ideal choices.

    Regards
    Amit
  • Hello Theron

    PE3 is the Analog Pin for AIN0. Please check the data sheet for the exact pins names for the AINx channel. Of the "x" channel is used then pin must be configured correctly. Also the Analog signal must be connected to the correct pin too.

    What does the current graph look like?

    In the end do note that USB interfacing may be more cumbersome considering the scope and time line of the project. Rather would suggest a serial port.

    Regards
    Amit
  • I have attached the graph. The random jagged peaks is when the function generator is set at 10hz and the smoother sine wave is at 1 hz.

  • It would appear you have a sample rate of around 30 hz

    Robert
  • Hello Theron

    Rather than use a single variable, I would suggest using an array to sample 1024 values and then run the plotter

    Regards
    Amit
  • So something like this? 
     
    
       for(i=1;i<1024;i++){
        	 ADCProcessorTrigger(ADC0_BASE, 3);
        	        while (!ADCIntStatus(ADC0_BASE, 3, false)) {
        	        }
        	        ADCIntClear(ADC0_BASE, 3);
        	        ADCSequenceDataGet(ADC0_BASE, 3, ui32ImpedanceValues);
        	        ui32Test[i] = ui32ImpedanceValues[0];
        	       
        }
        for(i=1; i<1024; i++){
        	ui32Impedance=ui32Test[i];
        }

  • Are you somehow new to C programming as well ? Leaving out the suggestion to use DMA for gathering ADC values, there are two things that strike the eye:

    1. arrays in C of size <n> are running from index 0 to <n-1>. In your loops, you always start with 1 as index. Not dangerous, but wasteful.

    2. The second loop doesn't make much sense to me:

    for(i=1; i<1024; i++) { ui32Impedance=ui32Test[i]; }

    That's equivalent to assigning ui32Test[1023] to ui32Impedance. The previous 1022 assignments are just busy-idling. Perhaps you meant: ui32Impedance+=ui32Test[i]; ?

  • In addition to the comments from f.m.

    How are you controlling the sample rate?
    I thought you wanted to have multiple points to display?

    Robert
  • I am new to C programming but I have done some Java programming. For the second for loop I was trying to use the plotter in code composer to plot out the values in the 1024 length array. I wasn't getting anything so I messed around with the array index.

    I thought the sampling was controlled by the ADC_PROCESSOR_TRIGGER where the processor initiated the ADC trigger. Originally I had ADC_TRIGGER_TIMER set up. Otherwise how should I control the sample rate?
  • Hello Theron,

    The timer as trigger is fine when controlling the ADC Sampling rate. Make sure that when you use CCS plotter, the code is halted at the correct place, in this case the filling up of 1024 samples

    Regards
    Amit
  • Theron Yutiza said:
    I thought the sampling was controlled by the ADC_PROCESSOR_TRIGGER where the processor initiated the ADC trigger.

    That triggers the conversion but how would you expect it to control the timing between conversions?

    Theron Yutiza said:
    Originally I had ADC_TRIGGER_TIMER set up. Otherwise how should I control the sample rate?

    That's one way.  You could also monitor a clock/timer and start every n'th count. Or use a delay function, or monitor an external timing signal or....

    Theron Yutiza said:
    For the second for loop I was trying to use the plotter in code composer to plot out the values in the 1024 length array

    How do you make sure that CC actually picks up every value change (and no values between conversions)? If it is the kind of facility I suspect it is then it will be sampling the converted value at some rate unrelated to the rate at which you perform the conversions. You will gain little to no insight in the frequency of the A/D operation. Look up aliasing for why.

    Robert