//**************************************************************
// ****** FUNCTIONS FOR SD RAW DATA TRANSFER *******
//**************************************************************
//Controller: ATmega8 (Clock: 8 Mhz-internal)
//Compiler: AVR-GCC
//Version : 2.0
//Author: CC Dharmani, Chennai (India)
// http://www.dharmanitech.com/
//Date: 26 Feb 2009
//**************************************************************

//Link to the Post: http://www.dharmanitech.com/2009/01/sd-card-interfacing-with-atmega8-fat32.html

//**************************************************
// ***** HEADER FILE : SD_routines.h ******
//**************************************************
#ifndef _SD_ROUTINES_H_
#define _SD_ROUTINES_H_

#define FAT_TESTING_ONLY

#define SD_CS_ASSERT     PORTB &= ~0x02
#define SD_CS_DEASSERT   PORTB |= 0x02

#define GO_IDLE_STATE            0
#define SEND_OP_COND             1
#define SEND_CSD                 9
#define STOP_TRANSMISSION        12
#define SEND_STATUS              13
#define SET_BLOCK_LEN            16
#define READ_SINGLE_BLOCK        17
#define READ_MULTIPLE_BLOCKS     18
#define WRITE_SINGLE_BLOCK       24
#define WRITE_MULTIPLE_BLOCKS    25
#define ERASE_BLOCK_START_ADDR   32
#define ERASE_BLOCK_END_ADDR     33
#define ERASE_SELECTED_BLOCKS    38
#define CRC_ON_OFF               59

#define ON     1
#define OFF    0

extern volatile unsigned long startBlock;
extern volatile unsigned long totalBlocks;
extern volatile unsigned char buffer[512];

unsigned char SD_init(void);
unsigned char SD_sendCommand(unsigned char cmd, unsigned long arg);
unsigned char SD_readSingleBlock(unsigned long startBlock);
unsigned char SD_writeSingleBlock(unsigned long startBlock);
unsigned char SD_readMultipleBlock (unsigned long startBlock, unsigned long totalBlocks);
unsigned char SD_writeMultipleBlock(unsigned long startBlock, unsigned long totalBlocks);
unsigned char SD_erase (unsigned long startBlock, unsigned long totalBlocks);

#endif

//**************************************************************
// ****** FUNCTIONS FOR SD RAW DATA TRANSFER *******
//**************************************************************
//Controller: ATmega8 (Clock: 8 Mhz-internal)
//Compiler: AVR-GCC
//Version : 2.0
//Author: CC Dharmani, Chennai (India)
// http://www.dharmanitech.com/
//Date: 26 Feb 2009
//**************************************************************

//Link to the Post: http://www.dharmanitech.com/2009/01/sd-card-interfacing-with-atmega8-fat32.html

//**************************************************
// ***** SOURCE FILE : SD_routines.c ******
//**************************************************
#include <avr/io.h>
#include <avr/pgmspace.h>
#include "SPI_routines.h"
#include "SD_routines.h"
#include "UART_routines.h"

//******************************************************************
//Function: to initialize the SD 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 i, response, retry=0 ;

SD_CS_ASSERT;
do
{
   for(i=0;i<10;i++)
      SPI_transmit(0xff);
   response = SD_sendCommand(GO_IDLE_STATE, 0);
//send 'reset & go idle' command
   retry++;
   if(retry>0xfe) {transmitString_F(PSTR("SD init fail..")); return 1; }
//time out
} while(response != 0x01);

SD_CS_DEASSERT;

SPI_transmit (0xff);
SPI_transmit (0xff);

retry = 0;

do
{
    response = SD_sendCommand(SEND_OP_COND, 0);
//activate card's initialization process

    response = SD_sendCommand(SEND_OP_COND, 0); //resend command (for compatibility with some cards)
    retry++;
    if(retry>0xfe) return 1;
//time out
}while(response);

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

return 0;
//normal return
}

//******************************************************************
//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;

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);
SPI_transmit(0x95);

while((response = SPI_receive()) == 0xff)
//wait response
   if(retry++ > 0xfe) break;
//time out error

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<<9);
//send starting block address
if(response != 0x00)
//check for SD status: 0x00 - OK (No flags set)
  return response;

response = SD_sendCommand(ERASE_BLOCK_END_ADDR,(startBlock + totalBlocks - 1)<<9);
//send end block address
if(response != 0x00)
  return response;

response = SD_sendCommand(ERASE_SELECTED_BLOCKS, 0);
//erase all selected blocks
if(response != 0x00)
  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<<9);
//read a Block command
//block address converted to starting address of 512 byte Block
if(response != 0x00)
//check for SD status: 0x00 - OK (No flags set)
  return response;

SD_CS_ASSERT;

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 clock pulses
SD_CS_DEASSERT;

return 0;
}

//******************************************************************
//Function: to write to a single block of SD card
//Arguments: none
//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<<9);
//write a Block command
if(response != 0x00)
//check for SD status: 0x00 - OK (No flags set)
return response;

SD_CS_ASSERT;

SPI_transmit(0xfe);    
//Send start block token 0xfe (0x11111110)

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;}

SD_CS_DEASSERT;
SPI_transmit(0xff);  
//just spend 8 clock cycle delay before reasserting the CS line
SD_CS_ASSERT;        
//re-asserting the CS line 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;
}

#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 <<9);
//read a Block command
//block address converted to starting address of 512 byte Block
if(response != 0x00)
//check for SD status: 0x00 - OK (No flags set)
return response;

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<<9);
//write a Block command
if(response != 0x00)
//check for SD status: 0x00 - OK (No flags set)
  return response;

SD_CS_ASSERT;

TX_NEWLINE;
transmitString_F(PSTR(" Enter text (End with ~): "));
TX_NEWLINE;

while( blockCounter < totalBlocks )
{
   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;

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;
}
//*********************************************
#endif


//******** END ****** http://www.dharmanitech.com/ *****