]> Pileus Git - ~andy/csm213a-hw/blobdiff - yue/serial_dma.c
Update main file add DMA code
[~andy/csm213a-hw] / yue / serial_dma.c
diff --git a/yue/serial_dma.c b/yue/serial_dma.c
new file mode 100644 (file)
index 0000000..9bca3e9
--- /dev/null
@@ -0,0 +1,118 @@
+#include <MKL46Z4.h>\r
+\r
+#include <stdint.h>\r
+#include <stdarg.h>\r
+#include <stdio.h>\r
+#include <string.h>\r
+\r
+#include "serial_dma.h"\r
+\r
+/* Defines */\r
+#define UART sdma_uart\r
+#define DMA  (&(DMA0->DMA[sdma_chan]))\r
+#define MUX  ((dma_mux_t*)&(DMAMUX0->CHCFG[sdma_chan]))\r
+\r
+#define NUM  2\r
+#define LEN  1024\r
+\r
+/* Types */\r
+typedef struct {uint8_t CHCFG;} dma_mux_t;\r
+\r
+/* Setup data */\r
+static UART0_Type *sdma_uart;\r
+static int         sdma_chan;\r
+\r
+/* Error logging */\r
+static int         sdma_stuck;\r
+static int         sdma_full;\r
+\r
+/* Data buffering */\r
+static int         sdma_index;\r
+static int         sdma_length[NUM];\r
+static uint8_t     sdma_queue[NUM][LEN];\r
+\r
+/* DMA Functions */\r
+void sdma_setup(UART0_Type *uart, int channel)\r
+{\r
+       // Save configuration data\r
+       sdma_uart = uart;\r
+       sdma_chan = channel;\r
+\r
+       // Enable DMA Cock\r
+       SIM->SCGC6   |= SIM_SCGC6_DMAMUX_MASK;\r
+       SIM->SCGC7   |= SIM_SCGC7_DMA_MASK;\r
+\r
+       // Reset channel\r
+       DMA->DSR_BCR  = DMA_DSR_BCR_DONE_MASK;\r
+\r
+       // Configure DMA transfer\r
+       DMA->DAR      = (uint32_t)&UART0->D;\r
+       DMA->DCR      = DMA_DCR_CS_MASK   |\r
+                       DMA_DCR_SINC_MASK |\r
+                       DMA_DCR_SSIZE(1)  |\r
+                       DMA_DCR_DSIZE(1)  |\r
+                       DMA_DCR_D_REQ_MASK;\r
+\r
+       // Configure DMA Mux\r
+       MUX->CHCFG    = DMAMUX_CHCFG_SOURCE(3) |\r
+                       DMAMUX_CHCFG_ENBL_MASK;\r
+\r
+       // Configure UART for DMA Channel 0\r
+       UART->C5     |= UART0_C5_TDMAE_MASK;\r
+}\r
+\r
+/* Write binary data out the DMA output queue */\r
+void sdma_write(void *data, int len)\r
+{\r
+       if (sdma_length[sdma_index] + len > LEN) {\r
+               sdma_full++;\r
+       } else {\r
+               int   pos = sdma_length[sdma_index];\r
+               void *dst = &sdma_queue[sdma_index][pos];\r
+               memcpy(dst, data, len);\r
+               sdma_length[sdma_index] += len;\r
+       }\r
+}\r
+\r
+/* Write ASCII data to the output queue */\r
+void sdma_vprintf(const char *fmt, va_list ap)\r
+{\r
+       int   pos = sdma_length[sdma_index];\r
+       void *dst = &sdma_queue[sdma_index][pos];\r
+       sdma_length[sdma_index] +=\r
+               vsnprintf((char*)dst, LEN-pos, fmt, ap);\r
+}\r
+\r
+void sdma_printf(const char *fmt, ...)\r
+{\r
+       va_list ap;\r
+       va_start(ap, fmt);\r
+       sdma_vprintf(fmt, ap);\r
+       va_end(ap);\r
+}\r
+\r
+/* Trigger DMA transmit of the current output queue\r
+ * and swap buffers so we can write into unused space */\r
+void sdma_flush(void)\r
+{\r
+       if (sdma_length[sdma_index] == 0)\r
+               return;\r
+\r
+       // Wait for transmit complete\r
+       while (DMA->DSR_BCR & DMA_DSR_BCR_BCR_MASK)\r
+               sdma_stuck++;\r
+\r
+       // Reset channel\r
+       DMA->DSR_BCR  = DMA_DSR_BCR_DONE_MASK;\r
+\r
+       // Set source address and length\r
+       DMA->SAR      = (uint32_t)&sdma_queue[sdma_index];\r
+       DMA->DSR_BCR  = DMA_DSR_BCR_BCR(sdma_length[sdma_index]);\r
+\r
+       // Enable DMA transmit\r
+       DMA->DCR     |= DMA_DCR_ERQ_MASK;\r
+\r
+       // Swap buffers\r
+       sdma_length[sdma_index] = 0;\r
+       sdma_index = (sdma_index + 1) % NUM;\r
+}\r