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: ADC help on Code Composer Studio

Part Number: TM4C123GH6PM

Tool/software: Code Composer Studio

For a project, our group has to convert analog measurement into digital data. We are using the Tiva C Series TM4C123GH6PM. How do we use Code Composer Studio to not only digitize the analog values, but to store those values as well. Our plan was to connect the output of a photo diode into the analog input pins of the micro controller and use the Tiva's ADC feature to digitize the analog values. However, we need some guidance as to how we can do so in Code Composer Studio. Lab 5 of the Tiva C Series TM4C123G LaunchPad Workshop deals with the ADC however they're dealing with data from the on-chip temperature sensor. 

Thank you.

  • First, have you downloaded the TivaWare library and examples: http://www.ti.com/tool/SW-TM4C

    Here is a simple ADC example that can be imported into your workspace. Use the Code Composer "File" "Import" feature to import the project directly from this .zip file: /cfs-file/__key/communityserver-discussions-components-files/908/5554.ADC.zip

  • I have the TivaWare library, TivaWare_C_Series-2.1.4.178, downloaded. However, I am having a hard time importing the ADC example in CCS. I downloaded and then extracted the files from the zip folder but it is not allowing me to import it in CCS. Any thoughts? Thank you.

  • This issue occurred because I had another project titled "ADC." I will take a look into the example. 

  • In regards to the ADC example linked above, I have some questions to clarify my understanding of how the ADC of the Tiva works.

    • From my understanding of reading the datasheet of the Tiva, The ADC has 4 sequencers (SS0, SS1, SS2, and SS3) for each ADC module (ADC0 and ADC1). The int array in the beginning of “main” was defined to have a length of 4 to match the FIFO depth of sequence 1, which the example arbitrarily selected, is this the reasoning for that?
    • I am unsure of why two pins (AIN0 and Ain1) are used in this example.
    • The following lines of code is more setup of the ADC. Are the functions from the TivaWare library? The comments help briefly explain the code.
    • Getting into the while loop
      • how is the conversion defined? Is it in the pre-built function “ADCProcessorTrigger()”?
      • Are all four indices of the int array going to eventually be filled?
      • What is the terminating condition, if there is one, of the while loop?
      • In regards to our project, we will need to convert a finite amount of analog values; this can be a large number of data that needs to be digitized. Is it possible to first create the array with the desired number of indices and then fill in each index with the converted values? How does the depth of the FIFO affect this?

    Thank you.

  • Muhammad Rezuna said:
    The int array in the beginning of “main” was defined to have a length of 4 to match the FIFO depth of sequence 1, which the example arbitrarily selected, is this the reasoning for that?

    Yes, that is correct.

    Muhammad Rezuna said:
    I am unsure of why two pins (AIN0 and Ain1) are used in this example.

    It was an arbitrary decision. It shows that more than one channel can be selected in a sequence and not all of the available steps need be used.

    Muhammad Rezuna said:
    The following lines of code is more setup of the ADC. Are the functions from the TivaWare library?

    Yes, all of the functions that start with ADC are from the TivaWare library and are documented in section 4 of: C:\ti\TivaWare_C_Series-2.1.4.178\docs\SW-TM4C-DRL-UG-2.1.4.178.pdf

    Muhammad Rezuna said:
    how is the conversion defined? Is it in the pre-built function “ADCProcessorTrigger()”?

    yes

    Muhammad Rezuna said:
    Are all four indices of the int array going to eventually be filled?

    In this example, no. If we were to call ADCProcessTrigger() twice before calling ADCSequenceDataGet(), then all four entries in the FIFO would be filled and all 4 values in array[] would be filled from the FIFO.

    Muhammad Rezuna said:
    What is the terminating condition, if there is one, of the while loop?

    There is none.

    Muhammad Rezuna said:
    In regards to our project, we will need to convert a finite amount of analog values; this can be a large number of data that needs to be digitized. Is it possible to first create the array with the desired number of indices and then fill in each index with the converted values? How does the depth of the FIFO affect this?

    That would be a good case to use the DMA with the ADC. If the samples are to be at a precise frequency, then the timer should be used to trigger the ADC instead of a processor trigger. Here is another example project that uses a timer to trigger an ADC and uses DMA to fill a 64 sample buffer, and then "ping-pong" to fill a second buffer while the first is being processed.

    /cfs-file/__key/communityserver-discussions-components-files/908/2821.ADCwDMA.zip

    • Can you elaborate more on the FIFO of a sequencer? When a sequencer has an FIFO depth of 4 does that mean it can only keep track of 4 values at a time?
    • Also can you elaborate the difference between having a timer used to trigger the ADC as opposed to a processor trigger? From what I understand, the trigger is used to let the Tiva know when to start converting/collecting data, is this correct?
    • In regards to the new example (ADCwDMA)
      • What is it that the example is trying to accomplish? Is there a document of the project/lab specification?
      • I am not familiar with many of the functions used in the example, are the functions documented in the TivaWare doc?
      • When you say that the project “uses a timer to trigger an ADC and uses DMA to fill a 64 sample buffer” does that mean the project’s trying to digitize 64 analog values?

  • Muhammad Rezuna said:
    Can you elaborate more on the FIFO of a sequencer? When a sequencer has an FIFO depth of 4 does that mean it can only keep track of 4 values at a time?

    That is correct. The purpose of the FIFO is to give the CPU more time before it needs to go read the results of the ADC controller before they are overwritten or lost. You may not see the value when using a conversion that is triggered by the CPU, but take the case of periodic conversions initiated by a timer. The CPU may be busy performing some other time critical task when an ADC conversion completes. The FIFO allows the ADC to store that conversion (up to 4 for sequence 1) and give more time for the CPU before it must read the results.

    Muhammad Rezuna said:
    Also can you elaborate the difference between having a timer used to trigger the ADC as opposed to a processor trigger? From what I understand, the trigger is used to let the Tiva know when to start converting/collecting data, is this correct?

    When doing digital analysis of AC analog signals it is important to know at what frequency the signal was sampled. If you are doing continuous conversions, you can start that with a CPU trigger once and know the sample frequency. But if you want a lower sample rate you should use a timer. If you have interrupts or any conditionally executing code, the sample rate of a software loop will not be consistent nor easy to predict.

    Muhammad Rezuna said:
    What is it that the example is trying to accomplish? Is there a document of the project/lab specification?

    It is just an example of sampling an analog signal at 16KHz. It demonstrates configuring the timer and the DMA to work with the ADC.

    Muhammad Rezuna said:
    I am not familiar with many of the functions used in the example, are the functions documented in the TivaWare doc?

    Sorry, I must chide you a bit here. Did you look at the TivaWare document I pointed to in my previous post? I understand you are a student, so I am cutting you some slack, but you need to understand that this forum cannot replace reading the required documentation. Attempting to do so wastes your time and mine.

    Muhammad Rezuna said:
    When you say that the project “uses a timer to trigger an ADC and uses DMA to fill a 64 sample buffer” does that mean the project’s trying to digitize 64 analog values?

    The example continually digitizes an analog signal at a 16KHz rate. It stores the results in one 64 sample buffer and then switches to a second buffer. When the second buffer is full, it switches back to the first buffer overwriting the original data. The program continues indefinitely. The idea is that the CPU can process one buffer of data while the other buffer is being filled. 

  • In our case, the AC analog signal we are analyzing has a frequency of 1.3KHz and we want to store the signal in an array of length 4096. We also want to change the sampling rate to 332.8KS/s rather than the default 1MS/s.  With the frequency being 1.3KHz, the period is approximately 317 ms. Would the correct approach be to use a timer to trigger the ADC, because we are lowering the sample rate, and an interrupt to let the ADC know when to stop converting? And would I then have to configure the uDMA controller to store the data needed to store 4096 digitized values? If the period of the signal is approximately 317ms (1/frequency), does that mean each element would have to be the average value of 77.4 microsecond (317ms/4096) interval?

  • Greetings,

    You may want to "Revisit your calculations" prior to seeking (further) vendor assistance.

    Muhammad Rezuna said:
    With the frequency being 1.3KHz, the period is approximately 317 ms.

    It is not.    And - this invalidates your "follow-on" average ADC conversion spacing of 77.4µS.

    In addition - how was 332.8KS/S chosen for sampling rate?    Nyquist accepts a (substantially) lower frequency - does it not?

    Muhammad Rezuna said:
    ... an interrupt to let the ADC know when to stop converting?

    Should you develop some means to, "stop converting" - have you not realized, "One ADC Snapshot (composed of 4096 elements) in Time?"   Is that what you want?    If so - how do you determine when, even if -  to launch your conversion?

    This post aims to enable the best vendor response in your behalf - via the early identification & addressing of (suspect) issues...

  • I am also confused by your math, but if you want to sample at 332.8K samples/second then use the ADCwDMA example I provided and change the TimerLoadSet() function in init_Timer from "MAP_SysCtlClockGet()/16000 -1" to "MAP_SysCtlClockGet()/332800 -1" . Since the maximum uDMA transfer size is 1024 items, use ping-pong mode to fill four sequential buffers to get your 4096 samples.

  • I've made an error in my calculation, the period (1/frequency) would be 0.769 mS which would then translate to an average ADC spacing of 0.189 µS (0.769mS/4096).

  • The TM4C ADC cannot do conversions in less than 1uS. Why do you want to do 4096 conversion during  a single cycle? What are you trying to accomplish?

  • I’ve realized that I made a calculation error, the period (1/frequency) would be 0.769mS and thus would translate to an average ADC conversion spacing of 0.189µS.

  • Let me take a step back and try to explain what we plan on trying to accomplish as I’ve realized that I made another explanation error

    • First, this is what we are doing: we are performing cross-correlation of the AC analog signal (the laser) with a square wave. Through simulations on MATLAB we found that cross-correlating 4096 data points was ideal. The project entails using data from the laser which we first have to digitize before we can cross-correlate
    • The sampling time should be the period (0.768mS) of the laser multiplied by the number of pulses (16) that we want to cross-correlate which is approximately 12.3mS. Thus, dividing the sampling time (12.3mS) by the number of points (4096) results in an ADC spacing of approximately 3µS.

    Hopefully this clears up the misunderstanding.

  • Converting one channel every 3uS for 4096 samples is doable. Use the ADCwDMA example I sent you as a starting point.

  • After reading some more of the TivaWare Peripheral’s Driver Library User’s Guide, I have some questions about the ADCwDMA example:

    • Regarding the ADC:
      • I have some questions with the function ADCSequenceStepConfigure(). The fourth parameter, which the document refers to as ui32Config, is a logical OR of several values.
        • The example has for parameter: ADC_CTL_CH0|ADC_CTL_END|ADC_CTL_IE. With these parameters I am getting that channel 0 is chosen, the step is the last step in the sequence, and that an interrupt will occur when the step is completed. My question is, How was Channel 0 selected? And what does the interrupt that is caused by specifying that this is the last step make the ADC do?

    • Regarding the µDMA controller:
      • on page 583 it says that “in order to support all channels and transfer modes, the control table array should be 1024 bytes, but it can be fewer depending on transfer modes used and number of channels actually used.” Does this mean, say we are using the Ping Pong transfer mode as you suggested, that both the primary and alternate data structure can hold 1024 data values at a time?

      • In the function intit_DMA() of the ADCwDMA example, the line “uDMAControlBaseSet(ucControlTable)” is called. “ucControlTable” is defined as having a length of 1024. Can you explain the significance of the lines #pragma DATA_ALIGN(ucControlTable, 1024) and uint8_t ucControlTable[1024]? From what I’ve researched, the first line will locate ucControlTable at an address that is evenly divisible by 1024 and that the function DATA_ALIGN is occasionally used when a symbol is to be located a special address boundary. The latter line creates a an unassigned int array of length 1024.

    More questions:

    • How is the function ADCseq0Handler() being utilized? It is not called in main.
    • How do we determine what to run the clock at? For this example, was 80MHz just chosen?

     

  • Muhammad Rezuna said:
    I have some questions with the function ADCSequenceStepConfigure(). The fourth parameter, which the document refers to as ui32Config, is a logical OR of several values.
    • The example has for parameter: ADC_CTL_CH0|ADC_CTL_END|ADC_CTL_IE. With these parameters I am getting that channel 0 is chosen, the step is the last step in the sequence, and that an interrupt will occur when the step is completed. My question is, How was Channel 0 selected? And what does the interrupt that is caused by specifying that this is the last step make the ADC do?

    Your understanding is correct. Channel 0 was chosen arbitrarily, but you must choose a channel for each step. The same channel may be used on multiple steps.

    I am not sure where to start on the second question. Are you not familiar with the concept of interrupts? Once the step with the interrupt enabled has completed, the interrupt (if also enabled at the NVIC-nested vector interrupt controller and enabled globally) will temporarily stop execution of the current thread of instructions and jump to the interrupt service routine. In this case that is the function ADCseq0Handler(). It uses that routine because the address of that routine was placed in the ADC0 interrupt routine spot of the vector table that is defined in the file tm4c123gh6pm_startup_ccs.c. When that function completes, the CPU returns to the original thread.

    Muhammad Rezuna said:
    on page 583 it says that “in order to support all channels and transfer modes, the control table array should be 1024 bytes, but it can be fewer depending on transfer modes used and number of channels actually used.” Does this mean, say we are using the Ping Pong transfer mode as you suggested, that both the primary and alternate data structure can hold 1024 data values at a time?

    Yes and no. Don't confuse the control table with the destination buffer. The control table should be allocated and aligned as 1024 bytes. The maximum  uDMA transfer count is also 1024. That means the maximum destination buffer size is 1024 bytes, half-words or words. In my example it was only 64 half-words.

    Muhammad Rezuna said:
    In the function intit_DMA() of the ADCwDMA example, the line “uDMAControlBaseSet(ucControlTable)” is called. “ucControlTable” is defined as having a length of 1024. Can you explain the significance of the lines #pragma DATA_ALIGN(ucControlTable, 1024) and uint8_t ucControlTable[1024]? From what I’ve researched, the first line will locate ucControlTable at an address that is evenly divisible by 1024 and that the function DATA_ALIGN is occasionally used when a symbol is to be located a special address boundary. The latter line creates a an unassigned int array of length 1024.

    You are mostly correct. The second line creates an array of 1024 unsigned bytes. The requirement to be aligned on a 1024 byte boundary address is because of the uDMA hardware. 

  • More question/clarification regarding the example:

    • I am familiar with the concept of interrupts, however I was unaware that line ADC_CTL_IE issued an interrupt that causes the CPU to jump to routine ADCseq0Handler(). I was unaware of the file tm4c123gh6pm_startup_ccs.c. 
    • Once the timer is enable, how does the CPU automatically know to fill-in the primary buffer first or does it?

    For our project:

    • Regarding the uDMA controller, the arbitration size is the number of items that are transferred in a burst before the uDMA controller re-arbitrates for channel priority. in the example, 64 was the arbitration size because that was the buffer size selected. I plan on using the max transfer value, 1024, is there any consequences for using the maximum value?
    • As mentioned before, we need the ADC to gather 4096 samples/data points. We are using the uDMA controller to transfer data from the ADC to two buffers. My approach for the project is:
      • We can have two buffers that will hold 1024 samples each and an array of length 4096. Have the first-set of 1024 samples stored in one buffer and then transfer the data to the array of length 4096. Once that buffer is full, we can start transferring data from the ADC to the second buffer to store the second-set of 1024 samples which we will then send to the array of length 4096. This process will repeat again once more to fully fill the 4096-length array. Is this the correct approach?
    • Converting and saving of data occurs once we enable the timer, like the ADCwDMA example, however how can we stop the ADC from converting more data?

  • Muhammad Rezuna said:
    Once the timer is enable, how does the CPU automatically know to fill-in the primary buffer first or does it?

    The CPU does not fill the buffer, the uDMA does. The CPU starts the timer, the timer starts the ADC. When the ADC has a conversion, it triggers the uDMA to copy the digital value to a buffer. The uDMA always starts using the  primary control structure. In ping-pong mode, when the uDMA has done the number of transfers requested, it causes an ADC interrupt and switches to using the alternate control structure. 

    Muhammad Rezuna said:
    Regarding the uDMA controller, the arbitration size is the number of items that are transferred in a burst before the uDMA controller re-arbitrates for channel priority. in the example, 64 was the arbitration size because that was the buffer size selected. I plan on using the max transfer value, 1024, is there any consequences for using the maximum value?

    No! If you are only converting one channel every 3uS, there is no need to set the arbitration size other than 1.

    You don't need to have separate buffers and then copy the data from one buffer to the other. Create one large array of 4096 half words. Setup the primary uDMA channel destination address to &ui16Array[0]. Setup the alternate uDMA channel destination address to &ui16Array[1024]. In your ADC interrupt routine have a static variable that keeps count of how many times you are interrupted. Initialize that variable to 0. In the interrupt routine, if that value is:

    0: it means the first 1024 samples are taken and now the uDMA is filling samples 1025-2048 using the alternate control packet. Setup the primary packet with destination address &ui16Array[2048] and re-enable it. Then increment your static variable

    1: means that the uDMA has finished filling the array with values 1025-2048 and has switched back to the primary control structure and started filling samples 2049-3072. Setup the alternate packet with destination address &ui16Array[3072] and re-enable it. Then increment your static variable.

    3: means the uDMA has finished filling the array with 3072 values and the alternate control packet is now getting the last 1024 values. Just increment your static variable.

    4: All 4096 values have been captured. Disable the ADC and the Timer. Set a flag (or use the fact that your static variable equals 4) to notify your main code that it can process the data.

  • After studying up on the syntax and code I tried to step through the example that you provided (ADCwDMA). Entering the while-loop, BufferStatus[0] = Filling and BufferStatus[1] = EMPTY.  In order to enter either of the if-statements, one of the BufferStatus has to == FULL. The only occurrence of change in BufferStatus exists in the function "ADCseq0Handler()." To get to the function, I understand that an interrupt must occur. I am confused as to when the interrupt is called so that the compiler will enter the interrupt handler (ADCseq0Handler) for the Bufferstatus to change. Because without the change in bufferstatus the while loop will never enter an if statement.

    Thank you,
    Muhammad Rezuna
  • After studying up on the syntax and code I tried to step through the example that you provided. Entering the while-loop BufferStatus[0] = Filling and BufferStatus[1] = EMPTY. In order to enter either of the if-statements, one of the BufferStatus has to == FULL.

    The only occurrence of change in BufferStatus exists in the function "ADCseq0Handler()." To get to the function, I understand that an interrupt must occur. I am confused as to when the interrupt is called so that the compiler will enter the interrupt handler (ADCseq0Handler) for the Bufferstatus to change. Because without thechange in bufferstatus the while loop will never enter an if statement.

    Thank you

    -Muhammad Rezuna

  • The ADC interrupt occurs when the uDMA has finished transferring the requested number of conversions that was programmed by the function uDMAChannelTransferSet(). In my example that is after 64 conversions.

    • To clarify, once the µDMA finishes the number of transfers that we set it to, the program jumps to the ADCseq0Handler() interrupt routine. There is no explicit call for an interrupt in the code.

    • In the ADCwDMA example the clock rate is set to ADC_CLOCK_RATE_HALF, in function ADCClockConfigSet(), which the TivaWare user’s guide states that it is providing back every other sample. Does this mean that every other sample is not being recorded? With that being said, if we want every sample to be recorded for our project we should change it to ADC_CLOCK_RATE_FULL?

    • Is it possible for the Tiva ADC to convert at 8-bits as opposed to 12-bits? The reason I ask this question is because of performance purposes of the correlation algorithm. If this is possible, then we would need to configure the µDMA data transfer size accordingly in uDMAChannelControlSet().

    • I also have a question regarding the System Tick (SysTick). In the example, an interrupt is called every 10 microseconds (the period). From what I understand, the interrupt handler for the SysTick just updates/increments the counter g_ui32SysTickCount. Is there any other significance of the SysTick?

    Regarding our project, we want to be able to collect different sizes of samples (i.e. 2048, 8192, etc) and so we made some changes to the ADC interrupt handler. Is it possible for you to quickly glance over our code? Due to the COVID-19 epidemic, we are unable to physically test our ADC algorithm to see if it operates properly as we don’t currently have the equipment to generate an AC signal(laser and photodiode). As an alternate option, I was wondering if you can look over to see that we have configured the ADC correctly. 

    There are a lot of comments that I’ve included to help me understand the functions involved in configuring the ADC. Those can be ignored.

    ADC_Capstone.zip

  • Muhammad Rezuna said:
    To clarify, once the µDMA finishes the number of transfers that we set it to, the program jumps to the ADCseq0Handler() interrupt routine. There is no explicit call for an interrupt in the code.

    Correct, it is a hardware interrupt.

    Muhammad Rezuna said:
    In the ADCwDMA example the clock rate is set to ADC_CLOCK_RATE_HALF, in function ADCClockConfigSet(), which the TivaWare user’s guide states that it is providing back every other sample. Does this mean that every other sample is not being recorded? With that being said, if we want every sample to be recorded for our project we should change it to ADC_CLOCK_RATE_FULL?

    That description is misleading. What that value does is it programs the phase delay. It probably should have been omitted from that example. Using ADC_CLOCK_RATE_HALF causes the ADC to delay 5 ADC clocks from the trigger to start the conversion. This feature is really only useful when you set different delays for each of the two ADCs and use the same trigger. For this example the best code would be:

        ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PIOSC, 1);
    

    Muhammad Rezuna said:
    Is it possible for the Tiva ADC to convert at 8-bits as opposed to 12-bits?

    No

    Muhammad Rezuna said:
    I also have a question regarding the System Tick (SysTick). In the example, an interrupt is called every 10 microseconds (the period). From what I understand, the interrupt handler for the SysTick just updates/increments the counter g_ui32SysTickCount. Is there any other significance of the SysTick?

    No, in this example it is irrelevant. It is used in some other examples.

    Muhammad Rezuna said:
    I was wondering if you can look over to see that we have configured the ADC correctly.

    I am sorry, but we do not do code reviews. However, if you have specific questions, I can attempt to answer them.

    • To clarify, when configuring the clock of the ADC we should omit ADC_CLOCK_RATE_HALF because we don’t want any delay for the ADC to do conversions from trigger; and configure the ADC just as you suggested ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PIOSC, 1);.
    • Is it possible to configure the ADC to operate for a voltage range of 1.5mV to 2.5mV? That is the range of the laser we will be working with. For our project, the ADC would have to have a resolution of 0.244µV (voltage range / 2^12 bits or 1mV/4096 bits).
    • For the ADCwDMA example I am unsure as to how the analog signal is linked to the ADC. In the function init_ADC() it mentions GPIO_PIN_2, which pin does this correspond to in the launchpad?

  • Hello,

    If I may - prior to vendor arrival - your "just announced" requirement of a, "1mV Full-Scale Range" is surely a, "Deal-Breaker!"

    Any "mixed signal" device (i.e. MCU) - attempting such a range - is almost surely to be, "Over-Challenged!"

    Should your laser "really" produce such a "restricted output range of interest" then your project almost certainly would benefit from a "Highly Specialized (TOP CABIN) external ADC!"   In addition - such a design must carefully consider the impact of temperature, reference voltage purity, and superb signal routing - even shielding!   "Gaining up" your laser signal will (also) introduce substantial challenge!   How will you account for: Impedance Matching, Input Offsets, Signal Drift w/temperature/aging, & device process variations?   Somehow the, "Quest for a broader, laser signal output range" demands (some) consideration - does it not?    (And ... Why that specific laser?)

    Our small tech firm produced a, "Less exhaustive design" (70mV full-scale range) for a local, U.S. Federal Laboratory - and we were (only) able to meet specification by selecting a highly advanced (external of course) ADC & then "Housing ALL circuitry" w/in a small (temperature controlled) metal encased (shielded) oven!

    Adding so "strict" (and unexpected) a requirement - so LATE w/in a thread - IS (both) unfortunate & concerning...

  • Muhammad Rezuna said:
    To clarify, when configuring the clock of the ADC we should omit ADC_CLOCK_RATE_HALF because we don’t want any delay for the ADC to do conversions from trigger; and configure the ADC just as you suggested ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PIOSC, 1);.

    Yes

    Muhammad Rezuna said:
    Is it possible to configure the ADC to operate for a voltage range of 1.5mV to 2.5mV? That is the range of the laser we will be working with. For our project, the ADC would have to have a resolution of 0.244µV (voltage range / 2^12 bits or 1mV/4096 bits).

    NO. 

    Muhammad Rezuna said:
    For the ADCwDMA example I am unsure as to how the analog signal is linked to the ADC. In the function init_ADC() it mentions GPIO_PIN_2, which pin does this correspond to in the launchpad?

        GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_2);
    

    This line of code configures GPIO Port E pin 2 as an ADC pin. Looking at the datasheet you will see that this is analog input channel 0.

    • Looking at Table 13-1 on the datasheet, if we have configured GPIO Port E pin 2, using GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_2), then that corresponds to analog input channel 1 or PE2 on the launchpad correct? With that being said, does the analog input channel and the µDMA channel that we select have to correspond to the same number? (i.e., since we configured analog input channel 1 then we need to configure µDMA Channel 1 as well).
      • How I tested to see if the setup was correct:
        • Had the function generator continuously output a square wave with an amplitude of 1.5 V at 1.3 kHz (with an offset of 1.5 V so that the values are either 0 V or 1.5V).
        • Connected the output of the function generator to pin PE2 and the grounds together.
        • Using the GUI composer, the plot showed that the data array only had zeros stored in it. This would make sense for the case that we have to use µDMA Channel 1 for analog input channel 1 because µDMA Channel 0 was selected.
  • Muhammad Rezuna said:
    does the analog input channel and the µDMA channel that we select have to correspond to the same number?

    The uDMA channel corresponds to which ADC converter you are using, not which channel you are converting. Your code uses ADC0_BASE so it should use UDMA_CHANNEL_ADC0.

    It looks like you changed the pin configuration to use PE2 (Analog input 1) but you left the ADCSequenceStepConfiguration() using Analog input 0 (ADC_CTL_CH0). Lines 278-279 should be:

        ADCSequenceStepConfigure(ADC0_BASE, 0u, 0u,
                                 ADC_CTL_CH1 | ADC_CTL_END | ADC_CTL_IE);
    

    • I understand now that the uDMA channel corresponds to the ADC module that we have selected as opposed to which ADC converter we are using. However I am still confused on the function ADCSequenceStepConfigure(). I configured the ADC similarly as the ADCwDMA example where I selected Port E Pin 2 (PE2). Changing to Analog input 1 (ADC_CTL_CH1) in function ADCSequenceStepConfiguration() makes sense because we’ve chosen Analog input channel 1 (PE2), but why in the example is Analog input channel 0 (ADC_CTL_CH0) being used when the same pin (PE2) is chosen?
    • Applying the same test, we are getting an error when uploading the code to the launchpad. We are unsure as to what is causing the program to exit and thus not storing any values in the data array.

    4540.ADC_Capstone.zip

  • More information for the previous post:

    When trying to debug the program, by stepping over one line at a time, the program runs into the following error. It is unable to execute line SysCtlClockSet().

  • Muhammad Rezuna said:
    why in the example is Analog input channel 0 (ADC_CTL_CH0) being used when the same pin (PE2) is chosen?

    Sorry, that was an error. Probably when I changed the example from using AiN0 and AIN1. It should be PE3 that is used for AIN0. I will update the example in the original post.

  • That is not an error saying it cannot execute the function SysCtlClockSet(). Rather it is a warning that it did not find the C source file for this function. The reason for that is that you linked from the TivaWare library driverlib.lib. Typically when you are debugging you would C step over the library call. Almost always errors are in the application code and it is not necessary to step into the library. If however, you do need to do C debug in a TivaWare library function, the easiest way is to copy the file from the TivaWare driverlib directory into your project and rebuild. Another option is to click on the "Locate File" button from the warning window and browse to the file: "C:\ti\TivaWare_C_Series-2.1.4.178\driverlib\sysctl.c". For the more advanced user, a third option is to  import the driverlib project and rebuild the driverlib on your own machine. This version of the driverlib.lib will point to the source files on your machine.

  • I see, rather than stepping into the library I should have stepped over. However, once I uploaded the program to the Tiva and clicked "resume," the program exited unexpectedly. I am not sure as to what is causing it to abort and close. The following screenshot show what occurred after I clicked "resume." I'm not sure if this occurred immediately after I hit resume once the program enters main or at some other point. No data seems to be collected.

  • Update: it turns out that I was able to step over all the lines of code in main and then program exits. However, still no conversions are stored in the data array.

  • I think our issue, regarding data capture, lies on the interrupt(s) that are being caused.

    • I want to clarify when the ADC sends an interrupt. We have setup, like the ADCwDMA example, ADCSequenceStepConfigure() to send an interrupt after every conversion/sample – this is specified by stating in the parameter ADC_CTL_IE. Thus, the program jumps to the interrupt routine ADCseq0Handler() after every sample.
    • I have mistaken in that I thought the program jumps into the interrupt routine, ADCseq0Handler(), when a buffer has reached the maximum number of transfers.
    • Thinking that ADCseq0Handler is executed when the uDMA has finished transferring 1024 samples, we made an algorithm to stop the ADC and timer within ADCseq0Handler with instructions you provided on April 16.
      • If it is the case that ADCseq0Handler() is executed after each conversion, we would have to keep track of how many conversions are completed (i.e. how many times it has entered the function) and for every 1024 times that the function has been called we execute the code (channel transfers and variable incrementation/decrementation) that we currently have.

     

     

     

  • No, you were correct the first time. When using the uDMA with the ADC you get an ADC interrupt when the number of samples (in your case 1024) have completed. What I see wrong in your code is that you don't allow any time for the ADC and uDMA to do its thing. Your main routine exits right after you start the timer. With the debugger attached, the device stops after you finish main. Try adding a while(1); loop at the end of main. Let your code run a second of two before you stop it. Then you will see values in data[].

    • I see. After adding a while(1) loop at the end of main I was able to capture data. However, when graphing the result, more than 16 periods of data has been captured. The analog signal, a square wave, I have connected has a frequency of 1.3 kHz and an amplitude of 1 V with a 1 V offset so that the values fluctuate between 0 and 2 V. The digitized value accurately represents that, the only issue is the number of periods that were captured which has to do with the sample rate. Using the equation Sampling rate = array size/ (# of pulses * period of data), with the array size being 4096 and wanting to capture 16 pulses or periods, we obtain approximately 332.8 kHz. As you suggested previously, I changed the sampling rate portion of my code to MAP_TimerLoadSet(TIMER0_BASE, TIMER_A, MAP_SysCtlClockGet() / 332800 -1); but ended up with almost three times the pulses. Any suggestions as to how I can collect only 16 pulses? The following image is the graph of the collected values:

  • Hello,

    The "student section" of our firm's staff has a vastly different interpretation of poster's "math basics."

    • Input Signal is 1.3KHz thus its period is 1/1300=769µS.   (the unexplained choice of the 1.3KHz, square-wave - as ADC input drive - proves "curious.")
    • Assuming a 50% duty cycle - that square-wave signal will alternate its level at ~384µS intervals
    • Poster's (claimed) 332,800 Hz sample rate yields a period of 1/332,800 = ~3µS

    Thus - will not "128" successive ADC "reads" (that's 384/3) occur prior to the "change of state of the input signal?"   And all (must) contain the (near) same ADC reading!

    Yet the graph shown reveals "ADC captures" which regularly alternated in level.    These were suspected to have occurred at one-half of the input signal's period.   (again ~384µS)   Thus the 332,800 Hz sample rate (every 3µS) - and/or "other processing" - prove almost certain to be improper.   (or 128 near identical ADC readings would have resulted!)

    It is believed that far more understanding would be gleaned by feeding the ADC w/a "Linear Ramp Voltage" - regularly spanning the ADC's 0-3V analog input range.  This method would reveal the linear, "building of the input voltage" - and by knowing the "Rate of Change of this Linear Ramp" - the (real/actual) sampling rate of the ADC can be (properly) confirmed!     (this Linear Ramp method we've developed has been approved at several, U.S. Federal Labs - provides (real), powerful & instructive insights!)

    • The reason for selecting a 1.3 KHz square-wave was because we plan on having a laser signal with a frequency of 1.3 KHz be the input to the ADC for our project. The amplitude of the laser signal will be different, however, for testing purposes we have selected easy values to work with that are appropriate and within the ADC range.
    • Regarding the duty cycle, 50% duty cycle is correct.
    I follow that with the frequency being 1.3 KHz, the period of the square-wave is 769 µs and since the duty cycle is 50%, meaning that it the signal is high 50% of the period and low 50% of the period, the square wave signal will alternate its level at approximately 384µs intervals.
    • For our project, we plan on capturing 16 pulses/periods which corresponds to approximately 12.3ms of data. With the 12.3ms of data, we need to be able to space the sampling so that it fits in an array that will be a multiple of 1024 (e.g. 2048 or 4096). Let’s say for example that the data array size is 4096, dividing the total sampling time by the array size yields ~3µs between each sample.
    • As mentioned in your reply, 128 successive readings should have approximately the same value before a change of state (going from high to low or low to high). From the data we obtained, much less than 128 readings occurred before changing states.
      • My question is, did we incorrectly code the sampling rate in CCS to have sampled at a slower rate than it should have (because there were less successive similar readings than there should be)? Or is the calculation of the sampling rate the issue?
      • Currently, this is how we are stating the sampling rate in CCS: MAP_TimerLoadSet(TIMER0BASE_BASE, TIMER_A, MAP_SysCtlClockGet() / 332800-1);
        • If we the issue lies in the code, then it would be the last parameter, based on the TivaWare user’s guide, as that specifies the loaded value.

  • Greetings,

    Good to hear back from you - our "student staff" believed that their (two past) reviews of your group's work would prove helpful.   (A neat goal would see, "Students around the world forming a bond - and helping each other...")

    Our tech firm believes strongly in the principles of "KISS" - in which the (large) problem is reduced to far smaller (constituent parts) - and then each of these is individually well measured, tested & resolved.    Only when each individual piece has "been verified" do we enable those pieces to be combined.   Most always - this method provides far sharper focus - and the smaller "playing field" (problem solving) proves, "Faster, Easier & Enhanced."    Our students recommend that your group adopt the "KISS" method.

    Following "KISS" guidelines - lets review your current writing:

    Muhammad Rezuna said:
    we plan on capturing 16 pulses/periods which corresponds to approximately 12.3ms of data

    • We agree - 16, 769µS periods yields ~12.3mS

    Muhammad Rezuna said:
    With the 12.3ms of data, we need to be able to space the sampling so that it fits in an array that will be a multiple of 1024 (e.g. 2048 or 4096).

    • While this may be a "fair objective" - it violates "KISS" (too much going on - difficult to uniquely test/verify) & resulted in your erroneous data capture
    • We agree that your math proves correct (4096 ADC "reads" forces a sampling rate of ~3µS - just as you've said.)   However - have you properly "recognized" any/all of the restrictions & "time adders" - which the complex interaction between ADC & µDMA demands?    Might those prove derailing?

    Muhammad Rezuna said:
    much less than 128 readings occurred before changing states.    My question is, did we incorrectly code the sampling rate in CCS to have sampled at a slower rate than it should have? 

    • Again - "KISS" greatly assists your resolution of this issue - rendered (nearly) impossibly complex by your "All in one" method!
    • We're glad that you've accepted our student staff's derivation of those "128 readings (likely)" prior to the 1300Hz input signal's "change of state."
    • Your use of the "Unexplained Graphic Chart" was extremely "KISS Defying!"    Much follow-on effort "depends" upon its accuracy - and that is (completely) UNKNOWN!   Does not your reliance upon that graph (in one go) force our group & others to "blindly" accept that:
      • each/every one of your ADC conversions succeeded
      • each/every one of your ADC conversions occurred at the correct, "Moment in Time"
      • that (undescribed) graphing method was properly implemented 
      • all values "fed" to the graph were correct and w/in the proper order!   The value of "KISS" - and its forced, systematic verification of each design element - should now be (especially) apparent!    (blind faith - in a multi-element procedure - which proves key/critical ... Not so much!)

    • Your formation of the question, "Did we incorrectly code the sampling rate ... at a slower rate" is clever.   (and indeed "One of several possibilities!")
      • Again - analysis of such (potential) "multiple causes" is where "KISS" shines!   
        • What are the "other" sources of "Sampling Rate" slow down?   
          • Recall - you have REALLY violated "KISS" by adding the µDMA to your process!   At this (very) early stage!
          • Your "single" ADC conversions are Timer Triggered - & that timer was set for a ~3µS period - is that true?
          • What other ADC & especially µDMA "house-keeping" is going on?   How does that impact your 3µS goal?

    It is believed that "our students" (having long practiced "KISS") can fully resolve your issue (even assisting in your "1mV laser issue") yet the goal is "your group's learning" - should not that prove so?

    You must begin somewhere - how would you propose "Proving that your ADC is being successfully triggered - at/around the 3µS rate you've chosen."    (that's a good "KISS" beginning - and the fact that even that "Basic NEED" remains unknown - soundly reveals the WEAKNESS of a "Non-KISS" approach...)    Hint: You are advised "Not to attempt more than 8 ADC Conversions" initially.   And - Not to employ µDMA at this early stage!    (Having done so - w/out great success - you now likely recognize the limitations of such a (pardon) disorderly - unverified, "Clumping of Multiple Objectives!")   

    Your objective instead is to "Achieve a series of small victories" - and build upon these - "One measured & test/verified Step at a time!"    (this is known as, "Proceeding by Refinement" - which is a subset w/in "KISS.")    Our preference is to "Guide you in this improved direction - aimed to enhance your Problem Solving" ... not provide "Cut/Paste" - which (almost) never proves "lasting nor insightful - nor a proper teaching objective!"

    • Our code was built from the ADCwDMA example that Bob shared with us. We are aware that we are trying to achieve something different than what he intended to do with his code, but we used it as a building block and tried to implement the basics for our needs. With that said, we are not completely sure that we have “properly recognized any/all of the restrictions & time adders – which the complex interaction between ADC & µDMA demands.”
    • You are correct in that we want the ADC conversions to be Timer triggered and that for a 4096-size array the timer should be set to approximately 3µs period. However, we are unsure as to where to configure the timer to trigger the ADC every 3µs. We assume that having a sampling rate of 332.8 KS/s that it is implied that every 3µs a sample is collected. Are we mistaken?

    What we have: we are using half-width timer (TIMER A) and configured it to work periodically and have the sampling rate be 332.8 KHz.

    • Currently, we found that the data alternates from high to low or low to high every 47 samples – which means that one period of data or 0.769 ms is captured within 94 samples. This analysis was concluded by “watching” the data array in CCS – after running the program we analyzed the values that were collected and recorded when/where the alternating values occurred. We will know that it is successfully triggering every 3µs when the data captured alternates, changing from high to low or low to high, every 128 samples. This implies that the sampling rate is slower than 332.8KHz.

    Regarding to ADC & µDMA “house-keeping”

    ADC

    • We have configured the launchpad to use Port E Pin 2 (PE2) for the analog-to-digital conversion .
    • We configured the clock of the ADC to utilize the PIOSC and selected a clock divider of 1
    • Then we disabled the interrupts that the ADC might cause as well as the sample sequencer that we wish to use.
    • Then we configured the ADC module and sample sequencer we will use to be triggered by a Timer and selected to use ADC input channel 1 as that corresponds to the (PE2) pin on the launchpad that we will connect our signal into.
    • Lastly, we clear the interrupts and enable the sequencer and interrupts.

    µDMA

    • First we set the base address of the channel control table. The table is aligned on a 1024-byte boundary.
    • Then we disabled the attributes, which are ALTSELECT, HIGH_PRIORITY_, and REQMASK, of the µDMA channel we selected (UDMA_CHANNEL_ADC0). Followed by enabling the burst transfer attribute.
    • Using the Ping Pong transfer mode, we have to set the channel so that it can utilize both the primary and alternate buffers – the data size is 16-bits, we are not increasing the source address increment, we are increasing the destination address by 16-bits, and setting the arbitration size to 1.
    • Then we did the first-set of uDMA channel transfer set where we tell the µDMA to store the conversions. We have the primary structure start filling in the array, that holds all the conversions, from the first position and once primary buffer reached the maximum number of transfers, 1024, the alternate structure will do the next set.
    • After configuring the µDMA, up to this point, we enable the uDMA channel.
    • Another house-keeping that we have to keep track of is whenever the µDMA finishes transferring 1024 data values. This is where the program will jump into an interrupt routine in which we have setup so that it the ADC and timer will be disabled after a certain number of times the program has reached the interrupt routine Every time the program enters the interrupt routine, we know that a set of 1024 values have been transferred and the we need to re-configure the buffer structure that just finished transferring data – simply put, this routine keeps track of how many transfers are needed to fill in all of the data array.
  • Thank you - we are in the midst of shipping orders & just noted your post's arrival.

    Please do "re-read" our earlier post - especially the "hints" our students provided in your behalf.   (it is apparent that our belief in "KISS" has not "fully/adequately" been accepted - you must "Accept the fact that such an involved program is FAR more successfully managed via "Small, methodical, Test & Verification!")    Continuing "en masse" - as you are reporting - is "Outside our recommendation!"   (Adds greatly to your (and our) time & effort expended - and provides 'No positive assurance' that it will ever succeed!)

    Our students have asked direct questions - you should examine & respond - indeed they succeed in "Limiting & Pointing you to a more efficient & PROPER Direction."   

    [edit/added] 09:50, 05 May:  With the exception of  your "experimentally determined" 94 ADC samples (when 128 were expected) your writing is (almost) entirely "Your Intent" - and "Not that achieved via "Systematic, Sequential, Measurement & Verification!"    Now the listing of "intent" is good - yet the "absence" of your "Measured Tests & Verification" greatly alarms & concerns!    Is it not true that your "error(s)" could appear "anywhere" - this is why our belief in the "KISS" approach - which systematically examines each/every of your "limited-scope code functions" proves so VITAL!   (As an ex military officer - fighting any battle along a "too extended front" - is rarely recommended!   [Yet that's what you've chosen - by enabling the entire program - w/out adequate "Test/Verification" of EACH smaller, constituent piece!]   known as "KISS!")

    We will continue to offer assistance - yet your "Acceptance of our suggested, "Divide & Conquer" (i.e. small, piece by piece, code test & verification) IS required.   At ~50 posts into your code objective - the "Non-KISS" approach (even w/superb vendor oversight {thank vendor Bob}) - has simply "Over-Challenged" - and an improved (more thoughtful, systematic, higher focused) approach - cries out for your adoption!

  • I checked the timer by enabling the timer interrupt and toggling a pin. The timer period looks correct at 3uS.

  • Bob Crosby said:
    I checked the timer by enabling the timer interrupt and toggling a pin.

    Indeed - yet that's (just) one check - and it is likely that (many more) are required.

    Note too that the Timer's Frequency has (now) been confirmed - yet that does not insure that the ADC has now been properly configured & altered (from poster's original settings) -  to "Accept (only) that Timer Trigger."   

    We had hoped that the poster would (also) "test & verify" that (at minimum) his Timer configuration had succeeded.   Of course that's (just one) of "Many such Checks" - which serve to enable this poster (and multiple others) to, "Break Down & Verify small elements of their code - ideally in a systematic fashion - which then enables their "REAL UNDERSTANDING!"     That degree of added poster competence - it is hoped - should be a central goal.    (even beyond - "post clearing.")

    As has been (convincingly) proved (again) w/in this thread - "Poster's ability to succeed w/(even) "Highly Duplicative Program Code" is NOT guaranteed!    Can we agree that (some) systematic, "Code Test & Verification - ideally on a 'Less than Full Program Scale" - serves as a potent means for, "Error Discovery & Correction" (i.e. REAL Poster Learning) - even if - and especially if - a skilled Vendor Agent is 'Not on the Scene!"

  • No, in this case the problem really is mine. The issue is that the ADC is running with the 16MHz clock divided by 8. It takes 8uS to do a conversion so triggering the ADC every 3uS misses a bunch of triggers. This was never seen in the original example because the trigger rate was much lower. For now the simple solution is to comment out the call to ADCClockConfigSet(). The default values will give you the 16MHz PIOSC as a clock. I will try to figure out (and correct) the example tomorrow.

  • Thank you for checking that the Timer periodically triggers correctly.

    We can confirm that it takes 8µs for a conversion as that is what we calculated with the data that we initially collected. The number of samples for 1 period can be found by dividing the period by the time of each conversion. Thus to find the time of the conversion, we can divide the signal period by the number of samples that we collected. From our data we found that it takes 0.769ms to complete 94 samples which corresponded to 1 period, thus the time of conversion was approximately 8µs.

    We omitted the code to configure the ADC clock, ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PIOSC, 1), and found that it solved the issue. After running the program again, we now find that 256 samples constitute 1 period which is what we want – supports the fact that the ADC is periodically triggered every 3µs. However, we are unsure as to why you propose the 16MHZ clock is divided by 8. We know that the internal PIOSC operates at 16MHz, but the third parameter is the clock divider and we have selected it to be 1 which means that having the code that we had should be the same as having it commented out.

  • I think we must, "Agree to Disagree."   With a proper, systematic code test & review (and a proper input signal waveform)  - this poster "Could have discovered that his conversion rate was, "Not as expected."    Note that my group's suggestion - feed the ADC input w/a "Known, Linear, Ramping Voltage" would have revealed the conversion upset far faster!

    Is it sufficient to simply, "Chain various API  ADC & µDMA functions together - "hoping for the best" - yet w/out a strong, "User effort-focused/enforced understanding?"    Those functions must be:

    • surveyed & selected - as most appropriate
    • fully & properly configured - to best satisfy the user's (particular) need!   At that moment!    Change is inevitable - a "Copy/Paste solution" (minus adequate user review & testing) is unlikely to survive (normal) change events!
    • sometimes called w/in a specific order
    • and must allow for various program "interactions" - even competing aspects w/in the same program

    The API, quality & capable Eval Boards (LPads), the IDE, the MCU Manual and the Peripheral Driver Library User Guide provide a vast & highly detailed resource inventory.    That noted - "Real World Experience in 'Surveying, Selecting, Positioning & Combining' (and then systematically debugging) the most appropriate API functions" -  proves (almost) certain to provide the, "HIGHEST VALUE USER LEARNING EXPERIENCE!"    By FAR!

    "Choosing & Connecting (even passing "time critical data" between key API functions") - really understanding function usage & impact - is a Skill which, "Users Really Require!"   Crutches & other devices which lessen (too often even remove) User Sacrifice, Focus & Commitment exert a "Long-Term Cost" - worthy of "real" consideration...

    Do we "really guide & properly assist" - by seemingly "short-circuiting" the User's Learning Process?    (thus the paradox - doing "too much" (i.e. extended blow by blow, "sample code" (thinking for the user) - likely results in  "too little" (user analysis, experimentation -> growth.)

  • The proper use of this function for a 1uS sample rate is:

        ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PIOSC | ADC_CLOCK_RATE_FULL, 1);
    

    Without the ADC_CLOCK_RATE_FULL parameter the ADC defaults to a rate of 125K samples/second. The clock divisor property is actually not used by the TM4C123 family of parts. It is used by the TM4C129 parts instead. It is included in the function for code compatibility between the two families.

  • Hello,

    Our group has long noted that you & (other) vendor agents focus primarily upon the TM4C129 device.

    Bob Crosby said:
    Without the ADC_CLOCK_RATE_FULL parameter the ADC defaults to a rate of 125K samples/second.

    That (may) be true (but only if the API intrudes) - as the ADC for the TM4C123 in fact defaults to its "top" 1MHz samples/second rate!   Thus appears to demand "No altered configuration of the ADC clock rate" by the user as this is set by MCU Reset.   (assumes the user seeks this 1MHz rate...)

    Support follows:   (a "true copy" from TM4C123 Manual, page 891)

    As earlier stated - posters (and others) gain immensely from the "Full Spectrum" usage of the richly sourced Technical Documents.   The fact that the TM4C123 & TM4C129 require "different treatment" is unfortunate - and further highlights the necessity for real, "user understanding."