/** \file SDCard_hard.c
   \version: 1.0
   
   \brief Este fichero contiene libreria para manejo de memoria SD a nivel hardware.
   
   \author Suky. 
   \web www.infopic.comlu.com
   \date 25/10/09
                                       
*/

#use spi(MASTER,BITS=8,MODE=3,FORCE_HW,stream=SDCard)


#define SD_CS PIN_C0
#define CMD0 0       // Resetea la SD Card.-
#define CMD1 1       // Activa proceso de inicialización de SD Card.
#define CMD9 9       // Lectura registro CSD.-
#define CMD10 10     // Lectura registro CID.-
#define CMD16 16     // Selecciona largo del bloque para lectura/escritura (1 a 512) 
#define CMD17 17     // Lectura de un único bloque.
#define CMD24 24     // Escritura de un único bloque.
#define CMD59 59     // Enciende/Apaga CRC.
#define CMD32 32     // Setea dirección  del primer bloque a borrar.-
#define CDM33 33     // Setea dirección del último bloque en un continuo rango a borrar.-
#define CMD38 38     // Borra todos los sectores entre las direcciónes establecidas.-

#define BLOCK_SIZE 512 // Tamaño del bloque de lectura/escritura.

/* *************************************************************************** */
void SDSelect(void);
void SDDeselect(void);
int1 SDCard_send_command(int cmd, int32 arg,int& res);
int1 SDCard_init(void);
int1 SDCard_read_block(int32 Address,int *Buffer);
int1 SDCard_write_block(int32 Address,int *Buffer);
int1 SDCard_read_CSD(void);
int1 SDCard_read_CID(void);
  
/* *************************************************************************** */
/** \brief Selecciona memoria llevando a low pin CS.-
   
   \param Ninguno
   \return  Ninguno
*/
void SDSelect(void){
   output_low(SD_CS);
   spi_xfer(SDCard,0xFF);
}
/** \brief Desactiva memoria llevando a high pin CS.-
   
   \param Ninguno
   \return  Ninguno
*/
void SDDeselect(void){
   spi_xfer(SDCard,0xFF);
   output_high(SD_CS);
}
/** \brief Envia comando a memoria SD.-
   
   \param cmd: Comando a enviar.-
   \param arg: Argumento del comando.-
   \return res: Respuesta al comando.-
   \return  0 a ocurrido algún error, 1 todo ok.-
*/
int1 SDCard_send_command(int cmd, int32 arg,int& res){ 

   int Trama[6]; // Comando, Argumento y CRC
   int i,ResTmp;
   // Construimos el paquete a enviar.-
   Trama[0] = cmd | 0x40;
   Trama[1] = make8(arg, 3);
   Trama[2] = make8(arg, 2);
   Trama[3] = make8(arg, 1);
   Trama[4] = make8(arg, 0);

   if(cmd==0){
      Trama[5]=0x95;
   }else{   
      Trama[5]=0xFF;
   }
   
   for( i=0;i<16;i++) spi_xfer(SDCard,0xFF);
   // Transfiere el paquete.-
   spi_xfer(SDCard, Trama[0]);
   spi_xfer(SDCard, Trama[1]);
   spi_xfer(SDCard, Trama[2]);
   spi_xfer(SDCard, Trama[3]);
   spi_xfer(SDCard, Trama[4]);
   spi_xfer(SDCard, Trama[5]);
      
   // // Se espera respuesta R1
   i=0;
   do{
      ResTmp=spi_xfer(SDCard,0xFF);
      i++;
   }while((ResTmp&0x80)!=0 && i<200);// mientras no corresponda con 0xxxxxxx.-
   if(i>=200){SDDeselect();return(0);}
   res=ResTmp;  
   #if RS232_DEBUG
      printf("           >> Repeticiones para respuesta de CmdXX: %u; Respuesta recibida:%u\r\n",i,Res);
   #endif
   return(1);
}

