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.

TM4C123G UART with uDMA

Hello everyone,

I do a basic code to understand how uDMA work but it got me into a problem, i use ping-pong mode to receive data from UART0 RX and after each interrupt it handle data receive and reset uDMA uart even the Tranfer size of buffer B not done yet . So after first successfull receive, it seem the uDMA and UART stop working, i sent another frame to tiva and use debug break point but no thing happend, it not jump in to interrupt part. Can you help me fix it pls?.  Thanks

#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_udma.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_uart.h"
#include "inc/hw_ints.h"
#include "inc/hw_gpio.h"
#include "inc/hw_types.h"
#include "inc/tm4c123gh6pm.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/systick.h"
#include "driverlib/uart.h"
#include "driverlib/interrupt.h"
#include "driverlib/udma.h"
#include "driverlib/timer.h"

#if defined(ewarm)
#pragma data_alignment=1024
uint8_t DMAcontroltable[1024];
#elif defined(ccs)
#pragma DATA_ALIGN(DMAcontroltable, 1024)
uint8_t DMAcontroltable[1024];
#else
uint8_t DMAcontroltable[1024] __attribute__ ((aligned(1024)));
#endif

uint8_t BufferA[100];
uint8_t BufferB[100],myArray[102];

uint8_t CRC[2],receivedone ;
uint32_t ReceiveLength, a, count = 0, count_uDMA_A =0,count_uDMA_B =0;

//=======================================reset UART uDMA=========================================//
void uDMAConfig(){
  /*  tDMAControlTable *psControlTable;
    uint32_t ui32Control;

	HWREG(UDMA_CTLBASE) = (uint32_t)DMAcontroltable;
	psControlTable = (tDMAControlTable *)HWREG(UDMA_CTLBASE);
	ui32Control = (psControlTable[UDMA_CH8_UART0RX].ui32Control)&~UDMA_CHCTL_XFERMODE_M;
	ui32Control |=UDMA_MODE_STOP;
	psControlTable[UDMA_CH8_UART0RX].ui32Control |= ui32Control;
*/
	uDMAChannelDisable(UDMA_CH8_UART0RX);

	uDMAChannelControlSet(UDMA_CH8_UART0RX | UDMA_PRI_SELECT,
	  UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 |
	    UDMA_ARB_1);

	uDMAChannelControlSet(UDMA_CH8_UART0RX | UDMA_ALT_SELECT,
	                              UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 |
	                              UDMA_ARB_1);
	uDMAChannelTransferSet(UDMA_CH8_UART0RX | UDMA_PRI_SELECT,
			UDMA_MODE_PINGPONG,
		(void *)(UART0_BASE + UART_O_DR), BufferA,
	    2);
	uDMAChannelTransferSet(UDMA_CH8_UART0RX | UDMA_ALT_SELECT,
				UDMA_MODE_PINGPONG,
			(void *)(UART0_BASE + UART_O_DR), BufferB,
		    100);

	uDMAChannelEnable(UDMA_CH8_UART0RX);
}

