Przeglądaj źródła

Improved Teensy SDIO, new features, bug fixes

Bill Greiman 4 lat temu
rodzic
commit
4582dd4d6c
45 zmienionych plików z 1512 dodań i 1395 usunięć
  1. 15 15
      doc/SdErrorCodes.txt
  2. 4 0
      doc/ZipMsg/index.html
  3. BIN
      doc/html.zip
  4. 4 835
      doc/html/index.html
  5. 0 2
      doc/html/index_files/colorschememapping.xml
  6. 0 6
      doc/html/index_files/filelist.xml
  7. BIN
      doc/html/index_files/themedata.thmx
  8. 0 85
      examples/ExFatFormatter/ExFatFormatter.ino
  9. 5 0
      examples/STM32Test/STM32Test.ino
  10. 4 0
      examples/SoftwareSpi/SoftwareSpi.ino
  11. 247 0
      examples/TeensyDmaAdcLogger/TeensyDmaAdcLogger.ino
  12. 5 0
      examples/TeensySdioDemo/TeensySdioDemo.ino
  13. 148 0
      examples/TeensySdioLogger/TeensySdioLogger.ino
  14. 4 1
      examples/UserSPIDriver/UserSPIDriver.ino
  15. 1 1
      library.properties
  16. 1 1
      src/ExFatLib/ExFatDbg.cpp
  17. 2 2
      src/ExFatLib/ExFatFile.cpp
  18. 62 53
      src/ExFatLib/ExFatFile.h
  19. 1 1
      src/ExFatLib/ExFatFilePrint.cpp
  20. 2 2
      src/ExFatLib/ExFatPartition.h
  21. 2 3
      src/ExFatLib/ExFatVolume.h
  22. 121 124
      src/FatLib/FatFile.h
  23. 15 14
      src/FatLib/FatFileLFN.cpp
  24. 7 6
      src/FatLib/FatFileSFN.cpp
  25. 8 8
      src/FatLib/FatPartition.h
  26. 1 1
      src/FatLib/FatVolume.h
  27. 21 1
      src/FsLib/FsFile.cpp
  28. 99 56
      src/FsLib/FsFile.h
  29. 43 34
      src/FsLib/FsVolume.h
  30. 358 0
      src/RingBuf.h
  31. 1 1
      src/SdCard/SdCardInfo.h
  32. 36 36
      src/SdCard/SdSpiCard.cpp
  33. 11 9
      src/SdCard/SdSpiCard.h
  34. 19 10
      src/SdCard/SdioCard.h
  35. 174 36
      src/SdCard/SdioTeensy.cpp
  36. 2 2
      src/SdFat.h
  37. 13 3
      src/SdFatConfig.h
  38. 24 12
      src/SpiDriver/SdSpiDriver.h
  39. 21 12
      src/SpiDriver/SdSpiESP.cpp
  40. 13 0
      src/SpiDriver/SdSpiLibDriver.h
  41. 1 1
      src/common/DebugMacros.h
  42. 2 2
      src/common/PrintTemplates.h
  43. 5 9
      src/iostream/StdioStream.h
  44. 8 9
      src/iostream/bufstream.h
  45. 2 2
      src/iostream/ios.h

+ 15 - 15
doc/SdErrorCodes.txt

@@ -1,8 +1,7 @@
-2019-12-10
+2021-01-06
 
 
 Run the SdErrorCode example to produce an updated list.
 Run the SdErrorCode example to produce an updated list.
 
 
-C
 Code,Symbol - failed operation
 Code,Symbol - failed operation
 0X00,SD_CARD_ERROR_NONE - No error
 0X00,SD_CARD_ERROR_NONE - No error
 0X01,SD_CARD_ERROR_CMD0 - Card reset failed
 0X01,SD_CARD_ERROR_CMD0 - Card reset failed
@@ -35,16 +34,17 @@ Code,Symbol - failed operation
 0X1C,SD_CARD_ERROR_READ_START - Bad readStart argument
 0X1C,SD_CARD_ERROR_READ_START - Bad readStart argument
 0X1D,SD_CARD_ERROR_READ_TIMEOUT - Read data timeout
 0X1D,SD_CARD_ERROR_READ_TIMEOUT - Read data timeout
 0X1E,SD_CARD_ERROR_STOP_TRAN - Multiple block stop failed
 0X1E,SD_CARD_ERROR_STOP_TRAN - Multiple block stop failed
-0X1F,SD_CARD_ERROR_WRITE_DATA - Write data not accepted
-0X20,SD_CARD_ERROR_WRITE_FIFO - SDIO fifo write timeout
-0X21,SD_CARD_ERROR_WRITE_START - Bad writeStart argument
-0X22,SD_CARD_ERROR_WRITE_PROGRAMMING - Flash programming
-0X23,SD_CARD_ERROR_WRITE_TIMEOUT - Write timeout
-0X24,SD_CARD_ERROR_DMA - DMA transfer failed
-0X25,SD_CARD_ERROR_ERASE - Card did not accept erase commands
-0X26,SD_CARD_ERROR_ERASE_SINGLE_SECTOR - Card does not support erase
-0X27,SD_CARD_ERROR_ERASE_TIMEOUT - Erase command timeout
-0X28,SD_CARD_ERROR_INIT_NOT_CALLED - Card has not been initialized
-0X29,SD_CARD_ERROR_INVALID_CARD_CONFIG - Invalid card config
-0X2A,SD_CARD_ERROR_FUNCTION_NOT_SUPPORTED - Unsupported SDIO command
-0X2B,SD_CARD_ERROR_UNKNOWN - Unknown error
+0X1F,SD_CARD_ERROR_TRANSFER_COMPLETE - SDIO transfer complete
+0X20,SD_CARD_ERROR_WRITE_DATA - Write data not accepted
+0X21,SD_CARD_ERROR_WRITE_FIFO - SDIO fifo write timeout
+0X22,SD_CARD_ERROR_WRITE_START - Bad writeStart argument
+0X23,SD_CARD_ERROR_WRITE_PROGRAMMING - Flash programming
+0X24,SD_CARD_ERROR_WRITE_TIMEOUT - Write timeout
+0X25,SD_CARD_ERROR_DMA - DMA transfer failed
+0X26,SD_CARD_ERROR_ERASE - Card did not accept erase commands
+0X27,SD_CARD_ERROR_ERASE_SINGLE_SECTOR - Card does not support erase
+0X28,SD_CARD_ERROR_ERASE_TIMEOUT - Erase command timeout
+0X29,SD_CARD_ERROR_INIT_NOT_CALLED - Card has not been initialized
+0X2A,SD_CARD_ERROR_INVALID_CARD_CONFIG - Invalid card config
+0X2B,SD_CARD_ERROR_FUNCTION_NOT_SUPPORTED - Unsupported SDIO command
+0X2C,SD_CARD_ERROR_UNKNOWN - Unknown error

+ 4 - 0
doc/ZipMsg/index.html

@@ -0,0 +1,4 @@
+<h3>Replace the content of the html folder by unzipping html.zip.</h3>
+<h3>I have zipped the documentation since Doxygen changes every file each time it runs.</h3>
+<h3>This makes viewing changes on GitHub difficult.</h3>
+<p>&nbsp;</p>

BIN
doc/html.zip


+ 4 - 835
doc/html/index.html

@@ -1,835 +1,4 @@
-<html xmlns:v="urn:schemas-microsoft-com:vml"
-xmlns:o="urn:schemas-microsoft-com:office:office"
-xmlns:w="urn:schemas-microsoft-com:office:word"
-xmlns:m="http://schemas.microsoft.com/office/2004/12/omml"
-xmlns="http://www.w3.org/TR/REC-html40">
-
-<head>
-<meta http-equiv=Content-Type content="text/html; charset=windows-1252">
-<meta name=ProgId content=Word.Document>
-<meta name=Generator content="Microsoft Word 15">
-<meta name=Originator content="Microsoft Word 15">
-<link rel=File-List href="index_files/filelist.xml">
-<!--[if gte mso 9]><xml>
- <o:DocumentProperties>
-  <o:Author>bill</o:Author>
-  <o:LastAuthor>bill</o:LastAuthor>
-  <o:Revision>6</o:Revision>
-  <o:TotalTime>9</o:TotalTime>
-  <o:Created>2020-12-26T14:24:00Z</o:Created>
-  <o:LastSaved>2020-12-30T14:38:00Z</o:LastSaved>
-  <o:Pages>1</o:Pages>
-  <o:Words>35</o:Words>
-  <o:Characters>201</o:Characters>
-  <o:Lines>1</o:Lines>
-  <o:Paragraphs>1</o:Paragraphs>
-  <o:CharactersWithSpaces>235</o:CharactersWithSpaces>
-  <o:Version>16.00</o:Version>
- </o:DocumentProperties>
- <o:OfficeDocumentSettings>
-  <o:AllowPNG/>
- </o:OfficeDocumentSettings>
-</xml><![endif]-->
-<link rel=themeData href="index_files/themedata.thmx">
-<link rel=colorSchemeMapping href="index_files/colorschememapping.xml">
-<!--[if gte mso 9]><xml>
- <w:WordDocument>
-  <w:SpellingState>Clean</w:SpellingState>
-  <w:GrammarState>Clean</w:GrammarState>
-  <w:TrackMoves>false</w:TrackMoves>
-  <w:TrackFormatting/>
-  <w:PunctuationKerning/>
-  <w:ValidateAgainstSchemas/>
-  <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid>
-  <w:IgnoreMixedContent>false</w:IgnoreMixedContent>
-  <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText>
-  <w:DoNotPromoteQF/>
-  <w:LidThemeOther>EN-US</w:LidThemeOther>
-  <w:LidThemeAsian>X-NONE</w:LidThemeAsian>
-  <w:LidThemeComplexScript>X-NONE</w:LidThemeComplexScript>
-  <w:Compatibility>
-   <w:BreakWrappedTables/>
-   <w:SnapToGridInCell/>
-   <w:WrapTextWithPunct/>
-   <w:UseAsianBreakRules/>
-   <w:UseWord2010TableStyleRules/>
-   <w:DontGrowAutofit/>
-   <w:SplitPgBreakAndParaMark/>
-   <w:EnableOpenTypeKerning/>
-   <w:DontFlipMirrorIndents/>
-   <w:OverrideTableStyleHps/>
-  </w:Compatibility>
-  <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel>
-  <m:mathPr>
-   <m:mathFont m:val="Cambria Math"/>
-   <m:brkBin m:val="before"/>
-   <m:brkBinSub m:val="&#45;-"/>
-   <m:smallFrac m:val="off"/>
-   <m:dispDef/>
-   <m:lMargin m:val="0"/>
-   <m:rMargin m:val="0"/>
-   <m:defJc m:val="centerGroup"/>
-   <m:wrapIndent m:val="1440"/>
-   <m:intLim m:val="subSup"/>
-   <m:naryLim m:val="undOvr"/>
-  </m:mathPr></w:WordDocument>
-</xml><![endif]--><!--[if gte mso 9]><xml>
- <w:LatentStyles DefLockedState="false" DefUnhideWhenUsed="false"
-  DefSemiHidden="false" DefQFormat="false" DefPriority="99"
-  LatentStyleCount="376">
-  <w:LsdException Locked="false" Priority="0" QFormat="true" Name="Normal"/>
-  <w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 1"/>
-  <w:LsdException Locked="false" Priority="9" SemiHidden="true"
-   UnhideWhenUsed="true" QFormat="true" Name="heading 2"/>
-  <w:LsdException Locked="false" Priority="9" SemiHidden="true"
-   UnhideWhenUsed="true" QFormat="true" Name="heading 3"/>
-  <w:LsdException Locked="false" Priority="9" SemiHidden="true"
-   UnhideWhenUsed="true" QFormat="true" Name="heading 4"/>
-  <w:LsdException Locked="false" Priority="9" SemiHidden="true"
-   UnhideWhenUsed="true" QFormat="true" Name="heading 5"/>
-  <w:LsdException Locked="false" Priority="9" SemiHidden="true"
-   UnhideWhenUsed="true" QFormat="true" Name="heading 6"/>
-  <w:LsdException Locked="false" Priority="9" SemiHidden="true"
-   UnhideWhenUsed="true" QFormat="true" Name="heading 7"/>
-  <w:LsdException Locked="false" Priority="9" SemiHidden="true"
-   UnhideWhenUsed="true" QFormat="true" Name="heading 8"/>
-  <w:LsdException Locked="false" Priority="9" SemiHidden="true"
-   UnhideWhenUsed="true" QFormat="true" Name="heading 9"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="index 1"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="index 2"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="index 3"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="index 4"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="index 5"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="index 6"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="index 7"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="index 8"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="index 9"/>
-  <w:LsdException Locked="false" Priority="39" SemiHidden="true"
-   UnhideWhenUsed="true" Name="toc 1"/>
-  <w:LsdException Locked="false" Priority="39" SemiHidden="true"
-   UnhideWhenUsed="true" Name="toc 2"/>
-  <w:LsdException Locked="false" Priority="39" SemiHidden="true"
-   UnhideWhenUsed="true" Name="toc 3"/>
-  <w:LsdException Locked="false" Priority="39" SemiHidden="true"
-   UnhideWhenUsed="true" Name="toc 4"/>
-  <w:LsdException Locked="false" Priority="39" SemiHidden="true"
-   UnhideWhenUsed="true" Name="toc 5"/>
-  <w:LsdException Locked="false" Priority="39" SemiHidden="true"
-   UnhideWhenUsed="true" Name="toc 6"/>
-  <w:LsdException Locked="false" Priority="39" SemiHidden="true"
-   UnhideWhenUsed="true" Name="toc 7"/>
-  <w:LsdException Locked="false" Priority="39" SemiHidden="true"
-   UnhideWhenUsed="true" Name="toc 8"/>
-  <w:LsdException Locked="false" Priority="39" SemiHidden="true"
-   UnhideWhenUsed="true" Name="toc 9"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Normal Indent"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="footnote text"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="annotation text"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="header"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="footer"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="index heading"/>
-  <w:LsdException Locked="false" Priority="35" SemiHidden="true"
-   UnhideWhenUsed="true" QFormat="true" Name="caption"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="table of figures"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="envelope address"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="envelope return"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="footnote reference"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="annotation reference"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="line number"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="page number"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="endnote reference"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="endnote text"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="table of authorities"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="macro"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="toa heading"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="List"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="List Bullet"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="List Number"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="List 2"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="List 3"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="List 4"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="List 5"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="List Bullet 2"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="List Bullet 3"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="List Bullet 4"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="List Bullet 5"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="List Number 2"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="List Number 3"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="List Number 4"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="List Number 5"/>
-  <w:LsdException Locked="false" Priority="10" QFormat="true" Name="Title"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Closing"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Signature"/>
-  <w:LsdException Locked="false" Priority="1" SemiHidden="true"
-   UnhideWhenUsed="true" Name="Default Paragraph Font"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Body Text"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Body Text Indent"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="List Continue"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="List Continue 2"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="List Continue 3"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="List Continue 4"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="List Continue 5"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Message Header"/>
-  <w:LsdException Locked="false" Priority="11" QFormat="true" Name="Subtitle"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Salutation"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Date"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Body Text First Indent"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Body Text First Indent 2"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Note Heading"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Body Text 2"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Body Text 3"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Body Text Indent 2"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Body Text Indent 3"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Block Text"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Hyperlink"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="FollowedHyperlink"/>
-  <w:LsdException Locked="false" Priority="22" QFormat="true" Name="Strong"/>
-  <w:LsdException Locked="false" Priority="20" QFormat="true" Name="Emphasis"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Document Map"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Plain Text"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="E-mail Signature"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="HTML Top of Form"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="HTML Bottom of Form"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Normal (Web)"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="HTML Acronym"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="HTML Address"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="HTML Cite"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="HTML Code"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="HTML Definition"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="HTML Keyboard"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="HTML Preformatted"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="HTML Sample"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="HTML Typewriter"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="HTML Variable"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Normal Table"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="annotation subject"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="No List"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Outline List 1"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Outline List 2"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Outline List 3"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Simple 1"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Simple 2"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Simple 3"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Classic 1"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Classic 2"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Classic 3"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Classic 4"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Colorful 1"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Colorful 2"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Colorful 3"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Columns 1"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Columns 2"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Columns 3"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Columns 4"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Columns 5"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Grid 1"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Grid 2"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Grid 3"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Grid 4"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Grid 5"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Grid 6"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Grid 7"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Grid 8"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table List 1"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table List 2"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table List 3"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table List 4"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table List 5"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table List 6"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table List 7"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table List 8"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table 3D effects 1"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table 3D effects 2"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table 3D effects 3"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Contemporary"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Elegant"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Professional"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Subtle 1"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Subtle 2"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Web 1"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Web 2"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Web 3"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Balloon Text"/>
-  <w:LsdException Locked="false" Priority="59" Name="Table Grid"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Table Theme"/>
-  <w:LsdException Locked="false" SemiHidden="true" Name="Placeholder Text"/>
-  <w:LsdException Locked="false" Priority="1" QFormat="true" Name="No Spacing"/>
-  <w:LsdException Locked="false" Priority="60" Name="Light Shading"/>
-  <w:LsdException Locked="false" Priority="61" Name="Light List"/>
-  <w:LsdException Locked="false" Priority="62" Name="Light Grid"/>
-  <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1"/>
-  <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2"/>
-  <w:LsdException Locked="false" Priority="65" Name="Medium List 1"/>
-  <w:LsdException Locked="false" Priority="66" Name="Medium List 2"/>
-  <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1"/>
-  <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2"/>
-  <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3"/>
-  <w:LsdException Locked="false" Priority="70" Name="Dark List"/>
-  <w:LsdException Locked="false" Priority="71" Name="Colorful Shading"/>
-  <w:LsdException Locked="false" Priority="72" Name="Colorful List"/>
-  <w:LsdException Locked="false" Priority="73" Name="Colorful Grid"/>
-  <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 1"/>
-  <w:LsdException Locked="false" Priority="61" Name="Light List Accent 1"/>
-  <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 1"/>
-  <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 1"/>
-  <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 1"/>
-  <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 1"/>
-  <w:LsdException Locked="false" SemiHidden="true" Name="Revision"/>
-  <w:LsdException Locked="false" Priority="34" QFormat="true"
-   Name="List Paragraph"/>
-  <w:LsdException Locked="false" Priority="29" QFormat="true" Name="Quote"/>
-  <w:LsdException Locked="false" Priority="30" QFormat="true"
-   Name="Intense Quote"/>
-  <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 1"/>
-  <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 1"/>
-  <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 1"/>
-  <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 1"/>
-  <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 1"/>
-  <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 1"/>
-  <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 1"/>
-  <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 1"/>
-  <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 2"/>
-  <w:LsdException Locked="false" Priority="61" Name="Light List Accent 2"/>
-  <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 2"/>
-  <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 2"/>
-  <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 2"/>
-  <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 2"/>
-  <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 2"/>
-  <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 2"/>
-  <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 2"/>
-  <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 2"/>
-  <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 2"/>
-  <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 2"/>
-  <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 2"/>
-  <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 2"/>
-  <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 3"/>
-  <w:LsdException Locked="false" Priority="61" Name="Light List Accent 3"/>
-  <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 3"/>
-  <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 3"/>
-  <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 3"/>
-  <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 3"/>
-  <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 3"/>
-  <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 3"/>
-  <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 3"/>
-  <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 3"/>
-  <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 3"/>
-  <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 3"/>
-  <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 3"/>
-  <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 3"/>
-  <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 4"/>
-  <w:LsdException Locked="false" Priority="61" Name="Light List Accent 4"/>
-  <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 4"/>
-  <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 4"/>
-  <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 4"/>
-  <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 4"/>
-  <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 4"/>
-  <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 4"/>
-  <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 4"/>
-  <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 4"/>
-  <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 4"/>
-  <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 4"/>
-  <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 4"/>
-  <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 4"/>
-  <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 5"/>
-  <w:LsdException Locked="false" Priority="61" Name="Light List Accent 5"/>
-  <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 5"/>
-  <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 5"/>
-  <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 5"/>
-  <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 5"/>
-  <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 5"/>
-  <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 5"/>
-  <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 5"/>
-  <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 5"/>
-  <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 5"/>
-  <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 5"/>
-  <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 5"/>
-  <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 5"/>
-  <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 6"/>
-  <w:LsdException Locked="false" Priority="61" Name="Light List Accent 6"/>
-  <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 6"/>
-  <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 6"/>
-  <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 6"/>
-  <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 6"/>
-  <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 6"/>
-  <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 6"/>
-  <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 6"/>
-  <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 6"/>
-  <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 6"/>
-  <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 6"/>
-  <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 6"/>
-  <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 6"/>
-  <w:LsdException Locked="false" Priority="19" QFormat="true"
-   Name="Subtle Emphasis"/>
-  <w:LsdException Locked="false" Priority="21" QFormat="true"
-   Name="Intense Emphasis"/>
-  <w:LsdException Locked="false" Priority="31" QFormat="true"
-   Name="Subtle Reference"/>
-  <w:LsdException Locked="false" Priority="32" QFormat="true"
-   Name="Intense Reference"/>
-  <w:LsdException Locked="false" Priority="33" QFormat="true" Name="Book Title"/>
-  <w:LsdException Locked="false" Priority="37" SemiHidden="true"
-   UnhideWhenUsed="true" Name="Bibliography"/>
-  <w:LsdException Locked="false" Priority="39" SemiHidden="true"
-   UnhideWhenUsed="true" QFormat="true" Name="TOC Heading"/>
-  <w:LsdException Locked="false" Priority="41" Name="Plain Table 1"/>
-  <w:LsdException Locked="false" Priority="42" Name="Plain Table 2"/>
-  <w:LsdException Locked="false" Priority="43" Name="Plain Table 3"/>
-  <w:LsdException Locked="false" Priority="44" Name="Plain Table 4"/>
-  <w:LsdException Locked="false" Priority="45" Name="Plain Table 5"/>
-  <w:LsdException Locked="false" Priority="40" Name="Grid Table Light"/>
-  <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light"/>
-  <w:LsdException Locked="false" Priority="47" Name="Grid Table 2"/>
-  <w:LsdException Locked="false" Priority="48" Name="Grid Table 3"/>
-  <w:LsdException Locked="false" Priority="49" Name="Grid Table 4"/>
-  <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark"/>
-  <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful"/>
-  <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful"/>
-  <w:LsdException Locked="false" Priority="46"
-   Name="Grid Table 1 Light Accent 1"/>
-  <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 1"/>
-  <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 1"/>
-  <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 1"/>
-  <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 1"/>
-  <w:LsdException Locked="false" Priority="51"
-   Name="Grid Table 6 Colorful Accent 1"/>
-  <w:LsdException Locked="false" Priority="52"
-   Name="Grid Table 7 Colorful Accent 1"/>
-  <w:LsdException Locked="false" Priority="46"
-   Name="Grid Table 1 Light Accent 2"/>
-  <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 2"/>
-  <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 2"/>
-  <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 2"/>
-  <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 2"/>
-  <w:LsdException Locked="false" Priority="51"
-   Name="Grid Table 6 Colorful Accent 2"/>
-  <w:LsdException Locked="false" Priority="52"
-   Name="Grid Table 7 Colorful Accent 2"/>
-  <w:LsdException Locked="false" Priority="46"
-   Name="Grid Table 1 Light Accent 3"/>
-  <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 3"/>
-  <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 3"/>
-  <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 3"/>
-  <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 3"/>
-  <w:LsdException Locked="false" Priority="51"
-   Name="Grid Table 6 Colorful Accent 3"/>
-  <w:LsdException Locked="false" Priority="52"
-   Name="Grid Table 7 Colorful Accent 3"/>
-  <w:LsdException Locked="false" Priority="46"
-   Name="Grid Table 1 Light Accent 4"/>
-  <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 4"/>
-  <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 4"/>
-  <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 4"/>
-  <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 4"/>
-  <w:LsdException Locked="false" Priority="51"
-   Name="Grid Table 6 Colorful Accent 4"/>
-  <w:LsdException Locked="false" Priority="52"
-   Name="Grid Table 7 Colorful Accent 4"/>
-  <w:LsdException Locked="false" Priority="46"
-   Name="Grid Table 1 Light Accent 5"/>
-  <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 5"/>
-  <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 5"/>
-  <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 5"/>
-  <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 5"/>
-  <w:LsdException Locked="false" Priority="51"
-   Name="Grid Table 6 Colorful Accent 5"/>
-  <w:LsdException Locked="false" Priority="52"
-   Name="Grid Table 7 Colorful Accent 5"/>
-  <w:LsdException Locked="false" Priority="46"
-   Name="Grid Table 1 Light Accent 6"/>
-  <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 6"/>
-  <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 6"/>
-  <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 6"/>
-  <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 6"/>
-  <w:LsdException Locked="false" Priority="51"
-   Name="Grid Table 6 Colorful Accent 6"/>
-  <w:LsdException Locked="false" Priority="52"
-   Name="Grid Table 7 Colorful Accent 6"/>
-  <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light"/>
-  <w:LsdException Locked="false" Priority="47" Name="List Table 2"/>
-  <w:LsdException Locked="false" Priority="48" Name="List Table 3"/>
-  <w:LsdException Locked="false" Priority="49" Name="List Table 4"/>
-  <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark"/>
-  <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful"/>
-  <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful"/>
-  <w:LsdException Locked="false" Priority="46"
-   Name="List Table 1 Light Accent 1"/>
-  <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 1"/>
-  <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 1"/>
-  <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 1"/>
-  <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 1"/>
-  <w:LsdException Locked="false" Priority="51"
-   Name="List Table 6 Colorful Accent 1"/>
-  <w:LsdException Locked="false" Priority="52"
-   Name="List Table 7 Colorful Accent 1"/>
-  <w:LsdException Locked="false" Priority="46"
-   Name="List Table 1 Light Accent 2"/>
-  <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 2"/>
-  <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 2"/>
-  <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 2"/>
-  <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 2"/>
-  <w:LsdException Locked="false" Priority="51"
-   Name="List Table 6 Colorful Accent 2"/>
-  <w:LsdException Locked="false" Priority="52"
-   Name="List Table 7 Colorful Accent 2"/>
-  <w:LsdException Locked="false" Priority="46"
-   Name="List Table 1 Light Accent 3"/>
-  <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 3"/>
-  <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 3"/>
-  <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 3"/>
-  <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 3"/>
-  <w:LsdException Locked="false" Priority="51"
-   Name="List Table 6 Colorful Accent 3"/>
-  <w:LsdException Locked="false" Priority="52"
-   Name="List Table 7 Colorful Accent 3"/>
-  <w:LsdException Locked="false" Priority="46"
-   Name="List Table 1 Light Accent 4"/>
-  <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 4"/>
-  <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 4"/>
-  <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 4"/>
-  <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 4"/>
-  <w:LsdException Locked="false" Priority="51"
-   Name="List Table 6 Colorful Accent 4"/>
-  <w:LsdException Locked="false" Priority="52"
-   Name="List Table 7 Colorful Accent 4"/>
-  <w:LsdException Locked="false" Priority="46"
-   Name="List Table 1 Light Accent 5"/>
-  <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 5"/>
-  <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 5"/>
-  <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 5"/>
-  <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 5"/>
-  <w:LsdException Locked="false" Priority="51"
-   Name="List Table 6 Colorful Accent 5"/>
-  <w:LsdException Locked="false" Priority="52"
-   Name="List Table 7 Colorful Accent 5"/>
-  <w:LsdException Locked="false" Priority="46"
-   Name="List Table 1 Light Accent 6"/>
-  <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 6"/>
-  <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 6"/>
-  <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 6"/>
-  <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 6"/>
-  <w:LsdException Locked="false" Priority="51"
-   Name="List Table 6 Colorful Accent 6"/>
-  <w:LsdException Locked="false" Priority="52"
-   Name="List Table 7 Colorful Accent 6"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Mention"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Smart Hyperlink"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Hashtag"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Unresolved Mention"/>
-  <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"
-   Name="Smart Link"/>
- </w:LatentStyles>
-</xml><![endif]-->
-<style>
-<!--
- /* Font Definitions */
- @font-face
-	{font-family:"Cambria Math";
-	panose-1:2 4 5 3 5 4 6 3 2 4;
-	mso-font-charset:0;
-	mso-generic-font-family:roman;
-	mso-font-pitch:variable;
-	mso-font-signature:3 0 0 0 1 0;}
-@font-face
-	{font-family:Calibri;
-	panose-1:2 15 5 2 2 2 4 3 2 4;
-	mso-font-charset:0;
-	mso-generic-font-family:swiss;
-	mso-font-pitch:variable;
-	mso-font-signature:-469750017 -1073732485 9 0 511 0;}
- /* Style Definitions */
- p.MsoNormal, li.MsoNormal, div.MsoNormal
-	{mso-style-unhide:no;
-	mso-style-qformat:yes;
-	mso-style-parent:"";
-	margin-top:0in;
-	margin-right:0in;
-	margin-bottom:10.0pt;
-	margin-left:0in;
-	line-height:115%;
-	mso-pagination:widow-orphan;
-	font-size:11.0pt;
-	font-family:"Calibri",sans-serif;
-	mso-ascii-font-family:Calibri;
-	mso-ascii-theme-font:minor-latin;
-	mso-fareast-font-family:Calibri;
-	mso-fareast-theme-font:minor-latin;
-	mso-hansi-font-family:Calibri;
-	mso-hansi-theme-font:minor-latin;
-	mso-bidi-font-family:"Times New Roman";
-	mso-bidi-theme-font:minor-bidi;}
-p.MsoEnvelopeAddress, li.MsoEnvelopeAddress, div.MsoEnvelopeAddress
-	{mso-style-noshow:yes;
-	mso-style-priority:99;
-	margin-top:0in;
-	margin-right:0in;
-	margin-bottom:0in;
-	margin-left:2.0in;
-	mso-pagination:widow-orphan;
-	mso-element:frame;
-	mso-element-frame-width:5.5in;
-	mso-element-frame-height:99.0pt;
-	mso-element-frame-hspace:9.0pt;
-	mso-element-wrap:auto;
-	mso-element-anchor-horizontal:page;
-	mso-element-left:center;
-	mso-element-top:bottom;
-	font-size:14.0pt;
-	mso-bidi-font-size:12.0pt;
-	font-family:"Arial",sans-serif;
-	mso-fareast-font-family:"Times New Roman";
-	mso-fareast-theme-font:major-fareast;
-	mso-bidi-font-family:"Times New Roman";
-	mso-bidi-theme-font:major-bidi;}
-p.MsoEnvelopeReturn, li.MsoEnvelopeReturn, div.MsoEnvelopeReturn
-	{mso-style-noshow:yes;
-	mso-style-priority:99;
-	margin:0in;
-	mso-pagination:widow-orphan;
-	font-size:11.0pt;
-	mso-bidi-font-size:10.0pt;
-	font-family:"Arial",sans-serif;
-	mso-fareast-font-family:"Times New Roman";
-	mso-fareast-theme-font:major-fareast;
-	mso-bidi-font-family:"Times New Roman";
-	mso-bidi-theme-font:major-bidi;}
-a:link, span.MsoHyperlink
-	{mso-style-noshow:yes;
-	mso-style-priority:99;
-	color:blue;
-	text-decoration:underline;
-	text-underline:single;}
-a:visited, span.MsoHyperlinkFollowed
-	{mso-style-noshow:yes;
-	mso-style-priority:99;
-	color:purple;
-	text-decoration:underline;
-	text-underline:single;}
-p.msonormal0, li.msonormal0, div.msonormal0
-	{mso-style-name:msonormal;
-	mso-style-unhide:no;
-	mso-margin-top-alt:auto;
-	margin-right:0in;
-	mso-margin-bottom-alt:auto;
-	margin-left:0in;
-	mso-pagination:widow-orphan;
-	font-size:12.0pt;
-	font-family:"Times New Roman",serif;
-	mso-fareast-font-family:"Times New Roman";
-	mso-fareast-theme-font:minor-fareast;}
-span.SpellE
-	{mso-style-name:"";
-	mso-spl-e:yes;}
-.MsoChpDefault
-	{mso-style-type:export-only;
-	mso-default-props:yes;
-	font-size:10.0pt;
-	mso-ansi-font-size:10.0pt;
-	mso-bidi-font-size:10.0pt;
-	font-family:"Calibri",sans-serif;
-	mso-ascii-font-family:Calibri;
-	mso-ascii-theme-font:minor-latin;
-	mso-fareast-font-family:Calibri;
-	mso-fareast-theme-font:minor-latin;
-	mso-hansi-font-family:Calibri;
-	mso-hansi-theme-font:minor-latin;
-	mso-bidi-font-family:"Times New Roman";
-	mso-bidi-theme-font:minor-bidi;}
-@page WordSection1
-	{size:8.5in 11.0in;
-	margin:1.0in 1.0in 1.0in 1.0in;
-	mso-header-margin:.5in;
-	mso-footer-margin:.5in;
-	mso-paper-source:0;}
-div.WordSection1
-	{page:WordSection1;}
--->
-</style>
-<!--[if gte mso 10]>
-<style>
- /* Style Definitions */
- table.MsoNormalTable
-	{mso-style-name:"Table Normal";
-	mso-tstyle-rowband-size:0;
-	mso-tstyle-colband-size:0;
-	mso-style-noshow:yes;
-	mso-style-priority:99;
-	mso-style-parent:"";
-	mso-padding-alt:0in 5.4pt 0in 5.4pt;
-	mso-para-margin:0in;
-	mso-pagination:widow-orphan;
-	font-size:10.0pt;
-	font-family:"Calibri",sans-serif;
-	mso-ascii-font-family:Calibri;
-	mso-ascii-theme-font:minor-latin;
-	mso-hansi-font-family:Calibri;
-	mso-hansi-theme-font:minor-latin;
-	mso-bidi-font-family:"Times New Roman";
-	mso-bidi-theme-font:minor-bidi;}
-</style>
-<![endif]--><!--[if gte mso 9]><xml>
- <o:shapedefaults v:ext="edit" spidmax="1026"/>
-</xml><![endif]--><!--[if gte mso 9]><xml>
- <o:shapelayout v:ext="edit">
-  <o:idmap v:ext="edit" data="1"/>
- </o:shapelayout></xml><![endif]-->
-</head>
-
-<body lang=EN-US link=blue vlink=purple style='tab-interval:.5in;word-wrap:
-break-word'>
-
-<div class=WordSection1>
-
-<p class=MsoNormal><span style='font-size:18.0pt;line-height:115%'>Replace the
-content of the html folder by unzipping html.zip.<o:p></o:p></span></p>
-
-<p class=MsoNormal><span style='font-size:18.0pt;line-height:115%'>I have
-zipped the documentation since <span class=SpellE>Doxygen</span> changes every
-file each time it runs.<o:p></o:p></span></p>
-
-<p class=MsoNormal style='margin-bottom:0in;line-height:normal'><span
-style='font-size:18.0pt'>This makes viewing changes on GitHub difficult.</span><span
-style='font-size:12.0pt;font-family:"Times New Roman",serif;mso-fareast-font-family:
-"Times New Roman"'><a href="https://www.doxygen.nl/"><br>
-</a></span><span style='font-size:18.0pt'><o:p></o:p></span></p>
-
-<p class=MsoNormal><span style='font-size:18.0pt;line-height:115%'><o:p>&nbsp;</o:p></span></p>
-
-</div>
-
-</body>
-
-</html>
+<h3>Replace the content of the html folder by unzipping html.zip.</h3>
+<h3>I have zipped the documentation since Doxygen changes every file each time it runs.</h3>
+<h3>This makes viewing changes on GitHub difficult.</h3>
+<p>&nbsp;</p>

