LCD Driver, some teensy code, SDCard code, MBR/FAT32
This commit is contained in:
13
Debug.h
13
Debug.h
@@ -88,8 +88,21 @@ extern "C" {
|
||||
#define IF_DEBUG(a) a
|
||||
#define debugShow(buf, len) hexdump(buf,len)
|
||||
|
||||
#elif TEENSY
|
||||
|
||||
#define debugMod(module, str) {char buf[64]; sprintf(buf, "i[%-10s] ", module); Serial.print(buf); Serial.println(str);}
|
||||
#define debugMod1(module, str, v1) {char buf[64]; sprintf(buf, "i[%-10s] ", module); Serial.print(buf); sprintf(buf, str, v1); Serial.println(buf);}
|
||||
#define debugMod2(module, str, v1, v2) {char buf[64]; sprintf(buf, "i[%-10s] ", module); Serial.print(buf); sprintf(buf, str, v1, v2); Serial.println(buf);}
|
||||
#define debugMod3(module, str, v1, v2, v3) {char buf[64]; sprintf(buf, "i[%-10s] ", module); Serial.print(buf); sprintf(buf, str, v1, v2, v3); Serial.println(buf);}
|
||||
#define debugMod4(module, str, v1, v2, v3, v4) {char buf[64]; sprintf(buf, "i[%-10s] ", module); Serial.print(buf); sprintf(buf, str, v1, v2, v3, v4); Serial.println(buf);}
|
||||
#define debugMod5(module, str, v1, v2, v3, v4, v5) {char buf[64]; sprintf(buf, "i[%-10s] ", module); Serial.print(buf); sprintf(buf, str, v1, v2, v3, v4, v5); Serial.println(buf);}
|
||||
|
||||
#define errorMod(module, str) debugMod(module, str); while(true) {}
|
||||
|
||||
#else
|
||||
|
||||
|
||||
|
||||
#error "unsupported platform"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
#elif (TEENSY)
|
||||
|
||||
#pragma message "Using Teensy"
|
||||
#define DELAY_MS(ms) delay(ms)
|
||||
|
||||
|
||||
#else
|
||||
|
||||
@@ -66,3 +68,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#if TEENSY
|
||||
#define IRAM_ATTR
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
|
||||
|
||||
/** https://fpgalover.com/images/manuals/SNES/time.JPG */
|
||||
|
||||
// Pinout on the chinese version
|
||||
// left to right: gnd data latch clk +5v (also works with 3.3)
|
||||
template <int PIN_LATCH, int PIN_DATA, int PIN_CLK> class SNESController {
|
||||
|
||||
public:
|
||||
|
||||
@@ -195,7 +195,7 @@ public:
|
||||
uint32_t res = 0xFFFFFFFF;
|
||||
readBytes(buf, 5); // dummy-byte + 4 data bytes
|
||||
chipDeselect();
|
||||
ESP_LOGI(MOD, "%d %d %d %d", buf[1],buf[2],buf[3],buf[4]);
|
||||
//ESP_LOGI(MOD, "%d %d %d %d", buf[1],buf[2],buf[3],buf[4]);
|
||||
read();
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ public:
|
||||
void draw(uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint16_t* data) {
|
||||
chipSelect();
|
||||
setAddrWindow(x,y,w,h);
|
||||
const int len = w*h;
|
||||
// const int len = w*h;
|
||||
// for (int i = 0; i < len; ++i) {
|
||||
// spi::writeWord(data[i]);
|
||||
// }
|
||||
@@ -289,7 +289,8 @@ private:
|
||||
}
|
||||
|
||||
void wait() {
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
//vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
DELAY_MS(100);
|
||||
}
|
||||
|
||||
|
||||
|
||||
301
ext/lcd/ILI9486.h
Normal file
301
ext/lcd/ILI9486.h
Normal file
@@ -0,0 +1,301 @@
|
||||
#ifndef LCD_ILI9341
|
||||
#define LCD_ILI9341
|
||||
|
||||
#include "../../Debug.h"
|
||||
#include <initializer_list>
|
||||
|
||||
// https://www.displayfuture.com/Display/datasheet/controller/ILI9486L.pdf
|
||||
|
||||
// https://www.waveshare.com/3.5inch-tft-touch-shield.htm
|
||||
// https://www.waveshare.com/wiki/File:3.5inch_TFT_Touch_Shield_Code.7z
|
||||
|
||||
// https://github.com/ZinggJM/ILI9486_SPI/blob/master/src/ILI9486_SPI.cpp
|
||||
|
||||
static constexpr const uint8_t ili9486_init[] = {
|
||||
|
||||
};
|
||||
|
||||
//template <int PIN_CS, int PIN_MISO, int PIN_MOSI, int PIN_CLK, int PIN_DATA_COMMAND> class ILI9341 {
|
||||
template <typename SPI, int PIN_CS, int PIN_DATA_COMMAND> class ILI9486 {
|
||||
|
||||
private:
|
||||
|
||||
int w = 320;
|
||||
int h = 240;
|
||||
|
||||
static constexpr const char* MOD = "ILI9486";
|
||||
|
||||
static constexpr const uint8_t REG_SWRESET = 0x01; // Software Reset
|
||||
static constexpr const uint8_t REG_RDDID = 0x04; // Read display identification information
|
||||
static constexpr const uint8_t REG_RDDST = 0x09; // Read Display Status
|
||||
static constexpr const uint8_t REG_CASET = 0x2A; // Column Address Set
|
||||
static constexpr const uint8_t REG_PASET = 0x2B; // Page Address Set
|
||||
static constexpr const uint8_t REG_RAMWR = 0x2C; // Memory Write
|
||||
static constexpr const uint8_t REG_RAMRD = 0x2E; // Memory Read
|
||||
|
||||
SPI& spi;
|
||||
|
||||
public:
|
||||
|
||||
ILI9486(SPI& spi) : spi(spi) {
|
||||
MyGPIO::setOutput(PIN_DATA_COMMAND);
|
||||
MyGPIO::setOutput(PIN_CS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** perform display software reset and initialization */
|
||||
void init() {
|
||||
|
||||
|
||||
// send reset command
|
||||
wait();
|
||||
sendCommand(REG_SWRESET);
|
||||
wait();
|
||||
|
||||
|
||||
getStatus();
|
||||
|
||||
//sendCommandAndData(0xb0, {0x00}); // Interface Mode Control
|
||||
sendCommandAndData(0x11, {}); // disable sleep
|
||||
|
||||
wait();
|
||||
|
||||
sendCommandAndData(0x3A, {0x55}); // Interface Pixel Format, 16 bits / pixel
|
||||
sendCommandAndData(0x36, {0x28}); // Memory Access Control
|
||||
sendCommandAndData(0xC2, {0x44}); // Power Control 3 (For Normal Mode)
|
||||
sendCommandAndData(0xC5, {0x00, 0x00, 0x00, 0x00}); // VCOM Control
|
||||
sendCommandAndData(0xE0, {0x0F, 0x1F, 0x1C, 0x0C, 0x0F, 0x08, 0x48, 0x98, 0x37, 0x0A, 0x13, 0x04, 0x11, 0x0D, 0x00}); // PGAMCTRL(Positive Gamma Control)
|
||||
sendCommandAndData(0xE1, {0x0F, 0x32, 0x2E, 0x0B, 0x0D, 0x05, 0x47, 0x75, 0x37, 0x06, 0x10, 0x03, 0x24, 0x20, 0x00}); // NGAMCTRL (Negative Gamma Correction)
|
||||
sendCommandAndData(0xE2, {0x0F, 0x32, 0x2E, 0x0B, 0x0D, 0x05, 0x47, 0x75, 0x37, 0x06, 0x10, 0x03, 0x24, 0x20, 0x00}); // Digital Gamma Control 1
|
||||
sendCommandAndData(0x36, {0x28}); // Memory Access Control, BGR
|
||||
sendCommandAndData(0x11, {}), // # Sleep OUT
|
||||
sendCommandAndData(0x29, {}), // Display ON
|
||||
|
||||
wait();
|
||||
|
||||
|
||||
/*
|
||||
sendCommand(0x3A);
|
||||
sendData(0x55); // use 16 bits per pixel color
|
||||
sendCommand(0x36);
|
||||
sendData(0x48); // MX, BGR == rotation 0
|
||||
// PGAMCTRL(Positive Gamma Control)
|
||||
sendCommand(0xE0);
|
||||
sendData(0x0F);
|
||||
sendData(0x1F);
|
||||
sendData(0x1C);
|
||||
sendData(0x0C);
|
||||
sendData(0x0F);
|
||||
sendData(0x08);
|
||||
sendData(0x48);
|
||||
sendData(0x98);
|
||||
sendData(0x37);
|
||||
sendData(0x0A);
|
||||
sendData(0x13);
|
||||
sendData(0x04);
|
||||
sendData(0x11);
|
||||
sendData(0x0D);
|
||||
sendData(0x00);
|
||||
// NGAMCTRL(Negative Gamma Control)
|
||||
sendCommand(0xE1);
|
||||
sendData(0x0F);
|
||||
sendData(0x32);
|
||||
sendData(0x2E);
|
||||
sendData(0x0B);
|
||||
sendData(0x0D);
|
||||
sendData(0x05);
|
||||
sendData(0x47);
|
||||
sendData(0x75);
|
||||
sendData(0x37);
|
||||
sendData(0x06);
|
||||
sendData(0x10);
|
||||
sendData(0x03);
|
||||
sendData(0x24);
|
||||
sendData(0x20);
|
||||
sendData(0x00);
|
||||
// Digital Gamma Control 1
|
||||
sendCommand(0xE2);
|
||||
sendData(0x0F);
|
||||
sendData(0x32);
|
||||
sendData(0x2E);
|
||||
sendData(0x0B);
|
||||
sendData(0x0D);
|
||||
sendData(0x05);
|
||||
sendData(0x47);
|
||||
sendData(0x75);
|
||||
sendData(0x37);
|
||||
sendData(0x06);
|
||||
sendData(0x10);
|
||||
sendData(0x03);
|
||||
sendData(0x24);
|
||||
sendData(0x20);
|
||||
sendData(0x00);
|
||||
sendCommand(0x11); // Sleep OUT
|
||||
wait();
|
||||
sendCommand(0x29); // Display ON
|
||||
wait();
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
void sendCommandAndData(uint8_t cmd, std::initializer_list<uint8_t> data) {
|
||||
sendCommand(cmd);
|
||||
for (uint8_t b : data) {sendData(b);}
|
||||
}
|
||||
|
||||
uint32_t getID() {
|
||||
sendCommand(REG_RDDID);
|
||||
uint32_t res = 0xFFFFFFFF;
|
||||
spi.read(reinterpret_cast<uint8_t*>(&res), 4); // dummy-byte + 3 data bytes
|
||||
return res;
|
||||
}
|
||||
|
||||
void getStatus() {
|
||||
uint8_t buf[5];
|
||||
sendCommand(REG_RDDST);
|
||||
uint32_t res = 0xFFFFFFFF;
|
||||
spi.read(buf, 5); // dummy-byte + 4 data bytes
|
||||
printf("Status: %02x %02x %02x %02x \n", buf[1],buf[2],buf[3],buf[4]);
|
||||
}
|
||||
|
||||
/*
|
||||
void read() {
|
||||
chipSelect();
|
||||
setAddrWindow(0,0,100,100);
|
||||
sendCommand(REG_RAMRD);
|
||||
uint8_t buf[32];
|
||||
readBytes(buf, 32);
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
printf("%d ", buf[i]);
|
||||
}
|
||||
printf("\n");
|
||||
chipDeselect();
|
||||
}
|
||||
*/
|
||||
|
||||
void draw(uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint16_t* data) {
|
||||
setAddrWindow(x,y,w,h);
|
||||
modeDATA();
|
||||
chipSelect();
|
||||
spi.write((const uint8_t*)data, w*h*2);
|
||||
chipDeselect();
|
||||
}
|
||||
|
||||
void fillRand() {
|
||||
setAddrWindow(0,0,w,h);
|
||||
modeDATA();
|
||||
chipSelect();
|
||||
for (int y = 0; y < h; ++y) {
|
||||
uint16_t buf[w];
|
||||
for (int x = 0; x < w; ++x) {buf[x] = rand();}
|
||||
spi.write(reinterpret_cast<uint8_t*>(buf), w*2);
|
||||
}
|
||||
chipDeselect();
|
||||
}
|
||||
|
||||
void fillColor(uint16_t color) {
|
||||
setAddrWindow(0,0,w,h);
|
||||
modeDATA();
|
||||
chipSelect();
|
||||
for (uint32_t i = 0; i < w*h; ++i) {
|
||||
spi.writeWord(color);
|
||||
}
|
||||
chipDeselect();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void wait() {
|
||||
vTaskDelay(250 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
void setAddrWindow(const uint16_t x1, const uint16_t y1, const uint16_t w, const uint16_t h) {
|
||||
|
||||
// end (x,y)
|
||||
uint16_t x2 = x1 + w - 1;
|
||||
uint16_t y2 = y1 + h - 1;
|
||||
|
||||
sendCommand(REG_CASET); // Column addr set
|
||||
sendData(x1 >> 8); //Set the horizontal starting point to the high octet
|
||||
sendData(x1 & 0xff); //Set the horizontal starting point to the low octet
|
||||
sendData(x2 >> 8); //Set the horizontal end to the high octet
|
||||
sendData(x2 & 0xff); //Set the horizontal end to the low octet
|
||||
|
||||
sendCommand(REG_PASET); // Row addr set
|
||||
sendData(y1 >> 8);
|
||||
sendData(y1 & 0xff );
|
||||
sendData(y2 >> 8);
|
||||
sendData(y2 & 0xff);
|
||||
|
||||
sendCommand(REG_RAMWR); // write to RAM
|
||||
// transmit data now
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/** send the given command to the display */
|
||||
void sendCommand(const uint8_t cmd) {
|
||||
modeCMD();
|
||||
chipSelect();
|
||||
spi.writeByte(0x00); // 16 bit transfer!
|
||||
spi.writeByte(cmd);
|
||||
chipDeselect();
|
||||
}
|
||||
|
||||
/** send the given data to the display */
|
||||
void sendData(const uint8_t data) {
|
||||
modeDATA();
|
||||
chipSelect();
|
||||
spi.writeByte(0x00); // 16 bit transfer!
|
||||
spi.writeByte(data);
|
||||
chipDeselect();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// /** send the given data to the display */
|
||||
// void sendBytes(const uint8_t* data, const size_t len) {
|
||||
// spi.write(data, len);
|
||||
// }
|
||||
|
||||
// /** read the given data from the display */
|
||||
// void readBytes(uint8_t* data, const size_t len) {
|
||||
// spi.read(data, len);
|
||||
// }
|
||||
|
||||
/** select the display (CS=0) */
|
||||
inline void chipSelect() {
|
||||
//asm("nop");asm("nop");asm("nop");asm("nop");
|
||||
MyGPIO::clear(PIN_CS);
|
||||
//asm("nop");asm("nop");asm("nop");asm("nop");
|
||||
}
|
||||
|
||||
/** unselect the display (CS=1) */
|
||||
inline void chipDeselect() {
|
||||
//asm("nop");asm("nop");asm("nop");asm("nop");
|
||||
MyGPIO::set(PIN_CS);
|
||||
//asm("nop");asm("nop");asm("nop");asm("nop");
|
||||
}
|
||||
|
||||
/** switch to command-mode */
|
||||
inline void modeCMD() {
|
||||
//asm("nop");asm("nop");asm("nop");asm("nop");
|
||||
MyGPIO::clear(PIN_DATA_COMMAND);
|
||||
//asm("nop");asm("nop");asm("nop");asm("nop");
|
||||
}
|
||||
|
||||
/** switch to data-mode */
|
||||
inline void modeDATA() {
|
||||
//asm("nop");asm("nop");asm("nop");asm("nop");
|
||||
MyGPIO::set(PIN_DATA_COMMAND);
|
||||
//asm("nop");asm("nop");asm("nop");asm("nop");
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
490
ext/lcd/ILI9486p.h
Normal file
490
ext/lcd/ILI9486p.h
Normal file
@@ -0,0 +1,490 @@
|
||||
#ifndef LCD_ILI9486p
|
||||
#define LCD_ILI9486p
|
||||
|
||||
#include "../../io/GPIO.h"
|
||||
#include "../../Debug.h"
|
||||
#include <initializer_list>
|
||||
|
||||
// https://www.displayfuture.com/Display/datasheet/controller/ILI9486L.pdf
|
||||
|
||||
// http://www.lcdwiki.com/3.5inch_Arduino_Display-UNO
|
||||
|
||||
// https://github.com/ZinggJM/ILI9486_SPI/blob/master/src/ILI9486_SPI.cpp
|
||||
|
||||
|
||||
|
||||
// PARALLEL VERSION WITH 8 BIT 8080 BUS
|
||||
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("Os")
|
||||
|
||||
|
||||
template <int PIN_RESET, int PIN_CS, int PIN_RS, int PIN_WR, int PIN_RD> class ILI9486p {
|
||||
|
||||
private:
|
||||
|
||||
/*
|
||||
static constexpr const uint8_t PIN_D0 = 15;
|
||||
static constexpr const uint8_t PIN_D1 = 14;
|
||||
static constexpr const uint8_t PIN_D2 = 21;
|
||||
static constexpr const uint8_t PIN_D3 = 20;
|
||||
static constexpr const uint8_t PIN_D4 = 19;
|
||||
static constexpr const uint8_t PIN_D5 = 18;
|
||||
static constexpr const uint8_t PIN_D6 = 17;
|
||||
static constexpr const uint8_t PIN_D7 = 16;
|
||||
|
||||
static constexpr const uint8_t PIN_B0 = CORE_PIN15_BIT;
|
||||
static constexpr const uint8_t PIN_B1 = CORE_PIN14_BIT;
|
||||
static constexpr const uint8_t PIN_B2 = CORE_PIN21_BIT;
|
||||
static constexpr const uint8_t PIN_B3 = CORE_PIN20_BIT;
|
||||
static constexpr const uint8_t PIN_B4 = CORE_PIN19_BIT;
|
||||
static constexpr const uint8_t PIN_B5 = CORE_PIN18_BIT;
|
||||
static constexpr const uint8_t PIN_B6 = CORE_PIN17_BIT;
|
||||
static constexpr const uint8_t PIN_B7 = CORE_PIN16_BIT;
|
||||
*/
|
||||
static constexpr const uint8_t PIN_D0 = 19;
|
||||
static constexpr const uint8_t PIN_D1 = 18;
|
||||
static constexpr const uint8_t PIN_D2 = 14;
|
||||
static constexpr const uint8_t PIN_D3 = 15;
|
||||
static constexpr const uint8_t PIN_D4 = 40;
|
||||
static constexpr const uint8_t PIN_D5 = 41;
|
||||
static constexpr const uint8_t PIN_D6 = 17;
|
||||
static constexpr const uint8_t PIN_D7 = 16;
|
||||
static constexpr const uint8_t PIN_B0 = CORE_PIN19_BIT;
|
||||
static constexpr const uint8_t PIN_B1 = CORE_PIN18_BIT;
|
||||
static constexpr const uint8_t PIN_B2 = CORE_PIN14_BIT;
|
||||
static constexpr const uint8_t PIN_B3 = CORE_PIN15_BIT;
|
||||
static constexpr const uint8_t PIN_B4 = CORE_PIN40_BIT;
|
||||
static constexpr const uint8_t PIN_B5 = CORE_PIN41_BIT;
|
||||
static constexpr const uint8_t PIN_B6 = CORE_PIN17_BIT;
|
||||
static constexpr const uint8_t PIN_B7 = CORE_PIN16_BIT;
|
||||
|
||||
int w = 320;
|
||||
int h = 240;
|
||||
|
||||
static constexpr const char* MOD = "ILI9486p";
|
||||
|
||||
static constexpr const uint8_t REG_SWRESET = 0x01; // Software Reset
|
||||
static constexpr const uint8_t REG_RDDID = 0x04; // Read display identification information
|
||||
static constexpr const uint8_t REG_RDDST = 0x09; // Read Display Status
|
||||
static constexpr const uint8_t REG_CASET = 0x2A; // Column Address Set
|
||||
static constexpr const uint8_t REG_PASET = 0x2B; // Page Address Set
|
||||
static constexpr const uint8_t REG_RAMWR = 0x2C; // Memory Write
|
||||
static constexpr const uint8_t REG_RAMRD = 0x2E; // Memory Read
|
||||
|
||||
char pbuf[64];
|
||||
|
||||
public:
|
||||
|
||||
ILI9486p() {
|
||||
|
||||
if (PIN_RESET > 0) {MyGPIO::setOutput(PIN_RESET);}
|
||||
if (PIN_CS > 0) {MyGPIO::setOutput(PIN_CS);}
|
||||
MyGPIO::setOutput(PIN_RS);
|
||||
MyGPIO::setOutput(PIN_WR);
|
||||
MyGPIO::setOutput(PIN_RD);
|
||||
|
||||
if (PIN_CS > 0) {MyGPIO::set(PIN_CS);}
|
||||
MyGPIO::set(PIN_RD);
|
||||
MyGPIO::set(PIN_WR);
|
||||
|
||||
outMode();
|
||||
chipSelect();
|
||||
|
||||
/*
|
||||
// see manual page 666
|
||||
uint32_t pus = 0b10;
|
||||
uint32_t pue = 0b1;
|
||||
uint32_t pke = 0;
|
||||
uint32_t ode = 1;
|
||||
uint32_t speed = 0b00;
|
||||
uint32_t dse = 0b111;
|
||||
uint32_t sre = 0b0;
|
||||
uint32_t s = (pus<<14)|(pue<<13)|(pke<<12)|(ode<<11)|(sre<<0)|(dse<<3)|(speed<<6);
|
||||
|
||||
|
||||
CORE_PIN14_PADCONFIG = s;
|
||||
CORE_PIN15_PADCONFIG = s;
|
||||
CORE_PIN16_PADCONFIG = s;
|
||||
CORE_PIN17_PADCONFIG = s;
|
||||
CORE_PIN18_PADCONFIG = s;
|
||||
CORE_PIN19_PADCONFIG = s;
|
||||
CORE_PIN40_PADCONFIG = s;
|
||||
CORE_PIN41_PADCONFIG = s;
|
||||
|
||||
CORE_PIN20_PADCONFIG = s;
|
||||
CORE_PIN21_PADCONFIG = s;
|
||||
CORE_PIN39_PADCONFIG = s;
|
||||
CORE_PIN38_PADCONFIG = s;
|
||||
*/
|
||||
|
||||
IOMUXC_GPR_GPR26 = 0xffffffff;
|
||||
|
||||
}
|
||||
|
||||
/** perform display software reset and initialization */
|
||||
void init() {
|
||||
|
||||
hardwareReset();
|
||||
|
||||
// send reset command
|
||||
wait();
|
||||
sendCommand(REG_SWRESET);
|
||||
wait();
|
||||
|
||||
//sendCommandAndData(0xb0, {0x00}); // Interface Mode Control
|
||||
sendCommandAndData(0x11, {}); // disable sleep
|
||||
|
||||
wait();
|
||||
|
||||
sendCommandAndData(0x3A, {0x55}); // Interface Pixel Format, 16 bits / pixel
|
||||
sendCommandAndData(0x36, {0x28}); // Memory Access Control
|
||||
sendCommandAndData(0xC2, {0x44}); // Power Control 3 (For Normal Mode)
|
||||
sendCommandAndData(0xC5, {0x00, 0x00, 0x00, 0x00}); // VCOM Control
|
||||
sendCommandAndData(0xE0, {0x0F, 0x1F, 0x1C, 0x0C, 0x0F, 0x08, 0x48, 0x98, 0x37, 0x0A, 0x13, 0x04, 0x11, 0x0D, 0x00}); // PGAMCTRL(Positive Gamma Control)
|
||||
sendCommandAndData(0xE1, {0x0F, 0x32, 0x2E, 0x0B, 0x0D, 0x05, 0x47, 0x75, 0x37, 0x06, 0x10, 0x03, 0x24, 0x20, 0x00}); // NGAMCTRL (Negative Gamma Correction)
|
||||
sendCommandAndData(0xE2, {0x0F, 0x32, 0x2E, 0x0B, 0x0D, 0x05, 0x47, 0x75, 0x37, 0x06, 0x10, 0x03, 0x24, 0x20, 0x00}); // Digital Gamma Control 1
|
||||
sendCommandAndData(0x36, {0x28}); // Memory Access Control, BGR
|
||||
sendCommandAndData(0x11, {}), // # Sleep OUT
|
||||
sendCommandAndData(0x29, {}), // Display ON
|
||||
|
||||
wait();
|
||||
|
||||
}
|
||||
|
||||
void sendCommandAndData(uint8_t cmd, std::initializer_list<uint8_t> data) {
|
||||
sendCommand(cmd);
|
||||
for (uint8_t b : data) {sendData(b);}
|
||||
}
|
||||
|
||||
/*
|
||||
uint32_t getID() {
|
||||
outMode();
|
||||
sendCommand(REG_RDDID);
|
||||
inMode();
|
||||
uint32_t res = 0xFFFFFFFF;
|
||||
readBytes(reinterpret_cast<uint8_t*>(&res), 4); // dummy-byte + 3 data bytes
|
||||
return res;
|
||||
}
|
||||
|
||||
void getStatus() {
|
||||
uint8_t buf[5];
|
||||
//chipSelect();
|
||||
outMode();
|
||||
sendCommand(REG_RDDST);
|
||||
uint32_t res = 0xFFFFFFFF;
|
||||
inMode();
|
||||
readBytes(buf, 5); // dummy-byte + 4 data bytes
|
||||
sprintf(pbuf, "Status: %02x %02x %02x %02x \n", buf[1],buf[2],buf[3],buf[4]);
|
||||
Serial.print(pbuf);
|
||||
//chipDeselect();
|
||||
}
|
||||
*/
|
||||
|
||||
void fill(const uint16_t color) {
|
||||
setAddrWindow(0,0,480,320);
|
||||
modeDATA();
|
||||
for (uint32_t i = 0; i < 480*320; ++i) {
|
||||
writeByte(color>>8); writeByte(color>>0);
|
||||
}
|
||||
}
|
||||
|
||||
/** draw 5-6-5 encoded input data */
|
||||
void draw(uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint16_t* data) {
|
||||
setAddrWindow(x,y,w,h);
|
||||
modeDATA();
|
||||
const uint32_t entries = uint32_t(w) * uint32_t(h);
|
||||
for (uint32_t i = 0; i < entries; ++i) {
|
||||
writeByte(data[i]>>8); writeByte(data[i]>>0);
|
||||
}
|
||||
}
|
||||
|
||||
/** draw 5-6-5 encoded input, stretch X by 2 */
|
||||
void draw565x2(uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint16_t* data) {
|
||||
setAddrWindow(x,y,w,h);
|
||||
modeDATA();
|
||||
for (uint32_t i = 0; i < w/2*h; ++i) {
|
||||
writeByte(data[i]>>8); writeByte(data[i]>>0);
|
||||
writeByte(data[i]>>8); writeByte(data[i]>>0);
|
||||
}
|
||||
}
|
||||
|
||||
void beginDraw(uint16_t x, uint16_t y, uint16_t w, uint16_t h) {
|
||||
setAddrWindow(x,y,w,h);
|
||||
modeDATA();
|
||||
}
|
||||
void drawData(uint16_t* data, uint32_t len) {
|
||||
for (uint32_t i = 0; i < len; ++i) {
|
||||
writeByte(data[i]>>8); writeByte(data[i]>>0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** draw 5-6-5 encoded input, stretch X and Y by 2 */
|
||||
void draw565x2y2(uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint16_t* data) {
|
||||
setAddrWindow(x,y,w,h);
|
||||
modeDATA();
|
||||
|
||||
uint32_t i = 0;
|
||||
for (uint16_t y = 0; y < h/2; ++y) {
|
||||
for (uint16_t x = 0; x < w/2; ++x) {
|
||||
writeByte(data[i+x]>>8); writeByte(data[i+x]>>0);
|
||||
writeByte(data[i+x]>>8); writeByte(data[i+x]>>0);
|
||||
}
|
||||
for (uint16_t x = 0; x < w/2; ++x) {
|
||||
writeByte(data[i+x]>>8); writeByte(data[i+x]>>0);
|
||||
writeByte(data[i+x]>>8); writeByte(data[i+x]>>0);
|
||||
}
|
||||
i += w/2;
|
||||
}
|
||||
}
|
||||
|
||||
/** draw 3-3-2 encoded input data */
|
||||
void draw332(uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint8_t* data) {
|
||||
setAddrWindow(x,y,w,h);
|
||||
modeDATA();
|
||||
for (uint32_t i = 0; i < w*h; ++i) {
|
||||
const uint8_t t = data[i];
|
||||
const uint16_t c565 = (t&0b11100000)<<8 | (t&0b000111000)<<6 | (t&0b00000011)<<3;
|
||||
writeByte(c565 >> 8);
|
||||
writeByte(c565 >> 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void fillRand() {
|
||||
setAddrWindow(0,0,w,h);
|
||||
modeDATA();
|
||||
const uint32_t entries = uint32_t(w) * uint32_t(h);
|
||||
for (uint32_t i = 0; i < entries; ++i) {
|
||||
const uint16_t rnd = rand();
|
||||
writeByte(rnd>>8); writeByte(rnd>>0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
void wait() {
|
||||
//vTaskDelay(250 / portTICK_PERIOD_MS);
|
||||
delay(250);
|
||||
}
|
||||
|
||||
void setAddrWindow(const uint16_t x1, const uint16_t y1, const uint16_t w, const uint16_t h) {
|
||||
|
||||
// end (x,y)
|
||||
const uint16_t x2 = x1 + w - 1;
|
||||
const uint16_t y2 = y1 + h - 1;
|
||||
|
||||
sendCommand(REG_CASET); // Column addr set
|
||||
sendData(x1 >> 8); //Set the horizontal starting point to the high octet
|
||||
sendData(x1 & 0xff); //Set the horizontal starting point to the low octet
|
||||
sendData(x2 >> 8); //Set the horizontal end to the high octet
|
||||
sendData(x2 & 0xff); //Set the horizontal end to the low octet
|
||||
|
||||
sendCommand(REG_PASET); // Row addr set
|
||||
sendData(y1 >> 8);
|
||||
sendData(y1 & 0xff );
|
||||
sendData(y2 >> 8);
|
||||
sendData(y2 & 0xff);
|
||||
|
||||
sendCommand(REG_RAMWR); // write to RAM
|
||||
|
||||
// transmit data now
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/** send the given command to the display */
|
||||
void sendCommand(const uint8_t cmd) {
|
||||
modeCMD();
|
||||
writeByte(cmd);
|
||||
}
|
||||
|
||||
/** send the given data to the display */
|
||||
void sendData(const uint8_t data) {
|
||||
modeDATA();
|
||||
writeByte(data);
|
||||
}
|
||||
|
||||
/** send the given data to the display */
|
||||
void writeBytes(const uint8_t* data, const uint32_t len) {
|
||||
for (uint32_t i = 0; i < len; ++i) {
|
||||
writeByte(data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// /** read the given data from the display */
|
||||
// void readBytes(uint8_t* data, const uint32_t len) {
|
||||
// for (uint32_t i = 0; i < len; ++i) {
|
||||
// data[i] = readByte();
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
/*
|
||||
void inMode() {
|
||||
MyGPIO::setInput(PIN_D0);
|
||||
MyGPIO::setInput(PIN_D1);
|
||||
MyGPIO::setInput(PIN_D2);
|
||||
MyGPIO::setInput(PIN_D3);
|
||||
MyGPIO::setInput(PIN_D4);
|
||||
MyGPIO::setInput(PIN_D5);
|
||||
MyGPIO::setInput(PIN_D6);
|
||||
MyGPIO::setInput(PIN_D7);
|
||||
}
|
||||
|
||||
|
||||
uint8_t readByte() {
|
||||
|
||||
waitShort();
|
||||
modeDATA();
|
||||
|
||||
chipSelect();
|
||||
|
||||
MyGPIO::clear(PIN_RD); // commit
|
||||
waitShort();
|
||||
MyGPIO::set(PIN_RD);
|
||||
waitShort();
|
||||
|
||||
uint8_t tmp =
|
||||
(MyGPIO::get(PIN_D0) << 0) |
|
||||
(MyGPIO::get(PIN_D1) << 1) |
|
||||
(MyGPIO::get(PIN_D2) << 2) |
|
||||
(MyGPIO::get(PIN_D3) << 3) |
|
||||
(MyGPIO::get(PIN_D4) << 4) |
|
||||
(MyGPIO::get(PIN_D5) << 5) |
|
||||
(MyGPIO::get(PIN_D6) << 6) |
|
||||
(MyGPIO::get(PIN_D7) << 7);
|
||||
|
||||
chipDeselect();
|
||||
|
||||
return tmp;
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
void outMode() {
|
||||
MyGPIO::setOutput(PIN_D0);
|
||||
MyGPIO::setOutput(PIN_D1);
|
||||
MyGPIO::setOutput(PIN_D2);
|
||||
MyGPIO::setOutput(PIN_D3);
|
||||
MyGPIO::setOutput(PIN_D4);
|
||||
MyGPIO::setOutput(PIN_D5);
|
||||
MyGPIO::setOutput(PIN_D6);
|
||||
MyGPIO::setOutput(PIN_D7);
|
||||
}
|
||||
|
||||
//static constexpr uint32_t mask = 0b11111111 << 16;
|
||||
static constexpr uint32_t mask = 0b11111111 << 16;
|
||||
|
||||
uint8_t lastSent = 0;
|
||||
|
||||
inline void writeByte(uint8_t b) {
|
||||
|
||||
// fastest option and does not require reading from the bus??
|
||||
GPIO6_DR_TOGGLE = (lastSent ^ b) << 16;
|
||||
lastSent = b;
|
||||
|
||||
//GPIO6_DR = (GPIO6_DR & ~mask) | (b << 16);
|
||||
|
||||
// commit on rising edge
|
||||
MyGPIO::clear(PIN_WR);
|
||||
MyGPIO::set(PIN_WR);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
static constexpr uint32_t mask = (1<<PIN_B0) | (1<<PIN_B1) | (1<<PIN_B2) | (1<<PIN_B3) | (1<<PIN_B4) | (1<<PIN_B5) | (1<<PIN_B6) | (1<<PIN_B7);
|
||||
const uint32_t toSet =
|
||||
(b>>0 & 1)<<PIN_B0 |
|
||||
(b>>1 & 1)<<PIN_B1 |
|
||||
(b>>2 & 1)<<PIN_B2 |
|
||||
(b>>3 & 1)<<PIN_B3 |
|
||||
(b>>4 & 1)<<PIN_B4 |
|
||||
(b>>5 & 1)<<PIN_B5 |
|
||||
(b>>6 & 1)<<PIN_B6 |
|
||||
(b>>7 & 1)<<PIN_B7;
|
||||
GPIO6_DR = (GPIO6_DR & ~mask) | toSet;
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
//cli();
|
||||
|
||||
//GPIO6_DR = (GPIO6_DR & ~mask) | (b << 16);
|
||||
//GPIO6_DR = (b << 16);
|
||||
|
||||
//uint32_t tmp = (b << 16);
|
||||
|
||||
//GPIO6_DR_CLEAR = mask;
|
||||
//GPIO6_DR_SET = (b << 16) & mask;
|
||||
//asm("nop");
|
||||
//GPIO6_DR_CLEAR = mask & ~(b << 16);
|
||||
//GPIO6_DR_SET = (b << 16);
|
||||
|
||||
//volatile uint32_t* reg = &GPIO6_DR;
|
||||
//*reg = (*reg & ~mask) | (b << 16);
|
||||
//static constexpr volatile uint8_t* reg = (uint8_t *)((uint32_t)&GPIO6_DR + 3);
|
||||
//*reg = b;
|
||||
|
||||
|
||||
/*
|
||||
MyGPIO::setOrClear(PIN_D0, b & (1<<0));
|
||||
MyGPIO::setOrClear(PIN_D1, b & (1<<1));
|
||||
MyGPIO::setOrClear(PIN_D2, b & (1<<2));
|
||||
MyGPIO::setOrClear(PIN_D3, b & (1<<3));
|
||||
MyGPIO::setOrClear(PIN_D4, b & (1<<4));
|
||||
MyGPIO::setOrClear(PIN_D5, b & (1<<5));
|
||||
MyGPIO::setOrClear(PIN_D6, b & (1<<6));
|
||||
MyGPIO::setOrClear(PIN_D7, b & (1<<7));
|
||||
*/
|
||||
|
||||
inline void waitShort() {
|
||||
//asm("nop");
|
||||
}
|
||||
|
||||
/** perform hardware reset (pull down the reset pin for some time) */
|
||||
inline void hardwareReset() {
|
||||
if (PIN_RESET > 0) {
|
||||
MyGPIO::clear(PIN_RESET);
|
||||
delay(250);
|
||||
MyGPIO::set(PIN_RESET);
|
||||
delay(400);
|
||||
}
|
||||
}
|
||||
|
||||
/** select the display (CS=0) */
|
||||
inline void chipSelect() {
|
||||
if (PIN_CS > 0) {MyGPIO::clear(PIN_CS);}
|
||||
}
|
||||
|
||||
/** unselect the display (CS=1) */
|
||||
inline void chipDeselect() {
|
||||
if (PIN_CS > 0) {MyGPIO::set(PIN_CS);}
|
||||
}
|
||||
|
||||
/** switch to command-mode */
|
||||
inline void modeCMD() {
|
||||
MyGPIO::clear(PIN_RS);
|
||||
}
|
||||
|
||||
/** switch to data-mode */
|
||||
inline void modeDATA() {
|
||||
MyGPIO::set(PIN_RS);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
#pragma GCC pop_options
|
||||
|
||||
|
||||
#endif
|
||||
73
ext/sd/MBR.h
Normal file
73
ext/sd/MBR.h
Normal file
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
template <typename BlockDev> class MBR {
|
||||
|
||||
BlockDev& dev;
|
||||
bool present = false;
|
||||
bool valid = false;
|
||||
|
||||
/** describes one partition */
|
||||
class Partition {
|
||||
|
||||
friend class MBR;
|
||||
|
||||
uint8_t type;
|
||||
uint32_t firstSector;
|
||||
uint32_t numSectors;
|
||||
|
||||
public:
|
||||
|
||||
bool isPresent() const {return type != 0;}
|
||||
uint8_t getType() const {return type;}
|
||||
uint32_t getFirstSector() const {return firstSector;}
|
||||
uint32_t getNumSectors() const {return numSectors;}
|
||||
|
||||
};
|
||||
|
||||
Partition partitions[4];
|
||||
|
||||
public:
|
||||
|
||||
MBR(BlockDev& dev) : dev(dev) {
|
||||
read();
|
||||
}
|
||||
|
||||
bool isPresent() const {return present;}
|
||||
bool isValid() const {return valid;}
|
||||
|
||||
Partition& getPartition(uint8_t idx) {
|
||||
return partitions[idx];
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void read() {
|
||||
|
||||
uint8_t buf[512];
|
||||
dev.read(0x00000000, 512, buf);
|
||||
present = (buf[510] == 0x55) && (buf[511] == 0xAA);
|
||||
valid = (buf[0x01BC] == 0x00) && (buf[0x01BD] == 0x00);
|
||||
|
||||
if (present && valid) {
|
||||
|
||||
// parse all 4 partitions
|
||||
for (uint8_t i = 0; i < 4; ++i) {
|
||||
|
||||
const uint8_t* src = &buf[0x1BE + i*16];
|
||||
Partition* p = &partitions[i];
|
||||
|
||||
p->type = src[0x4];
|
||||
p->firstSector = getU32(src, 8);
|
||||
p->numSectors = getU32(src, 12);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uint32_t getU32(const uint8_t* src, const uint8_t offset) {
|
||||
return src[offset+0]<<0 | src[offset+1]<<8 | src[offset+2]<<16 | src[offset+3]<<24;
|
||||
}
|
||||
|
||||
};
|
||||
302
ext/sd/SDCard.h
Normal file
302
ext/sd/SDCard.h
Normal file
@@ -0,0 +1,302 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../io/GPIO.h"
|
||||
#include "../../Debug.h"
|
||||
|
||||
#define TEENSY_SD_PIN_CS 46
|
||||
#define TEENSY_SD_PIN_MOSI 45
|
||||
#define TEENSY_SD_PIN_CLK 44
|
||||
#define TEENSY_SD_PIN_MISO 43
|
||||
|
||||
|
||||
// http://www.dejazzer.com/ee379/lecture_notes/lec12_sd_card.pdf
|
||||
// https://www.convict.lu/pdf/ProdManualSDCardv1.9.pdf
|
||||
// http://rjhcoding.com/avrc-sd-interface-3.php
|
||||
template <typename SPI, int PIN_CS> class SDCard {
|
||||
|
||||
SPI& spi;
|
||||
|
||||
union R1 { // SanDisk Manual Page 5-13
|
||||
uint8_t raw;
|
||||
struct {
|
||||
uint8_t inIdleState : 1;
|
||||
uint8_t eraseReset : 1;
|
||||
uint8_t illegalCommand : 1;
|
||||
uint8_t crcError : 1;
|
||||
uint8_t eraseSeqError : 1;
|
||||
uint8_t addressError : 1;
|
||||
uint8_t paramError : 1;
|
||||
uint8_t zero : 1;
|
||||
};
|
||||
R1() {}
|
||||
R1(uint8_t val) : raw(val) {}
|
||||
};
|
||||
|
||||
union R3 { // response to OCR command, SanDisk Manual Page 5-14
|
||||
uint8_t raw[5];
|
||||
struct {
|
||||
R1 state;
|
||||
uint8_t busy : 1;
|
||||
uint8_t reserved : 7;
|
||||
uint32_t allowedVoltages: 20;
|
||||
uint8_t checkPattern : 4;
|
||||
};
|
||||
};
|
||||
|
||||
union Rcmd8 { // response to cmd8
|
||||
uint8_t raw[5];
|
||||
struct {
|
||||
R1 state;
|
||||
uint8_t data[4];
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
static constexpr const char* NAME = "SDCard";
|
||||
|
||||
public:
|
||||
|
||||
SDCard(SPI& spi) : spi(spi) {
|
||||
|
||||
}
|
||||
|
||||
bool init() {
|
||||
|
||||
MyGPIO::setOutput(PIN_CS);
|
||||
debugMod(NAME, "init()");
|
||||
|
||||
// RESET: MOSI = 1, CS = 1 (deselected!!), at least 74 Clocks
|
||||
deselect();
|
||||
for (uint8_t i = 0; i < 10; ++i) {spi.writeByte(0xFF);}
|
||||
|
||||
// switch to SPI mode
|
||||
for (uint8_t i = 0; ; ++i) {
|
||||
const R1 res = cmd0();
|
||||
if (res.raw == 1) {break;}
|
||||
if (i == 4) {debugMod(NAME, "init failed"); return false;}
|
||||
delay(50);
|
||||
}
|
||||
|
||||
|
||||
// TODO?
|
||||
// Version 2.0 Card
|
||||
Rcmd8 r8 = cmd8();
|
||||
|
||||
if (r8.state.raw == 1) {
|
||||
debugMod(NAME, "V2/V3 Card");
|
||||
if (r8.data[2] == 0x01 && r8.data[3] == 0xAA) {
|
||||
debugMod(NAME, "Pattern Correct");
|
||||
} else {
|
||||
debugMod(NAME, "Pattern Mismatch");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Version 1.0 Card
|
||||
//R3 res = cmd58();
|
||||
|
||||
|
||||
|
||||
uint32_t hcs = 1<<30;
|
||||
|
||||
for (uint8_t i = 0; ; ++i) {
|
||||
delay(100);
|
||||
R1 r1 = acmd41(hcs);
|
||||
if (i == 8) {debugMod(NAME, "init failed"); return false;}
|
||||
if (r1.raw == 0) {break;} // finished
|
||||
if (r1.raw == 1) {continue;} // card is still idle
|
||||
if (r1.raw > 1) {debugMod(NAME, "init failed"); return false;}
|
||||
}
|
||||
|
||||
debugMod(NAME, "init OK");
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
uint32_t read(uint32_t addr, uint32_t size, uint8_t* dst) {
|
||||
|
||||
uint32_t read = 0;
|
||||
uint32_t readAddr = addr & (0xFFFFFFFF - 512); // 512 byte aligned staring address
|
||||
|
||||
if (readAddr != addr) { // non-aligned first read?
|
||||
uint8_t buf[512];
|
||||
uint16_t cOff = (addr-readAddr); // skip the unneeded bytes
|
||||
uint16_t cSize = 512 - cOff;
|
||||
readSingleBlock(readAddr, buf); // read a full block
|
||||
memcpy(dst, buf+cOff, cSize); // copy only the required bytes
|
||||
size -= cSize;
|
||||
dst += cSize;
|
||||
read += cSize;
|
||||
readAddr += 512;
|
||||
}
|
||||
|
||||
while(size >= 512) {
|
||||
readSingleBlock(readAddr, dst);
|
||||
readAddr += 512;
|
||||
dst += 512;
|
||||
read += 512;
|
||||
size -= 512;
|
||||
}
|
||||
|
||||
if (size > 0) {
|
||||
uint8_t buf[512];
|
||||
readSingleBlock(readAddr, buf);
|
||||
memcpy(dst, buf, size);
|
||||
read += size;
|
||||
}
|
||||
|
||||
return read;
|
||||
|
||||
}
|
||||
|
||||
/** read a single block of 512 bytes */
|
||||
bool readSingleBlock(uint32_t addr, uint8_t* dst) {
|
||||
|
||||
addr = addr / 512;
|
||||
sendCMD(17, addr>>24, addr>>16, addr>>8, addr>>0, 0xFF);
|
||||
R1 res; readResponse(&res.raw, 1);
|
||||
debugMod2(NAME, "readSingleBlock(%08x): %02x", addr, res.raw);
|
||||
|
||||
// read command OK?
|
||||
if (res.raw != 0) {endCMD(); return false;}
|
||||
|
||||
// wait for data to become available
|
||||
for (uint16_t i = 0; ; ++i) {
|
||||
uint8_t res = spi.readWriteByte(0xFF);
|
||||
if (res == 0xFE) {break;} // available!
|
||||
if (res != 0xFF) {debugMod(NAME, "invalid"); endCMD(); return false;} // invalid response
|
||||
if (i > 1024) {debugMod(NAME, "timeout"); endCMD(); return false;} // timeout
|
||||
}
|
||||
|
||||
// read data
|
||||
for (uint16_t i = 0; i < 512; ++i) {
|
||||
dst[i] = spi.readWriteByte(0xFF);
|
||||
}
|
||||
|
||||
// done
|
||||
endCMD();
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
|
||||
/** send a new command */
|
||||
void sendCMD(uint8_t cmd, uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t arg3, uint8_t crc) {
|
||||
uint8_t buf[6];
|
||||
buf[0] = (0b01 << 6) | (cmd&0b111111);
|
||||
buf[1] = arg0;
|
||||
buf[2] = arg1;
|
||||
buf[3] = arg2;
|
||||
buf[4] = arg3;
|
||||
buf[5] = ((crc&0b1111111) << 1) | 1;
|
||||
select();
|
||||
spi.write(buf, 6);
|
||||
}
|
||||
|
||||
/** end the current command */
|
||||
void endCMD() {
|
||||
deselect();
|
||||
//spi.writeByte(0xFF); // SanDisk Manual Page 5-6 (required for card to finish current operation)
|
||||
}
|
||||
|
||||
|
||||
R1 cmd0() { // SanDisk Manual Page 5-1
|
||||
sendCMD(0, 0x00, 0x00, 0x00, 0x00, 0x4A);
|
||||
R1 res; readResponse(&res.raw, 1);
|
||||
endCMD();
|
||||
debugMod1(NAME, "cmd0: %02x", res.raw);
|
||||
return res;
|
||||
}
|
||||
|
||||
/** determine whether this is a V2 card?? */
|
||||
Rcmd8 cmd8() {
|
||||
sendCMD(8, 0x00, 0x00, 0x01, 0xAA, 0b1000011); // correct? Needs Checksum!!
|
||||
//select(); uint8_t tmp[] = {0b01001000, val>>24, val>>16, val>>8, val>>0, 0b00001111}; spi.write(tmp, 6);
|
||||
//R3 res; readResponse(&res.raw[0], 1); // read the R1 part
|
||||
//if (res.raw[0] == 1) { // command supported
|
||||
// readResponse(&res.raw[1], 4); // read the rest of the response
|
||||
//}
|
||||
Rcmd8 res; readResponse(res.raw, 5);
|
||||
endCMD();
|
||||
debugMod5(NAME, "cmd8: %02x %02x %02x %02x %02x", res.raw[0], res.raw[1], res.raw[2], res.raw[3], res.raw[4]);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** read OCR (Operating Conditions Register) */
|
||||
R3 cmd58() {
|
||||
//select(); uint8_t tmp[] = {0b01111010, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b01110101}; spi.write(tmp, 6);
|
||||
sendCMD(58, 0x00, 0x00, 0x00, 0x00, 0xFF);//0b0111010);
|
||||
R3 res; readResponse(res.raw, 5); // typical response: 0x01 0x00 0xff 0x80 0x00
|
||||
endCMD();
|
||||
debugMod5(NAME, "cmd58: %02x %02x %02x %02x %02x", res.raw[0], res.raw[1], res.raw[2], res.raw[3], res.raw[4]);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** "next command is app specific" */
|
||||
R1 cmd55() {
|
||||
sendCMD(55, 0x00, 0x00, 0x00, 0x00, 0xFF);
|
||||
R1 res; readResponse(&res.raw, 1);
|
||||
endCMD();
|
||||
debugMod1(NAME, "cmd55: %02x", res.raw);
|
||||
return res;
|
||||
}
|
||||
|
||||
R1 acmd41(uint32_t val) {
|
||||
cmd55();
|
||||
sendCMD(41, val>>24, val>>16, val>>8, val>>0, 0xFF);
|
||||
R1 res; readResponse(&res.raw, 1);
|
||||
endCMD();
|
||||
debugMod1(NAME, "acmd41: %02x", res.raw);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void readResponse(uint8_t* dst, uint8_t len) {
|
||||
|
||||
// the SD card takes some time to answer, but must receive clock signals meanwhile!
|
||||
// we detect the actual response as soon as the received byte starts has the highest bit zero.
|
||||
// [while busy the SD card responds with 0xFF]
|
||||
// NOTE: it is IMPORTANT to send 0xFF to the card while reading its responses!
|
||||
|
||||
// wait for the first byte to arrive and read it
|
||||
for (uint8_t i = 0; i < 4; ++i) {
|
||||
dst[0] = spi.readWriteByte(0xFF);
|
||||
if ( (dst[0] & 0x80) == 0 ) {break;}
|
||||
}
|
||||
|
||||
// read all subsequent bytes (if any)
|
||||
for (uint8_t i = 1; i < len; ++i) {
|
||||
dst[i] = spi.readWriteByte(0xFF);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** select the card (CS = low) */
|
||||
void select() {
|
||||
spi.writeByte(0xFF);
|
||||
MyGPIO::clear(PIN_CS);
|
||||
spi.writeByte(0xFF);
|
||||
}
|
||||
|
||||
/** deselect the card (CS = high) */
|
||||
void deselect() {
|
||||
spi.writeByte(0xFF);
|
||||
MyGPIO::set(PIN_CS);
|
||||
spi.writeByte(0xFF);
|
||||
}
|
||||
|
||||
};
|
||||
45
ext/sd/fat32/DirIterator.h
Normal file
45
ext/sd/fat32/DirIterator.h
Normal file
@@ -0,0 +1,45 @@
|
||||
class DirIterator {
|
||||
|
||||
FS& fs;
|
||||
ClusterNr nextCluster;
|
||||
int curEntryInCluster;
|
||||
uint8_t buf[512];
|
||||
|
||||
public:
|
||||
|
||||
DirIterator(FS& fs, ClusterNr clusterNr) : fs(fs), nextCluster(clusterNr), curEntryInCluster(255) {
|
||||
|
||||
}
|
||||
|
||||
bool hasNext() {
|
||||
|
||||
while(true) {
|
||||
|
||||
++curEntryInCluster;
|
||||
|
||||
// reached end of cluster? load the next one
|
||||
if (curEntryInCluster > fs.tmp.dirEntriesPerSector) {
|
||||
fs.dev.read(fs.clusterToAbsPos(nextCluster), 512, buf);
|
||||
nextCluster = fs.getNextCluster(nextCluster);
|
||||
curEntryInCluster = 0;
|
||||
}
|
||||
|
||||
DirEntry* desc = reinterpret_cast<DirEntry*>(buf + (sizeof(DirEntry) * curEntryInCluster));
|
||||
|
||||
if (desc->isLongFileName()) {continue;}
|
||||
if (desc->isUnused()) {continue;}
|
||||
if (desc->isEndOfDirectory()) {return false;}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DirEntry next() {
|
||||
DirEntry* de = reinterpret_cast<DirEntry*>(buf + (sizeof(DirEntry) * curEntryInCluster));
|
||||
return *de;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
100
ext/sd/fat32/FS.h
Normal file
100
ext/sd/fat32/FS.h
Normal file
@@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "Structs.h"
|
||||
|
||||
// https://www.pjrc.com/tech/8051/ide/fat32.html
|
||||
namespace FAT32 {
|
||||
|
||||
template <typename BlockDev> class FS {
|
||||
|
||||
static constexpr const char* NAME = "FAT32";
|
||||
|
||||
BlockDev& dev;
|
||||
AbsOffset offset;
|
||||
|
||||
|
||||
FSDesc desc;
|
||||
Precomputed tmp;
|
||||
bool valid = false;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
#include "File.h"
|
||||
#include "DirIterator.h"
|
||||
|
||||
/** ctor with the absolute offset addr (in bytes) */
|
||||
FS(BlockDev& dev, AbsOffset offset) : dev(dev), offset(offset) {
|
||||
init();
|
||||
}
|
||||
|
||||
/** is the detected FS valid? */
|
||||
bool isValid() const {
|
||||
return valid;
|
||||
}
|
||||
|
||||
/** get an iterator for the root directory */
|
||||
DirIterator getRoot() {
|
||||
return DirIterator(*this, desc.rootDirFirstCluster);
|
||||
}
|
||||
|
||||
/** open the given file for reading*/
|
||||
File open(const DirEntry& de) {
|
||||
return File(*this, de.getFirstCluster(), de.size);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void init() {
|
||||
|
||||
debugMod3(NAME")
|
||||
uint8_t buf[512];
|
||||
dev.read(offset, 512, buf);
|
||||
|
||||
desc.bytesPerSector = getU16(&buf[0x0B]);
|
||||
desc.sectorsPerCluster = getU8(&buf[0x0D]);
|
||||
desc.numReservedSectors = getU16(&buf[0x0E]);
|
||||
desc.numberOfFATs = getU8(&buf[0x10]);
|
||||
desc.sectorsPerFAT = getU32(&buf[0x24]);
|
||||
desc.rootDirFirstCluster = getU32(&buf[0x2C]);
|
||||
|
||||
// basic sanity check based on constants
|
||||
valid = (desc.bytesPerSector == 512) && (desc.numberOfFATs == 2) && (getU16(&buf[0x1FE]) == 0xAA55);
|
||||
|
||||
tmp.bytesPerCluster = desc.sectorsPerCluster * desc.bytesPerSector;
|
||||
tmp.startOfFAT = offset + (desc.numReservedSectors * desc.bytesPerSector);
|
||||
tmp.startOfFirstDataCluster = offset + (desc.numReservedSectors * desc.bytesPerSector) + (desc.numberOfFATs * desc.sectorsPerFAT * desc.bytesPerSector);
|
||||
tmp.startOfFirstRootDirCluster = clusterToAbsPos(desc.rootDirFirstCluster);
|
||||
tmp.dirEntriesPerSector = desc.bytesPerSector / sizeof(DirEntry);
|
||||
|
||||
/*
|
||||
std::cout << (int)desc.bytesPerSector << std::endl;
|
||||
std::cout << (int)desc.sectorsPerCluster << std::endl;
|
||||
std::cout << (int)desc.numReservedSectors << std::endl;
|
||||
std::cout << (int)desc.numberOfFATs << std::endl;
|
||||
std::cout << (int)desc.sectorsPerFAT << std::endl;
|
||||
std::cout << (int)desc.rootDirFirstCluster << std::endl;
|
||||
|
||||
std::cout << tmp.startOfFAT << std::endl;
|
||||
std::cout << tmp.startOfFirstDataCluster << std::endl;
|
||||
std::cout << tmp.startOfFirstRootDirCluster << std::endl;
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
/** determine the ClusterNr following the given ClusterNr */
|
||||
ClusterNr getNextCluster(ClusterNr clusterNr) {
|
||||
const AbsPos pos = tmp.startOfFAT + ((clusterNr) * sizeof(uint32_t));
|
||||
ClusterNr next;
|
||||
int read = dev.read(pos, 4, reinterpret_cast<uint8_t*>(&next));
|
||||
return next;
|
||||
}
|
||||
|
||||
/** convert ClusterNr into an absolute position on disk */
|
||||
AbsPos clusterToAbsPos(ClusterNr clusterNr) {
|
||||
return tmp.startOfFirstDataCluster + ((clusterNr - 2) * desc.sectorsPerCluster * desc.bytesPerSector);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
100
ext/sd/fat32/FS.h.autosave
Normal file
100
ext/sd/fat32/FS.h.autosave
Normal file
@@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "Structs.h"
|
||||
|
||||
// https://www.pjrc.com/tech/8051/ide/fat32.html
|
||||
namespace FAT32 {
|
||||
|
||||
template <typename BlockDev> class FS {
|
||||
|
||||
static constexpr const char* NAME = "FAT32";
|
||||
|
||||
BlockDev& dev;
|
||||
AbsOffset offset;
|
||||
|
||||
|
||||
FSDesc desc;
|
||||
Precomputed tmp;
|
||||
bool valid = false;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
#include "File.h"
|
||||
#include "DirIterator.h"
|
||||
|
||||
/** ctor with the absolute offset addr (in bytes) */
|
||||
FS(BlockDev& dev, AbsOffset offset) : dev(dev), offset(offset) {
|
||||
init();
|
||||
}
|
||||
|
||||
/** is the detected FS valid? */
|
||||
bool isValid() const {
|
||||
return valid;
|
||||
}
|
||||
|
||||
/** get an iterator for the root directory */
|
||||
DirIterator getRoot() {
|
||||
return DirIterator(*this, desc.rootDirFirstCluster);
|
||||
}
|
||||
|
||||
/** open the given file for reading*/
|
||||
File open(const DirEntry& de) {
|
||||
return File(*this, de.getFirstCluster(), de.size);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void init() {
|
||||
|
||||
debugMod3(NAME)
|
||||
uint8_t buf[512];
|
||||
dev.read(offset, 512, buf);
|
||||
|
||||
desc.bytesPerSector = getU16(&buf[0x0B]);
|
||||
desc.sectorsPerCluster = getU8(&buf[0x0D]);
|
||||
desc.numReservedSectors = getU16(&buf[0x0E]);
|
||||
desc.numberOfFATs = getU8(&buf[0x10]);
|
||||
desc.sectorsPerFAT = getU32(&buf[0x24]);
|
||||
desc.rootDirFirstCluster = getU32(&buf[0x2C]);
|
||||
|
||||
// basic sanity check based on constants
|
||||
valid = (desc.bytesPerSector == 512) && (desc.numberOfFATs == 2) && (getU16(&buf[0x1FE]) == 0xAA55);
|
||||
|
||||
tmp.bytesPerCluster = desc.sectorsPerCluster * desc.bytesPerSector;
|
||||
tmp.startOfFAT = offset + (desc.numReservedSectors * desc.bytesPerSector);
|
||||
tmp.startOfFirstDataCluster = offset + (desc.numReservedSectors * desc.bytesPerSector) + (desc.numberOfFATs * desc.sectorsPerFAT * desc.bytesPerSector);
|
||||
tmp.startOfFirstRootDirCluster = clusterToAbsPos(desc.rootDirFirstCluster);
|
||||
tmp.dirEntriesPerSector = desc.bytesPerSector / sizeof(DirEntry);
|
||||
|
||||
/*
|
||||
std::cout << (int)desc.bytesPerSector << std::endl;
|
||||
std::cout << (int)desc.sectorsPerCluster << std::endl;
|
||||
std::cout << (int)desc.numReservedSectors << std::endl;
|
||||
std::cout << (int)desc.numberOfFATs << std::endl;
|
||||
std::cout << (int)desc.sectorsPerFAT << std::endl;
|
||||
std::cout << (int)desc.rootDirFirstCluster << std::endl;
|
||||
|
||||
std::cout << tmp.startOfFAT << std::endl;
|
||||
std::cout << tmp.startOfFirstDataCluster << std::endl;
|
||||
std::cout << tmp.startOfFirstRootDirCluster << std::endl;
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
/** determine the ClusterNr following the given ClusterNr */
|
||||
ClusterNr getNextCluster(ClusterNr clusterNr) {
|
||||
const AbsPos pos = tmp.startOfFAT + ((clusterNr) * sizeof(uint32_t));
|
||||
ClusterNr next;
|
||||
int read = dev.read(pos, 4, reinterpret_cast<uint8_t*>(&next));
|
||||
return next;
|
||||
}
|
||||
|
||||
/** convert ClusterNr into an absolute position on disk */
|
||||
AbsPos clusterToAbsPos(ClusterNr clusterNr) {
|
||||
return tmp.startOfFirstDataCluster + ((clusterNr - 2) * desc.sectorsPerCluster * desc.bytesPerSector);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
64
ext/sd/fat32/File.h
Normal file
64
ext/sd/fat32/File.h
Normal file
@@ -0,0 +1,64 @@
|
||||
class File {
|
||||
|
||||
static constexpr const int32_t F_EOF = -1;
|
||||
|
||||
FS& fs;
|
||||
uint32_t totalSize;
|
||||
|
||||
uint32_t curAbsPos = 0; // position withithin the whole file
|
||||
uint32_t curCluster = 0; // cluster we are currently reading
|
||||
uint32_t posInCluster = 0; // position within the current cluster
|
||||
|
||||
|
||||
public:
|
||||
|
||||
File(FS& fs, uint32_t firstCluster, uint32_t size) : fs(fs), totalSize(size), curCluster(firstCluster) {
|
||||
|
||||
}
|
||||
|
||||
/** the file's size */
|
||||
uint32_t getSize() const {return totalSize;}
|
||||
|
||||
uint32_t read(uint32_t size, uint8_t* dst) {
|
||||
uint32_t total = 0;
|
||||
while(true) {
|
||||
const uint32_t read = _read(size, dst);
|
||||
if (read == F_EOF) {break;}
|
||||
size -= read;
|
||||
dst += read;
|
||||
total += read;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
int32_t _read(uint32_t readSize, uint8_t* dst) {
|
||||
|
||||
// EOF reached?
|
||||
if (curAbsPos >= totalSize) {
|
||||
return F_EOF;
|
||||
}
|
||||
|
||||
// end of current cluster reached? -> determine the next one
|
||||
if (posInCluster == fs.tmp.bytesPerCluster) {
|
||||
curCluster = fs.getNextCluster(curCluster);
|
||||
posInCluster = 0;
|
||||
}
|
||||
|
||||
// how many bytes are left in the current cluster?
|
||||
const uint32_t remainingInCluster = fs.tmp.bytesPerCluster - posInCluster;
|
||||
|
||||
// determine how many bytes to read
|
||||
const uint32_t toRead = std::min(remainingInCluster, readSize);
|
||||
|
||||
const uint32_t offset = fs.clusterToAbsPos(curCluster) + posInCluster;
|
||||
const uint32_t read = fs.dev.read(offset, toRead, dst);
|
||||
posInCluster += read;
|
||||
curAbsPos += read;
|
||||
|
||||
return read;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
77
ext/sd/fat32/Structs.h
Normal file
77
ext/sd/fat32/Structs.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace FAT32 {
|
||||
|
||||
using ClusterNr = uint32_t;
|
||||
using AbsOffset = uint32_t;
|
||||
using AbsPos = uint32_t;
|
||||
|
||||
struct FSDesc {
|
||||
uint16_t bytesPerSector;
|
||||
uint8_t sectorsPerCluster;
|
||||
uint16_t numReservedSectors;
|
||||
uint8_t numberOfFATs;
|
||||
uint32_t sectorsPerFAT;
|
||||
ClusterNr rootDirFirstCluster;
|
||||
};
|
||||
|
||||
struct Precomputed {
|
||||
uint32_t bytesPerCluster;
|
||||
AbsPos startOfFAT; // absolute byte offset where the FAT begins
|
||||
AbsPos startOfFirstDataCluster; // absolute byte offset where the first cluster is
|
||||
AbsPos startOfFirstRootDirCluster; // absolute byte offset where the first root dir cluster is
|
||||
uint32_t dirEntriesPerSector;
|
||||
};
|
||||
|
||||
union Attributes {
|
||||
uint8_t raw;
|
||||
struct {
|
||||
uint8_t readOnly : 1;
|
||||
uint8_t hidden : 1;
|
||||
uint8_t system : 1;
|
||||
uint8_t volumeID : 1;
|
||||
uint8_t directory : 1;
|
||||
uint8_t archive : 1;
|
||||
uint8_t unused1 : 1;
|
||||
uint8_t unused2 : 1;
|
||||
} bits;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct DirEntry {
|
||||
|
||||
unsigned char name[8];
|
||||
unsigned char ext[3];
|
||||
Attributes attr;
|
||||
uint8_t dummy1[8];
|
||||
uint16_t firstClusterHi;
|
||||
uint8_t dummy2[4];
|
||||
uint16_t firstClusterLo;
|
||||
uint32_t size; // file size in bytes
|
||||
|
||||
bool isUnused() const {return name[0] == 0xE5;}
|
||||
bool isDirectory() const {return attr.bits.directory;}
|
||||
bool isEndOfDirectory() const {return name[0] == 0x00;}
|
||||
bool isLongFileName() const {return (attr.raw & 0b1111) == 0b1111;}
|
||||
|
||||
uint32_t getFirstCluster() const {return firstClusterHi<<16 | firstClusterLo<<0;}
|
||||
|
||||
std::string getName() const {
|
||||
char buf[16];
|
||||
uint8_t pos = 0;
|
||||
for (uint8_t i = 0; i < 8; ++i) {if (name[i] != ' ') {buf[pos++] = name[i];}}
|
||||
buf[pos++] = '.';
|
||||
for (uint8_t i = 0; i < 8; ++i) {if ( ext[i] != ' ') {buf[pos++] = ext[i];}}
|
||||
buf[pos] = 0;
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
static inline uint16_t getU8(const uint8_t* src) {return src[0];}
|
||||
static inline uint16_t getU16(const uint8_t* src) {return src[0]<<0 | src[1]<<8;}
|
||||
static inline uint32_t getU32(const uint8_t* src) {return src[0]<<0 | src[1]<<8 | src[2]<<16 | src[3]<<24;}
|
||||
|
||||
}
|
||||
66
ext/sd/main.cpp
Normal file
66
ext/sd/main.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
|
||||
#include "MBR.h"
|
||||
#include "fat32/FS.h"
|
||||
|
||||
class Simu {
|
||||
FILE* f;
|
||||
public:
|
||||
|
||||
Simu(const char* image) {
|
||||
f = fopen(image, "rb");
|
||||
if (!f) {throw std::runtime_error("failed to open");}
|
||||
}
|
||||
|
||||
uint32_t read(uint32_t addr, uint32_t size, uint8_t* dst) {
|
||||
fseek(f, addr, SEEK_SET);
|
||||
return fread(dst, size, 1, f) * size;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
int main(void) {
|
||||
|
||||
// diff /tmp/ram/TETRIS.GB /apps/workspace/gbemu/tests/tetris.gb
|
||||
|
||||
Simu simu("/tmp/ram/1.dat");
|
||||
|
||||
MBR<Simu> mbr(simu);
|
||||
std::cout << mbr.isPresent() << std::endl;
|
||||
std::cout << mbr.isValid() << std::endl;
|
||||
|
||||
if (mbr.isPresent() && mbr.isValid()) {
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
std::cout << (int) mbr.getPartition(i).getType() << " - " << mbr.getPartition(i).getFirstSector() << " - " << mbr.getPartition(i).getNumSectors() << std::endl;
|
||||
}
|
||||
|
||||
using FAT32FS = FAT32::FS<Simu>;
|
||||
|
||||
FAT32FS fat(simu, mbr.getPartition(0).getFirstSector() * 512);
|
||||
std::cout << "valid: " << fat.isValid() << std::endl;
|
||||
|
||||
FAT32FS::DirIterator dir = fat.getRoot();
|
||||
while(dir.hasNext()) {
|
||||
FAT32::DirEntry de = dir.next();
|
||||
std::cout << de.getName() << std::endl;
|
||||
FAT32FS::File f = fat.open(de);
|
||||
|
||||
uint8_t* bufff = (uint8_t*) malloc(1024*1024);
|
||||
uint32_t read = f.read(f.getSize(), bufff);
|
||||
|
||||
std::string name = de.getName();
|
||||
std::ofstream out("/tmp/ram/" + name);
|
||||
out.write((char*)bufff, read);
|
||||
out.close();
|
||||
|
||||
}
|
||||
|
||||
// diff /tmp/ram/TETRIS.GB /apps/workspace/gbemu/tests/tetris.gb
|
||||
// diif /tmp/ram/KIRBY1.GB /apps/workspace/gbemu/tests/Kirby\'s\ Dream\ Land\ \(USA\,\ Europe\).gb
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -60,6 +60,7 @@ public:
|
||||
return i2c.query(ADDR);
|
||||
}
|
||||
|
||||
/*
|
||||
void dumpConfig() {
|
||||
const uint16_t cfg = getConfig();
|
||||
const uint8_t mode = (cfg & 0b00000000000111) >> 0;
|
||||
@@ -75,6 +76,7 @@ public:
|
||||
if (brng) {printf("- bus voltage range [0:16]\n");}
|
||||
else {printf("- bus voltage range [0:32]\n");}
|
||||
}
|
||||
*/
|
||||
|
||||
private:
|
||||
|
||||
|
||||
14
io/GPIO.h
14
io/GPIO.h
@@ -184,15 +184,19 @@
|
||||
|
||||
public:
|
||||
|
||||
static void setOutput(uint8_t pin) {pinMode(pin, OUTPUT);}
|
||||
static inline void setOutput(uint8_t pin) {pinMode(pin, OUTPUT);}
|
||||
|
||||
static void setInput(uint8_t pin) {pinMode(pin, INPUT);}
|
||||
static inline void setInput(uint8_t pin) {pinMode(pin, INPUT);}
|
||||
|
||||
static void clear(uint8_t pin) {digitalWriteFast(pin, 0);}
|
||||
static inline void clear(uint8_t pin) {digitalWriteFast(pin, 0);}
|
||||
|
||||
static inline void set(uint8_t pin) {digitalWriteFast(pin, 1);}
|
||||
|
||||
static inline void setOrClear(uint8_t pin, uint8_t val) {digitalWriteFast(pin, val);}
|
||||
|
||||
static inline uint8_t get(uint8_t pin) {return digitalReadFast(pin);}
|
||||
|
||||
static void set(uint8_t pin) {digitalWriteFast(pin, 1);}
|
||||
|
||||
static uint8_t get(uint8_t pin) {return digitalReadFast(pin);}
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ private:
|
||||
|
||||
// DEVICE specific options
|
||||
memset(&devcfg, 0, sizeof(devcfg));
|
||||
devcfg.clock_speed_hz = 20 * 1000 * 1000; // IMPORTANT!
|
||||
devcfg.clock_speed_hz = 30 * 1000 * 1000; // IMPORTANT!
|
||||
devcfg.mode = 0;
|
||||
devcfg.spics_io_num = -1;//PIN_NUM_CS; // currently not used
|
||||
devcfg.queue_size = 2;
|
||||
|
||||
14
io/SoftI2C.h
14
io/SoftI2C.h
@@ -24,7 +24,7 @@ template <int PIN_SDA, int PIN_SCL, bool fast> class SoftI2C {
|
||||
inline void sclHi() {MyGPIO::set(PIN_SCL);}
|
||||
inline void sclLo() {MyGPIO::clear(PIN_SCL);}
|
||||
|
||||
inline void waitLong() {
|
||||
void waitLong() {
|
||||
if (fast) {
|
||||
for (uint8_t i = 0; i < 128; ++i) {
|
||||
__asm__ __volatile__("nop");
|
||||
@@ -36,7 +36,7 @@ template <int PIN_SDA, int PIN_SCL, bool fast> class SoftI2C {
|
||||
}
|
||||
}
|
||||
|
||||
inline void waitShort() {
|
||||
void waitShort() {
|
||||
if (fast) {
|
||||
for (uint8_t i = 0; i < 16; ++i) {
|
||||
__asm__ __volatile__("nop");
|
||||
@@ -114,13 +114,13 @@ public:
|
||||
|
||||
|
||||
// write one byte to the bus and check slave's ACK/NACK
|
||||
bool IN_FLASH writeByteAndCheck(const uint8_t byte) {
|
||||
bool writeByteAndCheck(const uint8_t byte) {
|
||||
_writeByte(byte);
|
||||
return _readAck();
|
||||
}
|
||||
|
||||
// write several bytes to the bus and check slave's ACK/NACK
|
||||
bool IN_FLASH writeBytesAndCheck(const uint8_t* src, uint8_t len) {
|
||||
bool writeBytesAndCheck(const uint8_t* src, uint8_t len) {
|
||||
while(len) {
|
||||
const bool ok = writeByteAndCheck(*src);
|
||||
if (!ok) {return false;}
|
||||
@@ -157,7 +157,7 @@ public:
|
||||
debugMod1(NAME, "found %d", i)
|
||||
}
|
||||
waitLong();
|
||||
if (i%16 == 0) {vTaskDelay(100 / portTICK_PERIOD_MS);}
|
||||
if (i%16 == 0) {delay(10);}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,9 +242,10 @@ private:
|
||||
sclHi(); // clock pulse and read
|
||||
waitShort();
|
||||
const uint8_t val = sdaRead();
|
||||
waitShort();
|
||||
sclLo();
|
||||
waitShort();
|
||||
return val;
|
||||
return val ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -267,6 +268,7 @@ private:
|
||||
|
||||
inline bool _readAck() {
|
||||
sdaDirIn(); // switch to input mode
|
||||
waitShort();
|
||||
const uint8_t res = _readBit();
|
||||
//printf("%d\n", res);
|
||||
return res == 0; // ACK when input pulled low
|
||||
|
||||
25
io/SoftSPI.h
25
io/SoftSPI.h
@@ -6,13 +6,18 @@
|
||||
#include "../Debug.h"
|
||||
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("O2")
|
||||
#pragma GCC optimize ("O3")
|
||||
|
||||
|
||||
template <int PIN_MISO, int PIN_MOSI, int PIN_CLK, bool fast> class SoftSPI {
|
||||
// NOTE: the last template argument has changed from "bool fast" to "int slowdown" where 0 is now fastest!
|
||||
template <int PIN_MISO, int PIN_MOSI, int PIN_CLK, int SLOWDOWN> class SoftSPI {
|
||||
|
||||
static constexpr const char* NAME = "softSPI";
|
||||
|
||||
#ifndef BIT
|
||||
#define BIT(x) (1<<x)
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
@@ -21,6 +26,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void init() {
|
||||
debugMod3(NAME, "init() MISO:%d MOSI:%d CLK:%d", PIN_MISO, PIN_MOSI, PIN_CLK);
|
||||
if (PIN_MISO) {MyGPIO::setInput(PIN_MISO);}
|
||||
@@ -30,16 +36,15 @@ private:
|
||||
|
||||
private:
|
||||
|
||||
|
||||
static inline void wait() {
|
||||
if (!fast) {
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
__asm__ __volatile__("nop");
|
||||
}
|
||||
for (int i = 0; i < SLOWDOWN; ++i) {
|
||||
__asm__ __volatile__("nop");
|
||||
}
|
||||
}
|
||||
|
||||
inline void clkLo() { MyGPIO::clear(PIN_CLK); }
|
||||
inline void clkHi() { MyGPIO::set(PIN_CLK); }
|
||||
static inline void clkLo() { MyGPIO::clear(PIN_CLK); }
|
||||
static inline void clkHi() { MyGPIO::set(PIN_CLK); }
|
||||
|
||||
/** write one bit to the bus */
|
||||
inline void writeBit(const bool out) {
|
||||
@@ -52,7 +57,7 @@ private:
|
||||
}
|
||||
|
||||
/** read one bit from the bus */
|
||||
inline uint8_t readBit() {
|
||||
static inline uint8_t readBit() {
|
||||
clkHi();
|
||||
wait();
|
||||
const bool val = MyGPIO::get(PIN_MISO);
|
||||
@@ -62,7 +67,7 @@ private:
|
||||
return (val) ? 1 : 0;
|
||||
}
|
||||
|
||||
inline uint8_t readWriteBit(const bool out) {
|
||||
static inline uint8_t readWriteBit(const bool out) {
|
||||
if(out) {MyGPIO::set(PIN_MOSI);} else {MyGPIO::clear(PIN_MOSI);}
|
||||
wait();
|
||||
clkHi();
|
||||
|
||||
Reference in New Issue
Block a user