1 /*
2  * @brief UART DMA example
3  *
4  * @note
5  * Copyright(C) NXP Semiconductors, 2013
6  * All rights reserved.
7  *
8  * @par
9  * Software that is described herein is for illustrative purposes only
10  * which provides customers with programming information regarding the
11  * LPC products.  This software is supplied "AS IS" without any warranties of
12  * any kind, and NXP Semiconductors and its licensor disclaim any and
13  * all warranties, express or implied, including all implied warranties of
14  * merchantability, fitness for a particular purpose and non-infringement of
15  * intellectual property rights.  NXP Semiconductors assumes no responsibility
16  * or liability for the use of the software, conveys no license or rights under any
17  * patent, copyright, mask work right, or any other intellectual property rights in
18  * or to any products. NXP Semiconductors reserves the right to make changes
19  * in the software without notification. NXP Semiconductors also makes no
20  * representation or warranty that such application will be suitable for the
21  * specified use without further testing or modification.
22  *
23  * @par
24  * Permission to use, copy, modify, and distribute this software and its
25  * documentation is hereby granted, under NXP Semiconductors' and its
26  * licensor's relevant copyrights in the software, without fee, provided that it
27  * is used in conjunction with NXP Semiconductors microcontrollers.  This
28  * copyright, permission, and disclaimer notice must appear in all copies of
29  * this code.
30  */
31 
32 #include "board.h"
33 #include "string.h"
34 
35 /*****************************************************************************
36  * Private types/enumerations/variables
37  ****************************************************************************/
38 
39 #define USE_INTEGER_CLOCK
40 
41 /* DMA send string arrays. DMA buffers must remain in memory during the DMA
42    transfer. */
43 #define DMASENDSTRCNT   6
44 static char dmaSendStr[DMASENDSTRCNT][32];
45 
46 /* Number of UART TX descriptors used for DMA */
47 #define UARTTXDESC 8
48 
49 /* Next available UART TX DMA descriptor and use counter */
50 static volatile int nextTXDesc, countTXDescUsed;
51 
52 /* Number of UART RX descriptors used for DMA */
53 #define UARTRXDESC 4
54 
55 /* Maximum size of each UART RX receive buffer */
56 #define UARTRXBUFFSIZE  8
57 
58 /* UART RX receive buffers */
59 static uint8_t dmaRXBuffs[UARTRXDESC][UARTRXBUFFSIZE];
60 
61 /* UART receive buffer that is available and availa flag */
62 static volatile int uartRXBuff;
63 static volatile bool uartRxAvail;
64 
65 /* DMA descriptors must be aligned to 16 bytes */
66 #if defined(__CC_ARM)
67 __align(16) static DMA_CHDESC_T dmaTXDesc[UARTTXDESC];
68 __align(16) static DMA_CHDESC_T dmaRXDesc[UARTRXDESC];
69 #endif /* defined (__CC_ARM) */
70 
71 /* IAR support */
72 #if defined(__ICCARM__)
73 #pragma data_alignment=16
74 static DMA_CHDESC_T dmaTXDesc[UARTTXDESC];
75 #pragma data_alignment=16
76 static DMA_CHDESC_T dmaRXDesc[UARTRXDESC];
77 #endif /* defined (__ICCARM__) */
78 
79 #if defined( __GNUC__ )
80 static DMA_CHDESC_T dmaTXDesc[UARTTXDESC] __attribute__ ((aligned(16)));
81 static DMA_CHDESC_T dmaRXDesc[UARTRXDESC] __attribute__ ((aligned(16)));
82 #endif /* defined (__GNUC__) */
83 
84 /*****************************************************************************
85  * Public types/enumerations/variables
86  ****************************************************************************/
87 
88 /*****************************************************************************
89  * Private functions
90  ****************************************************************************/
91 
Init_UART_PinMux(void)92 static void Init_UART_PinMux(void)
93 {
94 #if defined(BOARD_NXP_LPCXPRESSO_1549)
95 	Chip_IOCON_PinMuxSet(LPC_IOCON, 0, 13, (IOCON_MODE_INACT | IOCON_DIGMODE_EN));
96 	Chip_IOCON_PinMuxSet(LPC_IOCON, 0, 18, (IOCON_MODE_INACT | IOCON_DIGMODE_EN));
97 
98 	/* UART signal muxing via SWM */
99 	Chip_SWM_MovablePortPinAssign(SWM_UART0_RXD_I, 0, 13);
100 	Chip_SWM_MovablePortPinAssign(SWM_UART0_TXD_O, 0, 18);
101 
102 #else
103 #error "No UART setup defined"
104 #endif
105 }
106 
107 /* Setup DMA UART TX support, but do not queue descriptors yet */
dmaTXSetup(void)108 static void dmaTXSetup(void)
109 {
110 	/* Setup UART 0 TX channel for the following configuration:
111 	   - Peripheral DMA request (UART 0 TX channel)
112 	   - Single transfer
113 	   - Low channel priority */
114 	Chip_DMA_EnableChannel(LPC_DMA, DMAREQ_USART0_TX);
115 	Chip_DMA_EnableIntChannel(LPC_DMA, DMAREQ_USART0_TX);
116 	Chip_DMA_SetupChannelConfig(LPC_DMA, DMAREQ_USART0_TX,
117 								(DMA_CFG_PERIPHREQEN | DMA_CFG_TRIGBURST_SNGL | DMA_CFG_CHPRIORITY(3)));
118 
119 	countTXDescUsed = 0;
120 }
121 
122 /* Setup DMA UART RX support, but do not queue descriptors yet */
dmaRXSetup(void)123 static void dmaRXSetup(void)
124 {
125 	/* Setup UART 0 RX channel for the following configuration:
126 	   - Peripheral DMA request (UART 0 RX channel)
127 	   - Single transfer
128 	   - Low channel priority */
129 	Chip_DMA_EnableChannel(LPC_DMA, DMAREQ_USART0_RX);
130 	Chip_DMA_EnableIntChannel(LPC_DMA, DMAREQ_USART0_RX);
131 	Chip_DMA_SetupChannelConfig(LPC_DMA, DMAREQ_USART0_RX,
132 								(DMA_CFG_PERIPHREQEN | DMA_CFG_TRIGBURST_SNGL | DMA_CFG_CHPRIORITY(3)));
133 }
134 
135 /* Send data via the UART */
dmaTXSend(uint8_t * data,int bytes)136 static bool dmaTXSend(uint8_t *data, int bytes)
137 {
138 	/* Disable the DMA IRQ to prevent race conditions with shared data */
139 	NVIC_DisableIRQ(DMA_IRQn);
140 
141 	/* This is a limited example, limit descriptor and byte count */
142 	if ((countTXDescUsed >= UARTTXDESC) || (bytes > 1024)) {
143 		/* Re-enable the DMA IRQ */
144 		NVIC_EnableIRQ(DMA_IRQn);
145 
146 		/* All DMA descriptors are used, so just exit */
147 		return false;
148 	}
149 	else if (countTXDescUsed == 0) {
150 		/* No descriptors are currently used, so take the first one */
151 		nextTXDesc = 0;
152 	}
153 
154 	/* Create a descriptor for the data */
155 	dmaTXDesc[countTXDescUsed].source = DMA_ADDR(data + bytes - 1);	/* Last address here */
156 	dmaTXDesc[countTXDescUsed].dest = DMA_ADDR(&LPC_USART0->TXDATA);	/* Byte aligned */
157 
158 	/* If there are multiple buffers with non-contiguous addresses, they can be chained
159 	   together here (it is recommended to only use the DMA_XFERCFG_SETINTA on the
160 	   last chained descriptor). If another TX buffer needs to be sent, the DMA
161 	   IRQ handler will re-queue and send the buffer there without using chaining. */
162 	dmaTXDesc[countTXDescUsed].next = DMA_ADDR(0);
163 
164 	/* Setup transfer configuration */
165 	dmaTXDesc[countTXDescUsed].xfercfg = DMA_XFERCFG_CFGVALID | DMA_XFERCFG_SETINTA |
166 										 DMA_XFERCFG_SWTRIG | DMA_XFERCFG_WIDTH_8 | DMA_XFERCFG_SRCINC_1 |
167 										 DMA_XFERCFG_DSTINC_0 | DMA_XFERCFG_XFERCOUNT(bytes);
168 
169 	/* If a transfer is currently in progress, then stop here and let the DMA
170 	   handler re-queue the next transfer. Otherwise, start the transfer here. */
171 	if (countTXDescUsed == 0) {
172 		/* Setup transfer descriptor and validate it */
173 		Chip_DMA_SetupTranChannel(LPC_DMA, DMAREQ_USART0_TX, &dmaTXDesc[countTXDescUsed]);
174 
175 		/* Setup data transfer */
176 		Chip_DMA_SetupChannelTransfer(LPC_DMA, DMAREQ_USART0_TX,
177 									  dmaTXDesc[countTXDescUsed].xfercfg);
178 
179 		Chip_DMA_SetValidChannel(LPC_DMA, DMAREQ_USART0_TX);
180 	}
181 
182 	/* Update used descriptor count */
183 	countTXDescUsed++;
184 
185 	/* Re-enable the DMA IRQ */
186 	NVIC_EnableIRQ(DMA_IRQn);
187 
188 	return true;
189 }
190 
191 /* Queue up DMA descriptors and buffers for UART RX */
dmaRXQueue(void)192 static void dmaRXQueue(void)
193 {
194 	int i;
195 
196 	/* Linked list of descriptors that map to the 3 receive buffers */
197 	for (i = 0; i < UARTRXDESC; i++) {
198 		/* Setup next descriptor */
199 		if (i == (UARTRXDESC - 1)) {
200 			/* Wrap descriptors */
201 			dmaRXDesc[i].next = DMA_ADDR(&dmaRXDesc[0]);
202 		}
203 		else {
204 			dmaRXDesc[i].next = DMA_ADDR(&dmaRXDesc[i + 1]);
205 		}
206 
207 		/* Create a descriptor for the data */
208 		dmaRXDesc[i].source = DMA_ADDR(&LPC_USART0->RXDATA) + 0;	/* Byte aligned */
209 		dmaRXDesc[i].dest = DMA_ADDR(&dmaRXBuffs[i][0] + UARTRXBUFFSIZE - 1);
210 
211 		/* Setup transfer configuration */
212 		dmaRXDesc[i].xfercfg = DMA_XFERCFG_CFGVALID | DMA_XFERCFG_SETINTA |
213 							   DMA_XFERCFG_WIDTH_8 | DMA_XFERCFG_SRCINC_0 |
214 							   DMA_XFERCFG_DSTINC_1 | DMA_XFERCFG_RELOAD |
215 							   DMA_XFERCFG_XFERCOUNT(UARTRXBUFFSIZE);
216 	}
217 
218 	/* Setup transfer descriptor and validate it */
219 	Chip_DMA_SetupTranChannel(LPC_DMA, DMAREQ_USART0_RX, &dmaRXDesc[0]);
220 
221 	/* Setup data transfer */
222 	Chip_DMA_SetupChannelTransfer(LPC_DMA, DMAREQ_USART0_RX,
223 								  dmaRXDesc[0].xfercfg);
224 	Chip_DMA_SetValidChannel(LPC_DMA, DMAREQ_USART0_RX);
225 	Chip_DMA_SWTriggerChannel(LPC_DMA, DMAREQ_USART0_RX);
226 }
227 
228 /* Check and return UART RX data if it exists */
checkRxData(uint8_t * buff)229 static int checkRxData(uint8_t *buff)
230 {
231 	int bytesRec = 0;
232 
233 	if (uartRxAvail) {
234 		uartRxAvail = false;
235 
236 		memcpy(buff, dmaRXBuffs[uartRXBuff], UARTRXBUFFSIZE);
237 		uartRXBuff++;
238 		if (uartRXBuff >= UARTRXDESC) {
239 			uartRXBuff = 0;
240 		}
241 		bytesRec = UARTRXBUFFSIZE;
242 	}
243 
244 	return bytesRec;
245 }
246 
247 /* Clear an error on a DMA channel */
dmaClearChannel(DMA_CHID_T ch)248 static void dmaClearChannel(DMA_CHID_T ch) {
249 	Chip_DMA_DisableChannel(LPC_DMA, ch);
250 	while ((Chip_DMA_GetBusyChannels(LPC_DMA) & (1 << ch)) != 0) {}
251 
252 	Chip_DMA_AbortChannel(LPC_DMA, ch);
253 	Chip_DMA_ClearErrorIntChannel(LPC_DMA, ch);
254 }
255 
256 /*****************************************************************************
257  * Public functions
258  ****************************************************************************/
259 
260 /**
261  * @brief	DMA Interrupt Handler
262  * @return	None
263  */
DMA_IRQHandler(void)264 void DMA_IRQHandler(void)
265 {
266 	uint32_t errors, pending;
267 
268 	/* Get DMA error and interrupt channels */
269 	errors = Chip_DMA_GetErrorIntChannels(LPC_DMA);
270 	pending = Chip_DMA_GetActiveIntAChannels(LPC_DMA);
271 
272 	/* Check DMA interrupts of UART 0 TX channel */
273 	if ((errors | pending) & (1 << DMAREQ_USART0_TX)) {
274 		/* Clear DMA interrupt for the channel */
275 		Chip_DMA_ClearActiveIntAChannel(LPC_DMA, DMAREQ_USART0_TX);
276 
277 		/* Handle errors if needed */
278 		if (errors & (1 << DMAREQ_USART0_TX)) {
279 			/* DMA error, channel needs to be reset */
280 			dmaClearChannel(DMAREQ_USART0_TX);
281 			dmaTXSetup();
282 		}
283 		else {
284 			/* Descriptor is consumed */
285 			countTXDescUsed--;
286 		}
287 
288 		/* Is another DMA descriptor waiting that was not chained? */
289 		if (countTXDescUsed > 0) {
290 			nextTXDesc++;
291 
292 			/* Setup transfer descriptor and validate it */
293 			Chip_DMA_SetupTranChannel(LPC_DMA, DMAREQ_USART0_TX, &dmaTXDesc[nextTXDesc]);
294 
295 			/* Setup data transfer */
296 			Chip_DMA_SetupChannelTransfer(LPC_DMA, DMAREQ_USART0_TX,
297 										  dmaTXDesc[nextTXDesc].xfercfg);
298 
299 			Chip_DMA_SetValidChannel(LPC_DMA, DMAREQ_USART0_TX);
300 		}
301 	}
302 
303 	/* Check DMA interrupts of UART 0 RX channel */
304 	if ((errors | pending) & (1 << DMAREQ_USART0_RX)) {
305 		/* Clear DMA interrupt for the channel */
306 		Chip_DMA_ClearActiveIntAChannel(LPC_DMA, DMAREQ_USART0_RX);
307 
308 		/* Handle errors if needed */
309 		if (errors & (1 << DMAREQ_USART0_RX)) {
310 			/* DMA error, channel needs to be reset */
311 			dmaClearChannel(DMAREQ_USART0_RX);
312 			dmaRXSetup();
313 			dmaRXQueue();
314 		}
315 		else {
316 			uartRxAvail = true;
317 		}
318 	}
319 }
320 
321 /**
322  * @brief	Main UART/DMA program body
323  * @return	Does not exit
324  */
main(void)325 int main(void)
326 {
327 	int bytes, idx;
328 	uint8_t buff[UARTRXBUFFSIZE];
329 
330 	SystemCoreClockUpdate();
331 	Board_Init();
332 	Init_UART_PinMux();
333 	Board_LED_Set(0, false);
334 
335 #if defined(USE_INTEGER_CLOCK)
336 	/* Use main clock rate as base for UART baud rate divider */
337 	Chip_Clock_SetUARTBaseClockRate(Chip_Clock_GetMainClockRate(), false);
338 
339 #else
340 	/* Use 128x expected UART baud rate for fractional baud mode. */
341 	Chip_Clock_SetUARTBaseClockRate((115200 * 128), true);
342 #endif
343 	/* Setup UART */
344 	Chip_UART_Init(LPC_USART0);
345 	Chip_UART_ConfigData(LPC_USART0, UART_CFG_DATALEN_8 | UART_CFG_PARITY_NONE | UART_CFG_STOPLEN_1);
346 	Chip_UART_SetBaud(LPC_USART0, 115200);
347 	/* Optional for low clock rates only: Chip_UART_SetBaudWithRTC32K(LPC_USART, 300); */
348 	Chip_UART_Enable(LPC_USART0);
349 	Chip_UART_TXEnable(LPC_USART0);
350 
351 	/* DMA initialization - enable DMA clocking and reset DMA if needed */
352 	Chip_DMA_Init(LPC_DMA);
353 
354 	/* Enable DMA controller and use driver provided DMA table for current descriptors */
355 	Chip_DMA_Enable(LPC_DMA);
356 	Chip_DMA_SetSRAMBase(LPC_DMA, DMA_ADDR(Chip_DMA_Table));
357 
358 	/* Setup UART 0 TX DMA support */
359 	dmaTXSetup();
360 
361 	/* Setup UART 0 RX DMA support */
362 	dmaRXSetup();
363 
364 	/* Enable the DMA IRQ */
365 	NVIC_EnableIRQ(DMA_IRQn);
366 
367 	/* Enqueue a bunch of strings in DMA transmit descriptors and start
368 	   transmit. In this use of DMA, the descriptors aren't chained, so
369 	     the DMA restarts the next queued descriptor in the DMA interrupt
370 	     handler. */
371 	for (idx = 0; idx < DMASENDSTRCNT; idx++) {
372 		sprintf(dmaSendStr[idx], "DMA send string (unlinked) #%d\r\n", idx);
373 		dmaTXSend((uint8_t *) dmaSendStr[idx], strlen(dmaSendStr[idx]));
374 	}
375 
376 	/* Wait for UART TX DMA channel to go inactive */
377 	while (1) {
378 		__WFI();
379 		if (Chip_DMA_GetActiveChannels(LPC_DMA) & (1 << DMAREQ_USART0_TX)) {
380 			break;
381 		}
382 	}
383 
384 	/* Receive buffers are queued. The DMA interrupt will only trigger on a
385 	   full DMA buffer receive, so if the UART is idle, but the DMA is only
386 	   partially complete, the DMA interrupt won't fire. For UART data
387 	   receive where data is not continuous, a timeout method will be
388 	   required to flush the DMA when the DMA has pending data and no
389 	   data has been received on the UART in a specified timeout */
390 	dmaRXQueue();
391 
392 	/* Get RX data via DMA and send it out on TX via DMA */
393 	while (1) {
394 		/* Sleep until something happens */
395 		__WFI();
396 
397 		/* Did any data come in? */
398 		bytes = checkRxData(buff);
399 		if (bytes > 0) {
400 			/* RX data received, send it via TX DMA */
401 			dmaTXSend(buff, bytes);
402 		}
403 	}
404 
405 	return 1;
406 }
407