diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..9e2ff03 --- /dev/null +++ b/COPYING @@ -0,0 +1,7 @@ +intellectual property is bullshit. be gay, do crime + +some of this code comes from people who like licenses. if you care about that +kinda shit see the accompanying license files. i'm supposed to mention them +here: + + sd-spi/: Wade Penson, 2015, Apache 2.0 diff --git a/meson.build b/meson.build index bd9f97b..e95d441 100644 --- a/meson.build +++ b/meson.build @@ -1,14 +1,16 @@ -project('poly1', 'cpp', default_options: ['optimization=s', 'cpp_std=c++17']) +project('poly1', ['cpp', 'c'], default_options: ['optimization=s', 'cpp_std=c++17']) atpack_zip = 'Microchip.AVR-Dx_DFP.2.1.152.atpack' dependencies = [] -local_headers = ['.', 'etl/include'] +local_headers = ['.', 'etl/include', 'sd-spi'] sources = [ 'main.cpp', 'board.cpp', 'midix.cpp', + 'sd-spi/sd_spi.c', + 'sd-spi/sd_spi_platform_dependencies.c', ] _includes = local_headers diff --git a/sd-spi/LICENSE b/sd-spi/LICENSE new file mode 100644 index 0000000..8f71f43 --- /dev/null +++ b/sd-spi/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/sd-spi/README b/sd-spi/README new file mode 100644 index 0000000..2b43ab5 --- /dev/null +++ b/sd-spi/README @@ -0,0 +1,2 @@ +From https://github.com/wpenson/sd-spi-communications-library +See LICENSE diff --git a/sd-spi/sd_spi.c b/sd-spi/sd_spi.c new file mode 100644 index 0000000..115e671 --- /dev/null +++ b/sd-spi/sd_spi.c @@ -0,0 +1,1464 @@ +/******************************************************************************/ +/** +@file sd_spi.cpp +@author Wade Penson +@date June, 2015 +@brief SD SPI library implementation. +@copyright Copyright 2015 Wade Penson + +modified in 2022 by alexis + +@license Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. +*/ +/******************************************************************************/ + +#include "sd_spi_platform_dependencies.h" +#include "sd_spi.h" + +static uint8_t sd_spi_dirty_write = 0; + +/* An sd_spi_card_t structure for internal state. */ +static sd_spi_card_t card; + +/** +@brief Clears the buffer and sets the values to 0. + +@param[in] card An sd_spi_card_t which has been initialized. + +@return An error code as defined by one of the SD_ERR_* definitions. +*/ +static void +sd_spi_clear_buffer( + void +); + +/** +@brief Performs the direct write to the card. + +@param block_address The address of the block on the card. +@param[in] data An array of data / an address to the data in + memory. +@param number_of_bytes The size of the data in bytes. +@param byte_offset The byte offset of where to start writing in the + block. + +@return An error code as defined by one of the SD_ERR_* definitions. +*/ +static int8_t +sd_spi_write_out_data( + uint32_t block_address, + void *data, + uint16_t number_of_bytes, + uint16_t byte_offset +); + +/** +@brief Performs the direct read from the card. + +@param block_address The address of the block on the card. +@param[out] data_buffer A location in memory to write the data to. +@param number_of_bytes The number of bytes to read. +@param byte_offset The byte offset of where to start reading in the + block. + +@return An error code as defined by one of the SD_ERR_* definitions. +*/ +static int8_t +sd_spi_read_in_data( + uint32_t block_address, + void *data_buffer, + uint16_t number_of_bytes, + uint16_t byte_offset +); + +/** +@brief Sends a command to the card. + +@param command The command. +@param argument A four byte argument that depends on the command. + +@return The R1 response from the card. +*/ +static uint8_t +sd_spi_send_byte_command( + uint8_t command, + uint32_t argument +); + +/** +@brief Sends an application command to the card. + +@param command The command. +@param argument A four byte argument that depends on the command. + +@return The R1 response from the card. +*/ +static uint8_t +spi_send_byte_app_command( + uint8_t command, + uint32_t argument +); + +/** +@brief Waits for card to complete specific operations. + +@param max_time_to_wait The number of ms when to timeout. + +@return A 1 on timeout and 0 otherwise. +*/ +static int8_t +sd_spi_wait_if_busy( + uint32_t max_time_to_wait +); + +/** +@brief Get the first flag in the R1 register from the card that indicates an + error. + +@param r1_response The byte from the R1 register. + +@return An error code as defined by one of the SD_ERR_* definitions. +*/ +static int8_t +sd_spi_r1_error( + uint8_t r1_response +); + +/** +@brief Get the first flag in the R2 register from the card that indicates an + error. + +@param r2_response The two bytes from the R2 register. + +@return An error code as defined by one of the SD_ERR_* definitions. +*/ +static int8_t +sd_spi_r2_error( + uint16_t r2_response +); + +/** +@brief Asserts the chip select pin for the card. +*/ +static void +sd_spi_select_card( + void +); + +/** +@brief De-asserts the chip select pin for the card. +*/ +static void +sd_spi_unselect_card( + void +); + +int8_t +sd_spi_init( + uint8_t chip_select_pin +) +{ + //sd_spi_dirty_write = 0; + card.spi_speed = 0; + card.card_type = SD_CARD_TYPE_UNKNOWN; + card.chip_select_pin = chip_select_pin; + card.is_chip_select_high = 1; + card.is_read_write_continuous = 0; + card.continuous_block_address = 0; + +#if defined(SD_SPI_BUFFER) + card.buffered_block_address = 0; + card.is_buffer_current = 0; + card.is_buffer_written = 1; +#endif + + sd_spi_pin_mode(chip_select_pin, OUTPUT); + sd_spi_digital_write(chip_select_pin, HIGH); + + sd_spi_begin(); + sd_spi_begin_transaction(250000); + + /* Send at least 74 clock pulses to enter the native operating mode + (80 in this case). */ + uint16_t i; + for (i = 0; i < 10; i++) + { + sd_spi_send_byte(0xFF); + } + + sd_spi_end_transaction(); + + /* Record start time to check for initialization timeout. */ + uint16_t init_start_time = sd_spi_millis(); + + /* Send CMD0 to put card in SPI mode. The card will respond with 0x01. */ + while (sd_spi_send_byte_command(SD_CMD_GO_IDLE_STATE, 0) != SD_IN_IDLE_STATE) + { + if (((uint16_t) sd_spi_millis() - init_start_time) > SD_INIT_TIMEOUT) + { + sd_spi_unselect_card(); + return SD_ERR_INIT_TIMEOUT; + } + } + + /* Check SD card type. First send CMD8 with the argument for the voltage + range of 2.7 - 3.6 and a test pattern AA. If the card doesn't support + this command, then it is of type SD1 or MMC */ + if ((sd_spi_send_byte_command(SD_CMD_SEND_IF_COND, 0x1AA) & SD_ILLEGAL_COMMAND) + == 0) + { + /* Discard first 2 bytes of R7. */ + sd_spi_receive_byte(); + sd_spi_receive_byte(); + + /* Check if voltage range is accepted. */ + if ((sd_spi_receive_byte() & 0x01) == 0) + { + sd_spi_unselect_card(); + return SD_ERR_OUTSIDE_VOLTAGE_RANGE; + } + + /* Check if the test pattern is the same. */ + if (sd_spi_receive_byte() != 0xAA) + { + sd_spi_unselect_card(); + return SD_ERR_SEND_IF_COND_WRONG_TEST_PATTERN; + } + + card.card_type = SD_CARD_TYPE_SD2; + } + + init_start_time = sd_spi_millis(); + + /* Initialize card. */ + if (card.card_type == SD_CARD_TYPE_SD2) + { + while (spi_send_byte_app_command(SD_ACMD_SEND_OP_COND, 0x40000000) != 0) + { + if (((uint16_t) sd_spi_millis() - init_start_time) > SD_INIT_TIMEOUT) + { + sd_spi_unselect_card(); + return SD_ERR_INIT_TIMEOUT; + } + } + + /* Check to see if card is of type SDHC (or SDXC) by reading OCR. */ + if (sd_spi_send_byte_command(SD_CMD_READ_OCR, 0)) + { + sd_spi_unselect_card(); + return SD_ERR_OCR_REGISTER; + } + + if ((sd_spi_receive_byte() & 0x40) != 0) + { + card.card_type = SD_CARD_TYPE_SDHC; + } + + /* Discard rest of OCR. */ + sd_spi_receive_byte(); + sd_spi_receive_byte(); + sd_spi_receive_byte(); + } + else + { + /* Send AMCD41 to try initializing card and if card doesn't support this + command, it is most likely of type MMC or an earlier version of SD. */ + while (spi_send_byte_app_command(SD_ACMD_SEND_OP_COND, 0) != 0) + { + if (((uint16_t) sd_spi_millis() - init_start_time) > 500) + { + /* If sending CMD1 times out, the card is of unknown type. */ + while (sd_spi_send_byte_command(SD_CMD_SEND_OP_COND, 0) != + SD_IN_IDLE_STATE) + { + if (((uint16_t) sd_spi_millis() - init_start_time) > + SD_INIT_TIMEOUT + 500) + { + sd_spi_unselect_card(); + return SD_ERR_UNKNOWN_CARD_TYPE; + } + } + + card.card_type = SD_CARD_TYPE_MMC; + break; + } + } + + if (card.card_type != SD_CARD_TYPE_MMC) + { + card.card_type = SD_CARD_TYPE_SD1; + } + } + + /* Set block size to 512 bytes. */ + if (sd_spi_send_byte_command(SD_CMD_SET_BLOCKLEN, 512)) + { + sd_spi_unselect_card(); + return SD_ERR_SETTING_BLOCK_LENGTH; + } + + for (i = 0; i < 512; i++) + { + card.sd_spi_buffer[i] = 0; + } + + card.spi_speed = 1; + sd_spi_unselect_card(); + + return SD_ERR_OK; +} + +int8_t +sd_spi_write( + uint32_t block_address, + void *data, + uint16_t number_of_bytes, + uint16_t byte_offset +) +{ + /* Check to make sure that data is within page bounds. */ + if (number_of_bytes + byte_offset > 512) + { + return SD_ERR_WRITE_OUTSIDE_OF_BLOCK; + } + +#if defined(SD_SPI_BUFFER) + /* Write a whole block out if it is 512 bytes, otherwise read block into + buffer for partial writing. */ + if (number_of_bytes == 512 && !card.is_read_write_continuous) + { + int8_t response = sd_spi_write_block(block_address, data); + sd_spi_unselect_card(); + + return response; + } + else if (!card.is_read_write_continuous && (card.buffered_block_address != + block_address || !card.is_buffer_current)) + { + int8_t response; + if ((response = sd_spi_flush())) + { + return response; + } + + if (sd_spi_dirty_write) + { + card.buffered_block_address = block_address; + } + else + { + if ((response = sd_spi_read_in_data(block_address, card.sd_spi_buffer, + 512, 0))) + { + return response; + } + } + } + + memcpy(card.sd_spi_buffer + byte_offset, data, number_of_bytes); + card.is_buffer_written = 0; + + return SD_ERR_OK; +#else + int8_t response = sd_spi_write_out_data(block_address, data, + number_of_bytes, byte_offset); + + sd_spi_unselect_card(); + return response; +#endif +} + +int8_t +sd_spi_write_block( + uint32_t block_address, + void *data +) +{ + int8_t response; + +#if defined(SD_SPI_BUFFER) + if ((response = sd_spi_flush())) + { + return response; + } +#endif + + response = sd_spi_write_out_data(block_address, data, 512, 0); + +#if defined(SD_SPI_BUFFER) + if (!card.is_read_write_continuous || + block_address == card.continuous_block_address) // TODO: Does this logic make sense? + { + memcpy(card.sd_spi_buffer, data, 512); + } + + card.is_buffer_written = 1; + card.buffered_block_address = block_address; +#endif + + sd_spi_unselect_card(); + return response; +} + +int8_t +sd_spi_flush( + void +) +{ +#if defined(SD_SPI_BUFFER) + if (card.is_buffer_written) + { + return SD_ERR_OK; + } + + int8_t response; + if ((response = sd_spi_write_out_data(card.buffered_block_address, + card.sd_spi_buffer, 512, 0))) + { + return response; + } + + if (!card.is_read_write_continuous) + { + card.is_buffer_current = 1; + } + + card.is_buffer_written = 1; + sd_spi_unselect_card(); +#endif + + return SD_ERR_OK; +} + +int8_t +sd_spi_write_continuous_start( + uint32_t start_block_address, + uint32_t num_blocks_pre_erase +) +{ +#if defined(SD_SPI_BUFFER) + int8_t response; + if ((response = sd_spi_flush())) + { + return response; + } +#endif + + /* Keep track of block address for error checking and buffering. */ + card.continuous_block_address = start_block_address; + + /* SD cards 2GB or less address by bytes so multiply by 512 to address + by blocks. */ + if (card.card_type != SD_CARD_TYPE_SDHC) + { + start_block_address <<= 9; + } + + /* Optionally pre-erase blocks for faster writing. */ + if (num_blocks_pre_erase != 0) + { + if (spi_send_byte_app_command(SD_ACMD_SET_WR_BLK_ERASE_COUNT, + num_blocks_pre_erase)) + { + sd_spi_unselect_card(); + return SD_ERR_WRITE_PRE_ERASE; + } + } + + /* Start multiple block write. */ + if (sd_spi_send_byte_command(SD_CMD_WRITE_MULTIPLE_BLOCK, start_block_address)) + { + sd_spi_unselect_card(); + return SD_ERR_WRITE_FAILURE; + } + + card.is_read_write_continuous = 1; + +#if defined(SD_SPI_BUFFER) + sd_spi_clear_buffer(); + card.buffered_block_address = card.continuous_block_address; +#endif + + sd_spi_unselect_card(); + return SD_ERR_OK; +} + +int8_t +sd_spi_write_continuous( + void *data, + uint16_t number_of_bytes, + uint16_t byte_offset +) +{ + return sd_spi_write(card.continuous_block_address, data, number_of_bytes, + byte_offset); +} + +int8_t +sd_spi_write_continuous_next( + void +) +{ +#if defined(SD_SPI_BUFFER) + int8_t response = sd_spi_flush(); + sd_spi_clear_buffer(); + + return response; +#else + return SD_ERR_OK; +#endif +} + +int8_t +sd_spi_write_continuous_stop( + void +) +{ +#if defined(SD_SPI_BUFFER) + /* Flush buffer if it hasn't been written. */ + int8_t response; + if ((response = sd_spi_flush())) + { + return response; + } +#endif + + sd_spi_select_card(); + + /* Wait for card to complete the write. */ + if (sd_spi_wait_if_busy(SD_WRITE_TIMEOUT)) + { + sd_spi_unselect_card(); + return SD_ERR_WRITE_TIMEOUT; + } + + /* Token is sent to signal card to stop multiple block writing. */ + sd_spi_send_byte(SD_TOKEN_MULTIPLE_WRITE_STOP_TRANSFER); + + card.is_read_write_continuous = 0; + + /* Wait for card to complete the write. */ + if (sd_spi_wait_if_busy(SD_WRITE_TIMEOUT)) + { + sd_spi_unselect_card(); + return SD_ERR_WRITE_TIMEOUT; + } + + return sd_spi_card_status(); +} + +int8_t +sd_spi_read( + uint32_t block_address, + void *data_buffer, + uint16_t number_of_bytes, + uint16_t byte_offset +) +{ +#if defined(SD_SPI_BUFFER) + int8_t response; + + if ((response = sd_spi_flush())) + { + return response; + } + + /* Check to make sure that data is within page bounds. */ + if (number_of_bytes + byte_offset > 512) + { + return SD_ERR_READ_OUTSIDE_OF_BLOCK; + } + + if (card.buffered_block_address != block_address || + !card.is_buffer_current) + { + /* Read block into buffer. */ + if ((response = sd_spi_read_in_data(block_address, card.sd_spi_buffer, + 512, 0))) + { + return response; + } + } + + memcpy(data_buffer, card.sd_spi_buffer + byte_offset, number_of_bytes); + + return SD_ERR_OK; +#else + return sd_spi_read_in_data(block_address, data_buffer, number_of_bytes, + byte_offset); +#endif +} + +int8_t +sd_spi_read_continuous_start( + uint32_t start_block_address +) +{ +#if defined(SD_SPI_BUFFER) + int8_t response; + + if ((response = sd_spi_flush())) + { + return response; + } +#endif + + card.continuous_block_address = start_block_address; + + /* SD cards 2GB or less address by bytes so multiply by 512 to address + by blocks. */ + if (card.card_type != SD_CARD_TYPE_SDHC) + { + start_block_address <<= 9; + } + + /* Start multiple block reading. */ + if (sd_spi_send_byte_command(SD_CMD_READ_MULTIPLE_BLOCK, start_block_address)) + { + sd_spi_unselect_card(); + return SD_ERR_READ_FAILURE; + } + + card.is_read_write_continuous = 1; + +#if defined(SD_SPI_BUFFER) + if ((response = sd_spi_read_in_data(card.continuous_block_address, + card.sd_spi_buffer, 512, 0))) + { + sd_spi_unselect_card(); + return response; + } + + card.continuous_block_address--; + card.buffered_block_address = card.continuous_block_address; +#endif + + sd_spi_unselect_card(); + return SD_ERR_OK; +} + +int8_t +sd_spi_read_continuous( + void *data_buffer, + uint16_t number_of_bytes, + uint16_t byte_offset +) +{ +#if defined(SD_SPI_BUFFER) + return sd_spi_read(card.continuous_block_address, data_buffer, + number_of_bytes, byte_offset); +#else + return sd_spi_read_in_data(card.continuous_block_address, data_buffer, + number_of_bytes, byte_offset); +#endif +} + +int8_t +sd_spi_read_continuous_next( + void +) +{ +#if defined(SD_SPI_BUFFER) + return sd_spi_read_in_data(card.continuous_block_address, + card.sd_spi_buffer, 512, 0); +#else + return SD_ERR_OK; +#endif +} + +int8_t +sd_spi_read_continuous_stop( + void +) +{ + sd_spi_select_card(); + uint16_t timeout_start = sd_spi_millis(); + + /* Since the stop command is not sent during a read, it must be sent when a + read token is received. */ + while (sd_spi_receive_byte() != SD_TOKEN_START_BLOCK) + { + if (((uint16_t) sd_spi_millis() - timeout_start) > SD_READ_TIMEOUT) + { + return sd_spi_card_status(); + } + } + + timeout_start = sd_spi_millis(); + + /* Send command to stop continuous reading. */ + if ((sd_spi_send_byte_command(SD_CMD_STOP_TRANSMISSION, 0) & 0x08) != 0) + { + while ((sd_spi_receive_byte() & 0x08) != 0) + { + if (((uint16_t) sd_spi_millis() - timeout_start) > SD_READ_TIMEOUT) + { + sd_spi_unselect_card(); + + return SD_ERR_READ_FAILURE; + } + } + } + + card.is_read_write_continuous = 0; + sd_spi_unselect_card(); + + return SD_ERR_OK; +} + +int8_t +sd_spi_erase_all( + void +) +{ + return sd_spi_erase_blocks(0, sd_spi_card_size()); +} + +int8_t +sd_spi_erase_blocks( + uint32_t start_block_address, + uint32_t end_block_address +) +{ +#if defined(SD_SPI_BUFFER) + int8_t response; + if ((response = sd_spi_flush())) + { + return response; + } + + if (card.buffered_block_address > start_block_address && + card.buffered_block_address < end_block_address) + { + sd_spi_clear_buffer(); + card.is_buffer_written = 1; + card.is_buffer_current = 1; + } +#endif + + /* SD cards 2GB or less address by bytes so multiply by 512 to address + by blocks. */ + if (card.card_type != SD_CARD_TYPE_SDHC) + { + start_block_address <<= 9; + end_block_address <<= 9; + } + + /* The start and end address of the blocks to be erased must be sent to the + SD and then the erase command is called. */ + if (sd_spi_send_byte_command(SD_CMD_ERASE_WR_BLK_START, start_block_address) || + sd_spi_send_byte_command(SD_CMD_ERASE_WR_BLK_END, end_block_address) || + sd_spi_send_byte_command(SD_CMD_ERASE, 0)) + { + sd_spi_unselect_card(); + return SD_ERR_ERASE_FAILURE; + } + + if (sd_spi_wait_if_busy(SD_ERASE_TIMEOUT)) + { + sd_spi_unselect_card(); + return SD_ERR_ERASE_TIMEOUT; + } + + sd_spi_unselect_card(); + return SD_ERR_OK; +} + +uint32_t +sd_spi_card_size( + void +) +{ + if (sd_spi_send_byte_command(SD_CMD_SEND_CSD, 0)) + { + sd_spi_unselect_card(); + return SD_ERR_READ_REGISTER; + } + + uint16_t timeout_start = sd_spi_millis(); + + while (sd_spi_receive_byte() != SD_TOKEN_START_BLOCK) + { + if (((uint16_t) sd_spi_millis() - timeout_start) > SD_READ_TIMEOUT) + { + return sd_spi_card_status(); + } + } + + uint8_t c_size_high; + uint8_t c_size_mid; + uint8_t c_size_low; + uint32_t number_of_blocks; + + uint8_t b = sd_spi_receive_byte(); + uint8_t csd_structure = b >> 6; + + uint8_t current_byte; + + for (current_byte = 0; current_byte < 5; current_byte++) + { + sd_spi_receive_byte(); + } + + b = sd_spi_receive_byte(); + uint8_t max_read_bl_len = b & 0x0F; + + if (csd_structure == 0) + { + c_size_high = (b << 2) & 0x0C; + b = sd_spi_receive_byte(); + c_size_high |= b >> 6; + c_size_low = b << 2; + b = sd_spi_receive_byte(); + c_size_low |= b >> 6; + sd_spi_receive_byte(); + + uint8_t c_size_mult; + c_size_mult = (b << 1) & 0x06; + b = sd_spi_receive_byte(); + c_size_mult |= b >> 7; + + number_of_blocks = (uint32_t) ((c_size_high << 8 | c_size_low) + 1) * + (1 << (c_size_mult + 2)); + number_of_blocks *= (uint32_t) (1 << max_read_bl_len) / 512; + } + else + { + b = sd_spi_receive_byte(); + c_size_high = b; + b = sd_spi_receive_byte(); + c_size_mid = b; + b = sd_spi_receive_byte(); + c_size_low = b; + + number_of_blocks = (uint32_t) ((uint32_t) c_size_high << 16 | c_size_mid << 8 | + c_size_low) + 1; + number_of_blocks <<= 10; + } + + for (current_byte = 11; current_byte < 16; current_byte++) + { + sd_spi_receive_byte(); + } + + /* Discard CRC. */ + sd_spi_receive_byte(); + sd_spi_receive_byte(); + + sd_spi_unselect_card(); + return number_of_blocks; +} + +int8_t +sd_spi_read_cid_register( + sd_spi_cid_t *cid +) +{ + if (sd_spi_send_byte_command(SD_CMD_SEND_CID, 0)) + { + sd_spi_unselect_card(); + return SD_ERR_READ_REGISTER; + } + + uint16_t timeout_start = sd_spi_millis(); + + while (sd_spi_receive_byte() != SD_TOKEN_START_BLOCK) + { + if (((uint16_t) sd_spi_millis() - timeout_start) > SD_READ_TIMEOUT) + { + return sd_spi_card_status(); + } + } + + /* See SD Specifications for more information. */ + cid->mid = sd_spi_receive_byte(); + + uint8_t i; + for (i = 0; i < 2; i++) + { + cid->oid[i] = sd_spi_receive_byte(); + } + + for (i = 0; i < 5; i++) + { + cid->pnm[i] = sd_spi_receive_byte(); + } + + i = sd_spi_receive_byte(); + cid->prv_n = i >> 4; + cid->prv_m = i; + + cid->psn_high = sd_spi_receive_byte(); + cid->psn_mid_high = sd_spi_receive_byte(); + cid->psn_mid_low = sd_spi_receive_byte(); + cid->psn_low = sd_spi_receive_byte(); + + i = sd_spi_receive_byte(); + cid->mdt_year = (i << 4) & 0xF0; + i = sd_spi_receive_byte(); + cid->mdt_year |= i >> 4; + cid->mdt_month = i; + cid->crc = sd_spi_receive_byte() >> 1; + + /* Discard CRC */ + sd_spi_receive_byte(); + sd_spi_receive_byte(); + + sd_spi_unselect_card(); + return SD_ERR_OK; +} + +int8_t +sd_spi_read_csd_register( + sd_spi_csd_t *csd +) +{ + if (sd_spi_send_byte_command(SD_CMD_SEND_CSD, 0)) + { + sd_spi_unselect_card(); + return SD_ERR_READ_REGISTER; + } + + uint16_t timeout_start = sd_spi_millis(); + + while (sd_spi_receive_byte() != SD_TOKEN_START_BLOCK) + { + if (((uint16_t) sd_spi_millis() - timeout_start) > SD_READ_TIMEOUT) + { + return sd_spi_card_status(); + } + } + + /* See SD Specification for more information. */ + uint8_t b = sd_spi_receive_byte(); + csd->csd_structure = b >> 6; + b = sd_spi_receive_byte(); + csd->taac = b; + b = sd_spi_receive_byte(); + csd->nsac = b; + b = sd_spi_receive_byte(); + csd->tran_speed = b; + b = sd_spi_receive_byte(); + csd->ccc_high = b >> 4; + csd->ccc_low |= b << 4; + b = sd_spi_receive_byte(); + csd->ccc_low |= b >> 4; + csd->max_read_bl_len = b; + b = sd_spi_receive_byte(); + csd->read_bl_partial = b >> 7; + csd->write_bl_misalign = b >> 6; + csd->read_bl_misalign = b >> 5; + csd->dsr_imp = b >> 4; + + if (csd->csd_structure == 0) + { + csd->cvsi.v1.c_size_high = (b << 2) & 0x0C; + b = sd_spi_receive_byte(); + csd->cvsi.v1.c_size_high |= b >> 6; + csd->cvsi.v1.c_size_low = b << 2; + b = sd_spi_receive_byte(); + csd->cvsi.v1.c_size_low |= b >> 6; + csd->cvsi.v1.vdd_r_curr_min = b >> 3; + csd->cvsi.v1.vdd_r_curr_max = b; + b = sd_spi_receive_byte(); + csd->cvsi.v1.vdd_w_curr_min = b >> 5; + csd->cvsi.v1.vdd_w_curr_max = b >> 2; + csd->cvsi.v1.c_size_mult = (b << 1) & 0x06; + b = sd_spi_receive_byte(); + csd->cvsi.v1.c_size_mult |= b >> 7; + } + else + { + b = sd_spi_receive_byte(); + csd->cvsi.v2.c_size_high = b; + b = sd_spi_receive_byte(); + csd->cvsi.v2.c_size_mid = b; + b = sd_spi_receive_byte(); + csd->cvsi.v2.c_size_low = b; + b = sd_spi_receive_byte(); + } + + csd->erase_bl_en = b >> 6; + csd->erase_sector_size = (b << 1) & 0x7E; + b = sd_spi_receive_byte(); + csd->erase_sector_size |= b >> 7; + csd->wp_grp_size = b << 1; + b = sd_spi_receive_byte(); + csd->wp_grp_enable = b >> 7; + csd->r2w_factor = b >> 2; + csd->write_bl_len = (b << 2) & 0x0C; + b = sd_spi_receive_byte(); + csd->write_bl_len |= b >> 6; + csd->write_bl_partial = b >> 5; + b = sd_spi_receive_byte(); + csd->file_format_grp = b >> 7; + csd->copy = b >> 6; + csd->perm_write_protect = b >> 5; + csd->tmp_write_protect = b >> 4; + csd->file_format = b >> 2; + b = sd_spi_receive_byte(); + csd->crc = b >> 1; + + /* Discard CRC. */ + sd_spi_receive_byte(); + sd_spi_receive_byte(); + + sd_spi_unselect_card(); + return SD_ERR_OK; +} + +int8_t +sd_spi_card_status( + void +) +{ + int8_t response = sd_spi_r2_error((sd_spi_send_byte_command(SD_CMD_SEND_STATUS, 0) + << 8) | sd_spi_receive_byte()); + + sd_spi_unselect_card(); + return response; +} + +uint8_t +sd_spi_card_type( + void +) +{ + return card.card_type; +} + +uint32_t +sd_spi_current_buffered_block( + void +) +{ + return card.buffered_block_address; +} + +static void +sd_spi_clear_buffer( + void +) +{ +#if defined(SD_SPI_BUFFER) + uint16_t i; + for (i = 0; i < 512; i++) { + card.sd_spi_buffer[i] = 0; + } + + card.is_buffer_written = 0; + card.is_buffer_current = 0; +#endif +} + +static int8_t +sd_spi_write_out_data( + uint32_t block_address, + void *data, + uint16_t number_of_bytes, + uint16_t byte_offset +) +{ + sd_spi_select_card(); + + if (card.is_read_write_continuous) + { + /* Wait for card to complete the previous write. */ + if (sd_spi_wait_if_busy(SD_WRITE_TIMEOUT)) + { + sd_spi_unselect_card(); + return SD_ERR_WRITE_TIMEOUT; + } + + /* Send token for multiple block write. */ + sd_spi_send_byte(SD_TOKEN_MULTIPLE_WRITE_START_BLOCK); + } + else + { + /* SD cards 2GB or less address by bytes so multiply by 512 to address + by blocks. */ + if (card.card_type != SD_CARD_TYPE_SDHC) + { + block_address <<= 9; + } + + /* Send the command to start writing a single block. */ + if (sd_spi_send_byte_command(SD_CMD_SET_WRITE_BLOCK, block_address)) + { + sd_spi_unselect_card(); + return SD_ERR_WRITE_FAILURE; + } + + /* Send token for single block write. */ + sd_spi_send_byte(SD_TOKEN_START_BLOCK); + } + + uint16_t i; + + /* Pad data with 0. */ + for (i = 0; i < byte_offset; i++) + { + sd_spi_send_byte(0); + } + + /* Write block. */ + for (i = byte_offset; i < byte_offset + number_of_bytes; i++) + { + sd_spi_send_byte(((uint8_t *) data)[i - byte_offset]); + } + + /* Pad data with 0. */ + for (i = byte_offset + number_of_bytes; i < 512; i++) + { + sd_spi_send_byte(0); + } + + /* Send dummy CRC. */ + sd_spi_send_byte(0xFF); + sd_spi_send_byte(0xFF); + + /* Check if write was successful. */ + switch (sd_spi_receive_byte() & 0x0F) + { + case SD_TOKEN_DATA_REJECTED_CRC: + return SD_ERR_WRITE_DATA_CRC_REJECTED; + case SD_TOKEN_DATA_REJECTED_WRITE_ERR: + return SD_ERR_WRITE_DATA_REJECTED; + case SD_TOKEN_DATA_ACCEPTED: + break; + default: + return SD_ERR_WRITE_FAILURE; + } + + if (card.is_read_write_continuous) + { + card.continuous_block_address++; + +#if defined(SD_SPI_BUFFER) + card.buffered_block_address = card.continuous_block_address; +#endif + } + else { + /* Wait for card to complete the write. */ + if (sd_spi_wait_if_busy(SD_WRITE_TIMEOUT)) + { + sd_spi_unselect_card(); + return SD_ERR_WRITE_TIMEOUT; + } + + return sd_spi_card_status(); + } + + return SD_ERR_OK; +} + +static int8_t +sd_spi_read_in_data( + uint32_t block_address, + void *data_buffer, + uint16_t number_of_bytes, + uint16_t byte_offset +) +{ + sd_spi_select_card(); + + if (!card.is_read_write_continuous) + { + /* SD cards 2GB or less address by bytes so multiply by 512 to address + by blocks. */ + if (card.card_type != SD_CARD_TYPE_SDHC) + { + block_address <<= 9; + } + + /* Start single block reading. */ + if (sd_spi_send_byte_command(SD_CMD_READ_SINGLE_BLOCK, block_address)) + { + sd_spi_unselect_card(); + return SD_ERR_READ_FAILURE; + } + } + + uint16_t timeout_start = sd_spi_millis(); + + /* Must wait for read token from card before reading. */ + while (sd_spi_receive_byte() != SD_TOKEN_START_BLOCK) + { + if (((uint16_t) sd_spi_millis() - timeout_start) > SD_READ_TIMEOUT) + { + return sd_spi_card_status(); + } + } + + uint16_t i; + +#if defined(SD_SPI_BUFFER) /* Read block into sd_spi_buffer if it is defined. */ + /* Read in the bytes to the buffer. */ + for (i = 0; i < 512; i++) + { + card.sd_spi_buffer[i] = sd_spi_receive_byte(); + } + + /* Throw out CRC. */ + sd_spi_receive_byte(); + sd_spi_receive_byte(); + + if (card.is_read_write_continuous) + { + card.continuous_block_address++; + card.buffered_block_address = card.continuous_block_address; + } + else + { + if (card.card_type != SD_CARD_TYPE_SDHC) + { + block_address >>= 9; + } + + card.buffered_block_address = block_address; + } + + card.is_buffer_current = 1; +#else + /* Throw out the bytes until the offset is reached. */ + for (i = 0; i < byte_offset; i++) + { + sd_spi_receive_byte(); + } + + /* Read in the bytes to the buffer. */ + for (i = 0; i < number_of_bytes; i++) + { + ((uint8_t *) data_buffer)[i] = sd_spi_receive_byte(); + } + + /* Throw out any remaining bytes in the page plus CRC. */ + for (i = 0; number_of_bytes + byte_offset + i < 514; i++) + { + sd_spi_receive_byte(); + } +#endif + + return SD_ERR_OK; +} + +static uint8_t +sd_spi_send_byte_command( + uint8_t command, + uint32_t argument +) +{ + sd_spi_select_card(); + sd_spi_receive_byte(); + + /* Send command with transmission bit. */ + sd_spi_send_byte(0x40 | command); + + /* Send argument. */ + sd_spi_send_byte(argument >> 24); + sd_spi_send_byte(argument >> 16); + sd_spi_send_byte(argument >> 8); + sd_spi_send_byte(argument >> 0); + + /* Send CRC. */ + switch(command) + { + /* CRC for CMD0 with arg 0. */ + case SD_CMD_GO_IDLE_STATE: + sd_spi_send_byte(0x95); + break; + /* CRC for CMD8 with arg 0X1AA. */ + case SD_CMD_SEND_IF_COND: + sd_spi_send_byte(0x87); + break; + default: + sd_spi_send_byte(0xFF); + break; + } + + /* Wait for response. Can take up to 64 clock cycles. */ + uint8_t i; + uint8_t response; + for (i = 0; i < 8; i++) + { + response = sd_spi_receive_byte(); + + if (response != 0xFF) + { + break; + } + } + + return response; +} + +static uint8_t +spi_send_byte_app_command( + uint8_t command, + uint32_t argument +) +{ + sd_spi_send_byte_command(SD_CMD_APP, 0); + return sd_spi_send_byte_command(command, argument); +} + +static int8_t +sd_spi_wait_if_busy( + uint32_t max_time_to_wait +) +{ + uint32_t timeout_start = sd_spi_millis(); + + while (sd_spi_receive_byte() != 0xFF) + { + if (((uint32_t) sd_spi_millis() - timeout_start) > max_time_to_wait) + { + return 1; + } + } + + return 0; +} + +static int8_t +sd_spi_r1_error( + uint8_t r1_response +) +{ + if (r1_response == 0x00) + { + return SD_ERR_OK; + } + else if (r1_response & SD_IN_IDLE_STATE) + { + return SD_ERR_NOT_INITIALIZED; + } + else if (r1_response & SD_ERASE_RESET) + { + return SD_ERR_ERASE_RESET; + } + else if (r1_response & SD_ILLEGAL_COMMAND) + { + return SD_ERR_ILLEGAL_COMMAND; + } + else if (r1_response & SD_COMMUNICATION_CRC_ERR) + { + return SD_ERR_COMMUNICATION_CRC; + } + else if (r1_response & SD_ERASE_SEQUENCE_ERR) + { + return SD_ERR_ERASE_SEQUENCE; + } + else if (r1_response & SD_ADDRESS_ERR) + { + return SD_ERR_ILLEGAL_ADDRESS; + } + else if (r1_response & SD_PARAMETER_ERR) + { + return SD_ERR_ILLEGAL_PARAMETER; + } + + return SD_ERR_OK; +} + +static int8_t +sd_spi_r2_error( + uint16_t r2_response +) +{ + if (sd_spi_r1_error(r2_response >> 8)) + { + return sd_spi_r1_error(r2_response >> 8); + } + else + { + if (r2_response == 0x00) + { + return SD_ERR_OK; + } + else if (r2_response & SD_CARD_IS_LOCKED) + { + return SD_ERR_CARD_IS_LOCKED; + } + else if (r2_response & SD_WP_ERASE_SKIP) + { + return SD_ERR_WRITE_PROTECTION_ERASE_SKIP; + } + else if (r2_response & SD_GENERAL_ERR) + { + return SD_ERR_GENERAL; + } + else if (r2_response & SD_CC_ERR) + { + return SD_ERR_CARD_CONTROLLER; + } + else if (r2_response & SD_CARD_ECC_FAILURE) + { + return SD_ERR_CARD_ECC_FAILURE; + } + else if (r2_response & SD_WP_VIOLATION) + { + return SD_ERR_WRITE_PROTECTION_VIOLATION; + } + else if (r2_response & SD_ERASE_PARAM) + { + return SD_ERR_ERASE_PARAMETER; + } + else if (r2_response & SD_OUT_OF_RANGE) + { + return SD_ERR_ARGUMENT_OUT_OF_RANGE; + } + } + + return SD_ERR_OK; +} + +static void +sd_spi_select_card( + void +) +{ + sd_spi_digital_write(card.chip_select_pin, LOW); + + if (card.is_chip_select_high) + { + card.is_chip_select_high = 0; + + if (card.spi_speed == 0) + { + sd_spi_begin_transaction(250000); + + } + else + { + sd_spi_begin_transaction(25000000); + } + } +} + +static void +sd_spi_unselect_card( + void +) +{ + sd_spi_receive_byte(); + + /* Host has to wait 8 clock cycles after a command. */ + sd_spi_digital_write(card.chip_select_pin, HIGH); + + if (!card.is_chip_select_high) + { + card.is_chip_select_high = 1; + sd_spi_end_transaction(); + } +} diff --git a/sd-spi/sd_spi.h b/sd-spi/sd_spi.h new file mode 100644 index 0000000..2a758e4 --- /dev/null +++ b/sd-spi/sd_spi.h @@ -0,0 +1,530 @@ +/******************************************************************************/ +/** +@file sd_spi.h +@author Wade Penson +@date June, 2015 +@brief SD RAW library header. +@details This library supports MMC, SD1, SD2, and SDHC/SDXC type cards. + By defining SD_BUFFER, the library also implements a 512 byte buffer + for reading and writing. + +@copyright Copyright 2015 Wade Penson + +@license Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +@todo Support for AVR and SAMX AVR without using the Arduino SPI library. +@todo Support for using Software SPI (bit banging). +@todo Use CMD6 during initialization to switch card to high speed mode if + it supports it. This will be helpful for the due since the SPI + speed can be up to 84MHz. +@todo Allow setting CS high when card is in busy state for operations + instead of waiting. However, if an error is thrown for the + operation, the host will get that error on the next command it + sends. +@todo Send_status should be sent after all busy signals + (look at ch 4.3.7). +@todo Send stop_transmission if there was an error during + write continuous (and read continuous??). +@todo Get the number of well written blocks for sequential writing just in + case if there was an error. +@todo Card reads take up to 100ms and writes take up to 500ms. Make + timeouts reflect this. +@todo Reduce the number of error codes. +*/ +/******************************************************************************/ + +#if !defined(SD_SPI_H_) +#define SD_SPI_H_ + +#if defined(__cplusplus) +extern "C" { +#endif + +#include +#include +#include "sd_spi_info.h" +#include "sd_spi_commands.h" + +/** Define to enable the block buffer. */ +#define SD_SPI_BUFFER + +/** State variables used by the SD raw library. */ +typedef struct sd_spi_card { + /** Digital pin for setting CS high or low. */ + uint8_t chip_select_pin: 8; + /** The speed of the SPI bus. Use 0 for 25KHz and 1 for 25MHz. */ + uint8_t spi_speed: 3; + /** Determines if the card is MMC, SD1, SD2, or SDHC/SDXC. */ + uint8_t card_type: 3; + /** Flag to see if CS has already been set to high or low. */ + uint8_t is_chip_select_high: 1; + /** True if currently reading continually and false otherwise. */ + uint8_t is_read_write_continuous: 1; + /** True if currently writing continually and false otherwise. */ + uint8_t is_write_continuous: 1; + /** If the card is being read or written to continually, this keeps track + of the block being read or written to. */ + uint32_t continuous_block_address; + +#if defined(SD_SPI_BUFFER) + /** Buffer for SD blocks. */ + uint8_t sd_spi_buffer[512]; + /** The address of the block currently being buffered. */ + uint32_t buffered_block_address; + /** Keeps track of the consistency of the buffer for reading. */ + uint8_t is_buffer_current: 1; + /** Keeps track if the buffer has been flushed or not. */ + uint8_t is_buffer_written: 1; +#endif +} sd_spi_card_t; + +/** +@defgroup sd_spi_timeouts SD Timeouts +@brief Timeouts are used so that the SD does not freeze on + a task if a failure occurs. +@{ +*/ +#define SD_INIT_TIMEOUT 5000 +#define SD_WRITE_TIMEOUT 5000 +#define SD_READ_TIMEOUT 5000 +#define SD_ERASE_TIMEOUT 500000 + +/** @} End of group sd_spi_timeouts */ + +/** +@defgroup sd_spi_card_types SD/MMC Card Types +@brief Types of cards. +@{ +*/ +#define SD_CARD_TYPE_UNKNOWN 0 +#define SD_CARD_TYPE_SD1 1 +#define SD_CARD_TYPE_SD2 2 +#define SD_CARD_TYPE_SDHC 3 +#define SD_CARD_TYPE_MMC 4 + +/** @} End of group sd_spi_card_types */ + +/** +@defgroup sd_spi_error_codes SD Error Codes +@brief Error codes for the SD library. +@{ +*/ +#define SD_ERR_OK 0 + +#define SD_ERR_ALREADY_INITIALIZED 1 +#define SD_ERR_NOT_INITIALIZED 2 +#define SD_ERR_UNKNOWN_CARD_TYPE 3 +#define SD_ERR_INIT_TIMEOUT 4 +#define SD_ERR_COMMAND_TIMEOUT 5 +#define SD_ERR_OUTSIDE_VOLTAGE_RANGE 6 +#define SD_ERR_SEND_IF_COND_WRONG_TEST_PATTERN 7 +#define SD_ERR_OCR_REGISTER 8 +#define SD_ERR_SETTING_BLOCK_LENGTH 9 + +#define SD_ERR_ILLEGAL_COMMAND 10 +#define SD_ERR_COMMUNICATION_CRC 11 +#define SD_ERR_ILLEGAL_ADDRESS 12 +#define SD_ERR_ILLEGAL_PARAMETER 13 +#define SD_ERR_CARD_IS_LOCKED 14 +#define SD_ERR_CARD_CONTROLLER 15 +#define SD_ERR_CARD_ECC_FAILURE 16 +#define SD_ERR_ARGUMENT_OUT_OF_RANGE 17 +#define SD_ERR_GENERAL 18 + +#define SD_ERR_WRITE_FAILURE 19 +#define SD_ERR_WRITE_TIMEOUT 20 +#define SD_ERR_WRITE_OUTSIDE_OF_BLOCK 21 +#define SD_ERR_WRITE_DATA_REJECTED 22 +#define SD_ERR_WRITE_DATA_CRC_REJECTED 23 +#define SD_ERR_WRITE_PRE_ERASE 24 +#define SD_ERR_WRITE_PROTECTION_VIOLATION 25 + +#define SD_ERR_READ_FAILURE 26 +#define SD_ERR_READ_TIMEOUT 27 +#define SD_ERR_READ_OUTSIDE_OF_BLOCK 28 + +#define SD_ERR_READ_WRITE_CONTINUOUS 29 + +#define SD_ERR_ERASE_FAILURE 30 +#define SD_ERR_ERASE_TIMEOUT 31 +#define SD_ERR_ERASE_RESET 32 +#define SD_ERR_WRITE_PROTECTION_ERASE_SKIP 33 +#define SD_ERR_ERASE_PARAMETER 34 +#define SD_ERR_ERASE_SEQUENCE 35 + +#define SD_ERR_READ_REGISTER 36 + +/** @} End of group sd_spi_error_codes */ +/* R1 token responses */ +#define SD_IN_IDLE_STATE 0x01 +#define SD_ERASE_RESET 0x02 +#define SD_ILLEGAL_COMMAND 0x04 +#define SD_COMMUNICATION_CRC_ERR 0x08 +#define SD_ERASE_SEQUENCE_ERR 0x10 +#define SD_ADDRESS_ERR 0x20 +#define SD_PARAMETER_ERR 0x40 + +/* R2 token responses */ +#define SD_CARD_IS_LOCKED 0x01 +#define SD_WP_ERASE_SKIP 0x02 +#define SD_GENERAL_ERR 0x04 +#define SD_CC_ERR 0x08 +#define SD_CARD_ECC_FAILURE 0x10 +#define SD_WP_VIOLATION 0x20 +#define SD_ERASE_PARAM 0x40 +#define SD_OUT_OF_RANGE 0x80 + +/* Reading and writing tokens */ +#define SD_TOKEN_START_BLOCK 0xFE +#define SD_TOKEN_MULTIPLE_WRITE_START_BLOCK 0xFC +#define SD_TOKEN_MULTIPLE_WRITE_STOP_TRANSFER 0xFD +#define SD_TOKEN_DATA_ACCEPTED 0x05 +#define SD_TOKEN_DATA_REJECTED_CRC 0x0B +#define SD_TOKEN_DATA_REJECTED_WRITE_ERR 0x0D + +/** +@brief Initializes the card with the given chip select pin. +@details The card is put into a state where it can be sent read, write, and + other commands. + +@param chip_select_pin The digital pin connected to the CS on the card. + +@return An error code as defined by one of the SD_ERR_* definitions. +*/ +int8_t +sd_spi_init( + uint8_t chip_select_pin +); + +/** +@brief Writes data to a block on the card. +@details If buffering is enabled, the card will read the block on the card + into the buffer. The passed in data will then be stored in + the buffer. The data will be only written out to the card when a + different block is written to, read is called, write is called, + erase is called, or sd_spi_flush() is explicitly called. + If buffering is not enabled, the card will write out the data and + pad the rest of the block with zeros if number_of_bytes is less + than 512. + +@param block_address The address of the block on the card. +@param[in] data An array of data / an address to the data in + memory. +@param number_of_bytes The size of the data in bytes. +@param byte_offset The byte offset of where to start writing in the + block. + +@return An error code as defined by one of the SD_ERR_* definitions. +*/ +int8_t +sd_spi_write( + uint32_t block_address, + void *data, + uint16_t number_of_bytes, + uint16_t byte_offset +); + +/** +@brief Writes a block of data to a block on the card. +@details When buffering is enabled, this is faster than using sd_spi_write + since it does not buffer in the block from the card. + +@param block_address The address of the block on the card. +@param[in] data An array of data / an address to the data in + memory. + +@return An error code as defined by one of the SD_ERR_* definitions. +*/ +int8_t +sd_spi_write_block( + uint32_t block_address, + void *data +); + +/** +@brief Writes out the data in the buffer to the card if it has not been + flushed already. If writing continually, this will also advance to + the next block in the sequence. +@details This is to be used in conjunction with sd_spi_write or + sd_spi_continuous_write. + +@return An error code as defined by one of the SD_ERR_* definitions. +*/ +int8_t +sd_spi_flush( + void +); + +/** +@brief Notifies the card to prepare for sequential writing starting at the + specified block address. +@details A sequential write must be stopped by calling + sd_spi_write_continuous_stop(). When buffering is enabled, use + sd_spi_write_continuous() in conjunction with sd_spi_write_next(). + If buffering is disabled, use sd_spi_write_continuous() to write out + the data and it will advance to the next block in the sequence + automatically. + +@param start_block_address The address of the first block in the + sequence. +@param num_blocks_pre_erase (Optional) The number of block to pre-erase + to potentially speed up writing. It can be + set to 0 if the number if the number of + blocks is unknown or an estimate can be + given since it does not have to be exact. + +@return An error code as defined by one of the SD_ERR_* definitions. +*/ +int8_t +sd_spi_write_continuous_start( + uint32_t start_block_address, + uint32_t num_blocks_pre_erase +); + +/** +@brief Writes data to the current block in the sequence on the card. +@details If buffering is enabled, the data will be stored in the buffer which + has been cleared with zeros (current data in the block on the card + is not pre-buffered). The data will be only written out to the card + when sd_spi_write_continuous_next() or sd_spi_flush() is explicitly + called. If the buffering is not enabled, the card will write out the + data and advance to the next block in the sequence. If the number of + bytes given is less than 512, the rest of the block will be padded + with zeros. + +@param[in] data An array of data / an address to the data in + memory. +@param number_of_bytes The size of the data in bytes. +@param byte_offset The byte offset of where to start writing in the + block. + +@return An error code as defined by one of the SD_ERR_* definitions. +*/ +int8_t +sd_spi_write_continuous( + void *data, + uint16_t number_of_bytes, + uint16_t byte_offset +); + +/** +@brief Used only when buffering is enabled to write out the data in the + buffer to the card for continuous writing and advancing to the + next block in the sequence. +@details If buffering is not enabled, only use sd_spi_write_continuous(). + +@return An error code as defined by one of the SD_ERR_* definitions. +*/ +int8_t +sd_spi_write_continuous_next( + void +); + +/** +@brief Notifies the card to stop sequential writing and flushes the buffer + to the card if buffering is enabled. +@details This may take some time since it waits for the card to complete the + write. + +@return An error code as defined by one of the SD_ERR_* definitions. +*/ +int8_t +sd_spi_write_continuous_stop( + void +); + +/** +@brief Reads data from a block on the card. +@details If buffering is enabled, this will write in the block to the buffer. + Otherwise, the device will have to read in the data from the card + on every call (it has to read a 512 byte block only the relevant + data will be kept). + +@param block_address The address of the block on the card. +@param[out] data_buffer A location in memory to write the data to. +@param number_of_bytes The number of bytes to read. +@param byte_offset The byte offset of where to start reading in the + block. + +@return An error code as defined by one of the SD_ERR_* definitions. +*/ +int8_t +sd_spi_read( + uint32_t block_address, + void *data_buffer, + uint16_t number_of_bytes, + uint16_t byte_offset +); + +/** +@brief Notifies the card to prepare for sequential reading starting at the + specified block address. +@details A sequential read must be stopped by calling + sd_spi_read_continuous_stop(). When buffering is enabled, use + sd_spi_read_continuous() in conjunction with sd_spi_read_next(). + If buffering is disabled, use sd_spi_read_block() to read in data + and advance to the next block in the sequence. + +@param start_block_address The address of the first block in the + sequence. + +@return An error code as defined by one of the SD_ERR_* definitions. +*/ +int8_t +sd_spi_read_continuous_start( + uint32_t start_block_address +); + +/** +@brief Reads in data from the current block in the sequence on the card. +@details If buffering is enabled, the block will be stored in the buffer. + To advance to the next block, sd_spi_read_continuous_next() must + be explicitly called. If the buffering is not enabled, the card will + read in the data and advance to the next block in the sequence. + +@param[out] data_buffer A location in memory to write the data to. +@param number_of_bytes The number of bytes to read. +@param byte_offset The byte offset of where to start reading in the + block. + +@return An error code as defined by one of the SD_ERR_* definitions. +*/ +int8_t +sd_spi_read_continuous( + void *data_buffer, + uint16_t number_of_bytes, + uint16_t byte_offset +); + +/** +@brief Used only when buffering is enabled to advancing to the + next block in the sequence. +@details If buffering is not enabled, only use sd_spi_read_continuous(). + +@return An error code as defined by one of the SD_ERR_* definitions. +*/ +int8_t +sd_spi_read_continuous_next( + void +); + +/** +@brief Notifies the card to stop sequential reading. + +@return An error code as defined by one of the SD_ERR_* definitions. +*/ +int8_t +sd_spi_read_continuous_stop( + void +); + +/** +@brief Erases all the blocks on the card. +@details All of the bits will be set with the default value of 0 or 1 + HC depending on the card. + +@return An error code as defined by one of the SD_ERR_* definitions. +*/ +int8_t +sd_spi_erase_all( + void +); + +/** +@brief Erases the given sequence of blocks on the card. +@details All of the bits will be set with the default value of 0 or 1 + depending on the card. + +@param start_block_address The address of the first block in the + sequence. +@param end_block_address The address of the last block in the + sequence. + +@return An error code as defined by one of the SD_ERR_* definitions. +*/ +int8_t +sd_spi_erase_blocks( + uint32_t start_block_address, + uint32_t end_block_address +); + +/** +@brief Gets the number of blocks the card has. Blocks are 512 bytes. + +@return An error code as defined by one of the SD_ERR_* definitions. +*/ +uint32_t +sd_spi_card_size( + void +); + +/** +@brief Gets the type of the card. 1 = SD1, 2 = SD2, 3 = SDHC, and 4 = MMC. + +@return An error code as defined by one of the SD_ERR_* definitions. +*/ +uint8_t +sd_spi_card_type( + void +); + +/** +@brief Reads the Card Identification (CID) register on the card. +@details Information is stored in the sd_spi_cid_t structure. The details of + the structure can be found in sd_spi_info.h. + +@return An error code as defined by one of the SD_ERR_* definitions. +*/ +int8_t +sd_spi_read_cid_register( + sd_spi_cid_t *cid +); + +/** +@brief Reads the Card Specific Data (CSD) register on the card. +@details Information is stored in the sd_spi_csd_t structure. The details of + the structure can be found in sd_spi_info.h. + +@return An error code as defined by one of the SD_ERR_* definitions. +*/ +int8_t +sd_spi_read_csd_register( + sd_spi_csd_t *csd +); + +/** +@brief Returns the first error code (if any) found in the R2 response on + from the card. + +@return An error code as defined by one of the SD_ERR_* definitions. +*/ +int8_t +sd_spi_card_status( + void +); + +/** +@brief Getter for the address of the block that is currently buffered. + +@return Current buffered block address. +*/ +uint32_t +sd_spi_current_buffered_block( + void +); + +#if defined(__cplusplus) +} +#endif + +#endif /* SD_SPI_H_ */ diff --git a/sd-spi/sd_spi_commands.h b/sd-spi/sd_spi_commands.h new file mode 100644 index 0000000..fc6510e --- /dev/null +++ b/sd-spi/sd_spi_commands.h @@ -0,0 +1,280 @@ +/******************************************************************************/ +/** +@file sd_spi_commands.h +@author Wade Penson +@date June, 2015 +@brief SPI bus commands for SD/MMC. +@details The set of commands can be found in the simplified physical + SD specifications from the SD Association. +@copyright Copyright 2015 Wade Penson + +@license Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. +*/ +/******************************************************************************/ + +#if !defined(SD_SPI_COMMANDS_H_) +#define SD_SPI_COMMANDS_H_ + +#if defined(__cplusplus) +extern "C" { +#endif + +/** +@brief CMD0: Resets card. +@param [31:0] Stuff bits +@return Register R1 +*/ +#define SD_CMD_GO_IDLE_STATE 0x00 + +/** +@brief CMD1: Host sends HCS (Host Capacity Support) information. The card then + initializes. +@param [31] Zero bit +@param [30] HCS +@param [29:0] Zero bits +@return Register R1 +*/ +#define SD_CMD_SEND_OP_COND 0x01 + +/** +@brief CMD8: Host sends supply voltage and card replies with whether the card + can operate based on the voltage or not. +@param [31:12] Zero bits +@param [11:8] Supply voltage range +@param [7:0] Check pattern +@return Register R7 +*/ +#define SD_CMD_SEND_IF_COND 0x08 + +/** +@brief CMD9: Get the card specific data (CSD). +@param [31:0] Stuff bits +@return Register R1 +*/ +#define SD_CMD_SEND_CSD 0x09 + +/** +@brief CMD10: Get the card identification (CID). +@param [31:0] Stuff bits +@return Register R1 +*/ +#define SD_CMD_SEND_CID 0x0A + +/** +@brief CMD12: Stops the transmission for the multiple block read operation. +@param [31:0] Stuff bits +@return Register R1b (R1 with busy signal after) +*/ +#define SD_CMD_STOP_TRANSMISSION 0x0C + +/** +@brief CMD13: Get status from status register. +@param [31:0] Stuff bits +@return Register R2 +*/ +#define SD_CMD_SEND_STATUS 0x0D + +/** +@brief CMD16: Sets the block length, in bytes, for read and write commands of a + standard capacity card. However, the standard capacity cards usually + only support this command for the read commands. SDHC and SHXC only + support reading and writing blocks of size 512 bytes. +@param [31:0] Block length +@return Register R1 +*/ +#define SD_CMD_SET_BLOCKLEN 0x10 + +/** +@brief CMD17: Read a single block. The size can be set using CMD_SET_BLOCKLEN + for standard capacity cards. +@param [31:0] Data address. The address is determined by bytes for standard sd + cards and by blocks (512 bytes) for SDHC and SHXC cards. +@return Register R1 +*/ +#define SD_CMD_READ_SINGLE_BLOCK 0x11 + +/** +@brief CMD18: Read blocks continuously and end transmission by sending + CMD_STOP_TRANSMISSION. +@param [31:0] Data address. The address is determined by bytes for standard + sd cards and by blocks (512 bytes) for SDHC and SHXC cards. +@return Register R1 +*/ +#define SD_CMD_READ_MULTIPLE_BLOCK 0x12 + +/** +@brief CMD24: Write a single block. +@param [31:0] Data address. The address is determined by bytes for standard + sd cards and by blocks (512 bytes) for SDHC and SHXC cards. +@return Register R1 +*/ +#define SD_CMD_SET_WRITE_BLOCK 0x18 + +/** +@brief CMD25: Write blocks continuously and end transmission by sending + STOP_TRAN_TOKEN. +@param [31:0] Data address. The address is determined by bytes for standard + sd cards and by blocks (512 bytes) for SDHC and SHXC cards. +@return Register R1 +*/ +#define SD_CMD_WRITE_MULTIPLE_BLOCK 0x19 + +/** +@brief CMD27: Program the programmable bits of the CSD. These include being + able to write multiple times to the TMP_WRITE_PROTECT and CRC fields + and writing a single time to the FILE_FORMAT_GRP, COPY, + PERM_WRITE_PROTECT, and FILE_FORMAT fields. +@param [31:0] Stuff bits +@return Register R1 +*/ +#define SD_CMD_PROGRAM_CSD 0x1B + +/** +@brief CMD28: Sets write protection for the addressed group for standard + capacity cards that support write protected features. WP_GRP_ENABLE + in the CSD register can be used to determine if this command is + supported and WP_GROUP_SIZE determines the size of a group. +@param [31:0] Data address +@return Register R1b (R1 with busy signal after) +*/ +#define SD_CMD_SET_WRITE_PROT 0x1C + +/** +@brief CMD29: Clears write protection for the addressed group for standard + capacity cards that support write protected features. WP_GRP_ENABLE + in the CSD register can be used to determine if this command is + supported and WP_GROUP_SIZE determines the size of a group. +@param [31:0] Data address +@return Register R1b (R1 with busy signal after) +*/ +#define SD_CMD_CLR_WRITE_PROT 0x1D + +/** +@brief CMD30: Send status of the addressed group for standard capacity cards + that support write protected features. WP_GRP_ENABLE in the CSD register + can be used to determine if this command is supported and WP_GROUP_SIZE + determines the size of a group. +@param [31:0] Data address +@return Register R1 +*/ +#define SD_CMD_SEND_WRITE_PROT 0x1E + +/** +@brief CMD32: Sets the address of the first block for writing to be erased. +@param [31:0] Data address. The address is determined by bytes for standard + sd cards and by blocks (512 bytes) for SDHC and SHXC cards. +@return Register R1 +*/ +#define SD_CMD_ERASE_WR_BLK_START 0x20 + +/** +@brief CMD33: Sets the address of the last block for writing to be erased. +@param [31:0] Data address. The address is determined by bytes for standard + sd cards and by blocks (512 bytes) for SDHC and SHXC cards. +@return Register R1 +*/ +#define SD_CMD_ERASE_WR_BLK_END 0x21 + +/** +@brief CMD38: Erase the blocks selected by CMD_ERASE_WR_BLK_START and + CMD_ERASE_WR_BLK_END. +@param [31:0] Stuff bits +@return Register R1b (R1 with busy signal after) +*/ +#define SD_CMD_ERASE 0x26 + +/** +@brief CMD58: Reads OCR register. CCS bit is assigned to OCR[30]. +@param [31:0] Stuff bits +@return Register R3 +*/ +#define SD_CMD_READ_OCR 0x3A + +/** +@brief CMD59: Turns CRC on or off. +@param [31:1] Stuff bits +@param [0:0] A '1' to turn CRC on and '0' to turn CRC off +@return Register R1 +*/ +#define SD_CMD_CRC_ON_OFF 0x3B + +/** +@brief CMD55: Tells the card that the next command is an application specific + command (ACMD). +@param [31:0] Stuff bits +@return Register R1 +*/ +#define SD_CMD_APP 0x37 + +/** +@brief CMD56: For application specific commands, transfer a datablock to or + from the card. +@param [31:1] Stuff bits +@param [0:0] A '1' for reading data and '0' for writing data +@return Register R1 +*/ +#define SD_CMD_GEN 0x38 + +/** +@brief ACMD13: Get the SD status. +@param [31:0] Stuff bits +@return Register R2 +*/ +#define SD_ACMD_STATUS 0x0D + +/** +@brief ACMD22: Get the number of blocks that were written without errors. +@param [31:0] Stuff bits +@return Register R1 +*/ +#define SD_ACMD_SEND_NUM_WR_BLOCKS 0x16 + +/** +@brief ACMD23: Set the number of blocks to be erased before writing. +@param [31:23] Stuff bits +@param [22:0] Number of blocks +@return Register R1 +*/ +#define SD_ACMD_SET_WR_BLK_ERASE_COUNT 0x17 + +/** +@brief ACMD41: Host sends HCS (Host Capacity Support) information. The card + then initializes. +@param [31] Zero bit +@param [30] HCS +@param [29:0] Zero bits +@return Register R1 +*/ +#define SD_ACMD_SEND_OP_COND 0x29 + +/** +@brief ACMD42: Connects or disconnects the pull-up resistor on the CS pin of + the card. May be used for card detection. +@param [31:1] Stuff bits +@param [0:0] A '1' to connect pull-up and '0' to disconnect pull-up +@return Register R1 +*/ +#define SD_ACMD_SET_CLR_CARD_DETECT 0x2A + +/** +@brief ACMD51: Reads SCR (SD configuration) register. +@param [31:0] Stuff bits +@return Register R1 +*/ +#define SD_ACMD_SEND_SCR 0x33 + +#if defined(__cplusplus) +} +#endif + +#endif /* SD_SPI_COMMANDS_H_ */ diff --git a/sd-spi/sd_spi_info.h b/sd-spi/sd_spi_info.h new file mode 100644 index 0000000..c540992 --- /dev/null +++ b/sd-spi/sd_spi_info.h @@ -0,0 +1,201 @@ +/******************************************************************************/ +/** +@file sd_spi_info.h +@author Wade Penson +@date June, 2015 +@brief Structures to store information from CID (Card Identification) + and CSD (Card Specific Data) registers for SD cards. +@details Information about the fields can be found in the simplified + physical SD specifications from the SD Association. +@copyright Copyright 2015 Wade Penson + +@license Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. +*/ +/******************************************************************************/ + +#if !defined(SD_SPI_INFO_H_) +#define SD_SPI_INFO_H_ + +#if defined(__cplusplus) +extern "C" { +#endif + +/** +@brief CID register information +*/ +typedef struct sd_spi_cid +{ + /** Manufacturer ID. */ + unsigned int mid: 8; + /** OEM/Application ID. Composed of 2 characters. */ + char oid[2]; + /** Product name. Composed of 5 characters. */ + char pnm[5]; + /** n part of the product revision number, which is in the form of n.m */ + unsigned int prv_n: 4; + /** m part of the product revision number, which is in the form of n.m */ + unsigned int prv_m: 4; + /** Bits [31:24] of the 4 byte product serial number. */ + unsigned int psn_high: 8; + /** Bits [23:16] of the 4 byte product serial number. */ + unsigned int psn_mid_high: 8; + /** Bits [15:8] of the 4 byte product serial number. */ + unsigned int psn_mid_low: 8; + /** Bits [7:0] of the 4 byte product serial number. */ + unsigned int psn_low: 8; + /** Year manufactured. */ + unsigned int mdt_year: 8; + /** Month manufactured. */ + unsigned int mdt_month: 4; + /* Bitfield padding */ + unsigned int: 4; + /** Checksum for the CID. */ + unsigned int crc: 7; + /* Bitfield padding */ + unsigned int: 1; +} sd_spi_cid_t; + +/** +@brief Information exclusive to CSD register version 1. + +@details The following formula is used to compute the card's size:

+ Card size in MB = Number of blocks * 512 / 1000000
+ Number of blocks = ((c_size + 1) * 2c_size_mult + 2) * + (2max_read_bl_len / 512)
+*/ +typedef struct sd_spi_csd_v1 +{ + /** Low bits of c_size. Used to compute the card's size. */ + unsigned int c_size_low: 8; + /** High bits of c_size. Used to compute the card's size. */ + unsigned int c_size_high: 4; + /** Max read current for minimal supply voltage. */ + unsigned int vdd_r_curr_min: 3; + /* Bitfield padding */ + unsigned int: 1; + /** Max read current for max supply voltage. */ + unsigned int vdd_r_curr_max: 3; + /** Max write current for minimal supply voltage. */ + unsigned int vdd_w_curr_min: 3; + /* Bitfield padding */ + unsigned int: 2; + /** Max write current for max supply voltage. */ + unsigned int vdd_w_curr_max: 3; + /** Multiplier used to compute the card's size. */ + unsigned int c_size_mult: 3; + /* Bitfield padding */ + unsigned int: 2; +} sd_spi_csd_v1_t; + +/** +@brief Information exclusive to CSD register version 2 + +@details The card size is computed as follows:

+ Card size in MB = (c_size + 1) * 512 / 1000 +*/ +typedef struct sd_spi_csd_v2 +{ + /** Low bits of c_size. Used to compute the card's size. */ + unsigned int c_size_low: 8; + /** Mid bits of c_size. Used to compute the card's size. */ + unsigned int c_size_mid: 8; + /** High bits of c_size. Used to compute the card's size. */ + unsigned int c_size_high: 6; + /* Bitfield padding */ + unsigned int: 2; +} sd_spi_csd_v2_t; + +/** +@brief The CSD register is either version 1 or version 2. Space is saved by + unioning the specific version information for version 1 and 2. The + csd_v1 structure is for version 1 and the csd_v2 is for version 2. +*/ +typedef union sd_spi_csd_v_info +{ + sd_spi_csd_v1_t v1; + sd_spi_csd_v2_t v2; +} sd_spi_csd_v_info_t; + +/** +@brief CSD register information +@details The CSD version is determined by the csd_structure value. If the + value is 0, the CSD is version 1 so the csvi will contain the + v1 type specific information. Otherwise, the CSD is version 2 so + the csvi will contain the v2 type specific information. Further + details about the fields can be found in the SD specifications. +*/ +typedef struct sd_spi_csd +{ + /** Union of the CSD version specific info. */ + sd_spi_csd_v_info_t cvsi; + /** CSD version. */ + unsigned int csd_structure: 2; + /** The file format of the card. */ + unsigned int file_format: 2; + /* Bitfield padding */ + unsigned int: 4; + /** Asynchronous part of data access time. Table of the corresponding + values are found in the SD specifications. */ + unsigned int taac: 8; + /** Worst case for the clock dependent factor of the data access time.*/ + unsigned int nsac: 8; + /** Max data transfer speed for a single data line. */ + unsigned int tran_speed: 8; + /** Low bits of the ccc; the command class of the card. */ + unsigned int ccc_low: 8; + /** High bits of the ccc; the command class of the card. */ + unsigned int ccc_high: 4; + /** Max block length. */ + unsigned int max_read_bl_len: 4; + /** Defines if the card can read partial blocks. */ + unsigned int read_bl_partial: 1; + /** Defines if a data block can be spanned over a block boundary when + written. */ + unsigned int write_bl_misalign: 1; + /** Defines if a data can be read over block boundaries. */ + unsigned int read_bl_misalign: 1; + /** Defines if the configurable driver stage is implemented in the card. */ + unsigned int dsr_imp: 1; + /** Defines the unit size of data being erased. */ + unsigned int erase_bl_en: 1; + /** Ratio of read to write time. */ + unsigned int r2w_factor: 3; + /** The size of an erasable sector. */ + unsigned int erase_sector_size: 7; + /** Defines if write group protection is possible. */ + unsigned int wp_grp_enable: 1; + /** The size of a write protected group. */ + unsigned int wp_grp_size: 7; + /** Defines if the card can write partial blocks. */ + unsigned int write_bl_partial: 1; + /** The max write block length. */ + unsigned int write_bl_len: 4; + /** The file format group of the card. */ + unsigned int file_format_grp: 1; + /** Defines if the cards content is original or is a copy. */ + unsigned int copy: 1; + /** Determines if the card is permanently write protected or not. */ + unsigned int perm_write_protect: 1; + /** Determines if the card is temporarily write protected or not. */ + unsigned int tmp_write_protect: 1; + /** Checksum for the CSD. */ + unsigned int crc: 7; + /* Bitfield padding */ + unsigned int: 1; +} sd_spi_csd_t; + +#if defined(__cplusplus) +} +#endif + +#endif /* SD_SPI_INFO_H_ */ \ No newline at end of file diff --git a/sd-spi/sd_spi_platform_dependencies.c b/sd-spi/sd_spi_platform_dependencies.c new file mode 100644 index 0000000..aec758a --- /dev/null +++ b/sd-spi/sd_spi_platform_dependencies.c @@ -0,0 +1,67 @@ +#include "sd_spi_platform_dependencies.h" + +void +sd_spi_pin_mode( + uint8_t pin, + uint8_t mode +) +{ + +} + +void +sd_spi_digital_write( + uint8_t pin, + uint8_t state +) +{ + +} + +uint32_t +sd_spi_millis( + void +) +{ + +} + +void +sd_spi_begin( + void +) +{ + +} + +void +sd_spi_begin_transaction( + uint32_t transfer_speed_hz +) +{ + +} + +void +sd_spi_end_transaction( + void +) +{ + +} + +void +sd_spi_send_byte( + uint8_t b +) +{ + +} + +uint8_t +sd_spi_receive_byte( + void +) +{ + +} \ No newline at end of file diff --git a/sd-spi/sd_spi_platform_dependencies.h b/sd-spi/sd_spi_platform_dependencies.h new file mode 100644 index 0000000..b668cad --- /dev/null +++ b/sd-spi/sd_spi_platform_dependencies.h @@ -0,0 +1,94 @@ +/******************************************************************************/ +/** +@file sd_spi_platform_dependencies.cpp +@author Wade Penson +@date June, 2015 +@brief Interface for the dependencies needed by the SD SPI library. +@copyright Copyright 2015 Wade Penson + +@license Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. +*/ +/******************************************************************************/ + +#if !defined(SD_SPI_PLATFORM_DEPENDENCIES_H_) +#define SD_SPI_PLATFORM_DEPENDENCIES_H_ + +#if defined(__cplusplus) +extern "C" { +#endif + +#include + +#if !defined(INPUT) +#define INPUT 0 +#endif + +#if !defined(OUTPUT) +#define OUTPUT 1 +#endif + +#if !defined(LOW) +#define LOW 0 +#endif + +#if !defined(HIGH) +#define HIGH 1 +#endif + +void +sd_spi_pin_mode( + uint8_t pin, + uint8_t mode +); + +void +sd_spi_digital_write( + uint8_t pin, + uint8_t state +); + +uint32_t +sd_spi_millis( + void +); + +void +sd_spi_begin( + void +); + +void +sd_spi_begin_transaction( + uint32_t transfer_speed_hz +); + +void +sd_spi_end_transaction( + void +); + +void +sd_spi_send_byte( + uint8_t b +); + +uint8_t +sd_spi_receive_byte( + void +); + +#if defined(__cplusplus) +} +#endif + +#endif /* SD_SPI_PLATFORM_DEPENDENCIES_H_ */ \ No newline at end of file