/////////////////////////////////////////////////////////////////////////
//// ////
//// Driver para implementar comunicaciones bajo el bus RS485. ////
//// Modificación para usar solo con interrupción por el puerto B. ////
//// Programador: Moyano Jonathan. ////
//// Lineas de programa comentadas en español para mejor comprensión ////
//// por parte del programador. ////
//// ////
//// int1 rs485_recibe_datos(int *datos, int espera) ////
//// - Toma datos del bus RS485. ////
//// - La dirección puede ser 1 a 33. ////
//// - La función retorna 1 si hay error en la paridad de los ////
//// datos recibidos. ////
//// ////
//// int1 rs485_envia_mensaje(int destinatario,int len, int*dato) ////
//// - Envía un mensaje por el bus RS485. //// ////
//// - Destinatario : Dirección del destinatario. ////
//// - len: Longitud del mensaje. ////
//// - *dato: Puntero al mensaje. ////
//// - Retorna TRUE si lo envió con exito, FALSE en caso de ////
//// poder haber enviado el mensaje. ////
//// ////
//// void rs485_espera(int1 clrwdt) ////
//// - Espera que se abra el bus RS485. Normalmente usado antes ////
//// de efectuar un envio de mensajes para evitar colisiones ////
//// de datos. ////
//// - Resetea al perro guardían en caso de que no responda. //// ////
//// - Función no necesaria en caso de que se envíen datos ////
//// continuamente. ////
//// ////
//// void rs485_init() ////
//// - Inicia el bus RS485...se llama antes que cualquier ////
//// función. ////
//// ////
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
#ifndef RS485_ID
#define RS485_ID 0x10 // Dirección del dispositivo dentro del bus por defecto.
#endif
#define RS485_RX_PIN PIN_B0 // PIN recepción de datos.
#define RS485_TX_PIN PIN_B1 // PIN transmisión de datos.
#define RS485_ENABLE_PIN PIN_B3 // PIN DE. (recepción) low, (transmisión) high.
#define RS485_RX_ENABLE PIN_B2 // PIN RE. Debería mantenerse en nivel bajo...
#use rs232(baud=9600, xmit=RS485_TX_PIN, rcv=RS485_RX_PIN, enable=RS485_ENABLE_PIN, bits=9, long_data, errors, stream=RS485)
#use rs232(baud=9600, xmit=RS485_TX_PIN, rcv=RS485_RX_PIN, enable=RS485_ENABLE_PIN, bits=9, long_data, multi_master, errors, stream=RS485_CD)
#define RCV_OFF() {disable_interrupts(INT_EXT);} // Función para apagar recepciones de datos.
#define RS485_wait_time 20 // Periodo de espera en milisegundos.
#bit rs485_collision = rs232_errors.6 // Definimos bit indicador de colisión de datos.
#ifndef RS485_RX_BUFFER_SIZE // Definimos tamaño del BUFFER de recepción.
#define RS485_RX_BUFFER_SIZE 20 // Por defecto 20....
#endif
int rs485_state, rs485_ni, rs485_no;
int rs485_buffer[RS485_RX_BUFFER_SIZE]; // Definimos la variable que contendrá los datos.
// Su tamaño viene dado por RS485_RX_BUFFER_SIZE....
// Función: Habilitar la recepción de datos.
void RCV_ON(void) {
clear_interrupt(INT_EXT);
enable_interrupts(INT_EXT);
}
// Función: Iniciar el bus RS485.
void rs485_init() {
RCV_ON(); // Habilitamos recepción de datos.
rs485_state=0; // Ponemos variable a 0....
rs485_ni=0;
rs485_no=0;
ext_int_edge(H_TO_L); // Interrupción por flanco de bajada.
enable_interrupts(INT_EXT); // Habilitamos interrupción por cambio de estado PIN_RB0.
enable_interrupts(GLOBAL); // Habilitamos interrupciones globalmente.
output_low(RS485_RX_ENABLE); // Escuchamos....( modo recepción ).
}
// Indice para el buffer de recepción temporal..
int8 temp_ni;
// Función: Añade un byte al buffer de recepción temporal.
void rs485_add_to_temp(int8 b) {
// Guarda el byte.
rs485_buffer[temp_ni] = b;
// indexado ciclico..
if(++temp_ni >= RS485_RX_BUFFER_SIZE)
{
temp_ni = 0;
}
}
// Función: Rutina para manejar los datos entrantes...
#int_ext
void incomming_rs485() {
int16 b;
static int8 cs,state=0,len;
static int16 to,source;
b
=fgetc(RS485
); // Toma los datos del bus y los guarda en b. cs^=(int8)b; // Aplica una XOR a la variable b.
switch(state) {
case 0: // Toma desde la dirección...
temp_ni=rs485_ni;
source=b;
cs=b;
rs485_add_to_temp(source);
break;
case 1: // Llegar a la dirección...
to=b;
break;
case 2: // Obtiene la longitud...
len=b;
rs485_add_to_temp(len);
break;
case 255: // Hace un checksum..para ver si los datos están correctos.
if ((!cs)&&(bit_test(to,8))&&(bit_test(source,8))&&((int8)to==RS485_ID)) { // Si cs==0,entonces el checksum está bien.
rs485_ni=temp_ni;
}
state=0;
return;
default: // Obtiene los datos...
rs485_add_to_temp(b);
--len;
break;
}
if ((state>=3) && (!len)) {
state=255;
}
else {
++state;
}
}
// Función: Envía datos por el bus RS485.
// Entradas: 1) La dirección del destinatario.
// 2) El número de datos a enviar.
// 3) El puntero a los datos a enviar.
// Salidas: TRUE si puede concretar la operación.
// FALSE si falla.
// Nota: Formato: fuente| destinatario | longitud de datos |datos | checksum.
int1 rs485_envia_mensaje(int destinatario,int len, int*dato) {
int8 try, i, cs;
int1 ret = FALSE;
RCV_OFF(); // Desactivamos la recepción de datos.
disable_interrupts(GLOBAL); // Desactivamos las interrupciones.
for(try=0; try<5; ++try) {
rs485_collision = 0; // No hay colisión de datos.
fputc((int16)0x100|rs485_id
, RS485_CD
); // Transmitimos...quien envía los datos.. fputc((int16)0x100|destinatario
, RS485_CD
); // elejimos un destinatario... fputc(len
, RS485_CD
); // Enviamos la longitud de datos...
for(i=0, cs=rs485_id^to^len; i<len; ++i) {
cs ^= *dato;
fputc(*dato
, RS485_CD
); // enviamos los datos... ++dato;
}
fputc(cs
, RS485_CD
); // transmite checksum... if(!rs485_collision) { // Si no hay colisión de errores....
ret = TRUE; // Sigue y salta...
break;
}
delay_ms(RS485_ID);
}
RCV_ON(); // Habilitamos la recepción de datos nuevamente.
enable_interrupts(GLOBAL); // Habilitamos las interrupciones globales.
return(ret); // Retornamos reporte de fallas...
}
// Función: Espera a que el bus esté listo para poder enviar datos.
// Entrada: TRUE - Resetea el perro guardían para evitar el reset.
// FALSO - El perro guardían no ha sido reseteado.
// Salidas: Ninguna.
void rs485_espera(int1 clrwdt)
{
int16 i;
RCV_OFF(); // Apagamos la recepción de datos.
for(i=0; i <= (rs485_wait_time*20); ++i)
{
if(!input(RS485_RX_PIN)) // Esperamos a que el PIN RX esté a 0.
i = 0; // reseteamos el contador.
else // sino ....
delay_us(50);
if(clrwdt)
restart_wdt(); // reseteamos el perro guardían.
}
}
// Función: Toma un dato del bus RS485 y lo guarda en un buffer.
// Entradas: 1) Puntero al buffer para guardar el mensaje.
// 2) TRUE - En espera del mensaje.
// FALSE - Solo comprueba si un mensaje está disponible.
// Salidas: TRUE - Si el mensaje fue recibido.
// FALSE - Si espera es FALSO y ningún mensaje está disponible.
// Nota: Los datos rellenan el buffer con el siguiente orden:
// ID del emisor | Longitud del dato | datos...
int1 rs485_recibe_datos(int *datos, int espera)
{
while(espera && (rs485_ni == rs485_no)) {}
if(rs485_ni == rs485_no)
return FALSE;
else {
int n;
n = rs485_buffer[(rs485_no+1)%sizeof(rs485_buffer)] + 2;
for(; n>0; --n)
{
*datos = rs485_buffer[rs485_no];
if(++rs485_no >= sizeof(rs485_buffer))
{
rs485_no = 0;
}
++datos;
}
return TRUE;
}
}