]> cvs.zerfleddert.de Git - proxmark3-svn/blame - armsrc/iso15693.c
Added stand-alone mode (no PC) with HID reading and cloning, moved helper functions...
[proxmark3-svn] / armsrc / iso15693.c
CommitLineData
6658905f 1//-----------------------------------------------------------------------------\r
2// Routines to support ISO 15693. This includes both the reader software and\r
3// the `fake tag' modes, but at the moment I've implemented only the reader\r
4// stuff, and that barely.\r
5// Jonathan Westhues, split Nov 2006\r
6\r
7// Modified by Greg Jones, Jan 2009 to perform modulation onboard in arm rather than on PC\r
8// Also added additional reader commands (SELECT, READ etc.)\r
9\r
10//-----------------------------------------------------------------------------\r
11#include <proxmark3.h>\r
12#include "apps.h"\r
13#include <stdio.h>\r
14#include <stdlib.h>\r
15\r
16// FROM winsrc\prox.h //////////////////////////////////\r
17#define arraylen(x) (sizeof(x)/sizeof((x)[0]))\r
18\r
19//-----------------------------------------------------------------------------\r
20// Map a sequence of octets (~layer 2 command) into the set of bits to feed\r
21// to the FPGA, to transmit that command to the tag.\r
22//-----------------------------------------------------------------------------\r
23\r
24 \r
25\r
26\r
27 // The sampling rate is 106.353 ksps/s, for T = 18.8 us\r
28\r
29 // SOF defined as \r
30 // 1) Unmodulated time of 56.64us\r
31 // 2) 24 pulses of 423.75khz\r
32 // 3) logic '1' (unmodulated for 18.88us followed by 8 pulses of 423.75khz)\r
33\r
34 static const int FrameSOF[] = {\r
35 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
36 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
37 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\r
38 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\r
39 -1, -1, -1, -1,\r
40 -1, -1, -1, -1,\r
41 1, 1, 1, 1,\r
42 1, 1, 1, 1\r
43 };\r
44 static const int Logic0[] = {\r
45 1, 1, 1, 1,\r
46 1, 1, 1, 1,\r
47 -1, -1, -1, -1,\r
48 -1, -1, -1, -1\r
49 };\r
50 static const int Logic1[] = {\r
51 -1, -1, -1, -1,\r
52 -1, -1, -1, -1,\r
53 1, 1, 1, 1,\r
54 1, 1, 1, 1\r
55 };\r
56\r
57 // EOF defined as \r
58 // 1) logic '0' (8 pulses of 423.75khz followed by unmodulated for 18.88us)\r
59 // 2) 24 pulses of 423.75khz\r
60 // 3) Unmodulated time of 56.64us\r
61\r
62 static const int FrameEOF[] = {\r
63 1, 1, 1, 1,\r
64 1, 1, 1, 1,\r
65 -1, -1, -1, -1,\r
66 -1, -1, -1, -1,\r
67 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\r
68 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\r
69 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
70 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1\r
71 };\r
72\r
73\r
74\r
75\r
76static void CodeIso15693AsReader(BYTE *cmd, int n)\r
77{\r
78 int i, j;\r
79\r
80 ToSendReset();\r
81\r
82 // Give it a bit of slack at the beginning\r
83 for(i = 0; i < 24; i++) {\r
84 ToSendStuffBit(1);\r
85 }\r
86\r
87 ToSendStuffBit(0);\r
88 ToSendStuffBit(1);\r
89 ToSendStuffBit(1);\r
90 ToSendStuffBit(1);\r
91 ToSendStuffBit(1);\r
92 ToSendStuffBit(0);\r
93 ToSendStuffBit(1);\r
94 ToSendStuffBit(1);\r
95 for(i = 0; i < n; i++) {\r
96 for(j = 0; j < 8; j += 2) {\r
97 int these = (cmd[i] >> j) & 3;\r
98 switch(these) {\r
99 case 0:\r
100 ToSendStuffBit(1);\r
101 ToSendStuffBit(0);\r
102 ToSendStuffBit(1);\r
103 ToSendStuffBit(1);\r
104 ToSendStuffBit(1);\r
105 ToSendStuffBit(1);\r
106 ToSendStuffBit(1);\r
107 ToSendStuffBit(1);\r
108 break;\r
109 case 1:\r
110 ToSendStuffBit(1);\r
111 ToSendStuffBit(1);\r
112 ToSendStuffBit(1);\r
113 ToSendStuffBit(0);\r
114 ToSendStuffBit(1);\r
115 ToSendStuffBit(1);\r
116 ToSendStuffBit(1);\r
117 ToSendStuffBit(1);\r
118 break;\r
119 case 2:\r
120 ToSendStuffBit(1);\r
121 ToSendStuffBit(1);\r
122 ToSendStuffBit(1);\r
123 ToSendStuffBit(1);\r
124 ToSendStuffBit(1);\r
125 ToSendStuffBit(0);\r
126 ToSendStuffBit(1);\r
127 ToSendStuffBit(1);\r
128 break;\r
129 case 3:\r
130 ToSendStuffBit(1);\r
131 ToSendStuffBit(1);\r
132 ToSendStuffBit(1);\r
133 ToSendStuffBit(1);\r
134 ToSendStuffBit(1);\r
135 ToSendStuffBit(1);\r
136 ToSendStuffBit(1);\r
137 ToSendStuffBit(0);\r
138 break;\r
139 }\r
140 }\r
141 }\r
142 ToSendStuffBit(1);\r
143 ToSendStuffBit(1);\r
144 ToSendStuffBit(0);\r
145 ToSendStuffBit(1);\r
146\r
147 // And slack at the end, too.\r
148 for(i = 0; i < 24; i++) {\r
149 ToSendStuffBit(1);\r
150 }\r
151}\r
152\r
153//-----------------------------------------------------------------------------\r
154// The CRC used by ISO 15693.\r
155//-----------------------------------------------------------------------------\r
156static WORD Crc(BYTE *v, int n)\r
157{\r
158 DWORD reg;\r
159 int i, j;\r
160\r
161 reg = 0xffff;\r
162 for(i = 0; i < n; i++) {\r
163 reg = reg ^ ((DWORD)v[i]);\r
164 for (j = 0; j < 8; j++) {\r
165 if (reg & 0x0001) {\r
166 reg = (reg >> 1) ^ 0x8408;\r
167 } else {\r
168 reg = (reg >> 1);\r
169 }\r
170 }\r
171 }\r
172\r
173 return ~reg;\r
174}\r
175\r
176////////////////////////////////////////// code to do 'itoa'\r
177 \r
178\r
179\r
180/* reverse: reverse string s in place */\r
181void reverse(char s[])\r
182{\r
183 int c, i, j;\r
184\r
185 for (i = 0, j = strlen(s)-1; i<j; i++, j--) {\r
186 c = s[i];\r
187 s[i] = s[j];\r
188 s[j] = c;\r
189 }\r
190}\r
191\r
192/* itoa: convert n to characters in s */\r
193void itoa(int n, char s[])\r
194{\r
195 int i, sign;\r
196\r
197 if ((sign = n) < 0) /* record sign */\r
198 n = -n; /* make n positive */\r
199 i = 0;\r
200 do { /* generate digits in reverse order */\r
201 s[i++] = n % 10 + '0'; /* get next digit */\r
202 } while ((n /= 10) > 0); /* delete it */\r
203 if (sign < 0)\r
204 s[i++] = '-';\r
205 s[i] = '\0';\r
206 reverse(s);\r
207} \r
208\r
209//////////////////////////////////////// END 'itoa' CODE\r
210\r
211\r
212//-----------------------------------------------------------------------------\r
213// Encode (into the ToSend buffers) an identify request, which is the first\r
214// thing that you must send to a tag to get a response.\r
215//-----------------------------------------------------------------------------\r
216static void BuildIdentifyRequest(void)\r
217{\r
218 BYTE cmd[5];\r
219\r
220 WORD crc;\r
221 // one sub-carrier, inventory, 1 slot, fast rate\r
222 // AFI is at bit 5 (1<<4) when doing an INVENTORY\r
223 cmd[0] = (1 << 2) | (1 << 5) | (1 << 1); \r
224 // inventory command code\r
225 cmd[1] = 0x01;\r
226 // no mask\r
227 cmd[2] = 0x00;\r
228 //Now the CRC\r
229 crc = Crc(cmd, 3);\r
230 cmd[3] = crc & 0xff;\r
231 cmd[4] = crc >> 8;\r
232\r
233 CodeIso15693AsReader(cmd, sizeof(cmd));\r
234}\r
235\r
236static void BuildSysInfoRequest(BYTE *uid)\r
237{\r
238 BYTE cmd[12];\r
239\r
240 WORD crc;\r
241 // If we set the Option_Flag in this request, the VICC will respond with the secuirty status of the block\r
242 // followed by teh block data\r
243 // one sub-carrier, inventory, 1 slot, fast rate\r
244 cmd[0] = (1 << 5) | (1 << 1); // no SELECT bit\r
245 // System Information command code\r
246 cmd[1] = 0x2B;\r
247 // UID may be optionally specified here\r
248 // 64-bit UID\r
249 cmd[2] = 0x32;\r
250 cmd[3]= 0x4b;\r
251 cmd[4] = 0x03;\r
252 cmd[5] = 0x01;\r
253 cmd[6] = 0x00;\r
254 cmd[7] = 0x10;\r
255 cmd[8] = 0x05; \r
256 cmd[9]= 0xe0; // always e0 (not exactly unique) \r
257 //Now the CRC\r
258 crc = Crc(cmd, 10); // the crc needs to be calculated over 2 bytes \r
259 cmd[10] = crc & 0xff;\r
260 cmd[11] = crc >> 8;\r
261\r
262 CodeIso15693AsReader(cmd, sizeof(cmd));\r
263}\r
264\r
265static void BuildSelectRequest( BYTE uid[])\r
266{\r
267 \r
268// uid[6]=0x31; // this is getting ignored - the uid array is not happening...\r
269 BYTE cmd[12];\r
270\r
271 WORD crc;\r
272 // one sub-carrier, inventory, 1 slot, fast rate\r
273 //cmd[0] = (1 << 2) | (1 << 5) | (1 << 1); // INVENTROY FLAGS\r
274 cmd[0] = (1 << 4) | (1 << 5) | (1 << 1); // Select and addressed FLAGS\r
275 // SELECT command code\r
276 cmd[1] = 0x25;\r
277 // 64-bit UID\r
278// cmd[2] = uid[0];//0x32;\r
279// cmd[3]= uid[1];//0x4b;\r
280// cmd[4] = uid[2];//0x03;\r
281// cmd[5] = uid[3];//0x01;\r
282// cmd[6] = uid[4];//0x00;\r
283// cmd[7] = uid[5];//0x10;\r
284// cmd[8] = uid[6];//0x05; \r
285 cmd[2] = 0x32;//\r
286 cmd[3]= 0x4b;\r
287 cmd[4] = 0x03;\r
288 cmd[5] = 0x01;\r
289 cmd[6] = 0x00;\r
290 cmd[7] = 0x10;\r
291 cmd[8] = 0x05; // infineon?\r
292\r
293 cmd[9]= 0xe0; // always e0 (not exactly unique) \r
294\r
295// DbpIntegers(cmd[8],cmd[7],cmd[6]);\r
296 // Now the CRC\r
297 crc = Crc(cmd, 10); // the crc needs to be calculated over 10 bytes \r
298 cmd[10] = crc & 0xff;\r
299 cmd[11] = crc >> 8;\r
300\r
301 CodeIso15693AsReader(cmd, sizeof(cmd));\r
302}\r
303\r
304static void BuildReadBlockRequest(BYTE *uid, BYTE blockNumber )\r
305{\r
306 BYTE cmd[13];\r
307\r
308 WORD crc;\r
309 // If we set the Option_Flag in this request, the VICC will respond with the secuirty status of the block\r
310 // followed by teh block data\r
311 // one sub-carrier, inventory, 1 slot, fast rate\r
312 cmd[0] = (1 << 6)| (1 << 5) | (1 << 1); // no SELECT bit\r
313 // READ BLOCK command code\r
314 cmd[1] = 0x20;\r
315 // UID may be optionally specified here\r
316 // 64-bit UID\r
317 cmd[2] = 0x32;\r
318 cmd[3]= 0x4b;\r
319 cmd[4] = 0x03;\r
320 cmd[5] = 0x01;\r
321 cmd[6] = 0x00;\r
322 cmd[7] = 0x10;\r
323 cmd[8] = 0x05; \r
324 cmd[9]= 0xe0; // always e0 (not exactly unique) \r
325 // Block number to read\r
326 cmd[10] = blockNumber;//0x00;\r
327 //Now the CRC\r
328 crc = Crc(cmd, 11); // the crc needs to be calculated over 2 bytes \r
329 cmd[11] = crc & 0xff;\r
330 cmd[12] = crc >> 8;\r
331\r
332 CodeIso15693AsReader(cmd, sizeof(cmd));\r
333}\r
334\r
335\r
336static void BuildReadMultiBlockRequest(BYTE *uid)\r
337{\r
338 BYTE cmd[14];\r
339\r
340 WORD crc;\r
341 // If we set the Option_Flag in this request, the VICC will respond with the secuirty status of the block\r
342 // followed by teh block data\r
343 // one sub-carrier, inventory, 1 slot, fast rate\r
344 cmd[0] = (1 << 5) | (1 << 1); // no SELECT bit\r
345 // READ Multi BLOCK command code\r
346 cmd[1] = 0x23;\r
347 // UID may be optionally specified here\r
348 // 64-bit UID\r
349 cmd[2] = 0x32;\r
350 cmd[3]= 0x4b;\r
351 cmd[4] = 0x03;\r
352 cmd[5] = 0x01;\r
353 cmd[6] = 0x00;\r
354 cmd[7] = 0x10;\r
355 cmd[8] = 0x05; \r
356 cmd[9]= 0xe0; // always e0 (not exactly unique) \r
357 // First Block number to read\r
358 cmd[10] = 0x00;\r
359 // Number of Blocks to read\r
360 cmd[11] = 0x2f; // read quite a few\r
361 //Now the CRC\r
362 crc = Crc(cmd, 12); // the crc needs to be calculated over 2 bytes \r
363 cmd[12] = crc & 0xff;\r
364 cmd[13] = crc >> 8;\r
365\r
366 CodeIso15693AsReader(cmd, sizeof(cmd));\r
367}\r
368\r
369static void BuildArbitraryRequest(BYTE *uid,BYTE CmdCode)\r
370{\r
371 BYTE cmd[14];\r
372\r
373 WORD crc;\r
374 // If we set the Option_Flag in this request, the VICC will respond with the secuirty status of the block\r
375 // followed by teh block data\r
376 // one sub-carrier, inventory, 1 slot, fast rate\r
377 cmd[0] = (1 << 5) | (1 << 1); // no SELECT bit\r
378 // READ BLOCK command code\r
379 cmd[1] = CmdCode;\r
380 // UID may be optionally specified here\r
381 // 64-bit UID\r
382 cmd[2] = 0x32;\r
383 cmd[3]= 0x4b;\r
384 cmd[4] = 0x03;\r
385 cmd[5] = 0x01;\r
386 cmd[6] = 0x00;\r
387 cmd[7] = 0x10;\r
388 cmd[8] = 0x05; \r
389 cmd[9]= 0xe0; // always e0 (not exactly unique) \r
390 // Parameter\r
391 cmd[10] = 0x00;\r
392 cmd[11] = 0x0a;\r
393\r
394// cmd[12] = 0x00;\r
395// cmd[13] = 0x00; //Now the CRC\r
396 crc = Crc(cmd, 12); // the crc needs to be calculated over 2 bytes \r
397 cmd[12] = crc & 0xff;\r
398 cmd[13] = crc >> 8;\r
399\r
400 CodeIso15693AsReader(cmd, sizeof(cmd));\r
401}\r
402\r
403static void BuildArbitraryCustomRequest(BYTE *uid,BYTE CmdCode)\r
404{\r
405 BYTE cmd[14];\r
406\r
407 WORD crc;\r
408 // If we set the Option_Flag in this request, the VICC will respond with the secuirty status of the block\r
409 // followed by teh block data\r
410 // one sub-carrier, inventory, 1 slot, fast rate\r
411 cmd[0] = (1 << 5) | (1 << 1); // no SELECT bit\r
412 // READ BLOCK command code\r
413 cmd[1] = CmdCode;\r
414 // UID may be optionally specified here\r
415 // 64-bit UID\r
416 cmd[2] = 0x32;\r
417 cmd[3]= 0x4b;\r
418 cmd[4] = 0x03;\r
419 cmd[5] = 0x01;\r
420 cmd[6] = 0x00;\r
421 cmd[7] = 0x10;\r
422 cmd[8] = 0x05; \r
423 cmd[9]= 0xe0; // always e0 (not exactly unique) \r
424 // Parameter\r
425 cmd[10] = 0x05; // for custom codes this must be manufcturer code\r
426 cmd[11] = 0x00;\r
427\r
428// cmd[12] = 0x00;\r
429// cmd[13] = 0x00; //Now the CRC\r
430 crc = Crc(cmd, 12); // the crc needs to be calculated over 2 bytes \r
431 cmd[12] = crc & 0xff;\r
432 cmd[13] = crc >> 8;\r
433\r
434 CodeIso15693AsReader(cmd, sizeof(cmd));\r
435}\r
436\r
437/////////////////////////////////////////////////////////////////////////\r
438// Now the VICC>VCD responses when we are simulating a tag\r
439////////////////////////////////////////////////////////////////////\r
440\r
441 static void BuildInventoryResponse(void)\r
442{\r
443 BYTE cmd[12];\r
444\r
445 WORD crc;\r
446 // one sub-carrier, inventory, 1 slot, fast rate\r
447 // AFI is at bit 5 (1<<4) when doing an INVENTORY\r
448 cmd[0] = 0; //(1 << 2) | (1 << 5) | (1 << 1); \r
449 cmd[1] = 0;\r
450 // 64-bit UID\r
451 cmd[2] = 0x32;\r
452 cmd[3]= 0x4b;\r
453 cmd[4] = 0x03;\r
454 cmd[5] = 0x01;\r
455 cmd[6] = 0x00;\r
456 cmd[7] = 0x10;\r
457 cmd[8] = 0x05; \r
458 cmd[9]= 0xe0;\r
459 //Now the CRC\r
460 crc = Crc(cmd, 10);\r
461 cmd[10] = crc & 0xff;\r
462 cmd[11] = crc >> 8;\r
463\r
464 CodeIso15693AsReader(cmd, sizeof(cmd));\r
465}\r
466\r
467\r
468//-----------------------------------------------------------------------------\r
469// Transmit the command (to the tag) that was placed in ToSend[].\r
470//-----------------------------------------------------------------------------\r
471static void TransmitTo15693Tag(const BYTE *cmd, int len, int *samples, int *wait)\r
472{\r
473 int c;\r
474\r
475// FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD);\r
476 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX);\r
477 if(*wait < 10) { *wait = 10; }\r
478\r
479// for(c = 0; c < *wait;) {\r
480// if(SSC_STATUS & (SSC_STATUS_TX_READY)) {\r
481// SSC_TRANSMIT_HOLDING = 0x00; // For exact timing!\r
482// c++;\r
483// }\r
484// if(SSC_STATUS & (SSC_STATUS_RX_READY)) {\r
485// volatile DWORD r = SSC_RECEIVE_HOLDING;\r
486// (void)r;\r
487// }\r
488// WDT_HIT();\r
489// }\r
490\r
491 c = 0;\r
492 for(;;) {\r
493 if(SSC_STATUS & (SSC_STATUS_TX_READY)) {\r
494 SSC_TRANSMIT_HOLDING = cmd[c];\r
495 c++;\r
496 if(c >= len) {\r
497 break;\r
498 }\r
499 }\r
500 if(SSC_STATUS & (SSC_STATUS_RX_READY)) {\r
501 volatile DWORD r = SSC_RECEIVE_HOLDING;\r
502 (void)r;\r
503 }\r
504 WDT_HIT();\r
505 }\r
506 *samples = (c + *wait) << 3;\r
507}\r
508\r
509\r
510//-----------------------------------------------------------------------------\r
511// Transmit the command (to the reader) that was placed in ToSend[].\r
512//-----------------------------------------------------------------------------\r
513static void TransmitTo15693Reader(const BYTE *cmd, int len, int *samples, int *wait)\r
514{\r
515 int c;\r
516\r
517// FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX);\r
518 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR); // No requirement to energise my coils\r
519 if(*wait < 10) { *wait = 10; }\r
520\r
521 c = 0;\r
522 for(;;) {\r
523 if(SSC_STATUS & (SSC_STATUS_TX_READY)) {\r
524 SSC_TRANSMIT_HOLDING = cmd[c];\r
525 c++;\r
526 if(c >= len) {\r
527 break;\r
528 }\r
529 }\r
530 if(SSC_STATUS & (SSC_STATUS_RX_READY)) {\r
531 volatile DWORD r = SSC_RECEIVE_HOLDING;\r
532 (void)r;\r
533 }\r
534 WDT_HIT();\r
535 }\r
536 *samples = (c + *wait) << 3;\r
537}\r
538\r
539\r
540\r
541\r
542\r
543\r
544static int GetIso15693AnswerFromTag(BYTE *receivedResponse, int maxLen, int *samples, int *elapsed) \r
545{\r
546 int c = 0;\r
547 BYTE *dest = (BYTE *)BigBuf;\r
548 int getNext = 0;\r
549\r
550\r
551 SBYTE prev = 0;\r
552\r
553// NOW READ RESPONSE\r
554 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR);\r
555 //spindelay(60); // greg - experiment to get rid of some of the 0 byte/failed reads\r
556 c = 0;\r
557 getNext = FALSE;\r
558 for(;;) {\r
559 if(SSC_STATUS & (SSC_STATUS_TX_READY)) {\r
560 SSC_TRANSMIT_HOLDING = 0x43;\r
561 }\r
562 if(SSC_STATUS & (SSC_STATUS_RX_READY)) {\r
563 SBYTE b;\r
564 b = (SBYTE)SSC_RECEIVE_HOLDING;\r
565\r
566 // The samples are correlations against I and Q versions of the\r
567 // tone that the tag AM-modulates, so every other sample is I,\r
568 // every other is Q. We just want power, so abs(I) + abs(Q) is\r
569 // close to what we want.\r
570 if(getNext) {\r
571 SBYTE r;\r
572\r
573 if(b < 0) {\r
574 r = -b;\r
575 } else {\r
576 r = b;\r
577 }\r
578 if(prev < 0) {\r
579 r -= prev;\r
580 } else {\r
581 r += prev;\r
582 }\r
583\r
584 dest[c++] = (BYTE)r;\r
585\r
586 if(c >= 2000) {\r
587 break;\r
588 }\r
589 } else {\r
590 prev = b;\r
591 }\r
592\r
593 getNext = !getNext;\r
594 }\r
595 }\r
596\r
597//////////////////////////////////////////\r
598/////////// DEMODULATE ///////////////////\r
599//////////////////////////////////////////\r
600\r
601 int i, j;\r
602 int max = 0, maxPos;\r
603\r
604 int skip = 4;\r
605\r
606\r
607// if(GraphTraceLen < 1000) return; // THIS CHECKS FOR A BUFFER TO SMALL\r
608\r
609 // First, correlate for SOF\r
610 for(i = 0; i < 100; i++) {\r
611 int corr = 0;\r
612 for(j = 0; j < arraylen(FrameSOF); j += skip) {\r
613 corr += FrameSOF[j]*dest[i+(j/skip)];\r
614 }\r
615 if(corr > max) {\r
616 max = corr;\r
617 maxPos = i;\r
618 }\r
619 }\r
620// DbpString("SOF at %d, correlation %d", maxPos,max/(arraylen(FrameSOF)/skip));\r
621\r
622 int k = 0; // this will be our return value\r
623\r
624 // greg - If correlation is less than 1 then there's little point in continuing\r
625 if ((max/(arraylen(FrameSOF)/skip)) >= 1) \r
626 {\r
627\r
628 i = maxPos + arraylen(FrameSOF)/skip;\r
629 \r
630 BYTE outBuf[20];\r
631 memset(outBuf, 0, sizeof(outBuf));\r
632 BYTE mask = 0x01;\r
633 for(;;) {\r
634 int corr0 = 0, corr1 = 0, corrEOF = 0;\r
635 for(j = 0; j < arraylen(Logic0); j += skip) {\r
636 corr0 += Logic0[j]*dest[i+(j/skip)];\r
637 }\r
638 for(j = 0; j < arraylen(Logic1); j += skip) {\r
639 corr1 += Logic1[j]*dest[i+(j/skip)];\r
640 }\r
641 for(j = 0; j < arraylen(FrameEOF); j += skip) {\r
642 corrEOF += FrameEOF[j]*dest[i+(j/skip)];\r
643 }\r
644 // Even things out by the length of the target waveform.\r
645 corr0 *= 4;\r
646 corr1 *= 4;\r
647\r
648 if(corrEOF > corr1 && corrEOF > corr0) {\r
649// DbpString("EOF at %d", i);\r
650 break;\r
651 } else if(corr1 > corr0) {\r
652 i += arraylen(Logic1)/skip;\r
653 outBuf[k] |= mask;\r
654 } else {\r
655 i += arraylen(Logic0)/skip;\r
656 }\r
657 mask <<= 1;\r
658 if(mask == 0) {\r
659 k++;\r
660 mask = 0x01;\r
661 }\r
662 if((i+(int)arraylen(FrameEOF)) >= 2000) {\r
663 DbpString("ran off end!");\r
664 break;\r
665 }\r
666 }\r
667 if(mask != 0x01) {\r
668 DbpString("error, uneven octet! (discard extra bits!)");\r
669/// DbpString(" mask=%02x", mask);\r
670 }\r
671// BYTE str1 [8];\r
672// itoa(k,str1);\r
673// strcat(str1," octets read");\r
674\r
675// DbpString( str1); // DbpString("%d octets", k);\r
676\r
677// for(i = 0; i < k; i+=3) {\r
678// //DbpString("# %2d: %02x ", i, outBuf[i]);\r
679// DbpIntegers(outBuf[i],outBuf[i+1],outBuf[i+2]);\r
680// }\r
681\r
682 for(i = 0; i < k; i++) {\r
683 receivedResponse[i] = outBuf[i];\r
684 } \r
685 } // "end if correlation > 0" (max/(arraylen(FrameSOF)/skip))\r
686 return k; // return the number of bytes demodulated\r
687\r
688/// DbpString("CRC=%04x", Iso15693Crc(outBuf, k-2));\r
689\r
690\r
691}\r
692\r
693// Now the GetISO15693 message from sniffing command\r
694static int GetIso15693AnswerFromSniff(BYTE *receivedResponse, int maxLen, int *samples, int *elapsed) \r
695{\r
696 int c = 0;\r
697 BYTE *dest = (BYTE *)BigBuf;\r
698 int getNext = 0;\r
699\r
700\r
701 SBYTE prev = 0;\r
702\r
703// NOW READ RESPONSE\r
704 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR);\r
705 //spindelay(60); // greg - experiment to get rid of some of the 0 byte/failed reads\r
706 c = 0;\r
707 getNext = FALSE;\r
708 for(;;) {\r
709 if(SSC_STATUS & (SSC_STATUS_TX_READY)) {\r
710 SSC_TRANSMIT_HOLDING = 0x43;\r
711 }\r
712 if(SSC_STATUS & (SSC_STATUS_RX_READY)) {\r
713 SBYTE b;\r
714 b = (SBYTE)SSC_RECEIVE_HOLDING;\r
715\r
716 // The samples are correlations against I and Q versions of the\r
717 // tone that the tag AM-modulates, so every other sample is I,\r
718 // every other is Q. We just want power, so abs(I) + abs(Q) is\r
719 // close to what we want.\r
720 if(getNext) {\r
721 SBYTE r;\r
722\r
723 if(b < 0) {\r
724 r = -b;\r
725 } else {\r
726 r = b;\r
727 }\r
728 if(prev < 0) {\r
729 r -= prev;\r
730 } else {\r
731 r += prev;\r
732 }\r
733\r
734 dest[c++] = (BYTE)r;\r
735\r
736 if(c >= 20000) {\r
737 break;\r
738 }\r
739 } else {\r
740 prev = b;\r
741 }\r
742\r
743 getNext = !getNext;\r
744 }\r
745 }\r
746\r
747//////////////////////////////////////////\r
748/////////// DEMODULATE ///////////////////\r
749//////////////////////////////////////////\r
750\r
751 int i, j;\r
752 int max = 0, maxPos;\r
753\r
754 int skip = 4;\r
755\r
756\r
757// if(GraphTraceLen < 1000) return; // THIS CHECKS FOR A BUFFER TO SMALL\r
758\r
759 // First, correlate for SOF\r
760 for(i = 0; i < 19000; i++) {\r
761 int corr = 0;\r
762 for(j = 0; j < arraylen(FrameSOF); j += skip) {\r
763 corr += FrameSOF[j]*dest[i+(j/skip)];\r
764 }\r
765 if(corr > max) {\r
766 max = corr;\r
767 maxPos = i;\r
768 }\r
769 }\r
770// DbpString("SOF at %d, correlation %d", maxPos,max/(arraylen(FrameSOF)/skip));\r
771\r
772 int k = 0; // this will be our return value\r
773\r
774 // greg - If correlation is less than 1 then there's little point in continuing\r
775 if ((max/(arraylen(FrameSOF)/skip)) >= 1) // THIS SHOULD BE 1 \r
776 {\r
777\r
778 i = maxPos + arraylen(FrameSOF)/skip;\r
779 \r
780 BYTE outBuf[20];\r
781 memset(outBuf, 0, sizeof(outBuf));\r
782 BYTE mask = 0x01;\r
783 for(;;) {\r
784 int corr0 = 0, corr1 = 0, corrEOF = 0;\r
785 for(j = 0; j < arraylen(Logic0); j += skip) {\r
786 corr0 += Logic0[j]*dest[i+(j/skip)];\r
787 }\r
788 for(j = 0; j < arraylen(Logic1); j += skip) {\r
789 corr1 += Logic1[j]*dest[i+(j/skip)];\r
790 }\r
791 for(j = 0; j < arraylen(FrameEOF); j += skip) {\r
792 corrEOF += FrameEOF[j]*dest[i+(j/skip)];\r
793 }\r
794 // Even things out by the length of the target waveform.\r
795 corr0 *= 4;\r
796 corr1 *= 4;\r
797\r
798 if(corrEOF > corr1 && corrEOF > corr0) {\r
799// DbpString("EOF at %d", i);\r
800 break;\r
801 } else if(corr1 > corr0) {\r
802 i += arraylen(Logic1)/skip;\r
803 outBuf[k] |= mask;\r
804 } else {\r
805 i += arraylen(Logic0)/skip;\r
806 }\r
807 mask <<= 1;\r
808 if(mask == 0) {\r
809 k++;\r
810 mask = 0x01;\r
811 }\r
812 if((i+(int)arraylen(FrameEOF)) >= 2000) {\r
813 DbpString("ran off end!");\r
814 break;\r
815 }\r
816 }\r
817 if(mask != 0x01) {\r
818 DbpString("error, uneven octet! (discard extra bits!)");\r
819/// DbpString(" mask=%02x", mask);\r
820 }\r
821// BYTE str1 [8];\r
822// itoa(k,str1);\r
823// strcat(str1," octets read");\r
824\r
825// DbpString( str1); // DbpString("%d octets", k);\r
826\r
827// for(i = 0; i < k; i+=3) {\r
828// //DbpString("# %2d: %02x ", i, outBuf[i]);\r
829// DbpIntegers(outBuf[i],outBuf[i+1],outBuf[i+2]);\r
830// }\r
831\r
832 for(i = 0; i < k; i++) {\r
833 receivedResponse[i] = outBuf[i];\r
834 } \r
835 } // "end if correlation > 0" (max/(arraylen(FrameSOF)/skip))\r
836 return k; // return the number of bytes demodulated\r
837\r
838/// DbpString("CRC=%04x", Iso15693Crc(outBuf, k-2));\r
839\r
840\r
841}\r
842\r
843\r
844\r
845//-----------------------------------------------------------------------------\r
846// Start to read an ISO 15693 tag. We send an identify request, then wait\r
847// for the response. The response is not demodulated, just left in the buffer\r
848// so that it can be downloaded to a PC and processed there.\r
849//-----------------------------------------------------------------------------\r
850void AcquireRawAdcSamplesIso15693(void)\r
851{\r
852 int c = 0;\r
853 BYTE *dest = (BYTE *)BigBuf;\r
854 int getNext = 0;\r
855\r
856 SBYTE prev = 0;\r
857\r
858 BuildIdentifyRequest();\r
859\r
860 SetAdcMuxFor(GPIO_MUXSEL_HIPKD);\r
861\r
862 // Give the tags time to energize\r
863 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR);\r
864 SpinDelay(100);\r
865\r
866 // Now send the command\r
867 FpgaSetupSsc();\r
868 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX);\r
869\r
870 c = 0;\r
871 for(;;) {\r
872 if(SSC_STATUS & (SSC_STATUS_TX_READY)) {\r
873 SSC_TRANSMIT_HOLDING = ToSend[c];\r
874 c++;\r
875 if(c == ToSendMax+3) {\r
876 break;\r
877 }\r
878 }\r
879 if(SSC_STATUS & (SSC_STATUS_RX_READY)) {\r
880 volatile DWORD r = SSC_RECEIVE_HOLDING;\r
881 (void)r;\r
882 }\r
883 WDT_HIT();\r
884 }\r
885\r
886 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR);\r
887\r
888 c = 0;\r
889 getNext = FALSE;\r
890 for(;;) {\r
891 if(SSC_STATUS & (SSC_STATUS_TX_READY)) {\r
892 SSC_TRANSMIT_HOLDING = 0x43;\r
893 }\r
894 if(SSC_STATUS & (SSC_STATUS_RX_READY)) {\r
895 SBYTE b;\r
896 b = (SBYTE)SSC_RECEIVE_HOLDING;\r
897\r
898 // The samples are correlations against I and Q versions of the\r
899 // tone that the tag AM-modulates, so every other sample is I,\r
900 // every other is Q. We just want power, so abs(I) + abs(Q) is\r
901 // close to what we want.\r
902 if(getNext) {\r
903 SBYTE r;\r
904\r
905 if(b < 0) {\r
906 r = -b;\r
907 } else {\r
908 r = b;\r
909 }\r
910 if(prev < 0) {\r
911 r -= prev;\r
912 } else {\r
913 r += prev;\r
914 }\r
915\r
916 dest[c++] = (BYTE)r;\r
917\r
918 if(c >= 2000) {\r
919 break;\r
920 }\r
921 } else {\r
922 prev = b;\r
923 }\r
924\r
925 getNext = !getNext;\r
926 }\r
927 }\r
928}\r
929\r
930\r
931\r
932//-----------------------------------------------------------------------------\r
933// Simulate an ISO15693 reader, perform anti-collision and then attempt to read a sector\r
934// all demodulation performed in arm rather than host. - greg\r
935//-----------------------------------------------------------------------------\r
936void ReaderIso15693(DWORD parameter)\r
937{\r
938 LED_A_ON();\r
939 LED_B_ON();\r
940 LED_C_OFF();\r
941 LED_D_OFF();\r
942\r
943\r
944//DbpString(parameter);\r
945\r
946 BYTE *receivedAnswer0 = (((BYTE *)BigBuf) + 3560); // allow 100 bytes per reponse (way too much)\r
947 BYTE *receivedAnswer1 = (((BYTE *)BigBuf) + 3660); // \r
948 BYTE *receivedAnswer2 = (((BYTE *)BigBuf) + 3760);\r
949 BYTE *receivedAnswer3 = (((BYTE *)BigBuf) + 3860);\r
950 //BYTE *TagUID= (((BYTE *)BigBuf) + 3960); // where we hold the uid for hi15reader \r
951 int responseLen0 = 0;\r
952 int responseLen1 = 0;\r
953 int responseLen2 = 0;\r
954 int responseLen3 = 0;\r
955\r
956 // Blank arrays\r
957 int j;\r
958 for(j = 0; j < 100; j++) {\r
959 receivedAnswer3[j] = 0;\r
960 receivedAnswer2[j] =0;\r
961 receivedAnswer1[j] = 0;\r
962 receivedAnswer0[j] = 0;\r
963 }\r
964\r
965 // Setup SSC\r
966 FpgaSetupSsc();\r
967\r
968 // Start from off (no field generated)\r
969 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);\r
970 SpinDelay(200);\r
971\r
972 SetAdcMuxFor(GPIO_MUXSEL_HIPKD);\r
973 FpgaSetupSsc();\r
974\r
975 // Give the tags time to energize\r
976 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR);\r
977 SpinDelay(200);\r
978\r
979 LED_A_ON();\r
980 LED_B_OFF();\r
981 LED_C_OFF();\r
982 LED_D_OFF();\r
983\r
984 int samples = 0;\r
985 int tsamples = 0;\r
986 int wait = 0;\r
987 int elapsed = 0;\r
988\r
989 // FIRST WE RUN AN INVENTORY TO GET THE TAG UID\r
990 // THIS MEANS WE CAN PRE-BUILD REQUESTS TO SAVE CPU TIME\r
991 BYTE TagUID[7]; // where we hold the uid for hi15reader \r
992\r
993\r
994// BuildIdentifyRequest();\r
995// //TransmitTo15693Tag(ToSend,ToSendMax+3,&tsamples, &wait); \r
996// TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait); // No longer ToSendMax+3\r
997// // Now wait for a response\r
998// responseLen0 = GetIso15693AnswerFromTag(receivedAnswer0, 100, &samples, &elapsed) ; \r
999// if (responseLen0 >=12) // we should do a better check than this\r
1000// {\r
1001// // really we should check it is a valid mesg\r
1002// // but for now just grab what we think is the uid\r
1003// TagUID[0] = receivedAnswer0[2];\r
1004// TagUID[1] = receivedAnswer0[3];\r
1005// TagUID[2] = receivedAnswer0[4];\r
1006// TagUID[3] = receivedAnswer0[5];\r
1007// TagUID[4] = receivedAnswer0[6];\r
1008// TagUID[5] = receivedAnswer0[7];\r
1009// TagUID[6] = receivedAnswer0[8]; // IC Manufacturer code\r
1010// DbpIntegers(TagUID[6],TagUID[5],TagUID[4]); \r
1011//}\r
1012\r
1013 // Now send the IDENTIFY command\r
1014 BuildIdentifyRequest();\r
1015 //TransmitTo15693Tag(ToSend,ToSendMax+3,&tsamples, &wait); \r
1016 TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait); // No longer ToSendMax+3\r
1017 // Now wait for a response\r
1018 responseLen1 = GetIso15693AnswerFromTag(receivedAnswer1, 100, &samples, &elapsed) ;\r
1019 \r
1020 if (responseLen1 >=12) // we should do a better check than this\r
1021 {\r
1022 \r
1023 TagUID[0] = receivedAnswer1[2];\r
1024 TagUID[1] = receivedAnswer1[3];\r
1025 TagUID[2] = receivedAnswer1[4];\r
1026 TagUID[3] = receivedAnswer1[5];\r
1027 TagUID[4] = receivedAnswer1[6];\r
1028 TagUID[5] = receivedAnswer1[7];\r
1029 TagUID[6] = receivedAnswer1[8]; // IC Manufacturer code\r
1030 \r
1031 // Now send the SELECT command\r
1032 BuildSelectRequest(*TagUID);\r
1033 TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait); // No longer ToSendMax+3\r
1034 // Now wait for a response\r
1035 responseLen2 = GetIso15693AnswerFromTag(receivedAnswer2, 100, &samples, &elapsed); \r
1036\r
1037 // Now send the MULTI READ command\r
1038// BuildArbitraryRequest(*TagUID,parameter);\r
1039 BuildArbitraryCustomRequest(*TagUID,parameter);\r
1040// BuildReadBlockRequest(*TagUID,parameter);\r
1041// BuildSysInfoRequest(*TagUID);\r
1042 //TransmitTo15693Tag(ToSend,ToSendMax+3,&tsamples, &wait); \r
1043 TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait); // No longer ToSendMax+3 \r
1044 // Now wait for a response\r
1045 responseLen3 = GetIso15693AnswerFromTag(receivedAnswer3, 100, &samples, &elapsed) ;\r
1046\r
1047 }\r
1048\r
1049\r
1050\r
1051 BYTE str1 [4];\r
1052 //char str2 [200];\r
1053 int i;\r
1054\r
1055 itoa(responseLen1,str1);\r
1056 strcat(str1," octets read from IDENTIFY request");\r
1057 DbpString(str1);\r
1058 for(i = 0; i < responseLen1; i+=3) {\r
1059 DbpIntegers(receivedAnswer1[i],receivedAnswer1[i+1],receivedAnswer1[i+2]);\r
1060 }\r
1061\r
1062 itoa(responseLen2,str1);\r
1063 strcat(str1," octets read from SELECT request");\r
1064 DbpString(str1);\r
1065 for(i = 0; i < responseLen2; i+=3) {\r
1066 DbpIntegers(receivedAnswer2[i],receivedAnswer2[i+1],receivedAnswer2[i+2]);\r
1067 }\r
1068\r
1069 itoa(responseLen3,str1);\r
1070 strcat(str1," octets read from XXX request");\r
1071 DbpString(str1);\r
1072 for(i = 0; i < responseLen3; i+=3) {\r
1073 DbpIntegers(receivedAnswer3[i],receivedAnswer3[i+1],receivedAnswer3[i+2]);\r
1074 }\r
1075 \r
1076\r
1077// str2[0]=0;\r
1078// for(i = 0; i < responseLen3; i++) {\r
1079// itoa(str1,receivedAnswer3[i]);\r
1080// strcat(str2,str1);\r
1081// }\r
1082// DbpString(str2); \r
1083\r
1084 LED_A_OFF();\r
1085 LED_B_OFF();\r
1086 LED_C_OFF();\r
1087 LED_D_OFF();\r
1088\r
1089\r
1090}\r
1091\r
1092\r
1093\r
1094//-----------------------------------------------------------------------------\r
1095// Simulate an ISO15693 TAG, perform anti-collision and then print any reader commands\r
1096// all demodulation performed in arm rather than host. - greg\r
1097//-----------------------------------------------------------------------------\r
1098void SimTagIso15693(DWORD parameter)\r
1099{\r
1100 LED_A_ON();\r
1101 LED_B_ON();\r
1102 LED_C_OFF();\r
1103 LED_D_OFF();\r
1104\r
1105\r
1106//DbpString(parameter);\r
1107\r
1108 BYTE *receivedAnswer0 = (((BYTE *)BigBuf) + 3560); // allow 100 bytes per reponse (way too much)\r
1109 BYTE *receivedAnswer1 = (((BYTE *)BigBuf) + 3660); // \r
1110 BYTE *receivedAnswer2 = (((BYTE *)BigBuf) + 3760);\r
1111 BYTE *receivedAnswer3 = (((BYTE *)BigBuf) + 3860);\r
1112 //BYTE *TagUID= (((BYTE *)BigBuf) + 3960); // where we hold the uid for hi15reader \r
1113 int responseLen0 = 0;\r
1114 int responseLen1 = 0;\r
1115 int responseLen2 = 0;\r
1116 int responseLen3 = 0;\r
1117\r
1118 // Blank arrays\r
1119 int j;\r
1120 for(j = 0; j < 100; j++) {\r
1121 receivedAnswer3[j] = 0;\r
1122 receivedAnswer2[j] =0;\r
1123 receivedAnswer1[j] = 0;\r
1124 receivedAnswer0[j] = 0;\r
1125 }\r
1126\r
1127 // Setup SSC\r
1128 FpgaSetupSsc();\r
1129\r
1130 // Start from off (no field generated)\r
1131 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);\r
1132 SpinDelay(200);\r
1133\r
1134 SetAdcMuxFor(GPIO_MUXSEL_HIPKD);\r
1135 FpgaSetupSsc();\r
1136\r
1137 // Give the tags time to energize\r
1138// FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); // NO GOOD FOR SIM TAG!!!!\r
1139 SpinDelay(200);\r
1140\r
1141 LED_A_OFF();\r
1142 LED_B_OFF();\r
1143 LED_C_ON();\r
1144 LED_D_OFF();\r
1145\r
1146 int samples = 0;\r
1147 int tsamples = 0;\r
1148 int wait = 0;\r
1149 int elapsed = 0;\r
1150\r
1151 // FIRST WE RUN AN INVENTORY TO GET THE TAG UID\r
1152 // THIS MEANS WE CAN PRE-BUILD REQUESTS TO SAVE CPU TIME\r
1153 BYTE TagUID[7]; // where we hold the uid for hi15reader \r
1154\r
1155\r
1156\r
1157 // Now send the IDENTIFY command\r
1158// BuildIdentifyRequest();\r
1159// TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait); // No longer ToSendMax+3\r
1160\r
1161\r
1162 // Now wait for a command from the reader\r
1163 responseLen1=0;\r
1164// while(responseLen1=0) {\r
1165// if(BUTTON_PRESS()) break;\r
1166 responseLen1 = GetIso15693AnswerFromSniff(receivedAnswer1, 100, &samples, &elapsed) ;\r
1167// }\r
1168\r
1169 \r
1170 if (responseLen1 >=1) // we should do a better check than this\r
1171 {\r
1172 // Build a suitable reponse to the reader INVENTORY cocmmand\r
1173 BuildInventoryResponse;\r
1174 TransmitTo15693Reader(ToSend,ToSendMax,&tsamples, &wait);\r
1175\r
1176 // Now wait for a command from the reader\r
1177// responseLen2 = GetIso15693AnswerFromTag(receivedAnswer2, 100, &samples, &elapsed); \r
1178\r
1179 \r
1180 // Now wait for a command from the reader\r
1181// responseLen3 = GetIso15693AnswerFromTag(receivedAnswer3, 100, &samples, &elapsed) ;\r
1182\r
1183 }\r
1184\r
1185\r
1186\r
1187 BYTE str1 [4];\r
1188 //char str2 [200];\r
1189 int i;\r
1190\r
1191 itoa(responseLen1,str1);\r
1192 strcat(str1," octets read from reader command");\r
1193 DbpString(str1);\r
1194 for(i = 0; i < responseLen1; i+=3) {\r
1195 DbpIntegers(receivedAnswer1[i],receivedAnswer1[i+1],receivedAnswer1[i+2]);\r
1196 }\r
1197\r
1198// itoa(responseLen2,str1);\r
1199// strcat(str1," octets read from SELECT request");\r
1200// DbpString(str1);\r
1201// for(i = 0; i < responseLen2; i+=3) {\r
1202// DbpIntegers(receivedAnswer2[i],receivedAnswer2[i+1],receivedAnswer2[i+2]);\r
1203// }\r
1204//\r
1205// itoa(responseLen3,str1);\r
1206// strcat(str1," octets read from XXX request");\r
1207// DbpString(str1);\r
1208// for(i = 0; i < responseLen3; i+=3) {\r
1209// DbpIntegers(receivedAnswer3[i],receivedAnswer3[i+1],receivedAnswer3[i+2]);\r
1210// }\r
1211 \r
1212\r
1213// str2[0]=0;\r
1214// for(i = 0; i < responseLen3; i++) {\r
1215// itoa(str1,receivedAnswer3[i]);\r
1216// strcat(str2,str1);\r
1217// }\r
1218// DbpString(str2); \r
1219\r
1220 LED_A_OFF();\r
1221 LED_B_OFF();\r
1222 LED_C_OFF();\r
1223 LED_D_OFF();\r
1224\r
1225\r
1226}
Impressum, Datenschutz