+ 0 - 2
doc/html/index_files/colorschememapping.xml

@@ -1,2 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<a:clrMap xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" bg1="lt1" tx1="dk1" bg2="lt2" tx2="dk2" accent1="accent1" accent2="accent2" accent3="accent3" accent4="accent4" accent5="accent5" accent6="accent6" hlink="hlink" folHlink="folHlink"/>

+ 0 - 6
doc/html/index_files/filelist.xml

@@ -1,6 +0,0 @@
-<xml xmlns:o="urn:schemas-microsoft-com:office:office">
- <o:MainFile HRef="../index.html"/>
- <o:File HRef="themedata.thmx"/>
- <o:File HRef="colorschememapping.xml"/>
- <o:File HRef="filelist.xml"/>
-</xml>

BIN
doc/html/index_files/themedata.thmx


+ 0 - 85
examples/ExFatFormatter/ExFatFormatter.ino

@@ -1,85 +0,0 @@
-// Force exFAT formatting for all SD cards larger than 512MB.
-#include "SdFat.h"
-
-/*
-  Change the value of SD_CS_PIN if you are using SPI and
-  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
-*/
-
-// SDCARD_SS_PIN is defined for the built-in SD on some boards.
-#ifndef SDCARD_SS_PIN
-const uint8_t SD_CS_PIN = SS;
-#else  // SDCARD_SS_PIN
-// Assume built-in SD is used.
-const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
-#endif  // SDCARD_SS_PIN
-
-// Select fastest interface.
-#if HAS_SDIO_CLASS
-// SD config for Teensy 3.6 SDIO.
-#define SD_CONFIG SdioConfig(FIFO_SDIO)
-//#define SD_CONFIG SdioConfig(DMA_SDIO)
-#elif ENABLE_DEDICATED_SPI
-#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI)
-#else  // HAS_SDIO_CLASS
-#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI)
-#endif  // HAS_SDIO_CLASS
-
-SdExFat sd;
-//------------------------------------------------------------------------------
-void clearSerialInput() {
-  uint32_t m = micros();
-  do {
-    if (Serial.read() >= 0) {
-      m = micros();
-    }
-  } while (micros() - m < 10000);
-}
-//------------------------------------------------------------------------------
-void errorHalt() {
-  sd.printSdError(&Serial);
-  SysCall::halt();
-}
-#define error(s) (Serial.println(F(s)),errorHalt())
-//------------------------------------------------------------------------------
-void setup() {
-  Serial.begin(9600);
-  while (!Serial) {}
-  Serial.println(F("Type any character to begin"));
-
-  while (!Serial.available()) {
-    yield();
-  }
-  clearSerialInput();
-  Serial.println();
-  Serial.println(F(
-    "Your SD will be formated exFAT.\r\n"
-    "All data on the SD will be lost.\r\n"
-    "Type 'Y' to continue.\r\n"));
-
-  while (!Serial.available()) {
-    yield();
-  }
-  if (Serial.read() != 'Y') {
-    Serial.println(F("Exiting, 'Y' not typed."));
-    return;
-  }
-  if (!sd.cardBegin(SD_CONFIG)) {
-    error("cardBegin failed");
-  }
-  if(!sd.format(&Serial)) {
-    error("format failed");
-  }
-  if (!sd.volumeBegin()) {
-    error("volumeBegin failed");
-  }
-  Serial.print(F("Bytes per cluster: "));
-  Serial.println(sd.bytesPerCluster());
-  Serial.println(F("Done"));
-}
-void loop() {
-}

+ 5 - 0
examples/STM32Test/STM32Test.ino

@@ -14,7 +14,12 @@ FsFile file1;
 // Use mySPI2 since SPI2 is used in SPI.h as a different type.
 // Use mySPI2 since SPI2 is used in SPI.h as a different type.
 static SPIClass mySPI2(2);
 static SPIClass mySPI2(2);
 // Chip select PB21, dedicated SPI, 18 MHz, port 2.
 // Chip select PB21, dedicated SPI, 18 MHz, port 2.
+#if ENABLE_DEDICATED_SPI
 #define SD2_CONFIG SdSpiConfig(PB12, DEDICATED_SPI, SD_SCK_MHZ(18), &mySPI2)
 #define SD2_CONFIG SdSpiConfig(PB12, DEDICATED_SPI, SD_SCK_MHZ(18), &mySPI2)
+#else  // ENABLE_DEDICATED_SPI
+#define SD2_CONFIG SdSpiConfig(PB12, SHARED_SPI, SD_SCK_MHZ(18), &mySPI2)
+#endif  // ENABLE_DEDICATED_SPI
+
 SdFs sd2;
 SdFs sd2;
 FsFile file2;
 FsFile file2;
 
 

+ 4 - 0
examples/SoftwareSpi/SoftwareSpi.ino

@@ -21,7 +21,11 @@ const uint8_t SOFT_SCK_PIN  = 13;
 // SdFat software SPI template
 // SdFat software SPI template
 SoftSpiDriver<SOFT_MISO_PIN, SOFT_MOSI_PIN, SOFT_SCK_PIN> softSpi;
 SoftSpiDriver<SOFT_MISO_PIN, SOFT_MOSI_PIN, SOFT_SCK_PIN> softSpi;
 // Speed argument is ignored for software SPI.
 // Speed argument is ignored for software SPI.
+#if ENABLE_DEDICATED_SPI
 #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(0), &softSpi)
 #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(0), &softSpi)
+#else  // ENABLE_DEDICATED_SPI
+#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SD_SCK_MHZ(0), &softSpi)
+#endif  // ENABLE_DEDICATED_SPI
 
 
 #if SD_FAT_TYPE == 0
 #if SD_FAT_TYPE == 0
 SdFat sd;
 SdFat sd;

+ 247 - 0
examples/TeensyDmaAdcLogger/TeensyDmaAdcLogger.ino

@@ -0,0 +1,247 @@
+// Test of Teensy exFAT DMA ADC logger.
+// This is mainly to test use of RingBuf in an ISR.
+// You should modify it for serious use as a data logger.
+//
+#include "DMAChannel.h"
+#include "SdFat.h"
+#include "FreeStack.h"
+#include "RingBuf.h"
+
+// 400 sector RingBuf - could be larger on Teensy 4.1.
+const size_t RING_BUF_SIZE = 400*512;
+
+// Preallocate 8GiB file.
+const uint64_t PRE_ALLOCATE_SIZE = 8ULL << 30;
+
+// Use FIFO SDIO.
+#define SD_CONFIG SdioConfig(FIFO_SDIO)
+
+DMAChannel dma(true);
+
+SdFs sd;
+
+FsFile file;
+//------------------------------------------------------------------------------
+// Ping-pong DMA buffer.
+DMAMEM static uint16_t __attribute__((aligned(32))) dmaBuf[2][256];
+size_t dmaCount;
+
+// RingBuf for 512 byte sectors.
+RingBuf<FsFile, RING_BUF_SIZE> rb;
+
+// Shared between ISR and background.
+volatile size_t maxBytesUsed;
+
+volatile bool overrun;
+//------------------------------------------------------------------------------
+//ISR.
+static void isr() {
+  if (rb.bytesFreeIsr() >= 512 && !overrun) {
+    rb.memcpyIn(dmaBuf[dmaCount & 1], 512);
+    dmaCount++;
+    if (rb.bytesUsed() > maxBytesUsed) {
+      maxBytesUsed = rb.bytesUsed();
+    }
+  } else {
+    overrun = true;
+  }
+  dma.clearComplete();
+  dma.clearInterrupt();
+#if defined(__IMXRT1062__)
+  // Handle clear interrupt glitch in Teensy 4.x!
+  asm("DSB");
+#endif  // defined(__IMXRT1062__)
+}
+//------------------------------------------------------------------------------
+// Over-clocking will degrade quality - use only for stress testing.
+void overclock() {
+#if defined(__IMXRT1062__) // Teensy 4.0
+  ADC1_CFG  =
+    // High Speed Configuration
+    ADC_CFG_ADHSC |
+    // Sample period 3 clocks
+    ADC_CFG_ADSTS(0) |
+    // Input clock
+    ADC_CFG_ADIV(0) |
+    // Not selected - Long Sample Time Configuration
+    // ADC_CFG_ADLSMP |
+    // 12-bit
+    ADC_CFG_MODE(2) |
+    // Asynchronous clock
+    ADC_CFG_ADICLK(3);
+#else // defined(__IMXRT1062__)
+  // Set 12 bit mode and max over-clock
+  ADC0_CFG1 =
+    // Clock divide select, 0=direct, 1=div2, 2=div4, 3=div8
+    ADC_CFG1_ADIV(0) |
+    // Sample time configuration, 0=Short, 1=Long
+    // ADC_CFG1_ADLSMP |
+    // Conversion mode, 0=8 bit, 1=12 bit, 2=10 bit, 3=16 bit
+    ADC_CFG1_MODE(1) |
+    // Input clock, 0=bus, 1=bus/2, 2=OSCERCLK, 3=async
+    ADC_CFG1_ADICLK(0);
+
+  ADC0_CFG2 = ADC_CFG2_MUXSEL | ADC_CFG2_ADLSTS(3);
+#endif  // defined(__IMXRT1062__)
+}
+//------------------------------------------------------------------------------
+#if defined(__IMXRT1062__) // Teensy 4.0
+#define SOURCE_SADDR ADC1_R0
+#define SOURCE_EVENT DMAMUX_SOURCE_ADC1
+#else
+#define SOURCE_SADDR ADC0_RA
+#define SOURCE_EVENT DMAMUX_SOURCE_ADC0
+#endif
+//------------------------------------------------------------------------------
+// Should replace ADC stuff with calls to Teensy ADC library.
+// https://github.com/pedvide/ADC
+static void init(uint8_t pin) {
+  uint32_t adch;
+	uint32_t i, sum = 0;
+	// Actually, do many normal reads, to start with a nice DC level
+	for (i=0; i < 1024; i++) {
+		sum += analogRead(pin);
+	}
+#if defined(__IMXRT1062__) // Teensy 4.0
+  // save channel
+  adch = ADC1_HC0 & 0x1F;
+  // Continuous conversion , DMA enable
+  ADC1_GC = ADC_GC_ADCO | ADC_GC_DMAEN;
+  // start conversion
+  ADC1_HC0 = adch;
+#else  // defined(__IMXRT1062__) // Teensy 4.0
+  // save channel
+  adch = ADC0_SC1A & 0x1F;
+  // DMA enable
+  ADC0_SC2 |= ADC_SC2_DMAEN;
+  // Continuous conversion enable
+  ADC0_SC3 = ADC_SC3_ADCO;
+  // Start ADC
+  ADC0_SC1A = adch;
+ #endif  // defined(__IMXRT1062__) // Teensy 4.0
+	// set up a DMA channel to store the ADC data
+ 	dma.attachInterrupt(isr);
+	dma.begin();
+  dma.source((volatile const signed short &)SOURCE_SADDR);
+  dma.destinationBuffer((volatile uint16_t*)dmaBuf, sizeof(dmaBuf));
+  dma.interruptAtHalf();
+  dma.interruptAtCompletion();
+	dma.triggerAtHardwareEvent(SOURCE_EVENT);
+	dma.enable();
+}
+//------------------------------------------------------------------------------
+void stopDma() {
+#if defined(__IMXRT1062__) // Teensy 4.0
+  ADC1_GC = 0;
+#else  // defined(__IMXRT1062__)
+  ADC0_SC3 = 0;
+#endif  // defined(__IMXRT1062__)
+  dma.disable();
+}
+//------------------------------------------------------------------------------
+void printTest(Print* pr) {
+  if (file.fileSize() < 1024*2) {
+    return;
+  }
+  file.rewind();
+  rb.begin(&file);
+  // Could readIn RING_BUF_SIZE bytes and write to a csv file in a loop.
+  if (rb.readIn(2048) != 2048) {
+    sd.errorHalt("rb.readIn failed");
+  }
+  uint16_t data;
+  for (size_t i = 0; i < 1024; i++) {
+    pr->print(i);
+    pr->print(',');
+    rb.memcpyOut(&data, 2);
+    pr->println(data);
+  }
+}
+//------------------------------------------------------------------------------
+void runTest(uint8_t pin) {
+  dmaCount = 0;
+  maxBytesUsed = 0;
+  overrun = false;
+  do {
+    delay(10);
+  } while (Serial.read() >= 0);
+
+  if (!file.open("IsrLoggerTest.bin", O_CREAT | O_TRUNC | O_RDWR)) {
+    sd.errorHalt("file.open failed");
+  }
+  if (!file.preAllocate(PRE_ALLOCATE_SIZE)) {
+    sd.errorHalt("file.preAllocate failed");
+  }
+  rb.begin(&file);
+  Serial.println("Type any character to stop\n");
+
+  init(pin);
+  uint32_t samplingTime = micros();
+  while (!overrun && !Serial.available()) {
+    size_t n = rb.bytesUsed();
+    if ((n + file.curPosition()) >= (PRE_ALLOCATE_SIZE - 512)) {
+      Serial.println("File full - stopping");
+      break;
+    }
+    if (n >= 512) {
+      if (rb.writeOut(512) != 512) {
+        Serial.println("writeOut() failed");
+        file.close();
+        return;
+      }
+    }
+  }
+  stopDma();
+  samplingTime = (micros() - samplingTime);
+  if (!file.truncate()) {
+    sd.errorHalt("truncate failed");
+  }
+  if (overrun) {
+    Serial.println("Overrun ERROR!!");
+  }
+  Serial.print("RingBufSize ");
+  Serial.println(RING_BUF_SIZE);
+  Serial.print("maxBytesUsed ");
+  Serial.println(maxBytesUsed);
+  Serial.print("fileSize ");
+  Serial.println((uint32_t)file.fileSize());
+  Serial.print(0.000001*samplingTime);
+  Serial.println(" seconds");
+  Serial.print(1.0*file.fileSize()/samplingTime, 3);
+  Serial.println(" MB/sec\n");
+  printTest(&Serial);
+  file.close();
+}
+//------------------------------------------------------------------------------
+void waitSerial(const char* msg) {
+  uint32_t m = micros();
+  do {
+    if (Serial.read() >= 0) {
+      m = micros();
+    }
+  } while (micros() - m < 10000);
+  Serial.println(msg);
+  while (!Serial.available()) {}
+  Serial.println();
+}
+//------------------------------------------------------------------------------
+void setup() {
+  Serial.begin(9600);
+  while (!Serial) {
+    yield();
+  }
+  waitSerial("Type any character to begin");
+  Serial.print("FreeStack: ");
+  Serial.println(FreeStack());
+}
+//------------------------------------------------------------------------------
+void loop() {
+  if (!sd.begin(SD_CONFIG)) {
+    sd.initErrorHalt(&Serial);
+  }
+//analogReadAveraging(1);
+//analogReadResolution(12);
+//overclock(); // 3 Msps on Teensy 3.6 - requires high quality card.
+  runTest(A0);
+  waitSerial("Type any character to run test again");
+}

