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