#include <16F876A.h>
#device ICD=TRUE
#device ADC=8
#include <STDIO.H>          //Para las funciones RS232
#include <STDLIB.H>         //Para la función atol
#include <STRING.H>         //Para funciones con strings

#use delay(clock =19660800) //Cristal 20Mhz
#use i2c(master,sda=PIN_C4, scl=PIN_C3,FORCE_HW,NOFLOAT_HIGH)
#use rs232(baud=38400, xmit=PIN_C6, rcv=PIN_C7)      //Configuracion Puerto Serie
#fuses HS,NOWDT,PUT,NOPROTECT,BROWNOUT
#use standard_io(b)

int mmc_init();
int mmc_get_status();
int mmc_response(unsigned char response);
int mmc_read_block(unsigned long block_number);
int mmc_write_block(unsigned long block_number);

int return0;

int dato;
long int i;
long int x=0;
long int z=0;

/********************************** MMC Init **********************************/
/* Inicializa la tarjeta MMC en modo SPI y establece tamaño de bloque, devuelve 0 en el éxito */
int mmc_init()
{
   int i;
   SETUP_SPI(SPI_MASTER | SPI_H_TO_L | SPI_CLK_DIV_4 | SPI_SS_DISABLED);
   *0x94 |= 0x40;
   *0x14 &= 0xEF;

   OUTPUT_HIGH(PIN_C2); // set SS = 1 (off)
   delay_ms(2000);

   for(i=0;i<10;i++)       // initialise the MMC card into SPI mode by sending clks on
   {
      SPI_WRITE(0xFF);
   }

   OUTPUT_LOW(PIN_C2);     // set SS = 0 (on) tells card to go to spi mode when it receives reset

   SPI_WRITE(0x40);        // send reset command
   SPI_WRITE(0x00);        // all the arguments are 0x00 for the reset command
   SPI_WRITE(0x00);
   SPI_WRITE(0x00);
   SPI_WRITE(0x00);
   SPI_WRITE(0x95);        // precalculated checksum as we are still in MMC mode

if(mmc_response(0x01)==1) return 1;             // if = 1 then there was a timeout waiting for 0x01 from the mmc

i = 0;

   while((i < 255) && (mmc_response(0x00)==1))  // must keep sending command if response
   {
      SPI_WRITE(0x41); // send mmc command one to bring out of idle state
      SPI_WRITE(0x00); // all the arguments are 0x00 for command one
      SPI_WRITE(0x00);
      SPI_WRITE(0x00);
      SPI_WRITE(0x00);
      SPI_WRITE(0xFF); // checksum is no longer required but we always send 0xFF
      i++;
   }

   if(i >= 254) return 1;    // if >= 254 then there was a timeout waiting for 0x00 from the mmc

   OUTPUT_HIGH(PIN_C2);      // set SS = 1 (off)

   SPI_WRITE(0xFF);          // extra clocks to allow mmc to finish off what it is doing

   OUTPUT_LOW(PIN_C2);       // set SS = 0 (on)

   SPI_WRITE(0x50);          // send mmc command one to bring out of idle state
   SPI_WRITE(0x00);
   SPI_WRITE(0x00);
   SPI_WRITE(0x02);          // high block length bits - 512 bytes 0x02
   SPI_WRITE(0x00);          // low block length bits
   SPI_WRITE(0xFF);          // checksum is no longer required but we always send 0xFF

   if((mmc_response(0x00))==1) return 1;

   OUTPUT_HIGH(PIN_C2);      // set SS = 1 (off)
   return 0;
}

/********************************** MMC Get Status **********************************/
/* Obtener el estado de registro de la MMC, para propósitos de depuración */
int mmc_get_status()
{
   OUTPUT_LOW(PIN_C2);   // set SS = 0 (on)

   SPI_WRITE(0x58);      // send mmc command one to bring out of idle state
   SPI_WRITE(0x00);
   SPI_WRITE(0x00);
   SPI_WRITE(0x00);
   SPI_WRITE(0x00);      // always zero as mulitples of 512
   SPI_WRITE(0xFF);      // checksum is no longer required but we always send 0xFF

   OUTPUT_HIGH(PIN_C2); // set SS = 1 (off)
   OUTPUT_HIGH(PIN_C0);
   return 0;
}

/************************** MMC get response **************************************/
/*Lee la MMC repetidamente hasta que obtengamos la respuesta que queremos o de tiempo*/
int mmc_response(unsigned char response)
{
   unsigned long count = 0xFFFF;       // 16bit repeat, it may be possible to shrink this to 8 bit but there is not much point

   while(SPI_READ(0xFF) != response && --count > 0);

   if(count==0) return 1;              // loop was exited due to timeout
   else return 0;                      // loop was exited before timeout
}

