]> cvs.zerfleddert.de Git - proxmark3-svn/blob - armsrc/appmain.c
a3871eb64f7ab73558d62327901dc1cf7641acb8
[proxmark3-svn] / armsrc / appmain.c
1 //-----------------------------------------------------------------------------
2 // The main application code. This is the first thing called after start.c
3 // executes.
4 // Jonathan Westhues, Mar 2006
5 // Edits by Gerhard de Koning Gans, Sep 2007 (##)
6 //-----------------------------------------------------------------------------
7
8 #include <proxmark3.h>
9 #include <stdlib.h>
10 #include "apps.h"
11 #ifdef WITH_LCD
12 #include "fonts.h"
13 #include "LCD.h"
14 #endif
15
16
17 //=============================================================================
18 // A buffer where we can queue things up to be sent through the FPGA, for
19 // any purpose (fake tag, as reader, whatever). We go MSB first, since that
20 // is the order in which they go out on the wire.
21 //=============================================================================
22
23 BYTE ToSend[256];
24 int ToSendMax;
25 static int ToSendBit;
26
27 void BufferClear(void)
28 {
29 memset(BigBuf,0,sizeof(BigBuf));
30 DbpString("Buffer cleared");
31 }
32
33 void ToSendReset(void)
34 {
35 ToSendMax = -1;
36 ToSendBit = 8;
37 }
38
39 void ToSendStuffBit(int b)
40 {
41 if(ToSendBit >= 8) {
42 ToSendMax++;
43 ToSend[ToSendMax] = 0;
44 ToSendBit = 0;
45 }
46
47 if(b) {
48 ToSend[ToSendMax] |= (1 << (7 - ToSendBit));
49 }
50
51 ToSendBit++;
52
53 if(ToSendBit >= sizeof(ToSend)) {
54 ToSendBit = 0;
55 DbpString("ToSendStuffBit overflowed!");
56 }
57 }
58
59 //=============================================================================
60 // Debug print functions, to go out over USB, to the usual PC-side client.
61 //=============================================================================
62
63 void DbpString(char *str)
64 {
65 /* this holds up stuff unless we're connected to usb */
66 if (!UsbConnected())
67 return;
68
69 UsbCommand c;
70 c.cmd = CMD_DEBUG_PRINT_STRING;
71 c.ext1 = strlen(str);
72 memcpy(c.d.asBytes, str, c.ext1);
73
74 UsbSendPacket((BYTE *)&c, sizeof(c));
75 // TODO fix USB so stupid things like this aren't req'd
76 SpinDelay(50);
77 }
78
79 void DbpIntegers(int x1, int x2, int x3)
80 {
81 /* this holds up stuff unless we're connected to usb */
82 if (!UsbConnected())
83 return;
84
85 UsbCommand c;
86 c.cmd = CMD_DEBUG_PRINT_INTEGERS;
87 c.ext1 = x1;
88 c.ext2 = x2;
89 c.ext3 = x3;
90
91 UsbSendPacket((BYTE *)&c, sizeof(c));
92 // XXX
93 SpinDelay(50);
94 }
95
96 //-----------------------------------------------------------------------------
97 // Read an ADC channel and block till it completes, then return the result
98 // in ADC units (0 to 1023). Also a routine to average 32 samples and
99 // return that.
100 //-----------------------------------------------------------------------------
101 static int ReadAdc(int ch)
102 {
103 DWORD d;
104
105 ADC_CONTROL = ADC_CONTROL_RESET;
106 ADC_MODE = ADC_MODE_PRESCALE(32) | ADC_MODE_STARTUP_TIME(16) |
107 ADC_MODE_SAMPLE_HOLD_TIME(8);
108 ADC_CHANNEL_ENABLE = ADC_CHANNEL(ch);
109
110 ADC_CONTROL = ADC_CONTROL_START;
111 while(!(ADC_STATUS & ADC_END_OF_CONVERSION(ch)))
112 ;
113 d = ADC_CHANNEL_DATA(ch);
114
115 return d;
116 }
117
118 static int AvgAdc(int ch)
119 {
120 int i;
121 int a = 0;
122
123 for(i = 0; i < 32; i++) {
124 a += ReadAdc(ch);
125 }
126
127 return (a + 15) >> 5;
128 }
129
130 void MeasureAntennaTuning(void)
131 {
132 BYTE *dest = (BYTE *)BigBuf;
133 int i, ptr = 0, adcval = 0, peak = 0, peakv = 0, peakf = 0;;
134 int vLf125 = 0, vLf134 = 0, vHf = 0; // in mV
135
136 UsbCommand c;
137
138 DbpString("Measuring antenna characteristics, please wait.");
139 memset(BigBuf,0,sizeof(BigBuf));
140
141 /*
142 * Sweeps the useful LF range of the proxmark from
143 * 46.8kHz (divisor=255) to 600kHz (divisor=19) and
144 * read the voltage in the antenna, the result left
145 * in the buffer is a graph which should clearly show
146 * the resonating frequency of your LF antenna
147 * ( hopefully around 95 if it is tuned to 125kHz!)
148 */
149 FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER);
150 for (i=255; i>19; i--) {
151 FpgaSendCommand(FPGA_CMD_SET_DIVISOR, i);
152 SpinDelay(20);
153 // Vref = 3.3V, and a 10000:240 voltage divider on the input
154 // can measure voltages up to 137500 mV
155 adcval = ((137500 * AvgAdc(ADC_CHAN_LF)) >> 10);
156 if (i==95) vLf125 = adcval; // voltage at 125Khz
157 if (i==89) vLf134 = adcval; // voltage at 134Khz
158
159 dest[i] = adcval>>8; // scale int to fit in byte for graphing purposes
160 if(dest[i] > peak) {
161 peakv = adcval;
162 peak = dest[i];
163 peakf = i;
164 ptr = i;
165 }
166 }
167
168 // Let the FPGA drive the high-frequency antenna around 13.56 MHz.
169 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR);
170 SpinDelay(20);
171 // Vref = 3300mV, and an 10:1 voltage divider on the input
172 // can measure voltages up to 33000 mV
173 vHf = (33000 * AvgAdc(ADC_CHAN_HF)) >> 10;
174
175 c.cmd = CMD_MEASURED_ANTENNA_TUNING;
176 c.ext1 = (vLf125 << 0) | (vLf134 << 16);
177 c.ext2 = vHf;
178 c.ext3 = peakf | (peakv << 16);
179 UsbSendPacket((BYTE *)&c, sizeof(c));
180 }
181
182 void SimulateTagHfListen(void)
183 {
184 BYTE *dest = (BYTE *)BigBuf;
185 int n = sizeof(BigBuf);
186 BYTE v = 0;
187 int i;
188 int p = 0;
189
190 // We're using this mode just so that I can test it out; the simulated
191 // tag mode would work just as well and be simpler.
192 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ | FPGA_HF_READER_RX_XCORR_SNOOP);
193
194 // We need to listen to the high-frequency, peak-detected path.
195 SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
196
197 FpgaSetupSsc();
198
199 i = 0;
200 for(;;) {
201 if(SSC_STATUS & (SSC_STATUS_TX_READY)) {
202 SSC_TRANSMIT_HOLDING = 0xff;
203 }
204 if(SSC_STATUS & (SSC_STATUS_RX_READY)) {
205 BYTE r = (BYTE)SSC_RECEIVE_HOLDING;
206
207 v <<= 1;
208 if(r & 1) {
209 v |= 1;
210 }
211 p++;
212
213 if(p >= 8) {
214 dest[i] = v;
215 v = 0;
216 p = 0;
217 i++;
218
219 if(i >= n) {
220 break;
221 }
222 }
223 }
224 }
225 DbpString("simulate tag (now type bitsamples)");
226 }
227
228 void ReadMem(int addr)
229 {
230 const DWORD *data = ((DWORD *)addr);
231 int i;
232
233 DbpString("Reading memory at address");
234 DbpIntegers(0, 0, addr);
235 for (i = 0; i < 8; i+= 2)
236 DbpIntegers(0, data[i], data[i+1]);
237 }
238
239 // samy's sniff and repeat routine
240 void SamyRun()
241 {
242 DbpString("Stand-alone mode! No PC necessary.");
243
244 // 3 possible options? no just 2 for now
245 #define OPTS 2
246
247 int high[OPTS], low[OPTS];
248
249 // Oooh pretty -- notify user we're in elite samy mode now
250 LED(LED_RED, 200);
251 LED(LED_ORANGE, 200);
252 LED(LED_GREEN, 200);
253 LED(LED_ORANGE, 200);
254 LED(LED_RED, 200);
255 LED(LED_ORANGE, 200);
256 LED(LED_GREEN, 200);
257 LED(LED_ORANGE, 200);
258 LED(LED_RED, 200);
259
260 int selected = 0;
261 int playing = 0;
262
263 // Turn on selected LED
264 LED(selected + 1, 0);
265
266 for (;;)
267 {
268 UsbPoll(FALSE);
269 WDT_HIT();
270
271 // Was our button held down or pressed?
272 int button_pressed = BUTTON_HELD(1000);
273 SpinDelay(300);
274
275 // Button was held for a second, begin recording
276 if (button_pressed > 0)
277 {
278 LEDsoff();
279 LED(selected + 1, 0);
280 LED(LED_RED2, 0);
281
282 // record
283 DbpString("Starting recording");
284
285 // wait for button to be released
286 while(BUTTON_PRESS())
287 WDT_HIT();
288
289 /* need this delay to prevent catching some weird data */
290 SpinDelay(500);
291
292 CmdHIDdemodFSK(1, &high[selected], &low[selected], 0);
293 DbpString("Recorded");
294 DbpIntegers(selected, high[selected], low[selected]);
295
296 LEDsoff();
297 LED(selected + 1, 0);
298 // Finished recording
299
300 // If we were previously playing, set playing off
301 // so next button push begins playing what we recorded
302 playing = 0;
303 }
304
305 // Change where to record (or begin playing)
306 else if (button_pressed)
307 {
308 // Next option if we were previously playing
309 if (playing)
310 selected = (selected + 1) % OPTS;
311 playing = !playing;
312
313 LEDsoff();
314 LED(selected + 1, 0);
315
316 // Begin transmitting
317 if (playing)
318 {
319 LED(LED_GREEN, 0);
320 DbpString("Playing");
321 // wait for button to be released
322 while(BUTTON_PRESS())
323 WDT_HIT();
324 DbpIntegers(selected, high[selected], low[selected]);
325 CmdHIDsimTAG(high[selected], low[selected], 0);
326 DbpString("Done playing");
327 if (BUTTON_HELD(1000) > 0)
328 {
329 DbpString("Exiting");
330 LEDsoff();
331 return;
332 }
333
334 /* We pressed a button so ignore it here with a delay */
335 SpinDelay(300);
336
337 // when done, we're done playing, move to next option
338 selected = (selected + 1) % OPTS;
339 playing = !playing;
340 LEDsoff();
341 LED(selected + 1, 0);
342 }
343 else
344 while(BUTTON_PRESS())
345 WDT_HIT();
346 }
347 }
348 }
349
350
351 /*
352 OBJECTIVE
353 Listen and detect an external reader. Determine the best location
354 for the antenna.
355
356 INSTRUCTIONS:
357 Inside the ListenReaderField() function, there is two mode.
358 By default, when you call the function, you will enter mode 1.
359 If you press the PM3 button one time, you will enter mode 2.
360 If you press the PM3 button a second time, you will exit the function.
361
362 DESCRIPTION OF MODE 1:
363 This mode just listens for an external reader field and lights up green
364 for HF and/or red for LF. This is the original mode of the detectreader
365 function.
366
367 DESCRIPTION OF MODE 2:
368 This mode will visually represent, using the LEDs, the actual strength of the
369 current compared to the maximum current detected. Basically, once you know
370 what kind of external reader is present, it will help you spot the best location to place
371 your antenna. You will probably not get some good results if there is a LF and a HF reader
372 at the same place! :-)
373
374 LIGHT SCHEME USED:
375 */
376 static const char LIGHT_SCHEME[] = {
377 0x0, /* ---- | No field detected */
378 0x1, /* X--- | 14% of maximum current detected */
379 0x2, /* -X-- | 29% of maximum current detected */
380 0x4, /* --X- | 43% of maximum current detected */
381 0x8, /* ---X | 57% of maximum current detected */
382 0xC, /* --XX | 71% of maximum current detected */
383 0xE, /* -XXX | 86% of maximum current detected */
384 0xF, /* XXXX | 100% of maximum current detected */
385 };
386 static const int LIGHT_LEN = sizeof(LIGHT_SCHEME)/sizeof(LIGHT_SCHEME[0]);
387
388 void ListenReaderField(int limit)
389 {
390 int lf_av, lf_av_new, lf_baseline= 0, lf_count= 0, lf_max;
391 int hf_av, hf_av_new, hf_baseline= 0, hf_count= 0, hf_max;
392 int mode=1, display_val, display_max, i;
393
394 #define LF_ONLY 1
395 #define HF_ONLY 2
396
397 LEDsoff();
398
399 lf_av=lf_max=ReadAdc(ADC_CHAN_LF);
400
401 if(limit != HF_ONLY) {
402 DbpString("LF 125/134 Baseline:");
403 DbpIntegers(lf_av,0,0);
404 lf_baseline= lf_av;
405 }
406
407 hf_av=hf_max=ReadAdc(ADC_CHAN_HF);
408
409 if (limit != LF_ONLY) {
410 DbpString("HF 13.56 Baseline:");
411 DbpIntegers(hf_av,0,0);
412 hf_baseline= hf_av;
413 }
414
415 for(;;) {
416 if (BUTTON_PRESS()) {
417 SpinDelay(500);
418 switch (mode) {
419 case 1:
420 mode=2;
421 DbpString("Signal Strength Mode");
422 break;
423 case 2:
424 default:
425 DbpString("Stopped");
426 LEDsoff();
427 return;
428 break;
429 }
430 }
431 WDT_HIT();
432
433 if (limit != HF_ONLY) {
434 if(mode==1) {
435 if (abs(lf_av - lf_baseline) > 10) LED_D_ON();
436 else LED_D_OFF();
437 }
438
439 ++lf_count;
440 lf_av_new= ReadAdc(ADC_CHAN_LF);
441 // see if there's a significant change
442 if(abs(lf_av - lf_av_new) > 10) {
443 DbpString("LF 125/134 Field Change:");
444 DbpIntegers(lf_av,lf_av_new,lf_count);
445 lf_av= lf_av_new;
446 if (lf_av > lf_max)
447 lf_max = lf_av;
448 lf_count= 0;
449 }
450 }
451
452 if (limit != LF_ONLY) {
453 if (mode == 1){
454 if (abs(hf_av - hf_baseline) > 10) LED_B_ON();
455 else LED_B_OFF();
456 }
457
458 ++hf_count;
459 hf_av_new= ReadAdc(ADC_CHAN_HF);
460 // see if there's a significant change
461 if(abs(hf_av - hf_av_new) > 10) {
462 DbpString("HF 13.56 Field Change:");
463 DbpIntegers(hf_av,hf_av_new,hf_count);
464 hf_av= hf_av_new;
465 if (hf_av > hf_max)
466 hf_max = hf_av;
467 hf_count= 0;
468 }
469 }
470
471 if(mode == 2) {
472 if (limit == LF_ONLY) {
473 display_val = lf_av;
474 display_max = lf_max;
475 } else if (limit == HF_ONLY) {
476 display_val = hf_av;
477 display_max = hf_max;
478 } else { /* Pick one at random */
479 if( (hf_max - hf_baseline) > (lf_max - lf_baseline) ) {
480 display_val = hf_av;
481 display_max = hf_max;
482 } else {
483 display_val = lf_av;
484 display_max = lf_max;
485 }
486 }
487 for (i=0; i<LIGHT_LEN; i++) {
488 if (display_val >= ((display_max/LIGHT_LEN)*i) && display_val <= ((display_max/LIGHT_LEN)*(i+1))) {
489 if (LIGHT_SCHEME[i] & 0x1) LED_C_ON(); else LED_C_OFF();
490 if (LIGHT_SCHEME[i] & 0x2) LED_A_ON(); else LED_A_OFF();
491 if (LIGHT_SCHEME[i] & 0x4) LED_B_ON(); else LED_B_OFF();
492 if (LIGHT_SCHEME[i] & 0x8) LED_D_ON(); else LED_D_OFF();
493 break;
494 }
495 }
496 }
497 }
498 }
499
500 void UsbPacketReceived(BYTE *packet, int len)
501 {
502 UsbCommand *c = (UsbCommand *)packet;
503
504 switch(c->cmd) {
505 case CMD_ACQUIRE_RAW_ADC_SAMPLES_125K:
506 AcquireRawAdcSamples125k(c->ext1);
507 break;
508
509 case CMD_MOD_THEN_ACQUIRE_RAW_ADC_SAMPLES_125K:
510 ModThenAcquireRawAdcSamples125k(c->ext1,c->ext2,c->ext3,c->d.asBytes);
511 break;
512
513 case CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_15693:
514 AcquireRawAdcSamplesIso15693();
515 break;
516
517 case CMD_BUFF_CLEAR:
518 BufferClear();
519 break;
520
521 case CMD_READER_ISO_15693:
522 ReaderIso15693(c->ext1);
523 break;
524
525 case CMD_SIMTAG_ISO_15693:
526 SimTagIso15693(c->ext1);
527 break;
528
529 case CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_14443:
530 AcquireRawAdcSamplesIso14443(c->ext1);
531 break;
532
533 case CMD_READ_SRI512_TAG:
534 ReadSRI512Iso14443(c->ext1);
535 break;
536
537 case CMD_READER_ISO_14443a:
538 ReaderIso14443a(c->ext1);
539 break;
540
541 case CMD_SNOOP_ISO_14443:
542 SnoopIso14443();
543 break;
544
545 case CMD_SNOOP_ISO_14443a:
546 SnoopIso14443a();
547 break;
548
549 case CMD_SIMULATE_TAG_HF_LISTEN:
550 SimulateTagHfListen();
551 break;
552
553 case CMD_SIMULATE_TAG_ISO_14443:
554 SimulateIso14443Tag();
555 break;
556
557 case CMD_SIMULATE_TAG_ISO_14443a:
558 SimulateIso14443aTag(c->ext1, c->ext2); // ## Simulate iso14443a tag - pass tag type & UID
559 break;
560
561 case CMD_MEASURE_ANTENNA_TUNING:
562 MeasureAntennaTuning();
563 break;
564
565 case CMD_LISTEN_READER_FIELD:
566 ListenReaderField(c->ext1);
567 break;
568
569 case CMD_HID_DEMOD_FSK:
570 CmdHIDdemodFSK(0, 0, 0, 1); // Demodulate HID tag
571 break;
572
573 case CMD_HID_SIM_TAG:
574 CmdHIDsimTAG(c->ext1, c->ext2, 1); // Simulate HID tag by ID
575 break;
576
577 case CMD_FPGA_MAJOR_MODE_OFF: // ## FPGA Control
578 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
579 SpinDelay(200);
580 LED_D_OFF(); // LED D indicates field ON or OFF
581 break;
582
583 case CMD_READ_TI_TYPE:
584 ReadTItag();
585 break;
586
587 case CMD_WRITE_TI_TYPE:
588 WriteTItag(c->ext1,c->ext2,c->ext3);
589 break;
590
591 case CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K: {
592 UsbCommand n;
593 if(c->cmd == CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K) {
594 n.cmd = CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K;
595 } else {
596 n.cmd = CMD_DOWNLOADED_RAW_BITS_TI_TYPE;
597 }
598 n.ext1 = c->ext1;
599 memcpy(n.d.asDwords, BigBuf+c->ext1, 12*sizeof(DWORD));
600 UsbSendPacket((BYTE *)&n, sizeof(n));
601 break;
602 }
603 case CMD_DOWNLOADED_SIM_SAMPLES_125K: {
604 BYTE *b = (BYTE *)BigBuf;
605 memcpy(b+c->ext1, c->d.asBytes, 48);
606 break;
607 }
608 case CMD_SIMULATE_TAG_125K:
609 LED_A_ON();
610 SimulateTagLowFrequency(c->ext1, 1);
611 LED_A_OFF();
612 break;
613 case CMD_READ_MEM:
614 ReadMem(c->ext1);
615 break;
616 case CMD_SET_LF_DIVISOR:
617 FpgaSendCommand(FPGA_CMD_SET_DIVISOR, c->ext1);
618 break;
619 #ifdef WITH_LCD
620 case CMD_LCD_RESET:
621 LCDReset();
622 break;
623 case CMD_LCD:
624 LCDSend(c->ext1);
625 break;
626 #endif
627 case CMD_SETUP_WRITE:
628 case CMD_FINISH_WRITE:
629 case CMD_HARDWARE_RESET:
630 USB_D_PLUS_PULLUP_OFF();
631 SpinDelay(1000);
632 SpinDelay(1000);
633 RSTC_CONTROL = RST_CONTROL_KEY | RST_CONTROL_PROCESSOR_RESET;
634 for(;;) {
635 // We're going to reset, and the bootrom will take control.
636 }
637 break;
638
639 default:
640 DbpString("unknown command");
641 break;
642 }
643 }
644
645 void AppMain(void)
646 {
647 memset(BigBuf,0,sizeof(BigBuf));
648 SpinDelay(100);
649
650 LED_D_OFF();
651 LED_C_OFF();
652 LED_B_OFF();
653 LED_A_OFF();
654
655 UsbStart();
656
657 // The FPGA gets its clock from us from PCK0 output, so set that up.
658 PIO_PERIPHERAL_B_SEL = (1 << GPIO_PCK0);
659 PIO_DISABLE = (1 << GPIO_PCK0);
660 PMC_SYS_CLK_ENABLE = PMC_SYS_CLK_PROGRAMMABLE_CLK_0;
661 // PCK0 is PLL clock / 4 = 96Mhz / 4 = 24Mhz
662 PMC_PROGRAMMABLE_CLK_0 = PMC_CLK_SELECTION_PLL_CLOCK |
663 PMC_CLK_PRESCALE_DIV_4;
664 PIO_OUTPUT_ENABLE = (1 << GPIO_PCK0);
665
666 // Reset SPI
667 SPI_CONTROL = SPI_CONTROL_RESET;
668 // Reset SSC
669 SSC_CONTROL = SSC_CONTROL_RESET;
670
671 // Load the FPGA image, which we have stored in our flash.
672 FpgaDownloadAndGo();
673
674 #ifdef WITH_LCD
675
676 LCDInit();
677
678 // test text on different colored backgrounds
679 LCDString(" The quick brown fox ", &FONT6x8,1,1+8*0,WHITE ,BLACK );
680 LCDString(" jumped over the ", &FONT6x8,1,1+8*1,BLACK ,WHITE );
681 LCDString(" lazy dog. ", &FONT6x8,1,1+8*2,YELLOW ,RED );
682 LCDString(" AaBbCcDdEeFfGgHhIiJj ", &FONT6x8,1,1+8*3,RED ,GREEN );
683 LCDString(" KkLlMmNnOoPpQqRrSsTt ", &FONT6x8,1,1+8*4,MAGENTA,BLUE );
684 LCDString("UuVvWwXxYyZz0123456789", &FONT6x8,1,1+8*5,BLUE ,YELLOW);
685 LCDString("`-=[]_;',./~!@#$%^&*()", &FONT6x8,1,1+8*6,BLACK ,CYAN );
686 LCDString(" _+{}|:\\\"<>? ",&FONT6x8,1,1+8*7,BLUE ,MAGENTA);
687
688 // color bands
689 LCDFill(0, 1+8* 8, 132, 8, BLACK);
690 LCDFill(0, 1+8* 9, 132, 8, WHITE);
691 LCDFill(0, 1+8*10, 132, 8, RED);
692 LCDFill(0, 1+8*11, 132, 8, GREEN);
693 LCDFill(0, 1+8*12, 132, 8, BLUE);
694 LCDFill(0, 1+8*13, 132, 8, YELLOW);
695 LCDFill(0, 1+8*14, 132, 8, CYAN);
696 LCDFill(0, 1+8*15, 132, 8, MAGENTA);
697
698 #endif
699
700 for(;;) {
701 UsbPoll(FALSE);
702 WDT_HIT();
703
704 if (BUTTON_HELD(1000) > 0)
705 SamyRun();
706 }
707 }
Impressum, Datenschutz