Pongo aquí un código resumido de las funciones que entran en juego e ignorando las otras para sintetizar un poco, aunque ya aviso que el código puede ser algo lioso y no es 100% definitivo. No soy de los de pensar la idea al 100% antes, sino más bien de programar sobre la marcha, así que habrá muchas cosas que se puedan hacer mejor. Además, es el primer proyecto que hago en PIC, y a poco que te pasas con el 12F508 llenas la memoria...
En fin, que el código es espeso, pero bueno, céntrate en las partes en las que se leen y escriben los pines. Ahí va, y el proyecto entero lo adjunto.
#include <12F508.h>
#use delay(clock=4M)
#use fast_io(B)
#FUSES NOMCLR //Master Clear pin used for I/O
#FUSES INTRC
#include <wire_com.h> // Librería del protocolo de comunicación
void main()
{
wire_initialize(0x56); // Dirección local del dispositivo
while(1) {
int8 mensaje = wire_read(10000); // Timeout de ~= 10ms
if (wire_dataReceived()) { // Solo para testear que ha recibido datos
output_high(PIN_B4);
delay_ms( 1 );
output_low(PIN_B4);
delay_ms( 1 );
}
}
}
// Archivo "wire.h"
#include <manchester_tx_rx.h> // Archivo de transmisión/recepción de tramas
#define PIN_DATA PIN_B0
#define PIN_STATE PIN_B1
// Indica si el canal está libre. Se utiliza cuando se quiere iniciar
// una transmisión y para saber si se debe empezar a recibir un mensaje
int1 wire_isIdle() {
if ( input(PIN_STATE) ) {
return 0;
} else {
connected = 0;
return 1;
}
}
// Recibe la siguiente trama. Devuelve la trama si la recibe, y 0 si vence el timeout sin recibir nada.
// El timeout se expresa en milisegundos y solo es una aproximación.
frame wire_read(int16 max_timeout_us) {
if ( !wire_isIdle() ) // Si la línea está ocupada...
if (connected)
return manchester_receiveMessage(max_read_us); // Si existe conexión, se devuelve el siguiente dato
else
return 0; // Si no existe conexión pero la línea está ocupada, no se lee nada
// Si se llega a este punto, la línea está libre. Se espera un tiempo a que se ocupe.
setup_counters(RTCC_INTERNAL,RTCC_DIV_4); // TIMER0: Clock Interno, Prescaler 4 (Para 4MHZ -> Incrementos cada 4us)
set_rtcc(0);
while ( wire_isIdle() )
if (get_rtcc()*4 > max_timeout_us) return 0; // Si vence el timeout, se devuelve 0
// Si se llega a este punto, la línea está ocupada. La primera trama es la de la dirección de destino,
// que debe coincidir con la local. Si no es así, la conexión no va dirigida al dispositivo local.
if (manchester_receiveMessage(max_read_us) == local_address) {
connected = 1; // Se establece conexión con el dispositivo
remote_address = manchester_receiveMessage(max_read_us); // La segunda trama es la dirección remota
return manchester_receiveMessage(max_read_us); // Se devuelve la tercera trama, que ya es de datos
}
return 0;
}
Archivo "manchester_tx_rx.h"
#define PIN_RX PIN_B0
void manchester_restart() {
message = 0;
message_count = 0;
message_received = 0;
next_edge = 1;
timeout += get_rtcc(); // Se incrementa el timeout con el tiempo (us) transcurrido
set_rtcc(0);
}
// Se introduce el bit en el mensaje y se preparan las variables
// para seguir con la adquisición. NOTA: Se transmite primero el MSB
void manchester_setNextBit() {
message <<= 1;
message |= next_edge;
message_count++;
next_edge = !next_edge;
timeout = 0;
}
// Se espera a conseguir una trama completa, cuyo
// resultado se almacenará en 'message' (MSB primero)
// La trama está compuesto por un bit de inicio y los
// bits de datos
msg manchester_receiveMessage(int16 max_timeout_us) {
setup_counters(RTCC_INTERNAL,RTCC_DIV_4); // TIMER0: Clock Interno, Prescaler 4 (Para 4MHZ -> Incrementos cada 4us)
manchester_restart();
timeout = 0;
while (message_count < (message_length+1)) { // Se recibirá 1 bit más de la longitud de la trama (bit de start)
if (input(PIN_RX) == next_edge)
{
int8 timer = get_rtcc(); // Se captura el valor del timer y se
set_rtcc(0); // resetea con cada flanco de reloj
if (message == 0) {
manchester_setNextBit(); // Se establece el primer bit a 1 (bit de start). El último bit que se reciba
// desplazará este bit fuera de la trama, haciendo que se pierda (no son datos)
} else {
if (abs((signed)(pulse_us/2 - timer)) < tolerance) {
manchester_setNextBit();
} else if (abs((signed)(pulse_us/4 - timer)) < tolerance) {
next_edge = !next_edge; // Como este flanco no implica nada,
set_rtcc( timer + get_rtcc() ); // se deja el timer como estaba
} else {
//timeout += timer; // Se incrementa el timeout con el tiempo (us) transcurrido
manchester_restart();
}
}
}
if (get_rtcc() > (pulse_us/2 + tolerance)) manchester_restart();
if (input_state(PIN_B1) == 0) return 0; // Esta línea es para su uso con la librería wire.h (habría que cambiarla). Hace que si el
// pin de estado está libre se deje de esperar una trama, ya que no hay conexión activa.
if (timeout*4 > max_timeout_us) return 0; // Condición para el vencimiento del timeout
}
message_received = 1;
return message;
}
Lo dicho, es lioso, pero es parte de un protocolo más o menos robusto (a falta de ACKs, que podrían hacerse por software). Si puedes sacar algo sería genial (La parte de transmisión y otras funciones van en el proyecto adjunto).
Gracias!