SHOW:
|
|
- or go back to the newest paste.
1 | - | /********************************************************************** |
1 | + | // File: ppm-decode.cpp |
2 | - | Various Maple tests.. including timer capture to memory via dma =) |
2 | + | // Original code from http://pastebin.com/NQtbVCFh |
3 | - | */ |
3 | + | // Posted on the leaflabs.com forum by Dweller: |
4 | // http://forums.leaflabs.com/topic.php?id=1170 | |
5 | ///********************************************************************** | |
6 | // Various Maple tests.. including timer capture to memory via dma =) | |
7 | // */ | |
8 | #include "main.h" | |
9 | - | //quick method to return if there's anyone on the other end of the serialusb link yet. |
9 | + | #include "wirish.h" |
10 | - | boolean isConnected(){ |
10 | + | |
11 | - | return (SerialUSB.isConnected() && (SerialUSB.getDTR() || SerialUSB.getRTS())); |
11 | + | |
12 | #include "dma.h" | |
13 | #include "utils.h" | |
14 | - | void setup(){ |
14 | + | |
15 | - | SerialUSB.begin(); |
15 | + | //number of captures to do by dma |
16 | - | while(!isConnected()); //wait till console attaches. |
16 | + | #define TIMERS 9 |
17 | - | SerialUSB.println("Hello!"); |
17 | + | |
18 | // timer prescale | |
19 | #define TIMER_PRESCALE 26 | |
20 | - | /********************************************************************** |
20 | + | |
21 | - | timer tests.. seeing if the timer clock really does run at 72mhz.. |
21 | + | // TIMER_PRESCALE*(1/72 MHz) = |
22 | - | */ |
22 | + | #define TICK_PERIOD ( TIMER_PRESCALE*0.0000138888889f ) |
23 | ||
24 | - | volatile int overflowCount=0; |
24 | + | HardwareTimer timer4 = HardwareTimer(4); |
25 | - | void countOverflows(){ |
25 | + | volatile int dma_data_captured=0; //set to 1 when dma complete. |
26 | - | overflowCount++; |
26 | + | |
27 | uint16 delta=0; | |
28 | uint16 ppm_timeout=0; | |
29 | - | int PRESCALE=128; |
29 | + | int do_print=1; |
30 | - | int WAIT=50; |
30 | + | |
31 | - | int results[16]; |
31 | + | |
32 | - | int overflow[16]; |
32 | + | |
33 | - | void testHardwareTimer(HardwareTimer t){ |
33 | + | float duty; |
34 | - | t.attachInterrupt(1,countOverflows); |
34 | + | for(int i=0; i<TIMERS; i++){ |
35 | - | for(int i=0; i<16; i++){ |
35 | + | |
36 | - | t.setCount(0); |
36 | + | if(ppm_timeout==1){ |
37 | - | overflowCount=0; |
37 | + | if(do_print==1) |
38 | - | t.resume(); |
38 | + | { |
39 | - | delay(WAIT); |
39 | + | SerialUSB.println("PPM timeout!"); |
40 | - | t.pause(); |
40 | + | do_print=0; |
41 | - | results[i]=t.getCount(); |
41 | + | } |
42 | - | overflow[i]=overflowCount; |
42 | + | return; |
43 | - | } |
43 | + | } |
44 | - | t.detachInterrupt(1); |
44 | + | |
45 | if(i>0) delta = data[i]-data[i-1]; | |
46 | - | void printResults(){ |
46 | + | else delta = data[i] - data[TIMERS-1]; |
47 | - | SerialUSB.print("Timing results: "); |
47 | + | |
48 | - | for(int i=0; i<16; i++){ |
48 | + | duty=(delta)*TICK_PERIOD; |
49 | - | SerialUSB.print(results[i]); |
49 | + | |
50 | - | SerialUSB.print(" "); |
50 | + | SerialUSB.print(delta); |
51 | - | } |
51 | + | SerialUSB.print(":("); |
52 | - | SerialUSB.println(); |
52 | + | SerialUSB.print(duty); |
53 | - | SerialUSB.print("Overflows: "); |
53 | + | SerialUSB.print(")"); |
54 | - | for(int i=0; i<16; i++){ |
54 | + | if ((i+1)%9==0) SerialUSB.print("\r"); |
55 | - | SerialUSB.print(overflow[i]); |
55 | + | else SerialUSB.print("\t"); |
56 | - | SerialUSB.print(" "); |
56 | + | |
57 | - | } |
57 | + | SerialUSB.println(); |
58 | - | SerialUSB.println(); |
58 | + | |
59 | - | SerialUSB.print("Each count worth approx (ns): "); |
59 | + | |
60 | - | for(int i=0; i<16; i++){ |
60 | + | |
61 | - | SerialUSB.print( waitToNanos(overflow[i], results[i]) ); |
61 | + | |
62 | - | SerialUSB.print(" "); |
62 | + | |
63 | - | } |
63 | + | dma_irq_cause cause = dma_get_irq_cause(DMA1, DMA_CH1); |
64 | - | SerialUSB.println(); |
64 | + | //using serialusb to print messages here is nice, but |
65 | //it takes so long, we may never exit this isr invocation | |
66 | - | double expectedTimePeriod(){ |
66 | + | |
67 | - | //in nanos.. so 72mhz = 72000khz = 72000000hz 1/72000000hz = tick in seconds |
67 | + | |
68 | - | // 1/72000 = tick in ms, 1/72 = tick in us (1/72) * 1000 = tick in ns |
68 | + | timer4.setCount(0); // clear counter |
69 | - | //tick in ns * prescale == time we're supposed to see |
69 | + | if(ppm_timeout) ppm_timeout=0; |
70 | - | return ((double)1.0 / ((double)72.0)) * (double)1000.0 * (double)PRESCALE; |
70 | + | if(!do_print) do_print=1; |
71 | switch(cause) | |
72 | - | double waitToNanos( int overflows, int count ){ |
72 | + | |
73 | - | //wait is in millis, *1000 for micros, *1000 for nanos |
73 | + | |
74 | - | double time = (((double)WAIT * (double)1000.0 * (double)1000.0) ) ; |
74 | + | |
75 | - | time = time / ((double)count + ((double)65535*(double)overflows)); |
75 | + | //SerialUSB.println("DMA Complete"); |
76 | - | return time; |
76 | + | dma_data_captured=1; |
77 | break; | |
78 | case DMA_TRANSFER_HALF_COMPLETE: | |
79 | - | int readInt(char terminator){ |
79 | + | |
80 | - | char current; |
80 | + | SerialUSB.println("DMA Half Complete"); |
81 | - | int output=0; |
81 | + | |
82 | - | while(SerialUSB.available() && (current=SerialUSB.read())!=terminator){ |
82 | + | |
83 | - | if(current>='0' && current<='9'){ |
83 | + | |
84 | - | output=output*10; |
84 | + | |
85 | - | output+=(current-'0'); |
85 | + | dma_data_captured=1; |
86 | - | }else{ |
86 | + | |
87 | - | output=-1; |
87 | + | |
88 | - | break; |
88 | + | |
89 | - | } |
89 | + | |
90 | - | } |
90 | + | |
91 | - | return output; |
91 | + | dma_data_captured=1; |
92 | break; | |
93 | } | |
94 | - | HardwareTimer timer2 = HardwareTimer(2); |
94 | + | |
95 | - | HardwareTimer timer1 = HardwareTimer(1); |
95 | + | |
96 | - | void timingTest(){ |
96 | + | |
97 | - | SerialUSB.println("Starting Timing test"); |
97 | + | void ppm_timeout_isr() |
98 | - | SerialUSB.print(" Prescale: "); |
98 | + | |
99 | - | SerialUSB.println(PRESCALE); |
99 | + | // This failure mode indicates we lost comm with |
100 | - | SerialUSB.print(" Wait Period (ms): "); |
100 | + | // the transmitter |
101 | - | SerialUSB.println(WAIT); |
101 | + | |
102 | - | SerialUSB.print(" Expected value for each tick :"); |
102 | + | //TODO: re-initialize the timer and wait for ppm signal |
103 | - | SerialUSB.println(expectedTimePeriod()); |
103 | + | //re-initializing should ensure that the ppm signal |
104 | - | timer1.pause(); |
104 | + | // is captured on the sync pulse. |
105 | - | timer2.pause(); |
105 | + | ppm_timeout=1; |
106 | dma_data_captured=0; | |
107 | - | timer2.setMode(TIMER_CH1, TIMER_OUTPUT_COMPARE); |
107 | + | |
108 | - | timer2.setPrescaleFactor(PRESCALE); |
108 | + | |
109 | - | timer2.setOverflow(65535); |
109 | + | void init_timer_input_capture_dma() |
110 | - | timer2.setCompare(1,65535); |
110 | + | |
111 | - | timer2.setCount(0); |
111 | + | |
112 | - | timer2.refresh(); |
112 | + | timer_dev *t = TIMER4; |
113 | ||
114 | - | timer1.setMode(TIMER_CH1, TIMER_OUTPUT_COMPARE); |
114 | + | timer4.pause(); |
115 | - | timer1.setPrescaleFactor(PRESCALE); |
115 | + | timer4.setPrescaleFactor(TIMER_PRESCALE); |
116 | - | timer1.setOverflow(65535); |
116 | + | timer4.setOverflow(65535); |
117 | - | timer1.setCompare(1,65535); |
117 | + | timer4.setCount(0); |
118 | - | timer1.setCount(0); |
118 | + | |
119 | - | timer1.refresh(); |
119 | + | // use channel 2 to detect when we stop receiving |
120 | // a ppm signal from the encoder. | |
121 | - | testHardwareTimer(timer1); |
121 | + | timer4.setMode(TIMER_CH2, TIMER_OUTPUT_COMPARE); |
122 | - | printResults(); |
122 | + | timer4.setCompare(TIMER_CH2, 65535); |
123 | timer4.attachCompare2Interrupt(ppm_timeout_isr); | |
124 | - | testHardwareTimer(timer2); |
124 | + | |
125 | - | printResults(); |
125 | + | |
126 | timer4.refresh(); | |
127 | ||
128 | - | /****************************************************************************** |
128 | + | timer_reg_map r = t->regs; |
129 | - | DMA from Timer Capture to array test.. |
129 | + | |
130 | - | */ |
130 | + | //using timer4, channel1, maps to pin d16 (maple mini) //d6 (maple?) |
131 | //according to maple master pin map. | |
132 | - | //number of captures to do.. |
132 | + | pinMode(PPM_PIN, INPUT); |
133 | - | #define TIMERS 512 |
133 | + | |
134 | - | volatile int exit=0; //set to 1 when dma complete. |
134 | + | //capture compare regs TIMx_CCRx used to hold val after a transition on corresponding ICx |
135 | ||
136 | //when cap occurs, flag CCXIF (TIMx_SR register) is set, | |
137 | //and interrupt, or dma req can be sent if they are enabled. | |
138 | ||
139 | - | SerialUSB.print("DATA: "); |
139 | + | //if cap occurs while flag is already high CCxOF (overcapture) flag is set.. |
140 | - | for(int i=0; i<TIMERS; i++){ |
140 | + | |
141 | - | SerialUSB.print(data[i]); |
141 | + | //CCIX can be cleared by writing 0, or by reading the capped data from TIMx_CCRx |
142 | - | SerialUSB.print(" "); |
142 | + | //CCxOF is cleared by writing 0 to it. |
143 | - | } |
143 | + | |
144 | - | SerialUSB.println(); |
144 | + | //Clear the CC1E bit to disable capture from the counter as we set it up. |
145 | //CC1S bits aren't writeable when CC1E is set. | |
146 | //CC1E is bit 0 of CCER (page 401) | |
147 | bitClear(r.gen->CCER,0); | |
148 | ||
149 | ||
150 | - | dma_irq_cause cause = dma_get_irq_cause(DMA1, DMA_CH2); |
150 | + | //Capture/Compare 1 Selection |
151 | - | //using serialusb to print messages here is nice, but |
151 | + | // set CC1S bits to 01 in the capture compare mode register 1. |
152 | // 01 selects TI1 as the input to use. (page 399 stm32 reference) | |
153 | // (assuming here that TI1 is D16, according to maple master pin map) | |
154 | //CC1S bits are bits 1,0 | |
155 | bitClear(r.gen->CCMR1, 1); | |
156 | bitSet(r.gen->CCMR1, 0); | |
157 | ||
158 | - | SerialUSB.println("DMA Complete"); |
158 | + | |
159 | - | exit=1; |
159 | + | //Input Capture 1 Filter. |
160 | // need to set IC1F bits according to a table saying how long | |
161 | // we should wait for a signal to be 'stable' to validate a transition | |
162 | // on the input. | |
163 | - | SerialUSB.println("DMA Half Complete"); |
163 | + | // (page 401 stm32 reference) |
164 | //IC1F bits are bits 7,6,5,4 | |
165 | bitClear(r.gen->CCMR1, 7); | |
166 | bitClear(r.gen->CCMR1, 6); | |
167 | bitSet(r.gen->CCMR1, 5); | |
168 | - | exit=1; |
168 | + | bitSet(r.gen->CCMR1, 4); |
169 | ||
170 | //sort out the input capture prescaler IC1PSC.. | |
171 | //00 no prescaler.. capture is done at every edge detected | |
172 | bitClear(r.gen->CCMR1, 3); | |
173 | bitClear(r.gen->CCMR1, 2); | |
174 | - | exit=1; |
174 | + | |
175 | //select the edge for the transition on TI1 channel using CC1P in CCER | |
176 | //CC1P is bit 1 of CCER (page 401) | |
177 | // 0 = rising (non-inverted. capture is done on a rising edge of IC1) | |
178 | // 1 = falling (inverted. capture is done on a falling edge of IC1) | |
179 | bitClear(r.gen->CCER,1); | |
180 | ||
181 | - | void loop(){ |
181 | + | //set the CC1E bit to enable capture from the counter. |
182 | - | |
182 | + | //CC1E is bit 0 of CCER (page 401) |
183 | - | //simple main loop, listens for a char, and uses it as a command |
183 | + | bitSet(r.gen->CCER,0); |
184 | - | //to select what task to perform |
184 | + | |
185 | - | // |
185 | + | //enable dma for this timer.. |
186 | - | // a echo test |
186 | + | //sets the Capture/Compare 1 DMA request enable bit on the DMA/interrupt enable register. |
187 | - | // b echo test |
187 | + | //bit 9 is CC1DE as defined on page 393. |
188 | - | // c serialusb disable/enable test |
188 | + | bitSet(r.gen->DIER,9); |
189 | - | // d time the timer test, default params |
189 | + | |
190 | - | // e time the timer test, user supplied args |
190 | + | dma_init(DMA1); |
191 | - | // f DMA timer capture of pulse widths from D6 straight to memory |
191 | + | dma_setup_transfer( DMA1, //dma device, dma1 here because that's the only one we have |
192 | - | |
192 | + | DMA_CH1, //dma channel, channel1, because it looks after tim4_ch1 (timer4, channel1) |
193 | - | while(!SerialUSB.available()); |
193 | + | &(r.gen->CCR1), //peripheral address |
194 | - | char cmd = SerialUSB.read(); |
194 | + | DMA_SIZE_16BITS, //peripheral size |
195 | - | switch(cmd) |
195 | + | data, //memory address |
196 | - | { |
196 | + | DMA_SIZE_16BITS, //memory transfer size |
197 | - | case 'a': |
197 | + | (0 |
198 | - | { |
198 | + | //| DMA_FROM_MEM //set if going from memory, don't set if going to memory. |
199 | - | SerialUSB.println("Hello"); |
199 | + | | DMA_MINC_MODE //auto inc where the data does in memory (uses size_16bits to know how much) |
200 | - | break; |
200 | + | | DMA_TRNS_ERR //tell me if it's fubar |
201 | - | } |
201 | + | //| DMA_HALF_TRNS //tell me half way (actually, don't as I spend so long there, I dont see 'complete') |
202 | - | case 'b': |
202 | + | | DMA_TRNS_CMPLT //tell me at the end |
203 | - | { |
203 | + | | DMA_CIRC_MODE // circular mode... capture "num_transfers" (below) and repeat |
204 | - | SerialUSB.println("There"); |
204 | + | ) |
205 | - | break; |
205 | + | ); |
206 | - | } |
206 | + | |
207 | dma_attach_interrupt(DMA1, DMA_CH1, dma_isr); //hook up an isr for the dma chan to tell us if things happen. | |
208 | - | case 'c': |
208 | + | dma_set_num_transfers(DMA1, DMA_CH1, TIMERS); //only allow it to transfer TIMERS number of times. |
209 | - | { |
209 | + | dma_enable(DMA1, DMA_CH1); //enable it.. |
210 | - | SerialUSB.println("Going away for a while"); |
210 | + | |
211 | - | SerialUSB.end(); |
211 | + | |
212 | - | usbPowerOff(); |
212 | + | |
213 | - | delay(5000); |
213 | + | |
214 | - | usbPowerOn(); |
214 | + | void ppm_decode_interrupt_dma() |
215 | - | SerialUSB.begin(); |
215 | + | |
216 | - | delay(5000); |
216 | + | |
217 | - | while(!isConnected()); |
217 | + | SerialUSB.print("Starting timer.. rising edge of D"); |
218 | - | SerialUSB.println("Welcome back"); |
218 | + | SerialUSB.print(PPM_PIN); |
219 | - | break; |
219 | + | disable_usarts(); |
220 | - | } |
220 | + | |
221 | - | case 'd': |
221 | + | //start the timer counting. |
222 | - | { |
222 | + | timer4.resume(); |
223 | - | WAIT=50; |
223 | + | //the timer is now counting up, and any rising edges on D16 |
224 | - | PRESCALE=128; |
224 | + | //will trigger a DMA request to clone the timercapture to the array |
225 | - | timingTest(); |
225 | + | |
226 | - | break; |
226 | + | //If we weren't using DMA, we could busy wait on the CC1IF bit in SR |
227 | - | } |
227 | + | // |
228 | - | case 'e': |
228 | + | //when the timer captures, the CC1IF bit will be set on the timer SR register. |
229 | - | { |
229 | + | //CC1IF bit is bit 1 (page 332) |
230 | - | int arg1=0; |
230 | + | //while(!bitRead(r.gen->SR, 1)){ |
231 | - | int arg2=0; |
231 | + | //} |
232 | - | |
232 | + | //SerialUSB.println("Timer triggered : "); |
233 | - | if(!SerialUSB.available()) |
233 | + | //SerialUSB.println(r.gen->CCR1); |
234 | - | goto usage; |
234 | + | |
235 | - | if(SerialUSB.read()!=' ') |
235 | + | //SerialUSB.println("Waiting for dma_data_captured flag from dma..."); |
236 | - | goto usage; |
236 | + | //busy wait on the dma_data_captured flag |
237 | - | |
237 | + | //we could do real work here if wanted.. |
238 | - | arg1 = readInt(' '); |
238 | + | while(!dma_data_captured && !SerialUSB.available()); |
239 | - | arg2 = readInt(' '); |
239 | + | |
240 | - | |
240 | + | while(!SerialUSB.available() && do_print==1) |
241 | - | if(arg1==-1 || arg2==-1) |
241 | + | |
242 | - | goto usage; |
242 | + | //dump the data |
243 | - | |
243 | + | printData(); |
244 | - | SerialUSB.print("Setting prescale to: "); |
244 | + | delay(100); |
245 | - | SerialUSB.println(arg1); |
245 | + | |
246 | - | SerialUSB.print("Setting wait to: "); |
246 | + | |
247 | - | SerialUSB.println(arg2); |
247 | + | //turn off the timer & tidy up before we leave this cmd. |
248 | - | |
248 | + | timer4.pause(); |
249 | - | WAIT=arg2; |
249 | + | |
250 | - | PRESCALE=arg1; |
250 | + | // dma_disable(DMA1, DMA_CH2); |
251 | - | timingTest(); |
251 | + | // dma_detach_interrupt(DMA1, DMA_CH2); |
252 | - | |
252 | + | dma_data_captured=0; |
253 | - | goto done; |
253 | + | |
254 | - | usage: |
254 | + | } |