+ 5 - 0
examples/TeensySdioDemo/TeensySdioDemo.ino

@@ -197,10 +197,15 @@ void loop() {
     }
     }
     Serial.println("\nDMA SDIO mode - slow for small transfers.");
     Serial.println("\nDMA SDIO mode - slow for small transfers.");
   } else if (c == '3') {
   } else if (c == '3') {
+#if ENABLE_DEDICATED_SPI
     if (!sd.begin(SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(50)))) {
     if (!sd.begin(SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(50)))) {
       errorHalt("begin failed");
       errorHalt("begin failed");
     }
     }
     Serial.println("\nDedicated SPI mode.");
     Serial.println("\nDedicated SPI mode.");
+#else  // ENABLE_DEDICATED_SPI
+    Serial.println("ENABLE_DEDICATED_SPI must be non-zero.");
+    return;
+#endif  // ENABLE_DEDICATED_SPI
   } else if (c == '4') {
   } else if (c == '4') {
     if (!sd.begin(SdSpiConfig(SD_CS_PIN, SHARED_SPI, SD_SCK_MHZ(50)))) {
     if (!sd.begin(SdSpiConfig(SD_CS_PIN, SHARED_SPI, SD_SCK_MHZ(50)))) {
       errorHalt("begin failed");
       errorHalt("begin failed");

+ 148 - 0
examples/TeensySdioLogger/TeensySdioLogger.ino

@@ -0,0 +1,148 @@
+// Test Teensy SDIO with write busy in a data logger demo.
+//
+// The driver writes to the uSDHC controller's FIFO then returns
+// while the controller writes the data to the SD.  The first sector
+// puts the controller in write mode and takes about 11 usec on a
+// Teensy 4.1. About 5 usec is required to write a sector when the
+// controller is in write mode.
+
+#include "SdFat.h"
+#include "RingBuf.h"
+
+// Use Teensy SDIO
+#define SD_CONFIG  SdioConfig(FIFO_SDIO)
+
+// Interval between points for 25 ksps.
+#define LOG_INTERVAL_USEC 40
+
+// Size to log 10 byte lines at 25 kHz for more than ten minutes.
+#define LOG_FILE_SIZE 10*25000*600  // 150,000,000 bytes.
+
+// Space to hold more than 800 ms of data for 10 byte lines at 25 ksps.
+#define RING_BUF_CAPACITY 400*512
+#define LOG_FILENAME "SdioLogger.csv"
+
+SdFs sd;
+FsFile file;
+
+// RingBuf for File type FsFile.
+RingBuf<FsFile, RING_BUF_CAPACITY> rb;
+
+void logData() {
+  // Initialize the SD.
+  if (!sd.begin(SD_CONFIG)) {
+    sd.initErrorHalt(&Serial);
+  }
+  // Open or create file - truncate existing file.
+  if (!file.open(LOG_FILENAME, O_RDWR | O_CREAT | O_TRUNC)) {
+    Serial.println("open failed\n");
+    return;
+  }
+  // File must be pre-allocated to avoid huge
+  // delays searching for free clusters.
+  if (!file.preAllocate(LOG_FILE_SIZE)) {
+     Serial.println("preAllocate failed\n");
+     file.close();
+     return;
+  }
+  // initialize the RingBuf.
+  rb.begin(&file);
+  Serial.println("Type any character to stop");
+
+  // Max RingBuf used bytes. Useful to understand RingBuf overrun.
+  size_t maxUsed = 0;
+
+  // Min spare micros in loop.
+  int32_t minSpareMicros = INT32_MAX;
+
+  // Start time.
+  uint32_t logTime = micros();
+  // Log data until Serial input or file full.
+  while (!Serial.available()) {
+    // Amount of data in ringBuf.
+    size_t n = rb.bytesUsed();
+    if ((n + file.curPosition()) > (LOG_FILE_SIZE - 20)) {
+      Serial.println("File full - quiting.");
+      break;
+    }
+    if (n > maxUsed) {
+      maxUsed = n;
+    }
+    if (n >= 512 && !file.isBusy()) {
+      // Not busy only allows one sector before possible busy wait.
+      // Write one sector from RingBuf to file.
+      if (512 != rb.writeOut(512)) {
+        Serial.println("writeOut failed");
+        break;
+      }
+    }
+    // Time for next point.
+    logTime += LOG_INTERVAL_USEC;
+    int32_t spareMicros = logTime - micros();
+    if (spareMicros < minSpareMicros) {
+      minSpareMicros = spareMicros;
+    }
+    if (spareMicros <= 0) {
+      Serial.print("Rate too fast ");
+      Serial.println(spareMicros);
+      break;
+    }
+    // Wait until time to log data.
+    while (micros() < logTime) {}
+
+    // Read ADC0 - about 17 usec on Teensy 4, Teensy 3.6 is faster.
+    uint16_t adc = analogRead(0);
+    // Print spareMicros into the RingBuf as test data.
+    rb.print(spareMicros);
+    rb.write(',');
+    // Print adc into RingBuf.
+    rb.println(adc);
+    if (rb.getWriteError()) {
+      // Error caused by too few free bytes in RingBuf.
+      Serial.println("WriteError");
+      break;
+    }
+  }
+  // Write any RingBuf data to file.
+  rb.sync();
+  file.truncate();
+  file.rewind();
+  // Print first twenty lines of file.
+  Serial.println("spareMicros,ADC0");
+  for (uint8_t n = 0; n < 20 && file.available();) {
+    int c = file.read();
+    if (c < 0) {
+      break;
+    }
+    Serial.write(c);
+    if (c == '\n') n++;
+  }
+  Serial.print("fileSize: ");
+  Serial.println((uint32_t)file.fileSize());
+  Serial.print("maxBytesUsed: ");
+  Serial.println(maxUsed);
+  Serial.print("minSpareMicros: ");
+  Serial.println(minSpareMicros);
+  file.close();
+}
+void clearSerialInput() {
+  for (uint32_t m = micros(); micros() - m < 10000;) {
+    if (Serial.read() >= 0) {
+      m = micros();
+    }
+  }
+}
+void setup() {
+  Serial.begin(9600);
+  while (!Serial) {}
+  // Go faster or log more channels.  ADC quality will suffer.
+  // analogReadAveraging(1);
+}
+
+void loop() {
+  clearSerialInput();
+  Serial.println("Type any character to start");
+  while (!Serial.available()) {};
+  clearSerialInput();
+  logData();
+}

+ 4 - 1
examples/UserSPIDriver/UserSPIDriver.ino

@@ -59,8 +59,11 @@ class MySpiClass : public SdSpiBaseClass {
  private:
  private:
   SPISettings m_spiSettings;
   SPISettings m_spiSettings;
 } mySpi;
 } mySpi;
-
+#if ENABLE_DEDICATED_SPI
 #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(50), &mySpi)
 #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(50), &mySpi)
+#else  // ENABLE_DEDICATED_SPI
+#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SD_SCK_MHZ(50), &mySpi)
+#endif  // ENABLE_DEDICATED_SPI
 SdFat sd;
 SdFat sd;
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------

+ 1 - 1
library.properties

@@ -1,5 +1,5 @@
 name=SdFat
 name=SdFat
-version=2.0.4
+version=2.0.5
 license=MIT
 license=MIT
 author=Bill Greiman <fat16lib@sbcglobal.net>
 author=Bill Greiman <fat16lib@sbcglobal.net>
 maintainer=Bill Greiman <fat16lib@sbcglobal.net>
 maintainer=Bill Greiman <fat16lib@sbcglobal.net>

+ 1 - 1
src/ExFatLib/ExFatDbg.cpp

@@ -405,7 +405,7 @@ bool ExFatPartition::printDir(print_t* pr, ExFatFile* file) {
   uint16_t calcHash = 0;
   uint16_t calcHash = 0;
   uint16_t nameHash = 0;
   uint16_t nameHash = 0;
   uint16_t setChecksum = 0;
   uint16_t setChecksum = 0;
-  uint16_t calcChecksum = 0;;
+  uint16_t calcChecksum = 0;
   uint8_t  nameLength = 0;
   uint8_t  nameLength = 0;
   uint8_t  setCount = 0;
   uint8_t  setCount = 0;
   uint8_t  nUnicode;
   uint8_t  nUnicode;

+ 2 - 2
src/ExFatLib/ExFatFile.cpp

@@ -156,10 +156,10 @@ size_t ExFatFile::getName(ExChar_t* name, size_t length) {
       goto fail;
       goto fail;
     }
     }
     for (uint8_t in = 0; in < 15; in++) {
     for (uint8_t in = 0; in < 15; in++) {
-      if ((n + 1) >= length) {
+      uint16_t c = getLe16(dn->unicode + 2*in);
+      if (c == 0 || (n + 1) >= length) {
         goto done;
         goto done;
       }
       }
-      uint16_t c = getLe16(dn->unicode + 2*in);
       name[n++] = sizeof(ExChar_t) > 1 || c < 0X7F ? c : '?';
       name[n++] = sizeof(ExChar_t) > 1 || c < 0X7F ? c : '?';
     }
     }
   }
   }

+ 62 - 53
src/ExFatLib/ExFatFile.h