//===================================handle receive data =================================//
void CheckRequest(void)
{
	int i = 0;

	myArray[0] = BufferA[0];
	myArray[1] = BufferA[1];
	for(i = 0;i<100;i++)
	{
		myArray[2+i]=BufferB[i];
	}

	for(i = 0; i<100;i++){BufferB[i]=0;}

	BufferA[0]=0;BufferA[1]=0;

	if(myArray[0]==1)
	{
		CRC_calculation(myArray,ReceiveLength);
		if(CRC[1]==0 & CRC[0]==0)
		{
			FunctionCheck();
		}
	}

}
void FunctionCheck()
{

	switch(myArray[1])
	{
	case 2:
		a = 2;
		UARTCharPut(UART0_BASE, a);

		break;
	case 4:
		a = 4;
		UARTCharPut(UART0_BASE, a);

		break;
	case 15:
		a = 15;
		UARTCharPut(UART0_BASE, a);

		break;
	case 16:
		a = 16;
		UARTCharPut(UART0_BASE, a);

//====================interrupt after buffA full ========================///
void UARTInterrupt(){
	uint32_t ui32Status,ui32Mode;
	ui32Status = UARTIntStatus(UART0_BASE, true); //get interrupt status
	UARTIntClear(UART0_BASE, ui32Status); //clear the asserted interrupts

	//GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_PIN_1); //blink LED
	//SysCtlDelay(SysCtlClockGet() / (1000 * 3)); //delay ~1 msec
	//GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0); //turn off LED

	ui32Mode = uDMAChannelModeGet(UDMA_CH8_UART0RX | UDMA_PRI_SELECT);
	count++;
	if(ui32Mode == UDMA_MODE_STOP){
		count_uDMA_A++;
		/*uDMAChannelTransferSet(UDMA_CH8_UART0RX | UDMA_PRI_SELECT,
			    UDMA_MODE_PINGPONG,
				(void *)(UART0_BASE + UART_O_DR), BufferA,		
			    2);*/
		SysCtlDelay(20);                                       // wait for ALT buffB
		if(BufferA[0] == 1){
			if(BufferA[1] == 4 || BufferA[1] == 2)
			{
				ReceiveLength=8;
				receivedone = 1;
			}
			if(BufferA[1]==16 || BufferA[1] == 15)
			{

				receivedone = 1;ReceiveLength=BufferB[6]+9;

			}
		}
	}
//================================main ====================================================//
void main(void) {
	SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
	GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_2|GPIO_PIN_1|GPIO_PIN_3);

	SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
	GPIOPinConfigure(GPIO_PA0_U0RX);
	GPIOPinConfigure(GPIO_PA1_U0TX);
	GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
	UARTConfigSetExpClk(UART0_BASE, SysCtlClockGet(), 115200,
	(UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE));
	UARTFIFODisable(UART0_BASE);
	//UARTFIFOEnable(UART0_BASE);
	//UARTFIFOLevelSet(UART0_BASE,UART_FIFO_TX4_8,UART_FIFO_RX4_8);

	SysCtlPeripheralClockGating(true);

	//UARTIntRegister(UART0_BASE,&UARTInterrupt);
	IntMasterEnable();
	IntEnable(INT_UART0);
	//UARTIntEnable(UART0_BASE, UART_INT_RX );
	UARTDMAEnable(UART0_BASE, UART_DMA_RX );

	SysCtlPeripheralDisable(SYSCTL_PERIPH_UDMA);
	SysCtlPeripheralReset(SYSCTL_PERIPH_UDMA);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);

	SysCtlDelay(10);

	uDMAEnable();
	uDMAControlBaseSet(DMAcontroltable);

	uDMAChannelAssign(UDMA_CH8_UART0RX);

	uDMAChannelAttributeDisable(UDMA_CH8_UART0RX,
	  UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST |
	    UDMA_ATTR_HIGH_PRIORITY |
	    UDMA_ATTR_REQMASK);

	uDMAChannelControlSet(UDMA_CH8_UART0RX | UDMA_PRI_SELECT,
	  UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 |
	    UDMA_ARB_1);

	uDMAChannelControlSet(UDMA_CHANNEL_UART0RX | UDMA_ALT_SELECT,
	                              UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 |
	                              UDMA_ARB_1);
	uDMAChannelTransferSet(UDMA_CH8_UART0RX | UDMA_PRI_SELECT,
			UDMA_MODE_PINGPONG,
		(void *)(UART0_BASE + UART_O_DR), BufferA,
	    2);
	uDMAChannelTransferSet(UDMA_CH8_UART0RX | UDMA_ALT_SELECT,
				UDMA_MODE_PINGPONG,
			(void *)(UART0_BASE + UART_O_DR), BufferB,
		    100);

	uDMAChannelEnable(UDMA_CH8_UART0RX);

	UARTCharPut(UART0_BASE, 'E');
	UARTCharPut(UART0_BASE, 'n');
	UARTCharPut(UART0_BASE, 't');
	UARTCharPut(UART0_BASE, 'e');
	UARTCharPut(UART0_BASE, 'r');
	UARTCharPut(UART0_BASE, ' ');
	UARTCharPut(UART0_BASE, 'T');
	UARTCharPut(UART0_BASE, 'e');
	UARTCharPut(UART0_BASE, 'x');
	UARTCharPut(UART0_BASE, 't');
	UARTCharPut(UART0_BASE, ':');
	UARTCharPut(UART0_BASE, ' ');

	while (1)
	{
		if(receivedone == 1)
		{

			CheckRequest();
			uDMAConfig();
			receivedone = 0;
		}

  • Hello Nvadugx,

    In Ping Pong mode you do not need to reconfigure the uDMA completely. Only when one of the Ping or Pong transfer gets over, the PRI or ALT control structures need to be reinitialized. If you want to reconfigure for it some reason, then BASIC mode of transfer or AUTO mode of transfer (SW control) is a better option.

    Please check the udma example of TivaWare to see how to correctly use Ping Pong mechanism

    Regards
    Amit
  • Hello Amit,
    i understand how Ping Pong mechanism but is there any way to manual restart the uDMA transfer in Ping Pong mode when transfer not finish yet?. I try to disableUARTuDMA , set STOP mode in control table and enable pingpong mode but it not work.
    Thanks
  • Hello Nvadugx,

    I am curious to understand why would the application try's to manually restart the transfer if it is not finished yet? I can better formulate a solution based on your answer, so please describe it well.

    Regards
    Amit
  • Hello Amit,
    I try to send modbus frame to tiva c, i'm not try the basic mode yet but i think it can be done with it. In pingpong mode, buffer A gonna store id and function code, buffer B store PDU, when interrupt happend i use information from id and function code to handle data in buffer B
  • Hello Nvadugx,

    Then the correct approach would be store the id, function code and PDU in buffer A for the first frame and process it while the next frame goes to buffer B. This way on receiving and processing one frame, the ping or pong buffer which got the frame can be re-initialized while the next frame is going to pong or ping buffer respectively, without having the need to re-init the entire DMA

    Regards
    Amit
  • Hello Amit,
    Each modbus frame have a different length so it may be hard to indicate when a frame is successful stored in buffer A, i think i gonna try basic mode.
    Thanks and best regards
  • nvadugx said:
    Each modbus frame have a different length so it may be hard to indicate when a frame is successfully stored in buffer

    May I suggest that you (really) "Log the results and length" of different frames - so that you have a clear (and heightened) understanding of this process?   My firm has observed that (often) - when clients attempt such testing/observation (minus ANY logging of data) - conclusions are drawn which may be accurate (only) over a limited set of input conditions.

    By designing your test structure so that, "Short, medium & long length" frames are employed your odds of a more robust - general solution - increase...

  • Hello cb1_mobile
    Thanks for the advice, gonna add timer to handle the process, between each frames have a minimum silent time interval to separate them.
  • Thank you - your described "addition" improves - but (really) does not effectively deal - w/the variable length frames you earlier specified.

    New, "Minimum silent time" must be "sufficiently long" to accommodate your "longest" frame.   (i.e. when the "unwinding/processing of the frame takes longest.)   Does not that insure that it will be, "too long" to efficiently handle/process ALL short frames?   (i.e. more time than is truly required is spent "silent/dead.")    

    Should your "timer method" succeed - having that timer "adapt" to frame length (i.e. be linear - based upon frame length) may accomplish your objective of (both) frame robustness & processing efficiency...

  • Hello Nvadugx,

    Looking at the ModBus and the implementation using TM4C uDMA, the basic mode may have a good solution.

    Setup the basic mode to get only the payload upto function. Once the function is available and the size of payload known, reprogram the Control transfer word for the size of payload. In the meantime the UART with FIFO enabled shall provide for enough buffer to get the data while the CPU sets up the second basic mode transfer.

    Regards
    Amit
  • Greetings Amit,

    Outside of my realm (like that's stopped me - past) yet crack staff here note that there are "variations" w/in ModBus - and question whether this may "confound/complicate" use of your "basic mode."

    Recall I'm simple reporter.   (it is - more classically - the "messenger" who is shot.)

  • I'm left wondering from the discussion so far, why DMA?

    Robert
  • Hello cb1

    Looking at the implementation description, it seems to be ModBus ASCII, though you raise a very good point as to what type of ModBus it is!

    Regards
    Amit
  • Hello Amit,

    Yes - and as (some few) here have (once/twice) noted, "Devil lurks in such "Modbus/DMA" details..." (believe Robert qualifies as "messenger.")
  • Gee thanks cb1, I'll go dig out the kevlar.

    Amit, I'm thinking Modbus RTU because of the timing mentions and the function code check in the code. RTU's packet delimiter is time gaps. The ASCII variant is a lot simpler.

    I'm about to implement an RTU master from the looks of things so I am curious as to why DMA was chosen. The Modbus default speed is 19200 and the protocol was developed with much less capable micros in mind so it's hard to see why DMA would be desired.

    Robert
  • @Robert,

    If memory serves - the very best "Kevlar" is restricted from transport - your fruited plain.
    In my desire to NOT rise (at all) beyond "reporter" I may have bit mischaracterized you Sir - 1000 (bullet riddled) pardons - s'il vous plait...

    Now crack staff (the real trouble-makers) agree w/your sense that "ASCII Modbus" - as used on humble machines of the past - should be well suited, here...

  • Hello Robert,

    Modbus RTU has a 28 bit delimiter which is 4.5 bytes but the code does not seem to take the 1/2 byte into account.

    Regards
    Amit
  • Pardon but staff here (seem) to recall a well described/similar Modbus, "solution" done by the firm "Opto 22." This was beyond 10 years past. Should TM4C methods not succeed - perhaps a search (and/or request) from that firm may prove of value...
  • We can certainly get some but I've never been motivated to research.

    The ASCII version is definitely simpler and i have used it before. Its relative simplicity is a form of robustness itself. Unfortunately not all devices support it.

    Robert
  • Also, I believe advantech.

    Robert
  • Advantech - I recalled the "A" - you just "quieted" my struggle.   Iirc - they were among the leaders - and your comment, "Simplicity is a form of robustness" warrants inclusion in our, "Robert's Bookshelf/other pearls."

    Staff here scrambling (now) to find fresh stone & carving bones (tools.)   May take awhile as they employ ASM & DRM ... ... (not to ask what they use for "eraser.")

  • I didn't delve that deeply, good catch Amit.

    In the worst case this wouldn't be found until many units were in the field.

    The other obvious question is what happens if the number of bytes received does not match the expected? Especially if there are too few.

    Robert
  • Hello Robert,

    That is my understanding of the ModBus type being used based on the forum post code. The thread's original poster can only clarify.

    The only way that can be prevented is by using a timer when the first byte reception starts such that it is above the max packet size on bus, so that some form of timeout can be detected.

    Regards
    Amit
  • Hello cb1_mobile

    What i mean is when receive a byte i gonna start a timer, if next byte arrive in time it gonna reset timerload value and start it again, so it keep on going until the last byte, timer run out and it jump in interrupt indicate that a frame is received and process data. In my c# GUI have a minimum time (enough for tiva c receive and process data) between each frames send by master.
    Thanks for the advice gonna test it
  • Hello Nvadugx,

    Could you as well answer the ever lingering question(s)

    1. Which ModBus is it?
    2. Why DMA?

    Regards
    Amit
  • Hello Amit,

    it is ModBus RTU on rs485
    i already done it with uart module, just want to test it with uDMA( it faster ?)
  • Hello Nvadugx,

    What about the 28 bit time start of frame. Your original code post does not seem to take that into account?

    Regards
    Amit
  • Hello Amit,
    Yes the code in this post i don't have timer which i gonna add in next time
  • Hello Nvadugx

    The use of DMA may not be very fast as compared to the CPU, since the incoming data rate is low and the CPU processing of the data has still to be done.

    Regards
    Amit
  • As Amit pointed out you are limited by the speed at which the bytes arrive so DMA isn't of help on speed. Consider also that you need to add extra support for
    - determining the DMA transfer characteristics
    - detecting too long packets
    - detecting short packets

    This in addition to what you need to do for the polling or interrupt case. The last item in particular I would expect to add a fair amount of complexity. You may not even have less overhead than the interrupt solution.

    Also consider do you have a demonstrated problem with overhead now? Keep in mind the proverb "Premature optimization is the root of all evil"1. Fast and complex is not necessarily better than slow and simple.

    Robert

    1 - Attributed to C.A.R. Hoare. Or if you prefer Aesop remember the Tortoise and the Hare.