Library(Beta) for Gameduino 2
Diff: Utils.h
- Revision:
- 0:9c211972beb2
diff -r 000000000000 -r 9c211972beb2 Utils.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Utils.h Fri Apr 11 07:24:23 2014 +0000 @@ -0,0 +1,600 @@ +#ifndef _UTILS_H_ +#define _UTILS_H_ + +#include "arduino.h" + +void loop(); + +#define LOOP while(1) loop() + +typedef struct { + byte handle; + uint16_t w, h; + uint16_t size; +} shape_t; + +struct direntry +{ + char name[8]; + char ext[3]; + uint8_t attribute; + uint8_t reserved[8]; + uint16_t cluster_hi; // FAT32 only + uint16_t time; + uint16_t date; + uint16_t cluster; + uint32_t size; +}; + +// https://www.sdcard.org/downloads/pls/simplified_specs/Part_1_Physical_Layer_Simplified_Specification_Ver_3.01_Final_100518.pdf +// page 22 +// http://mac6.ma.psu.edu/space2008/RockSat/microController/sdcard_appnote_foust.pdf +// http://elm-chan.org/docs/mmc/mmc_e.html +// http://www.pjrc.com/tech/8051/ide/fat32.html + +#define FAT16 0 +#define FAT32 1 + +class sdcard +{ +public: + SPI* _spi; + DigitalOut _cs; + uint8_t ccs; + + uint8_t type; + uint16_t sectors_per_cluster; + uint16_t reserved_sectors; + uint16_t max_root_dir_entries; + uint16_t sectors_per_fat; + uint16_t cluster_size; + uint32_t root_dir_first_cluster; + + // These are all linear addresses, hence the o_ prefix + uint32_t o_partition; + uint32_t o_fat; + uint32_t o_root; + uint32_t o_data; +public: + sdcard(SPI* spi, PinName cs) : _cs(cs) + { + _spi = spi; + } + + void sel() + { + _cs = 0; + delay(1); + } + void desel() + { + _cs = 1; + _spi->write(0xff); // force DO release + } + void sd_delay(uint8_t n) + { + while (n--) + _spi->write(0xff); + } + + void cmd(uint8_t cmd, uint32_t lba = 0, uint8_t crc = 0x95) + { + sel(); + _spi->write(0xff); + _spi->write(0x40 | cmd); + _spi->write(0xff & (lba >> 24)); + _spi->write(0xff & (lba >> 16)); + _spi->write(0xff & (lba >> 8)); + _spi->write(0xff & (lba)); + _spi->write(crc); + _spi->write(0xff); + } + + uint8_t R1() + { // read response R1 + uint8_t r; + while ((r = _spi->write(0xff)) & 0x80) + ; + desel(); + _spi->write(0xff); // trailing uint8_t + return r; + } + + uint8_t sdR3(uint32_t &ocr) + { // read response R3 + uint32_t r; + while ((r = _spi->write(0xff)) & 0x80) + ; + for (uint8_t i = 4; i; i--) + ocr = (ocr << 8) | _spi->write(0xff); + _spi->write(0xff); // trailing uint8_t + + desel(); + return r; + } + + uint8_t sdR7() + { // read response R3 + uint32_t r; + while ((r = _spi->write(0xff)) & 0x80) + ; + for (uint8_t i = 4; i; i--) + // Serial.println(____spi->write(0xff), HEX); + _spi->write(0xff); + desel(); + + return r; + } + + void appcmd(uint8_t cc, uint32_t lba = 0) + { + cmd(55); R1(); + cmd(cc, lba); + } + + void begin() + { + uint8_t type_code; + uint8_t sdhc; + + desel(); + + delay(10); // wait for boot + sd_delay(10); // deselected, 80 pulses + + // Tty.printf("Attempting card reset... "); + // attempt reset + uint8_t r1; + int attempts = 0; + do + { // reset, enter idle + cmd(0); + while ((r1 = _spi->write(0xff)) & 0x80) + if (++attempts == 1000) + return; + desel(); + _spi->write(0xff); // trailing uint8_t + } while (r1 != 1); + // Tty.printf("reset ok\n"); + + sdhc = 0; + cmd(8, 0x1aa, 0x87); + r1 = sdR7(); + sdhc = (r1 == 1); + + // Tty.printf("card %s SDHC\n", sdhc ? "is" : "is not"); + + // Tty.printf("Sending card init command... "); + while (1) + { + appcmd(41, sdhc ? (1UL << 30) : 0); // card init + r1 = R1(); + if ((r1 & 1) == 0) + break; + delay(100); + } + // Tty.printf("OK\n"); + + if (sdhc) + { + cmd(58); + uint32_t OCR = 0; + sdR3(OCR); + ccs = 1UL & (OCR >> 30); + // Tty.printf("OCR register is %#010lx\n", long(OCR)); + } + + else + { + ccs = 0; + } + // Tty.printf("ccs = %d\n", ccs); + // REPORT(ccs); + + type_code = rd(0x1be + 0x4); + + switch (type_code) + { + default: + type = FAT16; + break; + case 0x0b: + case 0x0c: + type = FAT32; + break; + } + // REPORT(type_code); + // Tty.printf("Type code %#02x means FAT%d\n", type_code, (type == FAT16) ? 16 : 32); +#if VERBOSE + DEBUGOUT("Type "); + DEBUGOUT("%x", type_code); + DEBUGOUT(" so FAT"); + DEBUGOUT("%d\r\n", (type == FAT16) ? 16 : 32); +#endif + + o_partition = 512L * rd4(0x1be + 0x8); + sectors_per_cluster = rd(o_partition + 0xd); + reserved_sectors = rd2(o_partition + 0xe); + cluster_size = 512L * sectors_per_cluster; + // REPORT(sectors_per_cluster); + + // Tty.printf("Bytes per sector: %d\n", rd2(o_partition + 0xb)); + // Tty.printf("Sectors per cluster: %d\n", sectors_per_cluster); + + if (type == FAT16) + { + max_root_dir_entries = rd2(o_partition + 0x11); + sectors_per_fat = rd2(o_partition + 0x16); + o_fat = o_partition + 512L * reserved_sectors; + o_root = o_fat + (2 * 512L * sectors_per_fat); + // data area starts with cluster 2, so offset it here + o_data = o_root + (max_root_dir_entries * 32L) - (2L * cluster_size); + } + + else + { + uint32_t sectors_per_fat = rd4(o_partition + 0x24); + root_dir_first_cluster = rd4(o_partition + 0x2c); + uint32_t fat_begin_lba = (o_partition >> 9) + reserved_sectors; + uint32_t cluster_begin_lba = (o_partition >> 9) + reserved_sectors + (2 * sectors_per_fat); + + o_fat = 512L * fat_begin_lba; + o_root = (512L * (cluster_begin_lba + (root_dir_first_cluster - 2) * sectors_per_cluster)); + o_data = (512L * (cluster_begin_lba - 2 * sectors_per_cluster)); + } + } + + static char toupper(char sv) + { + char c = sv; + if( sv >= 'a' && sv <= 'z') + c = sv - ('a' - 'A'); + + return c; + } + + void cmd17(uint32_t off) + { + if (ccs) + cmd(17, off >> 9); + else + cmd(17, off & ~511L); + R1(); + sel(); + while (_spi->write(0xff) != 0xfe) + ; + } + + void rdn(uint8_t *d, uint32_t off, uint16_t n) + { + cmd17(off); + uint16_t i; + uint16_t bo = (off & 511); + for (i = 0; i < bo; i++) + _spi->write(0xff); + for (i = 0; i < n; i++) + *d++ = _spi->write(0xff); + for (i = 0; i < (514 - bo - n); i++) + _spi->write(0xff); + desel(); + } + + uint32_t rd4(uint32_t off) + { + uint32_t r; + rdn((uint8_t*)&r, off, sizeof(r)); + return r; + } + + uint16_t rd2(uint32_t off) + { + uint16_t r; + rdn((uint8_t*)&r, off, sizeof(r)); + return r; + } + + uint8_t rd(uint32_t off) + { + uint8_t r; + rdn((uint8_t*)&r, off, sizeof(r)); + return r; + } +}; + +static void dos83(uint8_t dst[11], const char *ps) +{ + uint8_t i = 0; + while (*ps) + { + if (*ps != '.') + dst[i++] = sdcard::toupper(*ps); + else + { + while (i < 8) + dst[i++] = ' '; + } + ps++; + } + while (i < 11) + dst[i++] = ' '; +} + +class Reader +{ + SPI* _spi; + sdcard* _sd; +public: + Reader(SPI* spi, sdcard* sd) + { + _spi = spi; + _sd = sd; + } + +public: + int openfile(const char *filename) + { + int i = 0; + uint8_t dosname[11] = {0, }; + direntry de = {0, }; + + dos83(dosname, filename); + + do { + _sd->rdn((uint8_t*)&de, _sd->o_root + i * 32, sizeof(de)); + if (0 == memcmp(de.name, dosname, 11)) + { + DEBUGOUT("begin(de)\r\n"); + begin(de); + return 1; + } + i++; + } while (de.name[0]); + return 0; + } + + void begin(direntry &de) + { + size = de.size; + cluster = de.cluster; + if (_sd->type == FAT32) + cluster |= ((long)de.cluster_hi << 16); + sector = 0; + offset = 0; + } + + void nextcluster() + { + if (_sd->type == FAT16) + cluster = _sd->rd2(_sd->o_fat + 2 * cluster); + else + cluster = _sd->rd4(_sd->o_fat + 4 * cluster); +#if VERBOSE + DEBUGOUT("nextcluster="); + DEBUGOUT("%d\r\n", cluster); +#endif + } + + void skipcluster() + { + nextcluster(); + offset += _sd->cluster_size; + } + void skipsector() + { + if (sector == _sd->sectors_per_cluster) { + sector = 0; + nextcluster(); + } + sector++; + offset += 512; + } + + void seek(uint32_t o) + { + while (offset < o) + { + if ((sector == _sd->sectors_per_cluster) && ((o - offset) > (long)_sd->cluster_size)) + skipcluster(); + else + skipsector(); + } + } + void readsector() + { + if (sector == _sd->sectors_per_cluster) + { + sector = 0; + nextcluster(); + } + uint32_t off = _sd->o_data + ((long)_sd->cluster_size * cluster) + (512L * sector); +#if VERBOSE + DEBUGOUT("off=0x"); + DEBUGOUT("%x", off); +#endif + _sd->cmd17(off & ~511L); + // Serial.println(2 * (micros() - t0), DEC); + sector++; + offset += 512; + } + void readsector(uint8_t *dst) + { + readsector(); + for (int i = 0; i < 64; i++) { + *dst++ = _spi->write(0xff); + *dst++ = _spi->write(0xff); + *dst++ = _spi->write(0xff); + *dst++ = _spi->write(0xff); + *dst++ = _spi->write(0xff); + *dst++ = _spi->write(0xff); + *dst++ = _spi->write(0xff); + *dst++ = _spi->write(0xff); + } + _spi->write(0xff); // consume CRC + _spi->write(0xff); + _sd->desel(); + } + uint32_t cluster; + uint32_t offset; + uint32_t size; + uint8_t sector; +}; + +class Poly +{ +public: + GDClass* _gd; + Poly(GDClass* gd) + { + _gd = gd; + } + + int x0, y0, x1, y1; + int x[8], y[8]; + uint8_t n; + + void restart() + { + n = 0; + x0 = 16 * 480; + x1 = 0; + y0 = 16 * 272; + y1 = 0; + } + + void perim() + { + for (uint8_t i = 0; i < n; i++) + _gd->Vertex2f(x[i], y[i]); + _gd->Vertex2f(x[0], y[0]); + } +public: + void begin() + { + restart(); + + _gd->ColorMask(0,0,0,0); + _gd->StencilOp(KEEP, INVERT); + _gd->StencilFunc(ALWAYS, 255, 255); + } + + void v(int _x, int _y) + { + x0 = min(x0, _x >> 4); + x1 = max(x1, _x >> 4); + y0 = min(y0, _y >> 4); + y1 = max(y1, _y >> 4); + x[n] = _x; + y[n] = _y; + n++; + } + + void paint() + { + x0 = max(0, x0); + y0 = max(0, y0); + x1 = min(16 * 480, x1); + y1 = min(16 * 272, y1); + _gd->ScissorXY(x0, y0); + _gd->ScissorSize(x1 - x0 + 1, y1 - y0 + 1); + _gd->Begin(EDGE_STRIP_B); + perim(); + } + + void finish() + { + _gd->ColorMask(1,1,1,1); + _gd->StencilFunc(EQUAL, 255, 255); + + _gd->Begin(EDGE_STRIP_B); + _gd->Vertex2ii(0, 0); + _gd->Vertex2ii(511, 0); + } + + void draw() + { + paint(); + finish(); + } + + void outline() + { + _gd->Begin(LINE_STRIP); + perim(); + } +}; + +class Streamer +{ +public: + GDClass* _gd; + + Streamer(GDClass* gd, sdcard* sd) + { + r = new Reader(gd->GDTR.SPI(), sd); + _gd = gd; + } + void begin(const char *rawsamples, + uint16_t freq = 44100, + uint8_t format = ADPCM_SAMPLES, + uint32_t _base = (0x40000UL - 8192), uint16_t size = 8192) + { + r->openfile(rawsamples); + + base = _base; + mask = size - 1; + wp = 0; + + for (uint8_t i = 10; i; i--) + feed(); + + _gd->sample(base, size, freq, format, 1); + } + + int feed() + { + uint16_t rp = _gd->rd32(REG_PLAYBACK_READPTR) - base; + uint16_t freespace = mask & ((rp - 1) - wp); + if (freespace >= 512) { + // REPORT(base); + // REPORT(rp); + // REPORT(wp); + // REPORT(freespace); + // DEBUGOUT("\r\n"); + uint8_t buf[512]; + // uint16_t n = min(512, r->size - r->offset); + // n = (n + 3) & ~3; // force 32-bit alignment + _gd->__end(); + r->readsector(buf); + _gd->resume(); + _gd->cmd_memwrite(base + wp, 512); + _gd->copyram(buf, 512); + wp = (wp + 512) & mask; + } + return r->offset < r->size; + } + + void progress(uint16_t &val, uint16_t &range) + { + uint32_t m = r->size; + uint32_t p = min(r->offset, m); + while (m > 0x10000) { + m >>= 1; + p >>= 1; + } + val = p; + range = m; + } +private: + Reader* r; + uint32_t base; + uint16_t mask; + uint16_t wp; +}; + + +static uint8_t sinus(GDClass* gd, uint8_t x) +{ + return 128 + gd->rsin(128, -16384 + (x << 7)); +} + +#endif \ No newline at end of file