//*************************************************************************** //Function: get cluster entry value from FAT to find out the next cluster in the chain //or set new cluster entry in FAT //Arguments: 1. current cluster number, 2. get_set (=GET, if next cluster is to be found or = SET, //if next cluster is to be set 3. next cluster number, if argument#2 = SET, else 0 //return: next cluster number, if if argument#2 = GET, else 0 //**************************************************************************** unsigned long getSetNextCluster (unsigned long clusterNumber, unsigned char get_set, unsigned long clusterEntry) { unsigned int FATEntryOffset; unsigned long *FATEntryValue; unsigned long FATEntrySector; unsigned char retry = 0; //get sector number of the cluster entry in the FAT FATEntrySector = unusedSectors + reservedSectorCount + ((clusterNumber * 4) / bytesPerSector) ; //get the offset address in that sector number FATEntryOffset = (unsigned int) ((clusterNumber * 4) % bytesPerSector); //read the sector into a buffer while(retry <10) { if(!SD_readSingleBlock(FATEntrySector)) break; retry++; } //get the cluster address from the buffer FATEntryValue = (unsigned long *) &buffer[FATEntryOffset]; if(get_set == GET) return ((*FATEntryValue) & 0x0fffffff); *FATEntryValue = clusterEntry; //for setting new value in cluster entry in FAT SD_writeSingleBlock(FATEntrySector); return 0; }
//******************************************************************************************** //Function: to get or set next free cluster or total free clusters in FSinfo sector of SD card //Arguments: 1.flag:TOTAL_FREE or NEXT_FREE, // 2.flag: GET or SET // 3.new FS entry, when argument2 is SET; or 0, when argument2 is GET //return: -next free cluster, if arg1 is NEXT_FREE & arg2 is GET // -total number of free clusters, if arg1 is TOTAL_FREE & arg2 is GET // -0xffffffff, if any error or if arg2 is SET //******************************************************************************************** unsigned long getSetFreeCluster(unsigned char totOrNext, unsigned char get_set, unsigned long FSEntry) { struct FSInfo_Structure *FS = (struct FSInfo_Structure *) &buffer; unsigned char error; SD_readSingleBlock(unusedSectors + 1); if((FS->leadSignature != 0x41615252) || (FS->structureSignature != 0x61417272) || (FS->trailSignature !=0xaa550000)) return 0xffffffff; if(get_set == GET) { if(totOrNext == TOTAL_FREE) return(FS->freeClusterCount); else // when totOrNext = NEXT_FREE return(FS->nextFreeCluster); } else { if(totOrNext == TOTAL_FREE) FS->freeClusterCount = FSEntry; else // when totOrNext = NEXT_FREE FS->nextFreeCluster = FSEntry; error = SD_writeSingleBlock(unusedSectors + 1); return error; //update FSinfo } return 0xffffffff; }
/* * fct_SDCard.c * * Created on: 21 juin 2016 * Author: formateur */ #include <avr/io.h> #include <util/delay.h> #include "../lib/usart.h" #include "../lib/fct_spi.h" #include "../lib/fct_SDCard.h" //****************************************************************** //Function : to initialize the SD/SDHC card in SPI mode //Arguments : none //return : unsigned char; will be 0 if no error, // otherwise the response byte will be sent //****************************************************************** unsigned char SD_init(void) { unsigned char response, SD_version; unsigned int retry=0 ; int i=0; //Spi init spi_init(); /**send init on spi peripheral**/ for(i=0;i<10;i++){ SD_CS_ASSERT; /**set chip select*/ do { response = SD_sendCommand(GO_IDLE_STATE, 0); /**send software reset */ retry++; if(retry>0x20){ // USART0_print("\rpas de carte\n"); // return 1; /**time out, card not detected*/ } } while(response != 0x01); /**response= 0x01 : card in idle state and no error*/ SD_CS_DEASSERT; SPI_transmit (0xff); /**1st byte transmission */ SPI_transmit (0xff); /**2nd byte transmission */ retry = 0; /**reset retries counter*/ SD_version = 2; //default set to SD compliance with ver2.x; //this may change after checking the next command do { response = SD_sendCommand(SEND_IF_COND,0x000001AA); /**check power supply status,for SDHC card*/ retry++; if(retry>0xfe) { SD_version = 1; cardType = 1; break; } //time out }while(response != 0x01); retry = 0; do { response = SD_sendCommand(APP_CMD,0); //CMD55, must be sent before sending any ACMD command response = SD_sendCommand(SD_SEND_OP_COND,0x40000000); //ACMD41 retry++; if(retry>0xfe) { USART0_print("\rinitialisation a échoué\n"); return 2; //time out, card initialization failed } }while(response != 0x00); retry = 0; SDHC_flag = 0; if (SD_version == 2) { do { response = SD_sendCommand(READ_OCR,0); retry++; if(retry>0xfe) { cardType = 0; break; } //time out }while(response != 0x00); if(SDHC_flag == 1) cardType = 2; else cardType = 3; } //SD_sendCommand(CRC_ON_OFF, OFF); //disable CRC; deafault - CRC disabled in SPI mode //SD_sendCommand(SET_BLOCK_LEN, 512); //set block size to 512; default size is 512 // switch (cardType) /** switch for card type (to check communication with the card)*/ { case 1 : USART0_print("\rver1.x\n");break; case 2 : USART0_print("\rSDHC\n");break; case 3 : USART0_print("\rver2.x\n");break; default : USART0_print("runknown Sd card\n"); } // return 0;break; /**successful return*/ } _delay_ms(1); /**give some time to end the init*/ return 0; } //****************************************************************** //Function : to send a command to SD card //Arguments : unsigned char (8-bit command value) // & unsigned long (32-bit command argument) //return : unsigned char; response byte //****************************************************************** unsigned char SD_sendCommand(unsigned char cmd, unsigned long arg) { unsigned char response, retry=0, status; //SD card accepts byte address while SDHC accepts block address in multiples of 512 //so, if it's SD card we need to convert block address into corresponding byte address by //multipying it with 512. which is equivalent to shifting it left 9 times //following 'if' loop does that if(SDHC_flag == 0) if(cmd == READ_SINGLE_BLOCK || cmd == READ_MULTIPLE_BLOCKS || cmd == WRITE_SINGLE_BLOCK || cmd == WRITE_MULTIPLE_BLOCKS || cmd == ERASE_BLOCK_START_ADDR|| cmd == ERASE_BLOCK_END_ADDR ) { arg = arg << 9; } SD_CS_ASSERT; SPI_transmit(cmd | 0x40); //send command, first two bits always '01' SPI_transmit(arg>>24); SPI_transmit(arg>>16); SPI_transmit(arg>>8); SPI_transmit(arg); if(cmd == SEND_IF_COND) //it is compulsory to send correct CRC for CMD8 (CRC=0x87) & CMD0 (CRC=0x95) SPI_transmit(0x87); //for remaining commands, CRC is ignored in SPI mode else SPI_transmit(0x95); while((response = SPI_receive()) == 0xff) //wait response if(retry++ > 0xfe) break; //time out error if(response == 0x00 && cmd == 58) //checking response of CMD58 { status = SPI_receive() & 0x40; //first byte of the OCR register (bit 31:24) if(status == 0x40) SDHC_flag = 1; //we need it to verify SDHC card else SDHC_flag = 0; SPI_receive(); //remaining 3 bytes of the OCR register are ignored here SPI_receive(); //one can use these bytes to check power supply limits of SD SPI_receive(); } SPI_receive(); //extra 8 CLK SD_CS_DEASSERT; return response; //return state } //***************************************************************** //Function : to erase specified no. of blocks of SD card //Arguments : none //return : unsigned char; will be 0 if no error, // otherwise the response byte will be sent //***************************************************************** unsigned char SD_erase (unsigned long startBlock, unsigned long totalBlocks) { unsigned char response; response = SD_sendCommand(ERASE_BLOCK_START_ADDR, startBlock); /**send address of starting block*/ if(response != 0x00) /**check the SD status: 0x00 - OK (No flags set)*/ return response; response = SD_sendCommand(ERASE_BLOCK_END_ADDR,(startBlock + totalBlocks - 1)); /**send adress of end block*/ if(response != 0x00) /**check the SD status: 0x00 - OK (No flags set)*/ return response; response = SD_sendCommand(ERASE_SELECTED_BLOCKS, 0); /**erase all selected blocks*/ if(response != 0x00) /**check SD status: 0x00 - OK (No flags set)*/ return response; return 0; //normal return } //****************************************************************** //Function : to read a single block from SD card //Arguments : none //return : unsigned char; will be 0 if no error, // otherwise the response byte will be sent //****************************************************************** unsigned char SD_readSingleBlock(unsigned long startBlock) { unsigned char response; unsigned int i, retry=0; response = SD_sendCommand(READ_SINGLE_BLOCK, startBlock); /**read a Block command*/ if(response != 0x00) return response; /**check if SD status: 0x00 - OK (no flag activated)*/ SD_CS_ASSERT; retry = 0; while(SPI_receive() != 0xfe) /**wait for start block to get the 0xfe value*/ if(retry++ > 0xfffe){ SD_CS_DEASSERT; return 1; } /**function output time-out*/ for(i=0; i<512; i++) /**read every 512 bytes*/ buffer[i] = SPI_receive(); SPI_receive(); /**let 8 clock pulses*/ SD_CS_DEASSERT; return 0; } //****************************************************************** //Function : to write to a single block of SD card //return : unsigned char; will be 0 if no error, // otherwise the response byte will be sent //****************************************************************** unsigned char SD_writeSingleBlock(unsigned long startBlock) { unsigned char response; unsigned int i, retry=0; response = SD_sendCommand(WRITE_SINGLE_BLOCK, startBlock); /** block's writing command*/ if(response != 0x00) return response; /**check if SD status: 0x00 - OK (no flag activated)*/ SD_CS_ASSERT; SPI_transmit(0xfe); /**send the 0xfe block (page 197 datasheet SD)*/ for(i=0; i<512; i++) /**send the 512 bytes data*/ SPI_transmit(buffer[i]); SPI_transmit(0xff); //transmit dummy CRC (16-bit), CRC is ignored here SPI_transmit(0xff); response = SPI_receive(); if( (response & 0x1f) != 0x05) /**response= 0xXXX0AAA1 ; if: { AAA='010' - data accepted AAA='101'-data rejected due to CRC error AAA='110'-data rejected due to write error }*/ { SD_CS_DEASSERT; return response; } while(!SPI_receive()) /**wait for SD card to complete the writing and go to get idle state*/ if(retry++ > 0xfffe){SD_CS_DEASSERT; return 1;} /**function output time-out*/ SD_CS_DEASSERT; SPI_transmit(0xff); /**allow a 8 clock cycles delay before CS line reaffirming*/ SD_CS_ASSERT; /**CS line reaffirming to check if the card is still busy*/ while(!SPI_receive()) /**wait for SD card to complete the writing and go to get idle state*/ if(retry++ > 0xfffe){ SD_CS_DEASSERT; return 1; } SD_CS_DEASSERT; return 0; } #ifndef FAT_TESTING_ONLY //***************************************************************************/ //Function : to read multiple blocks from SD card & send every block to UART //Arguments : none //return : unsigned char; will be 0 if no error, // otherwise the response byte will be sent //****************************************************************************/ unsigned char SD_readMultipleBlock (unsigned long startBlock, unsigned long totalBlocks) { unsigned char response; unsigned int i, retry=0; retry = 0; response = SD_sendCommand(READ_MULTIPLE_BLOCKS, startBlock); //write a Block command if(response != 0x00) return response; //check for SD status: 0x00 - OK (No flags set) SD_CS_ASSERT; while( totalBlocks ) { retry = 0; while(SPI_receive() != 0xfe) //wait for start block token 0xfe (0x11111110) if(retry++ > 0xfffe){SD_CS_DEASSERT; return 1;} //return if time-out for(i=0; i<512; i++) //read 512 bytes buffer[i] = SPI_receive(); SPI_receive(); //receive incoming CRC (16-bit), CRC is ignored here SPI_receive(); SPI_receive(); //extra 8 cycles TX_NEWLINE; transmitString_F(PSTR(" --------- ")); TX_NEWLINE; for(i=0; i<512; i++) //send the block to UART { if(buffer[i] == '~') break; transmitByte ( buffer[i] ); } TX_NEWLINE; transmitString_F(PSTR(" --------- ")); TX_NEWLINE; totalBlocks--; } SD_sendCommand(STOP_TRANSMISSION, 0); //command to stop transmission SD_CS_DEASSERT; SPI_receive(); //extra 8 clock pulses return 0; } //***************************************************************************/ //Function: to receive data from UART and write to multiple blocks of SD card //Arguments: none //return: unsigned char; will be 0 if no error, // otherwise the response byte will be sent //****************************************************************************/ unsigned char SD_writeMultipleBlock(unsigned long startBlock, unsigned long totalBlocks) { unsigned char response, data; unsigned int i, retry=0; unsigned long blockCounter=0, size; response = SD_sendCommand(WRITE_MULTIPLE_BLOCKS, startBlock); //write a Block command if(response != 0x00) return response; //check for SD status: 0x00 - OK (No flags set) SD_CS_ASSERT; TX_NEWLINE; transmitString_F(PSTR(" Enter text (End with ~): ")); TX_NEWLINE; while( blockCounter < totalBlocks ) {//e. Cette librairie est dépendante de la librairie SPI. Voici le code à m i=0; do { data = receiveByte(); if(data == 0x08) //'Back Space' key pressed { if(i != 0) { transmitByte(data); transmitByte(' '); transmitByte(data); i--; size--; } continue; } transmitByte(data); buffer[i++] = data; if(data == 0x0d) { transmitByte(0x0a); buffer[i++] = 0x0a; } if(i == 512) break; }while (data != '~'); TX_NEWLINE; transmitString_F(PSTR(" ---- ")); TX_NEWLINE; SPI_transmit(0xfc); //Send start block token 0xfc (0x11111100) for(i=0; i<512; i++) //send 512 bytes data SPI_transmit( buffer[i] ); SPI_transmit(0xff); //transmit dummy CRC (16-bit), CRC is ignored here SPI_transmit(0xff); response = SPI_receive(); if( (response & 0x1f) != 0x05) //response= 0xXXX0AAA1 ; AAA='010' - data accepted { //AAA='101'-data rejected due to CRC error SD_CS_DEASSERT; //AAA='110'-data rejected due to write error return response; } while(!SPI_receive()) //wait for SD card to complete writing and get idle if(retry++ > 0xfffe){SD_CS_DEASSERT; return 1;} SPI_receive(); //extra 8 bits blockCounter++; } SPI_transmit(0xfd); //send 'stop transmission token' retry = 0; if(data == '~') { fileSize--; //to remove the last entered '~' character i--; for(;i<512;i++) //fill the rest of the buffer with 0x00 buffer[i]= 0x00; error = SD_writeSingleBlock (startBlock); txString(_COM1, "data = ~\n"); break; } while(!SPI_receive()) //wait for SD card to complete writing and get idle if(retry++ > 0xfffe){SD_CS_DEASSERT; return 1;} SD_CS_DEASSERT; SPI_transmit(0xff); //just spend 8 clock cycle delay before reasserting the CS signal SD_CS_ASSERT; //re assertion of the CS signal is required to verify if card is still busy while(!SPI_receive()) //wait for SD card to complete writing and get idle if(retry++ > 0xfffe){SD_CS_DEASSERT; return 1;} SD_CS_DEASSERT; return 0; }
int main(void) { unsigned char option, error, data, FAT32_active; unsigned int i; unsigned char fileName[13]; _delay_ms(100); //delay for VCC stabilization init_devices(); PORTD |= 0x04; //switching ON the LED (for testing purpose only) TX_NEWLINE; TX_NEWLINE; transmitString_F (PSTR("***********************************")); TX_NEWLINE; transmitString_F (PSTR(" Dharmani's microSD Card Testing..")); TX_NEWLINE; transmitString_F (PSTR("***********************************")); TX_NEWLINE; SD_init(); SPI_HIGH_SPEED; //SCK - 4 MHz _delay_ms(1); FAT32_active = 1; error = getBootSectorData (); //read boot sector and keep necessary data in global variables if(error) { transmitString_F (PSTR("FAT32 not found!")); //FAT32 incompatible drive FAT32_active = 0; } while(1) { TX_NEWLINE; transmitString_F(PSTR("Press any key...")); TX_NEWLINE; option = receiveByte(); TX_NEWLINE; transmitString_F(PSTR("> 0 : Erase Blocks")); TX_NEWLINE; transmitString_F(PSTR("> 1 : Write single Block")); TX_NEWLINE; transmitString_F(PSTR("> 2 : Read single Block")); #ifndef FAT_TESTING_ONLY TX_NEWLINE; transmitString_F(PSTR("> 3 : Write multiple Blocks")); TX_NEWLINE; transmitString_F(PSTR("> 4 : Read multiple Blocks")); #endif TX_NEWLINE; transmitString_F(PSTR("> 5 : Get file list")); TX_NEWLINE; transmitString_F(PSTR("> 6 : Read File")); TX_NEWLINE; transmitString_F(PSTR("> 7 : Create File")); TX_NEWLINE; transmitString_F(PSTR("> 8 : Delete File")); TX_NEWLINE; transmitString_F(PSTR("> 9 : Read SD Memory Capacity (Total/Free)")); TX_NEWLINE; TX_NEWLINE; transmitString_F(PSTR("> Select Option (0-9): ")); /*WARNING: If option 0, 1 or 3 is selected, the card may not be detected by PC/Laptop again, as it disturbs the FAT format, and you may have to format it again with FAT32. This options are given for learning the raw data transfer to & from the SD Card*/ option = receiveByte(); transmitByte(option); if(option >= 0x35 && option <= 0x39) //options 5 to 9 disabled if FAT32 not found { if(!FAT32_active) { TX_NEWLINE; TX_NEWLINE; transmitString_F(PSTR("FAT32 options disabled!")); continue; } } if((option >= 0x30) && (option <=0x34)) //get starting block address for options 0 to 4 { TX_NEWLINE; TX_NEWLINE; transmitString_F(PSTR("Enter the Block number (0000-9999):")); data = receiveByte(); transmitByte(data); startBlock = (data & 0x0f) * 1000; data = receiveByte(); transmitByte(data); startBlock += (data & 0x0f) * 100; data = receiveByte(); transmitByte(data); startBlock += (data & 0x0f) * 10; data = receiveByte(); transmitByte(data); startBlock += (data & 0x0f); TX_NEWLINE; } totalBlocks = 1; #ifndef FAT_TESTING_ONLY if((option == 0x30) || (option == 0x33) || (option == 0x34)) //get total number of blocks for options 0, 3 or 4 { TX_NEWLINE; TX_NEWLINE; transmitString_F(PSTR("How many blocks? (000-999):")); data = receiveByte(); transmitByte(data); totalBlocks = (data & 0x0f) * 100; data = receiveByte(); transmitByte(data); totalBlocks += (data & 0x0f) * 10; data = receiveByte(); transmitByte(data); totalBlocks += (data & 0x0f); TX_NEWLINE; } #endif switch (option) { case '0': //error = SD_erase (block, totalBlocks); error = SD_erase (startBlock, totalBlocks); TX_NEWLINE; if(error) transmitString_F(PSTR("Erase failed..")); else transmitString_F(PSTR("Erased!")); break; case '1': TX_NEWLINE; transmitString_F(PSTR(" Enter text (End with ~):")); i=0; do { data = receiveByte(); transmitByte(data); buffer[i++] = data; if(data == '\r') //append 'newline' character whenevr 'carriage return' is received { transmitByte('\n'); buffer[i++] = '\n'; } if(i == 512) break; }while (data != '~'); error = SD_writeSingleBlock (startBlock); TX_NEWLINE; TX_NEWLINE; if(error) transmitString_F(PSTR("Write failed..")); else transmitString_F(PSTR("Write successful!")); break; case '2': error = SD_readSingleBlock (startBlock); TX_NEWLINE; if(error) transmitString_F(PSTR("Read failed..")); else { for(i=0;i<512;i++) { if(buffer[i] == '~') break; transmitByte(buffer[i]); } TX_NEWLINE; TX_NEWLINE; transmitString_F(PSTR("Read successful!")); } break; //next two options will work only if following macro is cleared from SD_routines.h #ifndef FAT_TESTING_ONLY case '3': error = SD_writeMultipleBlock (startBlock, totalBlocks); TX_NEWLINE; if(error) transmitString_F(PSTR("Write failed..")); else transmitString_F(PSTR("Write successful!")); break; case '4': error = SD_readMultipleBlock (startBlock, totalBlocks); TX_NEWLINE; if(error) transmitString_F(PSTR("Read failed..")); else transmitString_F(PSTR("Read successful!")); break; #endif case '5': TX_NEWLINE; findFiles(GET_LIST,0); break; case '6': case '7': case '8': TX_NEWLINE; TX_NEWLINE; transmitString_F(PSTR("Enter file name: ")); for(i=0; i<13; i++) fileName[i] = 0x00; //clearing any previously stored file name i=0; while(1) { data = receiveByte(); if(data == '\r') break; //'ENTER' key pressed if(data == 0x08) //'Back Space' key pressed { if(i != 0) { transmitByte(data); transmitByte(' '); transmitByte(data); i--; } continue; } if(data <0x20 || data > 0x7e) continue; //check for valid English text character transmitByte(data); fileName[i++] = data; if(i==13){transmitString_F(PSTR(" file name too long..")); break;} } if(i>12) break; TX_NEWLINE; if(option == '6') readFile( READ, fileName); if(option == '7') createFile(fileName); if(option == '8') deleteFile(fileName); break; case '9': memoryStatistics(); break; default: TX_NEWLINE; TX_NEWLINE; transmitString_F(PSTR(" Invalid option!")); TX_NEWLINE; } TX_NEWLINE; } return 0; }