Improved Teensy SDIO, new features, bug fixes

This commit is contained in:
Bill Greiman 2021-02-25 08:52:43 -08:00
parent cb5a7d2afb
commit 4582dd4d6c
45 changed files with 1571 additions and 1454 deletions

View file

@ -1,8 +1,7 @@
2019-12-10
2021-01-06
Run the SdErrorCode example to produce an updated list.
C
Code,Symbol - failed operation
0X00,SD_CARD_ERROR_NONE - No error
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
0X1D,SD_CARD_ERROR_READ_TIMEOUT - Read data timeout
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
doc/ZipMsg/index.html Normal file
View file

@ -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>

Binary file not shown.

View file

@ -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>

View file

@ -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"/>

View file

@ -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>

Binary file not shown.

View file

@ -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() {
}

View file

@ -14,7 +14,12 @@ FsFile file1;
// Use mySPI2 since SPI2 is used in SPI.h as a different type.
static SPIClass mySPI2(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)
#else // ENABLE_DEDICATED_SPI
#define SD2_CONFIG SdSpiConfig(PB12, SHARED_SPI, SD_SCK_MHZ(18), &mySPI2)
#endif // ENABLE_DEDICATED_SPI
SdFs sd2;
FsFile file2;

View file

@ -21,7 +21,11 @@ const uint8_t SOFT_SCK_PIN = 13;
// SdFat software SPI template
SoftSpiDriver<SOFT_MISO_PIN, SOFT_MOSI_PIN, SOFT_SCK_PIN> softSpi;
// 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)
#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
SdFat sd;

View file

@ -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");
}

View file

@ -197,10 +197,15 @@ void loop() {
}
Serial.println("\nDMA SDIO mode - slow for small transfers.");
} else if (c == '3') {
#if ENABLE_DEDICATED_SPI
if (!sd.begin(SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(50)))) {
errorHalt("begin failed");
}
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') {
if (!sd.begin(SdSpiConfig(SD_CS_PIN, SHARED_SPI, SD_SCK_MHZ(50)))) {
errorHalt("begin failed");

View file

@ -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();
}

View file

@ -59,8 +59,11 @@ class MySpiClass : public SdSpiBaseClass {
private:
SPISettings m_spiSettings;
} mySpi;
#if ENABLE_DEDICATED_SPI
#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;
//------------------------------------------------------------------------------

View file

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

View file

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

View file

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

View file

