1 //-----------------------------------------------------------------------------
2 // The main application code. This is the first thing called after start.c
4 // Jonathan Westhues, Mar 2006
5 // Edits by Gerhard de Koning Gans, Sep 2007 (##)
6 //-----------------------------------------------------------------------------
16 // The large multi-purpose buffer, typically used to hold A/D samples,
17 // maybe pre-processed in some way.
20 //=============================================================================
21 // A buffer where we can queue things up to be sent through the FPGA, for
22 // any purpose (fake tag, as reader, whatever). We go MSB first, since that
23 // is the order in which they go out on the wire.
24 //=============================================================================
31 void BufferClear(void)
33 memset(BigBuf
,0,sizeof(BigBuf
));
34 DbpString("Buffer cleared");
37 void ToSendReset(void)
43 void ToSendStuffBit(int b
)
47 ToSend
[ToSendMax
] = 0;
52 ToSend
[ToSendMax
] |= (1 << (7 - ToSendBit
));
57 if(ToSendBit
>= sizeof(ToSend
)) {
59 DbpString("ToSendStuffBit overflowed!");
63 //=============================================================================
64 // Debug print functions, to go out over USB, to the usual PC-side client.
65 //=============================================================================
67 void DbpString(char *str
)
70 c
.cmd
= CMD_DEBUG_PRINT_STRING
;
72 memcpy(c
.d
.asBytes
, str
, c
.ext1
);
74 UsbSendPacket((BYTE
*)&c
, sizeof(c
));
75 // TODO fix USB so stupid things like this aren't req'd
79 void DbpIntegers(int x1
, int x2
, int x3
)
82 c
.cmd
= CMD_DEBUG_PRINT_INTEGERS
;
87 UsbSendPacket((BYTE
*)&c
, sizeof(c
));
92 void AcquireRawAdcSamples125k(BOOL at134khz
)
95 FpgaSendCommand(FPGA_CMD_SET_DIVISOR
, 88); //134.8Khz
96 FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER
| FPGA_LF_READER_USE_134_KHZ
);
98 FpgaSendCommand(FPGA_CMD_SET_DIVISOR
, 95); //125Khz
99 FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER
| FPGA_LF_READER_USE_125_KHZ
);
102 // Connect the A/D to the peak-detected low-frequency path.
103 SetAdcMuxFor(GPIO_MUXSEL_LOPKD
);
105 // Give it a bit of time for the resonant antenna to settle.
108 // Now set up the SSC to get the ADC samples that are now streaming at us.
111 // Now call the acquisition routine
112 DoAcquisition125k(at134khz
);
115 // split into two routines so we can avoid timing issues after sending commands //
116 void DoAcquisition125k(BOOL at134khz
)
118 BYTE
*dest
= (BYTE
*)BigBuf
;
119 int n
= sizeof(BigBuf
);
125 if(SSC_STATUS
& (SSC_STATUS_TX_READY
)) {
126 SSC_TRANSMIT_HOLDING
= 0x43;
129 if(SSC_STATUS
& (SSC_STATUS_RX_READY
)) {
130 dest
[i
] = (BYTE
)SSC_RECEIVE_HOLDING
;
138 DbpIntegers(dest
[0], dest
[1], at134khz
);
141 void ModThenAcquireRawAdcSamples125k(int delay_off
,int period_0
,int period_1
,BYTE
*command
)
145 // see if 'h' was specified
146 if(command
[strlen(command
) - 1] == 'h')
152 FpgaSendCommand(FPGA_CMD_SET_DIVISOR
, 88); //134.8Khz
153 FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER
| FPGA_LF_READER_USE_134_KHZ
);
155 FpgaSendCommand(FPGA_CMD_SET_DIVISOR
, 95); //125Khz
156 FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER
| FPGA_LF_READER_USE_125_KHZ
);
159 // Give it a bit of time for the resonant antenna to settle.
162 // Now set up the SSC to get the ADC samples that are now streaming at us.
165 // now modulate the reader field
166 while(*command
!= '\0' && *command
!= ' ')
168 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF
);
170 SpinDelayUs(delay_off
);
172 FpgaSendCommand(FPGA_CMD_SET_DIVISOR
, 88); //134.8Khz
173 FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER
| FPGA_LF_READER_USE_134_KHZ
);
175 FpgaSendCommand(FPGA_CMD_SET_DIVISOR
, 95); //125Khz
176 FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER
| FPGA_LF_READER_USE_125_KHZ
);
179 if(*(command
++) == '0')
180 SpinDelayUs(period_0
);
182 SpinDelayUs(period_1
);
184 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF
);
186 SpinDelayUs(delay_off
);
188 FpgaSendCommand(FPGA_CMD_SET_DIVISOR
, 88); //134.8Khz
189 FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER
| FPGA_LF_READER_USE_134_KHZ
);
191 FpgaSendCommand(FPGA_CMD_SET_DIVISOR
, 95); //125Khz
192 FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER
| FPGA_LF_READER_USE_125_KHZ
);
196 DoAcquisition125k(at134khz
);
199 //-----------------------------------------------------------------------------
200 // Read an ADC channel and block till it completes, then return the result
201 // in ADC units (0 to 1023). Also a routine to average 32 samples and
203 //-----------------------------------------------------------------------------
204 static int ReadAdc(int ch
)
208 ADC_CONTROL
= ADC_CONTROL_RESET
;
209 ADC_MODE
= ADC_MODE_PRESCALE(32) | ADC_MODE_STARTUP_TIME(16) |
210 ADC_MODE_SAMPLE_HOLD_TIME(8);
211 ADC_CHANNEL_ENABLE
= ADC_CHANNEL(ch
);
213 ADC_CONTROL
= ADC_CONTROL_START
;
214 while(!(ADC_STATUS
& ADC_END_OF_CONVERSION(ch
)))
216 d
= ADC_CHANNEL_DATA(ch
);
221 static int AvgAdc(int ch
)
226 for(i
= 0; i
< 32; i
++) {
230 return (a
+ 15) >> 5;
234 * Sweeps the useful LF range of the proxmark from
235 * 46.8kHz (divisor=255) to 600kHz (divisor=19) and
236 * reads the voltage in the antenna: the result is a graph
237 * which should clearly show the resonating frequency of your
238 * LF antenna ( hopefully around 90 if it is tuned to 125kHz!)
242 BYTE
*dest
= (BYTE
*)BigBuf
;
246 memset(BigBuf
,0,sizeof(BigBuf
));
248 FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER
);
249 for (i
=255; i
>19; i
--) {
250 FpgaSendCommand(FPGA_CMD_SET_DIVISOR
, i
);
252 dest
[i
] = (137500 * AvgAdc(4)) >> 18;
256 void MeasureAntennaTuning(void)
258 // Impedances are Zc = 1/(j*omega*C), in ohms
259 #define LF_TUNING_CAP_Z 1273 // 1 nF @ 125 kHz
260 #define HF_TUNING_CAP_Z 235 // 50 pF @ 13.56 MHz
262 int vLf125
, vLf134
, vHf
; // in mV
266 // Let the FPGA drive the low-frequency antenna around 125 kHz.
267 FpgaSendCommand(FPGA_CMD_SET_DIVISOR
, 95); //125Khz
268 FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER
| FPGA_LF_READER_USE_125_KHZ
);
271 // Vref = 3.3V, and a 10000:240 voltage divider on the input
272 // can measure voltages up to 137500 mV
273 vLf125
= (137500 * vLf125
) >> 10;
275 // Let the FPGA drive the low-frequency antenna around 134 kHz.
276 FpgaSendCommand(FPGA_CMD_SET_DIVISOR
, 88); //134.8Khz
277 FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER
| FPGA_LF_READER_USE_134_KHZ
);
280 // Vref = 3.3V, and a 10000:240 voltage divider on the input
281 // can measure voltages up to 137500 mV
282 vLf134
= (137500 * vLf134
) >> 10;
284 // Let the FPGA drive the high-frequency antenna around 13.56 MHz.
285 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR
);
288 // Vref = 3300mV, and an 10:1 voltage divider on the input
289 // can measure voltages up to 33000 mV
290 vHf
= (33000 * vHf
) >> 10;
292 c
.cmd
= CMD_MEASURED_ANTENNA_TUNING
;
293 c
.ext1
= (vLf125
<< 0) | (vLf134
<< 16);
295 c
.ext3
= (LF_TUNING_CAP_Z
<< 0) | (HF_TUNING_CAP_Z
<< 16);
296 UsbSendPacket((BYTE
*)&c
, sizeof(c
));
299 void SimulateTagLowFrequency(int period
)
302 BYTE
*tab
= (BYTE
*)BigBuf
;
304 FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_SIMULATOR
);
306 PIO_ENABLE
= (1 << GPIO_SSC_DOUT
) | (1 << GPIO_SSC_CLK
);
308 PIO_OUTPUT_ENABLE
= (1 << GPIO_SSC_DOUT
);
309 PIO_OUTPUT_DISABLE
= (1 << GPIO_SSC_CLK
);
311 #define SHORT_COIL() LOW(GPIO_SSC_DOUT)
312 #define OPEN_COIL() HIGH(GPIO_SSC_DOUT)
316 while(!(PIO_PIN_DATA_STATUS
& (1<<GPIO_SSC_CLK
))) {
331 while(PIO_PIN_DATA_STATUS
& (1<<GPIO_SSC_CLK
)) {
339 if(i
== period
) i
= 0;
343 // compose fc/8 fc/10 waveform
344 static void fc(int c
, int *n
) {
345 BYTE
*dest
= (BYTE
*)BigBuf
;
348 // for when we want an fc8 pattern every 4 logical bits
359 // an fc/8 encoded bit is a bit pattern of 11000000 x6 = 48 samples
361 for (idx
=0; idx
<6; idx
++) {
373 // an fc/10 encoded bit is a bit pattern of 1110000000 x5 = 50 samples
375 for (idx
=0; idx
<5; idx
++) {
390 // prepare a waveform pattern in the buffer based on the ID given then
391 // simulate a HID tag until the button is pressed
392 static void CmdHIDsimTAG(int hi
, int lo
)
396 HID tag bitstream format
397 The tag contains a 44bit unique code. This is sent out MSB first in sets of 4 bits
398 A 1 bit is represented as 6 fc8 and 5 fc10 patterns
399 A 0 bit is represented as 5 fc10 and 6 fc8 patterns
400 A fc8 is inserted before every 4 bits
401 A special start of frame pattern is used consisting a0b0 where a and b are neither 0
402 nor 1 bits, they are special patterns (a = set of 12 fc8 and b = set of 10 fc10)
406 DbpString("Tags can only have 44 bits.");
410 // special start of frame marker containing invalid bit sequences
411 fc(8, &n
); fc(8, &n
); // invalid
412 fc(8, &n
); fc(10, &n
); // logical 0
413 fc(10, &n
); fc(10, &n
); // invalid
414 fc(8, &n
); fc(10, &n
); // logical 0
417 // manchester encode bits 43 to 32
418 for (i
=11; i
>=0; i
--) {
419 if ((i
%4)==3) fc(0,&n
);
421 fc(10, &n
); fc(8, &n
); // low-high transition
423 fc(8, &n
); fc(10, &n
); // high-low transition
428 // manchester encode bits 31 to 0
429 for (i
=31; i
>=0; i
--) {
430 if ((i
%4)==3) fc(0,&n
);
432 fc(10, &n
); fc(8, &n
); // low-high transition
434 fc(8, &n
); fc(10, &n
); // high-low transition
439 SimulateTagLowFrequency(n
);
443 // loop to capture raw HID waveform then FSK demodulate the TAG ID from it
444 static void CmdHIDdemodFSK(void)
446 BYTE
*dest
= (BYTE
*)BigBuf
;
447 int m
=0, n
=0, i
=0, idx
=0, found
=0, lastval
=0;
450 FpgaSendCommand(FPGA_CMD_SET_DIVISOR
, 95); //125Khz
451 FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER
| FPGA_LF_READER_USE_125_KHZ
);
453 // Connect the A/D to the peak-detected low-frequency path.
454 SetAdcMuxFor(GPIO_MUXSEL_LOPKD
);
456 // Give it a bit of time for the resonant antenna to settle.
459 // Now set up the SSC to get the ADC samples that are now streaming at us.
474 if(SSC_STATUS
& (SSC_STATUS_TX_READY
)) {
475 SSC_TRANSMIT_HOLDING
= 0x43;
478 if(SSC_STATUS
& (SSC_STATUS_RX_READY
)) {
479 dest
[i
] = (BYTE
)SSC_RECEIVE_HOLDING
;
480 // we don't care about actual value, only if it's more or less than a
481 // threshold essentially we capture zero crossings for later analysis
482 if(dest
[i
] < 127) dest
[i
] = 0; else dest
[i
] = 1;
493 // sync to first lo-hi transition
494 for( idx
=1; idx
<m
; idx
++) {
495 if (dest
[idx
-1]<dest
[idx
])
501 // count cycles between consecutive lo-hi transitions, there should be either 8 (fc/8)
502 // or 10 (fc/10) cycles but in practice due to noise etc we may end up with with anywhere
503 // between 7 to 11 cycles so fuzz it by treat anything <9 as 8 and anything else as 10
504 for( i
=0; idx
<m
; idx
++) {
505 if (dest
[idx
-1]<dest
[idx
]) {
520 // we now have a set of cycle counts, loop over previous results and aggregate data into bit patterns
525 for( idx
=0; idx
<m
; idx
++) {
526 if (dest
[idx
]==lastval
) {
529 // a bit time is five fc/10 or six fc/8 cycles so figure out how many bits a pattern width represents,
530 // an extra fc/8 pattern preceeds every 4 bits (about 200 cycles) just to complicate things but it gets
531 // swallowed up by rounding
532 // expected results are 1 or 2 bits, any more and it's an invalid manchester encoding
533 // special start of frame markers use invalid manchester states (no transitions) by using sequences
536 n
=(n
+1)/6; // fc/8 in sets of 6
538 n
=(n
+1)/5; // fc/10 in sets of 5
540 switch (n
) { // stuff appropriate bits in buffer
543 dest
[i
++]=dest
[idx
-1];
546 dest
[i
++]=dest
[idx
-1];
547 dest
[i
++]=dest
[idx
-1];
549 case 3: // 3 bit start of frame markers
550 dest
[i
++]=dest
[idx
-1];
551 dest
[i
++]=dest
[idx
-1];
552 dest
[i
++]=dest
[idx
-1];
554 // When a logic 0 is immediately followed by the start of the next transmisson
555 // (special pattern) a pattern of 4 bit duration lengths is created.
557 dest
[i
++]=dest
[idx
-1];
558 dest
[i
++]=dest
[idx
-1];
559 dest
[i
++]=dest
[idx
-1];
560 dest
[i
++]=dest
[idx
-1];
562 default: // this shouldn't happen, don't stuff any bits
572 // final loop, go over previously decoded manchester data and decode into usable tag ID
573 // 111000 bit pattern represent start of frame, 01 pattern represents a 1 and 10 represents a 0
574 for( idx
=0; idx
<m
-6; idx
++) {
575 // search for a start of frame marker
576 if ( dest
[idx
] && dest
[idx
+1] && dest
[idx
+2] && (!dest
[idx
+3]) && (!dest
[idx
+4]) && (!dest
[idx
+5]) )
580 if (found
&& (hi
|lo
)) {
582 DbpIntegers(hi
, lo
, (lo
>>1)&0xffff);
589 if (dest
[idx
] && (!dest
[idx
+1]) ) {
592 } else if ( (!dest
[idx
]) && dest
[idx
+1]) {
602 if ( dest
[idx
] && dest
[idx
+1] && dest
[idx
+2] && (!dest
[idx
+3]) && (!dest
[idx
+4]) && (!dest
[idx
+5]) )
606 if (found
&& (hi
|lo
)) {
608 DbpIntegers(hi
, lo
, (lo
>>1)&0xffff);
619 void SimulateTagHfListen(void)
621 BYTE
*dest
= (BYTE
*)BigBuf
;
622 int n
= sizeof(BigBuf
);
627 // We're using this mode just so that I can test it out; the simulated
628 // tag mode would work just as well and be simpler.
629 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR
| FPGA_HF_READER_RX_XCORR_848_KHZ
| FPGA_HF_READER_RX_XCORR_SNOOP
);
631 // We need to listen to the high-frequency, peak-detected path.
632 SetAdcMuxFor(GPIO_MUXSEL_HIPKD
);
638 if(SSC_STATUS
& (SSC_STATUS_TX_READY
)) {
639 SSC_TRANSMIT_HOLDING
= 0xff;
641 if(SSC_STATUS
& (SSC_STATUS_RX_READY
)) {
642 BYTE r
= (BYTE
)SSC_RECEIVE_HOLDING
;
662 DbpString("simulate tag (now type bitsamples)");
665 void UsbPacketReceived(BYTE
*packet
, int len
)
667 UsbCommand
*c
= (UsbCommand
*)packet
;
670 case CMD_ACQUIRE_RAW_ADC_SAMPLES_125K
:
671 AcquireRawAdcSamples125k(c
->ext1
);
674 case CMD_MOD_THEN_ACQUIRE_RAW_ADC_SAMPLES_125K
:
675 ModThenAcquireRawAdcSamples125k(c
->ext1
,c
->ext2
,c
->ext3
,c
->d
.asBytes
);
678 case CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_15693
:
679 AcquireRawAdcSamplesIso15693();
686 case CMD_READER_ISO_15693
:
687 ReaderIso15693(c
->ext1
);
690 case CMD_SIMTAG_ISO_15693
:
691 SimTagIso15693(c
->ext1
);
694 case CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_14443
:
695 AcquireRawAdcSamplesIso14443(c
->ext1
);
698 case CMD_READ_SRI512_TAG
:
699 ReadSRI512Iso14443(c
->ext1
);
702 case CMD_READER_ISO_14443a
:
703 ReaderIso14443a(c
->ext1
);
706 case CMD_SNOOP_ISO_14443
:
710 case CMD_SNOOP_ISO_14443a
:
714 case CMD_SIMULATE_TAG_HF_LISTEN
:
715 SimulateTagHfListen();
718 case CMD_SIMULATE_TAG_ISO_14443
:
719 SimulateIso14443Tag();
722 case CMD_SIMULATE_TAG_ISO_14443a
:
723 SimulateIso14443aTag(c
->ext1
, c
->ext2
); // ## Simulate iso14443a tag - pass tag type & UID
726 case CMD_MEASURE_ANTENNA_TUNING
:
727 MeasureAntennaTuning();
730 case CMD_HID_DEMOD_FSK
:
731 CmdHIDdemodFSK(); // Demodulate HID tag
734 case CMD_HID_SIM_TAG
:
735 CmdHIDsimTAG(c
->ext1
, c
->ext2
); // Simulate HID tag by ID
738 case CMD_FPGA_MAJOR_MODE_OFF
: // ## FPGA Control
739 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF
);
741 LED_D_OFF(); // LED D indicates field ON or OFF
744 case CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K
:
745 case CMD_DOWNLOAD_RAW_BITS_TI_TYPE
: {
747 if(c
->cmd
== CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K
) {
748 n
.cmd
= CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K
;
750 n
.cmd
= CMD_DOWNLOADED_RAW_BITS_TI_TYPE
;
753 memcpy(n
.d
.asDwords
, BigBuf
+c
->ext1
, 12*sizeof(DWORD
));
754 UsbSendPacket((BYTE
*)&n
, sizeof(n
));
757 case CMD_DOWNLOADED_SIM_SAMPLES_125K
: {
758 BYTE
*b
= (BYTE
*)BigBuf
;
759 memcpy(b
+c
->ext1
, c
->d
.asBytes
, 48);
762 case CMD_SIMULATE_TAG_125K
:
764 SimulateTagLowFrequency(c
->ext1
);
776 case CMD_SET_LF_DIVISOR
:
777 FpgaSendCommand(FPGA_CMD_SET_DIVISOR
, c
->ext1
);
784 case CMD_SETUP_WRITE
:
785 case CMD_FINISH_WRITE
:
786 case CMD_HARDWARE_RESET
:
787 USB_D_PLUS_PULLUP_OFF();
790 RSTC_CONTROL
= RST_CONTROL_KEY
| RST_CONTROL_PROCESSOR_RESET
;
792 // We're going to reset, and the bootrom will take control.
798 DbpString("unknown command");
805 memset(BigBuf
,0,sizeof(BigBuf
));
815 // The FPGA gets its clock from us from PCK0 output, so set that up.
816 PIO_PERIPHERAL_B_SEL
= (1 << GPIO_PCK0
);
817 PIO_DISABLE
= (1 << GPIO_PCK0
);
818 PMC_SYS_CLK_ENABLE
= PMC_SYS_CLK_PROGRAMMABLE_CLK_0
;
819 // PCK0 is PLL clock / 4 = 96Mhz / 4 = 24Mhz
820 PMC_PROGRAMMABLE_CLK_0
= PMC_CLK_SELECTION_PLL_CLOCK
|
821 PMC_CLK_PRESCALE_DIV_4
;
822 PIO_OUTPUT_ENABLE
= (1 << GPIO_PCK0
);
825 SPI_CONTROL
= SPI_CONTROL_RESET
;
827 SSC_CONTROL
= SSC_CONTROL_RESET
;
829 // Load the FPGA image, which we have stored in our flash.
836 // test text on different colored backgrounds
837 LCDString(" The quick brown fox ", &FONT6x8
,1,1+8*0,WHITE
,BLACK
);
838 LCDString(" jumped over the ", &FONT6x8
,1,1+8*1,BLACK
,WHITE
);
839 LCDString(" lazy dog. ", &FONT6x8
,1,1+8*2,YELLOW
,RED
);
840 LCDString(" AaBbCcDdEeFfGgHhIiJj ", &FONT6x8
,1,1+8*3,RED
,GREEN
);
841 LCDString(" KkLlMmNnOoPpQqRrSsTt ", &FONT6x8
,1,1+8*4,MAGENTA
,BLUE
);
842 LCDString("UuVvWwXxYyZz0123456789", &FONT6x8
,1,1+8*5,BLUE
,YELLOW
);
843 LCDString("`-=[]_;',./~!@#$%^&*()", &FONT6x8
,1,1+8*6,BLACK
,CYAN
);
844 LCDString(" _+{}|:\\\"<>? ",&FONT6x8
,1,1+8*7,BLUE
,MAGENTA
);
847 LCDFill(0, 1+8* 8, 132, 8, BLACK
);
848 LCDFill(0, 1+8* 9, 132, 8, WHITE
);
849 LCDFill(0, 1+8*10, 132, 8, RED
);
850 LCDFill(0, 1+8*11, 132, 8, GREEN
);
851 LCDFill(0, 1+8*12, 132, 8, BLUE
);
852 LCDFill(0, 1+8*13, 132, 8, YELLOW
);
853 LCDFill(0, 1+8*14, 132, 8, CYAN
);
854 LCDFill(0, 1+8*15, 132, 8, MAGENTA
);
864 void SpinDelayUs(int us
)
866 int ticks
= (48*us
) >> 10;
868 // Borrow a PWM unit for my real-time clock
869 PWM_ENABLE
= PWM_CHANNEL(0);
870 // 48 MHz / 1024 gives 46.875 kHz
871 PWM_CH_MODE(0) = PWM_CH_MODE_PRESCALER(10);
872 PWM_CH_DUTY_CYCLE(0) = 0;
873 PWM_CH_PERIOD(0) = 0xffff;
875 WORD start
= (WORD
)PWM_CH_COUNTER(0);
878 WORD now
= (WORD
)PWM_CH_COUNTER(0);
879 if(now
== (WORD
)(start
+ ticks
)) {
886 void SpinDelay(int ms
)
888 int ticks
= (48000*ms
) >> 10;
890 // Borrow a PWM unit for my real-time clock
891 PWM_ENABLE
= PWM_CHANNEL(0);
892 // 48 MHz / 1024 gives 46.875 kHz
893 PWM_CH_MODE(0) = PWM_CH_MODE_PRESCALER(10);
894 PWM_CH_DUTY_CYCLE(0) = 0;
895 PWM_CH_PERIOD(0) = 0xffff;
897 WORD start
= (WORD
)PWM_CH_COUNTER(0);
900 WORD now
= (WORD
)PWM_CH_COUNTER(0);
901 if(now
== (WORD
)(start
+ ticks
)) {