SD Cards (Part 6)

Previous posts on same subject Part 12345, 6

SD cards are a de facto standard for storing all sorts of information. They are cheap and their capacity seems to be (almost) limitless. As explained in previous posts, SD card can be used in two ways: as a logical unit for use with computer OS or in native mode. In the native mode, each single byte of memory can be used as per your own taste, and the driving microcode is pretty small in size compared to the code required for using the SD card with a FAT.

While my early SD card module is still working (The one with the resistor bridges)…

… I invested in a more rugged and state of the art shield bought from Adafruit Industries (ref. 1141); the presence of the Real Time Clock (RTC) makes sense as this type of shield is mandatory for data logging applications. The MicroSD card breakout will work fine too! (ref. 254)

shield_SDC

As many memory devices, SD card are organize in memory pages, just like plain books.  In the SD world, memory pages are named “blocks” and their size vary depending upon the card, starting at 512 Bytes. When reading the SD card, you must read it block by block. Writing blocks is a little bit more complex as the number of write cycles is large (typically 1.000.000 cycles) however not infinite. This constraint forces the code designer to care about the write cycles. Writing a byte in a block the rough way would simply consist in:

  • Read the block and store its content in  a buffer of bytes
  • Update the content of the byte location within the buffer
  • Write the content of the buffer back  on the card’s block

If we were to write 512 consecutive bytes in this way, we would require 512 write cycles and consequently decrease the expected write cycles to 2.000 for this card! So that we must use a more clever approach.

The idea is to keep the blocks “open” as long as there is no need to write (or read) in an other block.

Say for example that you want to write 513 bytes of data, starting at the very beginning of the SD card. The write procedure will be:

  • Read block 0 and store its content in  a buffer of bytes
  • Sequentially write 512 bytes in the buffer
  • Write the content of the buffer on block 0
  • Read block 1 and store its content in  a buffer of bytes
  • Write 1 byte in the buffer 
  • Leave the SD control like this, unless:
    • The write sequence is completed, and consequently you must write the content of the buffer on block 1
    • One or more bytes will be written in the same block and no write will be needed
    • One or more bytes must be written in an other block and consequently you must write the content of the buffer on block 1, read the other block and so on…

Writing such code is not plain trivial and it requires discipline! Best is to use a refined library which will relieve you from the burden of managing this read and write procedures. This is aim of PlainSDC. In its new version, PlainSDC allows you to care only for the address of data without any consideration for blocks of buffers. If your SD card has 16 Mb of memory space, you will read and write each single byte using addresses between 0 and 16.777.215.  Because you may want to store data of different types, PlainSDC provides you with read and write functions for 1, 2 and 4 bytes signed and unsigned integers as well as for 4 bytes floats.

Next is a list of the available functions from PlainDSP. Although most function are designed for simple use, it also contains functions for advanced users who which to work directly with buffers and blocks.

Initialization and release functions:

uint8_t InitializeSDC(volatile uint8_t *portCs, uint8_t pinCs);
void ReleaseSDC(void);

General purpose functions, including functions for advanced use

uint32_t ActualAddress(uint32_t address);
uint32_t ActualAddress(void);
uint32_t ActualBlock(uint32_t block);
uint32_t ActualBlock(void);
uint16_t BlockBufferSize(void);
uint32_t Blocks(void);
void ClearBlock(uint32_t block);
void ClearBlock(void);
void ClearBlocks();
void ClearBlocks(uint32_t firstBlock, uint32_t lastBlock);
void ClearBuffer(void);
void FillBuffer(uint8_t value);
void ReadBlock(uint32_t block);
void ReadBlock(void);

Read functions

float ReadFlt32(uint32_t address);
float ReadFlt32(void);
int16_t ReadInt16(uint32_t address);
int16_t ReadInt16(void);
int32_t ReadInt32(uint32_t address);
int32_t ReadInt32(void);
uint16_t ReadUInt16(uint32_t address);
uint16_t ReadUInt16(void);
uint32_t ReadUInt32(uint32_t address);
uint32_t ReadUInt32(void);
uint8_t ReadUInt8(uint32_t address);
uint8_t ReadUInt8(void);

Write functions

void WriteBlock(uint32_t block);
void WriteBlock(void);
void WriteFlt32(float data);
void WriteFlt32(float data, uint32_t address);
void WriteInt16(int16_t data);
void WriteInt16(int16_t data, uint32_t address);
void WriteInt32(int32_t data);
void WriteInt32(int32_t data, uint32_t address);
void WriteUInt16(uint16_t data);
void WriteUInt16(uint16_t data, uint32_t address);
void WriteUInt32(uint32_t data);
void WriteUInt32(uint32_t data, uint32_t address);
void WriteUInt8(uint8_t data);
void WriteUInt8(uint8_t data, uint32_t address);
void CompleteWrite(void);

In the Read and Write functions, is the address is not specified, the data is recorded at the next address. Back to the example above, writing 0xAA in the first 4 bytes of the SD card can be done in this way:

  • Set the actual address using the ActualAddress() function with argument 0x00
  • Write 0xAA at address 0x00 using WriteUInt8() function with argument 0xAA only
  • Write 0xAA at address 0x01 using WriteUInt8() function with argument 0xAA only
  • Write 0xAA at address 0x02 using WriteUInt8() function with argument 0xAA only
  • Write 0xAA at address 0x03 using WriteUInt8() function with argument 0xAA only
  • Complete write using the CompleteWrite() function

Reading back data is as easy as above, use ReadUInt8() function instead of WriteUInt8() and skip the RealeaseWrite() step.

PlainSDC comes with some examples which illustrate the use the library. These examples shall be presented in the next posts

 

Leave a Reply

You must be logged in to post a comment.