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.

Example of ADC uDMA ping-pong

Other Parts Discussed in Thread: TM4C1294NCPDT, TM4C1233H6PM

I studied the best example I could find: LM3S9B96 example

My question is: is there demo code available for ADC using uDMA in ping-pong mode for the TM4C1294NCPDT (Tiva Connected Launchpad)?

Edit: See working example at the bottom of the page.

This should output "a" every time the uDMA completes the first 8 samples, then "b" for the next 8 samples, then "a" again...

It should output "?" if it gets an ADC_INT_SS0 but checks both uDMA descriptors and can't find UDMA_MODE_STOP.

It outputs "u" for the uDMAErrorHandler() interrupt.

So, if it worked correctly I would see "abababab" etc. in the serial console, but only "a" is sent. I can verify that UART0 is still working because the UART0 interrupt handler echos everything it receives. The LED pattern is also a nice check that the CPU is still running.

I figure it's something obvious I overlooked?

P.S. Hopefully it is possible to increase the sample rate to the full 1 MSps or even 2 MSps like the CPU datasheet suggests. Any insights into that would be welcome.

  • Hello David,

    In the ADC Interrupt Handler why is the Alternate Structure for UART being checked? It should be the Alternate Structure for ADC Channel during Ping-Pong!!!

        for (ch = UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT; ch <= (UDMA_CHANNEL_UART1RX | UDMA_ALT_SELECT); ch += UDMA_ALT_SELECT - UDMA_PRI_SELECT) {

    I would replace this for loop with a simple if statement

            if (ROM_uDMAChannelModeGet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT) == UDMA_MODE_STOP) {
                // found the full buffer
                uint32_t * buf = &tempSens[0];
                // re-set up a DMA transfer to the buffer
                ROM_uDMAChannelTransferSet((UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT), UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO0), buf, TEMPSENS_SIZE);
                ROM_UARTCharPut(UART0_BASE, 'a');
                return;
            }

            if (ROM_uDMAChannelModeGet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT) == UDMA_MODE_STOP) {
                // found the full buffer
                uint32_t * buf = &tempSens[TEMPSENS_SIZE];
                // re-set up a DMA transfer to the buffer
                ROM_uDMAChannelTransferSet((UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT), UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO0), buf, TEMPSENS_SIZE);
                ROM_UARTCharPut(UART0_BASE, 'b');
                return;
            }

    On the sampling rate, 2MSPS can be used.

    Regards

    Amit

  • Hah! It was a copy-paste error, from looking at the udma_demo in tivaware. Good catch.

  • Ok, making some progress. But now I get "ab???????" (the ? repeats forever).

    What I noticed was that with UDMA_ARB_4, I need to set up transfers of size 4 (not 8) by changing TEMPSENS_SIZE.

    Here's the code now:

    #include <stdbool.h>
    #include <stdint.h>
    #include <string.h>
    
    #include "inc/hw_ints.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_adc.h"
    #include "inc/hw_types.h"
    #include "driverlib/debug.h"
    #include "driverlib/gpio.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/rom.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/uart.h"
    #include "driverlib/adc.h"
    #include "driverlib/udma.h"
    
    
    //*****************************************************************************
    //
    // The error routine that is called if the driver library encounters an error.
    //
    //*****************************************************************************
    #ifdef DEBUG
    void __error__(char *pcFilename, uint32_t ui32Line)
    {
    }
    #endif
    
    void UARTsend(char * str)
    {
    	while (*str) {
    		ROM_UARTCharPut(UART0_BASE, *str++);
    	}
    }
    
    void UART0IntHandler()
    {
    	uint32_t status = ROM_UARTIntStatus(UART0_BASE, true);
    	ROM_UARTIntClear(UART0_BASE, status);
    	while (ROM_UARTCharsAvail(UART0_BASE)) {
    		char c = (char) ROM_UARTCharGetNonBlocking(UART0_BASE);
    		ROM_UARTCharPutNonBlocking(UART0_BASE, c);
    	}
    }
    
    void uDMAErrorHandler()
    {
    	ROM_UARTCharPutNonBlocking(UART0_BASE, 'u');
    }
    
    #define TEMPSENS_SIZE (4)
    uint32_t tempSens[TEMPSENS_SIZE*2];
    
    uint32_t adc_count = 0, sum = 0, max = 0, min = 0;
    
    // uDMA descriptors used by the hardware are stored in this table
    // ROM_uDMAChannel* functions write to this table
    uint32_t udmaCtrlTable[1024/sizeof(uint32_t)] __attribute__(( aligned(1024) ));
    
    void ADCseq0Handler()
    {
    	ROM_ADCIntClearEx(ADC0_BASE, ADC_INT_DMA_SS0 | ADC_INT_SS0);
    
            if (ROM_uDMAChannelModeGet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT) == UDMA_MODE_STOP) {
                // found the full buffer
                uint32_t * buf = &tempSens[0];
                // re-set up a DMA transfer to the buffer
                ROM_uDMAChannelTransferSet((UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT), UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO0), buf, TEMPSENS_SIZE);
                ROM_UARTCharPut(UART0_BASE, 'a');
                return;
            }
    
            if (ROM_uDMAChannelModeGet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT) == UDMA_MODE_STOP) {
                // found the full buffer
                uint32_t * buf = &tempSens[TEMPSENS_SIZE];
                // re-set up a DMA transfer to the buffer
                ROM_uDMAChannelTransferSet((UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT), UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO0), buf, TEMPSENS_SIZE);
                ROM_UARTCharPut(UART0_BASE, 'b');
                return;
            }
    
    	ROM_UARTCharPut(UART0_BASE, '?');
    }
    
    int main(void)
    {
    	uint32_t sysclock;
    	do {
    		sysclock = ROM_SysCtlClockFreqSet(SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480, 32*1000*1000);
    	} while (!sysclock);
    
    	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPION);
    	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
    	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
    	ROM_SysCtlDelay(1);
    
    	ROM_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_0 | GPIO_PIN_4);
    	ROM_GPIOPinTypeGPIOOutput(GPIO_PORTN_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    
    	ROM_GPIOPinConfigure(GPIO_PA0_U0RX);
    	ROM_GPIOPinConfigure(GPIO_PA1_U0TX);
    	ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    	ROM_UARTConfigSetExpClk(UART0_BASE, sysclock, 115200, UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE);
    
    	ROM_IntMasterEnable();
    	ROM_IntEnable(INT_UART0);
    	ROM_UARTIntEnable(UART0_BASE, UART_INT_RX | UART_INT_RT);
    
    	// set ADC clock to ADC_CLOCK_SRC_PLL (ROM_ADCClockConfigSet() is not defined)
    	// PLL of 120 MHz, divider 0x70 = 120/8 = 15 MHz clk/16 = 0.9375 MSps/8 = 117 kHz interrupt rate
    	// PLL of  64 MHz, divider 0x30 =  64/4 = 16 MHz clk/16 = 1 MSps /8 = 125 kHz interrupt rate
    	// PLL of  32 MHz, divider 0x10 =  32/2 = 16 MHz clk/16 = 1 MSps /8 = 125 kHz interrupt rate
    	// PLL of  16 MHz, divider 0x00 =  16/1 = 16 MHz clk/16 = 1 MSps /8 = 125 kHz interrupt rate
    	{
    		uint32_t div;
    		if (sysclock < 16*1000*1000) {
    			div = ADC_CLOCK_SRC_PLL;
    		} else {
    			div = ADC_CLOCK_SRC_PLL | (((sysclock + 8*1000*1000 - 1) / (8*1000*1000) - 1) << 4);
    		}
    		*(uint32_t *) (ADC0_BASE + ADC_O_CC) = div;
    	}
    
    	ROM_ADCSequenceConfigure(ADC0_BASE, 0 /*SS0*/, ADC_TRIGGER_PROCESSOR, 3 /*priority*/);	// SS0-SS3 priorities must always be different
    	ROM_ADCSequenceConfigure(ADC0_BASE, 3 /*SS3*/, ADC_TRIGGER_PROCESSOR, 0 /*priority*/);	// so change SS3 to prio0 when SS0 gets set to prio3
    	ROM_ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 0, ADC_CTL_TS);	// ADC_CTL_TS = read temp sensor
    	ROM_ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 1, ADC_CTL_TS);
    	ROM_ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 2, ADC_CTL_TS);
    	ROM_ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 3, ADC_CTL_TS | ADC_CTL_IE);	// ADC_CTL_IE fires after to DMA first 4 samples (out of 8 total)
    	ROM_ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 4, ADC_CTL_TS);
    	ROM_ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 5, ADC_CTL_TS);
    	ROM_ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 6, ADC_CTL_TS);
    	ROM_ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 7, ADC_CTL_TS | ADC_CTL_END | ADC_CTL_IE);	// ADC_CTL_IE fires to DMA last 4 samples
    	ROM_ADCSequenceEnable(ADC0_BASE, 0);
    
    	ROM_uDMAEnable();
    	ROM_uDMAControlBaseSet(udmaCtrlTable);
    	ROM_ADCSequenceDMAEnable(ADC0_BASE, 0);
    
    	// disable some bits
    	ROM_uDMAChannelAttributeDisable(UDMA_CHANNEL_ADC0, UDMA_ATTR_ALTSELECT /*start with ping-pong PRI side*/ |
    		UDMA_ATTR_USEBURST /*allow both burst and single*/ | UDMA_ATTR_HIGH_PRIORITY /*low priority*/ | UDMA_ATTR_REQMASK /*unmask*/);
    
    	// set dma params on PRI_ and ALT_SELECT
    	// UDMA_ARB_4 must match ADC_CTL_IE, so if ADC_CTL_IE only fired once for 8 samples, then UDMA_ARB_ would have to be UDMA_ARB_8
    	ROM_uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_SIZE_32 | UDMA_SRC_INC_NONE | UDMA_DST_INC_32 | UDMA_ARB_4);
    	ROM_uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT, UDMA_SIZE_32 | UDMA_SRC_INC_NONE | UDMA_DST_INC_32 | UDMA_ARB_4);
    	ROM_uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO0), &tempSens[0], TEMPSENS_SIZE);
    	ROM_uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO0), &tempSens[TEMPSENS_SIZE], TEMPSENS_SIZE);
    	ROM_IntEnable(INT_ADC0SS0);
    
    	ROM_ADCIntEnableEx(ADC0_BASE, ADC_INT_DMA_SS0);
    	ROM_uDMAChannelEnable(UDMA_CHANNEL_ADC0);
    
    
    	// set ADC_TRIGGER_ALWAYS to start ADC
    	ROM_ADCSequenceConfigure(ADC0_BASE, 0 /*SS0*/, ADC_TRIGGER_ALWAYS, 3 /*priority*/);
    
    	unsigned count = 0;
    	#define TBIT (19)
    	for (;; count++) {
    		if (count >> TBIT) count = 0;
    
    		unsigned val[4];
    		unsigned scale = count >> (TBIT - 10);
    		unsigned i;
    		for (i = 0; i < 4; i++) {
    			val[i] = (scale - (i << (10-2))) & ((1 << 10) - 1);
    			if (val[i] >= (2 << 8)) val[i] = 0;
    			else if (val[i] >= (1 << 8)) val[i] = (2 << 8) - 1 - val[i];
    			//else if (val[i] >= (1 << 8)) val[i] = (1 << 8) - 1;
    		}
    
    		scale = ((count & ((1 << 10) - 1)) << 6);
    		for (i = 0; i < 4; i++) {
    			val[i] = ((val[i]*val[i] >> 7)*val[i] > scale) ? (unsigned) -1 : 0;
    		}
    
    		ROM_GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_0 | GPIO_PIN_4, (val[3] & GPIO_PIN_0) | (val[0] & GPIO_PIN_4));
    		ROM_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_0 | GPIO_PIN_1, (val[1] & GPIO_PIN_0) | (val[2] & GPIO_PIN_1));
    	}
    }

  • Hello David,

    First of all the buffer size can be kept as 8 and it does not need to be made to 4

    Secondly. The ADC Sample Sequencer is configured to generate an Interrupt at every 4th and 8th conversion. However in the ISR, there is no condition to demarcate it from the DMA Completion

    ui32ADCIntStatus = ADCIntStatus(ADC0_BASE,0,true)

    ADCIntClearEx(ADC0_BASE,ui32ADCIntStatus);

    if(ui32ADCIntStatus & ADC_INT_DMA_SS0) {

     // Do the UDMA Checks Here

    }

    if(ui32ADCIntStatus & ADC_INT_SS0) {

     // Do the ADC Sequencer Interrupt checks here...

    }

    Now coming back to the original problem, the Ping-Pong works one time and stops. What we need to check is when it stops working then what does the UDMA Control Base entry for ADC UDMA Request contain.

    Regards

    Amit

  • Hello David

    Some good news. I got the issue you are seeing reproduced in my setup. There are a few things that I would like to bring to your attention so that you can update the code accordingly.

    1. The ADC Clock is 32MHz and System Clock is 32MHz in the code.

    2. The UDMA is configured for single request transfer which is less efficient that the Burst Mode

    Under this condition the system bandwidth is not sufficient to be able to get re-initialized in time so that when the next set of ADC Transfers are ready the uDMA is re-initialized.

    So what needed to be done to get it working and streaming considering that the ADC is always in constant conversion mode.

    1. Reduce the ADC Clocking to 16MHz (1MSPS) sample rate

    2. Increase the System Clock to 120MHz

    3. Enable the Burst Attribute for the UDMA Channel

    4. Increase the Transfer Size so that each of the Ping/Pong runs longer than just 8 transfers giving the CPU/System to be able to re-init the uDMA on time.

    Out of the above, if #1 and #2 are a limitation for your application, I would strongly suggest #4 to be implemented.

    Regards

    Amit

  • Thanks, Amit! I've figured it out. Short version: the DMA was not getting reinitialized because I was using ROM_UARTCharPut(). Changing to ROM_UARTCharPutNonBlocking() fixed the problem.

    Long version: I started by making the changes you suggested:

    Increase PLL to 120 MHz

    Enable the UDMA_ATTR_USEBURST attribute

    Only set ADC_CTL_IE once in SS0, so uDMA is triggered with 8 samples.

    Ignore/don't care ADC_INT_SS0, only clearing ADC_INT_DMA_SS0: ROM_ADCIntClearEx(ADC0_BASE, ADC_INT_DMA_SS0)

    Reduce the ADC clock to 2 MHz (125 kSps) which is about as slow as the ADC divider will go.

    Increase UDMA_ARB_8 to UDMA_ARB_32. At this point I was seeing:

    • ADC clock 1 MSps, UDMA_ARB_8 = 9 interrupts before UDMA stops
    • ADC clock 125 kSps, UDMA_ARB_8 = 24 interrupts before UDMA stops
    • ADC clock 125 kSps, UDMA_ARB_32 = 70 interrupts before UDMA stops

    This was the point where I understood that the UART was blocking the interrupt long enough to stop the uDMA.

  • Hello David,

    Good catch on the UART.My test environment allows me to move between Block and Non Block UART.

    All the other steps including increasing the Transfer Size in the buffer helped.

    Regards

    Amit

  • I'm having some trouble getting the ethernet LEDs to work. I'm programming the PHY registers but it appears no matter what value I set them to, they only display LINK.

    I realize this is pretty minor considering all the help Amit has already given. So I might just leave this as it is: a self-contained example for ADC ping-pong DMA running at max speed (a little over 1 MSps) that them broadcasts the ADC samples over ethernet (using about 32 Mbps of ethernet bandwidth). The CPU is pretty idle, so I use it to do PWM for one LED, nothing fancy.

    #include <stdbool.h>
    #include <stdint.h>
    
    #include "inc/hw_ints.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_adc.h"
    #include "inc/hw_types.h"
    #include "inc/hw_udma.h"
    #include "inc/hw_emac.h"
    #include "driverlib/debug.h"
    #include "driverlib/gpio.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/rom.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/uart.h"
    #include "driverlib/adc.h"
    #include "driverlib/udma.h"
    #include "driverlib/emac.h"
    
    
    //*****************************************************************************
    //
    // The error routine that is called if the driver library encounters an error.
    //
    //*****************************************************************************
    #ifdef DEBUG
    void __error__(char *pcFilename, uint32_t ui32Line)
    {
    }
    #endif
    
    void UARTsend(char * str)
    {
    	while (*str) {
    		ROM_UARTCharPut(UART0_BASE, *str++);
    	}
    }
    
    void UART0IntHandler()
    {
    	uint32_t status = ROM_UARTIntStatus(UART0_BASE, true);
    	ROM_UARTIntClear(UART0_BASE, status);
    	while (ROM_UARTCharsAvail(UART0_BASE)) {
    		char c = (char) ROM_UARTCharGetNonBlocking(UART0_BASE);
    		ROM_UARTCharPutNonBlocking(UART0_BASE, c);
    	}
    }
    
    
    
    const uint8_t lookup_hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
    
    void u32tohex(char * out, uint32_t n)
    {
    	out[0] = lookup_hex[n >> 28]; out[1] = lookup_hex[(n >> 24) & 15]; out[2] = lookup_hex[(n >> 20) & 15]; out[3] = lookup_hex[(n >> 16) & 15];
    	out[4] = lookup_hex[(n >> 12) & 15]; out[5] = lookup_hex[(n >> 8) & 15]; out[6] = lookup_hex[(n >> 4) & 15]; out[7] = lookup_hex[n & 15];
    }
    
    void u16tohex(char * out, uint32_t n)
    {
    	out[0] = lookup_hex[(n >> 12) & 15]; out[1] = lookup_hex[(n >> 8) & 15]; out[2] = lookup_hex[(n >> 4) & 15]; out[3] = lookup_hex[n & 15];
    }
    
    
    
    
    #define TXDESC_SIZE (4)
    #define RXDESC_SIZE (4)
    #define EMAC_BUF_SIZE (1536)
    #define EMAC_INSIDE_BUF_OFS (14)
    typedef uint16_t adc_sample_t;
    #define ADC_SAMPLE_BUF_SIZE (1024/sizeof(adc_sample_t))
    
    tEMACDMADescriptor emacDescRx[TXDESC_SIZE] __attribute__(( aligned(4) ));
    tEMACDMADescriptor emacDescTx[RXDESC_SIZE] __attribute__(( aligned(4) ));
    uint8_t emacBufTx[EMAC_BUF_SIZE * TXDESC_SIZE] __attribute__(( aligned(4) ));
    
    uint32_t txdesc_index = 0, rxdesc_index = 0;
    uint32_t tx_adc_index = 0;
    
    // feed buffer pointers to the ADC uDMA descriptors in order
    //
    // the ADC data is transferred by DMA directly into the EMAC buffers
    // when the ADC interrupt fires, it calls tx_EMAC() to have the EMAC immediately send the data out
    //
    // get_ADC_buf() uses tx_adc_index which stores the position of the ADC uDMA descriptor
    uint8_t * get_ADC_buf()
    {
    	uint32_t i = tx_adc_index;
    	tx_adc_index = (i + 1) & (TXDESC_SIZE - 1);
    	return &emacBufTx[i * EMAC_BUF_SIZE + EMAC_INSIDE_BUF_OFS];
    }
    
    void EthernetIntHandler()
    {
    	// NOTE: the ethernet interrupt is never enabled since the TX completion interrupt is not needed and currently no packets are received
    	// the TX completion interrupt might be useful if the CPU were limited only by how fast the packets could be sent
    
    	uint32_t status = ROM_EMACIntStatus(EMAC0_BASE, true);
    	ROM_EMACIntClear(EMAC0_BASE, status);
    
    	// this 'e' should never appear on the serial console
    	ROM_UARTCharPutNonBlocking(UART0_BASE, 'e');
    }
    
    void init_EMAC(uint32_t sysclock)
    {
    	uint32_t user[2];
    	ROM_FlashUserGet(&user[0], &user[1]);
    
    	char macstr[64], * p = macstr;
    	// low 24 bits of each uint32_t have mac address in big endian order (network byte order)
    	unsigned v = ((user[0] & 0xff) << 8) | ((user[0] >> 8) & 0xff);
    	u16tohex(p, v); p += 4; *(p++) = ':';
    	v = ((user[0] >> 8) & 0xff00) | (user[1] & 0xff);
    	u16tohex(p, v); p += 4; *(p++) = ':';
    	v = ((user[1] >> 16) & 0xff) | (user[1] & 0xff00);
    	u16tohex(p, v); p += 4;
    	UARTsend(macstr);
    
    	// create an ethernet header in emacBufTx[0], then copy it to all buffers
    	emacBufTx[ 0] = 0xff;	// destination mac address: broadcast address (ff:ff:ff:ff:ff:ff)
    	emacBufTx[ 1] = 0xff;	// feel free to change this to suit your specific needs
    	emacBufTx[ 2] = 0xff;
    	emacBufTx[ 3] = 0xff;
    	emacBufTx[ 4] = 0xff;
    	emacBufTx[ 5] = 0xff;
    
    	emacBufTx[ 6] = (uint8_t) user[0];	// source mac address: this device
    	emacBufTx[ 7] = (uint8_t) (user[0] >> 8);
    	emacBufTx[ 8] = (uint8_t) (user[0] >> 16);
    	emacBufTx[ 9] = (uint8_t) user[1];
    	emacBufTx[10] = (uint8_t) (user[1] >> 8);
    	emacBufTx[11] = (uint8_t) (user[1] >> 16);
    
    	// EtherType field http://en.wikipedia.org/wiki/EtherType
    	// choose a type such that nobody using this will have conflicting uses of the EtherType
    	// this is always dangerous, so if your network responds to Veritas Low Latency Transport (LLT) for Veritas Cluster Server, choose a different type
    	emacBufTx[12] = 0xca;
    	emacBufTx[13] = 0xfe;
    
    	// EMAC_INSIDE_BUF_OFS points here in emacBufTx[]
    
    
    	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_EMAC0);
    	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_EPHY0);
    	ROM_SysCtlPeripheralReset(SYSCTL_PERIPH_EMAC0);
    	ROM_SysCtlPeripheralReset(SYSCTL_PERIPH_EPHY0);
    
    	while (!ROM_SysCtlPeripheralReady(SYSCTL_PERIPH_EMAC0)) ;
    
    	ROM_EMACPHYConfigSet(EMAC0_BASE, EMAC_PHY_TYPE_INTERNAL | EMAC_PHY_INT_MDIX_EN | EMAC_PHY_AN_100B_T_FULL_DUPLEX);
    	ROM_EMACReset(EMAC0_BASE);
    	ROM_EMACInit(EMAC0_BASE, sysclock, EMAC_BCONFIG_MIXED_BURST | EMAC_BCONFIG_PRIORITY_FIXED, 4 /*RxBurst*/, 4 /*TxBurst*/, 0 /*DescSkipSize*/);
    	ROM_EMACConfigSet(EMAC0_BASE,
    		EMAC_CONFIG_FULL_DUPLEX | EMAC_CONFIG_CHECKSUM_OFFLOAD | EMAC_CONFIG_7BYTE_PREAMBLE | EMAC_CONFIG_IF_GAP_96BITS |
    			EMAC_CONFIG_USE_MACADDR0 | EMAC_CONFIG_SA_FROM_DESCRIPTOR | EMAC_CONFIG_BO_LIMIT_1024,
                    EMAC_MODE_RX_STORE_FORWARD | EMAC_MODE_TX_STORE_FORWARD | EMAC_MODE_TX_THRESHOLD_64_BYTES | EMAC_MODE_RX_THRESHOLD_64_BYTES,
    		0 /*RxMaxFrameSize*/);
    
    	// TODO: change EMAC_CONFIG_SA_FROM_DESCRIPTOR to EMAC_CONFIG_SA_INSERT to reduce DMA utilization
    
    	unsigned i;
    	// fill in TX descriptors
    	for (i = 0; i < TXDESC_SIZE; i++) {
    		emacDescTx[i].ui32Count = DES1_TX_CTRL_SADDR_INSERT | (EMAC_BUF_SIZE << DES1_TX_CTRL_BUFF1_SIZE_S);
    		emacDescTx[i].pvBuffer1 = &emacBufTx[i * EMAC_BUF_SIZE];
    		emacDescTx[i].DES3.pLink = &emacDescTx[(i + 1) & (TXDESC_SIZE - 1)];
    		emacDescTx[i].ui32CtrlStatus = DES0_TX_CTRL_LAST_SEG | DES0_TX_CTRL_FIRST_SEG | DES0_TX_CTRL_INTERRUPT | DES0_TX_CTRL_CHAINED | DES0_TX_CTRL_IP_ALL_CKHSUMS;
    
    		unsigned j;
    		if (i) {
    			for (j = 0; j < EMAC_INSIDE_BUF_OFS; j++) emacBufTx[i * EMAC_BUF_SIZE + j] = emacBufTx[j];
    		}
    	}
    	// fill in RX descriptors
    	for (i = 0; i < RXDESC_SIZE; i++) {
    		emacDescRx[i].ui32CtrlStatus = 0;
    		emacDescRx[i].ui32Count = DES1_RX_CTRL_CHAINED | (EMAC_BUF_SIZE << DES1_RX_CTRL_BUFF1_SIZE_S);
    		emacDescRx[i].pvBuffer1 = &emacBufTx[0];	// FIXME: if any RX is done it needs to point to a different buffer where DMA can write
    		emacDescRx[i].DES3.pLink = &emacDescRx[(i + 1) & (RXDESC_SIZE - 1)];
    	}
    	ROM_EMACRxDMADescriptorListSet(EMAC0_BASE, emacDescRx);
    	ROM_EMACTxDMADescriptorListSet(EMAC0_BASE, emacDescTx);
    	// note: ROM_EMACAddrSet does not need to see a real ethernet header,
    	// it just needs a byte array with the mac address to set up the hardware mac address filter
    	ROM_EMACAddrSet(EMAC0_BASE, 0 /*index*/, &emacBufTx[6]);
    	ROM_EMACFrameFilterSet(EMAC0_BASE, EMAC_FRMFILTER_SADDR | EMAC_FRMFILTER_PASS_MULTICAST | EMAC_FRMFILTER_PASS_NO_CTRL);
    	ROM_EMACIntClear(EMAC0_BASE, ROM_EMACIntStatus(EMAC0_BASE, false));
    	ROM_EMACTxEnable(EMAC0_BASE);
    	//ROM_EMACRxEnable(EMAC0_BASE);	// currently no need to RX anything
    	//ROM_IntEnable(INT_EMAC0);	// currently no need to RX anything
    
    	p = macstr;
    	*(p++) = ' '; *(p++) = 'o'; *(p++) = 'k';
    	*(p++) = '\r'; *(p++) = '\n'; *(p++) = 0;
    	UARTsend(macstr);
    }
    
    // feed ethernet frames to the EMAC
    //
    // tx_EMAC() uses txdesc_index which stores the position of the EMAC DMA descriptor
    void tx_EMAC()
    {
    	uint32_t i = txdesc_index;
    
    	// if the current TX descriptor is still marked OWN by the hardware, it is still in use by the EMAC
    	// this means the ADC interrupt is creating packets too fast for the EMAC
    	// this is considered a fatal error, even though theoretically it could wait for the flag to be cleared
    	// (if the ethernet link is faster than 10 Mbps it should be impossible for the ADC to out pace the EMAC, 1 MSps is 16 Mbps plus overhead)
    	if (emacDescTx[i].ui32CtrlStatus & DES0_TX_CTRL_OWN) {
    		UARTsend("TX_CTRL_OWN: buf overflow\r\n");
    		ROM_IntMasterDisable();
    		for (;;) ;	// spin forever here until a debugger is attached
    	}
    
    	// set up descriptor and turn on DES0_TX_CTRL_OWN
    	emacDescTx[i].ui32Count = EMAC_INSIDE_BUF_OFS + ADC_SAMPLE_BUF_SIZE*sizeof(adc_sample_t);
    	emacDescTx[i].ui32CtrlStatus = DES0_TX_CTRL_LAST_SEG | DES0_TX_CTRL_FIRST_SEG | DES0_TX_CTRL_INTERRUPT | DES0_TX_CTRL_IP_ALL_CKHSUMS |
    		DES0_TX_CTRL_CHAINED | DES0_TX_CTRL_OWN;
    
    	// tell EMAC to re-read emacDescTx
    	ROM_EMACTxDMAPollDemand(EMAC0_BASE);
    
    	i++;
    	i &= TXDESC_SIZE - 1;
    	txdesc_index = i;
    }
    
    
    
    uint32_t adc_complete_count = 0;
    
    // uDMA descriptors used by the hardware are stored in this table
    // ROM_uDMAChannel* functions write to this table
    uint32_t udmaCtrlTable[1024/sizeof(uint32_t)] __attribute__(( aligned(1024) ));
    
    void uDMAErrorHandler()
    {
    	ROM_UARTCharPutNonBlocking(UART0_BASE, 'u');
    }
    
    void ADCprocess(uint32_t ch)
    {
    	if ((((tDMAControlTable *) udmaCtrlTable)[ch].ui32Control & UDMA_CHCTL_XFERMODE_M) != UDMA_MODE_STOP) return;
    
    	// store the next buffer in the uDMA transfer descriptor
    	// the ADC is read directly into the correct emacBufTx to be transmitted
    	ROM_uDMAChannelTransferSet(ch, UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO0), get_ADC_buf(), ADC_SAMPLE_BUF_SIZE);
    
    	adc_complete_count++;
    
    	tx_EMAC();
    }
    
    void ADCseq0Handler()
    {
    	// this interrupt handler is optimized to use as few CPU cycles as possible
    	// it must handle ADC_SAMPLE_BUF_SIZE samples in 1/125000 seconds
    	//
    	// optimizations include:
    	// 1. choosing ADC_SAMPLE_BUF_SIZE to minimize overhead
    	// 2. replacing calls to ROM_* functions with raw memory accesses (less portable)
    	*(uint32_t *) (ADC0_BASE + ADC_O_ISC) = ADC_INT_DMA_SS0;	// optimized form of ROM_ADCIntClearEx(ADC0_BASE, ADC_INT_DMA_SS0);
    
    	ADCprocess(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT);
    	ADCprocess(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT);
    }
    
    void init_ADC(uint32_t sysclock)
    {
    	// set ADC clock to ADC_CLOCK_SRC_PLL (ROM_ADCClockConfigSet() is not defined)
    	// for ADC_TARGET 30 MHz / 1.875 MSps, min sysclock of 120 MHz is required (div = 4) or uDMA cannot keep up with ADC samples
    	// for ADC_TARGET 16 MHz / 1 MSps,  min sysclock of 64 MHz is required (div = 4) note: changes to ADC_TARGET will require adjusting ADC_SAMPLE_BUF_SIZE
    	// for ADC_TARGET 8 MHz / 500 kSps, min sysclock of 48 MHz is required (div = 6)       to get the right balance for the ADC interrupt rate
    	// for ADC_TARGET 4 MHz / 250 kSps, min sysclock of 32 MHz is required (div = 8)
    	// for ADC_TARGET 2 MHz / 125 kSps, min sysclock of 22 MHz is required (div = 11)
    	//#define ADC_TARGET (18*1000*1000)	// 120 MHz/7 = about 17 MHz = 1.07143 MSps
    	#define ADC_TARGET (8*1000*1000)	// 120 MHz/7 = about 17 MHz = 1.07143 MSps
    
    	uint32_t div;
    	if (sysclock < 2*ADC_TARGET) {
    		div = ADC_CLOCK_SRC_PLL;
    	} else {
    		div = ADC_CLOCK_SRC_PLL | (((sysclock + ADC_TARGET - 1) / ADC_TARGET - 1) << 4);
    	}
    	#undef ADC_TARGET
    	*(uint32_t *) (ADC0_BASE + ADC_O_CC) = div;
    
    	ROM_ADCSequenceConfigure(ADC0_BASE, 0 /*SS0*/, ADC_TRIGGER_PROCESSOR, 3 /*priority*/);	// SS0-SS3 priorities must always be different
    	ROM_ADCSequenceConfigure(ADC0_BASE, 3 /*SS3*/, ADC_TRIGGER_PROCESSOR, 0 /*priority*/);	// so change SS3 to prio0 when SS0 gets set to prio3
    	ROM_ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 0, ADC_CTL_TS);	// ADC_CTL_TS = read temp sensor
    	ROM_ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 1, ADC_CTL_TS);
    	ROM_ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 2, ADC_CTL_TS);
    	ROM_ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 3, ADC_CTL_TS);
    	ROM_ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 4, ADC_CTL_TS);
    	ROM_ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 5, ADC_CTL_TS);
    	ROM_ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 6, ADC_CTL_TS);
    	ROM_ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 7, ADC_CTL_TS | ADC_CTL_END | ADC_CTL_IE);	// ADC_CTL_IE fires every 8 samples
    	ROM_ADCSequenceEnable(ADC0_BASE, 0);
    
    	ROM_uDMAEnable();
    	ROM_uDMAControlBaseSet(udmaCtrlTable);
    	ROM_ADCSequenceDMAEnable(ADC0_BASE, 0);
    
    	// disable some bits
    	ROM_uDMAChannelAttributeDisable(UDMA_CHANNEL_ADC0, UDMA_ATTR_ALTSELECT /*start with ping-pong PRI side*/ |
    		UDMA_ATTR_HIGH_PRIORITY /*low priority*/ | UDMA_ATTR_REQMASK /*unmask*/);
    	// enable some bits
    	ROM_uDMAChannelAttributeEnable(UDMA_CHANNEL_ADC0, UDMA_ATTR_USEBURST /*only allow burst transfers*/);
    
    	// set dma params on PRI_ and ALT_SELECT
    	ROM_uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_128);
    	ROM_uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_128);
    	ROM_uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO0), get_ADC_buf(), ADC_SAMPLE_BUF_SIZE);
    	ROM_uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO0), get_ADC_buf(), ADC_SAMPLE_BUF_SIZE);
    	ROM_IntEnable(INT_ADC0SS0);
    
    	ROM_ADCIntEnableEx(ADC0_BASE, ADC_INT_DMA_SS0);
    	ROM_uDMAChannelEnable(UDMA_CHANNEL_ADC0);
    }
    
    
    
    
    int main(void)
    {
    	uint32_t sysclock;
    	do {
    		sysclock = ROM_SysCtlClockFreqSet(SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480, 120*1000*1000);
    	} while (!sysclock);
    
    	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPION);
    	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
    	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
    	ROM_SysCtlDelay(1);
    
    	// configure PORTF for GPIO
    	ROM_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_0 | GPIO_PIN_4);
    	// configure PORTF PIN_0 and PIN_4 as Ethernet link and activity LEDs
    	ROM_GPIOPinConfigure(GPIO_PF0_EN0LED0);
    	ROM_GPIOPinConfigure(GPIO_PF4_EN0LED1);
    	// ROM_GPIOPinTypeEthernetLED(GPIO_PORTF_BASE, GPIO_PIN_0 | GPIO_PIN_4) - not in ROM
    	ROM_GPIODirModeSet(GPIO_PORTF_BASE, GPIO_PIN_0 | GPIO_PIN_4, GPIO_DIR_MODE_HW);
    
    	ROM_GPIOPinTypeGPIOOutput(GPIO_PORTN_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    	ROM_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_0 | GPIO_PIN_1, GPIO_PIN_0);
    
    	ROM_GPIOPinConfigure(GPIO_PA0_U0RX);
    	ROM_GPIOPinConfigure(GPIO_PA1_U0TX);
    	ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    	ROM_UARTConfigSetExpClk(UART0_BASE, sysclock, 115200, UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE);
    
    	ROM_IntMasterEnable();
    	ROM_IntEnable(INT_UART0);
    	ROM_UARTIntEnable(UART0_BASE, UART_INT_RX | UART_INT_RT);
    
    	init_ADC(sysclock);
    	init_EMAC(sysclock);
    
    	// tell PHY what to do with EN0LED0 and EN0LED1
    	// PHY addr: CPU datasheet section 20.5.2.5 says just use 0
    
    	ROM_EMACPHYWrite(EMAC0_BASE, 0 /*PHY addr*/, EPHY_CTL,
    		ROM_EMACPHYRead(EMAC0_BASE, 0 /*PHY addr*/, EPHY_CTL) & ~EPHY_CTL_BYPLEDSTRCH);	// make sure LED stretching is on
    	ROM_EMACPHYWrite(EMAC0_BASE, 0 /*PHY addr*/, EPHY_LEDCR,
    		(ROM_EMACPHYRead(EMAC0_BASE, 0 /*PHY addr*/, EPHY_LEDCR) & ~EPHY_LEDCR_BLINKRATE_M) |
    		EPHY_LEDCR_BLINKRATE_20HZ);
    	ROM_EMACPHYWrite(EMAC0_BASE, 0 /*PHY addr*/, EPHY_LEDCFG,
    		(ROM_EMACPHYRead(EMAC0_BASE, 0 /*PHY addr*/, EPHY_LEDCFG) & ~(EPHY_LEDCFG_LED0_M | EPHY_LEDCFG_LED1_M | EPHY_LEDCFG_LED2_M)) |
    		EPHY_LEDCFG_LED0_LINKTXRX |
    		EPHY_LEDCFG_LED1_100BT |
    		EPHY_LEDCFG_LED2_100BT);
    
    	// set ADC_TRIGGER_ALWAYS to start ADC
    
    	uint8_t have_link = 1;
    	uint32_t count = 0;
    	for (;;) {
    		char str[64], * p;
    		if (!(ROM_EMACPHYRead(EMAC0_BASE, 0, EPHY_BMSR) & EPHY_BMSR_LINKSTAT)) {
    			if (have_link) {
    				p = str;
    				*(p++) = 'l'; *(p++) = 'i'; *(p++) = 'n'; *(p++) = 'k';
    				*(p++) = ' '; *(p++) = 'd'; *(p++) = 'n';
    				*(p++) = '\r'; *(p++) = '\n'; *(p++) = 0;
    				UARTsend(str);
    			}
    			have_link = 0;
    			ROM_ADCSequenceConfigure(ADC0_BASE, 0 /*SS0*/, ADC_TRIGGER_PROCESSOR, 3 /*priority*/);
    			count = 0;
    			continue;
    		}
    		ROM_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_0 | GPIO_PIN_1, GPIO_PIN_1);
    		if (!have_link) {
    				p = str;
    				*(p++) = 'l'; *(p++) = 'i'; *(p++) = 'n'; *(p++) = 'k';
    				*(p++) = ' '; *(p++) = 'u'; *(p++) = 'p';
    				*(p++) = '\r'; *(p++) = '\n'; *(p++) = 0;
    				UARTsend(str);
    			have_link = 1;
    			ROM_ADCSequenceConfigure(ADC0_BASE, 0 /*SS0*/, ADC_TRIGGER_ALWAYS, 3 /*priority*/);
    		}
    
    		count++;
    		ROM_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_0 | GPIO_PIN_1, 0);
    		if (count < 0x7fff) continue;
    		count = 0;
    
    		uint32_t c = adc_complete_count;
    		adc_complete_count = 0;
    		u16tohex(str, c);
    		p = str + 4;
    
    		UARTsend(str);
    	}
    }

  • Hello David

    Can you use the place the function in flash

    GPIOPinTypeEthernetLED

    and remove

    ROM_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_0 | GPIO_PIN_4);

    We have recently tested the same using the above and the LED's work fine.

    Regards

    Amit

  •  ROM_ADCSequenceDMAEnable(ADC0_BASE, 0);

    why i cannot find this function??

    thank you!!!

  • Hello Yuanhang

    Which device are you using?

    Regards
    Amit
  • before 2013 called LM4F120H5QR
    now called tm4c1233h6pm
    i want use ADC uDMA ,but now cannot come in dma interrupt,DMA semms work only once.
  • Hello Yuanhang,

    This API does not exist on TM4C123x device in ROM.

    Regards
    Amit
  • do you have some example of ADC uDMA on TM4C123x device ?
    THANK YOU!!!
  • Hello Yuan,

    I believe on the forum there are some example codes, but nothing yet in TivaWare.

    Regards
    Amit