@ -91,7 +91,17 @@ struct ExFatPos_t {
class ExFatFile {
public:
/** 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
~ExFatFile() {
@ -108,12 +118,6 @@ class ExFatFile {
operator bool() {
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
* to EOF for normal files. INT_MAX is returned for very large files.
*
@ -126,6 +130,20 @@ class ExFatFile {
uint64_t n = available64();
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
* to be written to the storage device.
*
@ -200,22 +218,6 @@ class ExFatFile {
void fsetpos(const fspos_t* pos);
/** Arduino name for 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.
*
* \param[out] pdate Packed date for directory entry.
@ -244,6 +246,14 @@ class ExFatFile {
* \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.
*
* \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 */
bool getWriteError() const {
return isOpen() ? m_error & WRITE_ERROR : true;
@ -266,12 +276,12 @@ class ExFatFile {
bool isOpen() const {return m_attributes;}
/** \return True if file is 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. */
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;}
/** \return True if this is a subdirectory. */
bool isSubDir() const {return m_attributes & FILE_ATTR_SUBDIR;}
/** \return True file is writable. */
bool isWritable() const {return m_flags & FILE_FLAG_WRITE;}
/** List directory contents.
@ -381,6 +391,16 @@ class ExFatFile {
* \return true for success or false for failure.
*/
bool open(ExFatFile* dirFile, uint32_t index, oflag_t oflag);
/** Open a file in the current working directory.
*
* \param[in] path A path with a valid name for a file to be opened.
*
* \param[in] oflag bitwise-inclusive OR of open flags.
* See see ExFatFile::open(ExFatFile*, const char*, uint8_t).
*
* \return true for success or false for failure.
*/
bool open(const ExChar_t* path, int oflag = O_RDONLY);
/** Open the next file or subdirectory in a directory.
*
* \param[in] dirFile An open instance for the directory
@ -392,16 +412,6 @@ class ExFatFile {
* \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.
*
* \param[in] path A path with a valid name for a file to be opened.
*
* \param[in] oflag bitwise-inclusive OR of open flags.
* See see ExFatFile::open(ExFatFile*, const char*, uint8_t).
*
* \return true for success or false for failure.
*/
bool open(const ExChar_t* path, int oflag = O_RDONLY);
/** Open a volume's root directory.
*
* \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.
*/
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.
* \param[in] value The number to be printed.
* \param[in] term The field terminator. Use '\\n' for CR LF.
@ -488,21 +512,6 @@ class ExFatFile {
* \return The number of bytes printed.
*/
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
*
* \param[in] pr Print stream for output.
@ -780,9 +789,9 @@ class ExFatFile {
ExFatVolume* m_vol;
DirPos_t m_dirPos;
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"

View file

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

View file

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

View file

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

View file

@ -110,8 +110,7 @@ const uint8_t FNAME_FLAG_LC_EXT = FAT_CASE_LC_EXT;
class FatFile {
public:
/** 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.
*
* \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).
*/
FatFile(const char* path, oflag_t oflag) {
m_attributes = FILE_ATTR_CLOSED;
m_error = 0;
open(path, oflag);
}
#if DESTRUCTOR_CLOSES_FILE
/** Destructor */
~FatFile() {
if (isOpen()) {
close();
@ -135,11 +133,25 @@ class FatFile {
*
* \return true if a file is open.
*/
operator bool() {
return isOpen();
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.
*
* available32() is recomended for very large files.
*
* Zero is returned for directory files.
*
*/
int available() const {
uint32_t n = available32();
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.
*/
uint32_t available32() const {
return isFile() ? fileSize() - curPosition() : 0;
}
/** Arduino name for sync() */
void flush() {sync();}
/** Clear all error bits. */
void clearError() {
m_error = 0;
@ -148,8 +160,117 @@ class FatFile {
void clearWriteError() {
m_error &= ~WRITE_ERROR;
}
/** Close a file and force cached data and directory information
* to be written to the storage device.
*
* \return true for success or false for failure.
*/
bool close();
/** Check for contiguous file and return its raw sector range.
*
* \param[out] bgnSector the first sector address for the file.
* \param[out] endSector the last sector address for the file.
*
* Set the contiguous flag if the file is contiguous.
* The parameters may be nullptr to only set the flag.
* \return true for success or false for failure.
*/
bool contiguousRange(uint32_t* bgnSector, uint32_t* endSector);
/** Create and open a new contiguous file of a specified size.
*
* \param[in] dirFile The directory where the file will be created.
* \param[in] path A path with a valid file name.
* \param[in] size The desired file size.
*
* \return true for success or false for failure.
*/
bool createContiguous(FatFile* dirFile,
const char* path, uint32_t size);
/** Create and open a new contiguous file of a specified size.
*
* \param[in] path A path with a valid file name.
* \param[in] size The desired file size.
*
* \return true for success or false for failure.
*/
bool createContiguous(const char* path, uint32_t size);
/** \return The current cluster number for a file or directory. */
uint32_t curCluster() const {return m_curCluster;}
/** \return The current position for a file or directory. */
uint32_t curPosition() const {return m_curPosition;}
/** Return a file's directory entry.
*
* \param[out] dir Location for return of the file's directory entry.
*
* \return true for success or false for failure.
*/
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
* if an error occurs.
*/
uint32_t dirSize();
/** Dump file in Hex
* \param[in] pr Print stream for list.
* \param[in] pos Start position in file.
* \param[in] n number of locations to dump.
*/
void dmpFile(print_t* pr, uint32_t pos, size_t n);
/** Test for the existence of a file in a directory
*
* \param[in] path Path of the file to be tested for.
*
* The calling instance must be an open directory file.
*
* dirFile.exists("TOFIND.TXT") searches for "TOFIND.TXT" in the directory
* dirFile.
*
* \return True if the file exists.
*/
bool exists(const char* path) {
FatFile file;
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.
*
* fgets() reads bytes from a file into the array pointed to by \a str, until
* \a num - 1 bytes are read, or a delimiter is read and transferred to
* \a str, or end-of-file is encountered. The string is then terminated
* with a null byte.
*
* fgets() deletes CR, '\\r', from the string. This insures only a '\\n'
* terminates the string for Windows text files which use CRLF for newline.
*
* \param[out] str Pointer to the array where the string is stored.
* \param[in] num Maximum number of characters to be read
* (including the final null byte). Usually the length
* of the array \a str is used.
* \param[in] delim Optional set of delimiters. The default is "\n".
*
* \return For success fgets() returns the length of the string in \a str.
* If no data is read, fgets() returns zero for EOF or -1 if an error
* occurred.
*/
int fgets(char* str, int num, char* delim = nullptr);
/** \return The total number of bytes in a file. */
uint32_t fileSize() const {return m_fileSize;}
/** \return first sector of file or zero for empty file. */
uint32_t firstBlock() const {return firstSector();}
/** \return Address of first sector or zero for empty file. */
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.
@ -191,139 +312,6 @@ class FatFile {
* \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;
}
/** 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
* to EOF for normal files. Zero is returned for directory files.
*/
uint32_t available32() {
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;
}
/** Close a file and force cached data and directory information
* to be written to the storage device.
*
* \return true for success or false for failure.
*/
bool close();
/** Check for contiguous file and return its raw sector range.
*
* \param[out] bgnSector the first sector address for the file.
* \param[out] endSector the last sector address for the file.
*
* Set the contiguous flag if the file is contiguous.
* The parameters may be nullptr to only set the flag.
* \return true for success or false for failure.
*/
bool contiguousRange(uint32_t* bgnSector, uint32_t* endSector);
/** Create and open a new contiguous file of a specified size.
*
* \param[in] dirFile The directory where the file will be created.
* \param[in] path A path with a valid file name.
* \param[in] size The desired file size.
*
* \return true for success or false for failure.
*/
bool createContiguous(FatFile* dirFile,
const char* path, uint32_t size);
/** Create and open a new contiguous file of a specified size.
*
* \param[in] path A path with a valid file name.
* \param[in] size The desired file size.
*
* \return true for success or false for failure.
*/
bool createContiguous(const char* path, uint32_t size);
/** \return The current cluster number for a file or directory. */
uint32_t curCluster() const {return m_curCluster;}
/** \return The current position for a file or directory. */
uint32_t curPosition() const {return m_curPosition;}
/** Return a file's directory entry.
*
* \param[out] dir Location for return of the file's directory entry.
*
* \return true for success or false for failure.
*/
bool dirEntry(DirFat_t* dir);
/** \return The number of bytes allocated to a directory or zero
* if an error occurs.
*/
uint32_t dirSize();
/** Dump file in Hex
* \param[in] pr Print stream for list.
* \param[in] pos Start position in file.
* \param[in] n number of locations to dump.
*/
void dmpFile(print_t* pr, uint32_t pos, size_t n);
/** Test for the existence of a file in a directory
*
* \param[in] path Path of the file to be tested for.
*
* The calling instance must be an open directory file.
*
* dirFile.exists("TOFIND.TXT") searches for "TOFIND.TXT" in the directory
* dirFile.
*
* \return True if the file exists.
*/
bool exists(const char* path) {
FatFile file;
return file.open(this, path, O_RDONLY);
}
/**
* Get a string from a file.
*
* fgets() reads bytes from a file into the array pointed to by \a str, until
* \a num - 1 bytes are read, or a delimiter is read and transferred to
* \a str, or end-of-file is encountered. The string is then terminated
* with a null byte.
*
* fgets() deletes CR, '\\r', from the string. This insures only a '\\n'
* terminates the string for Windows text files which use CRLF for newline.
*
* \param[out] str Pointer to the array where the string is stored.
* \param[in] num Maximum number of characters to be read
* (including the final null byte). Usually the length
* of the array \a str is used.
* \param[in] delim Optional set of delimiters. The default is "\n".
*
* \return For success fgets() returns the length of the string in \a str.
* If no data is read, fgets() returns zero for EOF or -1 if an error
* occurred.
*/
int fgets(char* str, int num, char* delim = nullptr);
/** \return The total number of bytes in a file. */
uint32_t fileSize() const {return m_fileSize;}
/** \return first sector of file or zero for empty file. */
uint32_t firstBlock() const {return firstSector();}
/** \return Address of first sector or zero for empty file. */
uint32_t firstSector() const;
/**
* 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
* must be at least 13 bytes long. The file's name will be
* 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.
*
@ -341,7 +329,11 @@ class FatFile {
* The array must be at least 13 bytes long.
* \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.
*
@ -362,21 +354,21 @@ class FatFile {
bool isLFN() const {return m_lfnOrd;}
/** \return True if this is an open file/directory. */
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. */
bool isRoot() const {return m_attributes & FILE_ATTR_ROOT;}
/** \return True if this is the FAT32 root directory. */
bool isRoot32() const {return m_attributes & FILE_ATTR_ROOT32;}
/** \return True if this is the FAT12 of FAT16 root directory. */
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. */
bool isSubDir() const {return m_attributes & FILE_ATTR_SUBDIR;}
/** \return True if this is a system file. */
bool isSystem() const {return m_attributes & FILE_ATTR_SYSTEM;}
/** \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;}
/** Check for a legal 8.3 character.
* \param[in] c Character to be checked.
@ -430,7 +422,14 @@ class FatFile {
* \return true for success or false for failure.
*/
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.
*
* \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] 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.
* Only one of O_RDONLY, O_READ, O_WRONLY, O_WRITE, or
* O_RDWR is allowed.
@ -536,7 +535,7 @@ class FatFile {
* \return The byte if no error and not at eof else -1;
*/
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.
*
@ -546,13 +545,29 @@ class FatFile {
* \return true for success or false for failure.
*/
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
*
* \param[in] pr Print stream for output.
*
* \return The number of bytes printed.
*/
size_t printCreateDateTime(print_t* pr);
size_t printCreateDateTime(print_t* pr);
/** %Print a directory date field.
*
* Format is yyyy-mm-dd.
@ -627,22 +642,14 @@ class FatFile {
}
return write(str, &buf[sizeof(buf)] - str);
}
/** Print a file's access date
/** Print a file's size.
*
* \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 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);
}
size_t printFileSize(print_t* pr);
/** Print a file's modify date and time
*
* \param[in] pr Print stream for output.
@ -657,14 +664,7 @@ class FatFile {
* \return true for success or false for failure.
*/
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.
*
* \param[in] pr Print stream for output.
@ -731,10 +731,6 @@ class FatFile {
* \return true for success or false for failure.
*/
bool remove(const char* path);
/** Set the file's current position to zero. */
void rewind() {
seekSet(0);
}
/** Rename a file or subdirectory.
* \note the renamed file will be moved to the current volume working
* directory.
@ -752,6 +748,10 @@ class FatFile {
* \return true for success or false for failure.
*/
bool rename(FatFile* dirFile, const char* newPath);
/** Set the file's current position to zero. */
void rewind() {
seekSet(0);
}
/** Remove a directory file.
*
* 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.
*/
bool seekSet(uint32_t pos);
/** The sync() call causes all modified data and directory fields
* to be written to the storage device.
*
* \return true for success or false for failure.
*/
bool sync();
/** Set a file's timestamps in its directory entry.
*
* \param[in] flags Values for \a flags are constructed by a bitwise-inclusive
@ -862,7 +860,6 @@ class FatFile {
bool truncate(uint32_t length) {
return seekSet(length) && truncate();
}
/** Write a string to a file. Used by the Arduino Print class.
* \param[in] str Pointer to the string.
* Use getWriteError to check for errors.
@ -980,9 +977,9 @@ class FatFile {
static const uint8_t WRITE_ERROR = 0X1;
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;
uint16_t m_dirIndex; // index of directory entry in dir file
FatVolume* m_vol; // volume where file is located

View file

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

View file

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

View file

@ -67,26 +67,26 @@ class FatPartition {
public:
/** Create an instance of FatPartition
*/
FatPartition() : m_fatType(0) {}
FatPartition() {}
/** \return The shift count required to multiply by bytesPerCluster. */
uint8_t bytesPerClusterShift() {
uint8_t bytesPerClusterShift() const {
return m_sectorsPerClusterShift + m_bytesPerSectorShift;
}
/** \return Number of bytes in a cluster. */
uint16_t bytesPerCluster() {
uint16_t bytesPerCluster() const {
return m_bytesPerSector << m_sectorsPerClusterShift;
}
/** \return Number of bytes per sector. */
uint16_t bytesPerSector() {
uint16_t bytesPerSector() const {
return m_bytesPerSector;
}
/** \return The shift count required to multiply by bytesPerCluster. */
uint8_t bytesPerSectorShift() {
uint8_t bytesPerSectorShift() const {
return m_bytesPerSectorShift;
}
/** \return Mask for sector offset. */
uint16_t sectorMask() {
uint16_t sectorMask() const {
return m_sectorMask;
}
/** \return The volume's cluster size in sectors. */
@ -120,7 +120,7 @@ class FatPartition {
return m_dataStartSector;
}
/** \return The number of File Allocation Tables. */
uint8_t fatCount() {
uint8_t fatCount() const {
return 2;
}
/** \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_clusterSectorMask; // Mask to extract sector of cluster.
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.
uint32_t m_allocSearchStart; // Start cluster for alloc search.
uint32_t m_sectorsPerFat; // FAT size in sectors

View file

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

View file

@ -90,7 +90,6 @@ bool FsBaseFile::open(FsVolume* vol, const char* path, oflag_t oflag) {
return true;
}
m_fFile = nullptr;
return false;
} else if (vol->m_xVol) {
m_xFile = new (m_fileMem) ExFatFile;
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;
}
//------------------------------------------------------------------------------
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() {
if (m_fFile) {
if (m_fFile->remove()) {

View file

@ -37,7 +37,18 @@
*/
class FsBaseFile {
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();}
/** Copy constructor.
@ -58,11 +69,17 @@ class FsBaseFile {
/** \return number of bytes available from the current position to EOF
* or INT_MAX if more than INT_MAX bytes are available.
*/
int available() {
int available() const {
return m_fFile ? m_fFile->available() :
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. */
void clearWriteError() {
if (m_fFile) m_fFile->clearWriteError();
@ -186,7 +203,7 @@ class FsBaseFile {
m_xFile ? m_xFile->getCreateDateTime(pdate, ptime) : false;
}
/** \return All error bits. */
uint8_t getError() {
uint8_t getError() const {
return m_fFile ? m_fFile->getError() :
m_xFile ? m_xFile->getError() : 0XFF;
}
@ -248,6 +265,11 @@ class FsBaseFile {
* \return true if the file is a directory.
*/
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. */
bool isHidden() const {
return m_fFile ? m_fFile->isHidden() :
@ -255,11 +277,26 @@ class FsBaseFile {
}
/** \return True if this is an open file/directory else false. */
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. */
bool isSubDir() const {
return m_fFile ? m_fFile->isSubDir() :
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
/** List directory contents.
*
@ -412,6 +449,15 @@ class FsBaseFile {
* \return a file object.
*/
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 byte if no error and not at eof else -1;
@ -420,6 +466,21 @@ class FsBaseFile {
return m_fFile ? m_fFile->peek() :
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
*
* \param[in] pr Print stream for output.
@ -440,55 +501,7 @@ class FsBaseFile {
return m_fFile ? m_fFile->printCreateDateTime(pr) :
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] term The field terminator. Use '\\n' for CR LF.
* \param[in] prec Number of digits after decimal point.
@ -517,6 +530,37 @@ class FsBaseFile {
return m_fFile ? m_fFile->printField(value, term) :
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.
*
* \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) :
false;
}
/** Truncate a file to the current position.
*
* \return true for success or false for failure.
@ -746,8 +789,8 @@ class FsBaseFile {
private:
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

View file

@ -39,7 +39,7 @@ class FsFile;
*/
class FsVolume {
public:
FsVolume() : m_fVol(nullptr), m_xVol(nullptr) {}
FsVolume() {}
~FsVolume() {end();}
@ -49,42 +49,15 @@ class FsVolume {
* \return true for success or false for failure.
*/
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. */
uint32_t bytesPerCluster() const {
return m_fVol ? m_fVol->bytesPerCluster() :
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.
* \return true for success or false for failure.
@ -102,6 +75,18 @@ class FsVolume {
return m_fVol ? m_fVol->chdir(path) :
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 */
void end() {
m_fVol = nullptr;
@ -117,6 +102,11 @@ class FsVolume {
return m_fVol ? m_fVol->exists(path) :
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,
* FAT_TYPE_FAT16, or zero for error.
*/
@ -124,6 +114,20 @@ class FsVolume {
return m_fVol ? m_fVol->fatType() :
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.
*
* \param[in] pr Print object.
@ -227,6 +231,11 @@ class FsVolume {
return m_fVol ? m_fVol->rmdir(path) :
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
/** List directory contents.
* \return true for success or false for failure.
@ -369,8 +378,8 @@ class FsVolume {
FsVolume& operator=(const FsVolume& from);
static FsVolume* m_cwv;
FatVolume* m_fVol;
ExFatVolume* m_xVol;
FatVolume* m_fVol = nullptr;
ExFatVolume* m_xVol = nullptr;
BlockDevice* m_blockDev;
};
#endif // FsVolume_h

358
src/RingBuf.h Normal file
View file

@ -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

View file

@ -71,6 +71,7 @@
SD_CARD_ERROR(READ_START, "Bad readStart argument")\
SD_CARD_ERROR(READ_TIMEOUT, "Read data timeout")\
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_FIFO, "SDIO fifo write timeout")\
SD_CARD_ERROR(WRITE_START, "Bad writeStart argument")\
@ -84,7 +85,6 @@
SD_CARD_ERROR(INVALID_CARD_CONFIG, "Invalid card config")\
SD_CARD_ERROR(FUNCTION_NOT_SUPPORTED, "Unsupported SDIO command")
enum {
#define SD_CARD_ERROR(e, m) SD_CARD_ERROR_##e,
SD_ERROR_CODE_LIST

View file

@ -222,6 +222,7 @@ static uint16_t CRC_CCITT(const uint8_t* data, size_t n) {
//------------------------------------------------------------------------------
bool SdSpiCard::begin(SdSpiConfig spiConfig) {
SdMillis_t t0 = SysCall::curTimeMS();
m_spiActive = false;
m_errorCode = SD_CARD_ERROR_NONE;
m_type = 0;
m_csPin = spiConfig.csPin;
@ -238,16 +239,12 @@ bool SdSpiCard::begin(SdSpiConfig spiConfig) {
spiBegin(spiConfig);
uint32_t arg;
#if ENABLE_DEDICATED_SPI
m_sharedSpi = !(spiConfig.options & DEDICATED_SPI);
m_spiActive = false;
m_curState = IDLE_STATE;
m_sharedSpi = spiOptionShared(spiConfig.options);
#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
spiStart();
// 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
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
if (!m_spiActive) {
spiStart();
@ -430,6 +432,11 @@ bool SdSpiCard::eraseSingleSectorEnable() {
}
//------------------------------------------------------------------------------
bool SdSpiCard::isBusy() {
#if ENABLE_DEDICATED_SPI
if (m_curState == READ_STATE) {
return false;
}
#endif // ENABLE_DEDICATED_SPI
bool rtn = true;
bool spiActive = m_spiActive;
if (!spiActive) {
@ -508,7 +515,6 @@ bool SdSpiCard::readData(uint8_t* dst, size_t count) {
//------------------------------------------------------------------------------
bool SdSpiCard::readOCR(uint32_t* ocr) {
uint8_t* p = reinterpret_cast<uint8_t*>(ocr);
syncDevice();
if (cardCommand(CMD58, 0)) {
error(SD_CARD_ERROR_CMD58);
goto fail;
@ -527,7 +533,6 @@ bool SdSpiCard::readOCR(uint32_t* ocr) {
/** read CID or CSR register */
bool SdSpiCard::readRegister(uint8_t cmd, void* buf) {
uint8_t* dst = reinterpret_cast<uint8_t*>(buf);
syncDevice();
if (cardCommand(cmd, 0)) {
error(SD_CARD_ERROR_READ_REG);
goto fail;
@ -599,33 +604,32 @@ bool SdSpiCard::readStatus(uint8_t* status) {
bool SdSpiCard::readSectors(uint32_t sector, uint8_t* dst, size_t ns) {
#if ENABLE_DEDICATED_SPI
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_curState = READ_STATE;
}
for (size_t i = 0; i < ns; i++, dst += 512) {
if (!readData(dst, 512)) {
return false;
goto fail;
}
}
m_curSector += ns;
return m_sharedSpi ? syncDevice() : true;
#else // ENABLE_DEDICATED_SPI
if (!readStart(sector)) {
return false;
goto fail;
}
for (size_t i = 0; i < ns; i++, dst += 512) {
if (!readData(dst, 512)) {
return false;
goto fail;
}
}
return readStop();
#endif // ENABLE_DEDICATED_SPI
fail:
return false;
}
//------------------------------------------------------------------------------
bool SdSpiCard::readStop() {
@ -665,16 +669,15 @@ void SdSpiCard::spiStop() {
//------------------------------------------------------------------------------
bool SdSpiCard::syncDevice() {
#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;
if (state == WRITE_STATE) {
return writeStop();
}
if (state == READ_STATE) {
return readStop();
}
#endif // ENABLE_DEDICATED_SPI
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) {
#if ENABLE_DEDICATED_SPI
#if ENABLE_DEDICATED_SPI
if (m_curState != WRITE_STATE || m_curSector != sector) {
if (!syncDevice()) {
return false;
}
if (!writeStart(sector)) {
return false;
goto fail;
}
m_curSector = sector;
m_curState = WRITE_STATE;
}
for (size_t i = 0; i < ns; i++, src += 512) {
if (!writeData(src)) {
return false;
goto fail;
}
}
m_curSector += ns;
@ -808,11 +808,11 @@ bool SdSpiCard::writeSectors(uint32_t sector, const uint8_t* src, size_t ns) {
}
}
return writeStop();
#endif // ENABLE_DEDICATED_SPI
fail:
spiStop();
return false;
#endif // ENABLE_DEDICATED_SPI
}
//------------------------------------------------------------------------------
bool SdSpiCard::writeStart(uint32_t sector) {
@ -831,7 +831,7 @@ bool SdSpiCard::writeStart(uint32_t sector) {
return false;
}
//------------------------------------------------------------------------------
bool SdSpiCard::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
bool SdSpiCard::writeStart(uint32_t sector, uint32_t eraseCount) {
// send pre-erase count
if (cardAcmd(ACMD23, eraseCount)) {
error(SD_CARD_ERROR_ACMD23);
@ -839,9 +839,9 @@ bool SdSpiCard::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
}
// use address if not SDHC card
if (type() != SD_CARD_TYPE_SDHC) {
blockNumber <<= 9;
sector <<= 9;
}
if (cardCommand(CMD25, blockNumber)) {
if (cardCommand(CMD25, sector)) {
error(SD_CARD_ERROR_CMD25);
goto fail;
}

View file

@ -47,7 +47,7 @@ class SdSpiCard {
#endif // HAS_SDIO_CLASS
public:
/** Construct an instance of SdSpiCard. */
SdSpiCard() : m_errorCode(SD_CARD_ERROR_INIT_NOT_CALLED), m_type(0) {}
SdSpiCard() {}
/** Initialize the SD card.
* \param[in] spiConfig SPI card configuration.
* \return true for success or false for failure.
@ -214,11 +214,11 @@ class SdSpiCard {
* \return true for success or false for failure.
*/
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.
@ -355,12 +355,14 @@ class SdSpiCard {
static const uint8_t WRITE_STATE = 2;
uint32_t m_curSector;
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
SdCsPin_t m_csPin;
uint8_t m_errorCode;
uint8_t m_errorCode = SD_CARD_ERROR_INIT_NOT_CALLED;
bool m_spiActive;
uint8_t m_status;
uint8_t m_type;
uint8_t m_type = 0;
};
#endif // SdSpiCard_h

View file

@ -35,7 +35,7 @@
*/
class SdioConfig {
public:
SdioConfig() : m_options(FIFO_SDIO) {}
SdioConfig() {}
/**
* SdioConfig constructor.
* \param[in] opt SDIO options.
@ -46,7 +46,7 @@ class SdioConfig {
/** \return true if DMA_SDIO. */
bool useDma() {return m_options & DMA_SDIO;}
private:
uint8_t m_options;
uint8_t m_options = FIFO_SDIO;
};
//------------------------------------------------------------------------------
/**
@ -64,13 +64,7 @@ class SdioCard : public SdCardInterface {
* \return false - not implemented.
*/
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
// Use sectorCount(). cardSize() will be removed in the future.
uint32_t cardSize() __attribute__ ((deprecated)) {return sectorCount();}
@ -180,6 +174,21 @@ class SdioCard : public SdCardInterface {
bool readStop();
/** \return SDIO card 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. */
bool syncDevice();
/** 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;
uint32_t m_curSector;
SdioConfig m_sdioConfig;
uint8_t m_curState;
uint8_t m_curState = IDLE_STATE;
};
#endif // SdioCard_h

View file

@ -28,10 +28,7 @@
#include "SdioCard.h"
//==============================================================================
// 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
const uint32_t FIFO_WML = 16;
@ -201,6 +198,9 @@ static bool (*m_busyFcn)() = 0;
static bool m_initDone = false;
static bool m_version2;
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 uint32_t m_errorLine = 0;
static uint32_t m_rca;
@ -216,20 +216,53 @@ static csd_t m_csd;
#if USE_DEBUG_MODE
#define DBG_IRQSTAT() if (SDHC_IRQSTAT) {Serial.print(__LINE__);\
Serial.print(" IRQSTAT "); Serial.println(SDHC_IRQSTAT, HEX);}
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);
}
#else // USE_DEBUG_MODE
@ -299,7 +332,7 @@ static void gpioMux(uint8_t mode) {
static void enableGPIO(bool enable) {
const uint32_t CLOCK_MASK = IOMUXC_SW_PAD_CTL_PAD_PKE |
#if defined(ARDUINO_TEENSY41)
IOMUXC_SW_PAD_CTL_PAD_DSE(1) |
IOMUXC_SW_PAD_CTL_PAD_DSE(7) |
#else // defined(ARDUINO_TEENSY41)
IOMUXC_SW_PAD_CTL_PAD_DSE(4) | ///// WHG
#endif // defined(ARDUINO_TEENSY41)
@ -402,7 +435,7 @@ static void initSDHC() {
#if defined (__IMXRT1062__)
SDHC_MIX_CTRL |= 0x80000000;
#endif
#endif // (__IMXRT1062__)
// Reset SDHC. Use default Water Mark Level of 16.
SDHC_SYSCTL |= SDHC_SYSCTL_RSTA | SDHC_SYSCTL_SDCLKFS(0x80);
@ -433,11 +466,7 @@ static uint32_t statusCMD13() {
}
//------------------------------------------------------------------------------
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() {
@ -448,6 +477,10 @@ static bool isBusyCommandInhibit() {
return SDHC_PRSSTAT & SDHC_PRSSTAT_CIHB;
}
//------------------------------------------------------------------------------
static bool isBusyDat() {
return SDHC_PRSSTAT & (1 << 24) ? false : true;
}
//------------------------------------------------------------------------------
static bool isBusyDMA() {
return m_dmaBusy;
}
@ -536,20 +569,26 @@ static void setSdclk(uint32_t kHzMax) {
}
//------------------------------------------------------------------------------
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)) {
return sdError(SD_CARD_ERROR_CMD12);
}
if (yieldTimeout(isBusyCMD13)) {
// if (yieldTimeout(isBusyCMD13)) {
if (yieldTimeout(isBusyDat)) {
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;
}
//------------------------------------------------------------------------------
@ -585,6 +624,22 @@ static bool waitTimeout(bool (*fcn)()) {
}
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.
//==============================================================================
@ -675,6 +730,11 @@ bool SdioCard::begin(SdioConfig sdioConfig) {
}
//------------------------------------------------------------------------------
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
if (!m_csd.v1.erase_blk_en) {
// erase size mask
@ -716,7 +776,31 @@ uint32_t SdioCard::errorLine() const {
}
//------------------------------------------------------------------------------
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();
#endif // ENABLE_TEENSY_SDIO_MOD
}
//------------------------------------------------------------------------------
uint32_t SdioCard::kHzSdClk() {
@ -781,6 +865,11 @@ bool SdioCard::readSector(uint32_t sector, uint8_t* dst) {
memcpy(dst, aligned, 512);
}
} else {
#if ENABLE_TEENSY_SDIO_MOD
if (!waitTransferComplete()) {
return false;
}
#endif // ENABLE_TEENSY_SDIO_MOD
if (m_curState != READ_STATE || sector != m_curSector) {
if (!syncDevice()) {
return false;
@ -841,7 +930,7 @@ bool SdioCard::readStart(uint32_t sector) {
SDHC_BLKATTR = SDHC_BLKATTR_BLKSIZE(512);
#else // defined(__IMXRT1062__)
// 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__)
if (!cardCommand(CMD18_PGM_XFERTYP, m_highCapacity ? sector : 512*sector)) {
@ -862,7 +951,30 @@ uint32_t SdioCard::status() {
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() {
#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) {
m_curState = IDLE_STATE;
if (!readStop()) {
@ -874,6 +986,7 @@ bool SdioCard::syncDevice() {
return false;
}
}
#endif // ENABLE_TEENSY_SDIO_MOD
return true;
}
//------------------------------------------------------------------------------
@ -884,6 +997,11 @@ uint8_t SdioCard::type() const {
//------------------------------------------------------------------------------
bool SdioCard::writeData(const uint8_t* src) {
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);
if (!(SDHC_PRSSTAT & SDHC_PRSSTAT_WTA)) {
SDHC_PROCTL &= ~SDHC_PROCTL_SABGREQ;
@ -901,12 +1019,17 @@ bool SdioCard::writeData(const uint8_t* src) {
}
p32 += FIFO_WML;
}
#if ENABLE_TEENSY_SDIO_MOD
m_transferActive = true;
return true;
#else // ENABLE_TEENSY_SDIO_MOD
if (waitTimeout(isBusyTransferComplete)) {
return sdError(SD_CARD_ERROR_WRITE_TIMEOUT);
}
m_irqstat = SDHC_IRQSTAT;
SDHC_IRQSTAT = m_irqstat;
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) {
@ -919,10 +1042,23 @@ bool SdioCard::writeSector(uint32_t sector, const uint8_t* src) {
} else {
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);
}
} 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 (!syncDevice()) {
return false;
@ -937,6 +1073,7 @@ bool SdioCard::writeSector(uint32_t sector, const uint8_t* src) {
return false;
}
m_curSector++;
#if !ENABLE_TEENSY_SDIO_MOD
#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
// End transfer with CMD12 if required.
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 // !ENABLE_TEENSY_SDIO_MOD
}
return true;
}
@ -984,7 +1122,7 @@ bool SdioCard::writeStart(uint32_t sector) {
SDHC_BLKATTR = SDHC_BLKATTR_BLKSIZE(512);
#else // defined(__IMXRT1062__)
// 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__)
if (!cardCommand(CMD25_PGM_XFERTYP, m_highCapacity ? sector : 512*sector)) {
return sdError(SD_CARD_ERROR_CMD25);

View file

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

View file

@ -39,6 +39,9 @@
/** For Debug - must be one */
#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 */
#define USE_BLOCK_DEVICE_INTERFACE 0
//------------------------------------------------------------------------------
@ -82,7 +85,7 @@
#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
* performance by using very large multi-block transfers to and
* from the SD card.
@ -96,7 +99,7 @@
// All other boards.
#define ENABLE_DEDICATED_SPI 1
#endif // defined(__AVR__) && FLASHEND < 0X8000
//-----------------------------------------------------------------------------
//------------------------------------------------------------------------------
/**
* If the symbol SPI_DRIVER_SELECT is:
*
@ -110,6 +113,13 @@
* 3 - An external SPI driver derived from SdSpiBaseClass is always used.
*/
#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
@ -181,7 +191,7 @@ typedef uint8_t SdCsPin_t;
* Some cards will not sleep in low power mode unless CHECK_FLASH_PROGRAMMING
* 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

View file

@ -43,6 +43,24 @@ void sdCsInit(SdCsPin_t pin);
*/
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. */
#define SD_SCK_HZ(maxSpeed) (maxSpeed)
/** 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. */
#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
#include "SPI.h"
/** Port type for Arduino SPI hardware driver. */
@ -106,29 +120,27 @@ class SdSpiConfig {
* \param[in] maxSpeed Maximum SCK frequency.
*/
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.
*
* \param[in] cs Chip select pin.
* \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.
*
* \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. */
const SdCsPin_t csPin;
/** Options */
const uint8_t options;
const uint8_t options = 0;
/** Max SCK frequency */
const uint32_t maxSck;
const uint32_t maxSck = SD_SCK_MHZ(50);
/** SPI port */
SpiPort_t* spiPort;
SpiPort_t* spiPort = nullptr;
};
#if SPI_DRIVER_SELECT < 2
#include "SdSpiArduinoDriver.h"

View file

@ -28,46 +28,54 @@
#define ESP_UNALIGN_OK 1
//------------------------------------------------------------------------------
void SdSpiArduinoDriver::activate() {
SPI.beginTransaction(m_spiSettings);
m_spi->beginTransaction(m_spiSettings);
}
//------------------------------------------------------------------------------
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() {
SPI.endTransaction();
m_spi->endTransaction();
}
//------------------------------------------------------------------------------
uint8_t SdSpiArduinoDriver::receive() {
return SPI.transfer(0XFF);
return m_spi->transfer(0XFF);
}
//------------------------------------------------------------------------------
uint8_t SdSpiArduinoDriver::receive(uint8_t* buf, size_t count) {
#if ESP_UNALIGN_OK
SPI.transferBytes(nullptr, buf, count);
m_spi->transferBytes(nullptr, buf, count);
#else // ESP_UNALIGN_OK
// Adjust to 32-bit alignment.
while ((reinterpret_cast<uintptr_t>(buf) & 0X3) && count) {
*buf++ = SPI.transfer(0xff);
*buf++ = m_spi->transfer(0xff);
count--;
}
// Do multiple of four byte transfers.
size_t n4 = 4*(count/4);
if (n4) {
SPI.transferBytes(nullptr, buf, n4);
m_spi->transferBytes(nullptr, buf, n4);
}
// Transfer up to three remaining bytes.
for (buf += n4, count -= n4; count; count--) {
*buf++ = SPI.transfer(0xff);
*buf++ = m_spi->transfer(0xff);
}
#endif // ESP_UNALIGN_OK
return 0;
}
//------------------------------------------------------------------------------
void SdSpiArduinoDriver::send(uint8_t data) {
SPI.transfer(data);
m_spi->transfer(data);
}
//------------------------------------------------------------------------------
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--;
}
#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))

View file

@ -55,9 +55,14 @@ inline uint8_t SdSpiArduinoDriver::receive() {
}
//------------------------------------------------------------------------------
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++) {
buf[i] = m_spi->transfer(0XFF);
}
#endif // USE_SPI_ARRAY_TRANSFER
return 0;
}
//------------------------------------------------------------------------------
@ -66,6 +71,14 @@ inline void SdSpiArduinoDriver::send(uint8_t data) {
}
//------------------------------------------------------------------------------
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++) {
m_spi->transfer(buf[i]);
}

View file

@ -41,7 +41,7 @@ static void dbgPrint(uint16_t line) {
#define DBG_PRINT_IF(b) if (b) {Serial.print(F(__FILE__));\
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__);\
while (true) {}}
#define DBG_FAIL_MACRO dbgPrint(__LINE__);

View file

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

View file

@ -114,11 +114,7 @@ class StdioStream : private StreamBaseFile {
/** 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. */
void clearerr() {
@ -657,11 +653,11 @@ class StdioStream : private StreamBaseFile {
static const uint8_t S_EOF = 0x10; // found EOF
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_status = 0;
uint8_t* m_p = m_buf;
uint8_t m_r = 0;
uint8_t m_w;
};
//------------------------------------------------------------------------------
#endif // StdioStream_h

View file

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

View file

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