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 | + | } |