/** \brief Inicialización de SD Card

   \param Ninguno
   \return 1 si se ha iniciado correctamente, 0 caso contrario.
*/
int1 SDCard_init(void){
int i,Respuesta;
   
   SETUP_SPI(SPI_MASTER|SPI_CLK_DIV_16|SPI_H_TO_L|SPI_XMIT_L_TO_H ); 
   
   #if RS232_DEBUG
      printf("\r\n--> Se inicia sincronizacion\r\n");
   #endif
   output_high(SD_CS);
   delay_ms(20);
   for(i=0;i<20;i++) spi_xfer(SDCard,0xFF);   // Para sincronización.
   output_low(SD_CS);
   delay_ms(20);
   #if RS232_DEBUG
      printf("\r\n--> Se envia CMD0 (Se desactiva SD Card)\r\n");
   #endif
   i=0;
   do{
      if(SDCard_send_command(CMD0,0x00,Respuesta)==0){
         return(0);        // Si se recibe 0 error por timeout.-
      }else{i++;}   
   }while(((Respuesta&0x01)==0x00) && i<200);
   if(i>=200){return(0);} // Timeout esperando desactivación de SD Card.
   // Pasamos a esperar condición de SD Card activa.
   // Si R1=0x01, SD Card en estado desactivado.
   #if RS232_DEBUG
      printf("\r\n--> Se envia CMD1 (Se activa SD Card)\r\n");
   #endif
      i=0;
   do{
      if(SDCard_send_command(CMD1,0x00,Respuesta)==0){
         return(0);     // Si se recibe 0 error por timeout.-
      }else{i++;}
   }while(((Respuesta&0x01)==0x01) && (i<100)); // Mientras se reciba R1=0x01
   if(i>=100){return(0);} // Timeout esperando activación de SD Card.
   
   #if RS232_DEBUG
      printf("\r\n--> Se envia CMD16 (Se fija largo del Bloque para escritura/lectura : %lu)\r\n",BLOCK_SIZE);
   #endif
   // Se fija largo del bloque lectura/escitura.
   if(SDCard_send_command(CMD16,BLOCK_SIZE,Respuesta)==0){
      return(0);
   }else if(Respuesta!=0){
      return(0);
   }

   // Deshabilitado para simulación:
   /*#if RS232_DEBUG
      printf("\r\n--> Se envia CMD59 (Desactivación de CRC)\r\n");
   #endif
   // Se desactiva CRC.
   if(SDCard_send_command(CMD59,0,Respuesta)==0){
      return(0);
   }else if(Respuesta!=0){
      return(0);
   }*/
   
   SDDeselect();
   return(1);
}

/** \brief Realiza lectura de un bloque.-
   
   \param Address: Direccion del sector.-
   \return Buffer: Buffer donde se almacena lectura de memoria.-
   \return  0 a  ocurrido algún error, 1 todo ok.-
*/
int1 SDCard_read_block(int32 Address,int *Buffer){
int Respuesta, TokenTmp;
int16 i;
   // Se envia comando para leer bloque de bytes.-
   #if RS232_DEBUG
      printf("\r\n--> Se envia CMD17 (Lectura de bloque)\r\n");
   #endif
   SDSelect();
   if(SDCard_send_command(CMD17,Address,Respuesta)==0){
      return(0);
   }else if(Respuesta!=0){
      return(0);
   }  
   // Pasamos a esperar Token.
   i=0;
   do{
      TokenTmp=spi_xfer(SDCard,0xFF);
      i++;
   }while(TokenTmp==0xFF && i<100);// Mientras sea 0xFF.-
   if(i==100){SDDeselect();return(0);}
   if((TokenTmp&0xE0)==0){ // Si se recibe 000xxxxx y no 0xFE.-
      SDDeselect();
      return(0);
   }
   #if RS232_DEBUG
      printf("   >> Toquen recibido: 0x%X\r\n",TokenTmp);
   #endif
   // Todo ok, recibimos data.-
   for(i=0;i<BLOCK_SIZE;i++){
      *Buffer++=spi_xfer(SDCard,0xFF);
   }
   // Ignoramos CRC.-
   spi_xfer(SDCard,0xFF);
   spi_xfer(SDCard,0xFF);
   
   SDDeselect();
   #if RS232_DEBUG
      printf("   >> Terminada la lectura\r\n");
   #endif
   return(1);   
}

