2 * Mega + USB storage + optional DS1307 + optional expansion RAM + funky status LED,
3 * Includes interactive debug level setting, and supports hot-plug.
5 * IMPORTANT! PLEASE USE Arduino 1.0.5 or better!
6 * Older versions HAVE MAJOR BUGS AND WILL NOT WORK AT ALL!
7 * Use of gcc-avr and lib-c that is newer than the Arduino version is even better.
8 * If you experience random crashes, use make.
9 * The options that the IDE use can generate bad code and cause the AVR to crash.
11 * This sketch requires the following libraries:
12 * https://github.com/felis/USB_Host_Shield_2.0 Install as 'USB_Host_Shield_2_0'
13 * https://github.com/xxxajk/xmem2 Install as 'xmem', provides memory services.
14 * https://github.com/xxxajk/generic_storage provides access to FAT file system.
15 * https://github.com/xxxajk/RTClib provides access to DS1307, or fake clock.
17 * Optional, to use the Makefile (Recommended! See above!):
18 * https://github.com/xxxajk/Arduino_Makefile_master
22 /////////////////////////////////////////////////////////////
24 // This section is for info with the Arduino IDE ONLY. //
25 // Unfortunately due to short sightedness of the Arduino //
26 // code team, that you must set the following in the //
27 // respective libraries. //
28 // Changing them here will have _NO_ effect! //
29 /////////////////////////////////////////////////////////////
31 // Uncomment to enable debugging
32 //#define DEBUG_USB_HOST
33 // This is where stderr/USB debugging goes to
34 //#define USB_HOST_SERIAL Serial3
36 // If you have external memory, setting this to 0 enables FAT table caches.
37 // The 0 setting is recommended only if you have external memory.
44 /////////////////////////////////////////////////////////////
45 // End of Arduino IDE specific information //
46 /////////////////////////////////////////////////////////////
48 // You can set this to 0 if you are not using a USB hub.
49 // It will save a little bit of flash and RAM.
50 // Set to 1 if you want to use a hub.
51 #define WANT_HUB_TEST 1
54 #define EXT_RAM_STACK 1
55 #define EXT_RAM_HEAP 1
58 #if defined(CORE_TEENSY) && !defined(_AVR_)
60 #include <spi4teensy3.h>
66 #elif defined(ARDUINO_ARCH_SAM)
76 #include <masstorage.h>
78 #include <PCpartition/PCPartition.h>
79 #include <avr/interrupt.h>
83 static FILE tty_stdio;
84 static FILE tty_stderr;
85 volatile uint32_t LEDnext_time; // fade timeout
86 volatile uint32_t HEAPnext_time; // when to print out next heap report
87 volatile int brightness = 0; // how bright the LED is
88 volatile int fadeAmount = 80; // how many points to fade the LED by
93 volatile uint8_t current_state = 1;
94 volatile uint8_t last_state = 0;
95 volatile bool fatready = false;
96 volatile bool partsready = false;
97 volatile bool notified = false;
98 volatile bool runtest = false;
99 volatile bool usbon = false;
100 volatile uint32_t usbon_time;
101 volatile bool change = false;
102 volatile bool reportlvl = false;
108 USBHub *Hubs[MAX_HUBS];
111 static PFAT *Fats[_VOLUMES];
112 static part_t parts[_VOLUMES];
113 static storage_t sto[_VOLUMES];
115 /*make sure this is a power of two. */
117 static uint8_t My_Buff_x[mbxs]; /* File read buffer */
121 #define prescale1 ((1 << WGM12) | (1 << CS10))
122 #define prescale8 ((1 << WGM12) | (1 << CS11))
123 #define prescale64 ((1 << WGM12) | (1 << CS10) | (1 << CS11))
124 #define prescale256 ((1 << WGM12) | (1 << CS12))
125 #define prescale1024 ((1 << WGM12) | (1 << CS12) | (1 << CS10))
128 extern unsigned int freeHeap();
130 static int tty_stderr_putc(char c, FILE *t) {
131 USB_HOST_SERIAL.write(c);
135 static int __attribute__((unused)) tty_stderr_flush(FILE *t) {
136 USB_HOST_SERIAL.flush();
140 static int tty_std_putc(char c, FILE *t) {
145 static int tty_std_getc(FILE *t) {
146 while(!Serial.available());
147 return Serial.read();
150 static int __attribute__((unused)) tty_std_flush(FILE *t) {
156 // Supposedly the DUE has stdio already pointing to serial...
157 #if !defined(ARDUINO_ARCH_SAM)
158 // But newlib needs this...
160 int _write(int fd, const char *ptr, int len) {
162 for(j = 0; j < len; j++) {
164 Serial.write(*ptr++);
166 USB_HOST_SERIAL.write(*ptr++);
171 int _read(int fd, char *ptr, int len) {
172 if(len > 0 && fd == 0) {
173 while(!Serial.available());
174 *ptr = Serial.read();
180 #include <sys/stat.h>
182 int _fstat(int fd, struct stat *st) {
183 memset(st, 0, sizeof (*st));
184 st->st_mode = S_IFCHR;
185 st->st_blksize = 1024;
189 int _isatty(int fd) {
190 return (fd < 3) ? 1 : 0;
193 #endif // !defined(ARDUINO_ARCH_SAM)
198 for(int i = 0; i < _VOLUMES; i++) {
200 sto[i].private_data = new pvt_t;
201 ((pvt_t *)sto[i].private_data)->B = 255; // impossible
203 // Set this to higher values to enable more debug information
204 // minimum 0x00, maximum 0xff
207 #if !defined(CORE_TEENSY) && defined(__AVR__)
208 // make LED pin as an output:
209 pinMode(LED_BUILTIN, OUTPUT);
212 _SFR_BYTE(UCSR0B) &= ~_BV(TXEN0);
213 // Initialize 'debug' serial port
214 USB_HOST_SERIAL.begin(115200);
215 // Do not start primary Serial port if already started.
216 if(bit_is_clear(UCSR0B, TXEN0)) {
217 Serial.begin(115200);
224 analogWrite(LED_BUILTIN, 255);
226 analogWrite(LED_BUILTIN, 0);
230 Serial.begin(115200); // On the Teensy 3.x we get a delay at least!
233 // Set up stdio/stderr
234 tty_stdio.put = tty_std_putc;
235 tty_stdio.get = tty_std_getc;
236 tty_stdio.flags = _FDEV_SETUP_RW;
239 tty_stderr.put = tty_stderr_putc;
240 tty_stderr.get = NULL;
241 tty_stderr.flags = _FDEV_SETUP_WRITE;
242 tty_stderr.udata = 0;
246 stderr = &tty_stderr;
248 printf_P(PSTR("\r\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nStart\r\n"));
249 printf_P(PSTR("Current UsbDEBUGlvl %02x\r\n"), UsbDEBUGlvl);
250 printf_P(PSTR("'+' and '-' increase/decrease by 0x01\r\n"));
251 printf_P(PSTR("'.' and ',' increase/decrease by 0x10\r\n"));
252 printf_P(PSTR("'t' will run a 10MB write/read test and print out the time it took.\r\n"));
253 printf_P(PSTR("'e' will toggle vbus off for a few moments.\r\n\r\n"));
254 printf_P(PSTR("Long filename support: "
262 fprintf_P(stderr, PSTR("\r\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nStart\r\n"));
263 fprintf_P(stderr, PSTR("Current UsbDEBUGlvl %02x\r\n"), UsbDEBUGlvl);
264 fprintf_P(stderr, PSTR("Long filename support: "
273 #if !defined(CORE_TEENSY) && defined(__AVR__)
274 analogWrite(LED_BUILTIN, 255);
276 analogWrite(LED_BUILTIN, 0);
278 analogWrite(LED_BUILTIN, 255);
280 analogWrite(LED_BUILTIN, 0);
282 analogWrite(LED_BUILTIN, 255);
284 analogWrite(LED_BUILTIN, 0);
287 LEDnext_time = millis() + 1;
289 printf_P(PSTR("Total EXT RAM banks %i\r\n"), xmem::getTotalBanks());
291 printf_P(PSTR("Available heap: %u Bytes\r\n"), freeHeap());
292 printf_P(PSTR("SP %x\r\n"), (uint8_t *)(SP));
295 // Even though I'm not going to actually be deleting,
296 // I want to be able to have slightly more control.
297 // Besides, it is easier to initialize stuff...
299 for(int i = 0; i < MAX_HUBS; i++) {
300 Hubs[i] = new USBHub(&Usb);
302 printf_P(PSTR("Available heap: %u Bytes\r\n"), freeHeap());
306 // Initialize generic storage. This must be done before USB starts.
307 Init_Generic_Storage();
309 while(Usb.Init(1000) == -1) {
310 printf_P(PSTR("No USB HOST Shield?\r\n"));
311 Notify(PSTR("OSC did not start."), 0x40);
314 #if !defined(CORE_TEENSY) && defined(__AVR__)
318 // (0.01/(1/((16 *(10^6)) / 8))) - 1 = 19999
321 TIMSK3 |= (1 << OCIE1A);
324 HEAPnext_time = millis() + 10000;
327 HEAPnext_time = millis() + 10000;
332 // Adjust UsbDEBUGlvl level on-the-fly.
333 // + to increase, - to decrease, * to display current level.
334 // . to increase by 16, , to decrease by 16
336 // * to report debug level
337 if(Serial.available()) {
338 int inByte = Serial.read();
341 if(UsbDEBUGlvl < 0xff) UsbDEBUGlvl++;
345 if(UsbDEBUGlvl > 0x00) UsbDEBUGlvl--;
349 if(UsbDEBUGlvl < 0xf0) UsbDEBUGlvl += 16;
353 if(UsbDEBUGlvl > 0x0f) UsbDEBUGlvl -= 16;
370 #if !defined(CORE_TEENSY) && defined(__AVR__)
371 // ALL teensy versions LACK PWM ON LED
373 ISR(TIMER3_COMPA_vect) {
374 if((long)(millis() - LEDnext_time) >= 0L) {
375 LEDnext_time = millis() + 30;
377 // set the brightness of LED
378 analogWrite(LED_BUILTIN, brightness);
380 // change the brightness for next time through the loop:
381 brightness = brightness + fadeAmount;
383 // reverse the direction of the fading at the ends of the fade:
384 if(brightness <= 0) {
386 fadeAmount = -fadeAmount;
388 if(brightness >= 255) {
390 fadeAmount = -fadeAmount;
396 bool isfat(uint8_t t) {
397 return (t == 0x01 || t == 0x04 || t == 0x06 || t == 0x0b || t == 0x0c || t == 0x0e || t == 0x1);
400 void die(FRESULT rc) {
401 printf_P(PSTR("Failed with rc=%u.\r\n"), rc);
406 FIL My_File_Object_x; /* File object */
409 // Print a heap status report about every 10 seconds.
410 if((long)(millis() - HEAPnext_time) >= 0L) {
411 if(UsbDEBUGlvl > 0x50) {
412 printf_P(PSTR("Available heap: %u Bytes\r\n"), freeHeap());
414 HEAPnext_time = millis() + 10000;
418 #if defined(CORE_TEENSY)
419 // Teensy suffers here, oh well...
422 // Horrid! This sort of thing really belongs in an ISR, not here!
423 // We also will be needing to test each hub port, we don't do this yet!
424 if(!change && !usbon && (long)(millis() - usbon_time) >= 0L) {
432 Usb.vbusPower(vbus_on);
433 printf_P(PSTR("VBUS on\r\n"));
435 Usb.vbusPower(vbus_off);
436 usbon_time = millis() + 2000;
440 current_state = Usb.getUsbTaskState();
441 if(current_state != last_state) {
442 if(UsbDEBUGlvl > 0x50)
443 printf_P(PSTR("USB state = %x\r\n"), current_state);
444 #if !defined(CORE_TEENSY) && defined(__AVR__)
445 if(current_state == USB_STATE_RUNNING) {
449 if(current_state == USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE) {
450 #if !defined(CORE_TEENSY) && defined(__AVR__)
454 for(int i = 0; i < cpart; i++) {
463 last_state = current_state;
466 // only do any of this if usb is on
468 if(partsready && !fatready) {
469 if(cpart > 0) fatready = true;
471 // This is horrible, and needs to be moved elsewhere!
472 for(int B = 0; B < MAX_USB_MS_DRIVERS; B++) {
473 if((!partsready) && (UHS_USB_BulkOnly[B]->GetAddress())) {
476 int ML = UHS_USB_BulkOnly[B]->GetbMaxLUN();
477 //printf("MAXLUN = %i\r\n", ML);
479 for(int i = 0; i < ML; i++) {
480 if(UHS_USB_BulkOnly[B]->LUNIsGood(i)) {
482 ((pvt_t *)(sto[i].private_data))->lun = i;
483 ((pvt_t *)(sto[i].private_data))->B = B;
484 sto[i].Reads = *UHS_USB_BulkOnly_Read;
485 sto[i].Writes = *UHS_USB_BulkOnly_Write;
486 sto[i].Status = *UHS_USB_BulkOnly_Status;
487 sto[i].Initialize = *UHS_USB_BulkOnly_Initialize;
488 sto[i].Commit = *UHS_USB_BulkOnly_Commit;
489 sto[i].TotalSectors = UHS_USB_BulkOnly[B]->GetCapacity(i);
490 sto[i].SectorSize = UHS_USB_BulkOnly[B]->GetSectorSize(i);
491 printf_P(PSTR("LUN:\t\t%u\r\n"), i);
492 printf_P(PSTR("Total Sectors:\t%08lx\t%lu\r\n"), sto[i].TotalSectors, sto[i].TotalSectors);
493 printf_P(PSTR("Sector Size:\t%04x\t\t%u\r\n"), sto[i].SectorSize, sto[i].SectorSize);
494 // get the partition data...
495 PT = new PCPartition;
497 if(!PT->Init(&sto[i])) {
499 for(int j = 0; j < 4; j++) {
500 apart = PT->GetPart(j);
501 if(apart != NULL && apart->type != 0x00) {
502 memcpy(&(parts[cpart]), apart, sizeof (part_t));
503 printf_P(PSTR("Partition %u type %#02x\r\n"), j, parts[cpart].type);
505 if(isfat(parts[cpart].type)) {
506 Fats[cpart] = new PFAT(&sto[i], cpart, parts[cpart].firstSector);
507 //int r = Fats[cpart]->Good();
508 if(Fats[cpart]->MountStatus()) {
517 Fats[cpart] = new PFAT(&sto[i], cpart, 0);
518 //int r = Fats[cpart]->Good();
519 if(Fats[cpart]->MountStatus()) {
520 //printf_P(PSTR("Superblock error %x\r\n"), r);
528 sto[i].Writes = NULL;
530 sto[i].Initialize = NULL;
531 sto[i].TotalSectors = 0UL;
532 sto[i].SectorSize = 0;
540 if(Fats[0] != NULL) {
542 p = ((struct Pvt *)(Fats[0]->storage->private_data));
543 if(!UHS_USB_BulkOnly[p->B]->LUNIsGood(p->lun)) {
545 #if !defined(CORE_TEENSY) && defined(__AVR__)
549 for(int i = 0; i < cpart; i++) {
562 FRESULT rc; /* Result code */
565 #if !defined(CORE_TEENSY) && defined(__AVR__)
570 for(int zz = 0; zz < _VOLUMES; zz++) {
571 if(Fats[zz]->volmap == 0) fs = Fats[zz]->ffs;
573 printf_P(PSTR("\r\nOpen an existing file (message.txt).\r\n"));
574 rc = f_open(&My_File_Object_x, "0:/MESSAGE.TXT", FA_READ);
575 if(rc) printf_P(PSTR("Error %i, message.txt not found.\r\n"), rc);
577 printf_P(PSTR("\r\nType the file content.\r\n"));
579 rc = f_read(&My_File_Object_x, My_Buff_x, mbxs, &br); /* Read a chunk of file */
580 if(rc || !br) break; /* Error or end of file */
581 for(i = 0; i < br; i++) {
583 if(My_Buff_x[i] == '\n')
585 if(My_Buff_x[i] != '\r')
586 Serial.write(My_Buff_x[i]);
591 f_close(&My_File_Object_x);
595 printf_P(PSTR("\r\nClose the file.\r\n"));
596 rc = f_close(&My_File_Object_x);
599 printf_P(PSTR("\r\nCreate a new file (hello.txt).\r\n"));
600 rc = f_open(&My_File_Object_x, "0:/Hello.TxT", FA_WRITE | FA_CREATE_ALWAYS);
605 printf_P(PSTR("\r\nWrite a text data. (Hello world!)\r\n"));
606 rc = f_write(&My_File_Object_x, "Hello world!\r\n", 14, &bw);
610 printf_P(PSTR("%u bytes written.\r\n"), bw);
612 printf_P(PSTR("\r\nClose the file.\r\n"));
613 rc = f_close(&My_File_Object_x);
620 char lfn[_MAX_LFN + 1];
621 FILINFO My_File_Info_Object_x; /* File information object */
622 My_File_Info_Object_x.lfname = lfn;
624 DIR My_Dir_Object_x; /* Directory object */
625 printf_P(PSTR("\r\nOpen root directory.\r\n"));
626 rc = f_opendir(&My_Dir_Object_x, "0:/");
632 printf_P(PSTR("\r\nDirectory listing...\r\n"));
634 printf_P(PSTR("Available heap: %u Bytes\r\n"), freeHeap());
638 My_File_Info_Object_x.lfsize = _MAX_LFN;
641 rc = f_readdir(&My_Dir_Object_x, &My_File_Info_Object_x); /* Read a directory item */
642 if(rc || !My_File_Info_Object_x.fname[0]) break; /* Error or end of dir */
644 if(My_File_Info_Object_x.fattrib & AM_DIR) {
651 if(My_File_Info_Object_x.fattrib & AM_RDO) {
656 if(My_File_Info_Object_x.fattrib & AM_HID) {
662 if(My_File_Info_Object_x.fattrib & AM_SYS) {
668 if(My_File_Info_Object_x.fattrib & AM_ARC) {
675 if(*My_File_Info_Object_x.lfname)
676 printf_P(PSTR(" %8lu %s (%s)\r\n"), My_File_Info_Object_x.fsize, My_File_Info_Object_x.fname, My_File_Info_Object_x.lfname);
679 printf_P(PSTR(" %8lu %s\r\n"), My_File_Info_Object_x.fsize, &(My_File_Info_Object_x.fname[0]));
685 DISK_IOCTL(fs->drv, CTRL_COMMIT, 0);
686 printf_P(PSTR("\r\nTest completed.\r\n"));
691 ULONG ii, wt, rt, start, end;
693 for(int zz = 0; zz < _VOLUMES; zz++) {
694 if(Fats[zz]->volmap == 0) fs = Fats[zz]->ffs;
697 f_unlink("0:/10MB.bin");
698 printf_P(PSTR("\r\nCreate a new 10MB test file (10MB.bin).\r\n"));
699 rc = f_open(&My_File_Object_x, "0:/10MB.bin", FA_WRITE | FA_CREATE_ALWAYS);
701 for(bw = 0; bw < mbxs; bw++) My_Buff_x[bw] = bw & 0xff;
704 while(start == millis());
705 for(ii = 10485760LU / mbxs; ii > 0LU; ii--) {
706 rc = f_write(&My_File_Object_x, My_Buff_x, mbxs, &bw);
707 if(rc || !bw) goto failed;
709 rc = f_close(&My_File_Object_x);
712 wt = (end - start) - 1;
713 printf_P(PSTR("Time to write 10485760 bytes: %lu ms (%lu sec) \r\n"), wt, (500 + wt) / 1000UL);
714 rc = f_open(&My_File_Object_x, "0:/10MB.bin", FA_READ);
717 while(start == millis());
720 rc = f_read(&My_File_Object_x, My_Buff_x, mbxs, &bw); /* Read a chunk of file */
721 if(rc || !bw) break; /* Error or end of file */
725 rc = f_close(&My_File_Object_x);
727 rt = (end - start) - 1;
728 printf_P(PSTR("Time to read 10485760 bytes: %lu ms (%lu sec)\r\nDelete test file\r\n"), rt, (500 + rt) / 1000UL);
731 DISK_IOCTL(fs->drv, CTRL_COMMIT, 0);
732 printf_P(PSTR("10MB timing test finished.\r\n"));