@@ -91,7 +91,17 @@ struct ExFatPos_t {
 class ExFatFile {
 class ExFatFile {
  public:
  public:
   /** Create an instance. */
   /** Create an instance. */
-  ExFatFile() : m_attributes(FILE_ATTR_CLOSED), m_error(0), m_flags(0) {}
+  ExFatFile() {}
+  /**  Create a file object and open it in the current working directory.
+   *
+   * \param[in] path A path for a file to be opened.
+   *
+   * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive
+   * OR of open flags. see FatFile::open(FatFile*, const char*, uint8_t).
+   */
+  ExFatFile(const char* path, oflag_t oflag) {
+    open(path, oflag);
+  }
 
 
 #if DESTRUCTOR_CLOSES_FILE
 #if DESTRUCTOR_CLOSES_FILE
   ~ExFatFile() {
   ~ExFatFile() {
@@ -108,12 +118,6 @@ class ExFatFile {
   operator bool() {
   operator bool() {
     return isOpen();
     return isOpen();
   }
   }
-  /** \return The number of bytes available from the current position
-   * to EOF for normal files.  Zero is returned for directory files.
-   */
-  uint64_t available64() {
-    return isFile() ? fileSize() - curPosition() : 0;
-  }
   /** \return The number of bytes available from the current position
   /** \return The number of bytes available from the current position
    * to EOF for normal files.  INT_MAX is returned for very large files.
    * to EOF for normal files.  INT_MAX is returned for very large files.
    *
    *
@@ -126,6 +130,20 @@ class ExFatFile {
     uint64_t n = available64();
     uint64_t n = available64();
     return n > INT_MAX ? INT_MAX : n;
     return n > INT_MAX ? INT_MAX : n;
   }
   }
+  /** \return The number of bytes available from the current position
+   * to EOF for normal files.  Zero is returned for directory files.
+   */
+  uint64_t available64() {
+    return isFile() ? fileSize() - curPosition() : 0;
+  }
+  /** Clear all error bits. */
+  void clearError() {
+    m_error = 0;
+  }
+  /** Clear writeError. */
+  void clearWriteError() {
+    m_error &= ~WRITE_ERROR;
+  }
   /** Close a file and force cached data and directory information
   /** Close a file and force cached data and directory information
    *  to be written to the storage device.
    *  to be written to the storage device.
    *
    *
@@ -200,22 +218,6 @@ class ExFatFile {
   void fsetpos(const fspos_t* pos);
   void fsetpos(const fspos_t* pos);
   /** Arduino name for sync() */
   /** Arduino name for sync() */
   void flush() {sync();}
   void flush() {sync();}
-  /**
-   * Get a file's name followed by a zero byte.
-   *
-   * \param[out] name An array of characters for the file's name.
-   * \param[in] size The size of the array in characters.
-   * \return the name length.
-   */
-  size_t getName(ExChar_t* name, size_t size);
-  /** Clear all error bits. */
-  void clearError() {
-    m_error = 0;
-  }
-  /** Clear writeError. */
-  void clearWriteError() {
-    m_error &= ~WRITE_ERROR;
-  }
   /** Get a file's access date and time.
   /** Get a file's access date and time.
    *
    *
    * \param[out] pdate Packed date for directory entry.
    * \param[out] pdate Packed date for directory entry.
@@ -244,6 +246,14 @@ class ExFatFile {
    * \return true for success or false for failure.
    * \return true for success or false for failure.
    */
    */
   bool getModifyDateTime(uint16_t* pdate, uint16_t* ptime);
   bool getModifyDateTime(uint16_t* pdate, uint16_t* ptime);
+  /**
+   * Get a file's name followed by a zero byte.
+   *
+   * \param[out] name An array of characters for the file's name.
+   * \param[in] size The size of the array in characters.
+   * \return the name length.
+   */
+  size_t getName(ExChar_t* name, size_t size);
   /** \return value of writeError */
   /** \return value of writeError */
   bool getWriteError() const {
   bool getWriteError() const {
     return isOpen() ? m_error & WRITE_ERROR : true;
     return isOpen() ? m_error & WRITE_ERROR : true;
@@ -266,12 +276,12 @@ class ExFatFile {
   bool isOpen() const {return m_attributes;}
   bool isOpen() const {return m_attributes;}
   /** \return True if file is read-only */
   /** \return True if file is read-only */
   bool isReadOnly() const {return m_attributes & FILE_ATTR_READ_ONLY;}
   bool isReadOnly() const {return m_attributes & FILE_ATTR_READ_ONLY;}
-  /** \return True if this is a subdirectory. */
-  bool isSubDir() const {return m_attributes & FILE_ATTR_SUBDIR;}
   /** \return True if this is the root directory. */
   /** \return True if this is the root directory. */
   bool isRoot() const {return m_attributes & FILE_ATTR_ROOT;}
   bool isRoot() const {return m_attributes & FILE_ATTR_ROOT;}
-  /** \return True file is writable. */
+  /** \return True file is readable. */
   bool isReadable() const {return m_flags & FILE_FLAG_READ;}
   bool isReadable() const {return m_flags & FILE_FLAG_READ;}
+  /** \return True if this is a subdirectory. */
+  bool isSubDir() const {return m_attributes & FILE_ATTR_SUBDIR;}
   /** \return True file is writable. */
   /** \return True file is writable. */
   bool isWritable() const {return m_flags & FILE_FLAG_WRITE;}
   bool isWritable() const {return m_flags & FILE_FLAG_WRITE;}
   /** List directory contents.
   /** List directory contents.
@@ -381,27 +391,27 @@ class ExFatFile {
    * \return true for success or false for failure.
    * \return true for success or false for failure.
    */
    */
   bool open(ExFatFile* dirFile, uint32_t index, oflag_t oflag);
   bool open(ExFatFile* dirFile, uint32_t index, oflag_t oflag);
-  /** Open the next file or subdirectory in a directory.
+  /** Open a file in the current working directory.
    *
    *
-   * \param[in] dirFile An open instance for the directory
-   *                    containing the file to be opened.
+   * \param[in] path A path with a valid name for a file to be opened.
    *
    *
    * \param[in] oflag bitwise-inclusive OR of open flags.
    * \param[in] oflag bitwise-inclusive OR of open flags.
-   *                  See see open(ExFatFile*, const char*, uint8_t).
+   *                  See see ExFatFile::open(ExFatFile*, const char*, uint8_t).
    *
    *
    * \return true for success or false for failure.
    * \return true for success or false for failure.
    */
    */
-  bool openNext(ExFatFile* dirFile, oflag_t oflag = O_RDONLY);
-  /** Open a file in the current working directory.
+  bool open(const ExChar_t* path, int oflag = O_RDONLY);
+  /** Open the next file or subdirectory in a directory.
    *
    *
-   * \param[in] path A path with a valid name for a file to be opened.
+   * \param[in] dirFile An open instance for the directory
+   *                    containing the file to be opened.
    *
    *
    * \param[in] oflag bitwise-inclusive OR of open flags.
    * \param[in] oflag bitwise-inclusive OR of open flags.
-   *                  See see ExFatFile::open(ExFatFile*, const char*, uint8_t).
+   *                  See see open(ExFatFile*, const char*, uint8_t).
    *
    *
    * \return true for success or false for failure.
    * \return true for success or false for failure.
    */
    */
-  bool open(const ExChar_t* path, int oflag = O_RDONLY);
+  bool openNext(ExFatFile* dirFile, oflag_t oflag = O_RDONLY);
   /** Open a volume's root directory.
   /** Open a volume's root directory.
    *
    *
    * \param[in] vol The FAT volume containing the root directory to be opened.
    * \param[in] vol The FAT volume containing the root directory to be opened.
@@ -425,6 +435,20 @@ class ExFatFile {
    * \return true for success or false for failure.
    * \return true for success or false for failure.
    */
    */
   bool preAllocate(uint64_t length);
   bool preAllocate(uint64_t length);
+     /** Print a file's access date and time
+   *
+   * \param[in] pr Print stream for output.
+   *
+   * \return true for success or false for failure.
+   */
+  size_t printAccessDateTime(print_t* pr);
+   /** Print a file's creation date and time
+   *
+   * \param[in] pr Print stream for output.
+   *
+   * \return true for success or false for failure.
+   */
+  size_t printCreateDateTime(print_t* pr);
   /** Print a number followed by a field terminator.
   /** Print a number followed by a field terminator.
    * \param[in] value The number to be printed.
    * \param[in] value The number to be printed.
    * \param[in] term The field terminator.  Use '\\n' for CR LF.
    * \param[in] term The field terminator.  Use '\\n' for CR LF.
@@ -488,21 +512,6 @@ class ExFatFile {
    * \return The number of bytes printed.
    * \return The number of bytes printed.
    */
    */
   size_t printFileSize(print_t* pr);
   size_t printFileSize(print_t* pr);
-   /** Print a file's access date and time
-   *
-   * \param[in] pr Print stream for output.
-   *
-   * \return true for success or false for failure.
-   */
-  size_t printAccessDateTime(print_t* pr);
-  /** Print a file's creation date and time
-   *
-   * \param[in] pr Print stream for output.
-   *
-   * \return true for success or false for failure.
-   */
-  size_t printCreateDateTime(print_t* pr);
-
   /** Print a file's modify date and time
   /** Print a file's modify date and time
    *
    *
    * \param[in] pr Print stream for output.
    * \param[in] pr Print stream for output.
@@ -780,9 +789,9 @@ class ExFatFile {
   ExFatVolume*  m_vol;
   ExFatVolume*  m_vol;
   DirPos_t      m_dirPos;
   DirPos_t      m_dirPos;
   uint8_t       m_setCount;
   uint8_t       m_setCount;
-  uint8_t       m_attributes;
-  uint8_t       m_error;
-  uint8_t       m_flags;
+  uint8_t       m_attributes = FILE_ATTR_CLOSED;
+  uint8_t       m_error = 0;
+  uint8_t       m_flags = 0;
 };
 };
 
 
 #include "../common/ArduinoFiles.h"
 #include "../common/ArduinoFiles.h"

+ 1 - 1
src/ExFatLib/ExFatFilePrint.cpp

@@ -167,7 +167,7 @@ size_t ExFatFile::printName(print_t* pr) {
     for (in = 0; in < 15; in++) {
     for (in = 0; in < 15; in++) {
       uint16_t c = getLe16(dn->unicode + 2*in);
       uint16_t c = getLe16(dn->unicode + 2*in);
       if (!c) {
       if (!c) {
-        break;;
+        break;
       }
       }
       buf[in] = c < 0X7f ? c : '?';
       buf[in] = c < 0X7f ? c : '?';
       n++;
       n++;

+ 2 - 2
src/ExFatLib/ExFatPartition.h

@@ -45,7 +45,7 @@ class ExFatFile;
  */
  */
 class ExFatPartition {
 class ExFatPartition {
  public:
  public:
-  ExFatPartition() : m_fatType(0) {}
+  ExFatPartition() {}
   /** \return the number of bytes in a cluster. */
   /** \return the number of bytes in a cluster. */
   uint32_t bytesPerCluster() const {return m_bytesPerCluster;}
   uint32_t bytesPerCluster() const {return m_bytesPerCluster;}
   /** \return the power of two for bytesPerCluster. */
   /** \return the power of two for bytesPerCluster. */
@@ -203,7 +203,7 @@ class ExFatPartition {
   uint32_t m_clusterMask;
   uint32_t m_clusterMask;
   uint32_t m_bytesPerCluster;
   uint32_t m_bytesPerCluster;
   BlockDevice* m_blockDev;
   BlockDevice* m_blockDev;
-  uint8_t  m_fatType;
+  uint8_t  m_fatType = 0;
   uint8_t  m_sectorsPerClusterShift;
   uint8_t  m_sectorsPerClusterShift;
 };
 };
 #endif  // ExFatPartition_h
 #endif  // ExFatPartition_h

+ 2 - 3
src/ExFatLib/ExFatVolume.h

@@ -33,8 +33,7 @@
  */
  */
 class ExFatVolume : public ExFatPartition {
 class ExFatVolume : public ExFatPartition {
  public:
  public:
-  ExFatVolume() {
-  }
+  ExFatVolume() {}
   /**
   /**
    * Initialize an FatVolume object.
    * Initialize an FatVolume object.
    * \param[in] dev Device block driver.
    * \param[in] dev Device block driver.
@@ -49,7 +48,7 @@ class ExFatVolume : public ExFatPartition {
     if (!chdir()) {
     if (!chdir()) {
       return false;
       return false;
     }
     }
-    if (setCwv) {
+    if (setCwv || !m_cwv) {
       m_cwv = this;
       m_cwv = this;
     }
     }
     return true;
     return true;

+ 121 - 124
src/FatLib/FatFile.h

@@ -110,8 +110,7 @@ const uint8_t FNAME_FLAG_LC_EXT = FAT_CASE_LC_EXT;
 class FatFile {
 class FatFile {
  public:
  public:
   /** Create an instance. */
   /** Create an instance. */
-  FatFile() : m_attributes(FILE_ATTR_CLOSED), m_error(0), m_flags(0) {}
-
+  FatFile() {}
   /**  Create a file object and open it in the current working directory.
   /**  Create a file object and open it in the current working directory.
    *
    *
    * \param[in] path A path for a file to be opened.
    * \param[in] path A path for a file to be opened.
@@ -120,11 +119,10 @@ class FatFile {
    * OR of open flags. see FatFile::open(FatFile*, const char*, uint8_t).
    * OR of open flags. see FatFile::open(FatFile*, const char*, uint8_t).
    */
    */
   FatFile(const char* path, oflag_t oflag) {
   FatFile(const char* path, oflag_t oflag) {
-    m_attributes = FILE_ATTR_CLOSED;
-    m_error = 0;
     open(path, oflag);
     open(path, oflag);
   }
   }
 #if DESTRUCTOR_CLOSES_FILE
 #if DESTRUCTOR_CLOSES_FILE
+  /** Destructor */
   ~FatFile() {
   ~FatFile() {
     if (isOpen()) {
     if (isOpen()) {
       close();
       close();
@@ -135,91 +133,32 @@ class FatFile {
    *
    *
    * \return true if a file is open.
    * \return true if a file is open.
    */
    */
-  operator bool() {
-    return isOpen();
-  }
-  /** Arduino name for sync() */
-  void flush() {sync();}
-  /** Clear all error bits. */
-  void clearError() {
-    m_error = 0;
-  }
-  /** Set writeError to zero */
-  void clearWriteError() {
-    m_error &= ~WRITE_ERROR;
-  }
-  /** \return Directory entry index. */
-  uint16_t dirIndex() const {return m_dirIndex;}
-  /** Get a file's access date.
-   *
-   * \param[out] pdate Packed date for directory entry.
-   *
-   * \return true for success or false for failure.
-   */
-  bool getAccessDate(uint16_t* pdate);
-  /** Get a file's access date and time.
-   *
-   * \param[out] pdate Packed date for directory entry.
-   * \param[out] ptime return zero since FAT has no time.
-   *
-   * This function is for comparability in FsFile.
+  operator bool() const {return isOpen();}
+  /** \return The number of bytes available from the current position
+   * to EOF for normal files.  INT_MAX is returned for very large files.
    *
    *
-   * \return true for success or false for failure.
-   */
-  bool getAccessDateTime(uint16_t* pdate, uint16_t* ptime) {
-    if (!getAccessDate(pdate)) {
-      return false;
-    }
-    *ptime = 0;
-    return true;
-  }
-  /** Get a file's create date and time.
+   * available32() is recomended for very large files.
    *
    *
-   * \param[out] pdate Packed date for directory entry.
-   * \param[out] ptime Packed time for directory entry.
+   * Zero is returned for directory files.
    *
    *
-   * \return true for success or false for failure.
    */
    */
-  bool getCreateDateTime(uint16_t* pdate, uint16_t* ptime);
-  /** \return All error bits. */
-  uint8_t getError() const {return m_error;}
-  /** Get a file's modify date and time.
-   *
-   * \param[out] pdate Packed date for directory entry.
-   * \param[out] ptime Packed time for directory entry.
-   *
-   * \return true for success or false for failure.
-   */
-  bool getModifyDateTime(uint16_t* pdate, uint16_t* ptime);
-  /** \return value of writeError */
-  bool getWriteError() const {
-    return isOpen() ? m_error & WRITE_ERROR : true;
+  int available() const {
+    uint32_t n = available32();
+    return n > INT_MAX ? INT_MAX : n;
   }
   }
-  /** get position for streams
-   * \param[out] pos struct to receive position
-   */
-  void fgetpos(fspos_t* pos) const;
-  /** set position for streams
-   * \param[in] pos struct with value for new position
-   */
-  void fsetpos(const fspos_t* pos);
   /** \return The number of bytes available from the current position
   /** \return The number of bytes available from the current position
    * to EOF for normal files.  Zero is returned for directory files.
    * to EOF for normal files.  Zero is returned for directory files.
    */
    */
-  uint32_t available32() {
+  uint32_t available32() const {
     return isFile() ? fileSize() - curPosition() : 0;
     return isFile() ? fileSize() - curPosition() : 0;
   }
   }
-  /** \return The number of bytes available from the current position
-   * to EOF for normal files.  INT_MAX is returned for very large files.
-   *
-   * available64() is recomended for very large files.
-   *
-   * Zero is returned for directory files.
-   *
-   */
-  int available() {
-    uint32_t n = available32();
-    return n > INT_MAX ? INT_MAX : n;
+  /** Clear all error bits. */
+  void clearError() {
+    m_error = 0;
+  }
+  /** Set writeError to zero */
+  void clearWriteError() {
+    m_error &= ~WRITE_ERROR;
   }
   }
   /** Close a file and force cached data and directory information
   /** Close a file and force cached data and directory information
    *  to be written to the storage device.
    *  to be written to the storage device.
@@ -237,7 +176,6 @@ class FatFile {
    * \return true for success or false for failure.
    * \return true for success or false for failure.
    */
    */
   bool contiguousRange(uint32_t* bgnSector, uint32_t* endSector);
   bool contiguousRange(uint32_t* bgnSector, uint32_t* endSector);
-
   /** Create and open a new contiguous file of a specified size.
   /** Create and open a new contiguous file of a specified size.
    *
    *
    * \param[in] dirFile The directory where the file will be created.
    * \param[in] dirFile The directory where the file will be created.
@@ -256,13 +194,11 @@ class FatFile {
    * \return true for success or false for failure.
    * \return true for success or false for failure.
    */
    */
   bool createContiguous(const char* path, uint32_t size);
   bool createContiguous(const char* path, uint32_t size);
-
   /** \return The current cluster number for a file or directory. */
   /** \return The current cluster number for a file or directory. */
   uint32_t curCluster() const {return m_curCluster;}
   uint32_t curCluster() const {return m_curCluster;}
 
 
   /** \return The current position for a file or directory. */
   /** \return The current position for a file or directory. */
   uint32_t curPosition() const {return m_curPosition;}
   uint32_t curPosition() const {return m_curPosition;}
-
   /** Return a file's directory entry.
   /** Return a file's directory entry.
    *
    *
    * \param[out] dir Location for return of the file's directory entry.
    * \param[out] dir Location for return of the file's directory entry.
@@ -270,6 +206,8 @@ class FatFile {
    * \return true for success or false for failure.
    * \return true for success or false for failure.
    */
    */
   bool dirEntry(DirFat_t* dir);
   bool dirEntry(DirFat_t* dir);
+  /** \return Directory entry index. */
+  uint16_t dirIndex() const {return m_dirIndex;}
   /** \return The number of bytes allocated to a directory or zero
   /** \return The number of bytes allocated to a directory or zero
    *         if an error occurs.
    *         if an error occurs.
    */
    */
@@ -295,6 +233,10 @@ class FatFile {
     FatFile file;
     FatFile file;
     return file.open(this, path, O_RDONLY);
     return file.open(this, path, O_RDONLY);
   }
   }
+  /** get position for streams
+   * \param[out] pos struct to receive position
+   */
+  void fgetpos(fspos_t* pos) const;
   /**
   /**
    * Get a string from a file.
    * Get a string from a file.
    *
    *
@@ -317,13 +259,59 @@ class FatFile {
    * occurred.
    * occurred.
    */
    */
   int fgets(char* str, int num, char* delim = nullptr);
   int fgets(char* str, int num, char* delim = nullptr);
-
   /** \return The total number of bytes in a file. */
   /** \return The total number of bytes in a file. */
   uint32_t fileSize() const {return m_fileSize;}
   uint32_t fileSize() const {return m_fileSize;}
   /** \return first sector of file or zero for empty file. */
   /** \return first sector of file or zero for empty file. */
   uint32_t firstBlock() const {return firstSector();}
   uint32_t firstBlock() const {return firstSector();}
   /** \return Address of first sector or zero for empty file. */
   /** \return Address of first sector or zero for empty file. */
   uint32_t firstSector() const;
   uint32_t firstSector() const;
+  /** Arduino name for sync() */
+  void flush() {sync();}
+  /** set position for streams
+   * \param[in] pos struct with value for new position
+   */
+  void fsetpos(const fspos_t* pos);
+  /** Get a file's access date.
+   *
+   * \param[out] pdate Packed date for directory entry.
+   *
+   * \return true for success or false for failure.
+   */
+  bool getAccessDate(uint16_t* pdate);
+  /** Get a file's access date and time.
+   *
+   * \param[out] pdate Packed date for directory entry.
+   * \param[out] ptime return zero since FAT has no time.
+   *
+   * This function is for comparability in FsFile.
+   *
+   * \return true for success or false for failure.
+   */
+  bool getAccessDateTime(uint16_t* pdate, uint16_t* ptime) {
+    if (!getAccessDate(pdate)) {
+      return false;
+    }
+    *ptime = 0;
+    return true;
+  }
+  /** Get a file's create date and time.
+   *
+   * \param[out] pdate Packed date for directory entry.
+   * \param[out] ptime Packed time for directory entry.
+   *
+   * \return true for success or false for failure.
+   */
+  bool getCreateDateTime(uint16_t* pdate, uint16_t* ptime);
+  /** \return All error bits. */
+  uint8_t getError() const {return m_error;}
+  /** Get a file's modify date and time.
+   *
+   * \param[out] pdate Packed date for directory entry.
+   * \param[out] ptime Packed time for directory entry.
+   *
+   * \return true for success or false for failure.
+   */
+  bool getModifyDateTime(uint16_t* pdate, uint16_t* ptime);
   /**
   /**
    * Get a file's name followed by a zero byte.
    * Get a file's name followed by a zero byte.
    *
    *
@@ -331,9 +319,9 @@ class FatFile {
    * \param[in] size The size of the array in bytes. The array
    * \param[in] size The size of the array in bytes. The array
    *             must be at least 13 bytes long.  The file's name will be
    *             must be at least 13 bytes long.  The file's name will be
    *             truncated if the file's name is too long.
    *             truncated if the file's name is too long.
-   * \return true for success or false for failure.
+   * \return length for success or zero for failure.
    */
    */
-  bool getName(char* name, size_t size);
+  size_t getName(char* name, size_t size);
   /**
   /**
    * Get a file's Short File Name followed by a zero byte.
    * Get a file's Short File Name followed by a zero byte.
    *
    *
@@ -341,7 +329,11 @@ class FatFile {
    *                  The array must be at least 13 bytes long.
    *                  The array must be at least 13 bytes long.
    * \return true for success or false for failure.
    * \return true for success or false for failure.
    */
    */
-  bool getSFN(char* name);
+  size_t getSFN(char* name);
+  /** \return value of writeError */
+  bool getWriteError() const {
+    return isOpen() ? m_error & WRITE_ERROR : true;
+  }
   /**
   /**
    * Check for BlockDevice busy.
    * Check for BlockDevice busy.
    *
    *
@@ -362,21 +354,21 @@ class FatFile {
   bool isLFN() const {return m_lfnOrd;}
   bool isLFN() const {return m_lfnOrd;}
   /** \return True if this is an open file/directory. */
   /** \return True if this is an open file/directory. */
   bool isOpen() const {return m_attributes;}
   bool isOpen() const {return m_attributes;}
+  /** \return True file is readable. */
+  bool isReadable() const {return m_flags & FILE_FLAG_READ;}
+  /** \return True if file is read-only */
+  bool isReadOnly() const {return m_attributes & FILE_ATTR_READ_ONLY;}
   /** \return True if this is the root directory. */
   /** \return True if this is the root directory. */
   bool isRoot() const {return m_attributes & FILE_ATTR_ROOT;}
   bool isRoot() const {return m_attributes & FILE_ATTR_ROOT;}
   /** \return True if this is the FAT32 root directory. */
   /** \return True if this is the FAT32 root directory. */
   bool isRoot32() const {return m_attributes & FILE_ATTR_ROOT32;}
   bool isRoot32() const {return m_attributes & FILE_ATTR_ROOT32;}
   /** \return True if this is the FAT12 of FAT16 root directory. */
   /** \return True if this is the FAT12 of FAT16 root directory. */
   bool isRootFixed() const {return m_attributes & FILE_ATTR_ROOT_FIXED;}
   bool isRootFixed() const {return m_attributes & FILE_ATTR_ROOT_FIXED;}
-  /** \return True if file is read-only */
-  bool isReadOnly() const {return m_attributes & FILE_ATTR_READ_ONLY;}
   /** \return True if this is a subdirectory. */
   /** \return True if this is a subdirectory. */
   bool isSubDir() const {return m_attributes & FILE_ATTR_SUBDIR;}
   bool isSubDir() const {return m_attributes & FILE_ATTR_SUBDIR;}
   /** \return True if this is a system file. */
   /** \return True if this is a system file. */
   bool isSystem() const {return m_attributes & FILE_ATTR_SYSTEM;}
   bool isSystem() const {return m_attributes & FILE_ATTR_SYSTEM;}
   /** \return True file is writable. */
   /** \return True file is writable. */
-  bool isReadable() const {return m_flags & FILE_FLAG_READ;}
-  /** \return True file is writable. */
   bool isWritable() const {return m_flags & FILE_FLAG_WRITE;}
   bool isWritable() const {return m_flags & FILE_FLAG_WRITE;}
   /** Check for a legal 8.3 character.
   /** Check for a legal 8.3 character.
    * \param[in] c Character to be checked.
    * \param[in] c Character to be checked.
@@ -430,7 +422,14 @@ class FatFile {
    * \return true for success or false for failure.
    * \return true for success or false for failure.
    */
    */
   bool mkdir(FatFile* dir, const char* path, bool pFlag = true);
   bool mkdir(FatFile* dir, const char* path, bool pFlag = true);
-
+  /** No longer implemented due to Long File Names.
+   *
+   * Use getName(char* name, size_t size).
+   * \return a pointer to replacement suggestion.
+   */
+  const char* name() const {
+    return "use getName()";
+  }
   /** Open a file in the volume root directory.
   /** Open a file in the volume root directory.
    *
    *
    * \param[in] vol Volume where the file is located.
    * \param[in] vol Volume where the file is located.
@@ -464,7 +463,7 @@ class FatFile {
    *
    *
    * \param[in] path A path with a valid name for a file to be opened.
    * \param[in] path A path with a valid name for a file to be opened.
    *
    *
-  * \param[in] oflag Values for \a oflag are constructed by a
+   * \param[in] oflag Values for \a oflag are constructed by a
    *                  bitwise-inclusive OR of flags from the following list.
    *                  bitwise-inclusive OR of flags from the following list.
    *                  Only one of O_RDONLY, O_READ, O_WRONLY, O_WRITE, or
    *                  Only one of O_RDONLY, O_READ, O_WRONLY, O_WRITE, or
    *                  O_RDWR is allowed.
    *                  O_RDWR is allowed.
@@ -536,7 +535,7 @@ class FatFile {
    * \return The byte if no error and not at eof else -1;
    * \return The byte if no error and not at eof else -1;
    */
    */
   int peek();
   int peek();
-   /** Allocate contiguous clusters to an empty file.
+  /** Allocate contiguous clusters to an empty file.
    *
    *
    * The file must be empty with no clusters allocated.
    * The file must be empty with no clusters allocated.
    *
    *
@@ -546,13 +545,29 @@ class FatFile {
    * \return true for success or false for failure.
    * \return true for success or false for failure.
    */
    */
   bool preAllocate(uint32_t length);
   bool preAllocate(uint32_t length);
+  /** Print a file's access date
+   *
+   * \param[in] pr Print stream for output.
+   *
+   * \return The number of characters printed.
+   */
+  size_t printAccessDate(print_t* pr);
+  /** Print a file's access date
+   *
+   * \param[in] pr Print stream for output.
+   *
+   * \return The number of characters printed.
+   */
+  size_t printAccessDateTime(print_t* pr) {
+    return printAccessDate(pr);
+  }
   /** Print a file's creation date and time
   /** Print a file's creation date and time
    *
    *
    * \param[in] pr Print stream for output.
    * \param[in] pr Print stream for output.
    *
    *
    * \return The number of bytes printed.
    * \return The number of bytes printed.
    */
    */
-  size_t  printCreateDateTime(print_t* pr);
+  size_t printCreateDateTime(print_t* pr);
   /** %Print a directory date field.
   /** %Print a directory date field.
    *
    *
    *  Format is yyyy-mm-dd.
    *  Format is yyyy-mm-dd.
@@ -627,22 +642,14 @@ class FatFile {
     }
     }
     return write(str, &buf[sizeof(buf)] - str);
     return write(str, &buf[sizeof(buf)] - str);
   }
   }
-  /** Print a file's access date
-   *
-   * \param[in] pr Print stream for output.
-   *
-   * \return The number of characters printed.
-   */
-  size_t printAccessDate(print_t* pr);
-  /** Print a file's access date
+  /** Print a file's size.
    *
    *
    * \param[in] pr Print stream for output.
    * \param[in] pr Print stream for output.
    *
    *
-   * \return The number of characters printed.
+   * \return The number of characters printed is returned
+   *         for success and zero is returned for failure.
    */
    */
-  size_t printAccessDateTime(print_t* pr) {
-    return printAccessDate(pr);
-  }
+  size_t printFileSize(print_t* pr);
   /** Print a file's modify date and time
   /** Print a file's modify date and time
    *
    *
    * \param[in] pr Print stream for output.
    * \param[in] pr Print stream for output.
@@ -657,14 +664,7 @@ class FatFile {
    * \return true for success or false for failure.
    * \return true for success or false for failure.
    */
    */
   size_t printName(print_t* pr);
   size_t printName(print_t* pr);
-  /** Print a file's size.
-   *
-   * \param[in] pr Print stream for output.
-   *
-   * \return The number of characters printed is returned
-   *         for success and zero is returned for failure.
-   */
-  size_t printFileSize(print_t* pr);
+
   /** Print a file's Short File Name.
   /** Print a file's Short File Name.
    *
    *
    * \param[in] pr Print stream for output.
    * \param[in] pr Print stream for output.
@@ -731,10 +731,6 @@ class FatFile {
    * \return true for success or false for failure.
    * \return true for success or false for failure.
    */
    */
   bool remove(const char* path);
   bool remove(const char* path);
-  /** Set the file's current position to zero. */
-  void rewind() {
-    seekSet(0);
-  }
   /** Rename a file or subdirectory.
   /** Rename a file or subdirectory.
    * \note the renamed file will be moved to the current volume working
    * \note the renamed file will be moved to the current volume working
    * directory.
    * directory.
@@ -752,6 +748,10 @@ class FatFile {
    * \return true for success or false for failure.
    * \return true for success or false for failure.
    */
    */
   bool rename(FatFile* dirFile, const char* newPath);
   bool rename(FatFile* dirFile, const char* newPath);
+  /** Set the file's current position to zero. */
+  void rewind() {
+    seekSet(0);
+  }
   /** Remove a directory file.
   /** Remove a directory file.
    *
    *
    * The directory file will be removed only if it is empty and is not the
    * The directory file will be removed only if it is empty and is not the
@@ -802,14 +802,12 @@ class FatFile {
    * \return true for success or false for failure.
    * \return true for success or false for failure.
    */
    */
   bool seekSet(uint32_t pos);
   bool seekSet(uint32_t pos);
-
   /** The sync() call causes all modified data and directory fields
   /** The sync() call causes all modified data and directory fields
    * to be written to the storage device.
    * to be written to the storage device.
    *
    *
    * \return true for success or false for failure.
    * \return true for success or false for failure.
    */
    */
   bool sync();
   bool sync();
-
   /** Set a file's timestamps in its directory entry.
   /** Set a file's timestamps in its directory entry.
    *
    *
    * \param[in] flags Values for \a flags are constructed by a bitwise-inclusive
    * \param[in] flags Values for \a flags are constructed by a bitwise-inclusive
@@ -862,7 +860,6 @@ class FatFile {
   bool truncate(uint32_t length) {
   bool truncate(uint32_t length) {
     return seekSet(length) && truncate();
     return seekSet(length) && truncate();
   }
   }
-
   /** Write a string to a file. Used by the Arduino Print class.
   /** Write a string to a file. Used by the Arduino Print class.
    * \param[in] str Pointer to the string.
    * \param[in] str Pointer to the string.
    * Use getWriteError to check for errors.
    * Use getWriteError to check for errors.
@@ -980,9 +977,9 @@ class FatFile {
   static const uint8_t WRITE_ERROR = 0X1;
   static const uint8_t WRITE_ERROR = 0X1;
   static const uint8_t READ_ERROR  = 0X2;
   static const uint8_t READ_ERROR  = 0X2;
 
 
-  uint8_t    m_attributes;       // File attributes
-  uint8_t    m_error;            // Error bits.
-  uint8_t    m_flags;            // See above for definition of m_flags bits
+  uint8_t    m_attributes = FILE_ATTR_CLOSED;
+  uint8_t    m_error = 0;        // Error bits.
+  uint8_t    m_flags = 0;        // See above for definition of m_flags bits
   uint8_t    m_lfnOrd;
   uint8_t    m_lfnOrd;
   uint16_t   m_dirIndex;         // index of directory entry in dir file
   uint16_t   m_dirIndex;         // index of directory entry in dir file
   FatVolume* m_vol;              // volume where file is located
   FatVolume* m_vol;              // volume where file is located

+ 15 - 14
src/FatLib/FatFileLFN.cpp

@@ -70,23 +70,22 @@ static uint16_t lfnGetChar(DirLfn_t* ldir, uint8_t i) {
   return 0;
   return 0;
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
-static bool lfnGetName(DirLfn_t* ldir, char* name, size_t n) {
+static size_t lfnGetName(DirLfn_t* ldir, char* name, size_t n) {
   uint8_t i;
   uint8_t i;
   size_t k = 13*((ldir->order & 0X1F) - 1);
   size_t k = 13*((ldir->order & 0X1F) - 1);
   for (i = 0; i < 13; i++) {
   for (i = 0; i < 13; i++) {
     uint16_t c = lfnGetChar(ldir, i);
     uint16_t c = lfnGetChar(ldir, i);
-    if (c == 0 || k >= n) {
+    if (c == 0 || k >= (n - 1)) {
       break;
       break;
     }
     }
     name[k++] = c >= 0X7F ? '?' : c;
     name[k++] = c >= 0X7F ? '?' : c;
   }
   }
-  // Terminate with zero byte if name fits.
-  if (k < n && (ldir->order & FAT_ORDER_LAST_LONG_ENTRY)) {
-    name[k] = 0;
+  // Terminate with zero byte.
+  if (k >= n) {
+    k = n - 1;
   }
   }
-  // Truncate if name is too long.
-  name[n - 1] = 0;
-  return true;
+  name[k] = '\0';
+  return k;
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 inline bool lfnLegalChar(uint8_t c) {
 inline bool lfnLegalChar(uint8_t c) {
@@ -122,7 +121,8 @@ static void lfnPutName(DirLfn_t* ldir, const char* name, size_t n) {
   }
   }
 }
 }
 //==============================================================================
 //==============================================================================
-bool FatFile::getName(char* name, size_t size) {
+size_t FatFile::getName(char* name, size_t size) {
+  size_t n = 0;
   FatFile dirFile;
   FatFile dirFile;
   DirLfn_t* ldir;
   DirLfn_t* ldir;
   if (!isOpen() || size < 13) {
   if (!isOpen() || size < 13) {
@@ -154,20 +154,21 @@ bool FatFile::getName(char* name, size_t size) {
       DBG_FAIL_MACRO;
       DBG_FAIL_MACRO;
       goto fail;
       goto fail;
     }
     }
-    if (!lfnGetName(ldir, name, size)) {
+    n = lfnGetName(ldir, name, size);
+    if (n == 0) {
       DBG_FAIL_MACRO;
       DBG_FAIL_MACRO;
       goto fail;
       goto fail;
     }
     }
     if (ldir->order & FAT_ORDER_LAST_LONG_ENTRY) {
     if (ldir->order & FAT_ORDER_LAST_LONG_ENTRY) {
-      return true;
+      return n;
     }
     }
   }
   }
   // Fall into fail.
   // Fall into fail.
   DBG_FAIL_MACRO;
   DBG_FAIL_MACRO;
 
 
  fail:
  fail:
-  name[0] = 0;
-  return false;
+  name[0] = '\0';
+  return 0;
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 bool FatFile::openCluster(FatFile* file) {
 bool FatFile::openCluster(FatFile* file) {
@@ -643,7 +644,7 @@ bool FatFile::remove() {
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 bool FatFile::lfnUniqueSfn(fname_t* fname) {
 bool FatFile::lfnUniqueSfn(fname_t* fname) {
   const uint8_t FIRST_HASH_SEQ = 2;  // min value is 2
   const uint8_t FIRST_HASH_SEQ = 2;  // min value is 2
-  uint8_t pos = fname->seqPos;;
+  uint8_t pos = fname->seqPos;
   DirFat_t* dir;
   DirFat_t* dir;
   uint16_t hex;
   uint16_t hex;
 
 

+ 7 - 6
src/FatLib/FatFileSFN.cpp

@@ -28,7 +28,7 @@
 #include "FatFile.h"
 #include "FatFile.h"
 #include "FatVolume.h"
 #include "FatVolume.h"
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
-bool FatFile::getSFN(char* name) {
+size_t FatFile::getSFN(char* name) {
   uint8_t j = 0;
   uint8_t j = 0;
   uint8_t lcBit = FAT_CASE_LC_BASE;
   uint8_t lcBit = FAT_CASE_LC_BASE;
   DirFat_t* dir;
   DirFat_t* dir;
@@ -40,7 +40,7 @@ bool FatFile::getSFN(char* name) {
   if (isRoot()) {
   if (isRoot()) {
     name[0] = '/';
     name[0] = '/';
     name[1] = '\0';
     name[1] = '\0';
-    return true;
+    return 1;
   }
   }
   // cache entry
   // cache entry
   dir = reinterpret_cast<DirFat_t*>(cacheDirEntry(FsCache::CACHE_FOR_READ));
   dir = reinterpret_cast<DirFat_t*>(cacheDirEntry(FsCache::CACHE_FOR_READ));
@@ -64,11 +64,12 @@ bool FatFile::getSFN(char* name) {
     }
     }
     name[j++] = c;
     name[j++] = c;
   }
   }
-  name[j] = 0;
-  return true;
+  name[j] = '\0';
+  return j;
 
 
  fail:
  fail:
-  return false;
+  name[0] = '\0';
+  return 0;
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 size_t FatFile::printSFN(print_t* pr) {
 size_t FatFile::printSFN(print_t* pr) {
@@ -84,7 +85,7 @@ size_t FatFile::printSFN(print_t* pr) {
 }
 }
 #if !USE_LONG_FILE_NAMES
 #if !USE_LONG_FILE_NAMES
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
-bool FatFile::getName(char* name, size_t size) {
+size_t FatFile::getName(char* name, size_t size) {
   return size < 13 ? 0 : getSFN(name);
   return size < 13 ? 0 : getSFN(name);
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------

+ 8 - 8
src/FatLib/FatPartition.h

@@ -67,26 +67,26 @@ class FatPartition {
  public:
  public:
   /** Create an instance of FatPartition
   /** Create an instance of FatPartition
    */
    */
-  FatPartition() : m_fatType(0) {}
+  FatPartition() {}
 
 
   /** \return The shift count required to multiply by bytesPerCluster. */
   /** \return The shift count required to multiply by bytesPerCluster. */
-  uint8_t bytesPerClusterShift() {
+  uint8_t bytesPerClusterShift() const {
     return m_sectorsPerClusterShift + m_bytesPerSectorShift;
     return m_sectorsPerClusterShift + m_bytesPerSectorShift;
   }
   }
   /** \return Number of bytes in a cluster. */
   /** \return Number of bytes in a cluster. */
-  uint16_t bytesPerCluster() {
+  uint16_t bytesPerCluster() const {
     return m_bytesPerSector << m_sectorsPerClusterShift;
     return m_bytesPerSector << m_sectorsPerClusterShift;
   }
   }
   /** \return Number of bytes per sector. */
   /** \return Number of bytes per sector. */
-  uint16_t bytesPerSector() {
+  uint16_t bytesPerSector() const {
     return m_bytesPerSector;
     return m_bytesPerSector;
   }
   }
   /** \return The shift count required to multiply by bytesPerCluster. */
   /** \return The shift count required to multiply by bytesPerCluster. */
-  uint8_t bytesPerSectorShift() {
+  uint8_t bytesPerSectorShift() const {
     return m_bytesPerSectorShift;
     return m_bytesPerSectorShift;
   }
   }
   /** \return Mask for sector offset. */
   /** \return Mask for sector offset. */
-  uint16_t sectorMask() {
+  uint16_t sectorMask() const {
     return m_sectorMask;
     return m_sectorMask;
   }
   }
   /** \return The volume's cluster size in sectors. */
   /** \return The volume's cluster size in sectors. */
@@ -120,7 +120,7 @@ class FatPartition {
     return m_dataStartSector;
     return m_dataStartSector;
   }
   }
   /** \return The number of File Allocation Tables. */
   /** \return The number of File Allocation Tables. */
-  uint8_t fatCount() {
+  uint8_t fatCount() const {
     return 2;
     return 2;
   }
   }
   /** \return The logical sector number for the start of the first FAT. */
   /** \return The logical sector number for the start of the first FAT. */
@@ -195,7 +195,7 @@ class FatPartition {
   uint8_t  m_sectorsPerCluster;       // Cluster size in sectors.
   uint8_t  m_sectorsPerCluster;       // Cluster size in sectors.
   uint8_t  m_clusterSectorMask;       // Mask to extract sector of cluster.
   uint8_t  m_clusterSectorMask;       // Mask to extract sector of cluster.
   uint8_t  m_sectorsPerClusterShift;  // Cluster count to sector count shift.
   uint8_t  m_sectorsPerClusterShift;  // Cluster count to sector count shift.
-  uint8_t  m_fatType;                 // Volume type (12, 16, OR 32).
+  uint8_t  m_fatType = 0;             // Volume type (12, 16, OR 32).
   uint16_t m_rootDirEntryCount;       // Number of entries in FAT16 root dir.
   uint16_t m_rootDirEntryCount;       // Number of entries in FAT16 root dir.
   uint32_t m_allocSearchStart;        // Start cluster for alloc search.
   uint32_t m_allocSearchStart;        // Start cluster for alloc search.
   uint32_t m_sectorsPerFat;           // FAT size in sectors
   uint32_t m_sectorsPerFat;           // FAT size in sectors

+ 1 - 1
src/FatLib/FatVolume.h

@@ -51,7 +51,7 @@ class FatVolume : public  FatPartition {
     if (!chdir()) {
     if (!chdir()) {
       return false;
       return false;
     }
     }
-    if (setCwv) {
+    if (setCwv || !m_cwv) {
       m_cwv = this;
       m_cwv = this;
     }
     }
     return true;
     return true;

+ 21 - 1
src/FsLib/FsFile.cpp

@@ -90,7 +90,6 @@ bool FsBaseFile::open(FsVolume* vol, const char* path, oflag_t oflag) {
       return true;
       return true;
     }
     }
     m_fFile = nullptr;
     m_fFile = nullptr;
-    return false;
   } else if (vol->m_xVol) {
   } else if (vol->m_xVol) {
     m_xFile = new (m_fileMem) ExFatFile;
     m_xFile = new (m_fileMem) ExFatFile;
     if (m_xFile && m_xFile->open(vol->m_xVol, path, oflag)) {
     if (m_xFile && m_xFile->open(vol->m_xVol, path, oflag)) {
@@ -155,6 +154,27 @@ bool FsBaseFile::openNext(FsBaseFile* dir, oflag_t oflag) {
   return false;
   return false;
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
+bool FsBaseFile::openRoot(FsVolume* vol) {
+  if (!vol) {
+    return false;
+  }
+  close();
+  if (vol->m_fVol) {
+    m_fFile = new (m_fileMem) FatFile;
+    if (m_fFile && m_fFile->openRoot(vol->m_fVol)) {
+      return true;
+    }
+    m_fFile = nullptr;
+  } else if (vol->m_xVol) {
+    m_xFile = new (m_fileMem) ExFatFile;
+    if (m_xFile && m_xFile->openRoot(vol->m_xVol)) {
+      return true;
+    }
+    m_xFile = nullptr;
+  }
+  return false;
+}
+//------------------------------------------------------------------------------
 bool FsBaseFile::remove() {
 bool FsBaseFile::remove() {
   if (m_fFile) {
   if (m_fFile) {
     if (m_fFile->remove()) {
     if (m_fFile->remove()) {

+ 99 - 56
src/FsLib/FsFile.h

@@ -37,7 +37,18 @@
  */
  */
 class FsBaseFile {
 class FsBaseFile {
  public:
  public:
-  FsBaseFile() : m_fFile(nullptr), m_xFile(nullptr) {}
+  /** Create an instance. */
+  FsBaseFile() {}
+  /**  Create a file object and open it in the current working directory.
+   *
+   * \param[in] path A path for a file to be opened.
+   *
+   * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive
+   * OR of open flags. see FatFile::open(FatFile*, const char*, uint8_t).
+   */
+  FsBaseFile(const char* path, oflag_t oflag) {
+    open(path, oflag);
+  }
 
 
   ~FsBaseFile() {close();}
   ~FsBaseFile() {close();}
   /** Copy constructor.
   /** Copy constructor.
@@ -58,11 +69,17 @@ class FsBaseFile {
   /** \return number of bytes available from the current position to EOF
   /** \return number of bytes available from the current position to EOF
    *   or INT_MAX if more than INT_MAX bytes are available.
    *   or INT_MAX if more than INT_MAX bytes are available.
    */
    */
-  int available() {
+  int available() const {
     return m_fFile ? m_fFile->available() :
     return m_fFile ? m_fFile->available() :
            m_xFile ? m_xFile->available() : 0;
            m_xFile ? m_xFile->available() : 0;
   }
   }
-
+  /** \return The number of bytes available from the current position
+   * to EOF for normal files.  Zero is returned for directory files.
+   */
+  uint64_t available64() const {
+    return m_fFile ? m_fFile->available32() :
+           m_xFile ? m_xFile->available64() : 0;
+  }
   /** Clear writeError. */
   /** Clear writeError. */
   void clearWriteError() {
   void clearWriteError() {
     if (m_fFile) m_fFile->clearWriteError();
     if (m_fFile) m_fFile->clearWriteError();
@@ -186,7 +203,7 @@ class FsBaseFile {
            m_xFile ? m_xFile->getCreateDateTime(pdate, ptime) : false;
            m_xFile ? m_xFile->getCreateDateTime(pdate, ptime) : false;
   }
   }
   /** \return All error bits. */
   /** \return All error bits. */
-  uint8_t getError() {
+  uint8_t getError() const {
     return m_fFile ? m_fFile->getError() :
     return m_fFile ? m_fFile->getError() :
            m_xFile ? m_xFile->getError() : 0XFF;
            m_xFile ? m_xFile->getError() : 0XFF;
   }
   }
@@ -248,6 +265,11 @@ class FsBaseFile {
    * \return true if the file is a directory.
    * \return true if the file is a directory.
    */
    */
   bool isDirectory() const {return isDir();}
   bool isDirectory() const {return isDir();}
+  /** \return True if this is a normal file. */
+  bool isFile() const {
+    return m_fFile ? m_fFile->isFile() :
+           m_xFile ? m_xFile->isFile() : false;
+  }
   /** \return True if this is a hidden file else false. */
   /** \return True if this is a hidden file else false. */
   bool isHidden() const {
   bool isHidden() const {
     return m_fFile ? m_fFile->isHidden() :
     return m_fFile ? m_fFile->isHidden() :
@@ -255,11 +277,26 @@ class FsBaseFile {
   }
   }
   /** \return True if this is an open file/directory else false. */
   /** \return True if this is an open file/directory else false. */
   bool isOpen() const {return m_fFile || m_xFile;}
   bool isOpen() const {return m_fFile || m_xFile;}
+  /** \return True file is readable. */
+  bool isReadable() const {
+    return m_fFile ? m_fFile->isReadable() :
+           m_xFile ? m_xFile->isReadable() : false;
+    }
+  /** \return True if file is read-only */
+  bool isReadOnly() const {
+    return m_fFile ? m_fFile->isReadOnly() :
+           m_xFile ? m_xFile->isReadOnly() : false;
+  }
   /** \return True if this is a subdirectory file else false. */
   /** \return True if this is a subdirectory file else false. */
   bool isSubDir() const {
   bool isSubDir() const {
     return m_fFile ? m_fFile->isSubDir() :
     return m_fFile ? m_fFile->isSubDir() :
            m_xFile ? m_xFile->isSubDir() : false;
            m_xFile ? m_xFile->isSubDir() : false;
   }
   }
+  /** \return True file is writable. */
+  bool isWritable() const {
+    return m_fFile ? m_fFile->isWritable() :
+           m_xFile ? m_xFile->isWritable() : false;
+  }
 #if ENABLE_ARDUINO_SERIAL
 #if ENABLE_ARDUINO_SERIAL
   /** List directory contents.
   /** List directory contents.
    *
    *
@@ -412,6 +449,15 @@ class FsBaseFile {
    * \return a file object.
    * \return a file object.
    */
    */
   bool openNext(FsBaseFile* dir, oflag_t oflag = O_RDONLY);
   bool openNext(FsBaseFile* dir, oflag_t oflag = O_RDONLY);
+  /** Open a volume's root directory.
+   *
+   * \param[in] vol The SdFs volume containing the root directory to be opened.
+   *
+   * \return true for success or false for failure.
+   */
+  bool openRoot(FsVolume* vol);
+  /** \return the current file position. */
+  uint64_t position() const {return curPosition();}
   /** Return the next available byte without consuming it.
   /** Return the next available byte without consuming it.
    *
    *
    * \return The byte if no error and not at eof else -1;
    * \return The byte if no error and not at eof else -1;
@@ -420,6 +466,21 @@ class FsBaseFile {
     return m_fFile ? m_fFile->peek() :
     return m_fFile ? m_fFile->peek() :
            m_xFile ? m_xFile->peek() : -1;
            m_xFile ? m_xFile->peek() : -1;
   }
   }
+  /** Allocate contiguous clusters to an empty file.
+   *
+   * The file must be empty with no clusters allocated.
+   *
+   * The file will contain uninitialized data for FAT16/FAT32 files.
+   * exFAT files will have zero validLength and dataLength will equal
+   * the requested length.
+   *
+   * \param[in] length size of the file in bytes.
+   * \return true for success or false for failure.
+   */
+  bool preAllocate(uint64_t length) {
+    return m_fFile ? length < (1ULL << 32) && m_fFile->preAllocate(length) :
+           m_xFile ? m_xFile->preAllocate(length) : false;
+  }
   /** Print a file's access date and time
   /** Print a file's access date and time
    *
    *
    * \param[in] pr Print stream for output.
    * \param[in] pr Print stream for output.
@@ -440,55 +501,7 @@ class FsBaseFile {
     return m_fFile ? m_fFile->printCreateDateTime(pr) :
     return m_fFile ? m_fFile->printCreateDateTime(pr) :
            m_xFile ? m_xFile->printCreateDateTime(pr) : 0;
            m_xFile ? m_xFile->printCreateDateTime(pr) : 0;
   }
   }
-  /** Print a file's modify date and time
-   *
-   * \param[in] pr Print stream for output.
-   *
-   * \return true for success or false for failure.
-   */
-  size_t printModifyDateTime(print_t* pr) {
-    return m_fFile ? m_fFile->printModifyDateTime(pr) :
-           m_xFile ? m_xFile->printModifyDateTime(pr) : 0;
-  }
-  /** Print a file's name
-   *
-   * \param[in] pr Print stream for output.
-   *
-   * \return true for success or false for failure.
-   */
-  size_t printName(print_t* pr) {
-    return m_fFile ? m_fFile->printName(pr) :
-           m_xFile ? m_xFile->printName(pr) : 0;
-  }
-  /** Print a file's size.
-   *
-   * \param[in] pr Print stream for output.
-   *
-   * \return The number of characters printed is returned
-   *         for success and zero is returned for failure.
-   */
-  size_t printFileSize(print_t* pr) {
-    return m_fFile ? m_fFile->printFileSize(pr) :
-           m_xFile ? m_xFile->printFileSize(pr) : 0;
-  }
-  /** Allocate contiguous clusters to an empty file.
-   *
-   * The file must be empty with no clusters allocated.
-   *
-   * The file will contain uninitialized data for FAT16/FAT32 files.
-   * exFAT files will have zero validLength and dataLength will equal
-   * the requested length.
-   *
-   * \param[in] length size of the file in bytes.
-   * \return true for success or false for failure.
-   */
-  bool preAllocate(uint64_t length) {
-    return m_fFile ? length < (1ULL << 32) && m_fFile->preAllocate(length) :
-           m_xFile ? m_xFile->preAllocate(length) : false;
-  }
-  /** \return the current file position. */
-  uint64_t position() const {return curPosition();}
-   /** Print a number followed by a field terminator.
+  /** Print a number followed by a field terminator.
    * \param[in] value The number to be printed.
    * \param[in] value The number to be printed.
    * \param[in] term The field terminator.  Use '\\n' for CR LF.
    * \param[in] term The field terminator.  Use '\\n' for CR LF.
    * \param[in] prec Number of digits after decimal point.
    * \param[in] prec Number of digits after decimal point.
@@ -517,6 +530,37 @@ class FsBaseFile {
     return m_fFile ? m_fFile->printField(value, term) :
     return m_fFile ? m_fFile->printField(value, term) :
            m_xFile ? m_xFile->printField(value, term) : 0;
            m_xFile ? m_xFile->printField(value, term) : 0;
   }
   }
+  /** Print a file's size.
+   *
+   * \param[in] pr Print stream for output.
+   *
+   * \return The number of characters printed is returned
+   *         for success and zero is returned for failure.
+   */
+  size_t printFileSize(print_t* pr) {
+    return m_fFile ? m_fFile->printFileSize(pr) :
+           m_xFile ? m_xFile->printFileSize(pr) : 0;
+  }
+  /** Print a file's modify date and time
+   *
+   * \param[in] pr Print stream for output.
+   *
+   * \return true for success or false for failure.
+   */
+  size_t printModifyDateTime(print_t* pr) {
+    return m_fFile ? m_fFile->printModifyDateTime(pr) :
+           m_xFile ? m_xFile->printModifyDateTime(pr) : 0;
+  }
+  /** Print a file's name
+   *
+   * \param[in] pr Print stream for output.
+   *
+   * \return true for success or false for failure.
+   */
+  size_t printName(print_t* pr) {
+    return m_fFile ? m_fFile->printName(pr) :
+           m_xFile ? m_xFile->printName(pr) : 0;
+  }
   /** Read the next byte from a file.
   /** Read the next byte from a file.
    *
    *
    * \return For success return the next byte in the file as an int.
    * \return For success return the next byte in the file as an int.
@@ -698,7 +742,6 @@ class FsBaseFile {
            m_xFile->timestamp(flags, year, month, day, hour, minute, second) :
            m_xFile->timestamp(flags, year, month, day, hour, minute, second) :
            false;
            false;
   }
   }
-
   /** Truncate a file to the current position.
   /** Truncate a file to the current position.
    *
    *
    * \return true for success or false for failure.
    * \return true for success or false for failure.
@@ -746,8 +789,8 @@ class FsBaseFile {
 
 
  private:
  private:
   newalign_t m_fileMem[FS_ALIGN_DIM(ExFatFile, FatFile)];
   newalign_t m_fileMem[FS_ALIGN_DIM(ExFatFile, FatFile)];
-  FatFile*   m_fFile;
-  ExFatFile* m_xFile;
+  FatFile*   m_fFile = nullptr;
+  ExFatFile* m_xFile = nullptr;
 };
 };
 /**
 /**
  * \class FsFile
  * \class FsFile

+ 43 - 34
src/FsLib/FsVolume.h

@@ -39,7 +39,7 @@ class FsFile;
  */
  */
 class FsVolume {
 class FsVolume {
  public:
  public:
-  FsVolume() : m_fVol(nullptr), m_xVol(nullptr) {}
+  FsVolume() {}
 
 
   ~FsVolume() {end();}
   ~FsVolume() {end();}
 
 
@@ -49,42 +49,15 @@ class FsVolume {
    * \return true for success or false for failure.
    * \return true for success or false for failure.
    */
    */
   bool begin(BlockDevice* blockDev);
   bool begin(BlockDevice* blockDev);
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+  // Use sectorsPerCluster(). blocksPerCluster() will be removed in the future.
+  uint32_t blocksPerCluster() __attribute__ ((deprecated)) {return sectorsPerCluster();} //NOLINT
+#endif  // DOXYGEN_SHOULD_SKIP_THIS
   /** \return the number of bytes in a cluster. */
   /** \return the number of bytes in a cluster. */
   uint32_t bytesPerCluster() const {
   uint32_t bytesPerCluster() const {
     return m_fVol ? m_fVol->bytesPerCluster() :
     return m_fVol ? m_fVol->bytesPerCluster() :
            m_xVol ? m_xVol->bytesPerCluster() : 0;
            m_xVol ? m_xVol->bytesPerCluster() : 0;
   }
   }
-  /** Change global working volume to this volume. */
-  void chvol() {m_cwv = this;}
-  /** \return The total number of clusters in the volume. */
-  uint32_t clusterCount() const {
-    return m_fVol ? m_fVol->clusterCount() :
-           m_xVol ? m_xVol->clusterCount() : 0;
-  }
-  /** \return The logical sector number for the start of file data. */
-  uint32_t dataStartSector() const {
-    return m_fVol ? m_fVol->dataStartSector() :
-           m_xVol ? m_xVol->clusterHeapStartSector() : 0;
-  }
-  /** \return The logical sector number for the start of the first FAT. */
-  uint32_t fatStartSector() const {
-    return m_fVol ? m_fVol->fatStartSector() :
-           m_xVol ? m_xVol->fatStartSector() : 0;
-  }
-  /** \return the free cluster count. */
-  uint32_t freeClusterCount() const {
-    return m_fVol ? m_fVol->freeClusterCount() :
-           m_xVol ? m_xVol->freeClusterCount() : 0;
-  }
-  /** \return The volume's cluster size in sectors. */
-  uint32_t sectorsPerCluster() const {
-    return m_fVol ? m_fVol->sectorsPerCluster() :
-           m_xVol ? m_xVol->sectorsPerCluster() : 0;
-  }
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
-  // Use sectorsPerCluster(). blocksPerCluster() will be removed in the future.
-  uint32_t blocksPerCluster() __attribute__ ((deprecated)) {return sectorsPerCluster();} //NOLINT
-#endif  // DOXYGEN_SHOULD_SKIP_THIS
   /**
   /**
    * Set volume working directory to root.
    * Set volume working directory to root.
    * \return true for success or false for failure.
    * \return true for success or false for failure.
@@ -102,6 +75,18 @@ class FsVolume {
     return m_fVol ? m_fVol->chdir(path) :
     return m_fVol ? m_fVol->chdir(path) :
            m_xVol ? m_xVol->chdir(path) : false;
            m_xVol ? m_xVol->chdir(path) : false;
   }
   }
+  /** Change global working volume to this volume. */
+  void chvol() {m_cwv = this;}
+  /** \return The total number of clusters in the volume. */
+  uint32_t clusterCount() const {
+    return m_fVol ? m_fVol->clusterCount() :
+           m_xVol ? m_xVol->clusterCount() : 0;
+  }
+  /** \return The logical sector number for the start of file data. */
+  uint32_t dataStartSector() const {
+    return m_fVol ? m_fVol->dataStartSector() :
+           m_xVol ? m_xVol->clusterHeapStartSector() : 0;
+  }
   /** free dynamic memory and end access to volume */
   /** free dynamic memory and end access to volume */
   void end() {
   void end() {
     m_fVol = nullptr;
     m_fVol = nullptr;
@@ -117,6 +102,11 @@ class FsVolume {
     return m_fVol ? m_fVol->exists(path) :
     return m_fVol ? m_fVol->exists(path) :
            m_xVol ? m_xVol->exists(path) : false;
            m_xVol ? m_xVol->exists(path) : false;
   }
   }
+  /** \return The logical sector number for the start of the first FAT. */
+  uint32_t fatStartSector() const {
+    return m_fVol ? m_fVol->fatStartSector() :
+           m_xVol ? m_xVol->fatStartSector() : 0;
+  }
   /** \return Partition type, FAT_TYPE_EXFAT, FAT_TYPE_FAT32,
   /** \return Partition type, FAT_TYPE_EXFAT, FAT_TYPE_FAT32,
    *          FAT_TYPE_FAT16, or zero for error.
    *          FAT_TYPE_FAT16, or zero for error.
    */
    */
@@ -124,6 +114,20 @@ class FsVolume {
     return m_fVol ? m_fVol->fatType() :
     return m_fVol ? m_fVol->fatType() :
            m_xVol ? m_xVol->fatType() : 0;
            m_xVol ? m_xVol->fatType() : 0;
   }
   }
+  /** \return the free cluster count. */
+  uint32_t freeClusterCount() const {
+    return m_fVol ? m_fVol->freeClusterCount() :
+           m_xVol ? m_xVol->freeClusterCount() : 0;
+  }
+  /**
+   * Check for BlockDevice busy.
+   *
+   * \return true if busy else false.
+   */
+  bool isBusy() {
+    return m_fVol ? m_fVol->isBusy() :
+           m_xVol ? m_xVol->isBusy() : false;
+  }
   /** List directory contents.
   /** List directory contents.
    *
    *
    * \param[in] pr Print object.
    * \param[in] pr Print object.
@@ -227,6 +231,11 @@ class FsVolume {
     return m_fVol ? m_fVol->rmdir(path) :
     return m_fVol ? m_fVol->rmdir(path) :
            m_xVol ? m_xVol->rmdir(path) : false;
            m_xVol ? m_xVol->rmdir(path) : false;
   }
   }
+  /** \return The volume's cluster size in sectors. */
+  uint32_t sectorsPerCluster() const {
+    return m_fVol ? m_fVol->sectorsPerCluster() :
+           m_xVol ? m_xVol->sectorsPerCluster() : 0;
+  }
 #if ENABLE_ARDUINO_SERIAL
 #if ENABLE_ARDUINO_SERIAL
   /** List directory contents.
   /** List directory contents.
    * \return true for success or false for failure.
    * \return true for success or false for failure.
@@ -369,8 +378,8 @@ class FsVolume {
   FsVolume& operator=(const FsVolume& from);
   FsVolume& operator=(const FsVolume& from);
 
 
   static FsVolume* m_cwv;
   static FsVolume* m_cwv;
-  FatVolume*   m_fVol;
-  ExFatVolume* m_xVol;
+  FatVolume*   m_fVol = nullptr;
+  ExFatVolume* m_xVol = nullptr;
   BlockDevice* m_blockDev;
   BlockDevice* m_blockDev;
 };
 };
 #endif  // FsVolume_h
 #endif  // FsVolume_h

+ 358 - 0
src/RingBuf.h

@@ -0,0 +1,358 @@
+/**
+ * Copyright (c) 2011-2020 Bill Greiman
+ * This file is part of the SdFat library for SD memory cards.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#ifndef RingBuf_h
+#define RingBuf_h
+/**
+ * \file
+ * \brief Ring buffer for data loggers.
+ */
+#include "Arduino.h"
+#include "common/FmtNumber.h"
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+//  Teensy 3.5/3.6 has hard fault at 0x20000000 for unaligned memcpy.
+#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
+inline bool is_aligned(const void* ptr, uintptr_t alignment) {
+    auto iptr = reinterpret_cast<uintptr_t>(ptr);
+    return !(iptr % alignment);
+}
+inline void memcpyBuf(void* dst, const void* src, size_t len) {
+  const uint8_t* b = reinterpret_cast<const uint8_t*>(0X20000000UL);
+  uint8_t* d = reinterpret_cast<uint8_t*>(dst);
+  const uint8_t *s = reinterpret_cast<const uint8_t*>(src);
+  if ((is_aligned(d, 4) && is_aligned(s, 4) && (len & 3) == 0) ||
+    !((d < b && b <= (d + len)) || (s < b && b <= (s + len)))) {
+    memcpy(dst, src, len);
+  } else {
+    while (len--) {
+      *d++ = *s++;
+    }
+  }
+}
+#else  // defined(__MK64FX512__) || defined(__MK66FX1M0__)
+inline void memcpyBuf(void* dst, const void* src, size_t len) {
+  memcpy(dst, src, len);
+}
+#endif  // defined(__MK64FX512__) || defined(__MK66FX1M0__)
+#endif  // DOXYGEN_SHOULD_SKIP_THIS
+/**
+ * \class RingBuf
+ * \brief Ring buffer for data loggers.
+ *
+ * This ring buffer may be used in ISRs.  bytesFreeIsr(), bytesUsedIsr(),
+ * memcopyIn(), and memcopyOut() are ISR callable.  For ISR use call
+ * memcopyIn() in the ISR and use writeOut() in non-interrupt code
+ * to write data to a file. readIn() and memcopyOut can be use in a
+ * similar way to provide file data to an ISR.
+ *
+ * Print into a RingBuf in an ISR should also work but has not been verified.
+ */
+template<class F, size_t Size>
+class RingBuf : public Print {
+ public:
+  /**
+   * RingBuf Constructor.
+   */
+  RingBuf() {}
+  /**
+   * Initialize RingBuf.
+   * \param[in] file Underlying file.
+   */
+  void begin(F* file) {
+    m_file = file;
+    m_count = 0;
+    m_head = 0;
+    m_tail = 0;
+    clearWriteError();
+  }
+  /**
+   *
+   * \return the RingBuf free space in bytes. Not ISR callable.
+   */
+  size_t bytesFree() const {
+    size_t count;
+    noInterrupts();
+    count = m_count;
+    interrupts();
+    return Size - count;
+  }
+  /**
+   * \return the RingBuf free space in bytes. ISR callable.
+   */
+  size_t bytesFreeIsr() const {
+    return Size - m_count;
+  }
+  /**
+   * \return the RingBuf used space in bytes. Not ISR callable.
+   */
+  size_t bytesUsed() const {
+    size_t count;
+    noInterrupts();
+    count = m_count;
+    interrupts();
+    return count;
+  }
+  /**
+   * \return the RingBuf used space in bytes.  ISR callable.
+   */
+  size_t bytesUsedIsr() const {
+    return m_count;
+  }
+  /**
+   * Copy data to the RingBuf from buf.
+   * The number of bytes copied may be less than count if
+   * count is greater than bytesFree.
+   *
+   * This function may be used in an ISR with writeOut()
+   * in non-interrupt code.
+   *
+   * \param[in] buf Location of data to be copied.
+   * \param[in] count number of bytes to be copied.
+   * \return Number of bytes actually copied.
+   */
+  size_t memcpyIn(const void* buf, size_t count) {
+    const uint8_t* src = (const uint8_t*)buf;
+    size_t n = Size - m_count;
+    if (count > n) {
+      count = n;
+    }
+    size_t nread = 0;
+    while (nread != count) {
+        n = minSize(Size - m_head, count - nread);
+        memcpyBuf(m_buf + m_head, src + nread, n);
+        m_head = advance(m_head, n);
+        nread += n;
+    }
+    m_count += nread;
+    return nread;
+  }
+  /**
+   * Copy date from the RingBuf to buf.
+   * The number of bytes copied may be less than count if
+   * bytesUsed is less than count.
+   *
+   * This function may be used in an ISR with readIn() in
+   * non-interrupt code.
+   *
+   * \param[out] buf Location to receive the data.
+   * \param[in] count number of bytes to be copied.
+   * \return Number of bytes actually copied.
+   */
+  size_t memcpyOut(void* buf, size_t count) {
+    uint8_t* dst = reinterpret_cast<uint8_t*>(buf);
+    size_t nwrite = 0;
+    size_t n = m_count;
+    if (count > n) {
+      count = n;
+    }
+    while (nwrite != count) {
+      n = minSize(Size - m_tail, count - nwrite);
+      memcpyBuf(dst + nwrite, m_buf + m_tail, n);
+      m_tail = advance(m_tail, n);
+      nwrite += n;
+    }
+    m_count -= nwrite;
+    return nwrite;
+  }
+  /** Print a number followed by a field terminator.
+   * \param[in] value The number to be printed.
+   * \param[in] term The field terminator.  Use '\\n' for CR LF.
+   * \param[in] prec Number of digits after decimal point.
+   * \return The number of bytes written.
+   */
+  size_t printField(double value, char term, uint8_t prec = 2) {
+    char buf[24];
+    char* str = buf + sizeof(buf);
+    if (term) {
+      *--str = term;
+      if (term == '\n') {
+        *--str = '\r';
+      }
+    }
+    str = fmtDouble(str, value, prec, false);
+    return write(str, buf + sizeof(buf) - str);
+  }
+  /** Print a number followed by a field terminator.
+   * \param[in] value The number to be printed.
+   * \param[in] term The field terminator.  Use '\\n' for CR LF.
+   * \param[in] prec Number of digits after decimal point.
+   * \return The number of bytes written or -1 if an error occurs.
+   */
+  size_t printField(float value, char term, uint8_t prec = 2) {
+    return printField(static_cast<double>(value), term, prec);
+  }
+  /** Print a number followed by a field terminator.
+   * \param[in] value The number to be printed.
+   * \param[in] term The field terminator.  Use '\\n' for CR LF.
+   * \return The number of bytes written or -1 if an error occurs.
+   */
+  template <typename Type>
+  size_t printField(Type value, char term) {
+    char sign = 0;
+    char buf[3*sizeof(Type) + 3];
+    char* str = buf + sizeof(buf);
+
+    if (term) {
+      *--str = term;
+      if (term == '\n') {
+        *--str = '\r';
+      }
+    }
+    if (value < 0) {
+      value = -value;
+      sign = '-';
+    }
+    if (sizeof(Type) < 4) {
+      str = fmtBase10(str, (uint16_t)value);
+    } else {
+      str = fmtBase10(str, (uint32_t)value);
+    }
+    if (sign) {
+      *--str = sign;
+    }
+    return write((const uint8_t*)str, &buf[sizeof(buf)] - str);
+  }
+  /**
+   * Read data into the RingBuf from the underlying file.
+   * the number of bytes read may be less than count if
+   * bytesFree is less than count.
+   *
+   * This function may be used in non-interrupt code with
+   * memcopyOut() in an ISR.
+   *
+   * \param[in] count number of bytes to be read.
+   * \return Number of bytes actually read.
+   */
+  size_t readIn(size_t count) {
+    size_t nread = 0;
+    size_t n = bytesFree();  // Protected from interrupts.
+    if (count > n) {
+      count = n;
+    }
+    while (nread != count) {
+        n = minSize(Size - m_head, count - nread);
+        if ((size_t)m_file->read(m_buf + m_head, n) != n) {
+          return nread;
+        }
+        m_head = advance(m_head, n);
+        nread += n;
+    }
+    noInterrupts();
+    m_count += nread;
+    interrupts();
+    return nread;
+  }
+  /**
+   * Write all data in the RingBuf to the underlying file.
+   * \param[in] data Byte to be written.
+   * \return Number of bytes actually written.
+   */
+  bool sync() {
+    size_t n = bytesUsed();
+    return writeOut(n) == n;
+  }
+  /**
+   * Copy data to the RingBuf from buf.
+   *
+   * The number of bytes copied may be less than count if
+   * count is greater than bytesFree.
+   * Use getWriteError() to check for print errors and
+   * clearWriteError() to clear error.
+   *
+   * \param[in] buf Location of data to be written.
+   * \param[in] count number of bytes to be written.
+   * \return Number of bytes actually written.
+   */
+  size_t write(const void* buf, size_t count) {
+    if (count > bytesFree()) {
+      setWriteError();
+    }
+    return memcpyIn(buf, count);
+  }
+  /**
+   * Override virtual function in Print for efficiency.
+   *
+   * \param[in] buf Location of data to be written.
+   * \param[in] count number of bytes to be written.
+   * \return Number of bytes actually written.
+   */
+  size_t write(const uint8_t* buf, size_t count) override {
+    return write((const void*)buf, count);
+  }
+  /**
+   * Required function for Print.
+   * \param[in] data Byte to be written.
+   * \return Number of bytes actually written.
+   */
+  size_t write(uint8_t data) override {
+    return write(&data, 1);
+  }
+  /**
+   * Write data to file from RingBuf buffer.
+   * \param[in] count number of bytes to be written.
+   *
+   * The number of bytes written may be less than count if
+   * bytesUsed is less than count or if an error occurs.
+   *
+   * This function may be used in non-interrupt code with
+   * memcopyIn() in an ISR.
+   *
+   * \return Number of bytes actually written.
+   */
+  size_t writeOut(size_t count) {
+    size_t n = bytesUsed();  // Protected from interrupts;
+     if (count > n) {
+      count = n;
+    }
+    size_t nwrite = 0;
+    while (nwrite != count) {
+      n = minSize(Size - m_tail, count - nwrite);
+      if (m_file->write(m_buf + m_tail, n) != n) {
+        break;
+      }
+      m_tail = advance(m_tail, n);
+      nwrite += n;
+    }
+    noInterrupts();
+    m_count -= nwrite;
+    interrupts();
+    return nwrite;
+  }
+
+ private:
+  uint8_t __attribute__((aligned(4))) m_buf[Size];
+  F* m_file = nullptr;
+  volatile size_t m_count;
+  size_t m_head;
+  size_t m_tail;
+
+  size_t advance(size_t index, size_t n) {
+    index += n;
+    return index < Size ? index : index - Size;
+  }
+  // avoid macro MIN
+  size_t minSize(size_t a, size_t b) {return a < b ? a : b;}
+};
+#endif  // RingBuf_h

+ 1 - 1
src/SdCard/SdCardInfo.h

@@ -71,6 +71,7 @@
   SD_CARD_ERROR(READ_START, "Bad readStart argument")\
   SD_CARD_ERROR(READ_START, "Bad readStart argument")\
   SD_CARD_ERROR(READ_TIMEOUT, "Read data timeout")\
   SD_CARD_ERROR(READ_TIMEOUT, "Read data timeout")\
   SD_CARD_ERROR(STOP_TRAN, "Multiple block stop failed")\
   SD_CARD_ERROR(STOP_TRAN, "Multiple block stop failed")\
+  SD_CARD_ERROR(TRANSFER_COMPLETE, "SDIO transfer complete")\
   SD_CARD_ERROR(WRITE_DATA, "Write data not accepted")\
   SD_CARD_ERROR(WRITE_DATA, "Write data not accepted")\
   SD_CARD_ERROR(WRITE_FIFO, "SDIO fifo write timeout")\
   SD_CARD_ERROR(WRITE_FIFO, "SDIO fifo write timeout")\
   SD_CARD_ERROR(WRITE_START, "Bad writeStart argument")\
   SD_CARD_ERROR(WRITE_START, "Bad writeStart argument")\
@@ -84,7 +85,6 @@
   SD_CARD_ERROR(INVALID_CARD_CONFIG, "Invalid card config")\
   SD_CARD_ERROR(INVALID_CARD_CONFIG, "Invalid card config")\
   SD_CARD_ERROR(FUNCTION_NOT_SUPPORTED, "Unsupported SDIO command")
   SD_CARD_ERROR(FUNCTION_NOT_SUPPORTED, "Unsupported SDIO command")
 
 
-
 enum {
 enum {
 #define  SD_CARD_ERROR(e, m) SD_CARD_ERROR_##e,
 #define  SD_CARD_ERROR(e, m) SD_CARD_ERROR_##e,
   SD_ERROR_CODE_LIST
   SD_ERROR_CODE_LIST

+ 36 - 36
src/SdCard/SdSpiCard.cpp

@@ -222,6 +222,7 @@ static uint16_t CRC_CCITT(const uint8_t* data, size_t n) {
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 bool SdSpiCard::begin(SdSpiConfig spiConfig) {
 bool SdSpiCard::begin(SdSpiConfig spiConfig) {
   SdMillis_t t0 = SysCall::curTimeMS();
   SdMillis_t t0 = SysCall::curTimeMS();
+  m_spiActive = false;
   m_errorCode = SD_CARD_ERROR_NONE;
   m_errorCode = SD_CARD_ERROR_NONE;
   m_type = 0;
   m_type = 0;
   m_csPin = spiConfig.csPin;
   m_csPin = spiConfig.csPin;
@@ -238,16 +239,12 @@ bool SdSpiCard::begin(SdSpiConfig spiConfig) {
   spiBegin(spiConfig);
   spiBegin(spiConfig);
   uint32_t arg;
   uint32_t arg;
 #if ENABLE_DEDICATED_SPI
 #if ENABLE_DEDICATED_SPI
-  m_sharedSpi = !(spiConfig.options & DEDICATED_SPI);
-  m_spiActive = false;
   m_curState = IDLE_STATE;
   m_curState = IDLE_STATE;
+  m_sharedSpi = spiOptionShared(spiConfig.options);
 #else  // ENABLE_DEDICATED_SPI
 #else  // ENABLE_DEDICATED_SPI
-  if (spiConfig.options & DEDICATED_SPI) {
-    error(SD_CARD_ERROR_INVALID_CARD_CONFIG);
-    goto fail;
-  }
+  // m_sharedSpi is a static const bool in this case.
+  static_assert(m_sharedSpi == true, "m_sharedSpi bug");
 #endif  // ENABLE_DEDICATED_SPI
 #endif  // ENABLE_DEDICATED_SPI
-
   spiStart();
   spiStart();
 
 
   // must supply min of 74 clock cycles with CS high.
   // must supply min of 74 clock cycles with CS high.
@@ -333,6 +330,11 @@ bool SdSpiCard::begin(SdSpiConfig spiConfig) {
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 // send command and return error code.  Return zero for OK
 // send command and return error code.  Return zero for OK
 uint8_t SdSpiCard::cardCommand(uint8_t cmd, uint32_t arg) {
 uint8_t SdSpiCard::cardCommand(uint8_t cmd, uint32_t arg) {
+#if ENABLE_DEDICATED_SPI
+  if (m_curState != IDLE_STATE && !syncDevice()) {
+    return 0XFF;
+  }
+#endif  // ENABLE_DEDICATED_SPI
   // select card
   // select card
   if (!m_spiActive) {
   if (!m_spiActive) {
     spiStart();
     spiStart();
@@ -430,6 +432,11 @@ bool SdSpiCard::eraseSingleSectorEnable() {
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 bool SdSpiCard::isBusy() {
 bool SdSpiCard::isBusy() {
+#if ENABLE_DEDICATED_SPI
+  if (m_curState == READ_STATE) {
+    return false;
+  }
+#endif  // ENABLE_DEDICATED_SPI
   bool rtn = true;
   bool rtn = true;
   bool spiActive = m_spiActive;
   bool spiActive = m_spiActive;
   if (!spiActive) {
   if (!spiActive) {
@@ -508,7 +515,6 @@ bool SdSpiCard::readData(uint8_t* dst, size_t count) {
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 bool SdSpiCard::readOCR(uint32_t* ocr) {
 bool SdSpiCard::readOCR(uint32_t* ocr) {
   uint8_t* p = reinterpret_cast<uint8_t*>(ocr);
   uint8_t* p = reinterpret_cast<uint8_t*>(ocr);
-  syncDevice();
   if (cardCommand(CMD58, 0)) {
   if (cardCommand(CMD58, 0)) {
     error(SD_CARD_ERROR_CMD58);
     error(SD_CARD_ERROR_CMD58);
     goto fail;
     goto fail;
@@ -527,7 +533,6 @@ bool SdSpiCard::readOCR(uint32_t* ocr) {
 /** read CID or CSR register */
 /** read CID or CSR register */
 bool SdSpiCard::readRegister(uint8_t cmd, void* buf) {
 bool SdSpiCard::readRegister(uint8_t cmd, void* buf) {
   uint8_t* dst = reinterpret_cast<uint8_t*>(buf);
   uint8_t* dst = reinterpret_cast<uint8_t*>(buf);
-  syncDevice();
   if (cardCommand(cmd, 0)) {
   if (cardCommand(cmd, 0)) {
     error(SD_CARD_ERROR_READ_REG);
     error(SD_CARD_ERROR_READ_REG);
     goto fail;
     goto fail;
@@ -599,33 +604,32 @@ bool SdSpiCard::readStatus(uint8_t* status) {
 bool SdSpiCard::readSectors(uint32_t sector, uint8_t* dst, size_t ns) {
 bool SdSpiCard::readSectors(uint32_t sector, uint8_t* dst, size_t ns) {
 #if ENABLE_DEDICATED_SPI
 #if ENABLE_DEDICATED_SPI
   if (m_curState != READ_STATE || sector != m_curSector) {
   if (m_curState != READ_STATE || sector != m_curSector) {
-    if (!syncDevice()) {
-      return false;
-    }
-    if (!SdSpiCard::readStart(sector)) {
-      return false;
+    if (!readStart(sector)) {
+      goto fail;
     }
     }
     m_curSector = sector;
     m_curSector = sector;
     m_curState = READ_STATE;
     m_curState = READ_STATE;
   }
   }
   for (size_t i = 0; i < ns; i++, dst += 512) {
   for (size_t i = 0; i < ns; i++, dst += 512) {
     if (!readData(dst, 512)) {
     if (!readData(dst, 512)) {
-      return false;
+      goto fail;
     }
     }
   }
   }
   m_curSector += ns;
   m_curSector += ns;
   return m_sharedSpi ? syncDevice() : true;
   return m_sharedSpi ? syncDevice() : true;
 #else  // ENABLE_DEDICATED_SPI
 #else  // ENABLE_DEDICATED_SPI
   if (!readStart(sector)) {
   if (!readStart(sector)) {
-    return false;
+    goto fail;
   }
   }
   for (size_t i = 0; i < ns; i++, dst += 512) {
   for (size_t i = 0; i < ns; i++, dst += 512) {
     if (!readData(dst, 512)) {
     if (!readData(dst, 512)) {
-      return false;
+      goto fail;
     }
     }
   }
   }
   return readStop();
   return readStop();
 #endif  // ENABLE_DEDICATED_SPI
 #endif  // ENABLE_DEDICATED_SPI
+ fail:
+  return false;
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 bool SdSpiCard::readStop() {
 bool SdSpiCard::readStop() {
@@ -665,16 +669,15 @@ void SdSpiCard::spiStop() {
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 bool SdSpiCard::syncDevice() {
 bool SdSpiCard::syncDevice() {
 #if ENABLE_DEDICATED_SPI
 #if ENABLE_DEDICATED_SPI
-  if (m_curState == READ_STATE) {
-    if (!SdSpiCard::readStop()) {
-      return false;
-    }
-  } else if (m_curState == WRITE_STATE) {
-    if (!SdSpiCard::writeStop()) {
-      return false;
-    }
-  }
+  // Insure no recursive loop with cardCommand().
+  uint8_t state = m_curState;
   m_curState = IDLE_STATE;
   m_curState = IDLE_STATE;
+  if (state == WRITE_STATE) {
+    return writeStop();
+  }
+  if (state == READ_STATE) {
+    return readStop();
+  }
 #endif  // ENABLE_DEDICATED_SPI
 #endif  // ENABLE_DEDICATED_SPI
   return true;
   return true;
 }
 }
@@ -780,20 +783,17 @@ bool SdSpiCard::writeSingle(uint32_t sector, const uint8_t* src) {
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 bool SdSpiCard::writeSectors(uint32_t sector, const uint8_t* src, size_t ns) {
 bool SdSpiCard::writeSectors(uint32_t sector, const uint8_t* src, size_t ns) {
-  #if ENABLE_DEDICATED_SPI
+#if ENABLE_DEDICATED_SPI
   if (m_curState != WRITE_STATE || m_curSector != sector) {
   if (m_curState != WRITE_STATE || m_curSector != sector) {
-    if (!syncDevice()) {
-      return false;
-    }
     if (!writeStart(sector)) {
     if (!writeStart(sector)) {
-      return false;
+      goto fail;
     }
     }
     m_curSector = sector;
     m_curSector = sector;
     m_curState = WRITE_STATE;
     m_curState = WRITE_STATE;
   }
   }
   for (size_t i = 0; i < ns; i++, src += 512) {
   for (size_t i = 0; i < ns; i++, src += 512) {
     if (!writeData(src)) {
     if (!writeData(src)) {
-      return false;
+      goto fail;
     }
     }
   }
   }
   m_curSector += ns;
   m_curSector += ns;
@@ -808,11 +808,11 @@ bool SdSpiCard::writeSectors(uint32_t sector, const uint8_t* src, size_t ns) {
     }
     }
   }
   }
   return writeStop();
   return writeStop();
+#endif  // ENABLE_DEDICATED_SPI
 
 
  fail:
  fail:
   spiStop();
   spiStop();
   return false;
   return false;
-#endif  // ENABLE_DEDICATED_SPI
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 bool SdSpiCard::writeStart(uint32_t sector) {
 bool SdSpiCard::writeStart(uint32_t sector) {
@@ -831,7 +831,7 @@ bool SdSpiCard::writeStart(uint32_t sector) {
   return false;
   return false;
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
-bool SdSpiCard::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
+bool SdSpiCard::writeStart(uint32_t sector, uint32_t eraseCount) {
   // send pre-erase count
   // send pre-erase count
   if (cardAcmd(ACMD23, eraseCount)) {
   if (cardAcmd(ACMD23, eraseCount)) {
     error(SD_CARD_ERROR_ACMD23);
     error(SD_CARD_ERROR_ACMD23);
@@ -839,9 +839,9 @@ bool SdSpiCard::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
   }
   }
   // use address if not SDHC card
   // use address if not SDHC card
   if (type() != SD_CARD_TYPE_SDHC) {
   if (type() != SD_CARD_TYPE_SDHC) {
-    blockNumber <<= 9;
+    sector <<= 9;
   }
   }
-  if (cardCommand(CMD25, blockNumber)) {
+  if (cardCommand(CMD25, sector)) {
     error(SD_CARD_ERROR_CMD25);
     error(SD_CARD_ERROR_CMD25);
     goto fail;
     goto fail;
   }
   }

+ 11 - 9
src/SdCard/SdSpiCard.h

@@ -47,7 +47,7 @@ class SdSpiCard {
 #endif  // HAS_SDIO_CLASS
 #endif  // HAS_SDIO_CLASS
  public:
  public:
   /** Construct an instance of SdSpiCard. */
   /** Construct an instance of SdSpiCard. */
-  SdSpiCard() : m_errorCode(SD_CARD_ERROR_INIT_NOT_CALLED), m_type(0) {}
+  SdSpiCard() {}
   /** Initialize the SD card.
   /** Initialize the SD card.
    * \param[in] spiConfig SPI card configuration.
    * \param[in] spiConfig SPI card configuration.
    * \return true for success or false for failure.
    * \return true for success or false for failure.
@@ -214,11 +214,11 @@ class SdSpiCard {
    * \return true for success or false for failure.
    * \return true for success or false for failure.
    */
    */
   bool writeSector(uint32_t sector, const uint8_t* src) {
   bool writeSector(uint32_t sector, const uint8_t* src) {
-#if ENABLE_DEDICATED_SPI
-    return writeSectors(sector, src, 1);
-#else  // ENABLE_DEDICATED_SPI
-    return writeSingle(sector, src);
-#endif  // ENABLE_DEDICATED_SPI
+    if (m_sharedSpi) {
+      return writeSingle(sector, src);
+    } else {
+      return writeSectors(sector, src, 1);
+    }
   }
   }
   /**
   /**
    * Writes a 512 byte sector to an SD card.
    * Writes a 512 byte sector to an SD card.
@@ -355,12 +355,14 @@ class SdSpiCard {
   static const uint8_t WRITE_STATE = 2;
   static const uint8_t WRITE_STATE = 2;
   uint32_t m_curSector;
   uint32_t m_curSector;
   uint8_t m_curState;
   uint8_t m_curState;
-  bool    m_sharedSpi;
+  bool m_sharedSpi = true;
+#else  // ENABLE_DEDICATED_SPI
+  static const bool m_sharedSpi = true;
 #endif  // ENABLE_DEDICATED_SPI
 #endif  // ENABLE_DEDICATED_SPI
   SdCsPin_t m_csPin;
   SdCsPin_t m_csPin;
-  uint8_t m_errorCode;
+  uint8_t m_errorCode = SD_CARD_ERROR_INIT_NOT_CALLED;
   bool    m_spiActive;
   bool    m_spiActive;
   uint8_t m_status;
   uint8_t m_status;
-  uint8_t m_type;
+  uint8_t m_type = 0;
 };
 };
 #endif  // SdSpiCard_h
 #endif  // SdSpiCard_h

+ 19 - 10
src/SdCard/SdioCard.h

@@ -35,7 +35,7 @@
  */
  */
 class SdioConfig {
 class SdioConfig {
  public:
  public:
-  SdioConfig() : m_options(FIFO_SDIO) {}
+  SdioConfig() {}
   /**
   /**
    * SdioConfig constructor.
    * SdioConfig constructor.
    * \param[in] opt SDIO options.
    * \param[in] opt SDIO options.
@@ -46,7 +46,7 @@ class SdioConfig {
   /** \return true if DMA_SDIO. */
   /** \return true if DMA_SDIO. */
   bool useDma() {return m_options & DMA_SDIO;}
   bool useDma() {return m_options & DMA_SDIO;}
  private:
  private:
-  uint8_t m_options;
+  uint8_t m_options = FIFO_SDIO;
 };
 };
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 /**
 /**
@@ -64,13 +64,7 @@ class SdioCard : public SdCardInterface {
    * \return false - not implemented.
    * \return false - not implemented.
    */
    */
   bool end() {return false;}
   bool end() {return false;}
-  /**
-   * Determine the size of an SD flash memory card.
-   *
-   * \return The number of 512 byte data sectors in the card
-   *         or zero if an error occurs.
-   */
-  uint32_t sectorCount();
+
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
   // Use sectorCount(). cardSize() will be removed in the future.
   // Use sectorCount(). cardSize() will be removed in the future.
   uint32_t cardSize() __attribute__ ((deprecated)) {return sectorCount();}
   uint32_t cardSize() __attribute__ ((deprecated)) {return sectorCount();}
@@ -180,6 +174,21 @@ class SdioCard : public SdCardInterface {
   bool readStop();
   bool readStop();
   /** \return SDIO card status. */
   /** \return SDIO card status. */
   uint32_t status();
   uint32_t status();
+    /**
+   * Determine the size of an SD flash memory card.
+   *
+   * \return The number of 512 byte data sectors in the card
+   *         or zero if an error occurs.
+   */
+  uint32_t sectorCount();
+  /**
+   *  Send CMD12 to stop read or write.
+   *
+   * \param[in] blocking If true, wait for command complete.
+   *
+   * \return true for success or false for failure.
+   */
+  bool stopTransmission(bool blocking);
   /** \return success if sync successful. Not for user apps. */
   /** \return success if sync successful. Not for user apps. */
   bool syncDevice();
   bool syncDevice();
   /** Return the card type: SD V1, SD V2 or SDHC
   /** Return the card type: SD V1, SD V2 or SDHC
@@ -241,6 +250,6 @@ class SdioCard : public SdCardInterface {
   static const uint8_t WRITE_STATE = 2;
   static const uint8_t WRITE_STATE = 2;
   uint32_t m_curSector;
   uint32_t m_curSector;
   SdioConfig m_sdioConfig;
   SdioConfig m_sdioConfig;
-  uint8_t m_curState;
+  uint8_t m_curState = IDLE_STATE;
 };
 };
 #endif  // SdioCard_h
 #endif  // SdioCard_h

+ 174 - 36
src/SdCard/SdioTeensy.cpp

@@ -28,10 +28,7 @@
 #include "SdioCard.h"
 #include "SdioCard.h"
 //==============================================================================
 //==============================================================================
 // limit of K66 due to errata KINETIS_K_0N65N.
 // limit of K66 due to errata KINETIS_K_0N65N.
-const uint32_t MAX_SDHC_COUNT = 0XFFFF;
-
-// Max RU is 1024 sectors.
-const uint32_t RU_MASK = 0X03FF;
+const uint32_t MAX_BLKCNT = 0XFFFF;
 //==============================================================================
 //==============================================================================
 #define SDHC_PROCTL_DTW_4BIT 0x01
 #define SDHC_PROCTL_DTW_4BIT 0x01
 const uint32_t FIFO_WML = 16;
 const uint32_t FIFO_WML = 16;
@@ -201,6 +198,9 @@ static bool (*m_busyFcn)() = 0;
 static bool m_initDone = false;
 static bool m_initDone = false;
 static bool m_version2;
 static bool m_version2;
 static bool m_highCapacity;
 static bool m_highCapacity;
+#if ENABLE_TEENSY_SDIO_MOD
+static bool m_transferActive = false;
+#endif  // ENABLE_TEENSY_SDIO_MOD
 static uint8_t m_errorCode = SD_CARD_ERROR_INIT_NOT_CALLED;
 static uint8_t m_errorCode = SD_CARD_ERROR_INIT_NOT_CALLED;
 static uint32_t m_errorLine = 0;
 static uint32_t m_errorLine = 0;
 static uint32_t m_rca;
 static uint32_t m_rca;
@@ -216,20 +216,53 @@ static csd_t m_csd;
 #if USE_DEBUG_MODE
 #if USE_DEBUG_MODE
 #define DBG_IRQSTAT() if (SDHC_IRQSTAT) {Serial.print(__LINE__);\
 #define DBG_IRQSTAT() if (SDHC_IRQSTAT) {Serial.print(__LINE__);\
         Serial.print(" IRQSTAT "); Serial.println(SDHC_IRQSTAT, HEX);}
         Serial.print(" IRQSTAT "); Serial.println(SDHC_IRQSTAT, HEX);}
-
 static void printRegs(uint32_t line) {
 static void printRegs(uint32_t line) {
-  Serial.print(line);
-  Serial.print(" SDHC_BLKATTR ");
-  Serial.print(SDHC_BLKATTR, HEX);
-  Serial.print(" XFERTYP ");
-  Serial.print(SDHC_XFERTYP, HEX);
-  Serial.print(" PRSSTAT ");
-  Serial.print(SDHC_PRSSTAT, HEX);
-  Serial.print(" PROCTL ");
-  Serial.print(SDHC_PROCTL, HEX);
-  Serial.print(" IRQSTAT ");
-  Serial.print(SDHC_IRQSTAT, HEX);
-  Serial.print(" m_irqstat ");
+  uint32_t blkattr = SDHC_BLKATTR;
+  uint32_t xfertyp = SDHC_XFERTYP;
+  uint32_t prsstat = SDHC_PRSSTAT;
+  uint32_t proctl = SDHC_PROCTL;
+  uint32_t irqstat = SDHC_IRQSTAT;
+  Serial.print("\nLINE: ");
+  Serial.println(line);
+  Serial.print("BLKATTR ");
+  Serial.println(blkattr, HEX);
+  Serial.print("XFERTYP ");
+  Serial.print(xfertyp, HEX);
+  Serial.print(" CMD");
+  Serial.print(xfertyp >> 24);
+  Serial.print(" TYP");
+  Serial.print((xfertyp >> 2) & 3);
+  if (xfertyp & SDHC_XFERTYP_DPSEL) {Serial.print(" DPSEL");}
+  Serial.println();
+  Serial.print("PRSSTAT ");
+  Serial.print(prsstat, HEX);
+  if (prsstat & SDHC_PRSSTAT_BREN) {Serial.print(" BREN");}
+  if (prsstat & SDHC_PRSSTAT_BWEN) {Serial.print(" BWEN");}
+  if (prsstat & SDHC_PRSSTAT_RTA) {Serial.print(" RTA");}
+  if (prsstat & SDHC_PRSSTAT_WTA) {Serial.print(" WTA");}
+  if (prsstat & SDHC_PRSSTAT_SDOFF) {Serial.print(" SDOFF");}
+  if (prsstat & SDHC_PRSSTAT_PEROFF) {Serial.print(" PEROFF");}
+  if (prsstat & SDHC_PRSSTAT_HCKOFF) {Serial.print(" HCKOFF");}
+  if (prsstat & SDHC_PRSSTAT_IPGOFF) {Serial.print(" IPGOFF");}
+  if (prsstat & SDHC_PRSSTAT_SDSTB) {Serial.print(" SDSTB");}
+  if (prsstat & SDHC_PRSSTAT_DLA) {Serial.print(" DLA");}
+  if (prsstat & SDHC_PRSSTAT_CDIHB) {Serial.print(" CDIHB");}
+  if (prsstat & SDHC_PRSSTAT_CIHB) {Serial.print(" CIHB");}
+  Serial.println();
+  Serial.print("PROCTL ");
+  Serial.print(proctl, HEX);
+  if (proctl & SDHC_PROCTL_SABGREQ) Serial.print(" SABGREQ");
+  Serial.print(" EMODE");
+  Serial.print((proctl >>4) & 3);
+  Serial.print(" DWT");
+  Serial.print((proctl >>1) & 3);
+  Serial.println();
+  Serial.print("IRQSTAT ");
+  Serial.print(irqstat, HEX);
+  if (irqstat & SDHC_IRQSTAT_BGE) {Serial.print(" BGE");}
+  if (irqstat & SDHC_IRQSTAT_TC) {Serial.print(" TC");}
+  if (irqstat & SDHC_IRQSTAT_CC) {Serial.print(" CC");}
+  Serial.print("\nm_irqstat ");
   Serial.println(m_irqstat, HEX);
   Serial.println(m_irqstat, HEX);
 }
 }
 #else  // USE_DEBUG_MODE
 #else  // USE_DEBUG_MODE
@@ -299,7 +332,7 @@ static void gpioMux(uint8_t mode) {
 static void enableGPIO(bool enable) {
 static void enableGPIO(bool enable) {
   const uint32_t CLOCK_MASK = IOMUXC_SW_PAD_CTL_PAD_PKE |
   const uint32_t CLOCK_MASK = IOMUXC_SW_PAD_CTL_PAD_PKE |
 #if defined(ARDUINO_TEENSY41)
 #if defined(ARDUINO_TEENSY41)
-                              IOMUXC_SW_PAD_CTL_PAD_DSE(1) |
+                              IOMUXC_SW_PAD_CTL_PAD_DSE(7) |
 #else  // defined(ARDUINO_TEENSY41)
 #else  // defined(ARDUINO_TEENSY41)
                               IOMUXC_SW_PAD_CTL_PAD_DSE(4) |  ///// WHG
                               IOMUXC_SW_PAD_CTL_PAD_DSE(4) |  ///// WHG
 #endif  // defined(ARDUINO_TEENSY41)
 #endif  // defined(ARDUINO_TEENSY41)
@@ -402,7 +435,7 @@ static void initSDHC() {
 
 
 #if defined (__IMXRT1062__)
 #if defined (__IMXRT1062__)
   SDHC_MIX_CTRL |= 0x80000000;
   SDHC_MIX_CTRL |= 0x80000000;
-#endif
+#endif  //  (__IMXRT1062__)
 
 
   // Reset SDHC. Use default Water Mark Level of 16.
   // Reset SDHC. Use default Water Mark Level of 16.
   SDHC_SYSCTL |= SDHC_SYSCTL_RSTA | SDHC_SYSCTL_SDCLKFS(0x80);
   SDHC_SYSCTL |= SDHC_SYSCTL_RSTA | SDHC_SYSCTL_SDCLKFS(0x80);
@@ -433,11 +466,7 @@ static uint32_t statusCMD13() {
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 static bool isBusyCMD13() {
 static bool isBusyCMD13() {
-  if (!cardCommand(CMD13_XFERTYP, m_rca)) {
-    // Caller will timeout.
-    return true;
-  }
-  return !(SDHC_CMDRSP0 & CARD_STATUS_READY_FOR_DATA);
+  return !(statusCMD13() & CARD_STATUS_READY_FOR_DATA);
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 static bool isBusyCommandComplete() {
 static bool isBusyCommandComplete() {
@@ -448,6 +477,10 @@ static bool isBusyCommandInhibit() {
   return SDHC_PRSSTAT & SDHC_PRSSTAT_CIHB;
   return SDHC_PRSSTAT & SDHC_PRSSTAT_CIHB;
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
+static bool isBusyDat() {
+  return SDHC_PRSSTAT & (1 << 24) ? false : true;
+}
+//------------------------------------------------------------------------------
 static bool isBusyDMA() {
 static bool isBusyDMA() {
   return m_dmaBusy;
   return m_dmaBusy;
 }
 }
@@ -536,20 +569,26 @@ static void setSdclk(uint32_t kHzMax) {
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 static bool transferStop() {
 static bool transferStop() {
+  // This fix allows CDIHB to be cleared in Tennsy 3.x without a reset.
+  SDHC_PROCTL &= ~SDHC_PROCTL_SABGREQ;
   if (!cardCommand(CMD12_XFERTYP, 0)) {
   if (!cardCommand(CMD12_XFERTYP, 0)) {
     return sdError(SD_CARD_ERROR_CMD12);
     return sdError(SD_CARD_ERROR_CMD12);
   }
   }
-  if (yieldTimeout(isBusyCMD13)) {
+//  if (yieldTimeout(isBusyCMD13)) {
+  if (yieldTimeout(isBusyDat)) {
     return sdError(SD_CARD_ERROR_CMD13);
     return sdError(SD_CARD_ERROR_CMD13);
   }
   }
-  // Save registers before reset DAT lines.
-  uint32_t irqsststen = SDHC_IRQSTATEN;
-  uint32_t proctl = SDHC_PROCTL & ~SDHC_PROCTL_SABGREQ;
-  // Do reset to clear CDIHB.  Should be a better way!
-  SDHC_SYSCTL |= SDHC_SYSCTL_RSTD;
-  // Restore registers.
-  SDHC_IRQSTATEN = irqsststen;
-  SDHC_PROCTL = proctl;
+  if (SDHC_PRSSTAT & SDHC_PRSSTAT_CDIHB) {
+    // This should not happen after above fix.
+    // Save registers before reset DAT lines.
+    uint32_t irqsststen = SDHC_IRQSTATEN;
+    uint32_t proctl = SDHC_PROCTL & ~SDHC_PROCTL_SABGREQ;
+    // Do reset to clear CDIHB.  Should be a better way!
+    SDHC_SYSCTL |= SDHC_SYSCTL_RSTD;
+    // Restore registers.
+    SDHC_IRQSTATEN = irqsststen;
+    SDHC_PROCTL = proctl;
+  }
   return true;
   return true;
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
@@ -585,6 +624,22 @@ static bool waitTimeout(bool (*fcn)()) {
   }
   }
   return false;  // Caller will set errorCode.
   return false;  // Caller will set errorCode.
 }
 }
+#if ENABLE_TEENSY_SDIO_MOD
+//------------------------------------------------------------------------------
+static bool waitTransferComplete() {
+  if (!m_transferActive) {
+    return true;
+  }
+  bool timeOut = waitTimeout(isBusyTransferComplete);
+  m_transferActive = false;
+  m_irqstat = SDHC_IRQSTAT;
+  SDHC_IRQSTAT = m_irqstat;
+  if (timeOut || (m_irqstat & SDHC_IRQSTAT_ERROR)) {
+    return sdError(SD_CARD_ERROR_TRANSFER_COMPLETE);
+  }
+  return true;
+}
+#endif  // ENABLE_TEENSY_SDIO_MOD
 //==============================================================================
 //==============================================================================
 // Start of SdioCard member functions.
 // Start of SdioCard member functions.
 //==============================================================================
 //==============================================================================
@@ -675,6 +730,11 @@ bool SdioCard::begin(SdioConfig sdioConfig) {
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 bool SdioCard::erase(uint32_t firstSector, uint32_t lastSector) {
 bool SdioCard::erase(uint32_t firstSector, uint32_t lastSector) {
+#if ENABLE_TEENSY_SDIO_MOD
+  if (m_curState != IDLE_STATE && !syncDevice()) {
+    return false;
+  }
+#endif  // ENABLE_TEENSY_SDIO_MOD
   // check for single sector erase
   // check for single sector erase
   if (!m_csd.v1.erase_blk_en) {
   if (!m_csd.v1.erase_blk_en) {
     // erase size mask
     // erase size mask
@@ -716,7 +776,31 @@ uint32_t SdioCard::errorLine() const {
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 bool SdioCard::isBusy() {
 bool SdioCard::isBusy() {
+#if ENABLE_TEENSY_SDIO_MOD
+  if (m_sdioConfig.useDma()) {
+    return m_busyFcn ? m_busyFcn() : m_initDone && isBusyCMD13();
+  } else {
+    if (m_transferActive) {
+      if (isBusyTransferComplete()) {
+        return true;
+      }
+#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
+      if ((SDHC_BLKATTR & 0XFFFF0000) != 0) {
+        return false;
+      }
+      m_transferActive = false;
+      stopTransmission(false);
+      return true;
+#else  // defined(__MK64FX512__) || defined(__MK66FX1M0__)
+      return false;
+#endif  // defined(__MK64FX512__) || defined(__MK66FX1M0__)
+    }
+    // Use DAT0 low as busy.
+    return SDHC_PRSSTAT & (1 << 24) ? false : true;
+  }
+#else  // ENABLE_TEENSY_SDIO_MOD
   return m_busyFcn ? m_busyFcn() : m_initDone && isBusyCMD13();
   return m_busyFcn ? m_busyFcn() : m_initDone && isBusyCMD13();
+#endif  // ENABLE_TEENSY_SDIO_MOD
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 uint32_t SdioCard::kHzSdClk() {
 uint32_t SdioCard::kHzSdClk() {
@@ -781,6 +865,11 @@ bool SdioCard::readSector(uint32_t sector, uint8_t* dst) {
       memcpy(dst, aligned, 512);
       memcpy(dst, aligned, 512);
     }
     }
   } else {
   } else {
+#if ENABLE_TEENSY_SDIO_MOD
+    if (!waitTransferComplete()) {
+      return false;
+    }
+#endif  // ENABLE_TEENSY_SDIO_MOD
     if (m_curState != READ_STATE || sector != m_curSector) {
     if (m_curState != READ_STATE || sector != m_curSector) {
       if (!syncDevice()) {
       if (!syncDevice()) {
         return false;
         return false;
@@ -841,7 +930,7 @@ bool SdioCard::readStart(uint32_t sector) {
   SDHC_BLKATTR = SDHC_BLKATTR_BLKSIZE(512);
   SDHC_BLKATTR = SDHC_BLKATTR_BLKSIZE(512);
 #else  // defined(__IMXRT1062__)
 #else  // defined(__IMXRT1062__)
   // Errata - can't do infinite transfer.
   // Errata - can't do infinite transfer.
-  SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(0XFFFF) | SDHC_BLKATTR_BLKSIZE(512);
+  SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(MAX_BLKCNT) | SDHC_BLKATTR_BLKSIZE(512);
 #endif  // defined(__IMXRT1062__)
 #endif  // defined(__IMXRT1062__)
 
 
   if (!cardCommand(CMD18_PGM_XFERTYP, m_highCapacity ? sector : 512*sector)) {
   if (!cardCommand(CMD18_PGM_XFERTYP, m_highCapacity ? sector : 512*sector)) {
@@ -862,7 +951,30 @@ uint32_t SdioCard::status() {
   return statusCMD13();
   return statusCMD13();
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
+bool SdioCard::stopTransmission(bool blocking) {
+  m_curState = IDLE_STATE;
+  // This fix allows CDIHB to be cleared in Tennsy 3.x without a reset.
+  SDHC_PROCTL &= ~SDHC_PROCTL_SABGREQ;
+  if (!cardCommand(CMD12_XFERTYP, 0)) {
+    return sdError(SD_CARD_ERROR_CMD12);
+  }
+  if (blocking) {
+    if (yieldTimeout(isBusyDat)) {
+      return sdError(SD_CARD_ERROR_CMD13);
+    }
+  }
+  return true;
+}
+//------------------------------------------------------------------------------
 bool SdioCard::syncDevice() {
 bool SdioCard::syncDevice() {
+#if ENABLE_TEENSY_SDIO_MOD
+  if (!waitTransferComplete()) {
+    return false;
+  }
+  if (m_curState != IDLE_STATE) {
+    return stopTransmission(true);
+  }
+#else  // ENABLE_TEENSY_SDIO_MOD
   if (m_curState == READ_STATE) {
   if (m_curState == READ_STATE) {
     m_curState = IDLE_STATE;
     m_curState = IDLE_STATE;
     if (!readStop()) {
     if (!readStop()) {
@@ -874,6 +986,7 @@ bool SdioCard::syncDevice() {
       return false;
       return false;
     }
     }
   }
   }
+#endif  // ENABLE_TEENSY_SDIO_MOD
   return true;
   return true;
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
@@ -884,6 +997,11 @@ uint8_t SdioCard::type() const {
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 bool SdioCard::writeData(const uint8_t* src) {
 bool SdioCard::writeData(const uint8_t* src) {
   DBG_IRQSTAT();
   DBG_IRQSTAT();
+#if ENABLE_TEENSY_SDIO_MOD
+  if (!waitTransferComplete()) {
+    return false;
+  }
+#endif  // ENABLE_TEENSY_SDIO_MOD
   const uint32_t* p32 = reinterpret_cast<const uint32_t*>(src);
   const uint32_t* p32 = reinterpret_cast<const uint32_t*>(src);
   if (!(SDHC_PRSSTAT & SDHC_PRSSTAT_WTA)) {
   if (!(SDHC_PRSSTAT & SDHC_PRSSTAT_WTA)) {
     SDHC_PROCTL &= ~SDHC_PROCTL_SABGREQ;
     SDHC_PROCTL &= ~SDHC_PROCTL_SABGREQ;
@@ -901,12 +1019,17 @@ bool SdioCard::writeData(const uint8_t* src) {
     }
     }
     p32 += FIFO_WML;
     p32 += FIFO_WML;
   }
   }
+#if ENABLE_TEENSY_SDIO_MOD
+  m_transferActive = true;
+  return true;
+#else  // ENABLE_TEENSY_SDIO_MOD
   if (waitTimeout(isBusyTransferComplete)) {
   if (waitTimeout(isBusyTransferComplete)) {
     return sdError(SD_CARD_ERROR_WRITE_TIMEOUT);
     return sdError(SD_CARD_ERROR_WRITE_TIMEOUT);
   }
   }
   m_irqstat = SDHC_IRQSTAT;
   m_irqstat = SDHC_IRQSTAT;
   SDHC_IRQSTAT = m_irqstat;
   SDHC_IRQSTAT = m_irqstat;
   return (m_irqstat & SDHC_IRQSTAT_TC) && !(m_irqstat & SDHC_IRQSTAT_ERROR);
   return (m_irqstat & SDHC_IRQSTAT_TC) && !(m_irqstat & SDHC_IRQSTAT_ERROR);
+#endif  // ENABLE_TEENSY_SDIO_MOD
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 bool SdioCard::writeSector(uint32_t sector, const uint8_t* src) {
 bool SdioCard::writeSector(uint32_t sector, const uint8_t* src) {
@@ -919,10 +1042,23 @@ bool SdioCard::writeSector(uint32_t sector, const uint8_t* src) {
     } else {
     } else {
       ptr = const_cast<uint8_t*>(src);
       ptr = const_cast<uint8_t*>(src);
     }
     }
-  if (!rdWrSectors(CMD24_DMA_XFERTYP, sector, ptr, 1)) {
+    if (!rdWrSectors(CMD24_DMA_XFERTYP, sector, ptr, 1)) {
       return sdError(SD_CARD_ERROR_CMD24);
       return sdError(SD_CARD_ERROR_CMD24);
     }
     }
   } else {
   } else {
+#if ENABLE_TEENSY_SDIO_MOD
+    if (!waitTransferComplete()) {
+      return false;
+    }
+#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
+    // End transfer with CMD12 if required.
+    if ((SDHC_BLKATTR & 0XFFFF0000) == 0) {
+      if (!syncDevice()) {
+        return false;
+      }
+    }
+#endif  // defined(__MK64FX512__) || defined(__MK66FX1M0__)
+#endif  // ENABLE_TEENSY_SDIO_MOD
     if (m_curState != WRITE_STATE || m_curSector != sector) {
     if (m_curState != WRITE_STATE || m_curSector != sector) {
       if (!syncDevice()) {
       if (!syncDevice()) {
         return false;
         return false;
@@ -937,6 +1073,7 @@ bool SdioCard::writeSector(uint32_t sector, const uint8_t* src) {
       return false;
       return false;
     }
     }
     m_curSector++;
     m_curSector++;
+#if !ENABLE_TEENSY_SDIO_MOD
 #if defined(__MK64FX512__) || defined(__MK66FX1M0__)
 #if defined(__MK64FX512__) || defined(__MK66FX1M0__)
     // End transfer with CMD12 if required.
     // End transfer with CMD12 if required.
     if ((SDHC_BLKATTR & 0XFFFF0000) == 0) {
     if ((SDHC_BLKATTR & 0XFFFF0000) == 0) {
@@ -945,6 +1082,7 @@ bool SdioCard::writeSector(uint32_t sector, const uint8_t* src) {
       }
       }
     }
     }
 #endif  // defined(__MK64FX512__) || defined(__MK66FX1M0__)
 #endif  // defined(__MK64FX512__) || defined(__MK66FX1M0__)
+#endif  // !ENABLE_TEENSY_SDIO_MOD
   }
   }
   return true;
   return true;
 }
 }
@@ -984,7 +1122,7 @@ bool SdioCard::writeStart(uint32_t sector) {
   SDHC_BLKATTR = SDHC_BLKATTR_BLKSIZE(512);
   SDHC_BLKATTR = SDHC_BLKATTR_BLKSIZE(512);
 #else  // defined(__IMXRT1062__)
 #else  // defined(__IMXRT1062__)
   // Errata - can't do infinite transfer.
   // Errata - can't do infinite transfer.
-  SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(0XFFFF) | SDHC_BLKATTR_BLKSIZE(512);
+  SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(MAX_BLKCNT) | SDHC_BLKATTR_BLKSIZE(512);
 #endif  // defined(__IMXRT1062__)
 #endif  // defined(__IMXRT1062__)
   if (!cardCommand(CMD25_PGM_XFERTYP, m_highCapacity ? sector : 512*sector)) {
   if (!cardCommand(CMD25_PGM_XFERTYP, m_highCapacity ? sector : 512*sector)) {
     return sdError(SD_CARD_ERROR_CMD25);
     return sdError(SD_CARD_ERROR_CMD25);

+ 2 - 2
src/SdFat.h

@@ -38,9 +38,9 @@
 #endif  // INCLUDE_SDIOS
 #endif  // INCLUDE_SDIOS
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 /** SdFat version  for cpp use. */
 /** SdFat version  for cpp use. */
-#define SD_FAT_VERSION 20004
+#define SD_FAT_VERSION 20005
 /** SdFat version as string. */
 /** SdFat version as string. */
-#define SD_FAT_VERSION_STR "2.0.4"
+#define SD_FAT_VERSION_STR "2.0.5"
 //==============================================================================
 //==============================================================================
 /**
 /**
  * \class SdBase
  * \class SdBase

+ 13 - 3
src/SdFatConfig.h

@@ -39,6 +39,9 @@
 /** For Debug - must be one */
 /** For Debug - must be one */
 #define ENABLE_ARDUINO_STRING 1
 #define ENABLE_ARDUINO_STRING 1
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
+/** Set zero to disable mod for non-blocking write. */
+#define ENABLE_TEENSY_SDIO_MOD 1
+//------------------------------------------------------------------------------
 /** Set USE_BLOCK_DEVICE_INTERFACE nonzero to use generic block device */
 /** Set USE_BLOCK_DEVICE_INTERFACE nonzero to use generic block device */
 #define USE_BLOCK_DEVICE_INTERFACE 0
 #define USE_BLOCK_DEVICE_INTERFACE 0
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
@@ -82,7 +85,7 @@
 #endif  // defined(__AVR__) && FLASHEND < 0X8000
 #endif  // defined(__AVR__) && FLASHEND < 0X8000
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 /**
 /**
- * Set ENABLE_DEDICATED_SPI to enable dedicated use of the SPI bus.
+ * Set ENABLE_DEDICATED_SPI non-zero to enable dedicated use of the SPI bus.
  * Selecting dedicated SPI in SdSpiConfig() will produce better
  * Selecting dedicated SPI in SdSpiConfig() will produce better
  * performance by using very large multi-block transfers to and
  * performance by using very large multi-block transfers to and
  * from the SD card.
  * from the SD card.
@@ -96,7 +99,7 @@
 // All other boards.
 // All other boards.
 #define ENABLE_DEDICATED_SPI 1
 #define ENABLE_DEDICATED_SPI 1
 #endif  // defined(__AVR__) && FLASHEND < 0X8000
 #endif  // defined(__AVR__) && FLASHEND < 0X8000
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
 /**
 /**
  * If the symbol SPI_DRIVER_SELECT is:
  * If the symbol SPI_DRIVER_SELECT is:
  *
  *
@@ -110,6 +113,13 @@
  * 3 - An external SPI driver derived from SdSpiBaseClass is always used.
  * 3 - An external SPI driver derived from SdSpiBaseClass is always used.
  */
  */
 #define SPI_DRIVER_SELECT 0
 #define SPI_DRIVER_SELECT 0
+/**
+ * If USE_SPI_ARRAY_TRANSFER is non-zero and the standard SPI library is
+ * use, the array transfer function, transfer(buf, size), will be used.
+ * This option will allocate up to a 512 byte temporary buffer for send.
+ * This may be faster for some boards.  Do not use this with AVR boards.
+ */
+#define USE_SPI_ARRAY_TRANSFER 0
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 /**
 /**
  * SD_CHIP_SELECT_MODE defines how the functions
  * SD_CHIP_SELECT_MODE defines how the functions
@@ -181,7 +191,7 @@ typedef uint8_t SdCsPin_t;
  * Some cards will not sleep in low power mode unless CHECK_FLASH_PROGRAMMING
  * Some cards will not sleep in low power mode unless CHECK_FLASH_PROGRAMMING
  * is non-zero.
  * is non-zero.
  */
  */
-#define CHECK_FLASH_PROGRAMMING 1
+#define CHECK_FLASH_PROGRAMMING 0
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 /**
 /**
  * Set MAINTAIN_FREE_CLUSTER_COUNT nonzero to keep the count of free clusters
  * Set MAINTAIN_FREE_CLUSTER_COUNT nonzero to keep the count of free clusters

+ 24 - 12
src/SpiDriver/SdSpiDriver.h

@@ -43,6 +43,24 @@ void sdCsInit(SdCsPin_t pin);
  */
  */
 void sdCsWrite(SdCsPin_t pin, bool level);
 void sdCsWrite(SdCsPin_t pin, bool level);
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
+/** SPI bus is share with other devices. */
+const uint8_t SHARED_SPI = 0;
+#if ENABLE_DEDICATED_SPI
+/** The SD is the only device on the SPI bus. */
+const uint8_t DEDICATED_SPI = 1;
+/**
+ * \param[in] opt option field of SdSpiConfig.
+ * \return true for shared SPI.
+ */
+inline bool spiOptionShared(uint8_t opt) {return !(opt & DEDICATED_SPI);}
+#else  // ENABLE_DEDICATED_SPI
+/**
+ * \param[in] opt option field of SdSpiConfig.
+ * \return true for shared SPI.
+ */
+inline bool spiOptionShared(uint8_t opt) {(void)opt; return true;}
+#endif  // ENABLE_DEDICATED_SPI
+//------------------------------------------------------------------------------
 /** SPISettings for SCK frequency in Hz. */
 /** SPISettings for SCK frequency in Hz. */
 #define SD_SCK_HZ(maxSpeed) (maxSpeed)
 #define SD_SCK_HZ(maxSpeed) (maxSpeed)
 /** SPISettings for SCK frequency in MHz. */
 /** SPISettings for SCK frequency in MHz. */
@@ -63,10 +81,6 @@ void sdCsWrite(SdCsPin_t pin, bool level);
 /** Set SCK rate to 500 kHz for AVR. */
 /** Set SCK rate to 500 kHz for AVR. */
 #define SPI_SIXTEENTH_SPEED SD_SCK_HZ(500000)
 #define SPI_SIXTEENTH_SPEED SD_SCK_HZ(500000)
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
-/** The SD is the only device on the SPI bus. */
-#define DEDICATED_SPI 0X80
-/** SPI bus is share with other devices. */
-#define SHARED_SPI 0
 #if SPI_DRIVER_SELECT < 2
 #if SPI_DRIVER_SELECT < 2
 #include "SPI.h"
 #include "SPI.h"
 /** Port type for Arduino SPI hardware driver. */
 /** Port type for Arduino SPI hardware driver. */
@@ -106,29 +120,27 @@ class SdSpiConfig {
    * \param[in] maxSpeed Maximum SCK frequency.
    * \param[in] maxSpeed Maximum SCK frequency.
    */
    */
   SdSpiConfig(SdCsPin_t cs, uint8_t opt, uint32_t maxSpeed) :
   SdSpiConfig(SdCsPin_t cs, uint8_t opt, uint32_t maxSpeed) :
-    csPin(cs), options(opt), maxSck(maxSpeed), spiPort(nullptr) {}
+    csPin(cs), options(opt), maxSck(maxSpeed) {}
   /** SdSpiConfig constructor.
   /** SdSpiConfig constructor.
    *
    *
    * \param[in] cs Chip select pin.
    * \param[in] cs Chip select pin.
    * \param[in] opt Options.
    * \param[in] opt Options.
    */
    */
-  SdSpiConfig(SdCsPin_t cs, uint8_t opt) :
-    csPin(cs), options(opt), maxSck(SD_SCK_MHZ(50)), spiPort(nullptr)  {}
+  SdSpiConfig(SdCsPin_t cs, uint8_t opt) : csPin(cs), options(opt) {}
   /** SdSpiConfig constructor.
   /** SdSpiConfig constructor.
    *
    *
    * \param[in] cs Chip select pin.
    * \param[in] cs Chip select pin.
    */
    */
-  explicit SdSpiConfig(SdCsPin_t cs) : csPin(cs), options(SHARED_SPI),
-                                     maxSck(SD_SCK_MHZ(50)), spiPort(nullptr) {}
+  explicit SdSpiConfig(SdCsPin_t cs) : csPin(cs) {}
 
 
   /** Chip select pin. */
   /** Chip select pin. */
   const SdCsPin_t csPin;
   const SdCsPin_t csPin;
   /** Options */
   /** Options */
-  const uint8_t options;
+  const uint8_t options = 0;
   /** Max SCK frequency */
   /** Max SCK frequency */
-  const uint32_t maxSck;
+  const uint32_t maxSck = SD_SCK_MHZ(50);
   /** SPI port */
   /** SPI port */
-  SpiPort_t* spiPort;
+  SpiPort_t* spiPort = nullptr;
 };
 };
 #if SPI_DRIVER_SELECT < 2
 #if SPI_DRIVER_SELECT < 2
 #include "SdSpiArduinoDriver.h"
 #include "SdSpiArduinoDriver.h"

+ 21 - 12
src/SpiDriver/SdSpiESP.cpp

@@ -28,46 +28,54 @@
 #define ESP_UNALIGN_OK 1
 #define ESP_UNALIGN_OK 1
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 void SdSpiArduinoDriver::activate() {
 void SdSpiArduinoDriver::activate() {
-  SPI.beginTransaction(m_spiSettings);
+  m_spi->beginTransaction(m_spiSettings);
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 void SdSpiArduinoDriver::begin(SdSpiConfig spiConfig) {
 void SdSpiArduinoDriver::begin(SdSpiConfig spiConfig) {
-  (void)spiConfig;
-  SPI.begin();
+  if (spiConfig.spiPort) {
+    m_spi = spiConfig.spiPort;
+#if defined(SDCARD_SPI) && defined(SDCARD_SS_PIN)
+  } else if (spiConfig.csPin == SDCARD_SS_PIN) {
+    m_spi = &SDCARD_SPI;
+#endif  // defined(SDCARD_SPI) && defined(SDCARD_SS_PIN)
+  } else {
+    m_spi = &SPI;
+  }
+  m_spi->begin();
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 void SdSpiArduinoDriver::deactivate() {
 void SdSpiArduinoDriver::deactivate() {
-  SPI.endTransaction();
+  m_spi->endTransaction();
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 uint8_t SdSpiArduinoDriver::receive() {
 uint8_t SdSpiArduinoDriver::receive() {
-  return SPI.transfer(0XFF);
+  return m_spi->transfer(0XFF);
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 uint8_t SdSpiArduinoDriver::receive(uint8_t* buf, size_t count) {
 uint8_t SdSpiArduinoDriver::receive(uint8_t* buf, size_t count) {
 #if ESP_UNALIGN_OK
 #if ESP_UNALIGN_OK
-  SPI.transferBytes(nullptr, buf, count);
+  m_spi->transferBytes(nullptr, buf, count);
 #else  // ESP_UNALIGN_OK
 #else  // ESP_UNALIGN_OK
   // Adjust to 32-bit alignment.
   // Adjust to 32-bit alignment.
   while ((reinterpret_cast<uintptr_t>(buf) & 0X3) && count) {
   while ((reinterpret_cast<uintptr_t>(buf) & 0X3) && count) {
-    *buf++ = SPI.transfer(0xff);
+    *buf++ = m_spi->transfer(0xff);
     count--;
     count--;
   }
   }
   // Do multiple of four byte transfers.
   // Do multiple of four byte transfers.
   size_t n4 = 4*(count/4);
   size_t n4 = 4*(count/4);
   if (n4) {
   if (n4) {
-    SPI.transferBytes(nullptr, buf, n4);
+    m_spi->transferBytes(nullptr, buf, n4);
   }
   }
   // Transfer up to three remaining bytes.
   // Transfer up to three remaining bytes.
   for (buf += n4, count -= n4; count; count--) {
   for (buf += n4, count -= n4; count; count--) {
-    *buf++ = SPI.transfer(0xff);
+    *buf++ = m_spi->transfer(0xff);
   }
   }
 #endif  // ESP_UNALIGN_OK
 #endif  // ESP_UNALIGN_OK
   return 0;
   return 0;
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 void SdSpiArduinoDriver::send(uint8_t data) {
 void SdSpiArduinoDriver::send(uint8_t data) {
-  SPI.transfer(data);
+  m_spi->transfer(data);
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 void SdSpiArduinoDriver::send(const uint8_t* buf , size_t count) {
 void SdSpiArduinoDriver::send(const uint8_t* buf , size_t count) {
@@ -78,6 +86,7 @@ void SdSpiArduinoDriver::send(const uint8_t* buf , size_t count) {
     count--;
     count--;
   }
   }
 #endif  // #if ESP_UNALIGN_OK
 #endif  // #if ESP_UNALIGN_OK
-  SPI.transferBytes(const_cast<uint8_t*>(buf), nullptr, count);
+
+  m_spi->transferBytes(const_cast<uint8_t*>(buf), nullptr, count);
 }
 }
-#endif  // defined(SD_USE_CUSTOM_SPI) && defined(ESP8266)
+#endif  // defined(SD_USE_CUSTOM_SPI) && (defined(ESP8266) || defined(ESP32))

+ 13 - 0
src/SpiDriver/SdSpiLibDriver.h

@@ -55,9 +55,14 @@ inline uint8_t SdSpiArduinoDriver::receive() {
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 inline uint8_t SdSpiArduinoDriver::receive(uint8_t* buf, size_t count) {
 inline uint8_t SdSpiArduinoDriver::receive(uint8_t* buf, size_t count) {
+#if USE_SPI_ARRAY_TRANSFER
+  memset(buf, 0XFF, count);
+  m_spi->transfer(buf, count);
+#else  // USE_SPI_ARRAY_TRANSFER
   for (size_t i = 0; i < count; i++) {
   for (size_t i = 0; i < count; i++) {
     buf[i] = m_spi->transfer(0XFF);
     buf[i] = m_spi->transfer(0XFF);
   }
   }
+#endif  // USE_SPI_ARRAY_TRANSFER
   return 0;
   return 0;
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
@@ -66,6 +71,14 @@ inline void SdSpiArduinoDriver::send(uint8_t data) {
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 inline void SdSpiArduinoDriver::send(const uint8_t* buf, size_t count) {
 inline void SdSpiArduinoDriver::send(const uint8_t* buf, size_t count) {
+#if USE_SPI_ARRAY_TRANSFER
+  if (count <= 512) {
+    uint8_t tmp[count];    // NOLINT
+    memcpy(tmp, buf, count);
+    m_spi->transfer(tmp, count);
+    return;
+  }
+#endif  // USE_SPI_ARRAY_TRANSFER
   for (size_t i = 0; i < count; i++) {
   for (size_t i = 0; i < count; i++) {
     m_spi->transfer(buf[i]);
     m_spi->transfer(buf[i]);
   }
   }

+ 1 - 1
src/common/DebugMacros.h

@@ -41,7 +41,7 @@ static void dbgPrint(uint16_t line) {
 
 
 #define DBG_PRINT_IF(b) if (b) {Serial.print(F(__FILE__));\
 #define DBG_PRINT_IF(b) if (b) {Serial.print(F(__FILE__));\
                         Serial.println(__LINE__);}
                         Serial.println(__LINE__);}
-#define DBG_HALT_IF(b) if (b) { Serial.print(F("DBG_HALT "));\
+#define DBG_HALT_IF(b) if (b) {Serial.print(F("DBG_HALT "));\
                        Serial.print(F(__FILE__)); Serial.println(__LINE__);\
                        Serial.print(F(__FILE__)); Serial.println(__LINE__);\
                        while (true) {}}
                        while (true) {}}
 #define DBG_FAIL_MACRO dbgPrint(__LINE__);
 #define DBG_FAIL_MACRO dbgPrint(__LINE__);

+ 2 - 2
src/common/PrintTemplates.h

@@ -377,7 +377,7 @@ int vmprintf(F* file, const char *fmt, va_list ap) {
         break;
         break;
 
 
       default:
       default:
-        *--str = c;;
+        *--str = c;
         break;
         break;
     }
     }
     ns = ptr - str;
     ns = ptr - str;
@@ -468,7 +468,7 @@ int vmprintf(F file, const __FlashStringHelper *ifsh, va_list ap) {
         break;
         break;
 
 
       default:
       default:
-        *--str = c;;
+        *--str = c;
         break;
         break;
     }
     }
     ns = ptr - str;
     ns = ptr - str;

+ 5 - 9
src/iostream/StdioStream.h

@@ -114,11 +114,7 @@ class StdioStream : private StreamBaseFile {
   /** Constructor
   /** Constructor
    *
    *
    */
    */
-  StdioStream() {
-    m_w = m_r = 0;
-    m_p = m_buf;
-    m_status = 0;
-  }
+  StdioStream() {}
   //----------------------------------------------------------------------------
   //----------------------------------------------------------------------------
   /** Clear the stream's end-of-file and error indicators. */
   /** Clear the stream's end-of-file and error indicators. */
   void clearerr() {
   void clearerr() {
@@ -657,11 +653,11 @@ class StdioStream : private StreamBaseFile {
   static const uint8_t S_EOF = 0x10;  // found EOF
   static const uint8_t S_EOF = 0x10;  // found EOF
   static const uint8_t S_ERR = 0x20;  // found error
   static const uint8_t S_ERR = 0x20;  // found error
   //----------------------------------------------------------------------------
   //----------------------------------------------------------------------------
-  uint8_t  m_status;
-  uint8_t* m_p;
-  uint8_t  m_r;
-  uint8_t  m_w;
   uint8_t  m_buf[STREAM_BUF_SIZE];
   uint8_t  m_buf[STREAM_BUF_SIZE];
+  uint8_t  m_status = 0;
+  uint8_t* m_p = m_buf;
+  uint8_t  m_r = 0;
+  uint8_t  m_w;
 };
 };
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 #endif  // StdioStream_h
 #endif  // StdioStream_h

+ 8 - 9
src/iostream/bufstream.h

@@ -38,7 +38,7 @@
 class ibufstream : public istream {
 class ibufstream : public istream {
  public:
  public:
   /** Constructor */
   /** Constructor */
-  ibufstream() : m_buf(nullptr), m_len(0) {}
+  ibufstream() {}
   /** Constructor
   /** Constructor
    * \param[in] str pointer to string to be parsed
    * \param[in] str pointer to string to be parsed
    * Warning: The string will not be copied so must stay in scope.
    * Warning: The string will not be copied so must stay in scope.
@@ -89,8 +89,8 @@ class ibufstream : public istream {
   }
   }
   /// @endcond
   /// @endcond
  private:
  private:
-  const char* m_buf;
-  size_t m_len;
+  const char* m_buf = nullptr;
+  size_t m_len = 0;
   size_t m_pos;
   size_t m_pos;
 };
 };
 //==============================================================================
 //==============================================================================
@@ -101,7 +101,7 @@ class ibufstream : public istream {
 class obufstream : public ostream {
 class obufstream : public ostream {
  public:
  public:
   /** constructor */
   /** constructor */
-  obufstream() : m_in(0) {}
+  obufstream() {}
   /** Constructor
   /** Constructor
    * \param[in] buf buffer for formatted string
    * \param[in] buf buffer for formatted string
    * \param[in] size buffer size
    * \param[in] size buffer size
@@ -131,7 +131,7 @@ class obufstream : public ostream {
  protected:
  protected:
   /// @cond SHOW_PROTECTED
   /// @cond SHOW_PROTECTED
   void putch(char c) {
   void putch(char c) {
-    if (m_in >= (m_size - 1)) {
+    if ((m_in + 1) >= m_size) {
       setstate(badbit);
       setstate(badbit);
       return;
       return;
     }
     }
@@ -159,14 +159,13 @@ class obufstream : public ostream {
   bool sync() {
   bool sync() {
     return true;
     return true;
   }
   }
-
   pos_type tellpos() {
   pos_type tellpos() {
     return m_in;
     return m_in;
   }
   }
   /// @endcond
   /// @endcond
  private:
  private:
-  char *m_buf;
-  size_t m_size;
-  size_t m_in;
+  char *m_buf = nullptr;
+  size_t m_size = 0;
+  size_t m_in = 0;
 };
 };
 #endif  // bufstream_h
 #endif  // bufstream_h

+ 2 - 2
src/iostream/ios.h

@@ -389,7 +389,7 @@ inline ios_base& uppercase(ios_base& str) {
 class ios : public ios_base {
 class ios : public ios_base {
  public:
  public:
   /** Create ios with no error flags set */
   /** Create ios with no error flags set */
-  ios() : m_iostate(0) {}
+  ios() {}
 
 
   /** \return null pointer if fail() is true. */
   /** \return null pointer if fail() is true. */
   operator const void*() const {
   operator const void*() const {
@@ -443,6 +443,6 @@ class ios : public ios_base {
   }
   }
 
 
  private:
  private:
-  iostate m_iostate;
+  iostate m_iostate = 0;
 };
 };
 #endif  // ios_h
 #endif  // ios_h