SDIO Power drain

Status
Not open for further replies.

mborgerson

Well-known member
I'm working on a Teensy version of a Logger File System (LFS). It is a sequential file system with a simple directory structure and minimal file system overhead. It was originally written for data loggers using MSP430 chips with limited RAM and Flash. I've got it working and it it uses about 4K less flash and 1K less RAM than SDFat (the 2.0 beta release). On the Teensy 3.6. I am using the SDCard functions of SDFat with an SdioCard object configured for FIFO_SDIO. As I had hoped, using SDFat.h, but instantiating only the SdioCard object means that the code doesn't include all the FATFS functions and data structures present in an instantiation of an SDFat object. Using the same shell program to write files, the LFS version is not only smaller, but also runs a bit faster. This is what you would expect for a system that doesn't have the overhead of maintaining multiple copies of the FAT and a more complex directory structure.

The one thing that surprised me was that, after the first file write, the SDFat version consumed significantly less quiescent power than the LFS version. (about 15mA vs 25mA on a teensy 3.6 at 24MHz with an asm("WFI\n") in the loop function. The power reduction seems to be tied to the transferStop() function in the SDIO library. Apparently, this function does something with the SDIO hardware registers that reduces quiescent power significantly. I've tried various combinations of readStart(), writeStart() and transferStop() without success. Some combinations will reduce the power, but cause problems with the next read or write to the SD. Does anyone have any insight into how these functions affect the hardware?

My test program writes 10MB files, with consecutive writes of 128Bytes---not an efficient way to write to SD, but it mimics the way a low-power logger will interact with the SD card. I would really like to figure out how to reduce power between file writes---particularly when the logger may write packets of less than 128 bytes at about 100 packets per second. My designed-for-Teensy loggers accumulate ~64KBytes in a pair of ping-pong buffers and write 64KB at a time. That's not an option on systems with only 8K RAM!
 
I confess I've never looked at the power consumption, but last year I was working on a project where getting a consistent speed of write was becoming important.
The SD card itself makes a number of its own decisions, it essentially manages it's own buffers, decides when to do writes, when to move things around and so on. I found I could get moderately consistent behaviour through trial and error, fiddling around with what functions I called. In the end, I found a combination that didn't have a fixed time, but was always fast enough.

Calling something like transferStop() may well signal to the card "I've done everything now, you can tidy yourself up and sleep", but I imagine if you don't appropriately call something like transferStart() you'll get problems. Equally, there may be a minimum time between starting and stopping that must be respected.
I would expect that you'd get best results by implementing the largest buffer available to you, and offloading in a big chunk to then completely stop the SD card afterwards.
 
readStart, writeStart are the beginning of a multisector read/write operation and are terminated with readStop,writeStop.
between Start and Stop the uSD card increments automatically the buffer index. Not sure that this influences the internal uSD activity and consequently reduces power consumption.
when you say 25 mA what Hardware and Software are you using?
I assume 15 mA is T3.6 at 24 MHz with SDFat-beta, right?
 
Apparently, this function does something with the SDIO hardware registers that reduces quiescent power significantly.

Looking at the code for transferStop() what I see is something used to terminate a data transfer that is in progress. I don't know for sure what the intent was because nobody comments their code anymore.

I can't tell what your code might be doing to increase power consumption since you haven't made it available. One thing you could do to reduce power is to disable the clock between transfers as suggested at 60.6.8 of the Reference Manual.
 
I've done some more tests with transferStop and I find that it does indeed reduce power drain significantly:
on a T3.6 at 24MHz using a 32GC SanDisk Ultra SD micro card.
After SDC.begin but before any calls to transferStop 32.5mA
After first call to transferStop 15.6mA
During large file writes 55.2mA (Average with multimeter--probably higher in peaks.

Write speed during 32-byte writes to an open file until 10MB written: about 4.2MB/second

The small file writes were chosen to simulate conditions on a simple data logger with limited RAM. I'm still working on optimizing the file system so the results will probably be different on systems with queues or ping -pong buffers and larger multi-sector writes.
Here's the source code for the test program:
Code:
/*****************************************************************
   This is a test sketch for the Logging File System (LFS)
   M. Borgerson   1/3/2020

   This file has the Arduino Setup and calls functions that test
   the Logging File System (LFS)
 *******************************************************************/
// Use the SDCard functions from SDFat  2.0 Beta
#include <SdFat.h>
#include <SdFatConfig.h>
#include <time.h>
#include <TimeLib.h>
// instantiate an SDCard object---NOT an SDFat object!
SdioCard sdc;
SdioConfig sdconfig;

#define  FILEOK  0
#define  FILEERROR  -1
#define  OPENREAD  1
#define  OPENWRITE 2

#define SD_CONFIG SdioConfig(FIFO_SDIO)
const char compileTime [] = "Compiled on " __DATE__ " " __TIME__;

int16_t InitLFS(void);

uint32_t readretries = 0;
uint32_t writeretries = 0;

void setup() {
  int16_t result;

  Serial.begin(9600);

  delay(200);
  Serial.print("\n\nLFS File Test ");
  Serial.println(compileTime);
  delay(40);
  SD_Init();
  delay(100);
  Serial.println("SDCard initialized.");
  result = InitLFS();
  Serial.print("Initializing File System.   Result = ");
  delay(100);
  Serial.println(result);
  setSyncProvider(getTeensy3Time);
  delay(100);
  if (timeStatus() != timeSet) {
    Serial.println("Unable to sync with the RTC");
  } else {
    Serial.println("RTC has set the system time");
  }
}

void loop() {
  // put your main code here, to run repeatedly:
  char ch;
  while (Serial.available() ) {
    ch = Serial.read();
    CheckCommand(ch);
  }
  // Put the processor to sleep between interrupts
  asm("WFI\n");
}

time_t getTeensy3Time() {
  return Teensy3Clock.get();
}

/*******************************************************
   Check for and execute valid commands from Serial input
 *******************************************************/
void CheckCommand(char ch) {
  switch (ch) {
    case 'e' : EraseSDC();
      break;
    case 'd' : Directory();
      break;
    case 'w' : WriteFiles();
      break;
    case 'p' : PowerTest();
      break;
    case 's' : ShowStats();
      break;
  }
}

/*****************************************************
  Test routines for Test_LFS
  These functions are called from
  CheckCommand()
****************************************/
// Erase the SDCard completely
void EraseSDC(void) {
  // add confirmation here is desired
  EraseCard();
  FormatCard();
}


void Directory(void) {
  ShowDirectory();
  SD_TransferStop();
}


/*******************************************
    Open and write to files
*/
void WriteFiles(void) {
  int16_t  result;
  uint16_t fdat[64];
  uint32_t i, mstart, minterval;
  uint32_t ustart, uinterval, usum, umax;
  float avgblock, maxblock;
  // initialize the fdat array
  for (i = 0; i < 64; i++) fdat[i] = i;

  Serial.print("10MB File: \t");
  mstart = millis();
  result = NewFile();
  if (result == FILEERROR) {
    Serial.println("\nCould not open new file.");
    return;
  }
  // File opened , now write 81920 records of 128 bytes,
  //  or  10MB of data, to file
  usum = 0; umax = 0;
#define UBLOCKS 81920
  for (i = 0; i < UBLOCKS; i++) {
    ustart = micros();
    WriteFile((unsigned char *)fdat, sizeof(fdat));
    uinterval = micros() - ustart;
    usum += uinterval;
    if (uinterval > umax) umax = uinterval;
  }
  CloseFile();
  minterval = millis() - mstart;
  avgblock = (usum / UBLOCKS) / 1000.0;
  maxblock = umax / 1000.0;
  //data displayed are:
  //  average time to write a 32-byte block in milliseconds
  //  longest time to write a 32-byte block--occurs when buffer is written to SDC
  //  total time to write the 10MB file. With 24MHz clock, write speed is about 4.6MB/second
  Serial.printf("% 8.3f\t % 8.3f\t % 6lu\t milliseconds\n", avgblock, maxblock, minterval);
  delay(10);
  // transferstop reduces power drain from about 31.5mA to 15.6mA
  // however it causes an error on the next operation--which is a read
  // of the pointer block.  The error disappears on a single retry of that read.
  SD_TransferStop();

}

void PowerTest(void) {
  int16_t i;
  Serial.println("Power Testing.");
  delay(2000);
  for (i = 0; i < 5; i++) {
    WriteFiles();
  }
  Serial.println("Test Complete.");
}

void ShowStats(void) {
  Serial.printf("Read retries:  % 8lu    Write retries:  % 8lu\n", readretries, writeretries);
}
/***************************************************
   These are the support functions for the LFS that
   interface with the system serial output and
   functions from SDCard.h
 **************************************************/
uint32_t UnixTime(void) {
  return now();
}

void Message(const char *msg) {
  Serial.print(msg);
}

void Message1(const char *msg, int32_t lnum) {
  Serial.print(msg);
  Serial.println(lnum);
}

void DirLineMessage(uint16_t fnum, uint32_t pctime, uint32_t flen) {
  //struct tm *tptr;
  time_t tm;
  tm = (time_t)pctime;
  //tptr = gmtime((time_t *)&lpctime);
  Serial.printf("%d:  %02d/%02d/%02d  %02d:%02d:%02d  %ld\n",
                fnum, month(tm), day(tm), year(tm) % 100,
                hour(tm), minute(tm), second(tm), flen);

}

// return the number of 512-byte blocks on the card
uint32_t CardCapacity(void) {
  uint32_t csize;
  csd_t csd;
  if (sdc.readCSD(&csd)) {
    csize = sdCardCapacity(&csd);
  } else {
    Serial.println("Error reading CSD");
    csize = 1000;
  }
  return csize;
}


// Initialize the SD card interface.
// in the Arduino system, we use the functions from SDcard library
int16_t SD_Init(void) {
  if (!sdc.begin(SdioConfig(FIFO_SDIO))) {
    Message("sdc.begin failed");
    // if (!sdc.begin(SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(50)))) {
    //     errorHalt("begin failed");
    return FILEERROR;
  } else {
    return FILEOK;
  }
}

inline void SendBytes(unsigned char *cp, uint16_t numbytes) {
  Serial.write(cp, numbytes);
}

//Read an SD sector and return an error code
int16_t SD_read( uint32_t secnum, uint8_t *buffptr) {
  bool rval;
  // sdc.readStart(secnum);
  //  rval = sdc.readData( buffptr);
  //  sdc.readStop();
  rval = sdc.readSector(secnum, buffptr);
  // do a single retry
  if (!rval) {
    rval = sdc.readSector(secnum, buffptr);
    readretries++;
  }
  if (rval == true) return FILEOK; else return FILEERROR;
}

// write an SD sector and return an error code
int16_t SD_write( uint32_t secnum, uint8_t *buffptr) {
  bool rval;
  // sdc.writeStart(secnum);
  //  rval = sdc.writeData(buffptr);
  // sdc.writeStop();
  rval = sdc.writeSector(secnum, buffptr);
  // do a single retry
  if (!rval) {
    rval = sdc.writeSector(secnum, buffptr);
    writeretries++;
  }
  if (rval == true) {
    return FILEOK;
  } else {
    return FILEERROR;
  }
}

void SD_TransferStop(void) {
  sdc.writeStop();
}

Here is the source for the LFS unit---which is written to be independent of the OS or SD Card system supporting it:
Code:
/*************************************************************
   Logging File System for Arduino
   MJB 12/31/19
   This version lacks the low-power elements of the MSP430 version
   And relies on the Arduino SDCard library from SDFat 2.0 Beta
 ***************************************************************
      Basic Principles:
              while a file is open the static direntry is valid
               and opendirblock is the SD block address of the directory entry
***********************************************************************/

#include    <stdarg.h>
#include    <stdint.h>
#include    <stdlib.h>
#include    <stdio.h>
#include    <string.h>
#include    <ctype.h>


#define  FILEOK  0
#define  FILEERROR  -1
#define  OPENREAD  1
#define  OPENWRITE 2

#define SECTORSIZE 512
/*
  extern uint32_t CardCapacity(void);
  extern void DirLineMessage(uint16_t fnum, uint32_t ctime, uint32_t flen);
  extern int16_t SD_Init(void);
  extern void SendBytes(unsigned char *cp, uint16_t numbytes);
  extern  int16_t SD_write( uint32_t secnum, uint8_t *buffptr);
  extern  int16_t SD_read( uint32_t secnum, uint8_t *buffptr);
  extern  uint32_t UnixTime(void);
  //  No header files  needed for Arduins

*/
unsigned int hardwrerror, softwrerror;

// the main program must provide the means to output
// error and status messages with or without 1 integer value
//extern void Message(const char *ch);
//extern void Message1(const char *ch, uint32_t num);

// this structure is contained in the first block of every
// file
struct direntry {
  uint32_t blocktype;
  uint32_t  firstblock;
  uint32_t  lastblock;
  uint32_t  createtime;
  uint32_t  closetime;  // 20 bytes
  uint32_t  length;
  uint32_t  reserved [10];   //64 bytes to here
  unsigned  char comment[448];  // We   use full block size
  // because we always read full sector into direntry
};

// Default to one directory sector for 128 files
#define  DIRSECTORS 1
#define  NUMDIRENTRIES 128 * DIRSECTORS
static  uint32_t  lastblock;
static  uint32_t  nextblock;
static  uint32_t  openblock;
static  uint16_t  writept;
static  uint16_t  readpt;
static  uint16_t  openfile;
static  uint32_t  opendirblock;
static  uint32_t  openlength;
static  uint32_t  openpos;
static  uint16_t fstatus;

// The directory entry requires 512 bytes RAM
static struct direntry de;
struct direntry *dirptr;


// use just one sector buffer of 512 bytes
uint8_t  filebuffer[512];


// read the directory entry into the static variable
int16_t GetDirBlock(uint32_t bnum) {
  int16_t result;
  result = SD_read(bnum, (unsigned char *)&de);
  return result;
}


// traverse the directory and set nextblock as one past
// the last used block in the last file
uint32_t FindNextBlock(uint32_t *p) {
  uint32_t  *lp, nb, hdrptr;
  int16_t  i, result;
  lp = p; hdrptr = 1;
  for (i = 0; i < 128; i++) {
    if (lp[i] != 0) {
      hdrptr = lp[i];
    } else break;
  }
  // read the directory block
  result = GetDirBlock(hdrptr);
  if (result != 0) return 1;
  nb = de.lastblock + 1;
  return nb;
}

//------------------------------------------------------------------------------
// flash erase all data on SD Card
uint32_t const ERASE_SIZE = 262144L;
void EraseCard() {
uint32_t cardSectorCount = 0;

uint32_t firstBlock = 0;
uint32_t lastBlock;

//------------------------------------------------------------------------------
// SdCardFactory constructs and initializes the appropriate card.
SdCardFactory cardFactory;
  Message("Block Erasing Card\n");
// Pointer to generic SD card.
SdCard* m_card = nullptr;
  m_card = cardFactory.newCard(SD_CONFIG);
  if (!m_card || m_card->errorCode()) {
    Message("card init failed.");
    return;
  }

  cardSectorCount = m_card->sectorCount();
  if (!cardSectorCount) {
    Message("Get sector count failed.");
    return;
  }

  do {
    lastBlock = firstBlock + ERASE_SIZE - 1;
    if (lastBlock >= cardSectorCount) {
      lastBlock = cardSectorCount - 1;
    }
    if (!m_card->erase(firstBlock, lastBlock)) {
      Message("erase failed");
    }
    firstBlock += ERASE_SIZE;
  } while (firstBlock < cardSectorCount);

  if (!m_card->readSector(0, filebuffer)) {
    Message("SD Card ReadBlock error");
  }
  Message("Block Erase Complete\n");
}
//------------------------------------------------------------------------------

/******************************************************
   This function zeros out the pointer block---effectively
   removing access to the files on the SD Card.
   Access to this function should be guarded by confirmation
   in the main program
 *******************************************************/

int16_t FormatCard(void) {

  int16_t i, *ip, result;
  result = FILEERROR;

  Message("\nInitializing pointer block.\n");
  // The directory pointers are at block zero.  Set them all to zero
  ip = (int16_t *)&filebuffer;
  for (i = 0; i < 256; i++) *ip++ = 0;

  result = SD_write(0, filebuffer);
  
  if (result != FILEOK) {
    Message("Could not initialize file system\n");
  }

  return result;
}


/**********************************************************
 * Initialize the LFS by checking pointer block and
 * determining SDC size.
 * If the pointer block hasn't been initialized,
 * do so at this time.
 ******************************************************/
int16_t InitLFS(void) {
  int16_t  result;
  uint32_t  *lp, faddr, cardcap;

  /*************************************************
    // SD Card initialized by sdc.begin()
  *****************************************************/
  cardcap = CardCapacity();
  Message1("Card Capacity in MB ", cardcap / 2048);
  Message(" MBytes\n");
  lastblock = cardcap - 10; // reserve some blocks at end of card

  // now read the directory pointer block

  result = SD_read(0, filebuffer);

  lp = (uint32_t *) filebuffer;
  faddr = *lp;
  // only two possibilities are valid for initialized file system:
  //  A.  faddr = 0;  no files have been created since format
  //  B.  faddr = DIRSECTORS;  the first file has been created

  if ((faddr != 0) & (faddr != DIRSECTORS)) {
    Message("Directory has not been initialized.\n");
    result = FormatCard();
    nextblock = 1;
    return result;
  }
  if (faddr == 0) nextblock = 1; else nextblock = FindNextBlock((uint32_t*)&filebuffer);
  Message1("Next file block will be ", nextblock);
  return FILEOK;

}

//  returns file number for new file
int16_t NewFile(void) {
  int16_t result, fnum;
  uint32_t *fp, bnum;
  // read the pointer block

  result = SD_read(0, filebuffer);

  if (result != FILEOK) {
    Message("Error reading pointer block in NewFile\n");
    return FILEERROR;
  }

  fnum = -1;
  fp = (uint32_t *)&filebuffer;
  for (fnum = 0; fnum < 128; fnum++) {
    if (*fp == 0) { // found an empty file pointer
      result = fnum;
      break;
    }
    fp++;
  } // end of for(fnum...
  if (fnum == NUMDIRENTRIES) { // directory is full
    Message("No more pointer entries!\n");
    return   FILEERROR;
  }
//  Message1("Next file number: ", (int32_t)fnum);
  // file number is valid, mark the ponter block with directory address
  bnum = nextblock;
  *fp = bnum;
  // now write back the modified pointer block

  result = SD_write(0, filebuffer);
  if (result != 0) { // if write fails do one retry
    result = SD_write(0, filebuffer);
    if (result != FILEOK){
      Message("Error writing File Pointer block 0\n");
      return result;
    }
  }
  openfile = fnum;
  nextblock++;
  de.blocktype = 0xFDFDFDFD;
  de.firstblock = bnum;
  de.lastblock =  bnum;
  de.createtime = UnixTime();
  de.closetime =  UnixTime();
  // now write the directory block at start of file
  result = SD_write(bnum, (unsigned char *)&de);

  if (result != FILEOK){
    Message1("Error writing Directory block \n", (int32_t)bnum);

    return FILEERROR;
  }

  opendirblock = bnum;

  fstatus = OPENWRITE;
  openblock = bnum + 1;
  openlength = 0;
  writept = 0;
//  Message1("Created file.  First block at \n",  (int32_t)opendirblock);
  return fnum;
}

int16_t FileStatus(void) {
  return fstatus;
}

// open a file for reading
int16_t OpenFile(uint16_t filenumber) {
  uint32_t  *lp,  hdrptr;
  int16_t result;
  // now read the directory pointer block

  result = SD_read(0, filebuffer);

  lp = (uint32_t *)filebuffer;

  if (lp[filenumber] != 0) {
    hdrptr = lp[filenumber];
    result = GetDirBlock(hdrptr);
    if (result != 0) return FILEERROR;
    openlength = de.length;
    openblock = de.firstblock + 1; // data starts after header block
 //   Message1("Opened File  ", (int32_t)filenumber);
    opendirblock = hdrptr;
  }

  fstatus = OPENREAD;
  openpos = 0;
  readpt = 0;
  openfile = filenumber;
  return result;
}

/**************************************************************/

int16_t CloseFile(void) {
  int16_t result, i;

  // struct direntry de;
  // write any buffered bytes
  if (writept != 0) {
    for (i = writept; i < SECTORSIZE; i++) filebuffer[i] = 0;
    SD_write(openblock, filebuffer);
    nextblock++;
  }

  de.lastblock =  openblock;
  de.closetime = UnixTime();
  de.length = openlength;
  result = SD_write(opendirblock, (unsigned char *)&de);
//  Message1("Updating file directory for file ", (int32_t)openfile);
  if (result != 0) {

    result = SD_write(opendirblock, (unsigned char *)&de);
    if (result != 0) Message1("Error writing file header block ", opendirblock);
  }
  fstatus = FILEOK;
  opendirblock = 0;
  return 0;
}


int16_t UpdateDirectory(void) {
  int16_t result;
 // SD_WriteStart();
  de.lastblock =  openblock;
  de.closetime = UnixTime();
  de.length = openlength;
  result = SD_write(opendirblock, (unsigned char *)&de);
  if (result != 0) {

    result = SD_write(opendirblock, (unsigned char *)&de);
    if (result != 0) Message1("Error writing directory block \n", (int32_t)opendirblock);
  }
  fstatus = FILEOK;

  return 0;

}

/******************************************************
 * Write3 bytes to the file.  Write the buffer to SDC
 * as required.
 */
int16_t WriteFile( unsigned char *datap, uint16_t Num2Write) {
  int16_t spaceleft;

  unsigned char *dptr, *wrptr;
  if (Num2Write > 512) return FILEERROR;
  dptr = datap;

  //  first, copy the data to proper position in filebuffer
  spaceleft = SECTORSIZE - writept;
  if (Num2Write >= spaceleft) { // fill rest of buffer and write sector
    wrptr = filebuffer + writept;
    memcpy(wrptr, dptr, spaceleft);
    writept = 0; Num2Write -= spaceleft;
    dptr += spaceleft;
    
    SD_write(openblock, filebuffer);
    nextblock++;
    openblock = nextblock;
    openlength += spaceleft;
  }
  wrptr = filebuffer + writept;
  memcpy(wrptr, dptr, Num2Write);
  writept += Num2Write;
  openlength += Num2Write;

  if (nextblock > lastblock) return FILEERROR;

  return FILEOK;
}

/****************************************************************
 * Read bytes from a file.  File is assumed to be open.
 * Read new sector as required
 */
int16_t ReadFile(unsigned char *datap, uint16_t Num2Read, uint16_t *NumRead) {
  int16_t spaceleft;

  unsigned char *dptr, *rdptr;
  if (Num2Read > 512) return FILEERROR;
  dptr = datap;
  *NumRead = 0;
  if (openpos + Num2Read > openlength) Num2Read = openlength - openpos;
  *NumRead = Num2Read;
  //  first, copy the data from proper position in filebuffer
  spaceleft = SECTORSIZE - readpt;
  if (Num2Read >= spaceleft) { // read what we have and get next sector
    rdptr = filebuffer + readpt;
    memcpy(dptr, rdptr, spaceleft);
    readpt = 0; Num2Read -= spaceleft;
    dptr += spaceleft;
    openblock++;
    SD_read(openblock, filebuffer);
    openpos += spaceleft;
  }
  rdptr = filebuffer + readpt;
  memcpy(dptr, rdptr, Num2Read);
  readpt += Num2Read;
  openpos += Num2Read;
//  SD_TransferStop();
  return FILEOK;
}



/************************************************************** 
 *  
 *  open a file for reading and send to host 
 *  This function not tested yet
  */
int16_t BinSendFile(uint16_t filenumber) {
  //struct direntry de;

  uint32_t  *lp, hdrptr;
  uint16_t bpos, bytesleft;
  uint32_t  flength, fpos;
  uint32_t fblock = 0;
  unsigned char  *cp;
  int16_t result;
  // now read the directory pointer block
  result = SD_read(0, filebuffer);
  lp = (uint32_t *)filebuffer;

  if (lp[filenumber] != 0) { // test for valid pointer
    hdrptr = lp[filenumber];
    result = GetDirBlock(hdrptr);
    fblock = hdrptr + 1;
    if (result != 0) return FILEERROR;
  }

  flength = de.length;
  fpos = 0;
  while ((flength - fpos) > 512) {
    result = SD_read(fblock, filebuffer);
    fblock++;
    bpos = 0;
    fstatus = FILEOK;
    if (result != 0) {
      Message1("Error reading file block ", (int32_t)fblock);
      fstatus = FILEERROR;
      return fstatus;
    }
    cp = &filebuffer[0];

    for (bpos = 0; bpos < 512; bpos++) {
      SendBytes(cp, 512);
    }

    fpos += 512;
  }
  // now read the last part
  bytesleft = flength - fpos;
  if (bytesleft > 0) {
    result = SD_read(fblock, filebuffer);
    cp = &filebuffer[0];
    bpos = 0;
    SendBytes(cp, bytesleft);
  }
  fpos = 0;
  readpt = 0;

  return fstatus;
}


/******************************************************
 * Read the pointer block, into the file buffer
 * Then step through the pointers and show directory
 * information for existing file. The directory blocks
 * are read into the static dirEntry block.
 */
void ShowDirectory(void) {
  uint32_t *lp, hdrptr;
  int16_t i, result;
  // now read the directory pointer block
  result = SD_read(0, filebuffer);
  lp = (uint32_t*)filebuffer;
  Message("\nFile     Last Written   Size\n");
  for (i = 0; i < NUMDIRENTRIES; i++) {
    if (lp[i] != 0) {
      hdrptr = lp[i];
      result = GetDirBlock(hdrptr);
      if (result != 0) return;
      DirLineMessage(i, de.closetime, de.length);
    } else break;
  }

}
 
I've done some more tests with transferStop and I find that it does indeed reduce power drain significantly:
on a T3.6 at 24MHz

Is that better or worse than shutting down the clock? (SIM_SCGC3 &= ~SIM_SCGC3_SDHC;)

I find your concern about using writes of less than 512 bytes of data very odd considering that I see at least 1024 bytes of memory allocated for buffers in your code. That and the routine you call has to have a 512 byte buffer to read the sector into before writing. When I wrote some logger code I arranged it so it needed no dedicated buffers. When a buffer was required, the calling function had to provide it.
 
I still do not know what the issue is.
transferStop is part of the transfer protocol of multi-block transfers : (transferStart --- write consecutive blocks --- transferStop)
You cannot use it to manipulate power consumption.
 
I still do not know what the issue is.
transferStop is part of the transfer protocol of multi-block transfers : (transferStart --- write consecutive blocks --- transferStop)
You cannot use it to manipulate power consumption.

transferStop resets the SDHC data path ( SDHC_SYSCTL |= SDHC_SYSCTL_RSTD;) which is not required for multi block transfers.
 
transferStop resets the SDHC data path ( SDHC_SYSCTL |= SDHC_SYSCTL_RSTD; ) which is not required for multi block transfers.

Resetting the Dat lines, may indeed be useful to reset the uSD, but not calling transferStop, as this first calls CMD12.
 
Resetting the Dat lines, may indeed be useful to reset the uSD, but not calling transferStop, as this first calls CMD12.

CMD12 isn't required to terminate multi-sector I/O. I have written a couple of bare metal implementations for the 3.6 (C and Forth). Both do multi-sector writes and neither uses CMD12.
 
The concern for writes of less than 512 bytes goes back to the original implementation of the LFS on an MSP430F148, which has only 2KB of RAM. There are only two allocated sector buffers in the LFS, one for the directory sector and one for the open data file. The earlier logger wrote small data packets at 100 samples per second and had to run for about 6 months on lithium D-Cell batteries.
The present tests are an effort to produce a similar long-term logger in a very power-constrained environment. The LFS allows you to call the file write routines with packets of arbitrary size (but less than 512 bytes). The data is accumulated in the file buffer and sectors are written as required. There are no other buffers in the LFS---although the TEENSY USB driver does have it's own buffers.

The call to transferStop does reduce the power consumed by the system between sector writes. In the original MSP430 code, I had a call to a function called SDIdle() that accomplished the same thing. I suspect that the power reduction produced by transferStop has to do with the following lines:

// Do reset to clear CDIHB. Should be a better way!
SDHC_SYSCTL |= SDHC_SYSCTL_RSTD;

Note that readStart and readStop are also called in the single-sector routines of SDIOCard. It seems that those functions do the same things as the multi-sector transfers--but just stop after the first sector.

I'm going to spend some time with the reference manual to see if I can gain some insight into the SDIOCard internals.
 
I do see that resetting the data path engine (RSTD) is recommended when recovering from an error. I have never had to do that but I use the DMA system so perhaps that is something peculiar to the FIFO.

The PRSSTAT register has several flags showing if internal clocks are disabled. You might want to look at those and compare them before and after a RSTD.
 
CMD12 isn't required to terminate multi-sector I/O. I have written a couple of bare metal implementations for the 3.6 (C and Forth). Both do multi-sector writes and neither uses CMD12.

I know, it is not _required_ if transfer is not aborted, but here in transfer stop, CMD12 is related to multblock transfers.
 
It has been a while since I wrote that code and I missed where I set A12EN which causes the hardware to do an auto CMD12 when the DMA finishes.

Something that I find peculiar about transferStop() is that after issuing a CMD12 and CMD13, it doesn't actually test CDIHB (which the comment says is the reason for the reset) before toggling RSTD. In any case, RSTD is for error recovery, not routine ending of a transfer.
 
Status
Not open for further replies.
Back
Top