/** \brief Realiza escritura de un bloque.-
   
   \param Address: Direccion del sector.-
   \param Buffer: Buffer a escribir en memoria.-
   \return  0 a ocurrido algún error, 1 todo ok.-
*/
int1 SDCard_write_block(int32 Address,int *Buffer){
int Respuesta,ResTmp;
int16 i;

   #if RS232_DEBUG
      printf("\r\n--> Se envia CMD24 (Escritura de bloque)\r\n");
   #endif
   SDSelect();
   // Se envia comando para escribir bloque de bytes.-
   if(SDCard_send_command(CMD24,Address,Respuesta)==0){
      return(0);
   }else if(Respuesta!=0){
      return(0);
   }
   #if RS232_DEBUG
      printf("   >> Se envia Token (0xFE)\r\n");
   #endif
   // Enviamos Token.
   spi_xfer(SDCard,0xFE);
   #if RS232_DEBUG
      printf("   >> Se envia Bloque de datos\r\n");
   #endif
   // Enviamos data.-
   for(i=0;i<BLOCK_SIZE;i++){
      spi_xfer(SDCard,*Buffer++);
   }
   // Ignoramos CRC.-
   spi_xfer(SDCard,0xFF);
   spi_xfer(SDCard,0xFF);
   // Esperamos respuesta.-
   i=0;
   do{
      ResTmp=spi_xfer(SDCard,0xFF);
      i++;
   }while(ResTmp==0xFF && i<100);// Mientras sea 0xFF.-
   if(i==100){SDDeselect();return(0);}
   #if RS232_DEBUG
      printf("   >> Respuesta de recepción del bloque: 0x%X  (***00101)\r\n",ResTmp);
   #endif
   if((ResTmp&0x1F)!=0x05){ // Si no se recibe ***00101.-
      SDDeselect();
      return(0);
   }
   // Esperamos a que baje la linea, indicando escritura completa.-
   while(spi_xfer(SDCard,0xFF)==0);
   #if RS232_DEBUG
      printf("   >> Escritura Terminada\r\n");
   #endif
   SDDeselect();
   return(1);   
}

/** \brief Realiza lectura del registro CSD.-
   
   \param Ninguno
   \return  0 a ocurrido algún error, 1 todo ok.-
*/
int1 SDCard_read_CSD(void){
int i, Respuesta,TokenTmp, buf[16];   
   #if RS232_DEBUG
      printf("\r\n--> Se envia CMD9 (Lectura de CSD)\r\n");
   #endif
   SDSelect();
   if(SDCard_send_command(CMD9,0,Respuesta)==0){
      return(0);
   }else if(Respuesta!=0){
      return(0);
   } 
   // Pasamos a esperar Token.
   i=0;
   do{
      TokenTmp=spi_xfer(SDCard,0xFF);
      i++;
   }while(TokenTmp==0xFF && i<200);// Mientras sea 0xFF.-
   if(i==200){SDDeselect();return(0);}
   if((TokenTmp&0xE0)==0){ // Si se recibe 000xxxxx y no 0xFE.-
      SDDeselect();
      return(0);
   }
   #if RS232_DEBUG
      printf("   >> Toquen recibido: 0x%X\r\n",TokenTmp);
   #endif
   // Todo ok, recibimos data.-
   for(i=0;i<16;i++){
      buf[i]=spi_xfer(SDCard,0xFF);
   }
   SDDeselect();
   
   #if RS232_DEBUG
   printf("\r\nCSD_STRUCTURE: %X", (buf[0] & 0x0C) >> 2);
   printf("\r\nTAAC: %X", buf[1]);
   printf("\r\nNSAC: %X", buf[2]);
   printf("\r\nTRAN_SPEED: %X", buf[3]);
   printf("\r\nCCC: %lX", (make16(buf[4], buf[5]) & 0xFFF0) >> 4);
   printf("\r\nREAD_BL_LEN: %X", buf[5] & 0x0F);   
   printf("\r\nREAD_BL_PARTIAL: %X", (buf[6] & 0x80) >> 7);
   printf("\r\nWRITE_BLK_MISALIGN: %X", (buf[6] & 0x40) >> 6);
   printf("\r\nREAD_BLK_MISALIGN: %X", (buf[6] & 0x20) >> 5);
   printf("\r\nDSR_IMP: %X", (buf[6] & 0x10) >> 4);
   printf("\r\nC_SIZE: %lX", (((buf[6] & 0x03) << 10) | (buf[7] << 2) | ((buf[8] & 0xC0) >> 6)));
   printf("\r\nVDD_R_CURR_MIN: %X", (buf[8] & 0x38) >> 3);
   printf("\r\nVDD_R_CURR_MAX: %X", buf[8] & 0x07);
   printf("\r\nVDD_W_CURR_MIN: %X", (buf[9] & 0xE0) >> 5);
   printf("\r\nVDD_W_CURR_MAX: %X", (buf[9] & 0x1C) >> 2);
   printf("\r\nC_SIZE_MULT: %X", ((buf[9] & 0x03) << 1) | ((buf[10] & 0x80) >> 7));
   printf("\r\nERASE_BLK_EN: %X", (buf[10] & 0x40) >> 6);
   printf("\r\nSECTOR_SIZE: %X", ((buf[10] & 0x3F) << 1) | ((buf[11] & 0x80) >> 7));
   printf("\r\nWP_GRP_SIZE: %X", buf[11] & 0x7F);
   printf("\r\nWP_GRP_ENABLE: %X", (buf[12] & 0x80) >> 7);
   printf("\r\nR2W_FACTOR: %X", (buf[12] & 0x1C) >> 2);
   printf("\r\nWRITE_BL_LEN: %X", ((buf[12] & 0x03) << 2) | ((buf[13] & 0xC0) >> 6));
   printf("\r\nWRITE_BL_PARTIAL: %X", (buf[13] & 0x20) >> 5);
   printf("\r\nFILE_FORMAT_GRP: %X", (buf[14] & 0x80) >> 7);
   printf("\r\nCOPY: %X", (buf[14] & 0x40) >> 6);
   printf("\r\nPERM_WRITE_PROTECT: %X", (buf[14] & 0x20) >> 5);
   printf("\r\nTMP_WRITE_PROTECT: %X", (buf[14] & 0x10) >> 4);
   printf("\r\nFILE_FORMAT: %X", (buf[14] & 0x0C) >> 2);
   printf("\r\nCRC: %X\r\n", buf[15]);
   #endif
   return(1);
}

/** \brief Realiza lectura del registro CID.-
   
   \param Ninguno
   \return  0 a ocurrido algún error, 1 todo ok.-
*/
int1 SDCard_read_CID(void){
int i, Respuesta,TokenTmp, buf[16];   
   #if RS232_DEBUG
      printf("\r\n--> Se envia CMD10 (Lectura de CID)\r\n");
   #endif
   SDSelect();
   if(SDCard_send_command(CMD10,0,Respuesta)==0){
      return(0);
   }else if(Respuesta!=0){
      return(0);
   } 
   // Pasamos a esperar Token.
   i=0;
   do{
      TokenTmp=spi_xfer(SDCard,0xFF);
      i++;
   }while(TokenTmp==0xFF && i<200);// Mientras sea 0xFF.-
   if(i==200){SDDeselect();return(0);}
   if((TokenTmp&0xE0)==0){ // Si se recibe 000xxxxx y no 0xFE.-
      SDDeselect();
      return(0);
   }
   #if RS232_DEBUG
      printf("   >> Toquen recibido: 0x%X\r\n",TokenTmp);
   #endif
   // Todo ok, recibimos data.-
   for(i=0;i<16;i++){
      buf[i]=spi_xfer(SDCard,0xFF);
   }
   SDDeselect();
   
   #if RS232_DEBUG
   printf("\r\nManufacturer ID: %X", buf[0]);
   printf("\r\nOEM/Application ID: %c%c", buf[1], buf[2]);
   printf("\r\nProduct Name: %c%c%c%c%c", buf[3], buf[4], buf[5], buf[6], buf[7]);
   printf("\r\nProduct Revision: %X", buf[8]);
   printf("\r\nSerial Number: %X%X%X%X", buf[9], buf[10], buf[11], buf[12]);
   printf("\r\nManufacturer Date Code: %X%X", buf[13] & 0x0F, buf[14]);
   printf("\r\nCRC-7 Checksum: %X\r\n", buf[15]);
   #endif
   return(1);
}

