]> Pileus Git - ~andy/csm213a-hw/blob - hw2/timer_dma.c
Fix rising/falling edge capture
[~andy/csm213a-hw] / hw2 / timer_dma.c
1 #include <MKL46Z4.h>\r
2 \r
3 #include <stdint.h>\r
4 #include <stdarg.h>\r
5 #include <stdio.h>\r
6 #include <string.h>\r
7 \r
8 #include "timer_dma.h"\r
9 #include "serial_irq.h"\r
10 \r
11 /* Defines */\r
12 enum {\r
13         TDMA_REQ_PTA  = 49,\r
14         TDMA_REQ_PTC  = 51,\r
15         TDMA_REQ_PTD  = 52,\r
16 };\r
17 \r
18 /* Port structure */\r
19 struct tdma_t {\r
20         /* DMA channel */\r
21         struct {\r
22                 uint32_t sar; // offset 0x00, Source Address Register\r
23                 uint32_t dar; // offset 0x04, Destination Address Register\r
24                 uint32_t dsr; // offset 0x08, DMA Status Register / Byte Count Register\r
25                 uint32_t dcr; // offset 0x0C, DMA Control Register\r
26         } *dma;\r
27 \r
28         /* DMA mux */\r
29         struct {\r
30                 uint8_t  cfg; // offset 0x00, Channel Configuration register\r
31         } *mux;\r
32 \r
33         /* Pin names */\r
34         struct {\r
35                 uint32_t pcr; // offset 0x00, Pin Control register\r
36         } *pin;\r
37 \r
38         /* Time stamping */\r
39         uint32_t time[2];\r
40 \r
41         /* Save state */\r
42         int      req;\r
43         int      irqc;\r
44 };\r
45 \r
46 /* Port data */\r
47 static tdma_t tdma_ports[TDMA_NUM_CHAN];\r
48 \r
49 /* Global timer initialization */\r
50 void tdma_init(void)\r
51 {\r
52         static int tdma_init_done = 0;\r
53         if (tdma_init_done)\r
54                 return;\r
55 \r
56         // Enable DMA Cock\r
57         SIM->SCGC5 |= SIM_SCGC5_PORTA_MASK;\r
58         SIM->SCGC5 |= SIM_SCGC5_PORTC_MASK;\r
59         SIM->SCGC5 |= SIM_SCGC5_PORTD_MASK;\r
60         SIM->SCGC6 |= SIM_SCGC6_DMAMUX_MASK;\r
61         SIM->SCGC7 |= SIM_SCGC7_DMA_MASK;\r
62 \r
63         // Enable timer Clock\r
64         SIM->SCGC6 |= SIM_SCGC6_PIT_MASK;\r
65 \r
66         // Enable PIT\r
67         PIT->MCR               = 0;\r
68 \r
69         // Channel 0\r
70         PIT->CHANNEL[0].LDVAL  = 0xFFFFFFFF;\r
71         PIT->CHANNEL[0].TCTRL  = 0;\r
72 \r
73         // Channel 1\r
74         PIT->CHANNEL[1].LDVAL  = 0xFFFFFFFF;\r
75         PIT->CHANNEL[1].TCTRL  = PIT_TCTRL_CHN_MASK;\r
76 \r
77         // Start timers\r
78         PIT->CHANNEL[0].TCTRL |= PIT_TCTRL_TEN_MASK;\r
79         PIT->CHANNEL[1].TCTRL |= PIT_TCTRL_TEN_MASK;\r
80 \r
81         // Done\r
82         tdma_init_done = 1;\r
83 }\r
84 \r
85 /* DMA Functions */\r
86 tdma_t *tdma_open(tdma_chan_t chan, int alt, PinName pin, PinMode mode)\r
87 {\r
88         int req  = pin >= PTD0 ? TDMA_REQ_PTD :\r
89                    pin >= PTC0 ? TDMA_REQ_PTC :\r
90                    pin >= PTA0 ? TDMA_REQ_PTA : 0;\r
91 \r
92         int irqc = mode == PullDown ? 1 : 2;\r
93 \r
94         // Initialize global registers\r
95         tdma_init();\r
96 \r
97         // Allocate port\r
98         tdma_t *port    = &tdma_ports[chan];\r
99 \r
100         // Setup port pointers\r
101         port->dma       = (void*)&DMA0->DMA[chan];\r
102         port->mux       = (void*)&DMAMUX0->CHCFG[chan];\r
103         port->pin       = (void*)(PORTA_BASE + pin);\r
104 \r
105         // Reset DMA channel\r
106         port->dma->dsr  = DMA_DSR_BCR_DONE_MASK;\r
107 \r
108         // Configure DMA channel\r
109         port->dma->dcr  = DMA_DCR_SINC_MASK         // Source increment\r
110                         | DMA_DCR_DINC_MASK         // Dest increment\r
111                         | DMA_DCR_SSIZE(0)          // 32-bit access\r
112                         | DMA_DCR_DSIZE(0)          // 32-bit access\r
113                         | DMA_DCR_D_REQ_MASK;       // Only run once\r
114 \r
115         // Setup and enable DMA MUX\r
116         port->mux->cfg  = DMAMUX_CHCFG_SOURCE(req)  // Request source\r
117                         | DMAMUX_CHCFG_ENBL_MASK;   // Enable DMA mux\r
118 \r
119         // Set pin to generate DMA req\r
120         port->pin->pcr  = PORT_PCR_ISF_MASK         // Clear ISR flag\r
121                         | PORT_PCR_MUX(alt)         // Pin mapping\r
122                         | PORT_PCR_IRQC(irqc)       // DMA on falling edge\r
123                         | mode;                     // Pin pull up/down\r
124 \r
125         // Save IRC for later\r
126         port->req       = req;\r
127         port->irqc      = irqc;\r
128 \r
129         return port;\r
130 }\r
131 \r
132 void tdma_start(tdma_t *port)\r
133 {\r
134         if (!port)\r
135                 return;\r
136 \r
137         //sirq_printf("isfr2: %08x\r\n", PORTD->ISFR);\r
138 \r
139         // Clear previous time\r
140         port->time[0] = 0;\r
141         port->time[1] = 0;\r
142 \r
143         // Reset DMA Mux\r
144         port->mux->cfg &= DMAMUX_CHCFG_ENBL_MASK;\r
145 \r
146         // Freeze DMA channel\r
147         port->dma->dcr &= ~DMA_DCR_ERQ_MASK;\r
148 \r
149         // Reset DMA channel\r
150         port->dma->dsr  =  DMA_DSR_BCR_DONE_MASK;\r
151 \r
152         // Set addresses and size\r
153         port->dma->sar  =  (uint32_t)&PIT->LTMR64H;  // Global timer\r
154         port->dma->dar  =  (uint32_t)&port->time;    // Temp timer buffer\r
155         port->dma->dsr  =  DMA_DSR_BCR_BCR(8);       // 64-bit timer\r
156 \r
157         // Set pin to generate DMA req\r
158         port->pin->pcr |=  PORT_PCR_ISF_MASK;\r
159         port->pin->pcr |=  PORT_PCR_IRQC(port->irqc);\r
160 \r
161         // Enable port request\r
162         port->dma->dcr |=  DMA_DCR_ERQ_MASK;\r
163 \r
164         // Enable DMA Mux\r
165         port->mux->cfg  = DMAMUX_CHCFG_SOURCE(port->req)\r
166                         | DMAMUX_CHCFG_ENBL_MASK;\r
167 \r
168         //sirq_printf("isfr3: %08x\r\n", PORTD->ISFR);\r
169 }\r
170 \r
171 void tdma_stop(tdma_t *port, int wait)\r
172 {\r
173         if (!port)\r
174                 return;\r
175 \r
176         //sirq_printf("isfr0: %08x\r\n", PORTD->ISFR);\r
177 \r
178         for (int i = 0; port->dma->dsr & DMA_DSR_BCR_BCR_MASK; i++)\r
179                 if (i > wait)\r
180                         return;\r
181 \r
182         // Disable DMA Mux\r
183         port->mux->cfg &= DMAMUX_CHCFG_ENBL_MASK;\r
184 \r
185         // Freeze DMA channel\r
186         port->dma->dcr &= ~DMA_DCR_ERQ_MASK;\r
187 \r
188         // Reset DMA channel\r
189         port->dma->dsr  =  DMA_DSR_BCR_DONE_MASK;\r
190 \r
191         // Disable pin DMA request\r
192         port->pin->pcr &= ~PORT_PCR_IRQC_MASK;\r
193         port->pin->pcr |=  PORT_PCR_ISF_MASK;\r
194 \r
195         //sirq_printf("isfr1: %08x\r\n", PORTD->ISFR);\r
196 }\r
197 \r
198 int tdma_stamp(tdma_t *port, uint64_t *time)\r
199 {\r
200         uint64_t clocks;\r
201 \r
202         if (!port)\r
203                 return 0;\r
204 \r
205         if (port->dma->dsr & DMA_DSR_BCR_BCR_MASK)\r
206                 return 0;\r
207 \r
208         // Read the timestamp\r
209         clocks = ((uint64_t)~port->time[0]) << 32\r
210                | ((uint64_t)~port->time[1]) << 0;\r
211 \r
212         // Convert to nanoseconds\r
213         *time  = clocks * 1000 / 24;\r
214 \r
215         return 1;\r
216 }\r
217 \r
218 uint64_t tdma_time(void)\r
219 {\r
220         uint32_t tmh = PIT->LTMR64H;\r
221         uint32_t tml = PIT->LTMR64L;\r
222 \r
223         // Read the timestamp\r
224         uint64_t clocks = ((uint64_t)~tmh) << 32\r
225                         | ((uint64_t)~tml) << 0;\r
226 \r
227         // Convert to nanoseconds\r
228         return clocks * 125 / 3;\r
229 }\r
230 \r
231 void tdma_debug(tdma_t *port)\r
232 {\r
233         int dsr = port->dma->dsr;\r
234         int dcr = port->dma->dcr;\r
235 \r
236         printf("dsr: %s %s %s %s %s %s %d\r\n",\r
237                          dsr & DMA_DSR_BCR_CE_MASK   ? "CE"      : "ce",\r
238                          dsr & DMA_DSR_BCR_BES_MASK  ? "BSE"     : "bse",\r
239                          dsr & DMA_DSR_BCR_BED_MASK  ? "BED"     : "bed",\r
240                          dsr & DMA_DSR_BCR_REQ_MASK  ? "REQ"     : "req",\r
241                          dsr & DMA_DSR_BCR_BSY_MASK  ? "BSY"     : "bsy",\r
242                          dsr & DMA_DSR_BCR_DONE_MASK ? "DONE"    : "done",\r
243                          dsr & DMA_DSR_BCR_BCR_MASK);\r
244 \r
245         printf("dcr: %s %s %s %s %s %s %s %s ssize=%d:%d mod=%d:%d link=%d:%d:%d\r\n",\r
246                          dcr & DMA_DCR_EINT_MASK     ? "EINT"    : "eint",\r
247                          dcr & DMA_DCR_ERQ_MASK      ? "ERQ"     : "erq",\r
248                          dcr & DMA_DCR_CS_MASK       ? "CS"      : "cs",\r
249                          dcr & DMA_DCR_AA_MASK       ? "AA"      : "aa",\r
250                          dcr & DMA_DCR_EADREQ_MASK   ? "EADRREQ" : "eadrreq",\r
251                          dcr & DMA_DCR_SINC_MASK     ? "SINC"    : "sinc",\r
252                          dcr & DMA_DCR_DINC_MASK     ? "DINC"    : "dinc",\r
253                          dcr & DMA_DCR_D_REQ_MASK    ? "DREQ"    : "dreq",\r
254                         (dcr & DMA_DCR_SSIZE_MASK ) >> DMA_DCR_SSIZE_SHIFT,\r
255                         (dcr & DMA_DCR_DSIZE_MASK ) >> DMA_DCR_DSIZE_SHIFT,\r
256                         (dcr & DMA_DCR_SMOD_MASK  ) >> DMA_DCR_SMOD_SHIFT,\r
257                         (dcr & DMA_DCR_DMOD_MASK  ) >> DMA_DCR_DMOD_SHIFT,\r
258                         (dcr & DMA_DCR_LINKCC_MASK) >> DMA_DCR_LINKCC_SHIFT,\r
259                         (dcr & DMA_DCR_LCH1_MASK  ) >> DMA_DCR_LCH1_SHIFT,\r
260                         (dcr & DMA_DCR_LCH2_MASK  ) >> DMA_DCR_LCH2_SHIFT);\r
261 }\r