SD-Card with CCS C Compiler

SD-Card with CCS C Compiler

Description


This project shows how to use a SD-Card with FAT16 file system. It is based on the example MMC/SD-Card driver (mmcsd.c) and FAT file system driver (fat.c) delivered with the CCS C compiler.

Unfortunately, the example didn't work properly for me. So I searched the web and found some required changes to make it work. All modifications are described in the following article.

 

Hardware


SD-cards can be interfaced to a microcontroller via SPI-Bus, hence only 4 I/O pins (SDI, SDO, SCK, CS) are required to connect the SD-card to the PIC.

SD-cards operate at 2.7V .. 3.6V while the 18F PICs usually operate at 5V. Hence a voltage level translation is required for the SD-card inpus (SDI, SCK, CS). There are a lot of examples available in the web where a voltage divider with resistors is used to translate the 5V signals to 3.3V. This will work, but at higher SPI speeds this might cause problems. Due to that, I used a 74AC125 buffer to do the level translation which works more reliable than the voltage divider.

SD-Card Module

Here the schematic of the SD-Card module with the voltage level translation. The pins on the connector SV1 need to be connected to the microcontroller pins.

Important: SDO and SDI are the pin names from the SD-card point of view, i.e. SDO needs to be connected to SDI on the microcontroller and SDI needs to be connected to SDO in the microcontroller!

SD-Card module schematic

Here some pictures of the SD-card module which I have build on a breadboard and the experimental setup. I used the PIC1618Exp-28d experiment board assembled with a PIC18F2620 for the experimental setup.

SD-Card module on breadboard SD-Card experimental setup

 

Software


As mentioned in the description for this article, I used the ex_fat.c example which is delivered with the CCS C compiler.

Unforunately, when I run the demo and created some files with the demo, I was not able to see the files when I pluged the SD-card into my PC. After some investigations I found a solution for this problem in the CCS forum which I will summarize here.

Modifications in mmcsd.c

Search for

uint32_t g_mmcsdBufferAddress;

and add under it:

uint32_t g_mmcsdPartitionOffset;

Next, just before the "mmcsd_init" function add a new function:

void mmcsd_check_part(uint16_t off)
{
if (g_mmcsd_buffer[off + 0] == 0x80)
{
// active partition 

uint8_t t;
t = g_mmcsd_buffer[off + 4];
if (t == 0x04 || t == 0x06 || t == 0x0B)
{
// FAT16 or FAT32 partition 

g_mmcsdPartitionOffset = make32(
g_mmcsd_buffer[off + 11], g_mmcsd_buffer[off + 10],
g_mmcsd_buffer[off + 9], g_mmcsd_buffer[off + 8]) * MMCSD_MAX_BLOCK_SIZE;
}
}
}

In the "mmcsd_init" function, right after the line:

r1 = mmcsd_load_buffer();

Add the lines:

g_mmcsdPartitionOffset = 0;
mmcsd_check_part(0x1EE);
mmcsd_check_part(0x1DE);
mmcsd_check_part(0x1CE);
mmcsd_check_part(0x1BE);

Finally, in the function "mmcsd_move_buffer", after the line:

new_block = new_addr - (new_addr % MMCSD_MAX_BLOCK_SIZE);

Add the line:

new_block += g_mmcsdPartitionOffset;

Modifications in mmcsd.c

The CCS "fat.c" functions "get_next_addr" and "get_prev_addr" do not correctly handle cluster transitions. The following are corrected functions, so replace the function with the following code:

signed int get_next_addr(int32* my_addr)
{
int32 temp;
#ifdef FAT32
int32 c;
#else
int16 c;
#endif
// check to make sure that the next iteration will give us a contiguous address 

temp = *my_addr + 1;
if((temp > Data_Start) && ((temp - Data_Start) % Bytes_Per_Cluster == 0))
{
// convert the current address into the address of where information about 

//  the address is stored in the FAT, and put this value into the current address 

c = addr_to_cluster(temp - 1);
if(get_next_cluster(&c) == EOF)
return EOF;
if (c >=
#ifdef FAT32
0x0FFFFFF8
#else
0xFFF8
#endif
)
return EOF;
temp = cluster_to_addr(c);
}
*my_addr = temp;
return GOODEC;
}

Further, get_prev_addr() needs to be updated:

signed int8 get_prev_addr(int32* my_addr)
{
int32 temp;
#ifdef FAT32
int32 c;
#else
int16 c;
#endif
temp = *my_addr;
// if we're trying to go backwards one entry from the beginning of the root, 

//  we won't be able to... 

if(temp <= Root_Dir)
return GOODEC;
// check to make sure that the next iteration will give us a contiguous address 

if((temp >= Data_Start) && ((temp - Data_Start) % Bytes_Per_Cluster == 0))
{
c = addr_to_cluster(temp);
if(get_prev_cluster(&c) == EOF)
return EOF;
temp = cluster_to_addr(c) + Bytes_Per_Cluster;
}
*my_addr = temp - 1;
return GOODEC;
}

In fat_init() function search for the following lines

#ifdef FAT32
Data_Start = Bytes_Per_Cluster + Root_Dir;
#else // FAT16

and replace it with the following code (i.e. remove "Bytes_Per_Cluster +"):

#ifdef FAT32
Data_Start = Root_Dir;
#else // FAT16

And finally, get_short_file_name() needs to be replaced with the following code:

signed int get_short_file_name(int32 file_entry_addr, char sname[], int8 type)
{
int
buf,
i,
j = 0;
// one short file name has, at the most, 11 characters 

for(i = 0; i < 11; ++i)
{
// read in a character 

if(mmcsd_read_data(i + file_entry_addr, 1, &buf) != GOODEC)
return EOF;
// convert the character 

if(buf != ' ')
{
if (i == 8 && type != 0x10) sname[j++] = '.';
sname[j++] = tolower(buf);
}
}
if (sname[j - 1] == '.') --j;
sname[j] = '\0';
return GOODEC;
}

With this modifications everything worked fine. But currently, the changes have only been verifed with FAT16 enabled, i.e. I have enabled FAT16 support and disabled FAT32 in fat.c:

/// Define your FAT type here ///

#define FAT16
//#define FAT32

 

Links


Here some usefull links: