805 lines
25 KiB
C++
805 lines
25 KiB
C++
//<App !Start!>
|
|
// FILE: [sd_card_formatter.ino]
|
|
// Created by GUIslice Builder version: [0.15.b004]
|
|
//
|
|
// GUIslice Builder Generated File
|
|
//
|
|
// For the latest guides, updates and support view:
|
|
// https://github.com/ImpulseAdventure/GUIslice
|
|
//
|
|
//<App !End!>
|
|
|
|
// ------------------------------------------------
|
|
// Headers to include
|
|
// ------------------------------------------------
|
|
//<Includes !Start!>
|
|
// Varous system includes
|
|
#include <Arduino.h>
|
|
|
|
// Various library includes
|
|
#include <Adafruit_NeoPixel.h>
|
|
#include <Adafruit_GFX.h>
|
|
#include <Adafruit_ILI9341.h>
|
|
#include <BBQ10Keyboard.h>
|
|
#include <CircularBuffer.h>
|
|
|
|
// Various local includes
|
|
#include "sd_card_formatter_GSLC.h"
|
|
|
|
//<Includes !End!>
|
|
|
|
// ------------------------------------------------
|
|
// Program Globals
|
|
// ------------------------------------------------
|
|
|
|
// Battery level measurement
|
|
#define VBATPIN A6
|
|
float measuredVBat;
|
|
int batteryPercent;
|
|
|
|
// TFT Setup
|
|
#define TFT_CS 9
|
|
#define TFT_DC 10
|
|
Adafruit_ILI9341 tft(TFT_CS, TFT_DC);
|
|
|
|
// Keyboard
|
|
BBQ10Keyboard keyboard;
|
|
#define KBD_BTN_1 0x06
|
|
#define KBD_BTN_2 0x11
|
|
#define KBD_BTN_3 0x07
|
|
#define KBD_BTN_4 0x12
|
|
#define KBD_SW_UP 0x01
|
|
#define KBD_SW_DN 0x02
|
|
#define KBD_SW_LF 0x03
|
|
#define KBD_SW_RT 0x04
|
|
#define KBD_SW_OK 0x05
|
|
CircularBuffer<int16_t,8> uiKeyBuffer;
|
|
|
|
// NeoPixels
|
|
#define PIXELS_NUM_BOARD 1
|
|
#define PIXELS_NUM_WING 1
|
|
#define PIXELS_WING_PIN 11
|
|
Adafruit_NeoPixel pixels_board(PIXELS_NUM_BOARD, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800);
|
|
Adafruit_NeoPixel pixels_wing(PIXELS_NUM_WING, PIXELS_WING_PIN, NEO_GRB + NEO_KHZ800);
|
|
|
|
// GUI state globals
|
|
bool popupOnScreen = false;
|
|
|
|
// Various loop handlers
|
|
void handlerKeyboard();
|
|
void handlerBatteryLevel();
|
|
void eraseAndFormatCard();
|
|
|
|
// Save some element references for direct access
|
|
//<Save_References !Start!>
|
|
gslc_tsElemRef* m_pElemBatteryLevel= NULL;
|
|
gslc_tsElemRef* m_pElemBtSDNo = NULL;
|
|
gslc_tsElemRef* m_pElemBtSDYes = NULL;
|
|
gslc_tsElemRef* m_pElemSDInfo = NULL;
|
|
gslc_tsElemRef* m_pElemStatusText = NULL;
|
|
//<Save_References !End!>
|
|
|
|
// Keyboard map related
|
|
#define MAX_INPUT_MAP 5
|
|
gslc_tsInputMap m_asInputMap[MAX_INPUT_MAP];
|
|
|
|
// Define debug message function
|
|
static int16_t DebugOut(char ch) { if (ch == (char)'\n') Serial.println(""); else Serial.write(ch); return 0; }
|
|
|
|
// ------------------------------------------------
|
|
// Callback Methods
|
|
// ------------------------------------------------
|
|
// Common Button callback
|
|
bool CbBtnCommon(void* pvGui,void *pvElemRef,gslc_teTouch eTouch,int16_t nX,int16_t nY)
|
|
{
|
|
// Typecast the parameters to match the GUI and element types
|
|
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
|
|
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
|
|
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
|
|
|
|
if ( eTouch == GSLC_TOUCH_UP_IN ) {
|
|
// From the element's ID we can determine which button was pressed.
|
|
switch (pElem->nId) {
|
|
//<Button Enums !Start!>
|
|
case E_ELEM_BTN_SD_FMT:
|
|
gslc_PopupShow(&m_gui, E_PG_SD_CONFIRM, false);
|
|
popupOnScreen = true;
|
|
break;
|
|
case E_ELEM_SD_NO:
|
|
gslc_PopupHide(&m_gui);
|
|
popupOnScreen = false;
|
|
break;
|
|
case E_ELEM_SD_YES:
|
|
gslc_PopupHide(&m_gui);
|
|
popupOnScreen = false;
|
|
gslc_ElemXTextboxReset(&m_gui, m_pElemSDInfo);
|
|
gslc_ElemXTextboxAdd(&m_gui, m_pElemSDInfo, "Formatting SD Card\n");
|
|
gslc_Update(&m_gui);
|
|
eraseAndFormatCard();
|
|
gslc_ElemXTextboxAdd(&m_gui, m_pElemSDInfo, "Formatting Complete\n");
|
|
gslc_Update(&m_gui);
|
|
break;
|
|
//<Button Enums !End!>
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
//<Checkbox Callback !Start!>
|
|
//<Checkbox Callback !End!>
|
|
//<Keypad Callback !Start!>
|
|
//<Keypad Callback !End!>
|
|
//<Spinner Callback !Start!>
|
|
//<Spinner Callback !End!>
|
|
//<Listbox Callback !Start!>
|
|
//<Listbox Callback !End!>
|
|
//<Draw Callback !Start!>
|
|
//<Draw Callback !End!>
|
|
//<Slider Enums !Start!>
|
|
//<Slider Enums !End!>
|
|
//<Tick Callback !Start!>
|
|
//<Tick Callback !End!>
|
|
|
|
// Keyboard Input polling callback function
|
|
bool CbKbdPoll(void* pvGui, int16_t* pnPinInd, int16_t* pnPinVal) {
|
|
// Check for new keyboard events just in case
|
|
handlerKeyboard();
|
|
|
|
// If the key buffer is empty no events to process
|
|
if (uiKeyBuffer.isEmpty()) {
|
|
return false;
|
|
}
|
|
|
|
// Process a key that's on the key buffer for the UI event(s)
|
|
*pnPinInd = uiKeyBuffer.pop();
|
|
*pnPinVal = 1;
|
|
|
|
return true;
|
|
}
|
|
|
|
// ------------------------------------------------
|
|
// Setup
|
|
// ------------------------------------------------
|
|
void setup() {
|
|
|
|
Serial.begin(115200);
|
|
|
|
// If GUI Slice debug of drivers is turned on, wait for serial so we get proper debug output
|
|
#if defined(DBG_LOG) || defined(DBG_TOUCH) || defined(DBG_FRAME_RATE) || defined(DBG_DRAW_IMM) || defined(DBG_DRIVER)
|
|
while (!Serial) {
|
|
;
|
|
}
|
|
#endif
|
|
|
|
// GUI Slice debugging
|
|
gslc_InitDebug(&DebugOut);
|
|
|
|
// Setup red LED to indicate device is on (in case we disable NeoPixel battery level later)
|
|
pinMode(3, OUTPUT);
|
|
digitalWrite(3, HIGH);
|
|
|
|
// Setup NeoPixels
|
|
pixels_board.begin();
|
|
pixels_wing.begin();
|
|
pixels_board.clear();
|
|
pixels_wing.clear();
|
|
pixels_board.setBrightness(10);
|
|
pixels_wing.setBrightness(5);
|
|
|
|
// Setup neopixels to indicate we are initializing gui slice
|
|
// This can serve as an indication something has gone wrong like the sd card is missing
|
|
pixels_wing.setPixelColor(0, pixels_board.Color(255, 0, 0));
|
|
pixels_board.setPixelColor(0, pixels_board.Color(255, 0, 0));
|
|
pixels_wing.show();
|
|
pixels_board.show();
|
|
|
|
// Setup BBQ10Keyboard
|
|
Wire.begin();
|
|
keyboard.begin();
|
|
keyboard.setBacklight(0.5f);
|
|
|
|
// Init GUI Slicle
|
|
InitGUIslice_gen();
|
|
|
|
// Set the pin poll callback function
|
|
gslc_SetPinPollFunc(&m_gui, CbKbdPoll);
|
|
|
|
// Create the GUI input mapping (pin event to GUI action)
|
|
gslc_InitInputMap(&m_gui, m_asInputMap, MAX_INPUT_MAP);
|
|
gslc_InputMapAdd(&m_gui, GSLC_INPUT_PIN_ASSERT, KBD_SW_UP, GSLC_ACTION_FOCUS_NEXT, 0);
|
|
gslc_InputMapAdd(&m_gui, GSLC_INPUT_PIN_ASSERT, KBD_SW_DN, GSLC_ACTION_FOCUS_PREV, 0);
|
|
gslc_InputMapAdd(&m_gui, GSLC_INPUT_PIN_ASSERT, KBD_SW_LF, GSLC_ACTION_FOCUS_NEXT, 0);
|
|
gslc_InputMapAdd(&m_gui, GSLC_INPUT_PIN_ASSERT, KBD_SW_RT, GSLC_ACTION_FOCUS_PREV, 0);
|
|
gslc_InputMapAdd(&m_gui, GSLC_INPUT_PIN_ASSERT, KBD_SW_OK, GSLC_ACTION_SELECT, 0);
|
|
|
|
// Flip to main screen
|
|
// This wouldn't be necessary if we hadn't chopped off the formatter from the main _controller project and actually updated the main screen ;)
|
|
gslc_SetPageCur(&m_gui, E_SD_CARD);
|
|
|
|
// Set neopixels to standard start state
|
|
pixels_wing.clear();
|
|
pixels_wing.show();
|
|
|
|
pixels_board.setPixelColor(0, pixels_board.Color(0, 0, 255));
|
|
pixels_board.show();
|
|
}
|
|
|
|
// -----------------------------------
|
|
// Main event loop
|
|
// -----------------------------------
|
|
void loop() {
|
|
// Handle keyboard events
|
|
handlerKeyboard();
|
|
|
|
// Update battery level as appropriate
|
|
handlerBatteryLevel();
|
|
|
|
gslc_Update(&m_gui);
|
|
}
|
|
|
|
// ------------------------------------------------
|
|
// Keyboard Handler
|
|
// ------------------------------------------------
|
|
void handlerKeyboard() {
|
|
// Don't do anything if there are no key presses to process
|
|
if (keyboard.keyCount() == 0) {
|
|
return;
|
|
}
|
|
|
|
// Check if keys were pressed and drain queue of pressed keys
|
|
while (keyboard.keyCount() > 0) {
|
|
// Get keyboard event
|
|
const BBQ10Keyboard::KeyEvent key = keyboard.keyEvent();
|
|
|
|
// Process key press events
|
|
if (key.state == BBQ10Keyboard::StatePress) {
|
|
// Add keys to the key buffer that are mapped to UI functions (only for config screens)
|
|
if (key.key == KBD_SW_UP
|
|
|| key.key == KBD_SW_DN
|
|
|| key.key == KBD_SW_LF
|
|
|| key.key == KBD_SW_RT
|
|
|| key.key == KBD_SW_OK) {
|
|
uiKeyBuffer.push(key.key);
|
|
}
|
|
}
|
|
|
|
// Pick color for signaling a key was pressed (short or long press)
|
|
uint32_t keyboard_pixel_color = pixels_wing.Color(0, 0, 255);
|
|
if (key.state == BBQ10Keyboard::StateLongPress) {
|
|
keyboard_pixel_color = pixels_wing.Color(0, 255, 255);
|
|
}
|
|
|
|
// Display indicator accordingly
|
|
if (key.state == BBQ10Keyboard::StatePress || key.state == BBQ10Keyboard::StateLongPress) {
|
|
pixels_wing.setPixelColor(0, keyboard_pixel_color);
|
|
pixels_wing.show();
|
|
}
|
|
else {
|
|
pixels_wing.clear();
|
|
pixels_wing.show();
|
|
}
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------
|
|
// Battery level handler
|
|
// ------------------------------------------------
|
|
void handlerBatteryLevel() {
|
|
measuredVBat = analogRead(VBATPIN);
|
|
measuredVBat *= 2; // we divided by 2, so multiply back
|
|
measuredVBat *= 3.3; // Multiply by 3.3V, our reference voltage
|
|
measuredVBat /= 1024; // convert to voltage
|
|
batteryPercent = (measuredVBat / 3.3) * 100;
|
|
|
|
gslc_tsColor colorForHeaderElements;
|
|
if (batteryPercent >= 75) {
|
|
pixels_board.setPixelColor(0, pixels_board.Color(0, 255, 0));
|
|
colorForHeaderElements = GSLC_COL_GREEN;
|
|
}
|
|
else if (batteryPercent >= 50) {
|
|
colorForHeaderElements = GSLC_COL_YELLOW;
|
|
pixels_board.setPixelColor(0, pixels_board.Color(255, 255, 0));
|
|
}
|
|
else if (batteryPercent >= 25) {
|
|
colorForHeaderElements = GSLC_COL_ORANGE;
|
|
pixels_board.setPixelColor(0, pixels_board.Color(255, 128, 0));
|
|
}
|
|
else {
|
|
colorForHeaderElements = GSLC_COL_RED;
|
|
pixels_board.setPixelColor(0, pixels_board.Color(255, 0, 0));
|
|
}
|
|
|
|
gslc_ElemXProgressSetVal(&m_gui, m_pElemBatteryLevel, batteryPercent);
|
|
gslc_tsXProgress* pGauge = (gslc_tsXProgress*)gslc_GetXDataFromRef(&m_gui,m_pElemBatteryLevel,GSLC_TYPEX_PROGRESS,__LINE__);
|
|
pGauge->colGauge = colorForHeaderElements;
|
|
gslc_ElemSetTxtCol(&m_gui, m_pElemStatusText, colorForHeaderElements);
|
|
|
|
// Update GUI and NeoPixel with battery status information
|
|
pixels_board.show();
|
|
}
|
|
|
|
// ------------------------------------------------
|
|
// Format SD Card
|
|
// ------------------------------------------------
|
|
/*
|
|
* This program will format an SD or SDHC card.
|
|
* Warning all data will be deleted!
|
|
*
|
|
* For SD/SDHC cards larger than 64 MB this
|
|
* program attempts to match the format
|
|
* generated by SDFormatter available here:
|
|
*
|
|
* http://www.sdcard.org/consumers/formatter/
|
|
*
|
|
* For smaller cards this program uses FAT16
|
|
* and SDFormatter uses FAT12.
|
|
*/
|
|
|
|
// Set USE_SDIO to zero for SPI card access.
|
|
#define USE_SDIO 0
|
|
//
|
|
// Change the value of chipSelect if your hardware does
|
|
// not use the default value, SS. Common values are:
|
|
// Arduino Ethernet shield: pin 4
|
|
// Sparkfun SD shield: pin 8
|
|
// Adafruit SD shields and modules: pin 10
|
|
const uint8_t chipSelect = 5;
|
|
|
|
// Initialize at highest supported speed not over 50 MHz.
|
|
// Reduce max speed if errors occur.
|
|
#define SPI_SPEED SD_SCK_MHZ(50)
|
|
|
|
// Print extra info for debug if DEBUG_PRINT is nonzero
|
|
#include <SPI.h>
|
|
#include "src/SdFat/SdFat.h"
|
|
#include "src/SdFat/sdios.h"
|
|
|
|
#if USE_SDIO
|
|
// Use faster SdioCardEX
|
|
SdioCardEX card;
|
|
// SdioCard card;
|
|
#else // USE_SDIO
|
|
Sd2Card card;
|
|
#endif // USE_SDIO
|
|
|
|
uint32_t cardSizeBlocks;
|
|
uint32_t cardCapacityMB;
|
|
|
|
// cache for SD block
|
|
cache_t cache;
|
|
|
|
// MBR information
|
|
uint8_t partType;
|
|
uint32_t relSector;
|
|
uint32_t partSize;
|
|
|
|
// Fake disk geometry
|
|
uint8_t numberOfHeads;
|
|
uint8_t sectorsPerTrack;
|
|
|
|
// FAT parameters
|
|
uint16_t reservedSectors;
|
|
uint8_t sectorsPerCluster;
|
|
uint32_t fatStart;
|
|
uint32_t fatSize;
|
|
uint32_t dataStart;
|
|
|
|
// constants for file system structure
|
|
uint16_t const BU16 = 128;
|
|
uint16_t const BU32 = 8192;
|
|
|
|
// strings needed in file system structures
|
|
char noName[] = "NO NAME ";
|
|
char fat16str[] = "FAT16 ";
|
|
char fat32str[] = "FAT32 ";
|
|
//------------------------------------------------------------------------------
|
|
void sdError(char* msg) {
|
|
gslc_ElemXTextboxAdd(&m_gui, m_pElemSDInfo, "error: ");
|
|
gslc_ElemXTextboxAdd(&m_gui, m_pElemSDInfo, msg);
|
|
gslc_ElemXTextboxAdd(&m_gui, m_pElemSDInfo, "\n");
|
|
gslc_Update(&m_gui);
|
|
|
|
sdErrorHalt();
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void sdErrorHalt() {
|
|
if (card.errorCode()) {
|
|
char textForDisplay[32];
|
|
snprintf(textForDisplay, 32, "SD error: %d\n", int(card.errorCode()));
|
|
gslc_ElemXTextboxAdd(&m_gui, m_pElemSDInfo, textForDisplay);
|
|
gslc_Update(&m_gui);
|
|
}
|
|
SysCall::halt();
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
// write cached block to the card
|
|
uint8_t writeCache(uint32_t lbn) {
|
|
return card.writeBlock(lbn, cache.data);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
// initialize appropriate sizes for SD capacity
|
|
void initSizes() {
|
|
if (cardCapacityMB <= 6) {
|
|
sdError("Card is too small.");
|
|
} else if (cardCapacityMB <= 16) {
|
|
sectorsPerCluster = 2;
|
|
} else if (cardCapacityMB <= 32) {
|
|
sectorsPerCluster = 4;
|
|
} else if (cardCapacityMB <= 64) {
|
|
sectorsPerCluster = 8;
|
|
} else if (cardCapacityMB <= 128) {
|
|
sectorsPerCluster = 16;
|
|
} else if (cardCapacityMB <= 1024) {
|
|
sectorsPerCluster = 32;
|
|
} else if (cardCapacityMB <= 32768) {
|
|
sectorsPerCluster = 64;
|
|
} else {
|
|
// SDXC cards
|
|
sectorsPerCluster = 128;
|
|
}
|
|
|
|
char textForDisplay[64];
|
|
snprintf(textForDisplay, 64, "Blocks/Cluster: %d\n", int(sectorsPerCluster));
|
|
gslc_ElemXTextboxAdd(&m_gui, m_pElemSDInfo, textForDisplay);
|
|
gslc_Update(&m_gui);
|
|
|
|
// set fake disk geometry
|
|
sectorsPerTrack = cardCapacityMB <= 256 ? 32 : 63;
|
|
|
|
if (cardCapacityMB <= 16) {
|
|
numberOfHeads = 2;
|
|
} else if (cardCapacityMB <= 32) {
|
|
numberOfHeads = 4;
|
|
} else if (cardCapacityMB <= 128) {
|
|
numberOfHeads = 8;
|
|
} else if (cardCapacityMB <= 504) {
|
|
numberOfHeads = 16;
|
|
} else if (cardCapacityMB <= 1008) {
|
|
numberOfHeads = 32;
|
|
} else if (cardCapacityMB <= 2016) {
|
|
numberOfHeads = 64;
|
|
} else if (cardCapacityMB <= 4032) {
|
|
numberOfHeads = 128;
|
|
} else {
|
|
numberOfHeads = 255;
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
// zero cache and optionally set the sector signature
|
|
void clearCache(uint8_t addSig) {
|
|
memset(&cache, 0, sizeof(cache));
|
|
if (addSig) {
|
|
cache.mbr.mbrSig0 = BOOTSIG0;
|
|
cache.mbr.mbrSig1 = BOOTSIG1;
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
// zero FAT and root dir area on SD
|
|
void clearFatDir(uint32_t bgn, uint32_t count) {
|
|
clearCache(false);
|
|
#if USE_SDIO
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
if (!card.writeBlock(bgn + i, cache.data)) {
|
|
sdError("Clear FAT/DIR writeBlock failed");
|
|
}
|
|
if ((i & 0XFF) == 0) {
|
|
gslc_ElemXTextboxAdd(&m_gui, m_pElemSDInfo, ".");
|
|
gslc_Update(&m_gui);
|
|
}
|
|
}
|
|
#else // USE_SDIO
|
|
if (!card.writeStart(bgn, count)) {
|
|
sdError("Clear FAT/DIR writeStart failed");
|
|
}
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
if ((i & 0XFF) == 0) {
|
|
gslc_ElemXTextboxAdd(&m_gui, m_pElemSDInfo, ".");
|
|
gslc_Update(&m_gui);
|
|
}
|
|
if (!card.writeData(cache.data)) {
|
|
sdError("Clear FAT/DIR writeData failed");
|
|
}
|
|
}
|
|
if (!card.writeStop()) {
|
|
sdError("Clear FAT/DIR writeStop failed");
|
|
}
|
|
#endif // USE_SDIO
|
|
gslc_ElemXTextboxAdd(&m_gui, m_pElemSDInfo, "\n");
|
|
gslc_Update(&m_gui);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
// return cylinder number for a logical block number
|
|
uint16_t lbnToCylinder(uint32_t lbn) {
|
|
return lbn / (numberOfHeads * sectorsPerTrack);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
// return head number for a logical block number
|
|
uint8_t lbnToHead(uint32_t lbn) {
|
|
return (lbn % (numberOfHeads * sectorsPerTrack)) / sectorsPerTrack;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
// return sector number for a logical block number
|
|
uint8_t lbnToSector(uint32_t lbn) {
|
|
return (lbn % sectorsPerTrack) + 1;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
// format and write the Master Boot Record
|
|
void writeMbr() {
|
|
clearCache(true);
|
|
part_t* p = cache.mbr.part;
|
|
p->boot = 0;
|
|
uint16_t c = lbnToCylinder(relSector);
|
|
if (c > 1023) {
|
|
sdError("MBR CHS");
|
|
}
|
|
p->beginCylinderHigh = c >> 8;
|
|
p->beginCylinderLow = c & 0XFF;
|
|
p->beginHead = lbnToHead(relSector);
|
|
p->beginSector = lbnToSector(relSector);
|
|
p->type = partType;
|
|
uint32_t endLbn = relSector + partSize - 1;
|
|
c = lbnToCylinder(endLbn);
|
|
if (c <= 1023) {
|
|
p->endCylinderHigh = c >> 8;
|
|
p->endCylinderLow = c & 0XFF;
|
|
p->endHead = lbnToHead(endLbn);
|
|
p->endSector = lbnToSector(endLbn);
|
|
} else {
|
|
// Too big flag, c = 1023, h = 254, s = 63
|
|
p->endCylinderHigh = 3;
|
|
p->endCylinderLow = 255;
|
|
p->endHead = 254;
|
|
p->endSector = 63;
|
|
}
|
|
p->firstSector = relSector;
|
|
p->totalSectors = partSize;
|
|
if (!writeCache(0)) {
|
|
sdError("write MBR");
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
// generate serial number from card size and micros since boot
|
|
uint32_t volSerialNumber() {
|
|
return (cardSizeBlocks << 8) + micros();
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
// format the SD as FAT16
|
|
void makeFat16() {
|
|
uint32_t nc;
|
|
for (dataStart = 2 * BU16;; dataStart += BU16) {
|
|
nc = (cardSizeBlocks - dataStart)/sectorsPerCluster;
|
|
fatSize = (nc + 2 + 255)/256;
|
|
uint32_t r = BU16 + 1 + 2 * fatSize + 32;
|
|
if (dataStart < r) {
|
|
continue;
|
|
}
|
|
relSector = dataStart - r + BU16;
|
|
break;
|
|
}
|
|
// check valid cluster count for FAT16 volume
|
|
if (nc < 4085 || nc >= 65525) {
|
|
sdError("Bad cluster count");
|
|
}
|
|
reservedSectors = 1;
|
|
fatStart = relSector + reservedSectors;
|
|
partSize = nc * sectorsPerCluster + 2 * fatSize + reservedSectors + 32;
|
|
if (partSize < 32680) {
|
|
partType = 0X01;
|
|
} else if (partSize < 65536) {
|
|
partType = 0X04;
|
|
} else {
|
|
partType = 0X06;
|
|
}
|
|
// write MBR
|
|
writeMbr();
|
|
clearCache(true);
|
|
fat_boot_t* pb = &cache.fbs;
|
|
pb->jump[0] = 0XEB;
|
|
pb->jump[1] = 0X00;
|
|
pb->jump[2] = 0X90;
|
|
for (uint8_t i = 0; i < sizeof(pb->oemId); i++) {
|
|
pb->oemId[i] = ' ';
|
|
}
|
|
pb->bytesPerSector = 512;
|
|
pb->sectorsPerCluster = sectorsPerCluster;
|
|
pb->reservedSectorCount = reservedSectors;
|
|
pb->fatCount = 2;
|
|
pb->rootDirEntryCount = 512;
|
|
pb->mediaType = 0XF8;
|
|
pb->sectorsPerFat16 = fatSize;
|
|
pb->sectorsPerTrack = sectorsPerTrack;
|
|
pb->headCount = numberOfHeads;
|
|
pb->hidddenSectors = relSector;
|
|
pb->totalSectors32 = partSize;
|
|
pb->driveNumber = 0X80;
|
|
pb->bootSignature = EXTENDED_BOOT_SIG;
|
|
pb->volumeSerialNumber = volSerialNumber();
|
|
memcpy(pb->volumeLabel, noName, sizeof(pb->volumeLabel));
|
|
memcpy(pb->fileSystemType, fat16str, sizeof(pb->fileSystemType));
|
|
// write partition boot sector
|
|
if (!writeCache(relSector)) {
|
|
sdError("FAT16 write PBS failed");
|
|
}
|
|
// clear FAT and root directory
|
|
clearFatDir(fatStart, dataStart - fatStart);
|
|
clearCache(false);
|
|
cache.fat16[0] = 0XFFF8;
|
|
cache.fat16[1] = 0XFFFF;
|
|
// write first block of FAT and backup for reserved clusters
|
|
if (!writeCache(fatStart)
|
|
|| !writeCache(fatStart + fatSize)) {
|
|
sdError("FAT16 reserve failed");
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
// format the SD as FAT32
|
|
void makeFat32() {
|
|
uint32_t nc;
|
|
relSector = BU32;
|
|
for (dataStart = 2 * BU32;; dataStart += BU32) {
|
|
nc = (cardSizeBlocks - dataStart)/sectorsPerCluster;
|
|
fatSize = (nc + 2 + 127)/128;
|
|
uint32_t r = relSector + 9 + 2 * fatSize;
|
|
if (dataStart >= r) {
|
|
break;
|
|
}
|
|
}
|
|
// error if too few clusters in FAT32 volume
|
|
if (nc < 65525) {
|
|
sdError("Bad cluster count");
|
|
}
|
|
reservedSectors = dataStart - relSector - 2 * fatSize;
|
|
fatStart = relSector + reservedSectors;
|
|
partSize = nc * sectorsPerCluster + dataStart - relSector;
|
|
// type depends on address of end sector
|
|
// max CHS has lbn = 16450560 = 1024*255*63
|
|
if ((relSector + partSize) <= 16450560) {
|
|
// FAT32
|
|
partType = 0X0B;
|
|
} else {
|
|
// FAT32 with INT 13
|
|
partType = 0X0C;
|
|
}
|
|
writeMbr();
|
|
clearCache(true);
|
|
|
|
fat32_boot_t* pb = &cache.fbs32;
|
|
pb->jump[0] = 0XEB;
|
|
pb->jump[1] = 0X00;
|
|
pb->jump[2] = 0X90;
|
|
for (uint8_t i = 0; i < sizeof(pb->oemId); i++) {
|
|
pb->oemId[i] = ' ';
|
|
}
|
|
pb->bytesPerSector = 512;
|
|
pb->sectorsPerCluster = sectorsPerCluster;
|
|
pb->reservedSectorCount = reservedSectors;
|
|
pb->fatCount = 2;
|
|
pb->mediaType = 0XF8;
|
|
pb->sectorsPerTrack = sectorsPerTrack;
|
|
pb->headCount = numberOfHeads;
|
|
pb->hidddenSectors = relSector;
|
|
pb->totalSectors32 = partSize;
|
|
pb->sectorsPerFat32 = fatSize;
|
|
pb->fat32RootCluster = 2;
|
|
pb->fat32FSInfo = 1;
|
|
pb->fat32BackBootBlock = 6;
|
|
pb->driveNumber = 0X80;
|
|
pb->bootSignature = EXTENDED_BOOT_SIG;
|
|
pb->volumeSerialNumber = volSerialNumber();
|
|
memcpy(pb->volumeLabel, noName, sizeof(pb->volumeLabel));
|
|
memcpy(pb->fileSystemType, fat32str, sizeof(pb->fileSystemType));
|
|
// write partition boot sector and backup
|
|
if (!writeCache(relSector)
|
|
|| !writeCache(relSector + 6)) {
|
|
sdError("FAT32 write PBS failed");
|
|
}
|
|
clearCache(true);
|
|
// write extra boot area and backup
|
|
if (!writeCache(relSector + 2)
|
|
|| !writeCache(relSector + 8)) {
|
|
sdError("FAT32 PBS ext failed");
|
|
}
|
|
fat32_fsinfo_t* pf = &cache.fsinfo;
|
|
pf->leadSignature = FSINFO_LEAD_SIG;
|
|
pf->structSignature = FSINFO_STRUCT_SIG;
|
|
pf->freeCount = 0XFFFFFFFF;
|
|
pf->nextFree = 0XFFFFFFFF;
|
|
// write FSINFO sector and backup
|
|
if (!writeCache(relSector + 1)
|
|
|| !writeCache(relSector + 7)) {
|
|
sdError("FAT32 FSINFO failed");
|
|
}
|
|
clearFatDir(fatStart, 2 * fatSize + sectorsPerCluster);
|
|
clearCache(false);
|
|
cache.fat32[0] = 0x0FFFFFF8;
|
|
cache.fat32[1] = 0x0FFFFFFF;
|
|
cache.fat32[2] = 0x0FFFFFFF;
|
|
// write first block of FAT and backup for reserved clusters
|
|
if (!writeCache(fatStart)
|
|
|| !writeCache(fatStart + fatSize)) {
|
|
sdError("FAT32 reserve failed");
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
// flash erase all data
|
|
uint32_t const ERASE_SIZE = 262144L;
|
|
void eraseCard() {
|
|
gslc_ElemXTextboxAdd(&m_gui, m_pElemSDInfo, "Erasing\n");
|
|
gslc_Update(&m_gui);
|
|
uint32_t firstBlock = 0;
|
|
uint32_t lastBlock;
|
|
uint16_t n = 0;
|
|
|
|
do {
|
|
lastBlock = firstBlock + ERASE_SIZE - 1;
|
|
if (lastBlock >= cardSizeBlocks) {
|
|
lastBlock = cardSizeBlocks - 1;
|
|
}
|
|
if (!card.erase(firstBlock, lastBlock)) {
|
|
sdError("erase failed");
|
|
}
|
|
gslc_ElemXTextboxAdd(&m_gui, m_pElemSDInfo, ".");
|
|
gslc_Update(&m_gui);
|
|
if ((n++)%32 == 31) {
|
|
gslc_ElemXTextboxAdd(&m_gui, m_pElemSDInfo, "\n");
|
|
gslc_Update(&m_gui);
|
|
}
|
|
firstBlock += ERASE_SIZE;
|
|
} while (firstBlock < cardSizeBlocks);
|
|
gslc_ElemXTextboxAdd(&m_gui, m_pElemSDInfo, "\n");
|
|
gslc_Update(&m_gui);
|
|
|
|
if (!card.readBlock(0, cache.data)) {
|
|
sdError("readBlock");
|
|
}
|
|
gslc_ElemXTextboxAdd(&m_gui, m_pElemSDInfo, "Erase done\n");
|
|
gslc_Update(&m_gui);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void formatCard() {
|
|
gslc_ElemXTextboxAdd(&m_gui, m_pElemSDInfo, "Formatting\n");
|
|
gslc_Update(&m_gui);
|
|
initSizes();
|
|
if (card.type() != SD_CARD_TYPE_SDHC) {
|
|
gslc_ElemXTextboxAdd(&m_gui, m_pElemSDInfo, "FAT16\n");
|
|
gslc_Update(&m_gui);
|
|
makeFat16();
|
|
} else {
|
|
gslc_ElemXTextboxAdd(&m_gui, m_pElemSDInfo, "FAT32\n");
|
|
gslc_Update(&m_gui);
|
|
makeFat32();
|
|
}
|
|
#if DEBUG_PRINT
|
|
debugPrint();
|
|
#endif // DEBUG_PRINT
|
|
gslc_ElemXTextboxAdd(&m_gui, m_pElemSDInfo, "Format done\n");
|
|
gslc_Update(&m_gui);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void eraseAndFormatCard() {
|
|
#if USE_SDIO
|
|
if (!card.begin()) {
|
|
sdError("card.begin failed");
|
|
}
|
|
#else // USE_SDIO
|
|
if (!card.begin(chipSelect, SPI_SPEED)) {
|
|
sdError("card.begin failed");
|
|
}
|
|
#endif
|
|
cardSizeBlocks = card.cardSize();
|
|
if (cardSizeBlocks == 0) {
|
|
sdError("cardSize");
|
|
}
|
|
cardCapacityMB = (cardSizeBlocks + 2047)/2048;
|
|
|
|
char textForDisplay[128];
|
|
snprintf(textForDisplay, 128, "Card Size: : %.0fMB, (MB = 1,000,000 bytes)\n", 1.048576*cardCapacityMB);
|
|
gslc_ElemXTextboxAdd(&m_gui, m_pElemSDInfo, textForDisplay);
|
|
gslc_Update(&m_gui);
|
|
|
|
eraseCard();
|
|
formatCard();
|
|
}
|
|
//------------------------------------------------------------------------------
|