/* Implementacion de I2C por software */
/* De fabianjm para foro TodoPIC */
/* Compilar con Hi-Tech C  */

/* Estas lineas tienen que ser modificadas para definir los
 * pines asociados a las señales SDA y SCL del bus */
#define sda	RA0
#define scl	RA1
#define sda_entrante()	TRISA |= 1
#define sda_saliente()	TRISA &= ~1
#define scl_entrante()	TRISA |= 2
#define scl_saliente()	TRISA &= ~2

int iniciar_i2c();
void retardo();
int leer_ack();
void enviar(unsigned int val);

int main()
{
	unsigned int dir;
	
	while(iniciar_i2c()) {
		printf("Error: Bus I2C\r\n");
		retardo();
	}
	
	/* Los bits de direccion son los 7 bits altos de la variable 'dir'
	 * sumando 2 no afectamos al LSB, solo la direccion. */
	for(dir=1; dir<=255; dir+=2) {
		/* Todas las funciones toman el cuidado de dejar la linea SCL en alto,
		 * de modo que para enviar un START basta bajar SDA */
		sda = 0;
		retardo();
		/* Enviamos la direccion y el bit R/W */
		enviar(dir);
		/* Verificamos si recibimos un ACK */
		if(!leer_ack()) {
		}
		/* No es necesario enviar un STOP,  pero seria de este modo */
		//sda = 1;
		//retardo();
	}
	
	while(1)
		;
}

/* Deja las lineas SDA y SCL configuradas como salidas y en estado alto */
int iniciar_i2c()
{
	/* Cuando el bus esta libre SDA y SCL están en estado alto forzado
	 * por resistencias de pull-up. Vamos a verificar si esta condición se
	 * esta cumpliendo, y que ademas no existe otro maestro en el bus */
	
	/* Enviamos una señal de STOP al bus. Es para no tener problemas cuando
	 * un fallo en la alimentacion reinicia al dispositivo maestro durante la lectura de
	 * un esclavo, que en ese preciso instante habia bajado la señal SDA para enviar
	 * un bit 0, lo que despues nos hace pensar que el bus esta en uso. */
	sda_saliente();
	scl_saliente();
	sda = 0;
	scl = 1;
	retardo();
	sda = 1; /* stop */
	retardo();
	
	sda_entrante();
	scl_entrante();
	if(sda && scl) {
		sda_saliente();
		scl_saliente();
		return 0;
	}
	return 1;
}

void retardo()
{
	int i;
	/* ajustar segun se necesite */
	for(i = 0; i<20; ++i)
		;
}

void enviar(unsigned int val)
{
	int i;
	for(i=128; i; i>>=1) {
		scl = 0;	/* bajamos el reloj */
		retardo();
		if(val & i)	/* seteamos el dato */
			sda = 1;
		else
			sda = 0;
		scl = 1;	/* subimos el reloj */
		retardo();
	}
}

int leer_ack()
{
	int r;
	sda_entrante();
	r = sda;
	sda_saliente();
	return r;
}