/********************************** MMC Read Block **********************************/
/**** Lee un bloque de 512 bytes de la tarjeta MMC y salidas cada byte a RS232 ****/
int mmc_read_block(unsigned long block_number)
{
   unsigned long i;
   unsigned long varh,varl;
   int parte_alta_varh;
   int parte_baja_varh;
   int parte_alta_varl;

   OUTPUT_HIGH(PIN_C1);
   varl=((block_number&0x003F)<<9);
   varh=((block_number&0xFFC0)>>7);

   parte_alta_varh=make8(varh,1);
   parte_baja_varh=make8(varh,0);
   parte_alta_varl=make8(varl,1);

   OUTPUT_LOW(PIN_C2);           // set SS = 0 (on)

   SPI_WRITE(0x51);              // send mmc read single block command
   SPI_WRITE(parte_alta_varh);   // arguments are address
   SPI_WRITE(parte_baja_varh);
   SPI_WRITE(parte_alta_varl);
   SPI_WRITE(0x00);
   SPI_WRITE(0xFF);              // checksum is no longer required but we always send 0xFF

   if((mmc_response(0x00))==1) return 1;   // if mmc_response returns 1 then we failed to get a 0x00 response (affirmative)

   OUTPUT_LOW(PIN_C1);
   if((mmc_response(0xFE))==1) return 1;   // wait for data token

   for(i=0;i!=512;i++)
   {
      putc(SPI_READ(0xFF));      // we should now receive 512 bytes
   }

   SPI_READ(0xFF);               // CRC bytes that are not needed
   SPI_READ(0xFF);

   OUTPUT_HIGH(PIN_C2);          // set SS = 1 (off)
   SPI_WRITE(0xFF);              // give mmc the clocks it needs to finish off

   return 0;
}

/************************** MMC Write Block **************************************/
int mmc_write_block(unsigned long block_number)
{
   unsigned long varh,varl;

   varl=((block_number&0x003F)<<9);
   varh=((block_number&0xFFC0)>>7);

   OUTPUT_LOW(PIN_C2);       // set SS = 0 (on)

   SPI_WRITE(0x58);          // send mmc write block
   SPI_WRITE(make8(varh,1));
   SPI_WRITE(make8(varh,0));
   SPI_WRITE(make8(varl,1));
   SPI_WRITE(0x00);          // always zero as mulitples of 512
   SPI_WRITE(0xFF);          // checksum is no longer required but we always send 0xFF

   if((mmc_response(0x00))==1) return 1;

   SPI_WRITE(0xFE);// send data token

   printf("Listo para recibir datos\r\n");

output_b(0x00);
do
{
   dato=getch();
   if(dato!=0x00)
   {
      if(dato=='?')
      {
         output_high(PIN_B0);
         dato=getch();
         delay_ms(100);
         x=x+1;
         goto next;
      }
      else
      {SPI_WRITE(dato);
      }
   }
}while(TRUE);
Next:

   SPI_WRITE(0xFF); // dummy CRC
   SPI_WRITE(0xFF);

   if((SPI_READ(0xFF)&0x0F)!=0x05) return 1;

   OUTPUT_HIGH(PIN_C2); // set SS = 1 (off)

return 0;
}

/************************ PROGRAMA PRINCIPAL ************************/
void main (void)
{
reset:
   set_tris_c(0b10111111);        //Configuracion PORTC Para Rs232
   set_tris_b(0b00010011);        //Configuracion PORTB

   disable_interrupts(GLOBAL);    // Apaga todas las interrupciones
   enable_interrupts(INT_RDA);    // Da permiso a la interrupcion por recepcion RS232
   enable_interrupts(GLOBAL);     // Ahora habilitamos todas
x=0;
z=0;
   delay_ms(2000);
   return0=mmc_init();            //Inicializa la MMC en modo SPI

do{
output_low(PIN_B1);
output_low(PIN_B4);

   if(input(PIN_B4)){
     do{
         return0=mmc_read_block(z);
         z++;
        }while(z!=x+1);
        goto reset;
   }
   if(input(PIN_B1)){
   printf("%x",x);
   return0=mmc_write_block(x);
   }

}while(TRUE);

}//Fin del Main;
#INT_RDA                       //Tratamiento de la interrupcion por recepcion
void rs232_isr(void)
{
  dato=getch();
}