forked from CorshamTech/SD-drive
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSD_Drive.ino
954 lines (751 loc) · 31.1 KB
/
SD_Drive.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
//=============================================================================
// This sketch provides an interface between older 8 bit processors and an
// SD card via a parallel port. This is not a perfectly clean implementation
// and can be improved, but it's good enough to get the job done.
//
// This came about when I was building an SWTPC clone system for Corsham
// Technologies and people kept asking if I was going to design a disk controller
// card. I looked at existing designs but decided there were better modern
// technologies and decided to use a micro SD card instead. It would allow
// a modern computer to put files to/from the SD, and use the common DSK file
// format to access the large number of available collections of files for
// older machines.
//
// This uses a Link class object to communicate with the other device. This
// was initially a parallel connection but can be almost any kind of data
// path.
//
// For terminology, the other processor is the host, and the Arduino (this
// code) is the slave. The host drives all communications and controls
// the flow of data.
//
// This was originally done on a basic Arduino with an ATMEGA328 but it was
// a constant battle to reduce RAM usage, so I switched to a MEGA instead.
// The code is a bit easier to follow now, and a lot more functionality
// can be added as desired without running out of code space. However, you'll
// notice inconsistent coding styles as I was trying to make this lean-and-mean
// at first, but then starting doing things a bit cleaner later. Feel free
// to clean up code or make it better... please!
//
// Legal mumbo-jumbo: I don't really follow all the current licensing
// nonsense, so I'll just clearly state my desires: This code includes
// other open code, such as the Arduino SD library. Observe their
// requirements. I simply ask: (A) anything derived from this code must
// include my name/email as the original author, (B) any commerical use must
// make the current code available for free to users, and (C) I'd like an
// email letting me know what you're doing with the code, what platforms
// you're using it on, etc... it's fun to see how others are using it!
//
// Note that the protocol used between the host and this code is The
// Remote Disk Protocol Guide, available for download from the
// Corsham Tech website.
//
// This is NOT a complete implementation of the protocol! Know problems
// (but there are probably others):
//
// * Only supports 256 byte sectors
// * RTC messages do not handle fields with values of FF nor the century byte
//
// 04/11/2014 - Bob Applegate, Corsham Technologies. bob@corshamtech.com
// wwww.corshamtech.com
//
// Revision 1.1 enhancements:
// * Supports new SD Shield from Corsham Technologies, which has more
// pins brought to the parallel port, interrupt capabilities, option
// switches, etc.
//
// Revision 1.2:
// * Now supports the new sector read/write LONG commands in the
// protocol guide rev 1.1. Also added the ability to boot from an
// alternate config file.
//
// Revision 1.3:
// * General code clean-up. Added debounce code for all digital inputs.
//
// Revision 1.4:
// * Fixed bug that caused all DSK file updates to be appended rather than applied
// to the requested sector. Removed a bit of excess debugging output.
#include <Arduino.h>
#ifdef USE_SDFAT
#include <SdFat.h>
#include <SdFatConfig.h>
#include <SysCall.h>
#endif
#include <SPI.h>
#include <SD.h>
#include "Link.h"
#include "Disks.h"
#include "UserInt.h"
#include <Wire.h>
#include "RTC.h"
#include "Errors.h"
#include "SdFuncs.h"
// Debugging options. They usually produce lots of serial output so be careful what you turn on.
#undef DEBUG_SECTOR_READ
#undef DEBUG_DIR
#undef DEBUG_FILE_READ
#undef DEBUG_FILE_WRITE
#undef DEBUG_SAVE_CONFIG
#undef DEBUG_SET_TIMER
// This is the speed of the faster timer processing, expressed in milliseconds.
#define FAST_POLL_DELAY 10 // 1/100th of a second
// This sets the intervale between the passes through the poll functions, expressed
// in milliseconds.
#define POLL_DELAY 100 // 1/10th of a second
// Number of times an input pin must be the same before it is counted.
#define DEBOUNCE_COUNT 5
// The link to the remote system.
Link *link;
// This is the collection of disks
Disks *disks;
// This is the user interface. Its a singleton.
UserInt *uInt;
// The real time clock
RTC *rtc;
static unsigned long nextPoll;
// The pins used on the newer SD Shields.
#define NEW_SHIELD_PIN 38
#define OPTION_1_PIN 42
#define OPTION_2_PIN 41
#define OPTION_3_PIN 40
#define OPTION_4_PIN 39
#define TIMER_OUT_PIN 43 //maps to pin 18, CB1
// Counters used for the timer.
unsigned int timerValue = 0;
unsigned int timerCount = 0;
int pollCounter = 0;
//=============================================================================
// This is the usual Arduino setup function. Do initialization, then return.
void setup()
{
Serial.begin(9600);
Serial.println("");
Serial.println("SD Drive version 1.4");
Serial.println("Brought to you by Bob Applegate and Corsham Technologies");
Serial.println("bob@corshamtech.com, www.corshamtech.com");
// Start up the UI soon so it can display some initial info
// while the rest of the system comes up.
uInt = UserInt::getInstance();
pinMode(SD_PIN, OUTPUT); // required by SD library
// Configure the new option pins as inputs with pull-ups
pinMode(NEW_SHIELD_PIN, INPUT_PULLUP);
pinMode(OPTION_1_PIN, INPUT_PULLUP);
pinMode(OPTION_2_PIN, INPUT_PULLUP);
pinMode(OPTION_3_PIN, INPUT_PULLUP);
pinMode(OPTION_4_PIN, INPUT_PULLUP);
// Configure new output pins
pinMode(TIMER_OUT_PIN, OUTPUT);
int WhichConfigFile = CONFIG_FILE_PRIMARY;
// If it's a new board with the option switches, display the setting
// of the switches.
if (isNewBoard())
{
Serial.println("This is a new SD Shield");
Serial.println("Configuration switch settings:");
Serial.print("Config file: ");
Serial.println(debounceInputPin(OPTION_1_PIN) ? "Default" : "Alternate");
if (!debounceInputPin(OPTION_1_PIN))
{
WhichConfigFile = CONFIG_FILE_ALTERNATE;
}
Serial.print("Option 2: ");
Serial.println(debounceInputPin(OPTION_2_PIN) ? "Off" : "On");
Serial.print("Option 3: ");
Serial.println(debounceInputPin(OPTION_3_PIN) ? "Off" : "On");
Serial.print("Option 4: ");
Serial.println(debounceInputPin(OPTION_4_PIN) ? "Off" : "On");
}
link = new Link();
link->begin();
disks = new Disks();
disks->mountDefaults(WhichConfigFile);
Wire.begin();
rtc = new RTC();
// The timers are based on a fairly fast timer and everything is
// derived from it. This sets up the initial timer time-out.
nextPoll = millis() + FAST_POLL_DELAY;
pollCounter = 0;
freeRam("Initialization complete");
}
//=============================================================================
// Displays the amount of free memory to the serial port. Useful for debugging
// but will be removed eventually.
int freeRam(char *text)
{
extern int __heap_start, *__brkval;
int v;
int freemem = (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
Serial.print(text);
Serial.print(" - Free memory: ");
Serial.println(freemem);
}
//=============================================================================
// This is the main loop. Loop gets called over and over by the Arduino
// framework, so it can return and get called again, or just have an infinite
// loop.
void loop()
{
// Poll for any incoming commands. If there is something to process,
// go ahead and handle it now. There is no time delay here; check often
// and process incoming messages ASAP.
if (link->poll())
{
Event *ep = link->getEvent(); // this waits for an event
bool deleteEvent = processEvent(ep);
// If the event isn't needed, delete it. Some events need
// responses sent back and the event is re-used.
if (deleteEvent)
{
link->freeAnEvent(ep);
}
}
// See if it's time to poll the various subsystems. This is a
// slow poll, so put high speed polling before this logic.
if (nextPoll <= millis())
{
nextPoll = millis() + FAST_POLL_DELAY;
// See if there is an active real time timer running
// and process it if so.
if (timerValue)
{
if (++timerCount >= timerValue)
{
timerCount = 0; // reset counter
//digitalWrite(TIMER_OUT_PIN, !debounceInputPin(TIMER_OUT_PIN));
digitalWrite(TIMER_OUT_PIN, LOW);
digitalWrite(TIMER_OUT_PIN, HIGH);
}
}
// Now see if it's time to do the slow poll logic. This should
// always be 100 ms. Again, there is no guarantee about jitter,
// but this should be close enough for most cases.
if (++pollCounter >= POLL_DELAY / FAST_POLL_DELAY)
{
// Poll the various subsystems
uInt->poll(); // user interface
disks->poll(); // disk subsystem
pollCounter = 0;
}
}
}
//=============================================================================
// This processes an event that came from the host. Returns a flag indicating
// if the event should be freed or not. If the event is reused, returns false.
// Else returns true indicating the caller should dispose of the event.
static bool processEvent(Event *ep)
{
bool deleteEvent = false; // assume event should not be deleted
// Now process the event based on the type.
switch (ep->getType())
{
case EVT_GET_DIRECTORY:
#ifdef DEBUG_DIR
Serial.println("Got GET DIRECTORY");
#endif
link->freeAnEvent(ep); // free it up so sendDirectory can use it
sendDirectory();
break;
case EVT_TYPE_FILE:
#ifdef DEBUG_FILE_READ
Serial.print("Got TYPE FILE: ");
#endif
Serial.println((char *)(ep->getData()));
openFileForRead(ep);
break;
case EVT_SEND_DATA:
#ifdef DEBUG_FILE_READ
Serial.println("Main loop got SEND_DATA");
#endif
// they want bytes from the open file
nextDataBlock(ep);
break;
case EVT_GET_MOUNTED:
//Serial.println("Got request for mounted drives");
link->freeAnEvent(ep); // free it up for sendMounted to use
sendMounted();
break;
case EVT_MOUNT:
{
byte *bptr = ep->getData(); // pointer to data
byte drive = *bptr++; // drive number
byte readonly = *bptr++; // read-only flag
if (disks->mount(drive, (char *)bptr, readonly))
{
ep->clean(EVT_ACK);
}
else
{
ep->clean(EVT_NAK);
ep->addByte(disks->getErrorCode());
}
link->sendEvent(ep);
break;
}
case EVT_UNMOUNT: // unmount a drive. One more byte which is the drive number
{
byte *bptr = ep->getData();
disks->unmount(*bptr);
ep->clean(EVT_ACK);
link->sendEvent(ep);
break;
}
case EVT_READ_SECTOR:
readSector(ep);
break;
case EVT_READ_SECTOR_LONG:
readSectorLong(ep);
break;
case EVT_WRITE_SECTOR:
writeSector(ep);
break;
case EVT_WRITE_SECTOR_LONG:
writeSectorLong(ep);
break;
case EVT_GET_STATUS:
getDriveStatus(ep);
break;
case EVT_GET_VERSION:
{
ep->clean(EVT_VERSION_INFO); // same event type but clear all other data
byte *ptr = ep->getData();
strcpy((char *)ptr, "Corsham Technology\r\nv1.4");
link->sendEvent(ep);
break;
}
case EVT_GET_CLOCK:
//Serial.println("Got a clock request");
ep->clean(EVT_CLOCK_DATA);
if (rtc->getClock(ep->getData()) == false)
{
// Failed to get the data, so send back an error.
ep->clean(EVT_NAK);
ep->addByte(ERR_DEVICE_NOT_PRESENT);
}
link->sendEvent(ep);
break;
case EVT_SET_CLOCK:
//Serial.println("Got a set clock request");
if (rtc->setClock(ep->getData()))
{
ep->clean(EVT_ACK);
}
else
{
ep->clean(EVT_NAK);
ep->addByte(ERR_DEVICE_NOT_PRESENT);
}
link->sendEvent(ep);
break;
case EVT_DONE:
// Close any open file.
closeFiles();
deleteEvent = true;
break;
case EVT_WRITE_FILE:
openFileForWrite(ep);
break;
case EVT_WRITE_BYTES:
writeBytes(ep);
break;
case EVT_SAVE_CONFIG:
#ifdef DEBUG_SAVE_CONFIG
Serial.println("Got request to save configuration file");
#endif
if (disks->saveConfig())
{
ep->clean(EVT_ACK);
}
else
{
ep->clean(EVT_NAK);
ep->addByte(ERR_WRITE_ERROR);
}
link->sendEvent(ep);
break;
case EVT_SET_TIMER:
{
if (isNewBoard())
{
byte *bptr = ep->getData(); // pointer to data
byte interval = *bptr++;
setTimerValue(interval);
ep->clean(EVT_ACK); // new boards support timers
}
else
{
ep->clean(EVT_NAK);
ep->addByte(ERR_NOT_IMPLEMENTED);
}
link->sendEvent(ep);
break;
}
default:
// All the unwanted toys end up here. Maybe a garbage Event,
// maybe an old type, or one we haven't implemented yet.
//
// This recovery logic should be re-worked. One solution might
// be to grab/discard bytes until the DIRECTION line changes
// state, then send back a NAK.
Serial.print("processEvent: Got unknown event type: 0x");
Serial.println(ep->getType(), HEX);
deleteEvent = true;
break;
}
return deleteEvent;
}
//=============================================================================
// This handles a request to read a disk sector where there are tracks and
// sectors. The data structure contains a standard header with five bytes of
// data: (1) Drive number, 0 to 3, (2) sector size (coded), (3) track number,
// zero based, (3) sector number, 0 based, and sectors per track (actual value,
// one based).
static void readSector(Event *ep)
{
byte *bptr = ep->getData(); // start of arguments
byte drive = *bptr++;
byte sectorSize = *bptr++; // note used for now
unsigned long track = (unsigned long)(*bptr++);
//Serial.println(*bptr);
unsigned long sector = (unsigned long)(*bptr++);
unsigned long sectorsPerTrack = (unsigned long)(*bptr++);
// NOTE: Need to add checks for valid drive, track, sector, etc
// Compute the offset. Very simple offset calculation.
unsigned long offset = ((track * sectorsPerTrack) + sector) * SECTOR_SIZE;
// Now prepare the event for sending back the data. Same event type,
// but get rid of the old data and replace with exactly SECTOR_SIZE
// number of bytes.
#ifdef DEBUG_SECTOR_READ
Serial.print("readSector drive ");
Serial.print(drive);
Serial.print(", track 0x");
Serial.print(track, HEX);
Serial.print(", sector 0x");
Serial.print(sector, HEX);
Serial.print(", sec/track 0x");
Serial.print(sectorsPerTrack, HEX);
Serial.print(", offset 0x");
Serial.print(offset >> 16, HEX);
Serial.print(offset & 0xffff, HEX);
Serial.print(" first two: ");
#endif // DEBUG_SECTOR_READ
byte *ptr = ep->getData();
*ptr++ = 2; // sector size 256 bytes
ep->clean(EVT_READ_SECTOR); // same event type but clear all other data
if (disks->read(drive, offset, ptr) == false)
{
ep->clean(EVT_NAK); // send error status
ep->addByte(disks->getErrorCode());
}
#ifdef DEBUG_SECTOR_READ
else
{
Serial.print(ptr[0], HEX);
Serial.print(" ");
Serial.println(ptr[1], HEX);
}
#endif
// send back something
link->sendEvent(ep);
}
//=============================================================================
// This handles a request to read a disk sector where only a large sector
// number is provided. The data structure contains a standard header with six
// bytes of data: (1) Drive number, 0 to X, (2) sector size (coded), then a
// four byte sector number with the MSB first.
static void readSectorLong(Event *ep)
{
byte *bptr = ep->getData(); // start of arguments
byte drive = *bptr++;
byte sectorSize = *bptr++; // note used for now
unsigned long sector = (unsigned long)(*bptr++);
sector <<= 8;
sector |= (unsigned long)(*bptr++);
sector <<= 8;
sector |= (unsigned long)(*bptr++);
sector <<= 8;
sector |= (unsigned long)(*bptr++);
// NOTE: Need to add checks for valid drive, sector, etc
// Compute the offset. Very simple offset calculation.
unsigned long offset = sector * SECTOR_SIZE;
// Now prepare the event for sending back the data. Same event type,
// but get rid of the old data and replace with exactly SECTOR_SIZE
// number of bytes.
#ifdef DEBUG_SECTOR_READ
Serial.print("readSectorLong drive ");
Serial.print(drive);
Serial.print(", sector 0x");
Serial.print(sector, HEX);
Serial.print(", offset 0x");
//Serial.print(offset >> 16, HEX);
Serial.print(offset, HEX);
Serial.print(" first two: ");
#endif // DEBUG_SECTOR_READ
byte *ptr = ep->getData();
*ptr++ = 2; // sector size 256 bytes
ep->clean(EVT_READ_SECTOR); // same event type but clear all other data
if (disks->read(drive, offset, ptr) == false)
{
ep->clean(EVT_NAK); // send error status
ep->addByte(disks->getErrorCode());
}
#ifdef DEBUG_SECTOR_READ
else
{
Serial.print(ptr[0], HEX);
Serial.print(" ");
Serial.println(ptr[1], HEX);
}
#endif
// send back something
link->sendEvent(ep);
}
//=============================================================================
// This handles a request to write a disk sector using only a long sector
// number. The data structure contains a standard header with six bytes of
// data: (1) Drive number, 0 to 3, (2) sector size (coded), bytes 3 to 6
// contain the long sector number, MSB first, zero based.
//
// That is followed by one sector's worth of data to be written.
static void writeSectorLong(Event *ep)
{
// Pull off the arguments from the start of the message
byte *bptr = ep->getData(); // start of arguments
byte drive = *bptr++;
byte sectorSize = *bptr++; // note used for now
unsigned long sector = (unsigned long)(*bptr++);
sector <<= 8;
sector |= (unsigned long)(*bptr++);
sector <<= 8;
sector |= (unsigned long)(*bptr++);
sector <<= 8;
sector |= (unsigned long)(*bptr++);
// NOTE: Need to add checks for valid drive, sector, etc
// Compute the offset. Very simple offset calculation.
unsigned long offset = sector * SECTOR_SIZE;
// Dump the data
#ifdef LOG_WRITE
Serial.print("writeSectorLong drive ");
Serial.print(drive);
Serial.print(", offset 0x");
Serial.print(offset >> 16, HEX);
Serial.println(offset & 0xffff, HEX);
#endif // LOG_WRITE
if (disks->write(drive, offset, bptr))
{
ep->clean(EVT_ACK);
}
else
{
ep->clean(EVT_NAK); // send error status
ep->addByte(disks->getErrorCode());
Serial.print("got write error: ");
Serial.println(disks->getErrorCode());
}
// no error handling at all!
link->sendEvent(ep);
}
//=============================================================================
// This handles a request to write a disk sector using tracks and sectors. The
// data structure contains a standard header with five bytes of data: (1) Drive
// number, 0 to 3, (2) sector size (coded), (3) track number, zero based, (3)
// sector number, 0 based, and sectors per track (actual value, one based).
//
// That is followed by one sector's worth of data to be written.
static void writeSector(Event *ep)
{
// Pull off the arguments from the start of the message
byte *bptr = ep->getData(); // start of arguments
byte drive = *bptr++;
byte sectorSize = *bptr++; // note used for now
unsigned long track = (unsigned long)(*bptr++);
unsigned long sector = (unsigned long)(*bptr++);
unsigned long sectorsPerTrack = (unsigned long)(*bptr++);
// bptr is now pointing to the start of the user data to be written.
// NOTE: Need to add checks for valid drive, sector, etc
// Compute the offset. Very simple offset calculation.
unsigned long offset = ((track * sectorsPerTrack) + sector) * SECTOR_SIZE;
// Dump the data
#ifdef LOG_WRITE
Serial.print("writeSector drive ");
Serial.print(drive);
Serial.print(", track ");
Serial.print(track);
Serial.print(", sector ");
Serial.print(sector);
Serial.print(", sec/track ");
Serial.print(sectorsPerTrack);
Serial.print(", offset 0x");
Serial.print(offset >> 16, HEX);
Serial.println(offset & 0xffff, HEX);
#endif // LOG_WRITE
if (disks->write(drive, offset, bptr))
{
ep->clean(EVT_ACK);
}
else
{
ep->clean(EVT_NAK); // send error status
ep->addByte(disks->getErrorCode());
Serial.print("got write error: ");
Serial.println(disks->getErrorCode());
}
// no error handling at all!
link->sendEvent(ep);
}
//=============================================================================
// This gets a disk drive's status, such as whether it's available or not,
// read-only, etc.
static void getDriveStatus(Event *ep)
{
byte *bptr = ep->getData(); // start of arguments
byte drive = *bptr++;
byte result = disks->getStatus(drive);
ep->clean(EVT_DISK_STATUS);
ep->addByte(result);
link->sendEvent(ep);
}
//=============================================================================
// Send a list of all mounted drives
void sendMounted(void)
{
Event *eptr;
char *cptr;
for (int i = 0; i < MAX_DISKS; i++)
{
eptr = link->getAnEvent();
eptr->clean(EVT_MOUNTED);
// add the info about this mounted drive
eptr->addByte((byte)i); // drive number
// if the drive is mounted, then fill in all the details.
if (disks->isMounted(i))
{
eptr->addByte(disks->isReadOnly(i));
cptr = disks->getFilename(i);
while (*cptr)
{
eptr->addByte(*cptr++);
}
eptr->addByte(0);
}
else // not mounted so indicate so
{
eptr->addByte(0);
eptr->addByte(0);
}
link->sendEvent(eptr);
}
// Indicate all the drives were sent
eptr = link->getAnEvent();
eptr->clean(EVT_DIR_END);
link->sendEvent(eptr);
}
//=============================================================================
// This does a simple hex dump to the serial port. On entry, this is given a
// pointer to the data and the number of bytes to dump. Very simplistic
// function that has no features.
void hexdump(unsigned char *ptr, unsigned int size)
{
unsigned int offset = 0;
while (size)
{
Serial.print(*ptr++, HEX);
Serial.print(" ");
size--;
}
Serial.println("");
}
//=============================================================================
// Given a standard disk sector size code, convert to the actual number and
// return it. If no valid value is provided, this always returns 256.
unsigned int getSectorSize(byte code)
{
unsigned int size = 256; // default size
switch (code)
{
case 1:
size = 128;
break;
case 2:
size = 256;
break;
case 3:
size = 512;
break;
case 4:
size = 1024;
break;
default:
Serial.print("Got bad sector size value: ");
Serial.println(code);
}
return size;
}
//=============================================================================
// This returns non-zero for the newer shield, zero for the older version.
bool isNewBoard(void)
{
// This pin is pulled high for the older boards but is pulled low
// for new boards.
return !debounceInputPin(NEW_SHIELD_PIN);
}
//=============================================================================
// Sets a timer. On entry, the interval specifies the time:
//
// 0 = disable timer
// 1 = 10 ms
// 2 = 20 ms
// 3 = 30 ms
// 4 = 40 ms
// 5 = 50 ms
// 6 = 100 ms
// 7 = 250 ms
// 8 = 500 ms
// 9 = 1 second
//
// Any other value will be ignored.
//
// This sets the timerValue and also resets the timerCount value. Before
// calling, the user should verify this shield supports the timer.
void setTimerValue(unsigned char interval)
{
unsigned int values[] = { 0, 1, 2, 3, 4, 5, 10, 25, 50, 100 };
if (interval < (sizeof(values) / sizeof(unsigned int)))
{
timerValue = values[interval];
#ifdef DEBUG_SET_TIMER
Serial.print("setTimerValue(");
Serial.print(interval);
Serial.print(") set timerValue to ");
Serial.println(timerValue);
#endif
}
#ifdef DEBUG_SET_TIMER
else
{
Serial.print("setTimerValue got illegal value: ");
Serial.println(interval);
}
#endif
}
//=============================================================================
// Given an input pin number, read and debounce it. Returns the final
// debounced value. It must be the same value for DEBOUNCE_COUNT times.
bool debounceInputPin(int pin)
{
bool val, last; // it's okay not to initialize them
int goodCount = 0;
do
{
if ((last = digitalRead(pin)) == val)
{
goodCount++;
}
else
{
val = last; // new value
goodCount = 0; // start counting again
}
}
while (goodCount < DEBOUNCE_COUNT);
return val;
}