|
| 1 | +// The MIT License (MIT) |
| 2 | +// Copyright (c) 2019 Ha Thach for Adafruit Industries |
| 3 | + |
| 4 | +/* This example exposes both external flash and SD card as mass storage |
| 5 | + * using Adafruit_SPIFlash+Adafruit_QSPI and SdFat Library |
| 6 | + */ |
| 7 | + |
| 8 | +#include "SPI.h" |
| 9 | +#include "SdFat.h" |
| 10 | + |
| 11 | +#include "Adafruit_TinyUSB.h" |
| 12 | +#include "Adafruit_SPIFlash.h" |
| 13 | + |
| 14 | +const int chipSelect = 10; |
| 15 | +const int spi_freq_mhz = 50; |
| 16 | + |
| 17 | +#if defined(__SAMD51__) || defined(NRF52840_XXAA) |
| 18 | + #include "Adafruit_QSPI.h" |
| 19 | + #include "Adafruit_QSPI_Flash.h" |
| 20 | + |
| 21 | + Adafruit_QSPI_Flash flash; |
| 22 | +#else |
| 23 | + // Configuration of the flash chip pins and flash fatfs object. |
| 24 | + // You don't normally need to change these if using a Feather/Metro |
| 25 | + // M0 express board. |
| 26 | + |
| 27 | + // Flash chip type. If you change this be sure to change the fatfs to match as well |
| 28 | + #define FLASH_TYPE SPIFLASHTYPE_W25Q16BV |
| 29 | + |
| 30 | + #if (SPI_INTERFACES_COUNT == 1) |
| 31 | + #define FLASH_SS SS // Flash chip SS pin. |
| 32 | + #define FLASH_SPI_PORT SPI // What SPI port is Flash on? |
| 33 | + #else |
| 34 | + #define FLASH_SS SS1 // Flash chip SS pin. |
| 35 | + #define FLASH_SPI_PORT SPI1 // What SPI port is Flash on? |
| 36 | + #endif |
| 37 | + |
| 38 | + Adafruit_SPIFlash flash(FLASH_SS, &FLASH_SPI_PORT); // Use hardware SPI |
| 39 | +#endif |
| 40 | + |
| 41 | +Adafruit_USBD_MSC usb_msc; |
| 42 | +SdFat sd; |
| 43 | + |
| 44 | +// the setup function runs once when you press reset or power the board |
| 45 | +void setup() |
| 46 | +{ |
| 47 | + // MSC with 2 Logical Units |
| 48 | + usb_msc.setMaxLun(2); |
| 49 | + |
| 50 | + //------------- Lun 0 for external flash -------------// |
| 51 | +#if defined(__SAMD51__) || defined(NRF52840_XXAA) |
| 52 | + flash.begin(); |
| 53 | +#else |
| 54 | + flash.begin(FLASH_TYPE); |
| 55 | +#endif |
| 56 | + usb_msc.setID(0, "Adafruit", "External Flash", "1.0"); |
| 57 | + usb_msc.setCapacity(0, flash.pageSize()*flash.numPages()/512, 512); |
| 58 | + usb_msc.setReadWriteCallback(0, external_flash_read_cb, external_flash_write_cb, external_flash_flush_cb); |
| 59 | + usb_msc.setUnitReady(0, true); |
| 60 | + |
| 61 | + //------------- Lun 1 for SD card -------------// |
| 62 | + usb_msc.setID(1, "Adafruit", "SD Card", "1.0"); |
| 63 | + usb_msc.setReadWriteCallback(1, sdcard_read_cb, sdcard_write_cb, sdcard_flush_cb); |
| 64 | + |
| 65 | + if ( sd.cardBegin(chipSelect, SD_SCK_MHZ(spi_freq_mhz)) ) |
| 66 | + { |
| 67 | + uint32_t block_count = sd.card()->cardSize(); |
| 68 | + usb_msc.setCapacity(1, block_count, 512); |
| 69 | + usb_msc.setUnitReady(1, true); |
| 70 | + } |
| 71 | + |
| 72 | + usb_msc.begin(); |
| 73 | + |
| 74 | + Serial.begin(115200); |
| 75 | + while ( !Serial ) delay(10); // wait for native usb |
| 76 | + |
| 77 | + Serial.println("Adafruit TinyUSB Mass Storage External Flash + SD Card example"); |
| 78 | +} |
| 79 | + |
| 80 | +void loop() |
| 81 | +{ |
| 82 | + // nothing to do |
| 83 | +} |
| 84 | + |
| 85 | + |
| 86 | +//--------------------------------------------------------------------+ |
| 87 | +// SD Card callbacks |
| 88 | +//--------------------------------------------------------------------+ |
| 89 | + |
| 90 | +int32_t sdcard_read_cb (uint32_t lba, void* buffer, uint32_t bufsize) |
| 91 | +{ |
| 92 | + (void) bufsize; |
| 93 | + return sd.card()->readBlocks(lba, (uint8_t*) buffer, bufsize/512) ? bufsize : -1; |
| 94 | +} |
| 95 | + |
| 96 | +// Callback invoked when received WRITE10 command. |
| 97 | +// Process data in buffer to disk's storage and |
| 98 | +// return number of written bytes (must be multiple of block size) |
| 99 | +int32_t sdcard_write_cb (uint32_t lba, uint8_t* buffer, uint32_t bufsize) |
| 100 | +{ |
| 101 | + return sd.card()->writeBlocks(lba, buffer, bufsize/512) ? bufsize : -1; |
| 102 | +} |
| 103 | + |
| 104 | +// Callback invoked when WRITE10 command is completed (status received and accepted by host). |
| 105 | +// used to flush any pending cache. |
| 106 | +void sdcard_flush_cb (void) |
| 107 | +{ |
| 108 | + sd.card()->syncBlocks(); |
| 109 | +} |
| 110 | + |
| 111 | + |
| 112 | +//--------------------------------------------------------------------+ |
| 113 | +// External Flash callbacks |
| 114 | +//--------------------------------------------------------------------+ |
| 115 | + |
| 116 | +// Callback invoked when received READ10 command. |
| 117 | +// Copy disk's data to buffer (up to bufsize) and |
| 118 | +// return number of copied bytes (must be multiple of block size) |
| 119 | +int32_t external_flash_read_cb (uint32_t lba, void* buffer, uint32_t bufsize) |
| 120 | +{ |
| 121 | + const uint32_t addr = lba*512; |
| 122 | + flash_cache_read((uint8_t*) buffer, addr, bufsize); |
| 123 | + return bufsize; |
| 124 | +} |
| 125 | + |
| 126 | +// Callback invoked when received WRITE10 command. |
| 127 | +// Process data in buffer to disk's storage and |
| 128 | +// return number of written bytes (must be multiple of block size) |
| 129 | +int32_t external_flash_write_cb (uint32_t lba, uint8_t* buffer, uint32_t bufsize) |
| 130 | +{ |
| 131 | + // need to erase & caching write back |
| 132 | + const uint32_t addr = lba*512; |
| 133 | + flash_cache_write(addr, buffer, bufsize); |
| 134 | + return bufsize; |
| 135 | +} |
| 136 | + |
| 137 | +// Callback invoked when WRITE10 command is completed (status received and accepted by host). |
| 138 | +// used to flush any pending cache. |
| 139 | +void external_flash_flush_cb (void) |
| 140 | +{ |
| 141 | + flash_cache_flush(); |
| 142 | +} |
| 143 | + |
| 144 | + |
| 145 | +//--------------------------------------------------------------------+ |
| 146 | +// Flash Caching for External Flash |
| 147 | +//--------------------------------------------------------------------+ |
| 148 | +#define FLASH_CACHE_SIZE 4096 // must be a erasable page size |
| 149 | +#define FLASH_CACHE_INVALID_ADDR 0xffffffff |
| 150 | + |
| 151 | +uint32_t cache_addr = FLASH_CACHE_INVALID_ADDR; |
| 152 | +uint8_t cache_buf[FLASH_CACHE_SIZE]; |
| 153 | + |
| 154 | +static inline uint32_t page_addr_of (uint32_t addr) |
| 155 | +{ |
| 156 | + return addr & ~(FLASH_CACHE_SIZE - 1); |
| 157 | +} |
| 158 | + |
| 159 | +static inline uint32_t page_offset_of (uint32_t addr) |
| 160 | +{ |
| 161 | + return addr & (FLASH_CACHE_SIZE - 1); |
| 162 | +} |
| 163 | + |
| 164 | +void flash_cache_flush (void) |
| 165 | +{ |
| 166 | + if ( cache_addr == FLASH_CACHE_INVALID_ADDR ) return; |
| 167 | + |
| 168 | + // indicator |
| 169 | + digitalWrite(LED_BUILTIN, HIGH); |
| 170 | + |
| 171 | + flash.eraseSector(cache_addr/FLASH_CACHE_SIZE); |
| 172 | + flash.writeBuffer(cache_addr, cache_buf, FLASH_CACHE_SIZE); |
| 173 | + |
| 174 | + digitalWrite(LED_BUILTIN, LOW); |
| 175 | + |
| 176 | + cache_addr = FLASH_CACHE_INVALID_ADDR; |
| 177 | +} |
| 178 | + |
| 179 | +uint32_t flash_cache_write (uint32_t dst, void const * src, uint32_t len) |
| 180 | +{ |
| 181 | + uint8_t const * src8 = (uint8_t const *) src; |
| 182 | + uint32_t remain = len; |
| 183 | + |
| 184 | + // Program up to page boundary each loop |
| 185 | + while ( remain ) |
| 186 | + { |
| 187 | + uint32_t const page_addr = page_addr_of(dst); |
| 188 | + uint32_t const offset = page_offset_of(dst); |
| 189 | + |
| 190 | + uint32_t wr_bytes = FLASH_CACHE_SIZE - offset; |
| 191 | + wr_bytes = min(remain, wr_bytes); |
| 192 | + |
| 193 | + // Page changes, flush old and update new cache |
| 194 | + if ( page_addr != cache_addr ) |
| 195 | + { |
| 196 | + flash_cache_flush(); |
| 197 | + cache_addr = page_addr; |
| 198 | + |
| 199 | + // read a whole page from flash |
| 200 | + flash.readBuffer(page_addr, cache_buf, FLASH_CACHE_SIZE); |
| 201 | + } |
| 202 | + |
| 203 | + memcpy(cache_buf + offset, src8, wr_bytes); |
| 204 | + |
| 205 | + // adjust for next run |
| 206 | + src8 += wr_bytes; |
| 207 | + remain -= wr_bytes; |
| 208 | + dst += wr_bytes; |
| 209 | + } |
| 210 | + |
| 211 | + return len - remain; |
| 212 | +} |
| 213 | + |
| 214 | +void flash_cache_read (uint8_t* dst, uint32_t addr, uint32_t count) |
| 215 | +{ |
| 216 | + // overwrite with cache value if available |
| 217 | + if ( (cache_addr != FLASH_CACHE_INVALID_ADDR) && |
| 218 | + !(addr < cache_addr && addr + count <= cache_addr) && |
| 219 | + !(addr >= cache_addr + FLASH_CACHE_SIZE) ) |
| 220 | + { |
| 221 | + int dst_off = cache_addr - addr; |
| 222 | + int src_off = 0; |
| 223 | + |
| 224 | + if ( dst_off < 0 ) |
| 225 | + { |
| 226 | + src_off = -dst_off; |
| 227 | + dst_off = 0; |
| 228 | + } |
| 229 | + |
| 230 | + int cache_bytes = min(FLASH_CACHE_SIZE-src_off, count - dst_off); |
| 231 | + |
| 232 | + // start to cached |
| 233 | + if ( dst_off ) flash.readBuffer(addr, dst, dst_off); |
| 234 | + |
| 235 | + // cached |
| 236 | + memcpy(dst + dst_off, cache_buf + src_off, cache_bytes); |
| 237 | + |
| 238 | + // cached to end |
| 239 | + int copied = dst_off + cache_bytes; |
| 240 | + if ( copied < count ) flash.readBuffer(addr + copied, dst + copied, count - copied); |
| 241 | + } |
| 242 | + else |
| 243 | + { |
| 244 | + flash.readBuffer(addr, dst, count); |
| 245 | + } |
| 246 | +} |
0 commit comments